2 * This module is an SMTP and ESMTP implementation for the Citadel system.
3 * It is compliant with all of the following:
5 * RFC 821 - Simple Mail Transfer Protocol
6 * RFC 876 - Survey of SMTP Implementations
7 * RFC 1047 - Duplicate messages and SMTP
8 * RFC 1652 - 8 bit MIME
9 * RFC 1869 - Extended Simple Mail Transfer Protocol
10 * RFC 1870 - SMTP Service Extension for Message Size Declaration
11 * RFC 2033 - Local Mail Transfer Protocol
12 * RFC 2197 - SMTP Service Extension for Command Pipelining
13 * RFC 2476 - Message Submission
14 * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
15 * RFC 2554 - SMTP Service Extension for Authentication
16 * RFC 2821 - Simple Mail Transfer Protocol
17 * RFC 2822 - Internet Message Format
18 * RFC 2920 - SMTP Service Extension for Command Pipelining
20 * The VRFY and EXPN commands have been removed from this implementation
21 * because nobody uses these commands anymore, except for spammers.
23 * Copyright (c) 1998-2009 by the citadel.org team
25 * This program is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation; either version 3 of the License, or
28 * (at your option) any later version.
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public License
36 * along with this program; if not, write to the Free Software
37 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
49 #include <sys/types.h>
52 #if TIME_WITH_SYS_TIME
53 # include <sys/time.h>
57 # include <sys/time.h>
67 #include <sys/socket.h>
68 #include <netinet/in.h>
69 #include <arpa/inet.h>
70 #include <libcitadel.h>
73 #include "citserver.h"
80 #include "internet_addressing.h"
83 #include "clientsocket.h"
84 #include "locate_host.h"
85 #include "citadel_dirs.h"
87 #include "ctdl_module.h"
89 #include "smtp_util.h"
91 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
93 /*****************************************************************************/
94 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
95 /*****************************************************************************/
102 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
105 void smtp_try(const char *key, const char *addr, int *status,
106 char *dsn, size_t n, long msgnum, char *envelope_from)
113 char user[1024], node[1024], name[1024];
128 /* Parse out the host portion of the recipient address */
129 process_rfc822_addr(addr, user, node, name);
131 syslog(LOG_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
134 /* Load the message out of the database */
135 CCC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
136 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, (ESC_DOT|SUPPRESS_ENV_TO) );
137 msg_size = StrLength(CC->redirect_buffer);
138 msgtext = SmashStrBuf(&CC->redirect_buffer);
140 /* If no envelope_from is supplied, extract one from the message */
141 if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
142 strcpy(mailfrom, "");
146 if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0) {
149 if (!strncasecmp(buf, "From:", 5)) {
150 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
152 for (i=0; mailfrom[i]; ++i) {
153 if (!isprint(mailfrom[i])) {
154 strcpy(&mailfrom[i], &mailfrom[i+1]);
159 /* Strip out parenthesized names */
162 for (i=0; mailfrom[i]; ++i) {
163 if (mailfrom[i] == '(') lp = i;
164 if (mailfrom[i] == ')') rp = i;
166 if ((lp>0)&&(rp>lp)) {
167 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
170 /* Prefer brokketized names */
173 for (i=0; mailfrom[i]; ++i) {
174 if (mailfrom[i] == '<') lp = i;
175 if (mailfrom[i] == '>') rp = i;
177 if ( (lp>=0) && (rp>lp) ) {
179 strcpy(mailfrom, &mailfrom[lp + 1]);
184 } while (scan_done == 0);
185 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
186 stripallbut(mailfrom, '<', '>');
187 envelope_from = mailfrom;
190 /* Figure out what mail exchanger host we have to connect to */
191 num_mxhosts = getmx(mxhosts, node);
192 syslog(LOG_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
193 if (num_mxhosts < 1) {
195 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
200 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
202 extract_token(buf, mxhosts, mx, '|', sizeof buf);
205 if (num_tokens(buf, '@') > 1) {
206 strcpy (mx_user, buf);
207 endpart = strrchr(mx_user, '@');
209 strcpy (mx_host, endpart + 1);
210 endpart = strrchr(mx_user, ':');
211 if (endpart != NULL) {
212 strcpy(mx_pass, endpart+1);
217 strcpy (mx_host, buf);
218 endpart = strrchr(mx_host, ':');
221 strcpy(mx_port, endpart + 1);
224 strcpy(mx_port, "25");
226 syslog(LOG_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
227 sock = sock_connect(mx_host, mx_port);
228 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
231 syslog(LOG_DEBUG, "SMTP client: connected!\n");
233 fdflags = fcntl(sock, F_GETFL);
236 "unable to get SMTP-Client socket flags! %s \n",
238 fdflags = fdflags | O_NONBLOCK;
239 if (fcntl(sock, F_SETFL, fdflags) < 0)
241 "unable to set SMTP-Client socket nonblocking flags! %s \n",
246 snprintf(dsn, SIZ, "%s", strerror(errno));
249 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
255 *status = 4; /* dsn is already filled in */
259 CCC->sReadBuf = NewStrBuf();
260 CCC->sMigrateBuf = NewStrBuf();
263 /* Process the SMTP greeting from the server */
264 if (ml_sock_gets(&sock, buf, 90) < 0) {
266 strcpy(dsn, "Connection broken during SMTP conversation");
269 syslog(LOG_DEBUG, "<%s\n", buf);
273 safestrncpy(dsn, &buf[4], 1023);
278 safestrncpy(dsn, &buf[4], 1023);
283 /* At this point we know we are talking to a real SMTP server */
285 /* Do a EHLO command. If it fails, try the HELO command. */
286 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
287 syslog(LOG_DEBUG, ">%s", buf);
288 sock_write(&sock, buf, strlen(buf));
289 if (ml_sock_gets(&sock, buf, 30) < 0) {
291 strcpy(dsn, "Connection broken during SMTP HELO");
294 syslog(LOG_DEBUG, "<%s\n", buf);
296 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
297 syslog(LOG_DEBUG, ">%s", buf);
298 sock_write(&sock, buf, strlen(buf));
299 if (ml_sock_gets(&sock, buf, 30) < 0) {
301 strcpy(dsn, "Connection broken during SMTP HELO");
308 safestrncpy(dsn, &buf[4], 1023);
313 safestrncpy(dsn, &buf[4], 1023);
318 /* Do an AUTH command if necessary */
319 if (!IsEmptyStr(mx_user)) {
321 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
322 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
323 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
324 syslog(LOG_DEBUG, ">%s", buf);
325 sock_write(&sock, buf, strlen(buf));
326 if (ml_sock_gets(&sock, buf, 30) < 0) {
328 strcpy(dsn, "Connection broken during SMTP AUTH");
331 syslog(LOG_DEBUG, "<%s\n", buf);
335 safestrncpy(dsn, &buf[4], 1023);
340 safestrncpy(dsn, &buf[4], 1023);
346 /* previous command succeeded, now try the MAIL FROM: command */
347 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
348 syslog(LOG_DEBUG, ">%s", buf);
349 sock_write(&sock, buf, strlen(buf));
350 if (ml_sock_gets(&sock, buf, 30) < 0) {
352 strcpy(dsn, "Connection broken during SMTP MAIL");
355 syslog(LOG_DEBUG, "<%s\n", buf);
359 safestrncpy(dsn, &buf[4], 1023);
364 safestrncpy(dsn, &buf[4], 1023);
369 /* MAIL succeeded, now try the RCPT To: command */
370 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
371 syslog(LOG_DEBUG, ">%s", buf);
372 sock_write(&sock, buf, strlen(buf));
373 if (ml_sock_gets(&sock, buf, 30) < 0) {
375 strcpy(dsn, "Connection broken during SMTP RCPT");
378 syslog(LOG_DEBUG, "<%s\n", buf);
382 safestrncpy(dsn, &buf[4], 1023);
387 safestrncpy(dsn, &buf[4], 1023);
392 /* RCPT succeeded, now try the DATA command */
393 syslog(LOG_DEBUG, ">DATA\n");
394 sock_write(&sock, "DATA\r\n", 6);
395 if (ml_sock_gets(&sock, buf, 30) < 0) {
397 strcpy(dsn, "Connection broken during SMTP DATA");
400 syslog(LOG_DEBUG, "<%s\n", buf);
404 safestrncpy(dsn, &buf[4], 1023);
409 safestrncpy(dsn, &buf[4], 1023);
414 /* If we reach this point, the server is expecting data.*/
415 sock_write_timeout(&sock,
418 (msg_size / 128) + 50);
419 if (msgtext[msg_size-1] != 10) {
420 syslog(LOG_WARNING, "Possible problem: message did not "
421 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
423 sock_write(&sock, "\r\n", 2);
426 sock_write(&sock, ".\r\n", 3);
428 if (ml_sock_gets(&sock, buf, 90) < 0) {
430 strcpy(dsn, "Connection broken during SMTP message transmit");
433 syslog(LOG_DEBUG, "%s\n", buf);
437 safestrncpy(dsn, &buf[4], 1023);
442 safestrncpy(dsn, &buf[4], 1023);
448 safestrncpy(dsn, &buf[4], 1023);
451 syslog(LOG_DEBUG, ">QUIT\n");
452 sock_write(&sock, "QUIT\r\n", 6);
453 ml_sock_gets(&sock, buf, 30);
454 syslog(LOG_DEBUG, "<%s\n", buf);
455 syslog(LOG_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
459 FreeStrBuf(&CCC->sReadBuf);
460 FreeStrBuf(&CCC->sMigrateBuf);
464 /* Write something to the syslog(which may or may not be where the
465 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
467 syslog((LOG_MAIL | LOG_INFO),
468 "%ld: to=<%s>, relay=%s, stat=%s",
481 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
482 * instructions for "5" codes (permanent fatal errors) and produce/deliver
483 * a "bounce" message (delivery status notification).
485 void smtp_do_bounce(char *instr) {
497 long bounce_msgid = (-1);
498 time_t submitted = 0L;
499 struct CtdlMessage *bmsg = NULL;
501 struct recptypes *valid;
502 int successful_bounce = 0;
507 syslog(LOG_DEBUG, "smtp_do_bounce() called\n");
508 strcpy(bounceto, "");
509 boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
510 StrBufAppendPrintf(boundary, "%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
511 lines = num_tokens(instr, '\n');
513 /* See if it's time to give up on delivery of this message */
514 for (i=0; i<lines; ++i) {
515 extract_token(buf, instr, i, '\n', sizeof buf);
516 extract_token(key, buf, 0, '|', sizeof key);
517 extract_token(addr, buf, 1, '|', sizeof addr);
518 if (!strcasecmp(key, "submitted")) {
519 submitted = atol(addr);
523 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
527 /* Start building our bounce message */
529 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
530 if (bmsg == NULL) return;
531 memset(bmsg, 0, sizeof(struct CtdlMessage));
532 BounceMB = NewStrBufPlain(NULL, 1024);
534 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
535 bmsg->cm_anon_type = MES_NORMAL;
536 bmsg->cm_format_type = FMT_RFC822;
537 bmsg->cm_fields['A'] = strdup("Citadel");
538 bmsg->cm_fields['O'] = strdup(MAILROOM);
539 bmsg->cm_fields['N'] = strdup(config.c_nodename);
540 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
541 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
542 StrBufAppendBuf(BounceMB, boundary, 0);
543 StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
544 StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
545 StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
546 StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
547 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
548 StrBufAppendBuf(BounceMB, boundary, 0);
549 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
550 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);
552 if (give_up) StrBufAppendBufPlain(BounceMB, HKEY(
553 "A message you sent could not be delivered to some or all of its recipients\n"
554 "due to prolonged unavailability of its destination(s).\n"
555 "Giving up on the following addresses:\n\n"
558 else StrBufAppendBufPlain(BounceMB, HKEY(
559 "A message you sent could not be delivered to some or all of its recipients.\n"
560 "The following addresses were undeliverable:\n\n"
564 * Now go through the instructions checking for stuff.
566 for (i=0; i<lines; ++i) {
569 extract_token(buf, instr, i, '\n', sizeof buf);
570 extract_token(key, buf, 0, '|', sizeof key);
571 addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
572 status = extract_int(buf, 2);
573 dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
576 syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
577 key, addr, status, dsn);
579 if (!strcasecmp(key, "bounceto")) {
580 strcpy(bounceto, addr);
583 if (!strcasecmp(key, "msgid")) {
587 if (!strcasecmp(key, "remote")) {
588 if (status == 5) bounce_this = 1;
589 if (give_up) bounce_this = 1;
595 StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
596 StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
597 StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
598 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
600 remove_token(instr, i, '\n');
606 /* Attach the original message */
608 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
609 StrBufAppendBuf(BounceMB, boundary, 0);
610 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
611 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0);
612 StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
613 StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0);
614 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
616 CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
617 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
618 StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
619 FreeStrBuf(&CC->redirect_buffer);
622 /* Close the multipart MIME scope */
623 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
624 StrBufAppendBuf(BounceMB, boundary, 0);
625 StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
626 if (bmsg->cm_fields['A'] != NULL)
627 free(bmsg->cm_fields['A']);
628 bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
629 /* Deliver the bounce if there's anything worth mentioning */
630 syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
631 if (num_bounces > 0) {
633 /* First try the user who sent the message */
634 syslog(LOG_DEBUG, "bounce to user? <%s>\n", bounceto);
635 if (IsEmptyStr(bounceto)) {
636 syslog(LOG_ERR, "No bounce address specified\n");
637 bounce_msgid = (-1L);
640 /* Can we deliver the bounce to the original sender? */
641 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
643 if (valid->num_error == 0) {
644 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
645 successful_bounce = 1;
649 /* If not, post it in the Aide> room */
650 if (successful_bounce == 0) {
651 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
654 /* Free up the memory we used */
656 free_recipients(valid);
659 FreeStrBuf(&boundary);
660 CtdlFreeMessage(bmsg);
661 syslog(LOG_DEBUG, "Done processing bounces\n");
666 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
667 * set of delivery instructions for completed deliveries and remove them.
669 * It returns the number of incomplete deliveries remaining.
671 int smtp_purge_completed_deliveries(char *instr) {
682 lines = num_tokens(instr, '\n');
683 for (i=0; i<lines; ++i) {
684 extract_token(buf, instr, i, '\n', sizeof buf);
685 extract_token(key, buf, 0, '|', sizeof key);
686 extract_token(addr, buf, 1, '|', sizeof addr);
687 status = extract_int(buf, 2);
688 extract_token(dsn, buf, 3, '|', sizeof dsn);
692 if (!strcasecmp(key, "remote")) {
693 if (status == 2) completed = 1;
698 remove_token(instr, i, '\n');
711 * Called by smtp_do_queue() to handle an individual message.
713 void smtp_do_procmsg(long msgnum, void *userdata) {
714 struct CtdlMessage *msg = NULL;
716 char *results = NULL;
724 char envelope_from[1024];
725 long text_msgid = (-1);
726 int incomplete_deliveries_remaining;
727 time_t attempted = 0L;
728 time_t last_attempted = 0L;
729 time_t retry = SMTP_RETRY_INTERVAL;
731 syslog(LOG_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
732 strcpy(envelope_from, "");
734 msg = CtdlFetchMessage(msgnum, 1);
736 syslog(LOG_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
740 instr = strdup(msg->cm_fields['M']);
741 CtdlFreeMessage(msg);
743 /* Strip out the headers amd any other non-instruction line */
744 lines = num_tokens(instr, '\n');
745 for (i=0; i<lines; ++i) {
746 extract_token(buf, instr, i, '\n', sizeof buf);
747 if (num_tokens(buf, '|') < 2) {
748 remove_token(instr, i, '\n');
754 /* Learn the message ID and find out about recent delivery attempts */
755 lines = num_tokens(instr, '\n');
756 for (i=0; i<lines; ++i) {
757 extract_token(buf, instr, i, '\n', sizeof buf);
758 extract_token(key, buf, 0, '|', sizeof key);
759 if (!strcasecmp(key, "msgid")) {
760 text_msgid = extract_long(buf, 1);
762 if (!strcasecmp(key, "envelope_from")) {
763 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
765 if (!strcasecmp(key, "retry")) {
766 /* double the retry interval after each attempt */
767 retry = extract_long(buf, 1) * 2L;
768 if (retry > SMTP_RETRY_MAX) {
769 retry = SMTP_RETRY_MAX;
771 remove_token(instr, i, '\n');
773 if (!strcasecmp(key, "attempted")) {
774 attempted = extract_long(buf, 1);
775 if (attempted > last_attempted)
776 last_attempted = attempted;
781 * Postpone delivery if we've already tried recently.
783 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
784 syslog(LOG_DEBUG, "SMTP client: Retry time not yet reached.\n");
791 * Bail out if there's no actual message associated with this
793 if (text_msgid < 0L) {
794 syslog(LOG_ERR, "SMTP client: no 'msgid' directive found!\n");
799 /* Plow through the instructions looking for 'remote' directives and
800 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
801 * were experienced and it's time to try again)
803 lines = num_tokens(instr, '\n');
804 for (i=0; i<lines; ++i) {
805 extract_token(buf, instr, i, '\n', sizeof buf);
806 extract_token(key, buf, 0, '|', sizeof key);
807 extract_token(addr, buf, 1, '|', sizeof addr);
808 status = extract_int(buf, 2);
809 extract_token(dsn, buf, 3, '|', sizeof dsn);
810 if ( (!strcasecmp(key, "remote"))
811 && ((status==0)||(status==3)||(status==4)) ) {
813 /* Remove this "remote" instruction from the set,
814 * but replace the set's final newline if
815 * remove_token() stripped it. It has to be there.
817 remove_token(instr, i, '\n');
818 if (instr[strlen(instr)-1] != '\n') {
824 syslog(LOG_DEBUG, "SMTP client: Trying <%s>\n", addr);
825 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
827 if (results == NULL) {
828 results = malloc(1024);
829 memset(results, 0, 1024);
832 results = realloc(results, strlen(results) + 1024);
834 snprintf(&results[strlen(results)], 1024,
836 key, addr, status, dsn);
841 if (results != NULL) {
842 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
843 strcat(instr, results);
848 /* Generate 'bounce' messages */
849 smtp_do_bounce(instr);
851 /* Go through the delivery list, deleting completed deliveries */
852 incomplete_deliveries_remaining =
853 smtp_purge_completed_deliveries(instr);
857 * No delivery instructions remain, so delete both the instructions
858 * message and the message message.
860 if (incomplete_deliveries_remaining <= 0) {
863 delmsgs[1] = text_msgid;
864 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
868 * Uncompleted delivery instructions remain, so delete the old
869 * instructions and replace with the updated ones.
871 if (incomplete_deliveries_remaining > 0) {
872 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
873 msg = malloc(sizeof(struct CtdlMessage));
874 memset(msg, 0, sizeof(struct CtdlMessage));
875 msg->cm_magic = CTDLMESSAGE_MAGIC;
876 msg->cm_anon_type = MES_NORMAL;
877 msg->cm_format_type = FMT_RFC822;
878 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
879 snprintf(msg->cm_fields['M'],
881 "Content-type: %s\n\n%s\n"
884 SPOOLMIME, instr, (long)time(NULL), (long)retry );
885 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
886 CtdlFreeMessage(msg);
893 /*****************************************************************************/
894 /* SMTP UTILITY COMMANDS */
895 /*****************************************************************************/
897 void cmd_smtp(char *argbuf) {
904 if (CtdlAccessCheck(ac_aide)) return;
906 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
908 if (!strcasecmp(cmd, "mx")) {
909 extract_token(node, argbuf, 1, '|', sizeof node);
910 num_mxhosts = getmx(buf, node);
911 cprintf("%d %d MX hosts listed for %s\n",
912 LISTING_FOLLOWS, num_mxhosts, node);
913 for (i=0; i<num_mxhosts; ++i) {
914 extract_token(node, buf, i, '|', sizeof node);
915 cprintf("%s\n", node);
921 else if (!strcasecmp(cmd, "runqueue")) {
923 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
928 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
935 * smtp_queue_thread()
937 * Run through the queue sending out messages.
939 void *smtp_queue_thread(void *arg) {
940 int num_processed = 0;
941 struct CitContext smtp_queue_CC;
943 CtdlFillSystemContext(&smtp_queue_CC, "SMTP Send");
944 citthread_setspecific(MyConKey, (void *)&smtp_queue_CC);
945 syslog(LOG_DEBUG, "smtp_queue_thread() initializing\n");
947 while (!CtdlThreadCheckStop()) {
949 syslog(LOG_INFO, "SMTP client: processing outbound queue\n");
951 if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
952 syslog(LOG_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
955 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
957 syslog(LOG_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
961 CtdlClearSystemContext();
967 * Initialize the SMTP outbound queue
969 void smtp_init_spoolout(void) {
970 struct ctdlroom qrbuf;
973 * Create the room. This will silently fail if the room already
974 * exists, and that's perfectly ok, because we want it to exist.
976 CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
979 * Make sure it's set to be a "system room" so it doesn't show up
980 * in the <K>nown rooms list for Aides.
982 if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
983 qrbuf.QRflags2 |= QR2_SYSTEM;
984 CtdlPutRoomLock(&qrbuf);
991 CTDL_MODULE_INIT(smtp_client)
995 smtp_init_spoolout();
996 CtdlThreadCreate("SMTP Send", CTDLTHREAD_BIGSTACK, smtp_queue_thread, NULL);
997 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1000 /* return our Subversion id for the Log */