#!/usr/local/bin/perl
#
# ruboard admin tool (rubadmin) 1.2.1
# $Id: rubadmin.PL,v 1.26 1998/10/30 18:49:06 am Exp $
#
###############################################################################
#
# Common subroutines
#
require "/usr/local/libexec/ruboard/rublib.pl";

# Restrictive default umask
#
umask(077);

# Parsing form or input string and reading configuration
#
&parse_form;
&read_config;
$command=$FORM{"c"};
$action=$FORM{"action"};
$printing_to_file=0;

###############################################################################
# Do we have password file?
#
if(! -s $passwd_file && $action ne "change_passwd")
{ &print_html_head("Asking Admin Password","Initializing administrator password");
  print <<EOH;
<CENTER>
Fill out the form below completely in order to use administration tool.
<P>
EOH
  &print_html_hr;
  print <<EOH;
<FORM METHOD=post ACTION="$admin_url">
<INPUT TYPE=HIDDEN NAME="config" VALUE="$config">
<INPUT TYPE=HIDDEN NAME="action" VALUE="change_passwd">
<TABLE BORDER=0>
<TR>
<TH ALIGN=LEFT>Username:</TH> <TD><INPUT TYPE=TEXT NAME="new_username"><BR></TD></TR>
<TR>
<TH ALIGN=LEFT>Password:</TH> <TD><INPUT TYPE=PASSWORD NAME="passwd_1"><BR></TD></TR>
<TR>
<TH ALIGN=LEFT>Re-type password:</TH> <TD><INPUT TYPE=PASSWORD NAME="passwd_2"><BR></TD></TR>
<TR>
<TD ALIGN=CENTER><INPUT TYPE=SUBMIT VALUE="Set Password"></TD>
<TD ALIGN=CENTER><INPUT TYPE=RESET></TD>
</TR></TABLE></CENTER>
</FORM>
EOH
  &print_html_footer;
}

###########################################################################
# Removing articles (listed in threads order), the form.
#
elsif ($command eq 'remove')
{ &read_mesgfile;
  &print_html_head("Removing Messages","Removing messages from `$title'");
  &show_buttons("no_top");
  &show_table_head("remove","remove");
  foreach (@ENTRIES)
   { $presubj=$postsubj="";
     if($TLA{$_})
      { $presubj="<B>";
        $postsubj="</B>";
      }
     print <<EOH;
<TR>
<TD ALIGN=RIGHT>&nbsp;$_&nbsp;</TD>
<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME="$_"></TD>
<TD ALIGN=RIGHT>&nbsp;$THREADSIZE{$_}&nbsp;</TD>
<TD>&nbsp;$presubj<A HREF="$baseurl/$mesgdir/$_.$ext">$SUBJECT{$_}</A>$postsubj&nbsp;</TD>
<TD>&nbsp;$AUTHOR{$_}&nbsp;</TD>
<TD ALIGN=RIGHT>&nbsp;$DATE{$_}&nbsp;<BR></TD>
</TR>
EOH
   }
  &show_table_foot;
}

###########################################################################
# Remove By Number                                                        #
#       This method is useful to see in what order the messages were      #
#   added to the wwwboard.html document.                                  #
###########################################################################

elsif ($command eq 'remove_by_num')
{ &read_mesgfile;
  &print_html_head("Removing messages by number","Removing messages from `$title' by number");
  &show_buttons("no_top");
  &show_table_head("remove","remove");
  foreach (@SORTED_ENTRIES)
   { $presubj=$postsubj="";
     if($TLA{$_})
      { $presubj="<B>";
        $postsubj="</B>";
      }
     print <<EOH;
<TR>
<TD ALIGN=RIGHT>&nbsp;$_&nbsp;</TH>
<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME="$_"></TD>
<TD ALIGN=RIGHT>&nbsp;$THREADSIZE{$_}&nbsp;</TD>
<TD>&nbsp;$presubj<A HREF="$baseurl/$mesgdir/$_.$ext">$SUBJECT{$_}</A>$postsubj&nbsp;</TD>
<TD>&nbsp;$AUTHOR{$_}&nbsp;</TD>
<TD ALIGN=RIGHT>&nbsp;$DATE{$_}&nbsp;<BR></TD>
</TR>
EOH
   }
  &show_table_foot;
}

###############################################################################
# Removing articles by date, the form.
#
elsif ($command eq 'remove_by_date')
{ &read_mesgfile;
  &print_html_head("Removing messages by date","Removing messages from `$title' by date");
  &show_buttons("no_top");
  print <<EOH;
Checking the checkbox beside the date will remove
all postings which occured on that date AND all followups to them.
<B>Be careful!</B>
EOH
  &show_table_head("remove_by_lists","remove_by_date",5);
  print <<EOH;
<TR><TH>x</TH> <TH>Date</TH> <TH>Messages</TH> <TH>Followups</TH> <TH>Message Numbers<BR></TH></TR>
EOH

# Removing time part
#
  foreach $id (keys %DATE)
   { ($time,$day) = split(/\s+/,$DATE{$id});
     $DATE{$id} = $day ? $day : $time;
   }
  undef %uval;
  foreach $key (keys %DATE)
   { $uval{$DATE{$key}}.="$key,";
   }
  undef %DATE; undef %AUTHOR;
  $maxid=0;
  foreach $date (sort date_sort keys %uval)
   { $klist=$uval{$date};
     chop $klist;
     print "<TR><TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=\"$maxid\" VALUE=\"$klist\"></TD>\n";
     $maxid++;
     print "<TD ALIGN=RIGHT>&nbsp;$date&nbsp;</TD>\n";
     @keys=split(/,/,$klist);
     print "<TD ALIGN=RIGHT>&nbsp;" . scalar(@keys) . "&nbsp;</TD>\n";
     $thread=0;
     foreach $key (@keys)
      { $thread+=$THREADSIZE{$key};
      }
     $thread="-" unless $thread;
     print "<TD ALIGN=RIGHT>&nbsp;$thread&nbsp;</TD>\n";
     print "<TD>&nbsp;";
     foreach $key (sort @keys)
      { print "<A HREF=\"$baseurl/$mesgdir/$key\.$ext\">$key</A>\n";
      }
     print "</TD></TR>\n";
   }
  print <<EOH;
</TABLE><P><CENTER>
<INPUT TYPE=HIDDEN NAME="maxid" VALUE="$maxid">
<INPUT TYPE=SUBMIT VALUE="Remove Messages"> <INPUT TYPE=RESET>
</FORM></CENTER>
EOH
  &print_html_footer;
}

###############################################################################
# Removing articles by Author, the form.
#
elsif ($command eq 'remove_by_author')
{ &read_mesgfile;
  &print_html_head("Removing messages by author","Removing messages from `$title' by author");
  &show_buttons("no_top");
  print <<EOH;
Checking the checkbox beside the name of an author will remove
all postings which that author has created AND all followups to his/her posts.
<B>Be careful!</B>
EOH
  &show_table_head("remove_by_lists","remove_by_author",5);
  print "<TR><TH>x</TH> <TH>Author</TH> <TH>Messages</TH>\n"
      . "<TH>Followups</TH> <TH>Message #'s<BR></TH></TR>\n";

  undef %uval;
  foreach $key (keys %AUTHOR)
   { $uval{$AUTHOR{$key}}.="$key,";
   }
  undef %DATE; undef %AUTHOR;
  $maxid=0;
  foreach $author (sort keys %uval)
   { $klist=$uval{$author};
     chop $klist;
     print "<TR><TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=\"$maxid\" VALUE=\"$klist\"></TD>\n";
     $maxid++;
     print "<TD>&nbsp;$author&nbsp;</TD>\n";
     @keys=split(/,/,$klist);
     print "<TD ALIGN=RIGHT>&nbsp;" . scalar(@keys) . "&nbsp;</TD>\n";
     $thread=0;
     foreach $key (@keys)
      { $thread+=$THREADSIZE{$key};
      }
     $thread="-" unless $thread;
     print "<TD ALIGN=RIGHT>&nbsp;$thread&nbsp;</TD>\n";
     print "<TD>&nbsp;";
     foreach $key (sort @keys)
      { print "<A HREF=\"$baseurl/$mesgdir/$key\.$ext\">$key</A>\n";
      }
     print "</TD></TR>\n";
   }
  print <<EOH;
</TABLE><P><CENTER>
<INPUT TYPE=HIDDEN NAME="maxid" VALUE="$maxid">
<INPUT TYPE=SUBMIT VALUE="Remove Messages"> <INPUT TYPE=RESET>
</FORM></CENTER>
EOH
  &print_html_footer;
}

###############################################################################
# Changing password, the form.
#
elsif ($command eq 'change_passwd')
{ &print_html_head("Change Admin Password","Changing administrator password");
  print <<EOH;
Fill out the form below completely to change your password and user name.
If new username is left blank, your old one will be assumed.
<P>
EOH
  &show_buttons;
  print <<EOH;
<FORM METHOD=post ACTION="$admin_url">
<INPUT TYPE=HIDDEN NAME="config" VALUE="$config">
<INPUT TYPE=HIDDEN NAME="action" VALUE="change_passwd">
<CENTER><TABLE BORDER=0>
<TR>
<TH ALIGN=LEFT>Username:</TH> <TD><INPUT TYPE=TEXT NAME="username"><BR></TD></TR>
<TR>
<TH ALIGN=LEFT>Password:</TH> <TD><INPUT TYPE=PASSWORD NAME="password"><BR></TD></TR>
<TR>
<TH ALIGN=LEFT>New Username:</TH> <TD><INPUT TYPE=TEXT NAME="new_username"><BR></TD></TR>
<TR>
<TH ALIGN=LEFT>New Password:</TH> <TD><INPUT TYPE=PASSWORD NAME="passwd_1"><BR></TD></TR>
<TR>
<TH ALIGN=LEFT>Re-type New Password:</TH> <TD><INPUT TYPE=PASSWORD NAME="passwd_2"><BR></TD></TR>
<TR>
<TD ALIGN=CENTER><INPUT TYPE=SUBMIT VALUE="Change Password"></TD>
<TD ALIGN=CENTER><INPUT TYPE=RESET></TD>
</TR></TABLE></CENTER>
</FORM>
EOH
  &print_html_footer;
}

###############################################################################
# Removing articles by by number (covers both sorted and unsorted
# lists), the action.
#
elsif ($action eq 'remove')
{ &check_passwd;
  undef %ALL;
  for($i = $FORM{'min'}; $i <= $FORM{'max'}; $i++)
   { if ($FORM{$i})
      { $ALL{$i}=1;
      }
   }
  &do_remove;
  &return_html($FORM{'type'});
}

###############################################################################
# Removing articles by Date or by Author, the action.
#
elsif ($action eq 'remove_by_lists')
{ &check_passwd;
  undef %ALL;
  $maxid=$FORM{'maxid'};
  for($i=0; $i!=$maxid; $i++)
   { if($FORM{$i})
      { @keys = split(/,/,$FORM{$i});
        foreach $key (@keys)
         { $ALL{$key}=1;
         }
      }
   }
  &do_remove;
  &return_html($FORM{'type'});
}

###############################################################################
# Changing password, the action.
#
elsif ($action eq 'change_passwd')
{ if(-s $passwd_file)
   { open(PASSWD,$passwd_file) || &error('io',$passwd_file,$!);
     $passwd_line = <PASSWD>;
     chop($passwd_line) if $passwd_line =~ /\n$/;
     close(PASSWD);
     ($username,$passwd) = split(/:/,$passwd_line);
   }

  $new_username = $FORM{'new_username'} ? $FORM{'new_username'} : $username;
  &error('not_same') unless
   $new_username &&
   $new_username !~ /:/ &&
   $FORM{'passwd_1'} &&
   $FORM{'passwd_1'} eq $FORM{'passwd_2'};

  $test_passwd = crypt($FORM{'password'}, $passwd);
  if(!-s $passwd_file || ($test_passwd eq $passwd && $FORM{'username'} eq $username))
   { open(PASSWD,">$passwd_file") || &error('io',$passwd_file,$!);
     $new_password = &my_crypt($FORM{'passwd_1'});
     print PASSWD "$new_username:$new_password\n";
     close(PASSWD);
   }
  else
   { &error(bad_combo);
   }

  &print_html_head("Password Changed","Ruboard admin password has been changed");
  $FORM{'username'}=$new_username;
  $FORM{'password'}=$FORM{'passwd_1'};
  &show_buttons("no_top");
  print "Your password for ruboard admin has been changed! Results are below:<P>";
  &print_html_hr;
  print "<B>New Username: " . &to_html_text($new_username) . "<P>\n";
  print "New Password: " . &to_html_text($FORM{'passwd_1'}) . "</B><P>\n";
  &print_html_hr;
  print "Do not forget these, since they are now encoded in a file, and not readable!.\n";
  &print_html_footer(0);
}

###############################################################################
# Blocking IP address or net, the form
#
elsif($command eq "ip_add" || ($action eq "ip_add" && $FORM{'qip'}))
{ &ask_password if $action eq "ip_add" && $FORM{'qip'};
  &print_html_head("Blocking IP address","Blocking IP address");
  &show_acc_buttons;
  local($u)=&hf($FORM{'username'});
  local($p)=&hf($FORM{'password'});
  print <<EOH;
By entering IP address here you will completely deny creating messages
for that address, no matter is this user registered or not. The
registration from denied addresses is denied also. Use bit length
(default is 32, complete address) to specify groups of addresses.
<P>
<FORM METHOD=POST ACTION="$admin_url">
<INPUT TYPE=HIDDEN NAME="action" VALUE="ip_add">
<INPUT TYPE=HIDDEN NAME="config" VALUE="$config">
<CENTER>
Username: <INPUT NAME="username" VALUE="$u">
Password: <INPUT TYPE=PASSWORD NAME="password" VALUE="$p"><P>
IP address: <INPUT NAME="ip" VALUE="" SIZE=16 MAXLENGTH=15> /
<INPUT NAME="bl" VALUE="32" SIZE=3 MAXLENGTH=2>
<INPUT TYPE=SUBMIT VALUE="  Block IP  "><BR>
Reason: <INPUT NAME="reason" SIZE=50 MAXLENGTH=200><P>
<HR WIDTH=90%>
Query IPs of messages (space separated ids):
<INPUT NAME=msgs VALUE="$FORM{'msgs'}" SIZE=20> <INPUT TYPE=SUBMIT NAME=qip VALUE="  Query  ">
EOH
  if($FORM{'msgs'})
   { @msgs=split(/[,\s]+/,$FORM{'msgs'});
     &lock_number;
     print "<TABLE BORDER><TH>Id</TH> <TH>IP addr</TH> <TH>Date</TH> <TH>Author<BR></TH></TR>\n";
     if(open(F,$acc_ip_msgs))
      { while(<F>)
         { chop if /\n$/;
           split(/:/);
           $found=0;
           foreach(@msgs)
            { if($_==$_[0])
               { $found=1;
                 last;
               }
            }
           next unless $found;
           print "<TR><TD ALIGN=RIGHT>&nbsp;$_[0]&nbsp;</TD> <TD>&nbsp;$_[1]&nbsp;</TD>\n"
               . "<TD>&nbsp;".(&calc_date($_[2],1))[0]."&nbsp;</TD>\n"
               . "<TD>&nbsp;".&to_html_text($_[3])."&nbsp;<BR></TD></TR>\n";
         }
        close(F);
      }
     print "</TABLE>\n";
   }
  print <<EOH;
</CENTER>
</FORM>
EOH
  &print_html_footer(0);
}

###############################################################################
# Blocking IP address or net, the action.
#
elsif($action eq "ip_add")
{ &check_passwd;
  ($ip=$FORM{'ip'}) =~ s/\s+//g;
  ($bl=int($FORM{'bl'})) =~ s/\s+//g;
  $bl=32 unless $bl;
  &error('bad_ip',$ip,$bl) unless $ip =~ /^\d+\.\d+\.\d+\.\d+$/ && $bl<=32 && $bl>0;
  &lock_number;
  if(open(IP,$acc_ip_conf))
   { @lines=<IP>;
     close(IP);
   }
  foreach(@lines)
   { &error('same_ip',$ip,$bl) if /^$ip:/;
   }
  if(open(IP,">$acc_ip_conf"))
   { print IP "$ip:$bl:" . time . ":-:" . $FORM{'reason'} . "\n";
     print IP @lines;
     close(IP);
   }
  else
   { &error('io',$acc_ip_conf,$!);
   }
  &unlock_number;
  &html_point("ip_rm");
}

###############################################################################
# The list of blocked IP addresses to be deleted, the form.
#
elsif($command eq "ip_rm")
{ &print_html_head("Blocked IP addresses","Blocked IP addresses");
  &show_acc_buttons;
  &ask_password;
  &show_table_head("ip_rm","ip_rm",5);
  print "<TR><TH>x</TH> <TH>IP</TH> <TH>When added</TH> <TH>Status</TH> <TH>Reason</TH></TR>\n";
  if(open(IP,$acc_ip_conf))
   { @lines=<IP>;
     close(IP);
   }
  foreach (@lines)
   { chop if /\n$/;
     ($ip,$bl,$when,$status,@_)=split(/:/);
     next unless $status;
     $reason=&to_html_text(join(":",@_));
     $date=(&calc_date($when,1))[0];
     $status=($status eq "-" ? "Denied" : "Allowed");
     print <<EOH;
<TR><TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME="$ip"></TD>
<TD>&nbsp;<TT>$ip/$bl</TT>&nbsp;</TD>
<TD>&nbsp;$date&nbsp;</TD>
<TD>&nbsp;$status&nbsp;</TD>
<TD>&nbsp;$reason&nbsp;</TD></TR>
EOH
   }
  print <<EOH;
</TABLE>
<INPUT TYPE=HIDDEN NAME="maxid" VALUE="$maxid">
<CENTER><P>
<INPUT TYPE=SUBMIT VALUE="   Remove selected IPs   ">
<INPUT TYPE=RESET VALUE="   Reset   ">
</FORM>
EOH
  &print_html_footer(0);
}

###############################################################################
# The list of blocked IP addresses to be deleted, the action.
# 
elsif($action eq "ip_rm")
{ &check_passwd;
  if(open(IP,$acc_ip_conf))
   { @lines=<IP>;
     close(IP);
   }
  for($i=0; $i<=@lines; $i++)
   { $_=$lines[$i];
     chop if /\n$/;
     ($ip,$bl,$when,$status)=split(/:/);
     next unless $status;
     next unless defined($FORM{$ip});
     push(@ip,"$ip/$bl");
     splice(@lines,$i,1);
     $i--;
   }
  &lock_number;
  if(open(IP,">$acc_ip_conf"))
   { print IP @lines;
     close(IP);
   }
  &unlock_number;
  &html_point("ip_rm");
}

###############################################################################
# The registration application, the form.
#
#@LANG@#
elsif($command eq "reg_form")
{ $nomenu=defined($FORM{'nomenu'}) ? 1 : 0;
  if($language eq "russian")
   { &print_html_head("Регистрация в `$title'","Заявка на регистрацию в `$title'");
     &show_acc_buttons unless $nomenu;
     print <<EOH;
<CENTER>
Заполните, пожалуйста, эту форму для регистрации в форуме.<BR>
С вопросами и
комментариями обращайтесь к <A HREF="mailto:$adm_email">администратору
форума</A>.
<P>
<FORM ACTION="$admin_url" METHOD=POST>
<INPUT TYPE=HIDDEN NAME="config" VALUE="$config">
<INPUT TYPE=HIDDEN NAME="action" VALUE="reg_form">
<INPUT TYPE=HIDDEN NAME="nomenu" VALUE="$nomenu">
<TABLE>
<TR><TD>&nbsp;Короткое имя:&nbsp;</TD> <TD>&nbsp;<INPUT NAME="name" SIZE=50>&nbsp;<BR></TD></TR>
<TR><TD>&nbsp;Полное имя:<SUP>*</SUP>&nbsp;</TD> <TD>&nbsp;<INPUT NAME="fname" SIZE=50>&nbsp;<BR></TD></TR>
<TR><TD>&nbsp;Пароль:&nbsp;</TD> <TD>&nbsp;<INPUT TYPE=PASSWORD NAME="pass1" SIZE=50>&nbsp;<BR></TD></TR>
<TR><TD>&nbsp;Повторите пароль:&nbsp;</TD> <TD>&nbsp;<INPUT TYPE=PASSWORD NAME="pass2" SIZE=50>&nbsp;<BR></TD></TR>
<TR><TD>&nbsp;E-Mail:<SUP>**</SUP>&nbsp;</TD> <TD>&nbsp;<INPUT NAME="email" SIZE=50>&nbsp;<BR></TD></TR>
</TABLE>
<INPUT TYPE=SUBMIT VALUE="   Послать заявку   ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<INPUT TYPE=RESET VALUE="   Сбросить   "><BR>
</CENTER>
</FORM>
<P>
<FONT SIZE=-1>
<SUP>*</SUP>
Необязательное поле. Можно использовать для автоматической подстановки
полного имени вместо короткого регистрационного.
Например, при коротком имени `пух' длинное может быть `Винни-Пух, любитель меда'.
Нет разницы, какое из имен использовать при авторизации, если были определены оба.
<P>
<SUP>**</SUP>
Почтовый адрес используется только для того, чтобы отправить Вам результаты регистрации.
Можете не заполнять это поле, если хотите.
<P>
</FONT>
EOH
   }
  else
   { &print_html_head("Registration in `$title'","Registration in `$title'");
     &show_acc_buttons unless $nomenu;
     print <<EOH;
<CENTER>
Please fill the following registration application form.<BR>
Feel free to mail your comments and questions to <A HREF="mailto:$adm_email">forum
administrator</A>.
<P>
<FORM ACTION="$admin_url" METHOD=POST>
<INPUT TYPE=HIDDEN NAME="config" VALUE="$config">
<INPUT TYPE=HIDDEN NAME="action" VALUE="reg_form">
<INPUT TYPE=HIDDEN NAME="nomenu" VALUE="$nomenu">
<TABLE>
<TR><TD>&nbsp;Short name:&nbsp;</TD> <TD>&nbsp;<INPUT NAME="name" SIZE=50>&nbsp;<BR></TD></TR>
<TR><TD>&nbsp;Real name:<SUP>*</SUP>&nbsp;</TD> <TD>&nbsp;<INPUT NAME="fname" SIZE=50>&nbsp;<BR></TD></TR>
<TR><TD>&nbsp;Password:&nbsp;</TD> <TD>&nbsp;<INPUT TYPE=PASSWORD NAME="pass1" SIZE=50>&nbsp;<BR></TD></TR>
<TR><TD>&nbsp;Re-type password:&nbsp;</TD> <TD>&nbsp;<INPUT TYPE=PASSWORD NAME="pass2" SIZE=50>&nbsp;<BR></TD></TR>
<TR><TD>&nbsp;E-Mail:<SUP>**</SUP>&nbsp;</TD> <TD>&nbsp;<INPUT NAME="email" SIZE=50>&nbsp;<BR></TD></TR>
</TABLE>
<INPUT TYPE=SUBMIT VALUE="   Submit application   ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<INPUT TYPE=RESET VALUE="   Reset   "><BR>
</CENTER>
</FORM>
<P>
<FONT SIZE=-1>
<SUP>*</SUP>
This is optional field. You may use it to automatically supply your
long/real name to your forum postings instead of short name. For example, short
name may be `pooh', but real name - `Winnie the Pooh'. Both short and
long names may be used in authorization.
<P>
<SUP>**</SUP>
E-Mail address is only used to mail registration results to you. You may
leave this field empty if you want.
<P>
</FONT>
EOH
   }
  &print_html_footer(0);
}

###############################################################################
# The registration application, the action.
# 
# File format is:
# short:long:passwd:email:when:status
elsif($action eq "reg_form")
{ $name=$FORM{'name'};
  $fname=$FORM{'fname'};
  $pass1=$FORM{'pass1'};
  $pass2=$FORM{'pass2'};
  $email=$FORM{'email'};
  &error('reg_bad') if $name eq "" || $name =~ /:/ || $fname =~ /:/ ||
                       $pass1 eq "" || $pass1 ne $pass2 ||
                       $email =~ /:/;
  undef $fname if $fname eq $name;
  &lock_number;
  if(open(F,$acc_passwd))
   { while(<F>)
      { chop if /\n$/;
        split(/:/);
        if($name eq $_[0])
         { close(F);
           &error('reg_already',$name);
         }
        if($fname ne "" && $fname eq $_[1])
         { close(F);
           &error('reg_already',$fname);
         }
      }
     close(F);
   }
  open(F,">>$acc_passwd") || &error('io',$acc_passwd,$!);
  $pass1=&my_crypt($pass1);
  print F "$name:$fname:$pass1:$email:".time.":0:\n";
  close(F);
  &unlock_number;

# Confirmation html
#@LANG@#
#
  if($language eq "russian")
   { &mailer($adm_email,"Заявка на регистрацию - $name"
            ,"В форуме `$title' зарегистрировался новый абонент:\n"
            ." short name=$name\n"
            ." long name =$fname\n"
            ." e-mail    =$email\n\n"
            ."Пожалуйста, зайдите на $admin_url\nчтобы подтвердить его/её регистрацию");
     &print_html_head("Результаты регистрации","-");
     &show_acc_buttons unless $FORM{'nomenu'};
     print <<EOH;
Ваша заявка на регистрацию в форуме `$title' под именем `$name' передана
администратору форума. Если у Вас возникнут вопросы - задайте их по адресу 
<A HREF="mailto:$adm_email">$adm_email</A>.
EOH
   }
  else				# english
   { &mailer($adm_email,"Registration - $name"
            ,"New user has applied for registration in forum `$title':\n"
            ." short name=$name\n"
            ." long name =$fname\n"
            ." e-mail    =$email\n\n"
            ."Please visit $admin_url\nto approve his/her application\n");
     &print_html_head("Registration results","-");
     &show_acc_buttons unless $FORM{'nomenu'};
     print <<EOH;
Your application for registration in the forum `$title' under the name `$name' is
now waiting for forum administrator approval. Should you wish to ask a question
please mail to <A HREF="mailto:$adm_email">$adm_email</A>.
EOH
   }
  &print_html_footer(0);
}

###############################################################################
# The list of registered (and yet to be confirmed) users, the form.
#
elsif($command eq "reg_list")
{ &print_html_head("Users list","The list of registered users");
  &show_acc_buttons;
  &ask_password;
  &show_table_head("reg_list","",7);
  print "<TR><TH>x</TH> <TH>User</TH> <TH>Long name</TH>\n"
      . "<TH>E-Mail</TH> <TH>Date</TH> <TH>Status</TH> <TH>Comment<BR></TH></TR>\n";
  if(open(F,$acc_passwd))
   { while(<F>)
      { chop if /\n$/;
        ($name,$fname,$pass,$email,$when,$status,@_)=split(/:/);
        $name1=&hf($name);
        $name=&to_html_text($name);
        $fname=&to_html_text($fname);
        $email=&to_html_text($email);
        $cmt=&hf(join(":",@_));
        $date=(&calc_date($when,1))[0];
        $status=($status ? "Active" : "<FONT COLOR=\"800000\">Not active</FONT>");
        print <<EOH;
<TR><TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME="_$name1"></TD>
<TD>&nbsp;$name&nbsp;</TD>
<TD>&nbsp;$fname&nbsp;</TD>
<TD>&nbsp;$email&nbsp;</TD>
<TD>&nbsp;$date&nbsp;</TD>
<TD>&nbsp;$status&nbsp;</TD>
<TD>&nbsp;<INPUT NAME="c_$name1" VALUE="$cmt" SIZE=20>&nbsp;</TD></TR>
EOH
      }
     close(F);
   }
  print <<EOH;
</TABLE>
<CENTER>
<INPUT TYPE=SUBMIT NAME=reg_cmt VALUE="   Change comments   ">
<INPUT TYPE=SUBMIT NAME=reg_act VALUE="   Activate   ">
<INPUT TYPE=SUBMIT NAME=reg_clo VALUE="   Close   ">
<INPUT TYPE=SUBMIT NAME=reg_rm VALUE="   Delete   ">
<INPUT TYPE=RESET VALUE="   Unmark   ">
<BR></CENTER>
</FORM>
EOH
  &print_html_footer(0);
}

###############################################################################
# The list of registered (and yet to be confirmed) users, the form.
#
elsif($action eq "reg_list")
{ &check_passwd;
  &lock_number;
  if(open(F,$acc_passwd))
   { @lines=<F>;
     close(F);
   }
  for($i=0; $i<@lines; $i++)
   { $_=$lines[$i];
     chop if /\n$/;
     ($name,$fname,$pass,$email,$when,$status,@_)=split(/:/);
     $cmt=join(":",@_);
     if($FORM{"_$name"})
      { if($FORM{'reg_cmt'})
         { $cmt=$FORM{"c_$name"};
         }
        elsif($FORM{"reg_act"})
         { &mailer($email,"Your registration in `$title'"
                  ,"Dear $fname!\n\n"
                  ."Congratulations! Your registration in the forum `$title'\n"
                  ."has been activated.\n") unless $status;
           $status=1;
         }
        elsif($FORM{"reg_clo"})
         { &mailer($email,"Your registration in `$title'"
                  ,"Dear $fname!\n\n"
                  ."Your registration in the forum `$title' has been temporary closed.\n"
                  ."Mail your complaints (if any) to $adm_email.\n") if $status;
           $status=0;
         }
        elsif($FORM{"reg_rm"})
         { &mailer($email,"Your registration in `$title'"
                  ,"Dear $fname!\n\n"
                  ."Your registration in the forum `$title' has been cancelled.\n"
                  ."Mail your complaints (if any) to $adm_email.\n");
           splice(@lines,$i,1);
           $i--;
           next;
         }
        $lines[$i]="$name:$fname:$pass:$email:$when:$status:$cmt\n";
      }
   }
  if(open(F,">$acc_passwd"))
   { print F @lines;
     close(F);
   }
  &unlock_number;
  &html_point("reg_list");
}

###############################################################################
# Banned words list, the form.
#
elsif($command eq "badwords")
{ &print_html_head("Banned words","The list of banned words");
  &show_acc_buttons;
  &ask_password;
  print <<EOH;
No words from this list may appear in postings at `$title'. You may use
both plain words or enter regular expressions (for example
`<TT>\\bsex.*</TT>' to match sex, sexual, sexy etc).
EOH
  &show_table_head("badwords","",3);
  print "<TR><TH>x</TH> <TH>Word or mask</TH> <TH>When</TH></TR>\n";
  if(open(F,$bw_file))
   { while(<F>)
      { chop while /[\s\n]$/;
        ($id,$when,$regex)=split(/:/,$_);
        print "<TR><TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=bw$id></TD>\n"
            . "<TD>&nbsp;" . &to_html_text($regex) . "&nbsp;</TD>\n"
            . "<TD>&nbsp;" . (&calc_date($when,1))[0] . "&nbsp;</TD></TR>\n";
      }
   }
  print <<EOH;
</TABLE>
<CENTER>
<INPUT TYPE=SUBMIT VALUE="   Drop   ">
<INPUT TYPE=RESET VALUE="   Unmark   ">
<HR>
<INPUT NAME="word" SIZE=30> <INPUT TYPE=SUBMIT VALUE="   Add new word   ">
<BR></CENTER>
</FORM>
EOH
  &print_html_footer(0);
}

###############################################################################
# Banned words list, the action.
#
elsif($action eq "badwords")
{ &check_passwd;
  &lock_number;
  if(open(F,$bw_file))
   { @lines=<F>;
     close(F);
   }
  $changed=0;
  if($FORM{"word"} ne "")	# Adding new word
   { $newid=1;
     foreach(@lines)
      { $id=(split(/:/,$_))[0];
        $newid=$id+1 if $id>=$newid;
      }
     push(@lines,"$newid:" . time . ":" . $FORM{'word'} . "\n");
     $changed=1;
   }
  else				# Deleting words
   { for($i=0; $i!=@lines; $i++)
      { $id=(split(/:/,$lines[$i]))[0];
        if($FORM{"bw$id"})
         { splice(@lines,$i,1);
           $i--;
           $changed=1;
         }
      }
   }
  if($changed)
   { open(F,">$bw_file") || &error('io',$bw_file,$!);
     print F @lines;
   }
  &html_point("badwords");
}

###############################################################################
# Main menu
#
else
{ &print_html_head("`$title' administrator shell","$title; Administrator shell");
  local($u)=$FORM{'username'} ? "&username=".&hf($FORM{'username'}) : "";
  local($p)=$FORM{'password'} ? "&password=".&hf($FORM{'password'}) : "";
  if($acc_policy)
   { $reg_list="<A HREF=\"$admin_url?config=$config&c=reg_list$u$p\">";
     $reg_form="<A HREF=\"$admin_url?config=$config&c=reg_form$u$p\">";
     $reg_close="</A>";
   }
  else
   { $reg_cnfm=$reg_list=$reg_form="<FONT COLOR=\"#808080\">";
     $reg_close="</FONT>";
   }
  if($acc_ip)
   { $ip_add="<A HREF=\"$admin_url?config=$config&c=ip_add$u$p\">";
     $ip_rm="<A HREF=\"$admin_url?config=$config&c=ip_rm$u$p\">";
     $ip_close="</A>";
   }
  else
   { $ip_add=$ip_rm="<FONT COLOR=\"#808080\">";
     $ip_close="</FONT>";
   }
  print <<EOH;
<CENTER><TABLE><TR VALIGN=TOP><TD>
<UL>
<LI>Messages
<UL>
<LI><A HREF="$admin_url?config=$config&c=remove$u$p">Remove messages</A>
<LI><A HREF="$admin_url?config=$config&c=remove_by_num$u$p">Remove messages by number</A>
<LI><A HREF="$admin_url?config=$config&c=remove_by_date$u$p">Remove messages by date</A>
<LI><A HREF="$admin_url?config=$config&c=remove_by_author$u$p">Remove messages by author</A>
</UL><BR>
<LI>Access
<UL>
<LI>${reg_list}List of registered users$reg_close
<LI>${reg_form}Registration form$reg_close
<LI>${ip_add}Block IP address$ip_close
<LI>${ip_rm}Unblock IP address$ip_close
</UL><BR>
</TD><TD>
<UL>
<LI>Post restrictions
<UL>
<LI><A HREF="$admin_url?config=$config&c=badwords$u$p">Banned words list</A>
</UL><BR>
<LI>Password
<UL>
<LI><A HREF="$admin_url?config=$config&c=change_passwd">Change admin password</A>
</UL><BR>
<LI>Go to board
<UL>
<LI><A HREF="$mainpage">$title</A>
<LI><A HREF="$cgi_url?config=$config&post">Post message</A>
</UL>
</UL>
</TD></TR></TABLE></CENTER>
EOH
  &print_html_footer;
}

# Finish
#
exit(0);

###############################################################################
# Removing all articles specified in %ALL array (both articles and
# references from index). Removing the article always means REMOVING
# THE COMPLETE TREE started at that article!  Index is locked while
# removing.
#
sub do_remove
{ &lock_number;
  $#ATTEMPTED=1;
  $#NO_FILE=1;
  $#NOT_REMOVED=1;

# Reading
#
  open(MSGS,"$mesgfile");
  local(@lines) = <MSGS>;
  close(MSGS);

# Finding dependences, removing files and modifying index
#
  foreach $all (keys %ALL)
   { undef $top; undef $bottom;
     undef @DELETE;
     foreach($j=0; $j<=@lines; $j++)
      { ($top=$j, next) if $lines[$j] =~ /<!--t[ol][pa]: $all-->/;
        ($bottom=$j, next) if $lines[$j] =~ /<!--end: $all-->/;
      }
     if($top && $bottom)
      { $diff=$bottom-$top+1;
        for($k=$top; $k<=$bottom; $k++)
         { if ($lines[$k] =~ /<!--t[ol][pa]: (\d*)-->/)
            { push(@DELETE,$1);
            }
         }
        splice(@lines, $top, $diff);
        foreach $delete (@DELETE)
         { $filename = "$basedir/$mesgdir/$delete\.$ext";
           if (-e $filename)
            { unlink($filename) || push(@NOT_REMOVED,$delete);
            }
           else
            { push(@NO_FILE,$delete);
            }
           push(@ATTEMPTED,$delete);
         }
      }
   }

# Writing index back to disk
#
  $um=umask(022);
  open(WWWBOARD,">$mesgfile.tmp");
  umask($um);
  print WWWBOARD @lines;
  close(WWWBOARD);      
  rename("$mesgfile.tmp",$mesgfile);
  &unlock_number;
}

###############################################################################
# Resulting pages
#
sub return_html
{ local($type) = $_[0];
  if ($type eq 'remove')
   { $head_text="Results of message board removal";
   }
  elsif ($type eq 'remove_by_num')
   { $head_text="Results of message board removal by number";
   }
  elsif ($type eq 'remove_by_date')
   { $head_text="Results of message board removal by date";
   }
  elsif ($type eq 'remove_by_author')
   { $head_text="Results of message board removal by author";
   }
  &print_html_head($head_text,$head_text);
  if ($type =~ /^remove/)
   { print "Below is a short summary of what messages were removed from $mainpage and the\n";
     print "$mesgdir directory.  All files that the script attempted to remove, were removed,\n";
     print "unless there is an error message stating otherwise.<BR>\n";
     &show_buttons;
     print "<TABLE>\n";
     print "<TR><TD>Attempted to Remove:</TD> <TD>@ATTEMPTED<BR></TD></TR>\n";
     print "<TR><TD>Files That Could Not Be Deleted:</TD> <TD>@NOT_REMOVED<BR></TD></TR>\n" if @NOT_REMOVED;
     print "<TR><TD>Files Not Found:</TD> <TD>@NO_FILE<BR></TD></TR>\n" if @NO_FILE;
     print "</TABLE>\n";
     &print_html_footer;
   }
}

###############################################################################
# Error
#
sub error
{ local($error) = $_[0];
  if ($error eq 'bad_combo')
   { delete $FORM{'username'};
     delete $FORM{'password'};
     &print_html_head("Bad username/password combination","Bad username/password combination");
     &show_buttons("no_top");
     print "You entered an invalid username/password pair. Please try again.<p>\n";
   }
  elsif ($error eq 'not_same')
   { &print_html_head("Incorrect password or user name",
                      "Incorrect password or user name typed in");
     print "<CENTER>You've typed no user name, no passwords or two copies of password were\n"
         . "not the same.<BR>\n"
         . "You may have mistyped, please try again.<P></CENTER>\n";
   }
  elsif ($error eq 'no_config')
   { &print_html_head("No configuration!","Could Not Open Configuration File");
     print "Could not find or open the configuration! Check script\n"
         . "parameters, <A HREF=\"$ENV{'HTTP_REFERER'}\">refering document</A> or\n"
         . "file permissions.\n";
   }
  elsif ($error eq 'wrong_cf')
   { &print_html_head("Wrong configuration","Wrong configuration file!");
     print "Please fix (or ask forum administrator to fix) the configuration file!\n";
   }
  elsif ($error eq 'bad_ip')
   { &print_html_head("Bad IP address","Bad IP address");
     print "You've entered wrong IP address ($_[1]/$_[2])!\n";
   }
  elsif ($error eq 'same_ip')
   { &print_html_head("IP address is already blocked"," IP address is already blocked");
     print "You've entered the IP address ($_[1]/$_[2]) which already present in blocked IPs list!\n"
         . "Delete it first if you want to change bit-length!\n";
   }
  elsif ($error eq 'io')
   { &print_html_head("I/o error!","I/o error!");
     print "I/o error occured on file $_[1]: $_[2]!";
   }
  elsif ($error eq 'reg_bad')
   { &print_html_head("Wrong registration data!","Wrong registration data!");
     print "You've either didn't filled required fields or used `:'\n"
         . "symbol somewhere in names or e-mail.\n";
   }
  elsif ($error eq 'reg_already')
   { &print_html_head("Wrong registration data!","Wrong registration data!");
     print "User with such name ($_[1]) is already exists.\n"
         . "Please choose another.\n"
   }
  else
   { &print_html_head("Unknown error","Unknown error");
     print "Something is wrong. Check script, configuration, environment, blood preasure etc..\n";
   }
  &print_html_footer(0);
  exit(0);
}

###############################################################################
# Checking Password
#
sub check_passwd
{ open(PASSWD,"$passwd_file") || &error(passwd_file);
  $passwd_line = <PASSWD>;
  close(PASSWD);
  chop($passwd_line) if $passwd_line =~ /\n$/;
  ($username,$passwd) = split(/:/,$passwd_line);
  $test_passwd = crypt($FORM{'password'}, $passwd);
  &error(bad_combo) if $test_passwd ne $passwd || $FORM{'username'} ne $username;
}

###############################################################################
# Common tool buttons
#
sub show_buttons
{ &print_html_hr unless $_[0] eq "no_top";
  local($u)=$FORM{'username'} ? "&username=".&hf($FORM{'username'}) : "";
  local($p)=$FORM{'password'} ? "&password=".&hf($FORM{'password'}) : "";
  print <<EOH;
<CENTER><FONT SIZE=-1>
[<A HREF="$admin_url\?config=$config$u$p">Main menu</A>]
[<A HREF="$admin_url\?config=$config&c=remove$u$p">Remove</A>]
[<A HREF="$admin_url\?config=$config&c=remove_by_num$u$p">Remove by Message Number</A>]
[<A HREF="$admin_url\?config=$config&c=remove_by_date$u$p">Remove by Date</A>]
[<A HREF="$admin_url\?config=$config&c=remove_by_author$u$p">Remove by Author</A>]
</FONT></CENTER>
EOH
  &print_html_hr unless $_[0] eq "no_bottom";
}

###############################################################################
# Access  buttons
#
sub show_acc_buttons
{ local($u)=$FORM{'username'} ? "&username=".&hf($FORM{'username'}) : "";
  local($p)=$FORM{'password'} ? "&password=".&hf($FORM{'password'}) : "";
  if($acc_policy)
   { $reg_list="<A HREF=\"$admin_url?config=$config&c=reg_list$u$p\">";
     $reg_form="<A HREF=\"$admin_url?config=$config&c=reg_form$u$p\">";
     $reg_close="</A>";
   }
  else
   { $reg_cnfm=$reg_list=$reg_form="<FONT COLOR=\"#808080\">";
     $reg_close="</FONT>";
   }
  if($acc_ip)
   { $ip_add="<A HREF=\"$admin_url?config=$config&c=ip_add$u$p\">";
     $ip_rm="<A HREF=\"$admin_url?config=$config&c=ip_rm$u$p\">";
     $ip_close="</A>";
   }
  else
   { $ip_add=$ip_rm="<FONT COLOR=\"#808080\">";
     $ip_close="</FONT>";
   }
  print <<EOH;
<CENTER><FONT SIZE=-1>
[<A HREF="$admin_url\?config=$config$u$p">Main menu</A>]
[${reg_list}List of users$reg_close]
[${reg_form}Registration form$reg_close]
[${ip_rm}Blocked IPs$ip_close]
[${ip_add}Add IP$ip_close]
[<A HREF="$admin_url?config=$config&c=badwords$u$p">Banned words</A>]
</FONT></CENTER>
EOH
  &print_html_hr unless $_[0] eq "no_bottom";
}


###############################################################################
# Reading entries from $mesgfile
#
sub read_mesgfile
{ local($line);
  open(MSGS,"$mesgfile");
  local(@lines) = <MSGS>;
  close(MSGS);

  foreach $line (@lines)
   { if ($line =~ /<!--t[ol][pa]: (\d+)--><li><a href="$mesgdir\/\1\.$ext">(.*)<\/a> - <b>(.*)<\/b>\s+<i>(.*)<\/i>/i)
      { push(@ENTRIES,$1);
        $SUBJECT{$1} = $2;
        $AUTHOR{$1} = $3;
        $DATE{$1} = $4;
        $TLA{$1}=1 if $line =~ /<!--tla/;
      }
     elsif ($line =~ /\(<!--responses: (\d+)-->(\d+)\)/)
      { $THREADSIZE{$1}=$2;
      }
   }

  @SORTED_ENTRIES = (sort { $b <=> $a } @ENTRIES);
  $min = $SORTED_ENTRIES[$#SORTED_ENTRIES];
  $max = $SORTED_ENTRIES[0];
}

###############################################################################
# Head of postings list
#
sub show_table_head
{ local($action) = $_[0];
  local($type) = $_[1];
  local($cols) = $_[2] ? $_[2] : 6;
  local($uname) = &hf($FORM{'username'});
  local($passwd) = &hf($FORM{'password'});
  print <<EOH;
<FORM METHOD=GET ACTION="$admin_url">
<INPUT TYPE=HIDDEN NAME="action" VALUE="$action">
<INPUT TYPE=HIDDEN NAME="config" VALUE="$config">
EOH
  print "<INPUT TYPE=HIDDEN NAME=\"min\" VALUE=\"$min\">\n" if defined($min);
  print "<INPUT TYPE=HIDDEN NAME=\"max\" VALUE=\"$max\">\n" if defined($max);
  print "<INPUT TYPE=HIDDEN NAME=\"type\" VALUE=\"$type\">\n" if $type ne "";
  print <<EOH;
<TABLE BORDER WIDTH=100%>
<TR>
<TH COLSPAN=$cols>Username: <INPUT TYPE=TEXT NAME="username" VALUE="$uname">
-- Password: <INPUT TYPE=PASSWORD NAME="password" VALUE="$passwd"><BR></TH>
</TR>
EOH
  print <<EOH if $cols==6;
<TR>
<TH>Post #</TH> <TH>x</TH> <TH>Answers</TH> <TH>Subject</TH> <TH>Author</TH> <TH>Date<BR></TH>
</TR>
EOH
}

###############################################################################
# Table, form and whole html document footer
#
sub show_table_foot
{ print <<EOH;
</TABLE>
<CENTER><P>
<INPUT TYPE=SUBMIT VALUE="   Remove Messages   ">
<INPUT TYPE=RESET VALUE="   Reset   ">
</FORM>
EOH
  &print_html_footer;
}

###############################################################################
# Checking the presence of password and asking it, if there is no one.
#
sub ask_password
{ local($u)=$FORM{'username'};
  if(defined($u) && defined($FORM{'password'}))
   { if(open(PASSWD,"$passwd_file"))
      { $passwd_line = <PASSWD>;
        close(PASSWD);
      }
     else
      { $passwd_line="";
      }
     chop($passwd_line) if $passwd_line =~ /\n$/;
     ($username,$passwd) = split(/:/,$passwd_line);
     return if $username eq $u && $passwd eq crypt($FORM{'password'},$passwd);
     print "You've entered invalid user name and/or password!<P>\n";
   }
  $u=&hf($u);
  print <<EOH;
In order to access this page you have to know correct administrator name and password.
Please, type them:
<CENTER>
<FORM ACTION="$admin_url" METHOD=POST>
<INPUT TYPE=HIDDEN NAME=config VALUE="$config">
User name: <INPUT NAME="username" SIZE=20 VALUE="$u">
Password: <INPUT TYPE=PASSWORD NAME="password" SIZE=20><P>
<INPUT TYPE=SUBMIT VALUE="   Continue   "><P>
EOH
  delete $FORM{'username'};
  delete $FORM{'password'};
  foreach(keys %FORM)
   { print "<INPUT TYPE=HIDDEN NAME=\"";
     print &hf($_);
     print "\" VALUE=\"";
     print &hf($FORM{$_});
     print "\">\n";
   }
  print "</FORM></CENTER>\n";
  &print_html_footer(0);
  exit(0);
}

###############################################################################
# HTML redirector
#
sub html_point
{ local($page)=shift(@_);
  local($u)=$FORM{'username'} ? "&username=".&hf($FORM{'username'}) : "";
  local($p)=$FORM{'password'} ? "&password=".&hf($FORM{'password'}) : "";
  local($url)="$admin_url?config=$config&c=$page$u$p&".time.join("&",@_);
  print <<EOH;
Location: $url

Please go <A HREF="$url">here</A>
EOH
}

###############################################################################
# Sorting by date
#
sub date_sort
{ local(@aa)=split("/",$a);
  local(@bb)=split("/",$b);
#@LANG@#
  if($language eq "russian")
   { @aa=($aa[1],$aa[0],$aa[2]);
     @bb=($bb[1],$bb[0],$bb[2]);
   }
  sprintf("%04u%02u%02u",$bb[2],$bb[0],$bb[1]) <=> sprintf("%04u%02u%02u",$aa[2],$aa[0],$aa[1]);
}
