From 0a4b5d24845fb86bade3ab3c38a6202862d6caad Mon Sep 17 00:00:00 2001
From: Guilhem Moulin <guilhem.moulin@fripost.org>
Date: Sun, 23 Sep 2012 20:43:08 +0200
Subject: List creation via a Postfix local alias.

---
 cgi-bin/index.cgi     | 48 --------------------------
 cgi-bin/index.fcgi    | 47 +++++++++++++++++++++++++
 config.in             |  8 +++++
 default.in            | 15 ++++----
 misc/mklist/INSTALL   | 56 ++++++++++++++++++++++++++++++
 misc/mklist/README    | 17 +++++++++
 misc/mklist/mklist.pl | 95 +++++++++++++++++++++++++--------------------------
 7 files changed, 182 insertions(+), 104 deletions(-)
 delete mode 100755 cgi-bin/index.cgi
 create mode 100755 cgi-bin/index.fcgi
 create mode 100644 misc/mklist/INSTALL
 create mode 100644 misc/mklist/README

diff --git a/cgi-bin/index.cgi b/cgi-bin/index.cgi
deleted file mode 100755
index 5efa469..0000000
--- a/cgi-bin/index.cgi
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/perl
-
-use 5.010_000;
-use strict;
-use warnings;
-use utf8;
-
-=head1 NAME
-
-index.cgi -
-
-=cut
-
-use lib 'lib';
-use Fripost::Panel::Interface;
-
-# TODO: Try out Fast CGI
-#use CGI::Fast();
-#
-#while (my $q = new CGI::Fast){
-#   my $app = new WebApp(QUERY => $q);
-#   $app->run();
-#}
-
-my @config = 'default.in';
-push @config, 'config.in' if -f 'config.in';
-
-my $cgi = Fripost::Panel::Interface->new(
-    PARAMS => { cfg_file => [ @config ], format => 'equal' }
-);
-$cgi->run();
-
-=head1 AUTHOR
-
-Guilhem Moulin C<< <guilhem at fripost.org> >>
-
-=head1 COPYRIGHT
-
-Copyright 2012 Guilhem Moulin.
-
-=head1 LICENSE
-
-This program is free software; you can redistribute it and/or modify it
-under the same terms as perl itself.
-
-=cut
-
-__END__
diff --git a/cgi-bin/index.fcgi b/cgi-bin/index.fcgi
new file mode 100755
index 0000000..8e551d8
--- /dev/null
+++ b/cgi-bin/index.fcgi
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+
+use 5.010_000;
+use strict;
+use warnings;
+use utf8;
+
+=head1 NAME
+
+index.fcgi -
+
+=cut
+
+use CGI::Fast ();
+use File::Spec::Functions 'catfile';
+use lib 'lib';
+use Fripost::Panel::Interface;
+
+
+my $config_dir = '/etc/fripost-panel';
+my @config = catfile ('./', 'default.in');
+push @config, catfile ($config_dir, 'config.in') if -f catfile ($config_dir, 'config.in');
+
+while (my $q = CGI::Fast::->new){
+    my $cgi = Fripost::Panel::Interface::->new(
+        QUERY => $q,
+        PARAMS => { cfg_file => [ @config ], format => 'equal' }
+    );
+    $cgi->run();
+}
+
+=head1 AUTHOR
+
+Guilhem Moulin C<< <guilhem at fripost.org> >>
+
+=head1 COPYRIGHT
+
+Copyright 2012 Guilhem Moulin.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as perl itself.
+
+=cut
+
+__END__
diff --git a/config.in b/config.in
index 1a245f7..c2011d1 100644
--- a/config.in
+++ b/config.in
@@ -20,3 +20,11 @@ ldap_authcPW = panel
 
 # The minimum password length.
 password_min_length = 12
+
+# GnuPG private key and passphrase.
+gpg_private_key_id = ECFA6E43
+gpg_private_key_passphrase = xxxxxxxxxxxx
+
+# URL prefixes of the admin web interface for the list managers.
+listurl_mailman = http://smtp.fripost.org/cgi-bin/mailman/admin/
+listurl_schleuder = http://smtp.fripost.org/
diff --git a/default.in b/default.in
index ef32710..ec6a4c7 100644
--- a/default.in
+++ b/default.in
@@ -4,13 +4,8 @@
 # which overrides the below.
 
 
-
-# Non absolute paths are be relative to this directory.
-pwd = ./
-
-
 # Directory for templates
-tmpl_path = template/
+tmpl_path = ./template/
 
 
 # Session configuration
@@ -32,11 +27,15 @@ cgi-bin = /cgi-bin/
 
 # A timeout for idle connections, after which the user is automatically
 # logged out
-timeout = 30m
+timeout = 15m
 
 
 # The minimum password length.
 password_min_length = 8
 
 # LDAP configuration
-ldap_uri =ldap://127.0.0.1:389
+ldap_uri = ldap://127.0.0.1:389
+
+
+# GnuPG home
+gpghome = /etc/fripost-panel/gnupg/
diff --git a/misc/mklist/INSTALL b/misc/mklist/INSTALL
new file mode 100644
index 0000000..660bcf9
--- /dev/null
+++ b/misc/mklist/INSTALL
@@ -0,0 +1,56 @@
+sudo apt-get install libmail-gnupg-perl libmime-tools-perl libconfig-auto-perl
+
+Also, Fripost's schema (module Fripost::Schema) needs to be found in the
+library path.
+
+    addgroup mklist
+    adduser list mklist
+    adduser schleuder mklist
+    mkdir -m 0700 -p /etc/mklist/gnupg-{mailman,schleuder}
+    chown 'list:list' /etc/mklist/gnupg-mailman
+    chown 'schleuder:schleuder' /etc/mklist/gnupg-schleuder
+    cp ./config /etc/mklist/
+
+    mkdir /etc/postfix-lists/{mailman,schleuder}
+    chown list:list /etc/postfix-lists/mailman
+    chown schleuder:schleuder /etc/postfix-lists/schleuder
+
+To add a key:
+
+    ggp --homedir /etc/mklist/gnupg --import < /path/to/pubkey.pub
+
+for each list manager. For instance,
+
+    sudo -u www-data gpg --homedir /etc/fripost-panel/gnupg/ -a --export ECFA6E43 | sudo -u list gpg --homedir /etc/mklist/gnupg-mailman/ --import
+    sudo -u www-data gpg --homedir /etc/fripost-panel/gnupg/ -a --export ECFA6E43 | sudo -u schleuder gpg --homedir /etc/mklist/gnupg-schleuder/ --import
+
+(Don't forget to whitelist the key ID in the configuration file.)
+
+
+Postfix's lookup table (/etc/postfix-lists/transport_mklist)
+
+    mklist#fripost.org+mailman@lists.fripost.org    mklist-mailman:
+    mklist#fripost.org+schleuder@lists.fripost.org  mklist-schleuder:
+
+Postfix's master.cf
+
+    mklist-mailman unix  -  n       n       -       -       pipe
+      flags=FR directory=/opt/fripost-panel/ user=list:list
+      argv=/opt/fripost-panel/misc/mklist/mklist.pl mailman ${size}
+    
+    mklist-schleuder unix - n       n       -       -       pipe
+      flags=FR directory=/opt/fripost-panel/ user=schleuder:schleuder
+      argv=/opt/fripost-panel/misc/mklist/mklist.pl schleuder ${size}
+
+Postfix's main.cf
+    transport_maps = cdb:$config_directory/transport_mklist
+                     cdb:$config_directory/mailman/transport
+                     cdb:$config_directory/schleuder/transport
+
+    mklist-mailman_destination_concurrency_limit = 1
+    mklist-mailman_destination_recpipient_limit = 1
+    mklist-mailman_time_limit = 10m
+    
+    mklist-schleuder_destination_concurrency_limit = 1
+    mklist-schleuder_destination_recpipient_limit = 1
+    mklist-schleuder_time_limit = 2h
diff --git a/misc/mklist/README b/misc/mklist/README
new file mode 100644
index 0000000..0454713
--- /dev/null
+++ b/misc/mklist/README
@@ -0,0 +1,17 @@
+This is the script that should be used to finalize list creation.
+Living alongside with the list managers, Postfix should feed it (pipe)
+with emails to mklist+{mailman,schleuder}@lists.fripost.org .
+
+The email's body must have the following format:
+
+  listname
+  admin
+  password
+
+Also, the email should have a valid GPG-signature. The list of GPG
+fingerprints allowed to create lists can be found in the configuration
+file.
+
+Upon GPG-verification, the script orders the list manager to create a
+new list with the given credentials, and updates the LDAP directory
+(removes the 'pending' status and adds list commands entries).
diff --git a/misc/mklist/mklist.pl b/misc/mklist/mklist.pl
index 105178a..37c05e5 100755
--- a/misc/mklist/mklist.pl
+++ b/misc/mklist/mklist.pl
@@ -9,11 +9,11 @@ our $VERSION = '0.01';
 
 =head1 NAME
 
-mklist.pl - Create a new list 
+mklist.pl - Create a new list
 
 =head1 SYNOPSIS
 
-B<mklist.pl> {B<mailman>|B<schleuder>} < email
+B<mklist.pl> {B<mailman>|B<schleuder>} [size] < email
 
 =cut
 
@@ -21,23 +21,27 @@ B<mklist.pl> {B<mailman>|B<schleuder>} < email
 #######################################################################
 #
 
-use Mail::GnuPG;
-use MIME::Parser;
-use File::Spec::Functions;
+use File::Spec::Functions qw/catfile catdir/;
 use Fcntl qw/:flock SEEK_END/;
 use POSIX qw/setuid setgid/;
 use Pod::Usage;
 use Config::Auto;
-use lib '../../../panel/lib';
+use lib 'lib';
+use Mail::GnuPG;
+use MIME::Parser;
 use Fripost::Schema;
 
 my $transport = shift;
 pod2usage(2) unless defined $transport and
                     grep { $transport eq $_}
                          qw/mailman schleuder/;
-                         use Cwd;
+my $configdir = catdir('/','etc','mklist');
+my $config = Config::Auto::parse( catfile($configdir, 'config'),
+                                  format => "equal" );
 
-my $config = Config::Auto::parse( './config', format => "equal" );
+my $size = shift;
+die "Email size $size is bigger than the maximum authorized (".$config->{max_size}.").\n"
+    if defined $config->{max_size} and defined $size and $size > $config->{max_size};
 
 # Drop root privileges
 unless ($<) {
@@ -66,7 +70,7 @@ my ($list, $owner, $password);
              $msg->mime_type eq 'multipart/signed' and
              $msg->parts;
 
-    my $gpg = Mail::GnuPG::->new;
+    my $gpg = Mail::GnuPG::->new( keydir => catdir($configdir,'gnupg-'.$transport) );
     die "Error: The message is not GPG/MIME signed.\n"
         unless $gpg->is_signed( $msg );
 
@@ -78,7 +82,7 @@ my ($list, $owner, $password);
 
     die "Error: 0x$key ($addr) is not authorized to create lists.\n"
         unless grep { $key eq $_ }
-                    (ref $config->{authorized_pubkeys} eq 'ARRAY' ? 
+                    (ref $config->{authorized_pubkeys} eq 'ARRAY' ?
                          @{$config->{authorized_pubkeys}} :
                          $config->{authorized_pubkeys}
                     );
@@ -100,7 +104,7 @@ my ($l,$d) = split /\@/, $list, 2;
 #######################################################################
 #
 # Ensure that the root DN has been created, otherwise doing the below is
-# useless. 
+# useless.
 
 {
     # It's pointless to have a global variable here, since after the list
@@ -116,34 +120,32 @@ my ($l,$d) = split /\@/, $list, 2;
 #
 # Create the list
 
-#if ($transport eq 'mailman') {
-#    system ( '/var/lib/mailman/bin/newlist', '-q'
-#           , '-u', 'smtp.fripost.org' # TODO: that should be $mydestination
-#           , $list
-#           , $owner 
-#           , $password
-#    );
-#    die "newlist exited with status $?\n" if $?;
-#}
-#
-#elsif ($transport eq 'schleuder') {
-#    system ( '/usr/bin/schleuder-newlist'
-#           , $list
-#           , '-email', $list
-#           , '-realname', $l
-#           , '-nointeractive'
-#           , '-adminaddress', $owner
-#           , '-initmember', $owner
-#           , '-initmemberkey', # TODO: that needs to be a file
-#    );
-#    die "schleuder-newlist exited with status $?\n" if $?;
-#    system ( '/usr/bin/ruby', '/opt/webschleuder/contrib/enable_webschleuder.rb'
-#           , $list
-#           , $password
-#    );
-#    # TODO: try not to use the password in the command argument
-#    die "enable_webschleuder exited with status $?\n" if $?;
-#}
+if ($transport eq 'mailman') {
+    system ( '/var/lib/mailman/bin/newlist', '-q'
+           , '-u', 'smtp.fripost.org' # TODO: that should be $mydestination
+           , $list
+           , $owner
+           , $password
+    );
+    die "newlist died with status $?\n" if $?;
+}
+
+elsif ($transport eq 'schleuder') {
+    system ( '/usr/bin/schleuder-newlist'
+           , $list
+           , '-email', $list
+           , '-realname', $l
+           , '-nointeractive'
+           , '-adminaddress', $owner
+    );
+    die "schleuder-newlist died with status $?\n" if $?;
+    my $pid = open PW, '|-', '/opt/webschleuder/contrib/enable_webschleuder.rb', $list
+              or die "Cannot open: $!";
+    print PW $password or die "Cannot print: $!";
+    close PW;
+    my $r = $? >> 8;
+    die "enable_webschleuder died with status $r\n" if $r;
+}
 
 # List the commands that are to be added to the LDAP directory and the lookup table.
 my @cmds = $transport eq 'mailman' ? qw/admin bounces confirm join leave owner request subscribe unsubscribe/ :
@@ -167,9 +169,7 @@ my @cmds = $transport eq 'mailman' ? qw/admin bounces confirm join leave owner r
 # Create/update the Postfix lookup table.
 
 {
-    
-    my $db = File::Spec::Functions::catfile( $config->{postfix_data_dir},
-                                             'transport_'.$transport );
+    my $db = catfile( $config->{postfix_data_dir}, $transport, 'transport' );
     my $new = not (-e $db);
     open my $fh, '>>', $db or die "Cannot open $db: $!";
     flock $fh, LOCK_EX or die "Cannot lock $db: $!";
@@ -178,17 +178,16 @@ my @cmds = $transport eq 'mailman' ? qw/admin bounces confirm join leave owner r
         print $fh "# Do not modify this file! Use $0 to create lists instead.\n"
             or die "Cannot print: $!";
     }
-    
-    
-    print $fh  "\n# ".$list."  -  ".localtime."\n";
+
+    print $fh  "\n# ".$list."  -  ".(localtime)."\n";
     &print_transport($fh, undef);
     foreach (@cmds) { &print_transport($fh, $_); }
-    
+
     close $fh or die "Cannot close $db: $!";
-    
+
     # Compile the lookup table (Postfix takes care of the race condition here).
     system ('/usr/sbin/postmap', '-c', $config->{postfix_config_dir}, $db);
-    die "postmap exited with status $?\n" if $?;
+    die "postmap died with status $?\n" if $?;
 }
 
 
-- 
cgit v1.2.3