]> git.ipfire.org Git - thirdparty/mlmmj.git/commitdiff
added digest support
authormortenp <none@none>
Mon, 13 Sep 2004 23:01:32 +0000 (09:01 +1000)
committermortenp <none@none>
Mon, 13 Sep 2004 23:01:32 +0000 (09:01 +1000)
23 files changed:
README
TUNABLES
include/mlmmj-maintd.h
include/mlmmj-sub.h
include/mlmmj-unsub.h
include/mlmmj.h
include/send_digest.h [new file with mode: 0644]
listtexts/notifysub-digest [new file with mode: 0644]
listtexts/notifyunsub-digest [new file with mode: 0644]
listtexts/sub-confirm-digest [new file with mode: 0644]
listtexts/sub-ok-digest [new file with mode: 0644]
listtexts/unsub-confirm-digest [new file with mode: 0644]
listtexts/unsub-ok-digest [new file with mode: 0644]
src/Makefile.am
src/listcontrol.c
src/log_error.c
src/mlmmj-maintd.c
src/mlmmj-make-ml.sh
src/mlmmj-send.c
src/mlmmj-sub.c
src/mlmmj-unsub.c
src/send_digest.c [new file with mode: 0644]
src/subscriberfuncs.c

diff --git a/README b/README
index 811768cc01f65e10b7efa73598e8283a8e5e153a..91a5643ae97e8f49cb69945a68d50a39a1a12460 100644 (file)
--- a/README
+++ b/README
@@ -16,6 +16,7 @@ The functionality:
  · Regular expression access control
  · Functionality to retrieve old posts
  · Web-interface
+ · Digests
 
 To use mlmmj, do the following:
 
index f6e090fd42a3cc2cb6a495817e79a6e623f37eed..0065988206b58732d9b0f4c22fff4b11dc545b35 100644 (file)
--- a/TUNABLES
+++ b/TUNABLES
@@ -78,6 +78,17 @@ to specify several entries (one pr. line), it's marked "list".
    If this file is present, the owner(s) will get a mail with the address of
    someone sub/unsubscribing to a mailinglist.
 
+ · digestinterval              (normal)
+
+   This file specifies how many seconds will pass before the next digest is
+   sent. Defaults to 604800 seconds, which is 7 days.
+
+ · digestmaxmails              (normal)
+
+   This file specifies how many mails can accumulate before digest sending is
+   triggered. Defaults to 50 mails, meaning that if 50 mails arrive to the list
+   before digestinterval have passed, the digest is delivered.
+
  · bouncelife                  (normal)
 
    Here is specified for how long time in seconds an address can bounce before
index 49f6d98e62d946a46565c8711539f9cf25920db9..7c66e5de534460a26c05d49e3156e8212e645f60 100644 (file)
@@ -34,6 +34,7 @@ int resend_requeue(const char *listdir, const char *mlmmjsend);
 int clean_nolongerbouncing(const char *listdir);
 int probe_bouncers(const char *listdir, const char *mlmmjbounce);
 int unsub_bouncers(const char *listdir, const char *mlmmjunsub);
+int run_digests(const char *listdir, const char *mlmmjsend);
 
 /* I know the below is nasty, but it requires C99 to have multiple
  * argument macros, and this would then be the only thing needing
index 65e26ef595ac972b745bd7e93ff29a64ac1e98ed..e0d0a614310332103d98c83414efdf8426bfb0b0 100644 (file)
@@ -25,8 +25,9 @@
 #define MLMMJ_SUBSCRIBE_H
 
 void confirm_sub(const char *listdir, const char *listaddr,
-                const char *subaddr, const char *mlmmjsend);
+                const char *subaddr, const char *mlmmjsend, int digest);
 void generate_subconfirm(const char *listdir, const char *listadr,
-               const char *subaddr, const char *mlmmjsend);
+               const char *subaddr, const char *mlmmjsend,
+               int digest);
 
 #endif /* MLMMJ_SUBSCRIBE_H */
index 6e2e07138f04f0ca96f5db3ad1dfbb000c3b9c11..974f926bbc2cdae3a3aadee60cd80e5596ba7eca 100644 (file)
 #include <sys/types.h>
 
 void confirm_unsub(const char *listdir, const char *listaddr,
-                  const char *subaddr, const char *mlmmj);
+                  const char *subaddr, const char *mlmmj, int digest);
 ssize_t unsubscribe(int subreadfd, int subwritefd, const char *address);
 void generate_unsubconfirm(const char *listdir, const char *listaddr,
-                          const char *subaddr, const char *mlmmjsend);
+                          const char *subaddr, const char *mlmmjsend,
+                          int digest);
 
 #endif /* MLMMJ_UNSUBSCRIBE_H */
index b7b539dd05b24c3d6169caa937676bdcca0bb5a9..eb8dd912586db6bc2ea60e299a16bb5a2f47807e 100644 (file)
@@ -28,7 +28,7 @@
 
 #define RELAYHOST "127.0.0.1"
 #define READ_BUFSIZE 2048
-#define RECIPDELIM '+'
+#define RECIPDELIM '+'  /* XXX Warning: not changable at the moment */
 #define MODREQLIFE 604800 /* How long time will moderation requests be kept?
                           * 604800s is 7 days */
 #define DISCARDEDLIFE 604800 /* How long time will discarded mails be kept?
 
 #define MEMORYMAILSIZE 16384  /* How big can a mail be before we don't want to
                                 it in memory? control/memorymailsize */
+#define DIGESTINTERVAL 604800  /* How long do we collect mails for digests
+                               * 604800s is 7 days */
+#define DIGESTMAXMAILS 50 /* How many mails can accumulate before we send the
+                          * digest */
+#define DIGESTMIMETYPE "digest" /* Which sub-type of multipart to use when
+                                * sending digest mails */
 
 struct strlist {
        int count;
diff --git a/include/send_digest.h b/include/send_digest.h
new file mode 100644 (file)
index 0000000..cacecd4
--- /dev/null
@@ -0,0 +1,30 @@
+/* Copyright (C) 2004 Morten K. Poulsen <morten at afdelingp.dk>
+ *
+ * $Id$
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef SEND_DIGEST_H
+#define SEND_DIGEST_H
+
+int send_digest(const char *listdir, int lastindex, int index,
+               const char *addr, const char *mlmmjsend);
+
+#endif /* SEND_DIGEST_H */
diff --git a/listtexts/notifysub-digest b/listtexts/notifysub-digest
new file mode 100644 (file)
index 0000000..6fe03ce
--- /dev/null
@@ -0,0 +1,10 @@
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+The following address has just subscribed to the digest of the
+mailinglist:
+
+*SUBADDR*
+
diff --git a/listtexts/notifyunsub-digest b/listtexts/notifyunsub-digest
new file mode 100644 (file)
index 0000000..f42c9ff
--- /dev/null
@@ -0,0 +1,10 @@
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+The following address has just unsubscribed from the digest of
+mailinglist:
+
+*SUBADDR*
+
diff --git a/listtexts/sub-confirm-digest b/listtexts/sub-confirm-digest
new file mode 100644 (file)
index 0000000..e1f4455
--- /dev/null
@@ -0,0 +1,21 @@
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+To confirm you want the address
+
+*SUBADDR*
+
+
+added to the digest of this list, please send a reply to
+
+*CNFADDR*
+
+
+Your mailer probably automatically replies to this address, when you hit
+the reply button.
+
+This confirmation serves two purposes. It tests that mail can be sent to your
+address. Second, it makes sure someone else did not try and subscribe your
+emailaddress.
diff --git a/listtexts/sub-ok-digest b/listtexts/sub-ok-digest
new file mode 100644 (file)
index 0000000..2209c84
--- /dev/null
@@ -0,0 +1,6 @@
+WELCOME! You have been subscribed to the digest of the
+
+*LSTADDR*
+
+
+mailinglist.
diff --git a/listtexts/unsub-confirm-digest b/listtexts/unsub-confirm-digest
new file mode 100644 (file)
index 0000000..c80b1ac
--- /dev/null
@@ -0,0 +1,21 @@
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+To confirm you want the address
+
+*SUBADDR*
+
+
+removed from the digest of this list, please send a reply to
+
+*CNFADDR*
+
+
+Your mailer probably automatically replies to this address, when you hit
+the reply button.
+
+If you're not subscribed with this list, you will recieve no reply. You can
+see in the From header of a mail to the mailinglist which mail you're sub-
+scribed with.
diff --git a/listtexts/unsub-ok-digest b/listtexts/unsub-ok-digest
new file mode 100644 (file)
index 0000000..5618fff
--- /dev/null
@@ -0,0 +1,6 @@
+GOODBYE! You have been removed from the digest of the
+
+*LSTADDR*
+
+
+mailinglist.
index a8a5a16596d479d8826658eaccb6b19469228190..2abc35dbe51bc9031fd2d878009908e05808deea 100644 (file)
@@ -47,4 +47,5 @@ mlmmj_bounce_SOURCES = mlmmj-bounce.c print-version.c log_error.c \
 
 mlmmj_maintd_SOURCES = mlmmj-maintd.c print-version.c log_error.c mygetline.c \
                       strgen.c random-int.c chomp.c writen.c memory.c \
-                      ctrlvalue.c
+                      ctrlvalue.c send_digest.c getlistaddr.c dumpfd2fd.c \
+                      mylocking.c
index 94dd74abe002dc3f4f5e3ad992e27d028117b629..62b9e16b615010b663cbdedc55acb4cc5595bb1b 100644 (file)
 #include "memory.h"
 
 enum ctrl_e {
+       CTRL_SUBSCRIBE_DIGEST,
        CTRL_SUBSCRIBE,
+       CTRL_CONFSUB_DIGEST,
        CTRL_CONFSUB,
+       CTRL_UNSUBSCRIBE_DIGEST,
        CTRL_UNSUBSCRIBE,
+       CTRL_CONFUNSUB_DIGEST,
        CTRL_CONFUNSUB,
        CTRL_BOUNCES,
        CTRL_MODERATE,
@@ -61,16 +65,22 @@ struct ctrl_command {
        unsigned int accepts_parameter;
 };
 
-/* must match the enum */
+/* Must match the enum. CAREFUL when using commands that are substrings
+ * of other commands. In that case the longest one have to be listed
+ * first to match correctly. */
 static struct ctrl_command ctrl_commands[] = {
-       { "subscribe",   0 },
-       { "confsub",     1 },
-       { "unsubscribe", 0 },
-       { "confunsub",   1 },
-       { "bounces",     1 },
-       { "moderate",    1 },
-       { "help",        0 },
-       { "get",         1 }
+       { "subscribe-digest",   0 },
+       { "subscribe",          0 },
+       { "confsub-digest",     1 },
+       { "confsub",            1 },
+       { "unsubscribe-digest", 0 },
+       { "unsubscribe",        0 },
+       { "confunsub-digest",   1 },
+       { "confunsub",          1 },
+       { "bounces",            1 },
+       { "moderate",           1 },
+       { "help",               0 },
+       { "get",                1 }
 };
 
 
@@ -122,96 +132,192 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                                                " discarding mail");
                                        exit(EXIT_SUCCESS);
                                }
-                               myfree(controlstr);
-                               break;
                        } else if (!ctrl_commands[ctrl].accepts_parameter &&
                                        (controlstr[cmdlen] == '\0')) {
                                param = NULL;
-                               myfree(controlstr);
-                               break;
                        } else {
-                               log_error(LOG_ARGS, "Received a malformed"
-                                       " list control request");
-                               myfree(controlstr);
-                               return -1;
+                               /* malformed request, ignore and clean up */
+                               unlink(mailname);
+                               exit(EXIT_SUCCESS);
                        }
 
+                       myfree(controlstr);
+                       break;
+
                }
        }
 
        switch (ctrl) {
 
+       /* listname+subscribe-digest@domain.tld */
+       case CTRL_SUBSCRIBE_DIGEST:
+               unlink(mailname);
+               if (closedlist)
+                       exit(EXIT_SUCCESS);
+               if (!strchr(fromemails->emaillist[0], '@'))
+                       /* Not a valid From: address, silently ignore */
+                       exit(EXIT_SUCCESS);
+               execlp(mlmmjsub, mlmmjsub,
+                               "-L", listdir,
+                               "-a", fromemails->emaillist[0],
+                               "-d",
+                               "-C", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                                       mlmmjsub);
+               exit(EXIT_FAILURE);
+               break;
+
+       /* listname+subscribe@domain.tld */
        case CTRL_SUBSCRIBE:
                unlink(mailname);
-               if (closedlist) exit(EXIT_SUCCESS);
-               if(strchr(fromemails->emaillist[0], '@')) {
-                       execlp(mlmmjsub, mlmmjsub,
-                                       "-L", listdir,
-                                       "-a", fromemails->emaillist[0],
-                                       "-C", NULL);
-                       log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub);
-                       exit(EXIT_FAILURE);
-               } else /* Not a valid From: address, so we silently ignore */
+               if (closedlist)
                        exit(EXIT_SUCCESS);
+               if (!strchr(fromemails->emaillist[0], '@'))
+                       /* Not a valid From: address, silently ignore */
+                       exit(EXIT_SUCCESS);
+               execlp(mlmmjsub, mlmmjsub,
+                               "-L", listdir,
+                               "-a", fromemails->emaillist[0],
+                               "-C", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                                       mlmmjsub);
+               exit(EXIT_FAILURE);
                break;
 
+       /* listname+subconf-digest-COOKIE@domain.tld */
+       case CTRL_CONFSUB_DIGEST:
+               unlink(mailname);
+               if (closedlist)
+                       exit(EXIT_SUCCESS);
+               conffilename = concatstr(3, listdir, "/subconf/", param);
+               myfree(param);
+               if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+                       /* invalid COOKIE, silently ignore */
+                       exit(EXIT_SUCCESS);
+               }
+               tmpstr = mygetline(tmpfd);
+               chomp(tmpstr);
+               close(tmpfd);
+               unlink(conffilename);
+               execlp(mlmmjsub, mlmmjsub,
+                               "-L", listdir,
+                               "-a", tmpstr,
+                               "-d",
+                               "-c", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                               mlmmjsub);
+               exit(EXIT_FAILURE);
+               break;
+
+       /* listname+subconf-COOKIE@domain.tld */
        case CTRL_CONFSUB:
                unlink(mailname);
-               if (closedlist) exit(EXIT_SUCCESS);
+               if (closedlist)
+                       exit(EXIT_SUCCESS);
                conffilename = concatstr(3, listdir, "/subconf/", param);
                myfree(param);
-               if((tmpfd = open(conffilename, O_RDONLY)) >= 0) {
-                       tmpstr = mygetline(tmpfd);
-                       chomp(tmpstr);
-                       close(tmpfd);
-                       unlink(conffilename);
-                       execlp(mlmmjsub, mlmmjsub,
-                                       "-L", listdir,
-                                       "-a", tmpstr,
-                                       "-c", NULL);
-                       log_error(LOG_ARGS, "execlp() of '%s' failed",
-                                       mlmmjsub);
-                       exit(EXIT_FAILURE);
-               } else /* Not a confirm so silently ignore */
+               if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+                       /* invalid COOKIE, silently ignore */
+                       exit(EXIT_SUCCESS);
+               }
+               tmpstr = mygetline(tmpfd);
+               chomp(tmpstr);
+               close(tmpfd);
+               unlink(conffilename);
+               execlp(mlmmjsub, mlmmjsub,
+                               "-L", listdir,
+                               "-a", tmpstr,
+                               "-c", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                               mlmmjsub);
+               exit(EXIT_FAILURE);
+               break;
+
+       /* listname+unsubscribe-digest@domain.tld */
+       case CTRL_UNSUBSCRIBE_DIGEST:
+               unlink(mailname);
+               if (closedlist)
                        exit(EXIT_SUCCESS);
+               if (!strchr(fromemails->emaillist[0], '@'))
+                       /* Not a valid From: address, silently ignore */
+                       exit(EXIT_SUCCESS);
+               execlp(mlmmjunsub, mlmmjunsub,
+                               "-L", listdir,
+                               "-a", fromemails->emaillist[0],
+                               "-d",
+                               "-C", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                               mlmmjunsub);
+               exit(EXIT_FAILURE);
                break;
 
+       /* listname+unsubscribe@domain.tld */
        case CTRL_UNSUBSCRIBE:
                unlink(mailname);
-               if (closedlist) exit(EXIT_SUCCESS);
-               if(strchr(fromemails->emaillist[0], '@')) {
-                       execlp(mlmmjunsub, mlmmjunsub,
-                                       "-L", listdir,
-                                       "-a", fromemails->emaillist[0],
-                                       "-C", NULL);
-                       log_error(LOG_ARGS, "execlp() of '%s' failed",
-                                       mlmmjunsub);
-                       exit(EXIT_FAILURE);
-               } else /* Not a valid From: address, so we silently ignore */
+               if (closedlist)
+                       exit(EXIT_SUCCESS);
+               if (!strchr(fromemails->emaillist[0], '@'))
+                       /* Not a valid From: address, silently ignore */
                        exit(EXIT_SUCCESS);
+               execlp(mlmmjunsub, mlmmjunsub,
+                               "-L", listdir,
+                               "-a", fromemails->emaillist[0],
+                               "-C", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                               mlmmjunsub);
+               exit(EXIT_FAILURE);
                break;
 
+       /* listname+unsubconf-digest-COOKIE@domain.tld */
+       case CTRL_CONFUNSUB_DIGEST:
+               unlink(mailname);
+               if (closedlist)
+                       exit(EXIT_SUCCESS);
+               conffilename = concatstr(3, listdir, "/unsubconf/", param);
+               myfree(param);
+               if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+                       /* invalid COOKIE, silently ignore */
+                       exit(EXIT_SUCCESS);
+               }
+               tmpstr = mygetline(tmpfd);
+               close(tmpfd);
+               chomp(tmpstr);
+               unlink(conffilename);
+               execlp(mlmmjunsub, mlmmjunsub,
+                               "-L", listdir,
+                               "-a", tmpstr,
+                               "-d",
+                               "-c", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                               mlmmjunsub);
+               exit(EXIT_FAILURE);
+               break;
+
+       /* listname+unsubconf-COOKIE@domain.tld */
        case CTRL_CONFUNSUB:
                unlink(mailname);
-               if (closedlist) exit(EXIT_SUCCESS);
+               if (closedlist)
+                       exit(EXIT_SUCCESS);
                conffilename = concatstr(3, listdir, "/unsubconf/", param);
                myfree(param);
-               if((tmpfd = open(conffilename, O_RDONLY)) >= 0) {
-                       tmpstr = mygetline(tmpfd);
-                       close(tmpfd);
-                       chomp(tmpstr);
-                       unlink(conffilename);
-                       execlp(mlmmjunsub, mlmmjunsub,
-                                       "-L", listdir,
-                                       "-a", tmpstr,
-                                       "-c", NULL);
-                       log_error(LOG_ARGS, "execlp() of '%s' failed",
-                                       mlmmjunsub);
-                       exit(EXIT_FAILURE);
-               } else /* Not a confirm so silently ignore */
+               if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+                       /* invalid COOKIE, silently ignore */
                        exit(EXIT_SUCCESS);
+               }
+               tmpstr = mygetline(tmpfd);
+               close(tmpfd);
+               chomp(tmpstr);
+               unlink(conffilename);
+               execlp(mlmmjunsub, mlmmjunsub,
+                               "-L", listdir,
+                               "-a", tmpstr,
+                               "-c", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                               mlmmjunsub);
+               exit(EXIT_FAILURE);
                break;
 
+       /* listname+bounces-user=example.tld-INDEX@domain.tld */
        case CTRL_BOUNCES:
                bouncenr = strrchr(param, '-');
                if (!bouncenr) { /* malformed bounce, ignore and clean up */
@@ -228,6 +334,7 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                exit(EXIT_FAILURE);
                break;
 
+       /* listname+moderate-COOKIE@domain.tld */
        case CTRL_MODERATE:
                /* TODO Add accept/reject parameter to moderate */
                unlink(mailname);
@@ -236,15 +343,16 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                if(stat(moderatefilename, &stbuf) < 0) {
                        myfree(moderatefilename);
                        exit(EXIT_SUCCESS); /* just exit, no mail to moderate */
-               } else {
-                       execlp(mlmmjsend, mlmmjsend,
-                                       "-L", listdir,
-                                       "-m", moderatefilename, NULL);
-                       log_error(LOG_ARGS, "execlp() of %s failed", mlmmjsend);
-                       exit(EXIT_FAILURE);
                }
+               execlp(mlmmjsend, mlmmjsend,
+                               "-L", listdir,
+                               "-m", moderatefilename, NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                                       mlmmjsend);
+               exit(EXIT_FAILURE);
                break;
 
+       /* listname+help@domain.tld */
        case CTRL_HELP:
                unlink(mailname);
                if(strchr(fromemails->emaillist[0], '@'))
@@ -252,32 +360,27 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                                  mlmmjsend);
                break;
 
+       /* listname+get-INDEX@domain.tld */
        case CTRL_GET:
-               errno = 0;
                unlink(mailname);
-               if(!param) /* malformed get, ignore silently */
-                       exit(EXIT_SUCCESS);
-               else { /* sanity check--is it all digits? */
-                       for(c = param; *c != '\0'; c++) {
-                               if(!isdigit((int)*c))
-                                       exit(EXIT_SUCCESS);
-                       }
-                       archivefilename = concatstr(3, listdir, "/archive/",
-                                                       param);
-                       if(stat(archivefilename, &stbuf) < 0)
+               /* sanity check--is it all digits? */
+               for(c = param; *c != '\0'; c++) {
+                       if(!isdigit((int)*c))
                                exit(EXIT_SUCCESS);
-                       else {
-                               execlp(mlmmjsend, mlmmjsend,
-                                       "-T", fromemails->emaillist[0],
-                                       "-L", listdir,
-                                       "-l", "6",
-                                       "-m", archivefilename,
-                                       "-a", "-D", NULL);
-                               log_error(LOG_ARGS, "execlp() of '%s' failed",
-                                                       mlmmjsend);
-                               exit(EXIT_FAILURE);
-                       }
                }
+               archivefilename = concatstr(3, listdir, "/archive/",
+                                               param);
+               if(stat(archivefilename, &stbuf) < 0)
+                       exit(EXIT_SUCCESS);
+               execlp(mlmmjsend, mlmmjsend,
+                               "-T", fromemails->emaillist[0],
+                               "-L", listdir,
+                               "-l", "6",
+                               "-m", archivefilename,
+                               "-a", "-D", NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed",
+                                       mlmmjsend);
+               exit(EXIT_FAILURE);
                break;
        }
 
index f1c55f75145af8727dcefeec2a48197dd34c6bc7..96a745a39129981982ffb2524ce02a9198417c73 100644 (file)
@@ -25,6 +25,8 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include "log_error.h"
 #include "../config.h"
index bd5be6a4f395eb11ce5ef9580afdcb14a3a6da92..f5ea1a7e67fe441249118659cea47ac164e4c7c4 100644 (file)
@@ -42,6 +42,8 @@
 #include "wrappers.h"
 #include "memory.h"
 #include "ctrlvalue.h"
+#include "send_digest.h"
+#include "mylocking.h"
 
 static int maintdlogfd = -1;
 
@@ -740,6 +742,134 @@ int unsub_bouncers(const char *listdir, const char *mlmmjunsub)
        return 0;
 }
 
+int run_digests(const char *listdir, const char *mlmmjsend)
+{
+       char *lasttimestr, *lastindexstr;
+       char *digestname, *indexname;
+       char *digestintervalstr, *digestmaxmailsstr;
+       char *s1, *s2, *s3;
+       time_t digestinterval, t, lasttime;
+       long digestmaxmails, lastindex, index;
+       int fd, indexfd, lock;
+       size_t lenbuf, lenstr;
+       
+       digestintervalstr = ctrlvalue(listdir, "digestinterval");
+       if (digestintervalstr) {
+               digestinterval = (time_t)atol(digestintervalstr);
+               myfree(digestintervalstr);
+       } else {
+               digestinterval = (time_t)DIGESTINTERVAL;
+       }
+
+       digestmaxmailsstr = ctrlvalue(listdir, "digestmaxmails");
+       if (digestmaxmailsstr) {
+               digestmaxmails = atol(digestmaxmailsstr);
+               myfree(digestmaxmailsstr);
+       } else {
+               digestmaxmails = DIGESTMAXMAILS;
+       }
+
+       digestname = concatstr(2, listdir, "/lastdigest");
+       fd = open(digestname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+       if (fd < 0) {
+               log_error(LOG_ARGS, "Could not open '%s'", digestname);
+               myfree(digestname);
+               return 1;
+       }
+       
+       lock = myexcllock(fd);
+       if(lock) {
+               log_error(LOG_ARGS, "Error locking lastdigest");
+               myfree(digestname);
+               close(fd);
+               return 1;
+       }
+
+       s1 = mygetline(fd);
+
+       /* Syntax is lastindex:lasttime */
+       if (s1 && (lasttimestr = strchr(s1, ':'))) {
+               *(lasttimestr++) = '\0';
+               lasttime = atol(lasttimestr);
+               lastindexstr = s1;
+               lastindex = atol(lastindexstr);
+       } else {
+               if (s1 && (strlen(s1) > 0)) {
+                       log_error(LOG_ARGS, "'%s' contains malformed data",
+                                       digestname);
+                       myfree(digestname);
+                       myfree(s1);
+                       myunlock(fd);
+                       close(fd);
+                       return 1;
+               }
+               /* If lastdigest is empty, we start from scratch */
+               lasttime = 0;
+               lastindex = 0;
+       }
+       
+       indexname = concatstr(2, listdir, "/index");
+       indexfd = open(indexname, O_RDONLY);
+       if (indexfd < 0) {
+               log_error(LOG_ARGS, "Could not open '%s'", indexname);
+               myfree(digestname);
+               myfree(indexname);
+               myfree(s1);
+               myunlock(fd);
+               close(fd);
+               return 1;
+       }
+       s2 = mygetline(indexfd);
+       close(indexfd);
+       if (!s2) {
+               /* If we don't have an index, no mails have been sent to the
+                * list, and therefore we don't need to send a digest */
+               myfree(digestname);
+               myfree(indexname);
+               myfree(s1);
+               myunlock(fd);
+               close(fd);
+               return 1;
+       }
+
+       index = atol(s2);
+       t = time(NULL);
+
+       if ((t - lasttime >= digestinterval) ||
+                       (index - lastindex >= digestmaxmails)) {
+
+               if (index > lastindex+digestmaxmails)
+                       index = lastindex+digestmaxmails;
+
+               send_digest(listdir, lastindex+1, index, NULL, mlmmjsend);
+
+               if (lseek(fd, 0, SEEK_SET) < 0) {
+                       log_error(LOG_ARGS, "Could not seek '%s'", digestname);
+               } else {
+                       /* index + ':' + time + '\n' + '\0' */
+                       lenbuf = 20 + 1 + 20 + 2;
+                       s3 = mymalloc(lenbuf);
+                       lenstr = snprintf(s3, lenbuf, "%ld:%ld\n", index, t);
+                       if (lenstr >= lenbuf)
+                               lenstr = lenbuf - 1;
+                       if (writen(fd, s3, lenstr) == -1) {
+                               log_error(LOG_ARGS, "Could not write new '%s'",
+                                               digestname);
+                       }
+                       myfree(s3);
+               }
+       }
+
+       myfree(digestname);
+       myfree(indexname);
+       myfree(s1);
+       myfree(s2);
+       myunlock(fd);
+       close(fd);
+       
+       return 0;
+}
+
 void do_maintenance(const char *listdir, const char *mlmmjsend,
                    const char *mlmmjbounce, const char *mlmmjunsub)
 {
@@ -825,6 +955,10 @@ void do_maintenance(const char *listdir, const char *mlmmjsend,
                        mlmmjbounce, ");\n");
        probe_bouncers(listdir, mlmmjbounce);
 
+       WRITEMAINTLOG6(5, "run_digests(", listdir, ", ", mlmmjsend,
+                       ");\n");
+       run_digests(listdir, mlmmjsend);
+
        close(maintdlogfd);
 
        logstr = concatstr(3, listdir, "/", MAINTD_LOGFILE);
index 9e7bc4ff7c53b147735d524fcc87683ffcf59505..55d8a2e2ceba359c70a1c190ea60cbd16bac49e2 100755 (executable)
@@ -67,12 +67,12 @@ LISTDIR="$SPOOLDIR/$LISTNAME"
 mkdir -p $LISTDIR
 
 for DIR in incoming queue queue/discarded archive text subconf unsubconf \
-          bounce control moderation subscribers.d requeue
+          bounce control moderation subscribers.d digesters.d requeue
 do
        mkdir "$LISTDIR"/"$DIR"
 done
 
-touch "$LISTDIR"/index
+test -f "$LISTDIR"/index || touch "$LISTDIR"/index
 
 echo -n "The Domain for the List? [] : "
 read FQDN
index 496ece72315225d7b4eba9fdfc372f4e0ce330b1..80e722049bce38e62f34ae475b3fd7e06bcb0f63 100644 (file)
@@ -369,11 +369,11 @@ int send_mail_many(int sockfd, const char *from, const char *replyto,
                } else
                        continue;
 
-               if(from)
+               if(from) {
                        sendres = send_mail(sockfd, from, addr, replyto,
                                            mailmap, mailsize, listdir, NULL,
                                            hdrs, hdrslen, body, bodylen);
-               else {
+               else {
                        bounceaddr = bounce_from_adr(addr, listaddr,
                                                     archivefilename);
                        sendres = send_mail(sockfd, bounceaddr, addr, replyto,
@@ -456,6 +456,7 @@ static void print_help(const char *prg)
               "    '4' means 'send to file with recipients'\n"
               "    '5' means 'bounceprobe'\n"
               "    '6' means 'single listmail to single recipient'\n"
+              "    '7' means 'digest'\n"
               " -L: Full path to list directory\n"
               " -m: Full path to mail file\n"
               " -r: Relayhost IP address (defaults to 127.0.0.1)\n"
@@ -470,7 +471,7 @@ int main(int argc, char **argv)
 {
        size_t len = 0, hdrslen, bodylen;
        int sockfd = 0, mailfd = 0, opt, mindex, subfd, tmpfd;
-       int deletewhensent = 1, sendres, archive = 1;
+       int deletewhensent = 1, sendres, archive = 1, digest = 0;
        char *listaddr, *mailfilename = NULL, *subfilename = NULL;
        char *replyto = NULL, *bounceaddr = NULL, *to_addr = NULL;
        char *relayhost = NULL, *archivefilename = NULL, *tmpstr;
@@ -574,6 +575,12 @@ int main(int argc, char **argv)
                exit(EXIT_FAILURE);
        }
 
+       if((listctrl[0] == '7' && listdir == NULL)) {
+               fprintf(stderr, "With -l 7 you need -L\n");
+               exit(EXIT_FAILURE);
+       }
+
+
        switch(listctrl[0]) {
                case '1':
                case '2':
@@ -581,6 +588,7 @@ int main(int argc, char **argv)
                case '4':
                case '5':
                case '6':
+               case '7':
                        archive = 0;
                default:
                        break;
@@ -797,8 +805,16 @@ int main(int argc, char **argv)
                        myfree(tmpstr);
                }
                break;
+       case '7':
+               digest = 1;
+               archivefilename = mystrdup("digest");
+               /* fall through */
        default: /* normal list mail */
-               subddirname = concatstr(2, listdir, "/subscribers.d/");
+               if (!digest) {
+                       subddirname = concatstr(2, listdir, "/subscribers.d/");
+               } else {
+                       subddirname = concatstr(2, listdir, "/digesters.d/");
+               }
                if((subddir = opendir(subddirname)) == NULL) {
                        log_error(LOG_ARGS, "Could not opendir(%s)",
                                            subddirname);
@@ -807,15 +823,13 @@ int main(int argc, char **argv)
                        myfree(body);
                        exit(EXIT_FAILURE);
                }
-               myfree(subddirname);
 
                while((dp = readdir(subddir)) != NULL) {
                        if(!strcmp(dp->d_name, "."))
                                continue;
                        if(!strcmp(dp->d_name, ".."))
                                continue;
-                       subfilename = concatstr(3, listdir, "/subscribers.d/",
-                                               dp->d_name);
+                       subfilename = concatstr(2, subddirname, dp->d_name);
                        if((subfd = open(subfilename, O_RDONLY)) < 0) {
                                log_error(LOG_ARGS, "Could not open '%s'",
                                                    subfilename);
@@ -841,6 +855,7 @@ int main(int argc, char **argv)
                        close(subfd);
                }
                closedir(subddir);
+               myfree(subddirname);
                break;
        }
        
index 05a376a2a92ee6c9ad317a3bb4b2ea42ae7cfce6..82072fe0910e1e2d934d21d9fe3abdbc376de964 100644 (file)
 #include "memory.h"
 
 void confirm_sub(const char *listdir, const char *listaddr,
-               const char *subaddr, const char *mlmmjsend)
+               const char *subaddr, const char *mlmmjsend,
+               int digest)
 {
        int subtextfd, queuefd;
        char *buf, *subtextfilename, *randomstr, *queuefilename = NULL;
        char *fromaddr, *listname, *listfqdn, *s1;
 
-       subtextfilename = concatstr(2, listdir, "/text/sub-ok");
+       if (!digest) {
+               subtextfilename = concatstr(2, listdir, "/text/sub-ok");
+       } else {
+               subtextfilename = concatstr(2, listdir, "/text/sub-ok-digest");
+       }
 
        if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
                log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
@@ -84,8 +89,16 @@ void confirm_sub(const char *listdir, const char *listaddr,
 
        fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
 
-       s1 = concatstr(9, "From: ", listname, "+help@", listfqdn, "\nTo: ",
-                       subaddr, "\nSubject: Welcome to ", listaddr, "\n\n");
+       if (!digest) {
+               s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+                               "\nTo: ", subaddr, "\nSubject: Welcome to ",
+                               listaddr, "\n\n");
+       } else {
+               s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+                               "\nTo: ", subaddr, "\nSubject: Welcome to "
+                               "digest of ", listaddr, "\n\n");
+       }
+
        if(writen(queuefd, s1, strlen(s1)) < 0) {
                log_error(LOG_ARGS, "Could not write welcome mail");
                exit(EXIT_FAILURE);
@@ -124,7 +137,8 @@ void confirm_sub(const char *listdir, const char *listaddr,
 }
 
 void notify_sub(const char *listdir, const char *listaddr,
-               const char *subaddr, const char *mlmmjsend)
+               const char *subaddr, const char *mlmmjsend,
+               int digest)
 {
        char *maildata[4] = { "*LSTADDR*", NULL, "*SUBADDR*", NULL };
        char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
@@ -136,9 +150,18 @@ void notify_sub(const char *listdir, const char *listaddr,
        maildata[3] = mystrdup(subaddr);
        fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
        fromstr = concatstr(3, listname, "+owner@", listfqdn);
-       subject = concatstr(2, "New subscription to ", listaddr);
-       queuefilename = prepstdreply(listdir, "notifysub", fromstr,
-                                    fromstr, NULL, subject, 2, maildata);
+       if (!digest) {
+               subject = concatstr(2, "New subscription to ", listaddr);
+               queuefilename = prepstdreply(listdir, "notifysub", fromstr,
+                                               fromstr, NULL, subject, 2,
+                                               maildata);
+       } else {
+               subject = concatstr(2, "New subscription to digest of ",
+                                       listaddr);
+               queuefilename = prepstdreply(listdir, "notifysub-digest",
+                                               fromstr, fromstr, NULL,
+                                               subject, 2, maildata);
+       }
        MY_ASSERT(queuefilename)
        myfree(listname);
        myfree(listfqdn);
@@ -158,7 +181,8 @@ void notify_sub(const char *listdir, const char *listaddr,
 }
 
 void generate_subconfirm(const char *listdir, const char *listaddr,
-                        const char *subaddr, const char *mlmmjsend)
+                        const char *subaddr, const char *mlmmjsend,
+                        int digest)
 {
        int subconffd, subtextfd, queuefd;
        char *confirmaddr, *listname, *listfqdn, *confirmfilename = NULL;
@@ -198,15 +222,20 @@ void generate_subconfirm(const char *listdir, const char *listaddr,
 
        close(subconffd);
 
-       confirmaddr = concatstr(5, listname, "+confsub-", randomstr, "@",
-                                  listfqdn);
-
        fromaddr = concatstr(5, listname, "+bounces-confsub-", randomstr,
                                "@", listfqdn);
 
-       myfree(randomstr);
+       if (!digest) {
+               subtextfilename = concatstr(2, listdir, "/text/sub-confirm");
+               confirmaddr = concatstr(5, listname, "+confsub-",
+                                       randomstr, "@", listfqdn);
+       } else {
+               subtextfilename = concatstr(2, listdir, "/text/sub-confirm-digest");
+               confirmaddr = concatstr(5, listname, "+confsub-digest-",
+                                       randomstr, "@", listfqdn);
+       }
 
-       subtextfilename = concatstr(2, listdir, "/text/sub-confirm");
+       myfree(randomstr);
 
        if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
                log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
@@ -233,9 +262,16 @@ void generate_subconfirm(const char *listdir, const char *listaddr,
                exit(EXIT_FAILURE);
        }
 
-       s1 = concatstr(9, "From: ", listname, "+help@", listfqdn, "\nTo: ", 
-                       subaddr, "\nSubject: Confirm subscribe to ", listaddr,
-                       "\n\n");
+       if (!digest) {
+               s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+                       "\nTo: ", subaddr, "\nSubject: Confirm subscribe to ",
+                       listaddr, "\n\n");
+       } else {
+               s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+                       "\nTo: ", subaddr, "\nSubject: Confirm subscribe to "
+                       "digest of ", listaddr, "\n\n");
+       }
+
        if(writen(queuefd, s1, strlen(s1)) < 0) {
                log_error(LOG_ARGS, "Could not write subconffile");
                exit(EXIT_FAILURE);
@@ -294,6 +330,7 @@ static void print_help(const char *prg)
               " -a: Email address to subscribe \n"
               " -c: Send welcome mail\n"
               " -C: Request mail confirmation\n"
+              " -d: Subscribe to digest of list\n"
               " -h: This help\n"
               " -L: Full path to list directory\n"
               " -U: Don't switch to the user id of the listdir owner\n"
@@ -308,7 +345,7 @@ int main(int argc, char **argv)
        char *listaddr, *listdir = NULL, *address = NULL, *subfilename = NULL;
        char *mlmmjsend, *bindir, chstr[2];
        int subconfirm = 0, confirmsub = 0, opt, subfilefd, lock, notifysub;
-       int changeuid = 1, status;
+       int changeuid = 1, status, digest = 0;
        size_t len;
        off_t suboff;
        struct stat st;
@@ -322,7 +359,7 @@ int main(int argc, char **argv)
        mlmmjsend = concatstr(2, bindir, "/mlmmj-send");
        myfree(bindir);
 
-       while ((opt = getopt(argc, argv, "hcCVL:a:")) != -1) {
+       while ((opt = getopt(argc, argv, "hcCdVL:a:")) != -1) {
                switch(opt) {
                case 'a':
                        address = optarg;
@@ -333,6 +370,9 @@ int main(int argc, char **argv)
                case 'C':
                        subconfirm = 1;
                        break;
+               case 'd':
+                       digest = 1;
+                       break;
                case 'h':
                        print_help(argv[0]);
                        break;
@@ -380,7 +420,11 @@ int main(int argc, char **argv)
 
        chstr[0] = address[0];
        chstr[1] = '\0';
-       subfilename = concatstr(3, listdir, "/subscribers.d/", chstr);
+       if (!digest) {
+               subfilename = concatstr(3, listdir, "/subscribers.d/", chstr);
+       } else {
+               subfilename = concatstr(3, listdir, "/digesters.d/", chstr);
+       }
 
        subfilefd = open(subfilename, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
        if(subfilefd == -1) {
@@ -398,7 +442,7 @@ int main(int argc, char **argv)
        if(suboff == -1) {
                if(subconfirm)
                        generate_subconfirm(listdir, listaddr, address,
-                                           mlmmjsend);
+                                           mlmmjsend, digest);
                else {
                        lseek(subfilefd, 0L, SEEK_END);
                        len = strlen(address);
@@ -419,7 +463,8 @@ int main(int argc, char **argv)
 
                if(childpid < 0) {
                        log_error(LOG_ARGS, "Could not fork");
-                       confirm_sub(listdir, listaddr, address, mlmmjsend);
+                       confirm_sub(listdir, listaddr, address, mlmmjsend,
+                                       digest);
                }
                
                if(childpid > 0) {
@@ -430,14 +475,15 @@ int main(int argc, char **argv)
 
                /* child confirms subscription */
                if(childpid == 0)
-                       confirm_sub(listdir, listaddr, address, mlmmjsend);
+                       confirm_sub(listdir, listaddr, address, mlmmjsend,
+                                       digest);
        }
 
        notifysub = statctrl(listdir, "notifysub");
 
        /* Notify list owner about subscription */
        if (notifysub)
-               notify_sub(listdir, listaddr, address, mlmmjsend);
+               notify_sub(listdir, listaddr, address, mlmmjsend, digest);
 
        myfree(listaddr);
 
index 7621748d0e0da528edc1aff12308e49f77643dc5..9721fd864adf7553dbb1ff77632764d92502acac 100644 (file)
 #include "prepstdreply.h"
 
 void confirm_unsub(const char *listdir, const char *listaddr,
-                  const char *subaddr, const char *mlmmjsend)
+                  const char *subaddr, const char *mlmmjsend, int digest)
 {
        int subtextfd, queuefd;
        char *buf, *subtextfilename, *randomstr, *queuefilename = NULL;
        char *fromaddr, *listname, *listfqdn, *s1;
 
-       subtextfilename = concatstr(2, listdir, "/text/unsub-ok");
+       if (!digest) {
+               subtextfilename = concatstr(2, listdir, "/text/unsub-ok");
+       } else {
+               subtextfilename = concatstr(2, listdir,
+                                           "/text/unsub-ok-digest");
+       }
 
        if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
                log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
@@ -84,9 +89,15 @@ void confirm_unsub(const char *listdir, const char *listaddr,
 
        fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
 
-       s1 = concatstr(9, "From: ", listname, "+help@", listfqdn, "\nTo: ",
-                       subaddr, "\nSubject: Goodbye from ", listaddr,
-                       "\n\n");
+       if (!digest) {
+               s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+                       "\nTo: ", subaddr, "\nSubject: Goodbye from ",
+                       listaddr, "\n\n");
+       } else {
+               s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+                       "\nTo: ", subaddr, "\nSubject: Goodbye from "
+                       "digest of ", listaddr, "\n\n");
+       }
 
        if(writen(queuefd, s1, strlen(s1)) < 0) {
                log_error(LOG_ARGS, "Could not write subconffile");
@@ -126,7 +137,7 @@ void confirm_unsub(const char *listdir, const char *listaddr,
 }
 
 void notify_unsub(const char *listdir, const char *listaddr,
-                 const char *subaddr, const char *mlmmjsend)
+                 const char *subaddr, const char *mlmmjsend, int digest)
 {
         char *maildata[4] = { "*LSTADDR*", NULL, "*SUBADDR*", NULL };
         char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
@@ -138,9 +149,17 @@ void notify_unsub(const char *listdir, const char *listaddr,
         maildata[3] = mystrdup(subaddr);
         fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
         fromstr = concatstr(3, listname, "+owner@", listfqdn);
-        subject = concatstr(2, "Unsubscription from ", listaddr);
-        queuefilename = prepstdreply(listdir, "notifyunsub", fromstr,
+       if (!digest) {
+               subject = concatstr(2, "Unsubscription from ", listaddr);
+               queuefilename = prepstdreply(listdir, "notifyunsub", fromstr,
                                      fromstr, NULL, subject, 2, maildata);
+       } else {
+               subject = concatstr(2, "Unsubscription from digest of ",
+                                       listaddr);
+               queuefilename = prepstdreply(listdir, "notifyunsub-digest",
+                                     fromstr, fromstr, NULL, subject, 2,
+                                     maildata);
+       }
         MY_ASSERT(queuefilename)
         myfree(listname);
         myfree(listfqdn);
@@ -161,7 +180,8 @@ void notify_unsub(const char *listdir, const char *listaddr,
 
 
 void generate_unsubconfirm(const char *listdir, const char *listaddr,
-                          const char *subaddr, const char *mlmmjsend)
+                          const char *subaddr, const char *mlmmjsend,
+                          int digest)
 {
        char *confirmaddr, *buf, *listname, *listfqdn;
        char *subtextfilename, *queuefilename = NULL, *fromaddr, *s1;
@@ -201,13 +221,21 @@ void generate_unsubconfirm(const char *listdir, const char *listaddr,
 
        close(subconffd);
 
-       confirmaddr = concatstr(5, listname, "+confunsub-", randomstr, "@",
-                                  listfqdn);
        fromaddr = concatstr(5, listname, "+bounces-confunsub-", randomstr,
                                "@", listfqdn);
-       myfree(randomstr);
 
-       subtextfilename = concatstr(2, listdir, "/text/unsub-confirm");
+       if (!digest) {
+               subtextfilename = concatstr(2, listdir, "/text/unsub-confirm");
+               confirmaddr = concatstr(5, listname, "+confunsub-", randomstr,
+                                       "@", listfqdn);
+       } else {
+               subtextfilename = concatstr(2, listdir,
+                                       "/text/unsub-confirm-digest");
+               confirmaddr = concatstr(5, listname, "+confunsub-digest-",
+                                       randomstr, "@", listfqdn);
+       }
+
+       myfree(randomstr);
 
        if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
                log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
@@ -233,9 +261,16 @@ void generate_unsubconfirm(const char *listdir, const char *listaddr,
                exit(EXIT_FAILURE);
        }
 
-       s1 = concatstr(9, "From: ", listname, "+help@", listfqdn, "\nTo: ",
-                       subaddr, "\nSubject: Confirm unsubscribe from ",
-                       listaddr, "\n\n");
+       if (!digest) {
+               s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+                               "\nTo: ", subaddr, "\nSubject: Confirm "
+                               "unsubscribe from ", listaddr, "\n\n");
+       } else {
+               s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+                               "\nTo: ", subaddr, "\nSubject: Confirm "
+                               "unsubscribe from digest of ", listaddr,
+                               "\n\n");
+       }
 
        if(writen(queuefd, s1, strlen(s1)) < 0) {
                log_error(LOG_ARGS, "Could not write subconffile");
@@ -331,6 +366,7 @@ static void print_help(const char *prg)
               " -a: Email address to unsubscribe \n"
               " -c: Send goodbye mail\n"
               " -C: Request mail confirmation\n"
+              " -d: Subscribe to digest of list\n"
               " -h: This help\n"
               " -L: Full path to list directory\n"
               " -V: Print version\n"
@@ -342,7 +378,7 @@ static void print_help(const char *prg)
 int main(int argc, char **argv)
 {
        int subread, subwrite, rlock, wlock, opt, unsubres, status;
-       int confirmunsub = 0, unsubconfirm = 0, notifysub = 0;
+       int confirmunsub = 0, unsubconfirm = 0, notifysub = 0, digest = 0;
        char *listaddr, *listdir = NULL, *address = NULL, *subreadname = NULL;
        char *subwritename, *mlmmjsend, *bindir;
        char *subddirname;
@@ -359,7 +395,7 @@ int main(int argc, char **argv)
        mlmmjsend = concatstr(2, bindir, "/mlmmj-send");
        myfree(bindir);
 
-       while ((opt = getopt(argc, argv, "hcCVL:a:")) != -1) {
+       while ((opt = getopt(argc, argv, "hcCdVL:a:")) != -1) {
                switch(opt) {
                case 'L':
                        listdir = optarg;
@@ -373,6 +409,9 @@ int main(int argc, char **argv)
                case 'C':
                        unsubconfirm = 1;
                        break;
+               case 'd':
+                       digest = 1;
+                       break;
                case 'h':
                        print_help(argv[0]);
                        break;
@@ -397,9 +436,14 @@ int main(int argc, char **argv)
        listaddr = getlistaddr(listdir);
 
        if(unsubconfirm)
-               generate_unsubconfirm(listdir, listaddr, address, mlmmjsend);
+               generate_unsubconfirm(listdir, listaddr, address, mlmmjsend,
+                               digest);
 
-       subddirname = concatstr(2, listdir, "/subscribers.d/");
+       if (!digest) {
+               subddirname = concatstr(2, listdir, "/subscribers.d/");
+       } else {
+               subddirname = concatstr(2, listdir, "/digesters.d/");
+       }
        if((subddir = opendir(subddirname)) == NULL) {
                log_error(LOG_ARGS, "Could not opendir(%s)",
                                    subddirname);
@@ -412,8 +456,13 @@ int main(int argc, char **argv)
                        continue;
                if(!strcmp(dp->d_name, ".."))
                        continue;
-               subreadname = concatstr(3, listdir, "/subscribers.d/",
+               if (!digest) {
+                       subreadname = concatstr(3, listdir, "/subscribers.d/",
                                dp->d_name);
+               } else {
+                       subreadname = concatstr(3, listdir, "/digesters.d/",
+                               dp->d_name);
+               }
 
                subread = open(subreadname, O_RDWR);
                if(subread == -1) {
@@ -509,7 +558,7 @@ int main(int argc, char **argv)
                        if(childpid < 0) {
                                log_error(LOG_ARGS, "Could not fork");
                                confirm_unsub(listdir, listaddr, address,
-                                               mlmmjsend);
+                                               mlmmjsend, digest);
                        }
 
                        if(childpid > 0) {
@@ -521,7 +570,7 @@ int main(int argc, char **argv)
                        /* child confirms subscription */
                        if(childpid == 0)
                                confirm_unsub(listdir, listaddr, address,
-                                               mlmmjsend);
+                                               mlmmjsend, digest);
                }
         }
 
@@ -530,7 +579,7 @@ int main(int argc, char **argv)
 
         /* Notify list owner about subscription */
         if (notifysub)
-                notify_unsub(listdir, listaddr, address, mlmmjsend);
+                notify_unsub(listdir, listaddr, address, mlmmjsend, digest);
 
        myfree(listaddr);
 
diff --git a/src/send_digest.c b/src/send_digest.c
new file mode 100644 (file)
index 0000000..4a7a45d
--- /dev/null
@@ -0,0 +1,199 @@
+/* Copyright (C) 2004 Morten K. Poulsen <morten at afdelingp.dk>
+ *
+ * $Id$
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "mlmmj.h"
+#include "send_digest.h"
+#include "log_error.h"
+#include "strgen.h"
+#include "memory.h"
+#include "getlistaddr.h"
+#include "wrappers.h"
+
+
+int send_digest(const char *listdir, int firstindex, int lastindex,
+               const char *addr, const char *mlmmjsend)
+{
+       int i;
+       int fd, archivefd;
+       char buf[45];
+       char *tmp, *queuename = NULL, *archivename;
+       char *boundary, *listaddr, *listname, *listfqdn;
+       pid_t childpid, pid;
+       int status;
+
+       if (addr) {
+               errno = 0;
+               log_error(LOG_ARGS, "send_digest() does not support sending "
+                               "digest mails to only one recipient yet");
+               return -1;
+       }
+
+       if (firstindex > lastindex)
+               return -1;
+       
+       do {
+               tmp = random_str();
+               myfree(queuename);
+               queuename = concatstr(3, listdir, "/queue/", tmp);
+               myfree(tmp);
+               fd = open(queuename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
+       } while ((fd < 0) && (errno == EEXIST));
+
+       if (fd < 0) {
+               log_error(LOG_ARGS, "Could not open digest queue file '%s'",
+                               queuename);
+               myfree(queuename);
+               return -1;
+       }
+
+       boundary = random_str();
+
+       listaddr = getlistaddr(listdir);
+       listname = genlistname(listaddr);
+       listfqdn = genlistfqdn(listaddr);
+       myfree(listaddr);
+       
+       if (lastindex == firstindex) {
+               snprintf(buf, sizeof(buf), " (%d)", firstindex);
+       } else {
+               snprintf(buf, sizeof(buf), " (%d-%d)", firstindex, lastindex);
+       }
+       tmp = concatstr(10,     "From: ", listname, "+help@", listfqdn,
+                               "\nMIME-Version: 1.0"
+                               "\nContent-Type: multipart/" DIGESTMIMETYPE "; "
+                                       "boundary=", boundary,
+                               "\nSubject: Digest of ", listname, buf,
+                               "\n\n");
+       myfree(listfqdn);
+
+       if (writen(fd, tmp, strlen(tmp)) == -1) {
+               log_error(LOG_ARGS, "Could not write digest headers to '%s'",
+                               queuename);
+               close(fd);
+               unlink(queuename);
+               myfree(boundary);
+               myfree(tmp);
+               myfree(queuename);
+               myfree(listname);
+               return -1;
+       }
+       myfree(tmp);
+
+       for (i=firstindex; i<=lastindex; i++) {
+               snprintf(buf, sizeof(buf), "%d", i);
+               
+               archivename = concatstr(3, listdir, "/archive/", buf);
+               archivefd = open(archivename, O_RDONLY);
+               myfree(archivename);
+               
+               if (archivefd < 0)
+                       continue;
+               
+               tmp = concatstr(7, "--", boundary,
+                               "\nContent-Type: message/rfc822"
+                               "\nContent-Disposition: inline; filename=\"",
+                                       listname, "_", buf, ".eml\""
+                               "\n\n");
+               if (writen(fd, tmp, strlen(tmp)) == -1) {
+                       log_error(LOG_ARGS, "Could not write digest part "
+                                       "headers for archive index %d to "
+                                       "'%s'", i, queuename);
+                       close(fd);
+                       close(archivefd);
+                       unlink(queuename);
+                       myfree(boundary);
+                       myfree(tmp);
+                       myfree(queuename);
+                       myfree(listname);
+                       return -1;
+               }
+               myfree(tmp);
+
+               if (dumpfd2fd(archivefd, fd) < 0) {
+                       log_error(LOG_ARGS, "Could not write digest part %d "
+                                       "to '%s'", i,
+                                       queuename);
+                       close(fd);
+                       close(archivefd);
+                       unlink(queuename);
+                       myfree(boundary);
+                       myfree(queuename);
+                       myfree(listname);
+                       return -1;
+               }
+               
+               close(archivefd);
+       }
+
+       tmp = concatstr(3, "--", boundary, "--\n");
+       if (writen(fd, tmp, strlen(tmp)) == -1) {
+               log_error(LOG_ARGS, "Could not write digest end to '%s'",
+                               queuename);
+               close(fd);
+               unlink(queuename);
+               myfree(boundary);
+               myfree(queuename);
+               myfree(listname);
+               return -1;
+       }
+
+       close(fd);
+       myfree(boundary);
+       myfree(listname);
+
+       childpid = fork();
+
+       if(childpid < 0) {
+               log_error(LOG_ARGS, "Could not fork");
+               myfree(queuename);
+               return -1;
+       }
+
+       if(childpid > 0) {
+               do /* Parent waits for the child */
+                     pid = waitpid(childpid, &status, 0);
+               while(pid == -1 && errno == EINTR);
+       } else {
+               execlp(mlmmjsend, mlmmjsend,
+                               "-l", "7",
+                               "-L", listdir,
+                               "-m", queuename,
+                               NULL);
+               log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend);
+               exit(EXIT_FAILURE);  /* It is OK to exit, as this is a child */
+       }
+
+       unlink(queuename);
+       myfree(queuename);
+
+       return 0;
+}
index a914471c7e486c2f6b838adc07767a8d3db6fc79..1000c2b4bb72534d174df1b16cdc7b991ebbe479 100644 (file)
@@ -87,31 +87,26 @@ off_t find_subscriber(int fd, const char *address)
        return (off_t)-1;
 }
 
-int is_subbed(const char *listdir, const char *address)
+static int is_subbed_in(const char *subddirname, const char *address)
 {
        int retval = 1, subread;
-       char *subddirname, *subreadname;
+       char *subreadname;
        off_t suboff;
        DIR *subddir;
        struct dirent *dp;
 
-       subddirname = concatstr(2, listdir, "/subscribers.d/");
        if((subddir = opendir(subddirname)) == NULL) {
                log_error(LOG_ARGS, "Could not opendir(%s)", subddirname);
-               myfree(subddirname);
                exit(EXIT_FAILURE);
        }
 
-       myfree(subddirname);
-
        while((dp = readdir(subddir)) != NULL) {
                if(!strcmp(dp->d_name, "."))
                        continue;
                if(!strcmp(dp->d_name, ".."))
                        continue;
 
-               subreadname = concatstr(3, listdir, "/subscribers.d/",
-                               dp->d_name);
+               subreadname = concatstr(2, subddirname, dp->d_name);
                subread = open(subreadname, O_RDONLY);
                if(subread < 0) {
                        log_error(LOG_ARGS, "Could not open '%s'",
@@ -135,3 +130,23 @@ int is_subbed(const char *listdir, const char *address)
 
        return retval;
 }
+
+int is_subbed(const char *listdir, const char *address)
+{
+       int retval;
+       char *subddirname;
+       
+       subddirname = concatstr(2, listdir, "/subscribers.d/");
+       retval = is_subbed_in(subddirname, address);
+       myfree(subddirname);
+       if (retval == 0)
+               return 0;
+
+       subddirname = concatstr(2, listdir, "/digesters.d/");
+       retval = is_subbed_in(subddirname, address);
+       myfree(subddirname);
+       if (retval == 0)
+               return 0;
+
+       return 1;
+}