chiark / gitweb /
61f2b13817dbeb1275c21b5551711982d10b5817
[modbot-ulm.git] / webstump / scripts / html_output.pl
1 #
2 # This is a module with functions for HTML output.
3 #
4 # I separate it from the main STUMP stuff because these functions are
5 # bulky and not very interesting.
6 #
7 #
8
9 use POSIX;
10 use CGI qw/escapeHTML/;
11
12 sub begin_html {
13   my $title = pop( @_ );
14   print 
15 "Content-Type: text/html\n\n
16 <TITLE>$title</TITLE>
17 <BODY>
18 <H1>$title</H1>\n\n";
19
20   if( &is_demo_mode ) {
21     print "<B> You are operating in demonstration mode. User actions will have no effect.</B><HR>\n";
22   }
23   
24 }
25
26 sub end_html {
27   print "\n<HR>Thank you for using <A HREF=$STUMP_URL>STUMP Robomoderator</A>.
28 <!-- <BR>
29 Click <A HREF=$base_address>here</A> to return to WebSTUMP. -->
30 ";
31 }
32
33 # prints a link to help
34 # accepts topic id and topic name.
35 #
36 sub link_to_help {
37   my $topic_name = pop( @_ );
38   my $topic = pop( @_ );
39
40   #&print_image( "help.gif", "" );
41
42   print "<A HREF=$base_address?action=help&topic=$topic TARGET=new>Click here for help on $topic_name</A>\n";
43 }
44
45 #
46 # prints image and an alt text
47 #
48 sub print_image { # image_file, alt_text
49   my $alt = pop( @_ );
50   my $file = pop( @_ );
51
52   print "<IMG SRC=$base_address_for_files/images/$file ALT=\"$alt\" ALIGN=BOTTOMP>\n";
53 }
54
55 # prints the welcome page and login screen.
56 sub html_welcome_page {
57   &begin_html( "Welcome to WebSTUMP" );
58
59   print 
60
61 "Welcome to WebSTUMP, the moderators' front end for <A
62 HREF=http://www.algebra.com/~ichudov/stump>STUMP</A> users -- USENET newsgroup
63 moderators. Only authorized users are allowed to log into this
64 program.
65
66 <HR>";
67
68   my $motd_file = "$webstump_home/config/motd";
69
70   if( -f $motd_file && -r $motd_file ){
71     open( MOTD, $motd_file );
72     print "<B>Message of the Day:</B><BR><PRE>\n";
73     print while( <MOTD> );
74     close( MOTD );
75     print "</PRE><HR>\n";
76   }
77
78   print "
79 Newsgroups Status:<BR>
80 <TABLE BORDER=3>\n";
81
82   for( sort @newsgroups_array ) {
83     print "<TR><TD>";
84     
85     my $count = &get_article_count( $_ );
86
87     print " <A HREF=$base_address?action=login_screen\&newsgroup=$_>$_</A>";
88     &print_image( "smiley.gif", "" ) if $count;
89     print "</TD>";
90
91
92     print "<TD>$count messages in queue<BR></TD>";
93 #    print "<TD><A HREF=$base_address?action=init_request_newsgroup_creation\&newsgroup=$_>Request creation</A></TD>\n";
94   }
95
96   print "</TABLE>\n";
97   print "<HR>Note: click on the newsgroup to login in as moderator. 
98 <!-- Click on 'Request Creation' to ask a sysadmin at a specific domain
99 to carry your newsgroup. -->\n<HR>
100 <A HREF=$base_address?action=admin_login>Click here to administer this WebSTUMP installation</A>
101 ";
102   &end_html;
103 }
104
105 # prints the login screen for newsgroup.
106 sub html_login_screen {
107   my $newsgroup = $request{'newsgroup'} || &error( "newsgroup not defined" );
108
109   my $count = &get_article_count( $newsgroup );
110
111
112   if( $count ) {
113     &begin_html( "$count articles in queue for $newsgroup" );
114   } else {
115     &begin_html( "Empty Queue for $newsgroup" );
116   }
117
118   print
119 " Welcome to the Moderation  Center for  $newsgroup. Please bookmark
120 this page. <HR>";
121
122
123   my $color = "", $end_color = "";
124
125   if( $count ) {
126     $color = "<font color=red>";
127     $end_color = "<font color=black>";
128   }
129
130   print 
131 "<FORM METHOD=$request_method action=$base_address>
132  <INPUT NAME=action VALUE=moderation_screen TYPE=hidden>
133   $color ($count ";
134   
135   &print_image( "new_tiny2.gif", "new" ) if $count;
136
137   print " articles available)<BR> $end_color
138  Login: <INPUT NAME=moderator VALUE=\"\" SIZE=20>
139  <BR>
140  Password: <INPUT NAME=password TYPE=password VALUE=\"\" SIZE=20>
141  <BR>
142  <INPUT TYPE=submit VALUE=\"Proceed with Login\">
143  <INPUT TYPE=reset VALUE=\"Reset\">
144  <INPUT NAME=newsgroup VALUE=\"$newsgroup\" TYPE=hidden>
145  </FORM><HR>
146   Please log into $newsgroup. You can only log in if you know your login id
147   and know the secret password. You should not give your password to any
148   unauthorized user. Your login id and password are NOT case sentitive, 
149   which means that,
150   for example, \"xyzzy\" and \"XyZZY\" are equally valid.<P>
151 ";
152
153 #  print "
154 # Log in as \"admin\" if you want to 
155 #<UL>
156 #  <LI> edit filtering lists.";
157 #
158 #  &link_to_help( "filter-lists", "Filter Lists" );
159 #
160 #  print "
161 #  <LI> add/delete users or change their passwords.
162 #  <LI> First Time Users: You have to log in as admin and add a moderator user
163 #  who will be able to moderate the newsgroup. Then log in again as that
164 #  user. If you are a new user, you have to have your admin password assigned to
165 #  you by the administrator.
166 #</UL>
167 #
168 #";
169   &end_html;
170 }
171
172 # prints the login screen for newsgroup.
173 sub admin_login_screen {
174   &begin_html( "Administrative login" );
175
176   print
177 "
178 Attention: this page is only for the maintainer of the whole WebSTUMP
179 installation. Please return to the main page if you are not the maintainer
180 of this installation. <HR>
181 ";
182
183   print 
184 "<FORM METHOD=$request_method action=$base_address>
185  <INPUT NAME=action VALUE=webstump_admin_screen TYPE=hidden>
186  Password: <INPUT NAME=password TYPE=password VALUE=\"\" SIZE=20>
187  <BR>
188  <INPUT TYPE=submit VALUE=\"Proceed with Login\">
189  <INPUT TYPE=reset VALUE=\"Reset\">
190  </FORM>
191 ";
192
193   &end_html;
194 }
195
196 # main moderation page -- single-article version
197 sub html_moderate_article {
198   my $newsgroup = &required_parameter( 'newsgroup' );
199   my $moderator = $request{'moderator'};
200   my $password = $request{'password'};
201   my $file = shift @_ || &required_parameter('file');
202
203   &begin_html( "Main Moderation Screen: $newsgroup" );
204   print "<HR>\n";
205
206   &read_rejection_reasons;
207
208   my $dir = "$queues_dir/$newsgroup";
209
210   if( -d "$dir/$file" && open( TEXT_FILES, "$dir/$file/text.files.lst" ) ) {
211
212       print "<HR>\n" if &print_article_warning( $file );
213
214       print "<PRE>\n";
215       my $filename;
216       my $inhead= 1;
217       while( $filename = <TEXT_FILES> ) {
218         open( ARTICLE, "$dir/$file/$filename" );
219         while( <ARTICLE> ) {
220           $embolden= m/^(?:from|subject)\s*\:/i;
221           s/\&/&amp;/g;
222           s/</&lt;/g;
223           s/>/&gt;/g;
224           $_= "<strong>$_</strong>" if $embolden;
225           print;
226           $inhead= 0 unless m/\S/;
227         }
228         close( ARTICLE );
229         $inhead= 0;
230       }
231
232       print "\n</PRE>\n\n";
233
234       &print_images( $newsgroup, "$dir/$file", $file);
235
236   } else {
237     print "This message ($dir/$file) no longer exists -- maybe it was " .
238           "approved or rejected by another moderator.";
239   }
240
241       print "<HR>
242 <FORM NAME=decision METHOD=$request_method action=$base_address>
243 ";
244
245   print "
246 <INPUT NAME=action VALUE=approval_decision TYPE=hidden>";
247   &html_print_credentials;
248         print "<INPUT TYPE=radio NAME=\"decision_$file\" VALUE=approve>Approve\n";
249         print "<INPUT TYPE=radio NAME=\"decision_$file\" VALUE=skip>Leave\n";
250         print "<INPUT TYPE=radio NAME=\"decision_$file\" VALUE=leave>Back of queue\n";
251         foreach (@short_rejection_reasons) {
252           print "<INPUT TYPE=radio NAME=\"decision_$file\" VALUE=\"reject $_\">Reject \u$_\n";
253         }
254
255       print "<BR> <BR> Comment (to poster, in rejection message): <INPUT NAME=comment VALUE=\"\" SIZE=80><BR>";
256
257   print "<BR>
258 <INPUT TYPE=radio NAME=poster_decision VALUE=nothing CHECKED>Don't change poster's status</INPUT>
259 <INPUT TYPE=radio NAME=poster_decision VALUE=preapprove 
260 >White-list poster</INPUT>
261 <INPUT TYPE=radio NAME=poster_decision VALUE=suspicious>
262 Add poster to watch list</INPUT>
263
264 <BR><BR>
265 <I>
266 NOTE: Decisions to watchlist and whitelist posters can be reversed by 
267 editing the respective lists of whitelisted and watchlisted posters.
268 ";
269
270   &link_to_help( "filter-lists", "automatic filtering and filter lists, blacklisting and preapproved threads." );
271
272   print "</I><BR><BR>
273
274 <INPUT TYPE=radio NAME=next_screen VALUE=single CHECKED> 
275         Review ONE article in next screen
276 <INPUT TYPE=radio NAME=next_screen VALUE=multiple> 
277         Review multiple articles in next screen
278 <HR>
279
280 <INPUT TYPE=submit VALUE=\"Submit\">
281 <INPUT TYPE=submit NAME=skip_submit VALUE=\"Skip\">
282 <INPUT TYPE=reset VALUE=\"Reset\">
283 ";
284
285       print "</FORM>\n\n";
286   print "<BR><A HREF=$base_address?action=change_password&newsgroup=$newsgroup&" .
287         "moderator=$moderator&password=$password>Change Password</A>";
288
289   closedir( QUEUE );
290   &end_html;
291 }
292
293 # WebSTUMP administrative screen
294 sub webstump_admin_screen {
295
296   &verify_admin_password;
297
298   my $password = $request{'password'};
299
300   &begin_html( "WebSTUMP Administration" );
301   print "
302 <FORM METHOD=$request_method action=$base_address>
303 <INPUT NAME=action VALUE=admin_add_newsgroup TYPE=hidden>
304 <INPUT NAME=password VALUE=\"$password\" TYPE=hidden>\n";
305
306
307   print "
308 <HR>
309 Create a new newsgroup on the server:<BR>
310
311 Newsgroup:<BR> <INPUT NAME=newsgroup_name VALUE=\"\" SIZE=50><BR>
312 Address to send approved/rejected messages <BR>
313         <INPUT NAME=newsgroup_approved_address VALUE=\"\" SIZE=30><BR>
314 Admin Password For this group:<BR> <INPUT NAME=newsgroup_password VALUE=\"\" SIZE=10><BR>
315 <INPUT TYPE=submit VALUE=\"Submit\">
316 <INPUT TYPE=reset VALUE=\"Reset\"><HR>
317 ";
318
319       print "</FORM>\n\n<PRE>\n";
320
321   &end_html;
322 }
323
324 # WebSTUMP "add newsgroup" function
325 sub admin_add_newsgroup {
326
327   &verify_admin_password;
328
329   my $newsgroup = &required_parameter( 'newsgroup_name' );
330
331   $newsgroup =~ s/\///g;
332   $newsgroup = &untaint( $newsgroup );
333
334   my $address = &required_parameter( 'newsgroup_approved_address' );
335   my $password = &required_parameter( 'newsgroup_password' );
336
337   &user_error( "Newsgroup $newsgroup already exists" )
338     if defined $newsgroups_index{$newsgroup};
339
340   &user_error( "Password may only contain letters and digits" )
341     if( ! ($password =~ /^[a-zA-Z0-9]+$/ ) );
342
343   &begin_html( "WebSTUMP Administration: Newsgroup created" );
344
345   print "<PRE>\n\n";
346
347   print "Adding $newsgroup to $webstump_home/config/newsgroups.lst...";
348   mkdir "$webstump_home/queues/$newsgroup", 0755;
349   print " done.\n";
350   
351   $dir = "$webstump_home/config/newsgroups/$newsgroup";
352   
353   print "Creating $dir...";
354   mkdir $dir, 0755;
355   print " done.\n";
356   
357   print "Creating files in $dir...";
358   
359   &append_to_file( "$dir/address.txt", "$address\n" );
360   &append_to_file( "$dir/moderators", "ADMIN \U$password\n" );
361   &append_to_file( "$dir/rejection-reasons",
362 "offtopic::a blatantly offtopic article, spam
363 harassing::message of harassing content
364 charter::message poorly formatted
365 " );
366   print " done.\n";
367
368
369   print "</PRE>\n";
370
371   &end_html;
372 }
373
374 #
375 #
376 sub print_images {
377   $web_subdir = pop( @_ );
378   $subdir = pop( @_ );
379   $newsgroup = pop( @_ );
380
381   opendir( SUBDIR, $subdir );
382
383   my $count = 0;
384
385   while( $_ = readdir( SUBDIR ) ) {
386     my $file = "$subdir/$_";
387     next if( ! -f $file || ! -r $file );
388     my $extension = $file;
389     $extension =~ s/^.*\.//;
390     $extension = "\L$extension";
391     
392     if( $extension eq "gif" || $extension eq "jpg" || $extension eq "jpeg" ) {
393       print "<CENTER> <IMG SRC=$base_address_for_files/queues/$newsgroup/$web_subdir/$_></CENTER><HR>\n";
394       $count++;
395     } else {
396       my $filename = $_;
397       $filename =~ s/^.*\///;
398       next if $filename eq "skeleton.skeleton" 
399               || $filename eq "headers.txt"
400               || $filename eq "full_message.txt"
401               || $filename eq "text.files.lst"
402               || $filename eq "stump-prolog.txt"
403               || $filename eq "stump-warning.txt"
404               || $filename =~ /msg-.*\.doc/;
405       
406       &print_image( "no_image.gif", "security warning" );
407       print "<B>Non-image attachment:</B><CODE>$filename</CODE> NOT SHOWN for security reasons.<BR>\n";
408     }
409   }
410   return $count;
411 }
412
413 # prints warning if there is warning stored about the article
414 sub print_article_warning { # short-subdir
415   my $file = pop( @_ );
416
417   my $warning_file = &article_file_name( $file ) . "/stump-warning.txt";
418
419   if( -r $warning_file ) {
420     open( WARNING, $warning_file );
421     while ($warning = <WARNING>) {
422         next unless $warning =~ m/\S/;
423         $warning =~ s/\&/&amp;/g;
424         $warning =~ s/</&lt;/g;
425         $warning =~ s/>/&gt;/g;
426         &print_image( "star.gif", "warning" );
427         print "<FONT COLOR=red>$warning</FONT><br>\n";
428     }
429     close( WARNING );
430     return 1;
431   }
432
433   return 0;
434 }
435
436 sub get_queue_list ($) {
437     my ($newsgroup) = @_;
438     my $dir = "$queues_dir/$newsgroup";
439     my %sortkeys;
440
441     opendir(QUEUED, $dir) or &error("could not open directory $dir");
442
443     for (;;) {
444         $!=0;
445         my $subdir= scalar readdir(QUEUED);
446         last unless defined $subdir;
447
448         my $subpath= "$dir/$subdir";
449         next if $subdir =~ /^\.+/;
450         next unless -d $subpath;
451         my $sortkey;
452         if (!stat "$subpath/stump-warning.txt") {
453             $!==&ENOENT or die "$subpath $!";
454             $sortkey= 0;
455         } else {
456             $sortkey= (stat _)[9];
457         }
458         $sortkeys{$subdir}= $sortkey;
459     }
460     closedir( QUEUED );
461     my @articles= sort { $sortkeys{$a} <=> $sortkeys{$b} } keys %sortkeys;
462     return ($dir, @articles);
463 }
464
465 # main moderation page -- multiple-articles version
466 sub html_moderation_screen {
467   my $newsgroup = &required_parameter( 'newsgroup' );
468   my $moderator = $request{'moderator'};
469   my $password = $request{'password'};
470
471
472   if( $request{'next_screen'} eq 'single' ) {
473     # we show a single article if the user so requested.
474     # just get the first article from the queue if any, otherwise show 
475     # an empty main screen.
476    
477     my ($dir, @articles)= get_queue_list($newsgroup);
478
479     my $i;
480     for ($i=0; $i<@articles; $i++) {
481         my $subdir= shift @articles;
482         push @articles, $subdir;
483         last if $request{"decision_$subdir"};
484     }
485
486     while( $subdir = shift @articles ) {
487       if( -d "$dir/$subdir" && !($subdir =~ /^\.+/) 
488           && open( PROLOG, "$dir/$subdir/stump-prolog.txt" ) ) {
489               &html_moderate_article( $subdir );
490               return;
491       }
492     }
493   } else {
494         # otherwise just show the moderator an empty main screen.
495   }
496     
497   &begin_html( "Main Moderation Screen: $newsgroup" );
498   print "Welcome to the main moderation screen. Its main purpose is to 
499 help you process most messages extremely quickly. For every message, it 
500 presents you who sent it, as well as the first three non-blank lines.
501 For those messages where the decision is obvious, simply select your
502 decision (approve/reject etc) and click submit. For those messages which
503 you would like to review in more details, do not select anything and
504 use Review/Comment function from this screen or from a subsequent screen.
505 Remember that if you do not make any decision, the article would stay in the
506 queue.\n";
507
508   &read_rejection_reasons;
509
510   my ($dir, @articles)= get_queue_list($newsgroup);
511
512   print "
513   <FORM METHOD=$request_method action=$base_address>
514   <INPUT NAME=action VALUE=approval_decision TYPE=hidden>";
515     &html_print_credentials;
516
517     print "<HR> <INPUT TYPE=submit VALUE=Submit>
518 <INPUT TYPE=reset VALUE=Reset>
519 ";
520   
521   my $file, $subject = "No Subject", $from = "From nobody";
522   my $form_not_empty = "";
523   my $article_count = 0;
524   my $warning = "";
525   while( ($subdir = shift @articles) && $article_count++ < 40 ) {
526     $file=$subdir;
527     if( -d "$dir/$subdir" && !($subdir =~ /^\.+/) 
528         && open( PROLOG, "$dir/$subdir/stump-prolog.txt" ) ) {
529         while( <PROLOG> ) {
530           chop;
531           if( /^Real-Subject: /i ) {
532             s/\&/&amp;/g;
533             s/</&lt;/g;
534             s/>/&gt;/g;
535             s/^Real-Subject: //g;
536             $subject = substr( $_, 0, 50 );
537           } elsif( /^From: /i ){
538             s/\&/&amp;/g;
539             s/</&lt;/g;
540             s/>/&gt;/g;
541             $from = substr( $_, 0, 50 );
542           } elsif( /^$/ ) {
543             last;
544           }
545         }
546
547         print "<HR><B>$from: $subject</B>(";
548         print "<A HREF=$base_address?action=moderate_article&newsgroup=$newsgroup&" .
549               "moderator=$moderator&password=$password&file=$subdir>Review/Comment/Whitelist</A>)<BR>\n";
550         print "<INPUT TYPE=radio NAME=\"decision_$file\" VALUE=approve>Approve\n";
551         print "<INPUT TYPE=radio NAME=\"decision_$file\" VALUE=skip>Leave\n";
552         print "<INPUT TYPE=radio NAME=\"decision_$file\" VALUE=leave>Back of queue\n";
553         foreach (@short_rejection_reasons) {
554           print "<INPUT TYPE=radio NAME=\"decision_$file\" VALUE=\"reject $_\">Reject \u$_\n";
555         }
556
557         print "<BR>\n";
558
559         &print_article_warning( $file );
560
561         print "<PRE>\n";
562
563         my $i = 0;
564
565         while( ($_ = <PROLOG>) && $i < 5 ) {
566             chop;
567             next if m/^\>/;
568             s/\&/&amp;/g;
569             s/</&lt;/g;
570             s/>/&gt;/g;
571             if( $_ ne "" ) {
572               print "]  " . substr( $_, 0, 75 ) . "\n";
573               $i++;
574             }
575         }
576
577         print "</PRE>";
578         $form_not_empty = "yes";
579         close( PROLOG );
580         $article_count += &print_images( $newsgroup, "$dir/$subdir", $subdir );
581     }
582   }
583
584   if( $form_not_empty ) {
585     print "<HR> <INPUT TYPE=submit VALUE=Submit>
586 <INPUT TYPE=reset VALUE=Reset>
587 ";
588   } else {
589     print "
590 <HR>
591 No articles present in the queue
592 <INPUT TYPE=submit VALUE=Refresh>
593 <HR>\n";
594   }
595
596   print "<A HREF=$base_address?action=change_password&newsgroup=$newsgroup&" .
597         "moderator=$moderator&password=$password>Change Password</A>";
598
599   print "</FORM>\n\n";
600
601   print "<FORM METHOD=$request_method action=$base_address>";
602   &html_print_credentials;
603   print "<INPUT NAME=action VALUE=moderator_admin TYPE=hidden>
604          <INPUT TYPE=submit VALUE=\"Management\">
605          </FORM>";
606
607   &end_html;
608 }
609
610 # prints hidden fields -- credentials
611 sub html_print_credentials {
612   my $newsgroup = $request{'newsgroup'};
613   my $moderator = $request{'moderator'};
614   my $password = $request{'password'};
615
616   print "
617  <INPUT NAME=newsgroup VALUE=\"$newsgroup\" TYPE=hidden>
618  <INPUT NAME=moderator VALUE=\"$moderator\" TYPE=hidden>
619  <INPUT NAME=password VALUE=\"$password\" TYPE=hidden>\n";
620 }
621
622 # logs
623
624 sub scanlogs ($$$) {
625     my ($forwards, $gotr, $callback) = @_;
626     my $dir= "$webstump_home/..";
627     opendir LOGSDIR, "$dir" or die "$dir $!";
628     my $num= sub {
629         local ($_) = @_;
630         return $forwards * (
631             m/^errs$/ ? -1 :
632             m/^errs\.(\d+)(?:\.gz$)$/ ? $1 :
633             undef
634                            );
635     };
636     foreach my $leaf (
637                       sort { $num->($a) <=> $num->($b) }
638                       grep { defined $num->($_) }
639                       readdir LOGSDIR
640                       ) {
641         my $file= "$dir/$leaf";
642         if ($file =~ m/\.gz$/) {
643             open LOGFILE, "zcat $file |" or die "zcat $file $!";
644         } else {
645             open LOGFILE, "< $file" or die "$file $!";
646         }
647         while (<LOGFILE>) {
648             my $tgot= $callback->();
649             next unless $tgot;
650             $$gotr= $tgot if $tgot > $$gotr;
651             last if $tgot > 1;
652         }
653         $!=0; $?=0; close LOGFILE or die "$file $? $!";
654         last if $$gotr > 1;
655     }
656     closedir LOGSDIR or die "$dir $!";
657 }        
658
659 sub html_search_logs {
660   &begin_html("Search logs for $request{'newsgroup'}");
661   my $reqnum;
662   my $forwards=1;
663   my $min= 9;
664   if ($request{'download_logs'}) {
665       print "<h2>Complete log download</h2>\n";
666       $min= 2;
667   } elsif ($request{'messagenum'} =~ m/^\s*(\d+)\s*$/) {
668       $reqnum= $1;
669       $forwards= -1;
670       $min= 1;
671       print "<h2>Log entry for single message $reqnum</h2>\n";
672   } else {
673       print "<h2>Log lookup - bad reference</h2>
674 Please supply the numerical reference as found in the \"recent activity\"
675 log or message headers.  Reference numbers consist entirely of digits,
676 and are often quoted in message headers in [square brackets].<p>
677         ";
678       &end_html;
679       return;
680   }
681   if ($mod_log_access < $min) {
682       print "Not permitted [$mod_log_access<$min].  Consult administrator.\n";
683       &end_html;
684       return;
685   }
686
687   my $sofar= 0;
688   &scanlogs($forwards, \$sofar, sub {
689       return 0 unless chomp;
690       return 0 unless m/^DECISION: /;
691       my @vals = split / \| /, $';
692       return 0 unless @vals >= 5;
693       my $subj= pop @vals;
694       my ($group,$dir,$act,$reason,$timet) = @vals;
695       my $date= $timet ? (strftime "%Y-%m-%d %H:%M:%S GMT", gmtime $timet)
696           : "(unknown)";
697       return 0 unless $group eq $request{'newsgroup'};
698       return 0 unless $subj =~ m,/(\d+)$,;
699       my $treqnum= $1;
700       return 0 if defined($reqnum) and $treqnum ne $reqnum;
701       print "<table rules=all><tr><th>Date<th>Reference<th>Disposal<th>Reason</tr>\n"
702           unless $sofar;
703       print "<tr>", (map { "<td>".escapeHTML($_) }
704                      $date,$treqnum,$act,$reason);
705       print "</tr>\n";
706       return defined($reqnum) ? 2 : 1;
707   });
708   if ($sofar) {
709       print "</table>" if $sofar;
710       print "\n";
711   } else {
712       print "Reference not found.".
713           "  (Perhaps message has expired, or is still in the queue?)";
714   }
715   &end_html;
716 }
717
718 # newsgroup admin page
719 sub html_newsgroup_management {
720   &begin_html( "Administer $request{'newsgroup'}" );
721
722   print "All usernames and passwords are not case sensitive.\n";
723   print "<HR>Use this form to add new moderators or change passwords:<BR>
724  <FORM METHOD=$request_method action=$base_address>
725  <INPUT NAME=action VALUE=add_user TYPE=hidden>";
726   &html_print_credentials;
727   print "
728  Username: <INPUT NAME=user VALUE=\"\" SIZE=20>
729  <BR>
730  Password: <INPUT NAME=new_password VALUE=\"\" SIZE=20>
731  <BR>
732  <INPUT TYPE=submit VALUE=\"Add/Change\">
733  <INPUT TYPE=reset VALUE=Reset>
734  </FORM>
735 ";
736
737   print "<HR>Use this form to delete moderators:<BR>
738  <FORM METHOD=$request_method action=$base_address>
739  <INPUT NAME=action VALUE=delete_user TYPE=hidden>";
740   &html_print_credentials;
741   print "
742  Username: <INPUT NAME=user VALUE=\"\" SIZE=20>
743  <BR>
744  <INPUT TYPE=submit VALUE=\"Delete Moderator\">
745  <INPUT TYPE=reset VALUE=Reset>
746  </FORM><HR>
747
748  <FORM METHOD=$request_method action=$base_address>
749  <INPUT NAME=action VALUE=edit_list TYPE=hidden>";
750   &html_print_credentials;
751   print "
752   Configuration List: <SELECT NAME=list_to_edit>
753
754     <OPTION VALUE=good.posters.list>Good Posters List
755     <OPTION VALUE=watch.posters.list>Suspicious Posters List
756     <OPTION VALUE=watch.words.list>Suspicious Words List
757
758   </SELECT>
759   <INPUT TYPE=submit VALUE=\"Edit\">
760   <INPUT TYPE=reset VALUE=Reset>";
761
762   &link_to_help( "filter-lists", "filtering lists" );
763
764   print "</FORM><HR>";
765
766   if ($mod_log_access) {
767       print "<form>
768         Use this form to search logs of past moderation decisions:
769         <br>
770         <form method=$request_method action=$base_address>
771         <input name=action value=search_logs type=hidden>";
772
773       &html_print_credentials;
774
775       print "
776         Reference number: <input name=messagenum size=30>
777         <input type=submit value=\"Lookup\">";
778
779       print "
780         <input type=submit value=\"Download all logs\" name=\"download_logs\">"
781         if $mod_log_access >= 2;
782
783       print "</form><hr>\n";
784   }
785
786   print "
787
788   List of current moderators:<P>
789
790   <UL>\n";
791
792   foreach (keys %moderators) {
793       print "<LI> $_\n";
794   }
795
796   print "</UL>\n";
797
798   print "<HR><FORM METHOD=$request_method action=$base_address>";
799   &html_print_credentials;
800   print "<INPUT NAME=action VALUE=moderation_screen TYPE=hidden>
801          <INPUT TYPE=submit VALUE=\"Go to moderation screen\">
802          </FORM>";
803
804   &end_html;
805 }
806
807
808 # edit config list
809 sub edit_configuration_list {
810
811   my $list_to_edit = &required_parameter( 'list_to_edit' );
812
813   $list_to_edit = &check_config_list( $list_to_edit );
814
815   my $list_file = &full_config_file_name( $list_to_edit );
816
817   my $list_content = "";
818
819   if( open( LIST, $list_file ) ) {
820     $list_content .= $_ while( <LIST> );
821     close( LIST );
822   }
823
824   $list_content =~ s/\&/&amp;/g;
825   $list_content =~ s/</&lt;/g;
826   $list_content =~ s/>/&gt/g;
827
828   &begin_html( "Edit $list_to_edit" );
829
830   print
831 " <FORM METHOD=$request_method action=$base_address>
832  <INPUT NAME=action VALUE=set_config_list TYPE=hidden>
833  <INPUT NAME=list_to_edit VALUE=$list_to_edit TYPE=hidden>";
834   &html_print_credentials;
835   &link_to_help( $list_to_edit, "$list_to_edit" );
836   print "
837  Edit this list: <HR>
838 <TEXTAREA NAME=list rows=20 COLS=50>
839 $list_content</TEXTAREA>
840
841  <BR>
842  <INPUT TYPE=submit VALUE=\"Set\">
843  </FORM>
844 ";
845
846   &end_html;
847 }
848
849 # password change page
850 sub html_change_password{
851   &begin_html( "Change Password" );
852
853   print "All usernames and passwords are not case sensitive.\n";
854   print "<HR>Use this form to change your password:<BR>
855  <FORM METHOD=$request_method action=$base_address>
856  <INPUT NAME=action VALUE=validate_change_password TYPE=hidden>";
857   &html_print_credentials;
858   print "
859  <BR>
860  New Password: <INPUT NAME=new_password VALUE=\"\" SIZE=20>
861  <BR>
862  <INPUT TYPE=submit VALUE=Submit>
863  <INPUT TYPE=reset VALUE=Reset>
864  </FORM>
865 ";
866
867   &end_html;
868 }
869
870
871 # newsgroup creation form
872 sub init_request_newsgroup_creation{
873   my $newsgroup = &required_parameter( 'newsgroup' );
874
875   &begin_html( "Request Creation of $newsgroup" );
876
877   print "This page helps you ask the system administrator of your domain
878 to create <B>$newsgroup</B> on your server. Type in your domain name and
879 click SUBMIT. An email will be sent to news\@domain and usenet\@domain
880 and postmaster\@domain
881 asking them to create your newsgroup. Please do NOT abuse this system.
882 NOTE: You can give the URL of this page to your group readers so that 
883 they could request creation of their newsgroups by themselves.\n";
884
885   print "<HR>
886  <FORM METHOD=$request_method action=$base_address>
887  <INPUT NAME=action VALUE=complete_newsgroup_creation_request TYPE=hidden>\n";
888   &html_print_credentials;
889   print "
890  <BR>
891  Domain Name ONLY: <INPUT NAME=domain_name VALUE=\"\" SIZE=40>
892  <BR>
893  <INPUT TYPE=submit VALUE=Submit>
894  <INPUT TYPE=reset VALUE=Reset>
895  </FORM>
896 ";
897
898   &end_html;
899 }
900
901
902 # newsgroup creation completion
903 sub complete_newsgroup_creation_request{
904   my $newsgroup = &required_parameter( 'newsgroup' );
905   my $domain_name = &required_parameter( 'domain_name' );
906
907   if( !($domain_name =~ /(^[a-zA-Z0-9\.-_]+$)/) ) {
908     &user_error( "invalid domain name" );
909   }
910
911   $domain_name = $1;
912
913
914   my $request = "To: news\@$domain_name, usenet\@$domain_name, postmaster\@$domain_name
915 Subject: Please create $newsgroup (Moderated)
916 From: devnull\@algebra.com ($newsgroup Moderator)
917 Organization: stump.algebra.com
918
919 Dear News Administrator:
920
921 A user of $domain_name has requested that you create a newsgroup
922
923         $newsgroup (Moderated) 
924
925 on your server. $newsgroup
926 is a legitimately created moderated newsgroup that is available worldwide.
927
928 Thank you very much for your help and cooperation.
929
930 Sincerely,
931
932         - Moderator of $newsgroup.
933
934 ";
935
936   &email_message( $request, "news\@$domain_name" );
937   &email_message( $request, "usenet\@$domain_name" );
938   &email_message( $request, "postmaster\@$domain_name" );
939
940   &begin_html( "Request to create $newsgroup sent" );
941
942   print "The following request has been sent:<HR><PRE>\n";
943
944   print "$request</PRE>\n";
945
946   &end_html;
947 }
948
949 # displays help
950 sub display_help {
951   my $topic_name = &required_parameter( "topic" );
952
953   $topic_name =~ s/\///g;
954   $topic_name =~ s/\.\.//g;
955   $topic_name = &untaint( $topic_name );
956
957   my $file = "$webstump_home/doc/help/$topic_name.html";
958
959   &error( "Topic $topic_name not found in $file." ) 
960         if ! -r $file;
961
962   open( FILE, "$file" );
963   my $help = "";
964   $help .= $_ while( <FILE> );
965   close( FILE );
966
967   $help =~ s/##/$base_address?action=help&topic=/g;
968
969   &begin_html( "$topic_name" );
970
971   print $help;
972
973   print "<HR>";
974 }
975
976
977
978
979
980
981
982 1;