pum.pl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. #!/usr/bin/perl -w
  2. ###
  3. ### pisg user manager version 3.1
  4. ###
  5. ### Copyleft (C) 2005 by Axel 'XTaran' Beckert <abe@deuxchevaux.org>
  6. ### Copyleft (C) 2005 by Torbjörn 'Azoff' Svensson <azoff@se.linux.org>
  7. ###
  8. #
  9. # This is complete reimplementation from scratch of addalias script
  10. # 2.2 by deadlock which itself was based on the original addalias
  11. # program by Doomshammer
  12. #
  13. # The purpose of this script is to let users manage themself their
  14. # info for the pisg ircstats program by mbrix.
  15. #
  16. # This program may be used, copied and distributed under the terms of
  17. # the GNU General Public License (GPL) version 2 or later. See
  18. # http://www.gnu.org/copyleft/gpl.txt or the file COPYING for the full
  19. # license text.
  20. #
  21. # Credits from XTaran to
  22. # + Christoph 'Myon' Berg for motivating me to rewrite addalias.pl
  23. # + #plant on IRCNet (and again Myon ;-) without which I probably
  24. # never would have used addalias.pl and therefore never felt the
  25. # urge to rewrite it from scratch. ;-)
  26. # + The Debian Project for the operating system running on my 133 MHz
  27. # IBM ThinkPad, on which I developed my parts of this piece of Open
  28. # Source Software (although I have other machines around, but I
  29. # entirely developed the script while sitting on the toilet, in bed
  30. # or in the bath tub. ;-)
  31. # + Larry for Perl
  32. # + RMS for GNU Emacs
  33. use Data::Dumper;
  34. use AppConfig qw(ARGCOUNT_ONE);
  35. use CGI qw(:standard *table);
  36. use CGI::Carp qw(fatalsToBrowser carpout);
  37. ###
  38. ### BEGIN CONFIG
  39. ###
  40. my $config_file = "pum.conf";
  41. ###
  42. ### END CONFIG
  43. ###
  44. ###
  45. ### BEGIN INIT
  46. ###
  47. my $VERSION = '3.1';
  48. my $title_prefix = "pisg IRC Statistics User Manager $VERSION";
  49. my $script_uri = $ENV{SCRIPT_NAME};
  50. my %data = ();
  51. my @attributes = qw(nick alias link sex pic bigpic ignore);
  52. param( -name => 'op', -value => 'list' ) unless defined param('op');
  53. # print the default css
  54. if (param('op') eq 'css') {
  55. print <<EOF
  56. Content-type: text/css
  57. table {
  58. border: 0;
  59. border-spacing: 2;
  60. }
  61. td {
  62. background-color: #E5E5E5;
  63. }
  64. #num {
  65. text-align: right;
  66. }
  67. EOF
  68. ;
  69. exit(0);
  70. }
  71. print header();
  72. my $config = AppConfig->new({ GLOBAL => { ARGCOUNT => ARGCOUNT_ONE }});
  73. $config->define('cgi_css', { DEFAULT => '' });
  74. $config->define('cgi_debug', { DEFAULT => 0 });
  75. $config->define('cgi_alias_disp', { DEFAULT => 30 });
  76. $config->define('cgi_user_del', { DEFAULT => 0 });
  77. $config->define('cgi_pics_prefix', { DEFAULT => '' });
  78. $config->define('backup_enable', { DEFAULT => '1' });
  79. $config->define('backup_dir', { DEFAULT => '/tmp' });
  80. $config->define('backup_suffix', { DEFAULT => '%t' });
  81. $config->define('pisg_user_config', { DEFAULT => 'users.conf' });
  82. -e $config_file or die "Configuration file $config_file doesn't exist";
  83. -f _ or die "Configuration file $config_file is no file";
  84. -r _ or die "Configuration file $config_file is not readable";
  85. $config->file($config_file);
  86. ###
  87. ### END INIT
  88. ###
  89. my $title = $title_prefix;
  90. my $css = $config->get('cgi_css');
  91. if (param('op') eq 'show') {
  92. $title .= ": Show user '".param('nick')."'";
  93. } elsif (param('op') eq 'edit') {
  94. $title .= ": Edit user '".param('nick')."'";
  95. } elsif (param('op') eq 'list') {
  96. $title .= ": List all known nicknames";
  97. } elsif (param('op') eq 'del') {
  98. $title .= ": Delete user '".param('nick')."'";
  99. }
  100. print start_html(-title => $title,
  101. -style => { src => ($css ? $css : "$script_uri?op=css")});
  102. print "\n" . h1($title) . "\n";
  103. if (param('op') eq 'show') {
  104. &show_data;
  105. print _p(a({ href => $script_uri.'?op=edit&nick='.param('nick') },
  106. "Edit this data set"));
  107. } elsif (param('op') eq 'edit') {
  108. &show_data_form;
  109. } elsif (param('op') eq 'save' or param('op') eq 'create') {
  110. &save_data;
  111. } elsif (param('op') eq 'list') {
  112. &show_nicks;
  113. } elsif (param('op') eq 'del' and $config->get('cgi_user_del')) {
  114. &del_nick;
  115. }
  116. if (not (param('op') eq 'del' and not param('confirm') and
  117. $config->get('cgi_user_del'))) {
  118. print _p(a({ href => "$script_uri?op=edit" }, 'Create new nick'));
  119. print _p(a({ href => "$script_uri?op=list" }, 'List all known nicks'));
  120. print _p('Back to the '.a({ href => $script_uri }, 'pum start page'));
  121. }
  122. print hr,pre(Dumper({ map { $_ => param($_) } param() },\%data,\%ENV))
  123. if ($config->get('cgi_debug') or param('debug'));
  124. print end_html();
  125. ###
  126. ### functions
  127. ###
  128. # make html readable
  129. sub _table { return table(@_) . "\n"; }
  130. sub _th { return th(@_) . "\n"; }
  131. sub _Tr { return Tr(@_) . "\n"; }
  132. sub _td { return td(@_) . "\n"; }
  133. sub _start_form { return start_form(@_) . "\n"; }
  134. sub _hidden { return hidden(@_) . "\n"; }
  135. sub _submit { return submit(@_) . "\n"; }
  136. sub _reset { return reset(@_) . "\n"; }
  137. sub _end_form { return "</form>\n"; }
  138. sub _p { return p(@_) . "\n"; }
  139. sub read_config {
  140. my ($user) = @_;
  141. my $filename = &get_user_config;
  142. open(CFG, '<', $filename) or
  143. die "Can't open pisg user configuration file '$filename' for reading: $!";
  144. while (my $line = <CFG>) {
  145. chomp($line);
  146. next if $line =~ /^(|#.*)$/;
  147. die "Unknown pisg user configuration file syntax: '$line'"
  148. unless $line =~ m|^\s*<user\s+(.*?)/?>\s*$|i;
  149. my $line_data_string = $1;
  150. my %line_data = ();
  151. while ($line_data_string =~ s/^(\w+)="([^\"]+)"\s*//) {
  152. $line_data{lc($1)} = $2;
  153. }
  154. my $nick = $line_data{nick};
  155. die "No nickname(s) found in '$line'" unless $nick;
  156. $data{lc($nick)} = \%line_data;
  157. last if lc($user) eq lc($nick);
  158. }
  159. close(CFG);
  160. }
  161. sub write_config {
  162. my $filename = &get_user_config;
  163. if ($config->get('backup_enable')) {
  164. use File::Basename;
  165. use File::Copy;
  166. my $time = time();
  167. my $dir = $config->get('backup_dir');
  168. my $name = basename($filename);
  169. my $suffix = $config->get('backup_suffix');
  170. my $newfile = "$dir/$name.$suffix";
  171. $newfile =~ s/\%t/$time/;
  172. copy($filename, $newfile) or
  173. warn "Couldn't copy '$filename' to $newfile': $!";
  174. }
  175. open(CFG, '>', $filename) or
  176. die "Can't open pisg user configuration file '$filename' for writing: $!";
  177. foreach my $key (sort { lc($a) cmp lc($b) } keys %data) {
  178. my $set = $data{$key};
  179. print CFG qq{<user};
  180. die "Data set without nick found: ".Dumper($set) unless $set->{nick};
  181. foreach my $attr (@attributes) {
  182. print CFG qq[ $attr="$set->{$attr}"] if $set->{$attr};
  183. }
  184. print CFG qq{>\n};
  185. }
  186. close(CFG);
  187. }
  188. sub get_user_config {
  189. my $filename = $config->get('pisg_user_config') or
  190. die "Can't find key user_config in section pisg in config file $config_file";
  191. return $filename;
  192. }
  193. sub save_data {
  194. die "No nick given" unless param('nick');
  195. die "Nick may be only changed in capitalisation"
  196. if lc(param('nick')) ne lc(param('old_nick')) and param('op') ne 'create';
  197. my %new_data = ();
  198. foreach my $attr (@attributes) {
  199. my $value = param($attr);
  200. next unless $value;
  201. next if $attr eq 'sex' and $value eq '-';
  202. die "No double quotes allowed in data: '$value'"
  203. if $value =~ /\"/;
  204. warn "Waka waka in data: '$value'"
  205. if $value =~ /[<>]/;
  206. $new_data{$attr} = $value;
  207. }
  208. my $nick = $new_data{nick};
  209. die "No nick in data found" unless $nick;
  210. &read_config;
  211. die "Data for nick '".lc($nick)."' already exists"
  212. if param('op') eq 'create' and $data{lc($nick)};
  213. $data{lc($nick)} = \%new_data;
  214. &write_config;
  215. print _p('Data successfully saved.');
  216. &show_data;
  217. &show_data_form;
  218. }
  219. sub show_data {
  220. my $this = shift;
  221. unless ($this) {
  222. my $nick = lc(param('nick'));
  223. read_config($nick);
  224. $this = $data{$nick};
  225. }
  226. my $pp = $config->get('cgi_pics_prefix');
  227. print start_table;
  228. print _Tr(_th('Nickname'), _th($this->{nick}));
  229. print _Tr(_td('Alias(ses)'), _td($this->{alias}));
  230. print _Tr(_td('Link'), _td(defined($this->{link}) and
  231. $this->{link} =~ m(^http://)i ?
  232. a({ href => $this->{link}}, $this->{link}) :
  233. $this->{link} ?
  234. a({ href => "mailto:$this->{link}"},
  235. $this->{link}) : '(unset)'));
  236. print _Tr(_td('Sex'), _td(defined $this->{sex} ?
  237. $this->{sex} eq 'm' ? 'male' :
  238. $this->{sex} eq 'f' ? 'female' :
  239. $this->{sex} eq 'b' ? 'bot' : '(unset)' :
  240. '(unset)'));
  241. print _Tr(_td('Picture'), _td($this->{pic} ?
  242. img({ src => $pp.$this->{pic},
  243. alt => $this->{pic} }) :
  244. '(unset)'));
  245. print _Tr(_td('Big picture'), _td($this->{bigpic} ?
  246. a({href => $pp.$this->{bigpic}},
  247. $this->{bigpic}) :
  248. '(unset)'));
  249. print _Tr(_td('Ignore'), _td($this->{ignore} ? 'True' : 'False'));
  250. print end_table;
  251. }
  252. sub show_data_form {
  253. my $nick = lc(param('nick'));
  254. read_config($nick) if $nick;
  255. my $this = $data{$nick};
  256. my $pp = $config->get('cgi_pics_prefix');
  257. print _start_form('GET', $script_uri);
  258. print _hidden( -name => 'op', -value => ( $nick ? 'save' : 'create' ),
  259. -override => 1);
  260. print _hidden('old_nick', $nick);
  261. print _table(_Tr(_td('Nickname'), _td(textfield('nick',$this->{nick},9))),
  262. _Tr(_td('Alias(ses)'), _td(textfield('alias',$this->{alias},30))),
  263. _Tr(_td('Link'), _td(textfield('link',$this->{link},30))),
  264. _Tr(_td('Sex'), _td(radio_group('sex',['f','m','b','-'],
  265. $this->{sex} || '-','',
  266. { f => 'female',
  267. m => 'male',
  268. b => 'bot',
  269. '-' => 'unspecified' }))),
  270. _Tr(_td('Picture'), _td(textfield('pic',$this->{pic},30))),
  271. _Tr(_td('Big picture'), _td(textfield('bigpic',$this->{bigpic},30))),
  272. _Tr(_td('Ignore'), _td(checkbox('ignore',
  273. ($this->{ignore} ? 'checked' : ''), 'y', ''))));
  274. print _submit('submit', 'Save data set');
  275. print _reset('reset', 'Reset form');
  276. print _end_form();
  277. if (defined $data{lc($nick)}) {
  278. print _start_form('GET', $script_uri);
  279. print _hidden( -name => 'op', -value => 'del', -override => 1);
  280. print _hidden('nick', $nick);
  281. print _submit('submit', "Remove data for '$nick'");
  282. print _end_form();
  283. }
  284. }
  285. sub _get_op($$) {
  286. my $op = shift;
  287. my $nick = shift;
  288. return a({ href => "$script_uri?op=$op&nick=".escapeHTML($nick) }, $op);
  289. }
  290. sub show_nicks {
  291. read_config();
  292. print start_table;
  293. my $i=1;
  294. my $alias_disp = $config->get('cgi_alias_disp');
  295. foreach my $nick (sort keys %data) {
  296. my $alias = $data{$nick}{alias} || '';
  297. $nick = $data{$nick}{nick};
  298. if (length($alias) > $alias_disp) {
  299. $alias = substr($alias, 0, $alias_disp) . '...';
  300. }
  301. print _Tr(
  302. _td({id => 'num'}, $i),
  303. _td(&_get_op('show', $nick)),
  304. _td(&_get_op('edit', $nick)),
  305. ($config->get('cgi_user_del') ? _td(&_get_op('del', $nick)) : '' ),
  306. _td(escapeHTML($nick.($alias ? " ($alias)" : ''))),
  307. );
  308. $i++;
  309. }
  310. print end_table;
  311. }
  312. sub del_nick {
  313. die "No nick given" unless param('nick');
  314. if (param('confirm')) {
  315. &read_config;
  316. die "No such nick '".param('nick')."'."
  317. unless defined $data{lc(param('nick'))};
  318. delete $data{lc(param('nick'))};
  319. &write_config;
  320. print _p('Data successfully updated.');
  321. &show_nicks;
  322. } else {
  323. print _p("Are you sure you want to delete the user '".
  324. param('nick')."'?");
  325. print _p(a({href => "$script_uri?op=del&confirm=1&nick=".
  326. escapeHTML(param('nick'))}, 'Yes'),
  327. a({href => ($ENV{HTTP_REFERER} ? $ENV{HTTP_REFERER} :
  328. "$script_uri?op=edit&nick=".
  329. escapeHTML(param('nick')))}, 'No'));
  330. }
  331. }