pum.pl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  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);
  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('cgi_backup', { DEFAULT => '1' });
  79. $config->define('pisg_user_config', { DEFAULT => 'users.conf' });
  80. -e $config_file or die "Configuration file $config_file doesn't exist";
  81. -f _ or die "Configuration file $config_file is no file";
  82. -r _ or die "Configuration file $config_file is not readable";
  83. $config->file($config_file);
  84. ###
  85. ### END INIT
  86. ###
  87. my $title = $title_prefix;
  88. my $css = $config->get('cgi_css');
  89. if (param('op') eq 'show') {
  90. $title .= ": Show user '".param('nick')."'";
  91. } elsif (param('op') eq 'edit') {
  92. $title .= ": Edit user '".param('nick')."'";
  93. } elsif (param('op') eq 'list') {
  94. $title .= ": List all known nicknames";
  95. } elsif (param('op') eq 'del') {
  96. $title .= ": Delete user '".param('nick')."'";
  97. }
  98. print start_html(-title => $title,
  99. -style => { src => ($css ? $css : "$script_uri?op=css")});
  100. print "\n" . h1($title) . "\n";
  101. if (param('op') eq 'show') {
  102. &show_data;
  103. print _p(a({ href => $script_uri.'?op=edit&nick='.param('nick') },
  104. "Edit this data set"));
  105. } elsif (param('op') eq 'edit') {
  106. &show_data_form;
  107. } elsif (param('op') eq 'save' or param('op') eq 'create') {
  108. &save_data;
  109. } elsif (param('op') eq 'list') {
  110. &show_nicks;
  111. } elsif (param('op') eq 'del' and $config->get('cgi_user_del')) {
  112. &del_nick;
  113. }
  114. if (not (param('op') eq 'del' and not param('confirm') and
  115. $config->get('cgi_user_del'))) {
  116. print _p(a({ href => "$script_uri?op=edit" }, 'Create new nick'));
  117. print _p(a({ href => "$script_uri?op=list" }, 'List all known nicks'));
  118. print _p('Back to the '.a({ href => $script_uri }, 'pum start page'));
  119. }
  120. print hr,pre(Dumper({ map { $_ => param($_) } param() },\%data,\%ENV))
  121. if ($config->get('cgi_debug') or param('debug'));
  122. print end_html();
  123. ###
  124. ### functions
  125. ###
  126. # make html readable
  127. sub _table { return table(@_) . "\n"; }
  128. sub _th { return th(@_) . "\n"; }
  129. sub _Tr { return Tr(@_) . "\n"; }
  130. sub _td { return td(@_) . "\n"; }
  131. sub _start_form { return start_form(@_) . "\n"; }
  132. sub _hidden { return hidden(@_) . "\n"; }
  133. sub _submit { return submit(@_) . "\n"; }
  134. sub _reset { return reset(@_) . "\n"; }
  135. sub _end_form { return "</form>\n"; }
  136. sub _p { return p(@_) . "\n"; }
  137. sub read_config {
  138. my ($user) = @_;
  139. my $filename = &get_user_config;
  140. open(CFG, '<', $filename) or
  141. die "Can't open pisg user configuration file '$filename' for reading: $!";
  142. while (my $line = <CFG>) {
  143. chomp($line);
  144. next if $line =~ /^(|#.*)$/;
  145. die "Unknown pisg user configuration file syntax: '$line'"
  146. unless $line =~ m|^\s*<user\s+(.*?)/?>\s*$|i;
  147. my $line_data_string = $1;
  148. my %line_data = ();
  149. while ($line_data_string =~ s/^(\w+)="([^\"]+)"\s*//) {
  150. $line_data{lc($1)} = $2;
  151. }
  152. my $nick = $line_data{nick};
  153. die "No nickname(s) found in '$line'" unless $nick;
  154. $data{lc($nick)} = \%line_data;
  155. last if lc($user) eq lc($nick);
  156. }
  157. close(CFG);
  158. }
  159. sub write_config {
  160. my $filename = &get_user_config;
  161. if ($config->get('cgi_backup')) {
  162. rename($filename, "$filename.backup") or
  163. warn "Couldn't copy '$filename' to $filename.backup': $!";
  164. }
  165. open(CFG, '>', $filename) or
  166. die "Can't open pisg user configuration file '$filename' for writing: $!";
  167. foreach my $key (sort { lc($a) cmp lc($b) } keys %data) {
  168. my $set = $data{$key};
  169. print CFG qq{<user};
  170. die "Data set without nick found: ".Dumper($set) unless $set->{nick};
  171. foreach my $attr (@attributes) {
  172. print CFG qq[ $attr="$set->{$attr}"] if $set->{$attr};
  173. }
  174. print CFG qq{>\n};
  175. }
  176. close(CFG);
  177. }
  178. sub get_user_config {
  179. my $filename = $config->get('pisg_user_config') or
  180. die "Can't find key user_config in section pisg in config file $config_file";
  181. return $filename;
  182. }
  183. sub save_data {
  184. die "No nick given" unless param('nick');
  185. die "Nick may be only changed in capitalisation"
  186. if lc(param('nick')) ne lc(param('old_nick')) and param('op') ne 'create';
  187. my %new_data = ();
  188. foreach my $attr (@attributes) {
  189. my $value = param($attr);
  190. next unless $value;
  191. die "No double quotes allowed in data: '$value'"
  192. if $value =~ /\"/;
  193. warn "Waka waka in data: '$value'"
  194. if $value =~ /[<>]/;
  195. $new_data{$attr} = $value;
  196. }
  197. my $nick = $new_data{nick};
  198. die "No nick in data found" unless $nick;
  199. &read_config;
  200. die "Data for nick '".lc($nick)."' already exists"
  201. if param('op') eq 'create' and $data{lc($nick)};
  202. $data{lc($nick)} = \%new_data;
  203. &write_config;
  204. print _p('Data successfully saved.');
  205. &show_data;
  206. &show_data_form;
  207. }
  208. sub show_data {
  209. my $this = shift;
  210. unless ($this) {
  211. my $nick = lc(param('nick'));
  212. read_config($nick);
  213. $this = $data{$nick};
  214. }
  215. my $pp = $config->get('cgi_pics_prefix');
  216. print table(_Tr(_th('Nickname'), _th($this->{nick})),
  217. _Tr(_td('Alias(ses)'), _td($this->{alias})),
  218. _Tr(_td('Link'), _td(defined($this->{link}) and
  219. $this->{link} =~ m(^http://)i ?
  220. a({ href => $this->{link}}, $this->{link}) :
  221. $this->{link} ?
  222. a({ href => "mailto:$this->{link}"},
  223. $this->{link}) : '(unset)')),
  224. _Tr(_td('Sex'), _td($this->{sex} eq 'm' ? 'male' :
  225. $this->{sex} eq 'f' ? 'female' :
  226. $this->{sex} eq 'b' ? 'bot' :
  227. '(unset)')),
  228. _Tr(_td('Picture'), _td($this->{pic} ?
  229. img({ src => $pp.$this->{pic},
  230. alt => $this->{pic} }) :
  231. '(unset)')),
  232. _Tr(_td('Big picture'), _td($this->{bigpic} ?
  233. a({href => $pp.$this->{bigpic}},
  234. $this->{bigpic}) :
  235. '(unset)')));
  236. }
  237. sub show_data_form {
  238. my $nick = lc(param('nick'));
  239. read_config($nick) if $nick;
  240. my $this = $data{$nick};
  241. my $pp = $config->get('cgi_pics_prefix');
  242. print _start_form('GET', $script_uri);
  243. print _hidden( -name => 'op', -value => ( $nick ? 'save' : 'create' ),
  244. -override => 1);
  245. print _hidden('old_nick', $nick);
  246. print _table(_Tr(_td('Nickname'), _td(textfield('nick',$this->{nick},9))),
  247. _Tr(_td('Alias(ses)'), _td(textfield('alias',$this->{alias},30))),
  248. _Tr(_td('Link'), _td(textfield('link',$this->{link},30))),
  249. _Tr(_td('Sex'), _td(radio_group('sex',['f','m','b','-'],
  250. $this->{sex} || '-','',
  251. { f => 'female',
  252. m => 'male',
  253. b => 'bot',
  254. '-' => 'unspecified' }))),
  255. _Tr(_td('Picture'), _td(textfield('pic',$this->{pic},30))),
  256. _Tr(_td('Big picture'), _td(textfield('bigpic',$this->{bigpic},30))));
  257. print _submit('submit', 'Save data set');
  258. print _reset('reset', 'Reset form');
  259. print _end_form();
  260. if (defined $data{lc($nick)}) {
  261. print _start_form('GET', $script_uri);
  262. print _hidden( -name => 'op', -value => 'del', -override => 1);
  263. print _hidden('nick', $nick);
  264. print _submit('submit', "Remove data for '$nick'");
  265. print _end_form();
  266. }
  267. }
  268. sub _get_op($$) {
  269. my $op = shift;
  270. my $nick = shift;
  271. return a({ href => "$script_uri?op=$op&nick=".escapeHTML($nick) }, $op);
  272. }
  273. sub show_nicks {
  274. read_config();
  275. print start_table;
  276. my $i=1;
  277. my $alias_disp = $config->get('cgi_alias_disp');
  278. foreach my $nick (sort keys %data) {
  279. my $alias = $data{$nick}{alias} || '';
  280. $nick = $data{$nick}{nick};
  281. if (length($alias) > $alias_disp) {
  282. $alias = substr($alias, 1, $alias_disp) . '...';
  283. }
  284. print _Tr(
  285. _td({id => 'num'}, $i),
  286. _td(&_get_op('show', $nick)),
  287. _td(&_get_op('edit', $nick)),
  288. ($config->get('cgi_user_del') ? _td(&_get_op('del', $nick)) : '' ),
  289. _td(escapeHTML($nick.($alias ? " ($alias)" : ''))),
  290. );
  291. $i++;
  292. }
  293. print end_table;
  294. }
  295. sub del_nick {
  296. die "No nick given" unless param('nick');
  297. if (param('confirm')) {
  298. &read_config;
  299. die "No such nick '".param('nick')."'."
  300. unless defined $data{lc(param('nick'))};
  301. delete $data{lc(param('nick'))};
  302. &write_config;
  303. print _p('Data successfully updated.');
  304. &show_nicks;
  305. } else {
  306. print _p("Are you sure you want to delete the user '".
  307. param('nick')."'?");
  308. print _p(a({href => "$script_uri?op=del&confirm=1&nick=".
  309. escapeHTML(param('nick'))}, 'Yes'),
  310. a({href => ($ENV{HTTP_REFERER} ? $ENV{HTTP_REFERER} :
  311. "$script_uri?op=edit&nick=".
  312. escapeHTML(param('nick')))}, 'No'));
  313. }
  314. }