Libev / libc-ares Migration
authorWilfried Goesgens <dothebart@citadel.org>
Mon, 10 Jan 2011 22:38:36 +0000 (23:38 +0100)
committerWilfried Goesgens <dothebart@citadel.org>
Mon, 10 Jan 2011 22:38:36 +0000 (23:38 +0100)
  - Bounce-o-matic:
   - use parsend Queue structure instead of parsing the queue message ourselves
   - only Compose the bounce message, if we're shure we'll need it
   - malloc all memory first, and fail if..
  - restructure Message Queue documentation
  - parse not yet identified Submitted-Header into queue item
  - Alloc QueueItem->StatusMessage on creation so we have it easier writing it later

  - Start implementing timeout/abort conditions

  - c-ares integration: start/stop the right event-watchers
  - c-ares integration: give it its own watcher structs

citadel/event_client.c
citadel/event_client.h
citadel/modules/c-ares-dns/serv_c-ares-dns.c
citadel/modules/smtp/serv_smtpeventclient.c
citadel/modules/smtp/serv_smtpqueue.c
citadel/modules/smtp/smtpqueue.h
citadel/techdoc/delivery-list.txt

index 1d1ca925c722663a33d19566cfcf0ea08a511394..7cbecd6e2557982a8fd28bc86a80b6dd63cc145c 100644 (file)
@@ -238,8 +238,7 @@ IO_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
        rc = StrBuf_write_one_chunk_callback(watcher->fd, 0/*TODO*/, &IO->SendBuf);
 
        if (rc == 0)
-       {
-               
+       {               
 #ifdef BIGBAD_IODBG
                {
                        int rv = 0;
@@ -298,13 +297,25 @@ IO_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
                        break;
                }
        }
-       else if (rc > 0)
-               ShutDownCLient(IO);
-//     else 
-               ///abort!
+       else if (rc < 0)
+               IO->Timeout(IO);
+       /* else : must write more. */
 }
 
+static void
+IO_Timout_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
+{
+       AsyncIO *IO = watcher->data;
+
+       IO->Timeout(IO);
+}
+static void
+IO_connfail_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
+{
+       AsyncIO *IO = watcher->data;
 
+       IO->ConnFail(IO);
+}
 static void
 IO_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
 {
@@ -326,7 +337,7 @@ IO_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
        if (nbytes > 0) {
                HandleInbound(IO);
        } else if (nbytes == 0) {
-               ShutDownCLient(IO);
+               IO->Timeout(IO);
 ///  TODO: this is a timeout???  sock_buff_invoke_free(sb, 0); seems as if socket is gone then?
                return;
        } else if (nbytes == -1) {
@@ -337,8 +348,13 @@ IO_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
 
 
 static void
-set_start_callback(struct ev_loop *loop, AsyncIO *IO, int revents)
+set_start_callback(struct ev_loop *loop, AsyncIO *IO, int revents, int first_rw_timeout)
 {
+       ev_timer_init(&IO->conn_timeout, 
+                     IO_Timout_callback, 
+                     first_rw_timeout, 0);
+       IO->conn_timeout.data = IO;
+       
        switch(IO->NextState) {
        case eReadMessage:
                ev_io_start(event_base, &IO->recv_event);
@@ -355,7 +371,7 @@ set_start_callback(struct ev_loop *loop, AsyncIO *IO, int revents)
        }
 }
 
-int event_connect_socket(AsyncIO *IO)
+int event_connect_socket(AsyncIO *IO, int conn_timeout, int first_rw_timeout)
 {
        struct sockaddr_in  saddr;
        int fdflags; 
@@ -413,9 +429,17 @@ IO->curr_ai->ai_family,
 /// TODO: ipv6??                    (IO->HEnt->h_addrtype == AF_INET6)?
                     /*     sizeof(in6_addr):*/
                     sizeof(struct sockaddr_in));
-       if ((rc >= 0) || (errno == EINPROGRESS)){
+       if (rc >= 0){
 ////           freeaddrinfo(res);
-               set_start_callback(event_base, IO, 0);
+               set_start_callback(event_base, IO, 0, first_rw_timeout);
+               return 0;
+       }
+       else if (errno == EINPROGRESS) {
+               set_start_callback(event_base, IO, 0, first_rw_timeout);
+               ev_timer_init(&IO->conn_fail, 
+                             IO_connfail_callback, 
+                             conn_timeout, 0);
+               IO->conn_fail.data = IO;
                
                return 0;
        }
@@ -439,6 +463,8 @@ void InitEventIO(AsyncIO *IO,
                 IO_CallBack Timeout, 
                 IO_CallBack ConnFail, 
                 IO_LineReaderCallback LineReader,
+                int conn_timeout, 
+                int first_rw_timeout,
                 int ReadFirst)
 {
        IO->Data = pData;
@@ -455,7 +481,7 @@ void InitEventIO(AsyncIO *IO,
        }
        IO->IP6 = IO->HEnt->h_addrtype == AF_INET6;
 //     IO->res = HEnt->h_addr_list[0];
-       event_connect_socket(IO);
+       event_connect_socket(IO, conn_timeout, first_rw_timeout);
 }
 
 
index f9cf037b5967fe907b08ef3449724a4d950dbe88..35ce4246e214eb7d8b6f22b563928f4851507295 100644 (file)
@@ -36,10 +36,11 @@ struct AsyncIO {
        unsigned short dport;
        int active_event;
                eNextState NextState;
+
+       ev_timer conn_fail, 
+               conn_timeout;
        ev_io recv_event, 
-               send_event, 
-               dns_recv_event, 
-               dns_send_event;
+               send_event;
        StrBuf *ErrMsg; /* if we fail to connect, or lookup, error goes here. */
 
        /* read/send related... */
@@ -56,10 +57,16 @@ struct AsyncIO {
 
        IO_LineReaderCallback LineReader; /* if we have linereaders, maybe we want to read more lines before the real application logic is called? */
 
+
+       int active_dns_event;
+       ev_io dns_recv_event, 
+               dns_send_event;
        struct ares_options DNSOptions;
        ares_channel DNSChannel;
+
        ParseDNSAnswerCb DNS_CB;
        IO_CallBack PostDNS;
+
        int DNSStatus;
        void *VParsedDNSReply;
        FreeDNSReply DNSReplyFree;
@@ -87,6 +94,7 @@ void InitEventIO(AsyncIO *IO,
                 IO_CallBack Timeout, 
                 IO_CallBack ConnFail, 
                 IO_LineReaderCallback LineReader,
+                int conn_timeout, int first_rw_timeout,
                 int ReadFirst);
 
 int QueueQuery(ns_type Type, char *name, AsyncIO *IO, IO_CallBack PostDNS);
index 9cd52b9afb9daa1ea468e7519990161b21ab12d0..6c1be1f167a9ed3bb701a5c0b2fac9c98579352c 100644 (file)
@@ -315,21 +315,27 @@ void SockStateCb(void *data, int sock, int read, int write)
 
        if (read) {
                if ((IO->dns_recv_event.fd != sock) &&
-                   (IO->dns_recv_event.fd != 0)) {
+                   (IO->dns_recv_event.fd != 0) && 
+                   ((IO->active_dns_event & EV_READ) != 0)) {
                        ev_io_stop(event_base, &IO->dns_recv_event);
                }
                IO->dns_recv_event.fd = sock;
                ev_io_init(&IO->dns_recv_event, DNS_recv_callback, IO->dns_recv_event.fd, EV_READ);
                IO->dns_recv_event.data = IO;
-
-       } else if (write) {
+               ev_io_start(event_base, &IO->dns_recv_event);
+               IO->active_dns_event = IO->active_dns_event | EV_READ;
+       } 
+       if (write) {
                if ((IO->dns_send_event.fd != sock) &&
-                   (IO->dns_send_event.fd != 0)) {
+                   (IO->dns_send_event.fd != 0) && 
+                   ((IO->active_dns_event & EV_WRITE) != 0)) {
                        ev_io_stop(event_base, &IO->dns_send_event);
                }
                IO->dns_send_event.fd = sock;
                ev_io_init(&IO->dns_send_event, DNS_send_callback, IO->dns_send_event.fd, EV_WRITE);
                IO->dns_send_event.data = IO;
+               ev_io_start(event_base, &IO->dns_send_event);
+               IO->active_dns_event = IO->active_dns_event | EV_WRITE;
        }
 /*
 
@@ -340,9 +346,14 @@ void SockStateCb(void *data, int sock, int read, int write)
                
                ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf);
        }
-       if ((read == 0) && (write == 0)) {
-//             ev_io_stop(event_base, &IO->dns_io_event);
 */
+       if ((read == 0) && (write == 0)) {
+               if ((IO->active_dns_event & EV_READ) != 0)
+                       ev_io_stop(event_base, &IO->dns_recv_event);
+               if ((IO->active_dns_event & EV_WRITE) != 0)
+                       ev_io_stop(event_base, &IO->dns_send_event);
+               IO->active_dns_event = 0;
+       }
 }
 
 CTDL_MODULE_INIT(c_ares_client)
index 839f138af9dc2b52146bf6bf2e1c429d2f16b855..8146959bcced81fecf12eb2a23dfeecc38694c99 100644 (file)
@@ -108,6 +108,7 @@ typedef enum _eSMTP_C_States {
        eMaxSMTPC
 } eSMTP_C_States;
 
+const long SMTP_C_ConnTimeout = 300; /* wail 5 minutes for connections... */
 const long SMTP_C_ReadTimeouts[eMaxSMTPC] = {
        90, /* Greeting... */
        30, /* EHLO */
@@ -167,6 +168,7 @@ void DeleteSmtpOutMsg(void *v)
        SmtpOutMsg *Msg = v;
 
        ares_free_data(Msg->AllMX);
+       
        FreeStrBuf(&Msg->msgtext);
        FreeAsyncIOContents(&Msg->IO);
        free(Msg);
@@ -191,7 +193,9 @@ typedef eNextState (*SMTPSendHandler)(SmtpOutMsg *Msg);
 
 #define SMTP_VERROR(WHICH_ERR) do {\
                SendMsg->MyQEntry->Status = WHICH_ERR; \
-               StrBufAppendBufPlain(SendMsg->MyQEntry->StatusMessage, &ChrPtr(SendMsg->IO.IOBuf)[4], -1, 0); \
+               StrBufPlain(SendMsg->MyQEntry->StatusMessage, \
+                           ChrPtr(SendMsg->IO.IOBuf) + 4, \
+                           StrLength(SendMsg->IO.IOBuf) - 4); \
                return eAbort; } \
        while (0)
 
@@ -210,14 +214,14 @@ void FinalizeMessageSend(SmtpOutMsg *Msg)
 
                nRemain = CountActiveQueueEntries(Msg->MyQItem);
 
-               if (nRemain > 0) 
-                       MsgData = SerializeQueueItem(Msg->MyQItem);
+               MsgData = SerializeQueueItem(Msg->MyQItem);
                /*
                 * Uncompleted delivery instructions remain, so delete the old
                 * instructions and replace with the updated ones.
                 */
                CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, "");
-
+               smtpq_do_bounce(Msg->MyQItem,
+                              Msg->msgtext); 
                if (nRemain > 0) {
                        struct CtdlMessage *msg;
                        msg = malloc(sizeof(struct CtdlMessage));
@@ -253,11 +257,15 @@ void get_one_mx_host_ip_done(void *Ctx,
        SmtpOutMsg *SendMsg = IO->Data;
        if ((status == ARES_SUCCESS) && (hostent != NULL) ) {
                CtdlLogPrintf(CTDL_DEBUG, 
-                             "SMTP client[%ld]: connecting to %s : %d ...\n", 
+                             "SMTP client[%ld]: connecting to %s [ip]: %d ...\n", 
                              SendMsg->n, 
                              SendMsg->mx_host, 
                              SendMsg->IO.dport);
 
+               SendMsg->MyQEntry->Status = 5; 
+               StrBufPrintf(SendMsg->MyQEntry->StatusMessage, 
+                            "Timeout while connecting %s", 
+                            SendMsg->mx_host);
 
                SendMsg->IO.HEnt = hostent;
                InitEventIO(IO, SendMsg, 
@@ -267,6 +275,8 @@ void get_one_mx_host_ip_done(void *Ctx,
                            SMTP_C_Timeout,
                            SMTP_C_ConnFail,
                            SMTP_C_ReadServerStatus,
+                           SMTP_C_ConnTimeout, 
+                           SMTP_C_ReadTimeouts[0],
                            1);
 
        }
index a05d2f26b592b91a9b752b1465028669cc1dfab4..4eccf60efb9d88f906289e813447a849ded655d6 100644 (file)
@@ -85,7 +85,7 @@
 
 #include "ctdl_module.h"
 
-#include "smtp_util.h"
+///#include "smtp_util.h"
 #include "smtpqueue.h"
 #include "event_client.h"
 
@@ -260,6 +260,9 @@ StrBuf *SerializeQueueItem(OneQueItem *MyQItem)
        StrBufAppendBufPlain(QMessage, HKEY("\nmsgid|"), 0);
        StrBufAppendPrintf(QMessage, "%ld", MyQItem->MessageID);
 
+       StrBufAppendBufPlain(QMessage, HKEY("\nsubmitted|"), 0);
+       StrBufAppendPrintf(QMessage, "%ld", MyQItem->Submitted);
+
        if (StrLength(MyQItem->BounceTo) > 0) {
                StrBufAppendBufPlain(QMessage, HKEY("\nbounceto|"), 0);
                StrBufAppendBuf(QMessage, MyQItem->BounceTo, 0);
@@ -280,6 +283,7 @@ StrBuf *SerializeQueueItem(OneQueItem *MyQItem)
                        continue; /* skip already sent ones from the spoolfile. */
 
                for (i=0; i < ThisItem->nAttempts; i++) {
+                       /* TODO: most probably there is just one retry/attempted per message! */
                        StrBufAppendBufPlain(QMessage, HKEY("\nretry|"), 0);
                        StrBufAppendPrintf(QMessage, "%ld", 
                                           ThisItem->Attempts[i].retry);
@@ -311,6 +315,7 @@ void NewMailQEntry(OneQueItem *Item)
 
        if (Item->MailQEntries == NULL)
                Item->MailQEntries = NewHash(1, Flathash);
+       Item->Current->StatusMessage = NewStrBuf();
        Item->Current->n = GetCount(Item->MailQEntries);
        Put(Item->MailQEntries, IKEY(Item->Current->n), Item->Current, FreeMailQEntry);
 }
@@ -360,6 +365,13 @@ void QItem_Handle_retry(OneQueItem *Item, StrBuf *Line, const char **Pos)
        Item->Current->Attempts[Item->Current->nAttempts].retry = StrBufExtractNext_int(Line, Pos, '|');
 }
 
+
+void QItem_Handle_Submitted(OneQueItem *Item, StrBuf *Line, const char **Pos)
+{
+       Item->Submitted = atol(*Pos);
+
+}
+
 void QItem_Handle_Attempted(OneQueItem *Item, StrBuf *Line, const char **Pos)
 {
        if (Item->Current == NULL)
@@ -409,6 +421,167 @@ StrBuf *smtp_load_msg(OneQueItem *MyQItem, int n)
 
 
 
+/*
+ * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
+ * instructions for "5" codes (permanent fatal errors) and produce/deliver
+ * a "bounce" message (delivery status notification).
+ */
+void smtpq_do_bounce(OneQueItem *MyQItem, StrBuf *OMsgTxt) 
+{
+       StrBuf *boundary, *Msg = NULL; 
+       int num_bounces = 0;
+       struct CtdlMessage *bmsg = NULL;
+       int give_up = 0;
+       struct recptypes *valid;
+       int successful_bounce = 0;
+       static int seq = 0;
+       StrBuf *BounceMB;
+       HashPos  *It;
+       void *vQE;
+       long len;
+       const char *Key;
+
+       CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
+       
+       if ( (time(NULL) - MyQItem->Submitted) > SMTP_GIVE_UP ) {
+               give_up = 1;/// TODO: replace time by libevq timer get
+       }
+
+       /*
+        * Now go through the instructions checking for stuff.
+        */
+       It = GetNewHashPos(MyQItem->MailQEntries, 0);
+       while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))
+       {
+               MailQEntry *ThisItem = vQE;
+               if ((ThisItem->Status == 5) || /* failed now? */
+                   ((give_up == 1) && (ThisItem->Status != 2))) /* giving up after failed attempts... */
+               {
+                       if (num_bounces == 0)
+                               Msg = NewStrBufPlain(NULL, 1024);
+                       ++num_bounces;
+       
+                       StrBufAppendBuf(Msg, ThisItem->Recipient, 0);
+                       StrBufAppendBufPlain(Msg, HKEY(": "), 0);
+                       StrBufAppendBuf(Msg, ThisItem->StatusMessage, 0);
+                       StrBufAppendBufPlain(Msg, HKEY("\r\n"), 0);
+               }
+       }
+       DeleteHashPos(&It);
+
+       /* Deliver the bounce if there's anything worth mentioning */
+       CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
+
+       if (num_bounces == 0)
+               return;
+
+       boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
+       StrBufAppendPrintf(boundary, "%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
+
+       /* Start building our bounce message; go shopping for memory first. */
+       BounceMB = NewStrBufPlain(NULL, 
+                                 1024 + /* mime stuff.... */
+                                 StrLength(Msg) +  /* the bounce information... */
+                                 StrLength(OMsgTxt)); /* the original message */
+       if (BounceMB == NULL) {
+               FreeStrBuf(&boundary);
+               CtdlLogPrintf(CTDL_ERR, "Failed to alloc() bounce message.\n");
+
+               return;
+       }
+
+       bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
+       if (bmsg == NULL) {
+               FreeStrBuf(&boundary);
+               FreeStrBuf(&BounceMB);
+               CtdlLogPrintf(CTDL_ERR, "Failed to alloc() bounce message.\n");
+
+               return;
+       }
+       memset(bmsg, 0, sizeof(struct CtdlMessage));
+
+
+       StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
+       StrBufAppendBuf(BounceMB, boundary, 0);
+        StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
+        StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
+        StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
+        StrBufAppendBuf(BounceMB, boundary, 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
+        StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);
+
+       if (give_up) 
+               StrBufAppendBufPlain(
+                       BounceMB, 
+                       HKEY(
+                               "A message you sent could not be delivered to some or all of its recipients\n"
+                               "due to prolonged unavailability of its destination(s).\n"
+                               "Giving up on the following addresses:\n\n"
+                               ), 0);
+       else 
+               StrBufAppendBufPlain(
+                       BounceMB, 
+                       HKEY(
+                               "A message you sent could not be delivered to some or all of its recipients.\n"
+                               "The following addresses were undeliverable:\n\n"
+                               ), 0);
+
+       StrBufAppendBuf(BounceMB, Msg, 0);
+       FreeStrBuf(&Msg);
+
+       /* Attach the original message */
+       StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
+       StrBufAppendBuf(BounceMB, boundary, 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
+       StrBufAppendBuf(BounceMB, OMsgTxt, 0);
+
+       /* Close the multipart MIME scope */
+        StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
+       StrBufAppendBuf(BounceMB, boundary, 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
+
+
+
+        bmsg->cm_magic = CTDLMESSAGE_MAGIC;
+        bmsg->cm_anon_type = MES_NORMAL;
+        bmsg->cm_format_type = FMT_RFC822;
+
+        bmsg->cm_fields['O'] = strdup(MAILROOM);
+        bmsg->cm_fields['N'] = strdup(config.c_nodename);
+        bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
+       bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
+
+       /* First try the user who sent the message */
+       if (StrLength(MyQItem->BounceTo) == 0) 
+               CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
+       else
+               CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", ChrPtr(MyQItem->BounceTo));
+
+       /* Can we deliver the bounce to the original sender? */
+       valid = validate_recipients(ChrPtr(MyQItem->BounceTo), NULL, 0);
+       if ((valid != NULL) && (valid->num_error == 0)) {
+               CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
+               successful_bounce = 1;
+       }
+
+       /* If not, post it in the Aide> room */
+       if (successful_bounce == 0) {
+               CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
+       }
+
+       /* Free up the memory we used */
+       free_recipients(valid);
+       FreeStrBuf(&boundary);
+       CtdlFreeMessage(bmsg);
+       CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
+}
+
 /*
  * smtp_do_procmsg()
  *
@@ -655,7 +828,8 @@ CTDL_MODULE_INIT(smtp_queu)
                Put(QItemHandlers, HKEY("attempted"), QItem_Handle_Attempted, reference_free_handler);
                Put(QItemHandlers, HKEY("remote"), QItem_Handle_Recipient, reference_free_handler);
                Put(QItemHandlers, HKEY("bounceto"), QItem_Handle_BounceTo, reference_free_handler);
-///submitted /TODO: flush qitemhandlers on exit
+               Put(QItemHandlers, HKEY("submitted"), QItem_Handle_Submitted, reference_free_handler);
+////TODO: flush qitemhandlers on exit
                smtp_init_spoolout();
 
                CtdlRegisterCleanupHook(smtp_evq_cleanup);
index a7b97cd90f150404af6abe68636be69cbc353f41..023b479caf285c2c2d2f3796acc58c48675771ee 100644 (file)
@@ -14,6 +14,15 @@ typedef struct _mailq_entry {
        StrBuf *Recipient;
        StrBuf *StatusMessage;
        int Status;
+       /**<
+        * 0 = No delivery has yet been attempted
+        * 2 = Delivery was successful
+        * 4 = A transient error was experienced ... try again later
+        * 5 = Delivery to this address failed permanently.  The error message
+         *     should be placed in the fourth field so that a bounce message may
+        *     be generated.
+        */
+
        int n;
        int Active;
 }MailQEntry;
@@ -21,6 +30,7 @@ typedef struct _mailq_entry {
 typedef struct queueitem {
        long MessageID;
        long QueMsgID;
+       long Submitted;
        int FailNow;
        HashList *MailQEntries;
        MailQEntry *Current; /* copy of the currently parsed item in the MailQEntries list; if null add a new one. */
@@ -35,3 +45,5 @@ int DecreaseQReference(OneQueItem *MyQItem);
 void RemoveQItem(OneQueItem *MyQItem);
 int CountActiveQueueEntries(OneQueItem *MyQItem);
 StrBuf *SerializeQueueItem(OneQueItem *MyQItem);
+
+void smtpq_do_bounce(OneQueItem *MyQItem, StrBuf *OMsgTxt);
index a852af55211c87a735680bfd55eb5392feb8bc95..f1c2219427e71924e00defac16893176e1da7dee 100644 (file)
@@ -23,7 +23,7 @@ a single instruction (usually a recipient).  Fields are separated by the
 vertical bar character ("|") and there will always be at least one field on
 each line.
  
+ -- Once per Queue-Item --
  
  INSTRUCTION:  msgid
  SYNTAX:       msgid|0000000
@@ -67,7 +67,16 @@ fails).
     CtdlSaveMsg(), and therefore may be a local username, a user on another
     Citadel, or an Internet e-mail address.
  
+
+ INSTRUCTION:  envelope_from
+ SYNTAX:       envelope_from|blurdybloop@example.com
+ DESCRIPTION:
+    Sets a value to be used as the envelope sender during the 'MAIL FROM:'
+    phase of the SMTP transaction.  If an envelope sender is not supplied,
+    one is extracted from the message body.
+
+-- Once per Remote-part per Queue-Item --
+
  INSTRUCTION:  remote
  SYNTAX:       remote|friko@mumjiboolean.com|0|delivery status message
  DESCRIPTION:
@@ -79,11 +88,3 @@ fails).
        5 = Delivery to this address failed permanently.  The error message
            should be placed in the fourth field so that a bounce message may
            be generated.
-
-
- INSTRUCTION:  envelope_from
- SYNTAX:       envelope_from|blurdybloop@example.com
- DESCRIPTION:
-    Sets a value to be used as the envelope sender during the 'MAIL FROM:'
-    phase of the SMTP transaction.  If an envelope sender is not supplied,
-    one is extracted from the message body.