home *** CD-ROM | disk | FTP | other *** search
/ zipcon.net / www.zipcon.net.tar / www.zipcon.net / pub / cgi / ezmlm-web / ezmlm-web.cgi < prev    next >
Text File  |  1999-10-07  |  35KB  |  903 lines

  1. #!/usr/bin/perl -T
  2.  
  3. #===========================================================================
  4. # ezmlm-web.cgi - version 1.0 - 01/08/1998
  5. #
  6. # Copyright (C) 1998, Guy Antony Halse, All Rights Reserved.
  7. #
  8. # This program is free for non-commercial use; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License (version 2 or
  10. # later) as published by the Free Software Foundation. This program is
  11. # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  12. # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  13. # PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  14. #
  15. # Please send any bug reports or comments to guy-ezmlm@rucus.ru.ac.za
  16. # ==========================================================================
  17. # Changelog moved to file CHANGES with this distribution.
  18. # ==========================================================================
  19.  
  20. # Suid stuff requires a secure path. Ensure that the following can be found;
  21. # ezmlm-make, ezmlm-list, ezmlm-sub, ezmlm-unsub, mv, rm
  22. $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
  23.  
  24. # We run suid so we can't use $ENV{'HOME'} and $ENV{'USER'} to determine the
  25. # user. :( Don't alter this line unless you are _sure_ you have to.
  26. @tmp = getpwuid($>); $USER=$tmp[0]; $HOME_DIR=$tmp[7];
  27.  
  28. # Where do we store lists on this server ... Try "$HOME_DIR/lists". 
  29. # This directory will automatically be created if needed.
  30. $LIST_DIR = "$HOME_DIR/lists";
  31.  
  32. # Default domain name to use if one isn't supplied. This is mainly used to 
  33. # determine if we are using a virtual host, and to allow the user to enter
  34. # "user@" instead of the full email address to add a local subscriber.
  35. open( DEFAULT_HOST_FILE, "</var/qmail/control/me" ) || die "Can't open /var/qmail/control/me: $!\n";
  36. $DEFAULT_HOST = <DEFAULT_HOST_FILE>;
  37. close( DEFAULT_HOST_FILE );
  38.  
  39. # Where is the qmail virtual domains file kept on this system?
  40. $VIRTUAL_DOMAINS = '/var/qmail/control/virtualdomains';
  41.  
  42. # Safe list deletion? 
  43. # 0 = move list to .list and the .qmails to deleted.qmail/. Recoverable :)
  44. # 1 = allow user to delete list completely. No backup, therefore no recovery.
  45. $UNSAFE_RM = 0;
  46.  
  47. # What switches to we want ezmlm-web to have on as default. The ones that 
  48. # ezmlm-make uses as builtin defaults are 'aip'. Available ones are 
  49. # 'adfgiklmnpqrstu5'. See the ezmlm-make(1) man page for more details.
  50. $DEFAULT_OPTIONS = 'aip5';
  51.  
  52. # ==========================================================================
  53. # User configurable stuff ends ... Don't alter anything below this line.
  54. # ==========================================================================
  55. require 5.001; # Check the perl version ...
  56.  
  57. # Set up a new CGI query ...
  58. use CGI;
  59. use CGI::Carp qw(fatalsToBrowser);
  60. $q = new CGI;
  61.  
  62. # Allow suid wrapper to over-ride default list directory ...
  63. use Getopt::Std;
  64. getopts('d:');
  65. if(defined($opt_d)) {
  66.    $LIST_DIR = $1 if ($opt_d =~ /^([-\@\w.\/]+)$/);
  67. }
  68.  
  69. # Untaint form input ...
  70. &untaint;
  71.  
  72. # Print header on every page ...
  73. print $q->header;
  74. print $q->start_html(-title=>'EZ Mailing List Manager', -author=>'guy-ezmlm@rucus.ru.ac.za', -BGCOLOR=>'#9040D0', -LINK=>'#2000C0', -expires=>'now');
  75. print <<'EOM';
  76.    <CENTER><TABLE BORDER=1 ALIGN=CENTER><TR><TD>
  77.    <FONT SIZE=+3 COLOR=#000080><STRONG>E Z Mailing List Manager</STRONG></FONT>
  78.    </TD></TR></TABLE></CENTER>
  79. EOM
  80.  
  81. # This is where we decide what to do, depending on the form state and the
  82. # users chosen course of action ...
  83. unless (defined($q->param('state'))) {
  84.    # Default action. Present a list of available lists to the user ...
  85.    &select_list; 
  86.  
  87. } elsif ($q->param('state') eq 'select') {
  88.    # User selects an action to perorm on a list ...
  89.    
  90.    if ($q->param('action') eq '[Create]') { # Create a new list ...
  91.       &allow_create_list;
  92.    } elsif (defined($q->param('list'))) {
  93.       if ($q->param('action') eq '[Edit]') { # Edit an existing list ...
  94.          &display_list;
  95.       } elsif ($q->param('action') eq '[Delete]') { # Delete a list ...
  96.          &confirm_delete;
  97.       }
  98.    } else {
  99.       &select_list; # NOP - Blank input ...
  100.    }
  101.    
  102. } elsif ($q->param('state') eq 'edit') {
  103.    # User chooses to edit a list
  104.    
  105.    my($list); $list = $LIST_DIR . '/' . $q->param('list'); 
  106.    if ($q->param('action') eq '[Delete Address]') { # Delete a subscriber ...
  107.       &delete_address($list);
  108.       &display_list;
  109.    
  110.    } elsif ($q->param('action') eq '[Add Address]') { # Add a subscriber ...
  111.       &add_address($list);
  112.       &display_list;
  113.    
  114.    } elsif ($q->param('action') eq '[Moderators]') { # Edit the moderators ...
  115.       &moderators;
  116.  
  117.    } elsif ($q->param('action') eq '[Blacklist]') { # Edit the blacklist ...
  118.       &blacklist;
  119.    
  120.    } elsif ($q->param('action') eq '[Digest]') { # Edit the digest subscribers ...
  121.       &digest;
  122.       
  123.    } elsif ($q->param('action') eq '[Configuration]') { # Edit the config ...
  124.       &list_config;
  125.  
  126.    } elsif ($q->param('action') eq '[Create Config]') { # Try and create conf file ...
  127.       &create_config;
  128.       &display_list;
  129.        
  130.    } else { # Cancel - Return a screen ...
  131.       &select_list;
  132.    }
  133.  
  134. } elsif ($q->param('state') eq 'moderators' || $q->param('state') eq 'blacklist' || $q->param('state') eq 'digest') {
  135.    # User edits moderators || blacklist || digest ...
  136.  
  137.    my($list); 
  138.    # Which list directory are we using ...
  139.    if($q->param('state') eq 'moderators') {
  140.       $list = $LIST_DIR . '/' . $q->param('list') . '/mod'; 
  141.    } elsif($q->param('state') eq 'blacklist' ) {
  142.       $list = $LIST_DIR . '/' . $q->param('list') . '/blacklist'; 
  143.    } else {
  144.       $list = $LIST_DIR . '/' . $q->param('list') . '/digest'; 
  145.    }
  146.    
  147.    if ($q->param('action') eq '[Delete Address]') { # Delete a subscriber ...
  148.       &delete_address($list);
  149.       if ($q->param('state') eq 'moderators') { &moderators; } elsif ($q->param('state') eq 'blacklist') {  &blacklist; } else { &digest; }
  150.  
  151.    } elsif ($q->param('action') eq '[Add Address]') { # Add a subscriber ...
  152.       &add_address($list);
  153.       if ($q->param('state') eq 'moderators') { &moderators; } elsif ($q->param('state') eq 'blacklist') {  &blacklist; } else { &digest; }
  154.  
  155.    } else { # Cancel - Return to the list ...
  156.       &display_list;
  157.    }
  158.  
  159. } elsif ($q->param('state') eq 'confirm_delete') {
  160.    # User wants to delete a list ...
  161.    
  162.    &delete_list if($q->param('confirm') eq '[Yes]'); # Do it ...
  163.    $q->delete_all;
  164.    &select_list;
  165.  
  166. } elsif ($q->param('state') eq 'create') {
  167.    # User wants to create a list ...
  168.  
  169.    if ($q->param('action') eq '[Create List]') {
  170.       if (&create_list) { # Return if list creation is unsuccessful ...
  171.          &allow_create_list;
  172.       } else {
  173.          &select_list; # Else choose a list ...
  174.       }
  175.    
  176.    } else { # Cancel ...
  177.       &select_list;
  178.    }
  179.    
  180. } elsif ($q->param('state') eq 'configuration') {
  181.    # User updates configuration ...
  182.    
  183.    if ($q->param('action') eq '[Update Configuration]') { # Save current settings ...
  184.       &update_config;
  185.       &display_list;
  186.       
  187.    } elsif ($q->param('action') eq '[Edit Texts]') { # Edit DIR/text ...
  188.       &list_text;
  189.    
  190.    } else { # Cancel - Return to list editing screen ...
  191.       &display_list;
  192.    }
  193.  
  194. } elsif ($q->param('state') eq 'list_text') {
  195.    # User wants to edit texts associated with the list ...
  196.    
  197.    if ($q->param('action') eq '[Edit File]') {
  198.       &edit_text;  
  199.    } else {
  200.       &list_config; # Cancel ...
  201.    }
  202.  
  203. } elsif ($q->param('state') eq 'edit_text') {   
  204.    # User wants to save a new version of something in DIR/text ...
  205.    
  206.    &save_text if ($q->param('action') eq '[Save File]');
  207.    &list_text;
  208.    
  209. } else {
  210.    print '<H2 ALIGN=CENTER>Action not yet implemented</H2><HR ALIGN=center WIDTH=25%>';
  211.  
  212. # Print HTML footer and exit :) ...
  213. print <<'EOM';
  214.    <HR><FONT SIZE=-2><A HREF=http://www.ezmlm.org/>ezmlm</A> web interface (v1.0) by
  215.    <A HREF=http://www.rucus.ru.ac.za/~guy/>Guy Antony Halse</A>
  216.    (<A HREF=MailTo:guy-ezmlm@rucus.ru.ac.za>guy-ezmlm@rucus.ru.ac.za</A>)</FONT><HR>
  217. EOM
  218. print $q->end_html;
  219. exit;
  220.  
  221. # =========================================================================
  222.  
  223. sub select_list {
  224.    # List all mailing lists (sub directories) in the list directory.
  225.    # Allow the user to choose a course of action; either editing an existing
  226.    # list, creating a new one, or deleting an old one.
  227.  
  228.    my (@lists, @files, $i);
  229.  
  230.    # Read the list directory for mailing lists.
  231.    opendir DIR, $LIST_DIR || die "Unable to read $LIST_DIR: $!";
  232.    @files = grep !/^\./, readdir DIR; 
  233.    closedir DIR;
  234.  
  235.    # Check that they actually are lists ...
  236.    foreach $i (0 .. $#files) {
  237.       $lists[$#lists + 1] = $files[$i] if (-e "$LIST_DIR/$files[$i]/lock");
  238.    }
  239.  
  240.    # Print a form
  241.    $q->delete_all;
  242.    print $q->startform;
  243.    print $q->hidden(-name=>'state', -default=>'select');
  244.    print '<CENTER><TABLE BORDER="0" CELLPADDING="10"><TR><TD ALIGN="center" VALIGN="top" ROWSPAN="2">';
  245.    print $q->scrolling_list(-name=>'list', -values=>\@lists) if defined(@lists);
  246.  
  247.    print <<'EOM';
  248.       </TD><TD ALIGN="left" VALIGN="top">
  249.       <UL>
  250.       <LI>Choose a mailing list from the selection box or click on [Create].
  251.       <LI>Click on the [Edit] button if you want to edit the selected list.
  252.       <LI>Click on the [Delete] button if you want to delete the selected list.<P>
  253.       </UL>
  254. EOM
  255.  
  256.    print $q->submit(-name=>'action', -value=>'[Create]'), ' ';
  257.    print $q->submit(-name=>'action', -value=>'[Edit]'), ' ' if(defined(@lists));
  258.    print $q->submit(-name=>'action', -value=>'[Delete]') if(defined(@lists));
  259.    print '</TD></TR><TR><TD> </TD></TR></TABLE></CENTER>';
  260.    print $q->endform;
  261. }
  262.  
  263. # ------------------------------------------------------------------------
  264.  
  265. sub confirm_delete {
  266.    # Make sure that the user really does want to delete the list!
  267.    
  268.    # Print a form ...
  269.    $q->delete('state');
  270.    print $q->startform;
  271.    print $q->hidden(-name=>'state', -default=>'confirm_delete');
  272.    print $q->hidden(-name=>'list', -default=>$q->param('list'));
  273.    print '<H2 ALIGN="center">Confirm deletion of ', $q->param('list'), '</H3><BR><CENTER>';
  274.    print $q->submit(-name=>'confirm', -value=>'[No]'), ' ';
  275.    print $q->submit(-name=>'confirm', -value=>'[Yes]'), '</CENTER>';
  276. }
  277.  
  278. # ------------------------------------------------------------------------
  279.  
  280. sub display_list {
  281.    # Show a list of subscribers to the user ...
  282.  
  283.    my ($i, $list, $listaddress, $moderated, @subscribers);
  284.    
  285.    # Work out the address of this list ...
  286.    $list = $LIST_DIR . '/' . $q->param('list');
  287.    $listaddress = &this_listaddress;
  288.  
  289.    
  290.    # Get a list of subscribers from ezmlm ...
  291.    @subscribers = sort `ezmlm-list $list`;
  292.    
  293.    # Print out a form of options ...
  294.    $q->delete('state');                     
  295.    print '<H2 ALIGN=center>Subscribers to ' . $q->param('list') . " ($listaddress)</H2><HR ALIGN=center WIDTH=25%>";
  296.    print $q->startform;
  297.    print '<CENTER><TABLE ALIGN="center" CELLPADDING="10"><TR><TD ROWSPAN="2" VALIGN="top" ALIGN="center">';
  298.    print $q->hidden(-name=>'state', -default=>'edit');
  299.    print $q->hidden(-name=>'list', -default=>$q->param('list'));
  300.    print $q->scrolling_list(-name=>'delsubscriber', -values=>\@subscribers) if defined(@subscribers);
  301.    print '</TD><TD VALIGN="top" ALIGN="left">';
  302.    print $q->submit(-name=>'action', -value=>'[Delete Address]'), '<P>' if defined(@subscribers);
  303.    print $q->textfield(-name=>'addsubscriber', -size=>'40'), '<BR>';
  304.    print $q->submit(-name=>'action', -value=>'[Add Address]'), '<P>';
  305.    print $q->submit(-name=>'action', -value=>'[Moderators]'), ' ' if (-e "$list/modpost" || -e "$list/modsub" || -e "$list/remote");
  306.    print $q->submit(-name=>'action', -value=>'[Blacklist]'), ' ' if (-e "$list/blacklist");
  307.    print $q->submit(-name=>'action', -value=>'[Digest]') if (-e "$list/digest");
  308.    print '<P>';
  309.    print $q->submit(-name=>'action', -value=>'[Configuration]'), ' ' if (-e "$list/config");
  310.    print $q->submit(-name=>'action', -value=>'[Create Config]'), ' ' if (!-e "$list/config");
  311.    print $q->submit(-name=>'action', -value=>'[Select List]');
  312.    print '</TD></TR><TR><TD> </TD></TR></TABLE></CENTER>';
  313.    print $q->endform;          
  314.  
  315. }
  316.  
  317. # ------------------------------------------------------------------------
  318.  
  319. sub delete_list {
  320.    # Delete a list ...
  321.  
  322.    # Fixes a bug from the previous version ... when the .qmail file has a
  323.    # different name to the list. We use outlocal to handle vhosts ...
  324.    my ($list, $listaddress, $listadd); $list = $LIST_DIR . '/' . $q->param('list');
  325.    if (open(OUTLOCAL, "<$list/outlocal")) {
  326.       chomp($listadd = <OUTLOCAL>);
  327.       close OUTLOCAL;
  328.    } else {
  329.       $listadd = $q->param('list');
  330.    }
  331.    $listaddress = $1 if ($listadd =~ /-?(\w+)$/);
  332.    
  333.    if ($UNSAFE_RM == 0) {
  334.       # This doesn't actually delete anything ... It just moves them so that
  335.       # they don't show up. That way they can always be recovered by a helpful
  336.       # sysadmin should he be in the mood :)
  337.       my ($newfile); $newfile = $LIST_DIR . '/.' . $q->param('list'); 
  338.       rename $list, $newfile || die "Unable to rename list: $!";
  339.       mkdir "$HOME_DIR/deleted.qmail", 0700 if(!-e "$HOME_DIR/deleted.qmail");
  340.       system "mv $HOME_DIR/.qmail-$listaddress $HOME_DIR/deleted.qmail" || die "Unable to move .qmail files: $!"; 
  341.       system "mv $HOME_DIR/.qmail-$listaddress-* $HOME_DIR/deleted.qmail" || die "Unable to move .qmail files: $!"; 
  342.       warn "List '$list' moved (deleted)";   
  343.    } else {
  344.       # This, however, does DELETE the list. I don't like the idea, but I was
  345.       # asked to include support for it so ...
  346.       system "rm -rf $LIST_DIR/" . $q->param('list') || die "Unable to delete list: $!";
  347.       system "rm -f $HOME_DIR/.qmail-$listaddress-*" || die "Unable to delete .qmail files: $!";
  348.       unlink "$HOME_DIR/.qmail-$listaddress" || die "Unable to delete .qmail files $!";
  349.       warn "List '$list' deleted";
  350.    }   
  351. }
  352.  
  353. # ------------------------------------------------------------------------
  354.  
  355. sub untaint {
  356.    # Go through all the CGI input and make sure it is not tainted. Log any
  357.    # tainted data that we come accross ... See the perlsec(1) man page ...
  358.  
  359.    my (@params, $i);
  360.    @params = $q->param;
  361.    
  362.    foreach $i (0 .. $#params) {
  363.       if ($q->param($params[$i]) =~ /^([-\@\w.\/\[\] ]+)$/) {
  364.          $q->param(-name=>$params[$i], -values=>$1);
  365.       } else { warn "Tainted input in '$params[$i]': " . $q->param($params[$i]); }
  366.    } 
  367. }
  368.  
  369. # ------------------------------------------------------------------------
  370.  
  371. sub add_address {
  372.    # Add an address to a list ..
  373.  
  374.    my ($address, $list); ($list) = @_;
  375.    return if ($q->param('addsubscriber') eq '');
  376.    $address = $q->param('addsubscriber');
  377.    $address .= $DEFAULT_HOST if ($q->param('addsubscriber') =~ /\@$/);
  378.    system "ezmlm-sub $list $address" || die "Unable to subscribe to list $list: $!";
  379.    $q->delete('addsubscriber');
  380. }
  381.  
  382. # ------------------------------------------------------------------------
  383.  
  384. sub delete_address {
  385.    # Delete an address from a list ...
  386.   
  387.    my ($list); ($list) = @_;
  388.    return if ($q->param('delsubscriber') eq '');
  389.    system "ezmlm-unsub $list " . $q->param('delsubscriber') || die "Unable to unsubscribe from list $list: $!";
  390.    $q->delete('delsubscriber');
  391. }
  392.  
  393. # ------------------------------------------------------------------------
  394.  
  395. sub moderators {
  396.    # Deal with list moderatos ....
  397.  
  398.    my ($i, $list, $listaddress, @subscribers, $moderated);
  399.    
  400.    # Work out the address of this list ...
  401.    $list = $LIST_DIR . '/' . $q->param('list');
  402.    $listaddress = &this_listaddress;
  403.  
  404.    # Lets know what is moderated :)
  405.    $moderated = '[Posting] ' if(-e ("$list/modpost"));
  406.    $moderated .= '[Subscription] ' if(-e ("$list/modsub"));
  407.    $moderated .= '[Remote Admin]' if(-e ("$list/remote"));
  408.  
  409.  
  410.    # Get a list of moderators from ezmlm ...
  411.    @subscribers = sort `ezmlm-list $list/mod`;
  412.    
  413.    # Print out a form of options ...
  414.    $q->delete('state');                     
  415.    print "<H2 ALIGN=center>Moderators for $listaddress</H2><HR ALIGN=center WIDTH=25%>";
  416.    print "<CENTER>$moderated</CENTER><P>";
  417.    print $q->startform;
  418.    print '<CENTER><TABLE ALIGN="center" CELLPADDING="10"><TR><TD ROWSPAN="2" VALIGN="top" ALIGN="center">';
  419.    print $q->hidden(-name=>'state', -default=>'moderators');
  420.    print $q->hidden(-name=>'list', -default=>$q->param('list')), "\n";
  421.    print $q->scrolling_list(-name=>'delsubscriber', -values=>\@subscribers) if defined(@subscribers);
  422.    print '</TD></TR><TR><TD VALIGN="top" ALIGN="left">';
  423.    print $q->submit(-name=>'action', -value=>'[Delete Address]'), '<P>' if defined(@subscribers);
  424.    print $q->textfield(-name=>'addsubscriber', -size=>'40'), '<BR>';
  425.    print $q->submit(-name=>'action', -value=>'[Add Address]'), '<P>';
  426.    print $q->submit(-name=>'action', -value=>'[Subscribers]');
  427.    print '</TD></TR><TR><TD> </TD></TR></TABLE></CENTER>';
  428.    print $q->endform;          
  429.  
  430. }
  431.  
  432. # ------------------------------------------------------------------------
  433.  
  434. sub allow_create_list {
  435.    # Let the user select options for list creation ...
  436.    
  437.    my($username, $hostname, %labels, $j);
  438.    
  439.    # Work out if this user has a virtual host and set input accordingly ...
  440.    open(VD, "<$VIRTUAL_DOMAINS") || warn "Can't read virtual domains file: $!";
  441.    while(<VD>) {
  442.       last if(($hostname) = /(.+?):$USER/);
  443.    }
  444.    close VD;
  445.    
  446.    if(!defined($hostname)) {
  447.       $username = "$USER-";
  448.       $hostname = $DEFAULT_HOST;
  449.    }
  450.                                     
  451.    # Print a form of options ...
  452.    $q->delete_all;
  453.    print '<H2 ALIGN=CENTER>Create A New List</H2><HR ALIGN=center WIDTH=25%>';
  454.    print $q->startform;
  455.    print $q->hidden(-name=>'state', -value=>'create');
  456.    print '<BIG><STRONG>List name: </STRONG></BIG>', $q->textfield(-name=>'list', -size=>'20'), '<P>';
  457.    print '<BIG><STRONG>List address: </STRONG></BIG>', $q->textfield(-name=>'inlocal', -default=>$username, -size=>'10');
  458.    print ' <BIG><STRONG>@</STRONG></BIG> ', $q->textfield(-name=>'inhost', -default=>$hostname, -size=>'30'), '<P>';
  459.    
  460.    # The labels to display against various ezmlm-make command line switches ...
  461.    %labels = (
  462.       a => 'Archived', d => 'Digest', f => 'Prefix', i => 'Indexed',
  463.       l => 'Subscriber List', m => 'Moderated Posting', n => 'Text Editing',
  464.       p => 'Public', r => 'Remote Admin', s => 'Moderated Subscription',
  465.       t => 'Trailer', u => 'User Posting Only', q => 'Service Request Address',
  466.       k => 'Blacklist', g => 'Guard Archive', 5 => 'Forward to Owner'
  467.    );
  468.  
  469.    # Not the best way to do it ... see below :(
  470.    print '<P><BIG><STRONG>List Options:</STRONG></BIG><TABLE BORDER="0" CELLPADDING="3"><TR><TD>';
  471.    foreach $i ('a','d','f','g','i','k','l','m','n','p','q','r','s','t','u','5') {
  472.       if ($DEFAULT_OPTIONS =~ /$i/) {
  473.          print $q->checkbox(-name=>$i, -value=>$i, -label=>$labels{$i}, -on=>'1');
  474.       } else {
  475.          print $q->checkbox(-name=>$i, -value=>$i, -label=>$labels{$i});
  476.       }
  477.       print '</TD>'; $j++;
  478.       if ($j >= 3) {
  479.          $j = 0; print '</TR><TR>';
  480.       }
  481.       print '<TD>';
  482.    }
  483.    print '</TD></TR></TABLE>';
  484.  
  485.    
  486. #  For some reason CGI.pm doesn't parse this properly ... any ideas why??
  487. #  print $q->checkbox_group(-name=>'options',
  488. #         -values=>['a','d','f','g','i','k','l','m','n','p','q','r','s','t','u'],
  489. #         -default=>['a','i','p'], -labels=>\%labels, -columns=>'3');
  490.    
  491.    print '<P>', $q->submit(-name=>'action', -value=>'[Create List]'), ' ';
  492.    print $q->reset(-value=>'[Reset Form]'), ' ';
  493.    print $q->submit(-name=>'action', -value=>'[Cancel]');
  494.    print $q->endform;  
  495.    
  496. }
  497.  
  498. # ------------------------------------------------------------------------
  499.  
  500. sub create_list {
  501.    # Create a lista acording to user selections ...
  502.    
  503.    # Check the list directory exists and create if necessary ...
  504.    if(!-e $LIST_DIR) {
  505.       die "Unable to create directory: $!" unless mkdir $LIST_DIR, 0700;
  506.    }
  507.    
  508.    my ($qmail, $list, $options, $i);
  509.    
  510.    # Some taint checking ...
  511.    $qmail = $1 if $q->param('inlocal') =~ /-?([\w.+:]+)$/;
  512.    $list = $q->param('list'); $list =~ s/ /_/; # In case some git tries to put a space in the file name
  513.  
  514.    # Sanity Checks ...
  515.    return 1 if ($list eq '' || $qmail eq '');
  516.    if(-e ("$LIST_DIR/$list/lock") || -e ("$HOME_DIR/.qmail-$qmail")) {
  517.       print "<H1 ALIGN=CENTER>List '$list' already exists :(</H1>";
  518.       return 1;
  519.    }
  520.   
  521.    # Work out the command line options
  522.    foreach $i ('a','d','f','g','i','k','l','m','n','p','q','r','s','t','u') {
  523.       if (defined($q->param($i))) {
  524.          $options .= $i;
  525.       } else {
  526.          $options .= uc($i);
  527.       }
  528.    }
  529.    
  530.    # Set owner if required ...
  531.    if (defined($q->param('5'))) {
  532.       $options .= "5 \'$USER\@$DEFAULT_HOST\'";
  533.    }
  534.    
  535.    # Actually make the list ...
  536.    system "ezmlm-make", "-x$options", "$LIST_DIR/$list", "$HOME_DIR/.qmail-$qmail", "$q->param('inlocal')", "$q->param('inhost')" ||
  537.       die "List creation failed";
  538.    
  539.    # Handle the change to inlocal for virtual hosts ...
  540.    if($q->param('inhost') ne $DEFAULT_HOST) {
  541.       open(INLOCAL, ">$LIST_DIR/$list/inlocal") || die "Error reading inlocal: $!";
  542.       print INLOCAL "$USER-" . $q->param('inlocal') . "\n";
  543.       close INLOCAL;
  544.    }
  545.  
  546.    return 0;
  547. }
  548.  
  549. # ------------------------------------------------------------------------
  550.  
  551. sub list_config {
  552.    # Allow user to alter the list configuration ...
  553.  
  554.    my ($list, $listaddress, $listname, $options, %labels);
  555.    my ($headeradd, $headerremove, $mimeremove, $prefix, $j);
  556.    
  557.    # Store some variables before we delete them ...
  558.    $list = $LIST_DIR . '/' . $q->param('list');
  559.    $listname = $q->param('list');
  560.    $listaddress = &this_listaddress;
  561.                                    
  562.    # Print a form of options ...
  563.    $q->delete_all;
  564.    print '<H2 ALIGN="center">Edit List Configuration</H2><HR ALIGN=center WIDTH=25%>';
  565.    print $q->startform;
  566.    print $q->hidden(-name=>'state', -value=>'configuration');
  567.    print $q->hidden(-name=>'list', -value=>$listname);
  568.    print "<BIG><STRONG>List name: <EM>$listname</EM><BR>";
  569.    print "List address: <EM>$listaddress</EM></STRONG></BIG><P>";
  570.    print '<BIG><STRONG>List Options</BIG></STRONG><BR>';
  571.    
  572.    # The labels to display against various ezmlm-make command line switches ...
  573.    %labels = (
  574.       a => 'Archived', d => 'Digest', f => 'Prefix', i => 'Indexed',
  575.       l => 'Subscriber List', m => 'Moderated Posting', n => 'Text Editing',
  576.       p => 'Public', r => 'Remote Admin', s => 'Moderated Subscription',
  577.       t => 'Trailer', u => 'User Posting Only',q => 'Service Request Address',
  578.       k => 'Blacklist', g => 'Guard Archive', 5 => 'Forward to Owner'
  579.    );
  580.    
  581.    # Work out what options are already selected ...
  582.    open(CONFIG, "<$list/config") || die "Unable to read DIR/config: $!. This probably means that your mailing list was created with an older version of ezmlm. Try recreating the list with the newer version. Died";
  583.    while(<CONFIG>) {
  584.       next unless /^F:-(\w+)/;
  585.       $options = $1; last;
  586.    }
  587.    close CONFIG;
  588.  
  589.    # Print a list of options, selecting the ones that apply to this list ...
  590.    print '<TABLE BORDER="0" CELLPADDING="3"><TR><TD>';
  591.    foreach $i ('a','d','f','g','i','k','l','m','n','p','q','r','s','t','u','5') {
  592.       if ($options =~ /$i/) {
  593.          print $q->checkbox(-name=>$i, -value=>$i, -label=>$labels{$i}, -on=>'1');
  594.       } else {
  595.          print $q->checkbox(-name=>$i, -value=>$i, -label=>$labels{$i});
  596.       }
  597.       print '</TD>'; $j++;
  598.       if ($j >= 3) {
  599.          $j = 0; print '</TR><TR>';
  600.       }
  601.       print '<TD>';
  602.    }
  603.    print '</TD></TR></TABLE>';
  604.  
  605.    # Get the contents of the headeradd, headerremove, mimeremove and prefix files
  606.    open(ADD, "<$list/headeradd") || die "Unable to read DIR/headeradd: $!";
  607.    open(DEL, "<$list/headerremove") || die "Unable to read DIR/headerremove: $!";
  608.    open(MIME, "<$list/mimeremove"); # no die since it may not exist.
  609.    open(PREFIX, "<$list/prefix"); # ditto mimeremove
  610.    while(<ADD>) { $headeradd .= $_; }
  611.    while(<DEL>) { $headerremove .= $_; }
  612.    while(<MIME>) { $mimeremove .= $_; }
  613.    while(<PREFIX>) { $prefix .= $_; }
  614.    close ADD, DEL, MIME, PREFIX;
  615.  
  616.    print '<P><BIG><STRONG>Subject prefex for outgoing messages: </STRONG></BIG>', $q->textfield(-name=>'prefix', -default=>$prefix, -size=>12) if defined($prefix);
  617.    print '<P><BIG><STRONG>Headers to strip from all outgoing mail:</BIG></STRONG><BR>', $q->textarea(-name=>'headerremove', -default=>$headerremove, -rows=>5, -columns=>70);
  618.    print '<P><BIG><STRONG>Headers to add to all outgoing mail:</BIG></STRONG><BR>', $q->textarea(-name=>'headeradd', -default=>$headeradd, -rows=>5, -columns=>70);
  619.    print '<P><BIG><STRONG>Mime types to strip from all outgoing mail:</BIG></STRONG><BR>', $q->textarea(-name=>'mimeremove', -default=>$mimeremove, -rows=>5, -columns=>70) if defined($mimeremove);
  620.    
  621.    print '<P>', $q->submit(-name=>'action', -value=>'[Update Configuration]'), ' ';
  622.    print $q->reset(-value=>'[Reset Form]'), ' ';
  623.    print $q->submit(-name=>'action', -value=>'[Cancel]'), ' ';   
  624.    print $q->submit(-name=>'action', -value=>'[Edit Texts]');
  625.    print $q->endform;  
  626.  
  627. }
  628.  
  629. # ------------------------------------------------------------------------
  630.  
  631. sub update_config {
  632.    # Save the new user entered config ...
  633.    
  634.    my ($list, $options, $i);
  635.    $list = $LIST_DIR . '/' . $q->param('list');
  636.  
  637.    # Small sanity check ...
  638.    if(!-e ("$list/lock")) {
  639.       print "<H1 ALIGN=CENTER>Warning: Errors detected in list '$list'</H1>";
  640.    }
  641.   
  642.    # Work out the command line options ...
  643.    foreach $i ('a','d','f','g','i','k','l','m','n','p','q','r','s','t','u') {
  644.       if (defined($q->param($i))) {
  645.          $options .= $i;
  646.       } else {
  647.          $options .= uc($i);
  648.       }
  649.    }
  650.  
  651.    # Sort out owner
  652.    if (defined($q->param('5'))) {
  653.       $options .= "5 \'$USER\@$DEFAULT_HOST\'";
  654.    }
  655.    
  656.    # Actually update the list ...
  657.    system "ezmlm-make",  "-ex$options", "$list" || die "List update failed";
  658.  
  659.    # Update headeradd, headerremove, mimeremove and prefix ...
  660.    open(ADD, ">$list/headeradd") || die "Unable to write DIR/headeradd: $!";
  661.    open(DEL, ">$list/headerremove") || die "Unable to write DIR/headerremove: $!";
  662.    (open(MIME, ">$list/mimeremove") || die "Unable to write DIR/mimeremove") if defined($q->param('mimeremove'));
  663.    (open(PREFIX, ">$list/prefix") || die "Unable to write DIR/prefix") if defined($q->param('prefix'));
  664.    print ADD $q->param('headeradd');
  665.    print DEL $q->param('headerremove');
  666.    print MIME $q->param('mimeremove') if defined($q->param('mimeremove'));
  667.    print PREFIX $q->param('prefix') if defined($q->param('prefix'));
  668.    close ADD, DEL, MIME, PREFIX;
  669.    
  670. }
  671.  
  672. # ------------------------------------------------------------------------
  673.  
  674. sub this_listaddress {
  675.    # Work out the address of this list ... Used often so put in its own subroutine ...
  676.    
  677.    my ($list, $listaddress);
  678.    $list = $LIST_DIR . '/' . $q->param('list');
  679.    open(OUTLOCAL, "<$list/outlocal") || die "Unable to read DIR/outlocal: $!";
  680.    chomp($listaddress = <OUTLOCAL>);
  681.    $listaddress .= '@';
  682.    open(OUTHOST, "<$list/outhost") || die "Unable to read DIR/outhost: $!";
  683.    chomp($listaddress .= <OUTHOST>);
  684.    close OUTHOST, OUTLOCAL;
  685.    return $listaddress;
  686. }
  687.  
  688. # ------------------------------------------------------------------------
  689.  
  690. sub blacklist {
  691.    # Update the blacklist ...
  692.  
  693.    my ($i, $list, $listaddress, @subscribers);
  694.    
  695.    # Work out the address of this list ...
  696.    $list = $LIST_DIR . '/' . $q->param('list');
  697.    $listaddress = &this_listaddress;
  698.  
  699.    # Get blacklist from ezmlm ...
  700.    @subscribers = sort `ezmlm-list $list/blacklist`;
  701.    
  702.    # Print out a form of options ...
  703.    $q->delete('state');                     
  704.    print "<H2 ALIGN=center>Blacklist for $listaddress</H2><HR ALIGN=center WIDTH=25%><P>";
  705.    print $q->startform;
  706.    print '<CENTER><TABLE ALIGN="center" BORDER="0" CELLPADDING="10"><TR><TD ROWSPAN="3" VALIGN="top" ALIGN="center">';
  707.    print $q->hidden(-name=>'state', -default=>'blacklist');
  708.    print $q->hidden(-name=>'list', -default=>$q->param('list')), "\n";
  709.    print $q->scrolling_list(-name=>'delsubscriber', -values=>\@subscribers) if defined(@subscribers);
  710.    print '</TD><TD VALIGN="top" ALIGN="left">';
  711.    print $q->submit(-name=>'action', -value=>'[Delete Address]'), '<P>' if defined(@subscribers);
  712.    print $q->textfield(-name=>'addsubscriber', -size=>'40'), '<BR>';
  713.    print $q->submit(-name=>'action', -value=>'[Add Address]'), '<P>';
  714.    print $q->submit(-name=>'action', -value=>'[Subscribers]');
  715.    print '</TD></TR><TR><TD> </TD></TR></TABLE></CENTER>';
  716.    print $q->endform;          
  717.  
  718. }
  719.  
  720. # ------------------------------------------------------------------------
  721.  
  722. sub list_text {
  723.    # Show a listing of what is in DIR/text ...
  724.    
  725.    my(@files, $list);
  726.    $list = $LIST_DIR . '/' . $q->param('list');
  727.  
  728.    # Read the list directory for text ...
  729.    opendir DIR, "$list/text" || die "Unable to read DIR/text: $!";
  730.    @files = grep !/^\./, readdir DIR; 
  731.    closedir DIR;
  732.  
  733.    # Print a form ...
  734.    $q->delete('state');
  735.    print $q->startform;
  736.    print $q->hidden(-name=>'state', -default=>'list_text');
  737.    print $q->hidden(-name=>'list', -default=>$q->param('list'));
  738.    print '<CENTER><TABLE BORDER="0" CELLPADDING="10" ALIGN="center"><TR><TD ALIGN="center" VALIGN="top" ROWSPAN="2">';
  739.    print $q->scrolling_list(-name=>'file', -values=>\@files);
  740.    print '</TD><TD ALIGN="center" VALIGN="top">';
  741.    print $q->submit(-name=>'action', -value=>'[Edit File]'), ' ';
  742.    print $q->submit(-name=>'action', -value=>'[Cancel]');
  743.    print << 'EOM';
  744.       <P>
  745.       The box on the left contains a list of files available in the<BR>
  746.       DIR/text directory. Thes files are sent out in response to<BR>
  747.       specfic user request, or as part of all outgoing messages<P>
  748.       To edit a file, select its name from the box. Then click on the<BR>
  749.       [Edit File] button.<P>
  750.       Press [Cancel] when you have finished editing. 
  751.       </TD></TR><TR><TD> </TD></TR></TABLE></CENTER>
  752. EOM
  753.    print $q->endform;
  754.    
  755. }
  756.  
  757. # ------------------------------------------------------------------------
  758.  
  759. sub edit_text {
  760.    # Allow user to edit the contents of DIR/text ...
  761.  
  762.    my ($file, $content);
  763.    $file = $LIST_DIR . '/' . $q->param('list') . '/text/' . $q->param('file');
  764.  
  765.    # Read the current file ...
  766.    open (FILE, "<$file") || die "Unable to read $file: $!";
  767.    while (<FILE>) { $content .= $_; }
  768.    close FILE;
  769.  
  770.    # Print a form ...
  771.    $q->delete('state');
  772.    print '<H2 ALIGN="CENTER">Editing file: ' . $q->param('file') . '</H2>';
  773.    print '<CENTER><TABLE ALIGN="center" CELLPADDING="5"><TR><TD VALIGN="top" ROWSPAN="2">';
  774.    print $q->startform;
  775.    print $q->hidden(-name=>'state', -default=>'edit_text');
  776.    print $q->hidden(-name=>'list', -default=>$q->param('list'));
  777.    print $q->hidden(-name=>'file', -default=>$q->param('file'));
  778.    print $q->textarea(-name=>'content', -default=>$content, -rows=>'25', -columns=>'72');
  779.    print '</TD><TD VALIGN="top" ALIGN="left">';
  780.    print $q->submit(-name=>'action', -value=>'[Save File]'), ' ';
  781.    print $q->reset(-value=>'[Reset]'), ' ';
  782.    print $q->submit(-name=>'action', -value=>'[Cancel]');
  783.    print << 'EOM';
  784.       <P><BIG><STRONG>ezmlm-manage</STRONG></BIG><BR>
  785.       <TT><STRONG><#l#></STRONG></TT> The list name<BR>
  786.       <TT><STRONG><#A#></STRONG></TT> The subscription address<BR>
  787.       <TT><STRONG><#R#></STRONG></TT> The address a subscriber must reply to
  788.       <P><BIG><STRONG>ezmlm-store</STRONG></BIG><BR>
  789.       <TT><STRONG><#l#></STRONG></TT> The list name<BR>
  790.       <TT><STRONG><#A#></STRONG></TT> The acceptance address<BR>
  791.       <TT><STRONG><#R#></STRONG></TT> The rejection address
  792.       </UL>
  793. EOM
  794.    print $q->endform;
  795.    print '</TD></TR><TR><TD> <TD></TR></TABLE><CENTER>'
  796.  
  797. }
  798.    
  799. # ------------------------------------------------------------------------
  800.  
  801. sub save_text {
  802.    # Save new text in DIR/text ...
  803.  
  804.    my ($file);
  805.    $file = $LIST_DIR . '/' . $q->param('list') . '/text/' . $q->param('file');
  806.    open (FILE, ">$file") || die "Unable to write $file: $!";
  807.    print FILE $q->param('content');
  808.    close FILE;
  809.    
  810. }   
  811.  
  812. # ------------------------------------------------------------------------
  813.  
  814. sub digest {
  815.    # Allow user to edit digest subscribers ...
  816.  
  817.    my ($i, $list, $listaddress, @subscribers);
  818.    
  819.    # Work out the address of this list ...
  820.    $list = $LIST_DIR . '/' . $q->param('list');
  821.    $listaddress = &this_listaddress;
  822.  
  823.    # Get digest subscribers from ezmlm ...
  824.    @subscribers = sort `ezmlm-list $list/digest`;
  825.    
  826.    # Print out a form of options ...
  827.    $q->delete('state');                     
  828.    print "<H2 ALIGN=center>Digest Subscribers for $listaddress</H2><HR ALIGN=center WIDTH=25%><P>";
  829.    print $q->startform;
  830.    print '<CENTER><TABLE ALIGN="center" BORDER="0" CELLPADDING="10"><TR><TD ROWSPAN="3" VALIGN="top" ALIGN="center">';
  831.    print $q->hidden(-name=>'state', -default=>'digest');
  832.    print $q->hidden(-name=>'list', -default=>$q->param('list')), "\n";
  833.    print $q->scrolling_list(-name=>'delsubscriber', -values=>\@subscribers) if defined(@subscribers);
  834.    print '</TD></TR><TR><TD VALIGN="top" ALIGN="left">';
  835.    print $q->submit(-name=>'action', -value=>'[Delete Address]'), '<P>' if defined(@subscribers);
  836.    print $q->textfield(-name=>'addsubscriber', -size=>'40'), '<BR>';
  837.    print $q->submit(-name=>'action', -value=>'[Add Address]'), '<P>';
  838.    print $q->submit(-name=>'action', -value=>'[Subscribers]');
  839.    print '</TD></TR><TR><TD> </TD></TR></TABLE></CENTER>';
  840.    print $q->endform;          
  841.  
  842. }
  843.  
  844. # ------------------------------------------------------------------------
  845.  
  846. sub create_config {
  847.    # Try and write a config file for the list.
  848.    # This is a HACK to allow owners of older lists to edit the list config :)
  849.    
  850.    my ($options, $list, $outlocal, $outhost); $list = $LIST_DIR . '/' . $q->param('list');
  851.  
  852.    $options = 'a' if (-e "$list/archived");
  853.    $options .= 'd' if (-e "$list/digest");
  854.    $options .= 'f' if (-e "$list/prefix");
  855.    # g
  856.    $options .= 'i' if (-e "$list/indexed");
  857.    $options .= 'k' if (-e "$list/blacklist");
  858.    # l
  859.    $options .= 'm' if (-e "$list/modpost");
  860.    # n
  861.    $options .= 'p' if (-e "$list/public");
  862.    # q
  863.    $options .= 'r' if (-e "$list/remote");
  864.    $options .= 's' if (-e "$list/modsub");
  865.    # t
  866.    # u
  867.  
  868.    open(OUTLOCAL, "<$list/outlocal") || die "Unable to read DIR/outlocal: $!";
  869.    chomp($outlocal = <OUTLOCAL>);
  870.    open(OUTHOST, "<$list/outhost") || die "Unable to read DIR/outhost: $!";
  871.    chomp($outhost .= <OUTHOST>);
  872.    close OUTHOST, OUTLOCAL;
  873.    
  874.    open(CONFIG, ">$list/config") || die "Unable to write config file";
  875.    print CONFIG "F:-$options\n";
  876.    print CONFIG "D:$lists\n";
  877.    print CONFIG "T:$HOME_DIR/.qmail-$outlocal\n";
  878.    print CONFIG "L:$outlocal\n";
  879.    print CONFIG "h:$outhost\n";
  880.    print CONFIG "C:\n";
  881.    print CONFIG "0:\n";
  882.    print CONFIG "1:\n";
  883.    print CONFIG "2:\n";
  884.    print CONFIG "3:\n";
  885.    print CONFIG "4:\n";
  886.    print CONFIG "5:\n";
  887.    print CONFIG "6:\n";
  888.    print CONFIG "7:\n";
  889.    print CONFIG "8:\n";
  890.    print CONFIG "9:\n";
  891.  
  892.    close CONFIG;
  893.  
  894.    print '<H2 ALIGN=CENTER>Config File Created</H2>';
  895.    print '<CENTER>Note that not all options may have been successfully determined</CENTER><P>';
  896.  
  897. }
  898.  
  899. # ------------------------------------------------------------------------
  900. # End of ezmlm-web.cgi v1.0
  901. # ------------------------------------------------------------------------
  902.