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-2011 by the citadel.org team
25 * This program is open source 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)) {
186 char badmail_filename[128];
187 snprintf(badmail_filename, sizeof badmail_filename, "/tmp/badmail.%d.%ld",
190 FILE *badmail_fp = fopen(badmail_filename, "w");
191 fwrite(msgtext, msg_size, 1, badmail_fp);
194 stripallbut(mailfrom, '<', '>');
195 envelope_from = mailfrom;
198 /* Figure out what mail exchanger host we have to connect to */
199 num_mxhosts = getmx(mxhosts, node);
200 syslog(LOG_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
201 if (num_mxhosts < 1) {
203 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
208 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
210 extract_token(buf, mxhosts, mx, '|', sizeof buf);
213 if (num_tokens(buf, '@') > 1) {
214 strcpy (mx_user, buf);
215 endpart = strrchr(mx_user, '@');
217 strcpy (mx_host, endpart + 1);
218 endpart = strrchr(mx_user, ':');
219 if (endpart != NULL) {
220 strcpy(mx_pass, endpart+1);
225 strcpy (mx_host, buf);
226 endpart = strrchr(mx_host, ':');
229 strcpy(mx_port, endpart + 1);
232 strcpy(mx_port, "25");
234 syslog(LOG_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
235 sock = sock_connect(mx_host, mx_port);
236 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
239 syslog(LOG_DEBUG, "SMTP client: connected!\n");
241 fdflags = fcntl(sock, F_GETFL);
244 "unable to get SMTP-Client socket flags! %s \n",
246 fdflags = fdflags | O_NONBLOCK;
247 if (fcntl(sock, F_SETFL, fdflags) < 0)
249 "unable to set SMTP-Client socket nonblocking flags! %s \n",
254 snprintf(dsn, SIZ, "%s", strerror(errno));
257 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
263 *status = 4; /* dsn is already filled in */
267 CCC->sReadBuf = NewStrBuf();
268 CCC->sMigrateBuf = NewStrBuf();
271 /* Process the SMTP greeting from the server */
272 if (ml_sock_gets(&sock, buf, 90) < 0) {
274 strcpy(dsn, "Connection broken during SMTP conversation");
277 syslog(LOG_DEBUG, "<%s\n", buf);
281 safestrncpy(dsn, &buf[4], 1023);
286 safestrncpy(dsn, &buf[4], 1023);
291 /* At this point we know we are talking to a real SMTP server */
293 /* Do a EHLO command. If it fails, try the HELO command. */
294 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
295 syslog(LOG_DEBUG, ">%s", buf);
296 sock_write(&sock, buf, strlen(buf));
297 if (ml_sock_gets(&sock, buf, 30) < 0) {
299 strcpy(dsn, "Connection broken during SMTP HELO");
302 syslog(LOG_DEBUG, "<%s\n", buf);
304 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
305 syslog(LOG_DEBUG, ">%s", buf);
306 sock_write(&sock, buf, strlen(buf));
307 if (ml_sock_gets(&sock, buf, 30) < 0) {
309 strcpy(dsn, "Connection broken during SMTP HELO");
316 safestrncpy(dsn, &buf[4], 1023);
321 safestrncpy(dsn, &buf[4], 1023);
326 /* Do an AUTH command if necessary */
327 if (!IsEmptyStr(mx_user)) {
329 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
330 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
331 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
332 syslog(LOG_DEBUG, ">%s", buf);
333 sock_write(&sock, buf, strlen(buf));
334 if (ml_sock_gets(&sock, buf, 30) < 0) {
336 strcpy(dsn, "Connection broken during SMTP AUTH");
339 syslog(LOG_DEBUG, "<%s\n", buf);
343 safestrncpy(dsn, &buf[4], 1023);
348 safestrncpy(dsn, &buf[4], 1023);
354 /* previous command succeeded, now try the MAIL FROM: command */
355 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
356 syslog(LOG_DEBUG, ">%s", buf);
357 sock_write(&sock, buf, strlen(buf));
358 if (ml_sock_gets(&sock, buf, 30) < 0) {
360 strcpy(dsn, "Connection broken during SMTP MAIL");
363 syslog(LOG_DEBUG, "<%s\n", buf);
367 safestrncpy(dsn, &buf[4], 1023);
372 safestrncpy(dsn, &buf[4], 1023);
377 /* MAIL succeeded, now try the RCPT To: command */
378 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
379 syslog(LOG_DEBUG, ">%s", buf);
380 sock_write(&sock, buf, strlen(buf));
381 if (ml_sock_gets(&sock, buf, 30) < 0) {
383 strcpy(dsn, "Connection broken during SMTP RCPT");
386 syslog(LOG_DEBUG, "<%s\n", buf);
390 safestrncpy(dsn, &buf[4], 1023);
395 safestrncpy(dsn, &buf[4], 1023);
400 /* RCPT succeeded, now try the DATA command */
401 syslog(LOG_DEBUG, ">DATA\n");
402 sock_write(&sock, "DATA\r\n", 6);
403 if (ml_sock_gets(&sock, buf, 30) < 0) {
405 strcpy(dsn, "Connection broken during SMTP DATA");
408 syslog(LOG_DEBUG, "<%s\n", buf);
412 safestrncpy(dsn, &buf[4], 1023);
417 safestrncpy(dsn, &buf[4], 1023);
422 /* If we reach this point, the server is expecting data.*/
423 sock_write_timeout(&sock,
426 (msg_size / 128) + 50);
427 if (msgtext[msg_size-1] != 10) {
428 syslog(LOG_WARNING, "Possible problem: message did not "
429 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
431 sock_write(&sock, "\r\n", 2);
434 sock_write(&sock, ".\r\n", 3);
436 if (ml_sock_gets(&sock, buf, 90) < 0) {
438 strcpy(dsn, "Connection broken during SMTP message transmit");
441 syslog(LOG_DEBUG, "%s\n", buf);
445 safestrncpy(dsn, &buf[4], 1023);
450 safestrncpy(dsn, &buf[4], 1023);
456 safestrncpy(dsn, &buf[4], 1023);
459 syslog(LOG_DEBUG, ">QUIT\n");
460 sock_write(&sock, "QUIT\r\n", 6);
461 ml_sock_gets(&sock, buf, 30);
462 syslog(LOG_DEBUG, "<%s\n", buf);
463 syslog(LOG_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
467 FreeStrBuf(&CCC->sReadBuf);
468 FreeStrBuf(&CCC->sMigrateBuf);
472 /* Write something to the syslog(which may or may not be where the
473 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
475 syslog((LOG_MAIL | LOG_INFO),
476 "%ld: to=<%s>, relay=%s, stat=%s",
489 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
490 * instructions for "5" codes (permanent fatal errors) and produce/deliver
491 * a "bounce" message (delivery status notification).
493 void smtp_do_bounce(char *instr) {
505 long bounce_msgid = (-1);
506 time_t submitted = 0L;
507 struct CtdlMessage *bmsg = NULL;
509 struct recptypes *valid;
510 int successful_bounce = 0;
515 syslog(LOG_DEBUG, "smtp_do_bounce() called\n");
516 strcpy(bounceto, "");
517 boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
518 StrBufAppendPrintf(boundary, "%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
519 lines = num_tokens(instr, '\n');
521 /* See if it's time to give up on delivery of this message */
522 for (i=0; i<lines; ++i) {
523 extract_token(buf, instr, i, '\n', sizeof buf);
524 extract_token(key, buf, 0, '|', sizeof key);
525 extract_token(addr, buf, 1, '|', sizeof addr);
526 if (!strcasecmp(key, "submitted")) {
527 submitted = atol(addr);
531 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
535 /* Start building our bounce message */
537 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
538 if (bmsg == NULL) return;
539 memset(bmsg, 0, sizeof(struct CtdlMessage));
540 BounceMB = NewStrBufPlain(NULL, 1024);
542 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
543 bmsg->cm_anon_type = MES_NORMAL;
544 bmsg->cm_format_type = FMT_RFC822;
545 bmsg->cm_fields['A'] = strdup("Citadel");
546 bmsg->cm_fields['O'] = strdup(MAILROOM);
547 bmsg->cm_fields['N'] = strdup(config.c_nodename);
548 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
549 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
550 StrBufAppendBuf(BounceMB, boundary, 0);
551 StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
552 StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
553 StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
554 StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
555 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
556 StrBufAppendBuf(BounceMB, boundary, 0);
557 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
558 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);
560 if (give_up) StrBufAppendBufPlain(BounceMB, HKEY(
561 "A message you sent could not be delivered to some or all of its recipients\n"
562 "due to prolonged unavailability of its destination(s).\n"
563 "Giving up on the following addresses:\n\n"
566 else StrBufAppendBufPlain(BounceMB, HKEY(
567 "A message you sent could not be delivered to some or all of its recipients.\n"
568 "The following addresses were undeliverable:\n\n"
572 * Now go through the instructions checking for stuff.
574 for (i=0; i<lines; ++i) {
577 extract_token(buf, instr, i, '\n', sizeof buf);
578 extract_token(key, buf, 0, '|', sizeof key);
579 addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
580 status = extract_int(buf, 2);
581 dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
584 syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
585 key, addr, status, dsn);
587 if (!strcasecmp(key, "bounceto")) {
588 strcpy(bounceto, addr);
591 if (!strcasecmp(key, "msgid")) {
595 if (!strcasecmp(key, "remote")) {
596 if (status == 5) bounce_this = 1;
597 if (give_up) bounce_this = 1;
603 StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
604 StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
605 StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
606 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
608 remove_token(instr, i, '\n');
614 /* Attach the original message */
616 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
617 StrBufAppendBuf(BounceMB, boundary, 0);
618 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
619 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0);
620 StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
621 StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0);
622 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
624 CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
625 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
626 StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
627 FreeStrBuf(&CC->redirect_buffer);
630 /* Close the multipart MIME scope */
631 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
632 StrBufAppendBuf(BounceMB, boundary, 0);
633 StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
634 if (bmsg->cm_fields['A'] != NULL)
635 free(bmsg->cm_fields['A']);
636 bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
637 /* Deliver the bounce if there's anything worth mentioning */
638 syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
639 if (num_bounces > 0) {
641 /* First try the user who sent the message */
642 syslog(LOG_DEBUG, "bounce to user? <%s>\n", bounceto);
643 if (IsEmptyStr(bounceto)) {
644 syslog(LOG_ERR, "No bounce address specified\n");
645 bounce_msgid = (-1L);
648 /* Can we deliver the bounce to the original sender? */
649 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
651 if (valid->num_error == 0) {
652 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
653 successful_bounce = 1;
657 /* If not, post it in the Aide> room */
658 if (successful_bounce == 0) {
659 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
662 /* Free up the memory we used */
664 free_recipients(valid);
667 FreeStrBuf(&boundary);
668 CtdlFreeMessage(bmsg);
669 syslog(LOG_DEBUG, "Done processing bounces\n");
674 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
675 * set of delivery instructions for completed deliveries and remove them.
677 * It returns the number of incomplete deliveries remaining.
679 int smtp_purge_completed_deliveries(char *instr) {
690 lines = num_tokens(instr, '\n');
691 for (i=0; i<lines; ++i) {
692 extract_token(buf, instr, i, '\n', sizeof buf);
693 extract_token(key, buf, 0, '|', sizeof key);
694 extract_token(addr, buf, 1, '|', sizeof addr);
695 status = extract_int(buf, 2);
696 extract_token(dsn, buf, 3, '|', sizeof dsn);
700 if (!strcasecmp(key, "remote")) {
701 if (status == 2) completed = 1;
706 remove_token(instr, i, '\n');
719 * Called by smtp_do_queue() to handle an individual message.
721 void smtp_do_procmsg(long msgnum, void *userdata) {
722 struct CtdlMessage *msg = NULL;
724 char *results = NULL;
732 char envelope_from[1024];
733 long text_msgid = (-1);
734 int incomplete_deliveries_remaining;
735 time_t attempted = 0L;
736 time_t last_attempted = 0L;
737 time_t retry = SMTP_RETRY_INTERVAL;
739 syslog(LOG_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
740 strcpy(envelope_from, "");
742 msg = CtdlFetchMessage(msgnum, 1);
744 syslog(LOG_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
748 instr = strdup(msg->cm_fields['M']);
749 CtdlFreeMessage(msg);
751 /* Strip out the headers amd any other non-instruction line */
752 lines = num_tokens(instr, '\n');
753 for (i=0; i<lines; ++i) {
754 extract_token(buf, instr, i, '\n', sizeof buf);
755 if (num_tokens(buf, '|') < 2) {
756 remove_token(instr, i, '\n');
762 /* Learn the message ID and find out about recent delivery attempts */
763 lines = num_tokens(instr, '\n');
764 for (i=0; i<lines; ++i) {
765 extract_token(buf, instr, i, '\n', sizeof buf);
766 extract_token(key, buf, 0, '|', sizeof key);
767 if (!strcasecmp(key, "msgid")) {
768 text_msgid = extract_long(buf, 1);
770 if (!strcasecmp(key, "envelope_from")) {
771 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
773 if (!strcasecmp(key, "retry")) {
774 /* double the retry interval after each attempt */
775 retry = extract_long(buf, 1) * 2L;
776 if (retry > SMTP_RETRY_MAX) {
777 retry = SMTP_RETRY_MAX;
779 remove_token(instr, i, '\n');
781 if (!strcasecmp(key, "attempted")) {
782 attempted = extract_long(buf, 1);
783 if (attempted > last_attempted)
784 last_attempted = attempted;
789 * Postpone delivery if we've already tried recently.
791 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
792 syslog(LOG_DEBUG, "SMTP client: Retry time not yet reached.\n");
799 * Bail out if there's no actual message associated with this
801 if (text_msgid < 0L) {
802 syslog(LOG_ERR, "SMTP client: no 'msgid' directive found!\n");
807 /* Plow through the instructions looking for 'remote' directives and
808 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
809 * were experienced and it's time to try again)
811 lines = num_tokens(instr, '\n');
812 for (i=0; i<lines; ++i) {
813 extract_token(buf, instr, i, '\n', sizeof buf);
814 extract_token(key, buf, 0, '|', sizeof key);
815 extract_token(addr, buf, 1, '|', sizeof addr);
816 status = extract_int(buf, 2);
817 extract_token(dsn, buf, 3, '|', sizeof dsn);
818 if ( (!strcasecmp(key, "remote"))
819 && ((status==0)||(status==3)||(status==4)) ) {
821 /* Remove this "remote" instruction from the set,
822 * but replace the set's final newline if
823 * remove_token() stripped it. It has to be there.
825 remove_token(instr, i, '\n');
826 if (instr[strlen(instr)-1] != '\n') {
832 syslog(LOG_DEBUG, "SMTP client: Trying <%s>\n", addr);
833 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
835 if (results == NULL) {
836 results = malloc(1024);
837 memset(results, 0, 1024);
840 results = realloc(results, strlen(results) + 1024);
842 snprintf(&results[strlen(results)], 1024,
844 key, addr, status, dsn);
849 if (results != NULL) {
850 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
851 strcat(instr, results);
856 /* Generate 'bounce' messages */
857 smtp_do_bounce(instr);
859 /* Go through the delivery list, deleting completed deliveries */
860 incomplete_deliveries_remaining =
861 smtp_purge_completed_deliveries(instr);
865 * No delivery instructions remain, so delete both the instructions
866 * message and the message message.
868 if (incomplete_deliveries_remaining <= 0) {
871 delmsgs[1] = text_msgid;
872 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
876 * Uncompleted delivery instructions remain, so delete the old
877 * instructions and replace with the updated ones.
879 if (incomplete_deliveries_remaining > 0) {
880 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
881 msg = malloc(sizeof(struct CtdlMessage));
882 memset(msg, 0, sizeof(struct CtdlMessage));
883 msg->cm_magic = CTDLMESSAGE_MAGIC;
884 msg->cm_anon_type = MES_NORMAL;
885 msg->cm_format_type = FMT_RFC822;
886 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
887 snprintf(msg->cm_fields['M'],
889 "Content-type: %s\n\n%s\n"
892 SPOOLMIME, instr, (long)time(NULL), (long)retry );
893 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
894 CtdlFreeMessage(msg);
901 /*****************************************************************************/
902 /* SMTP UTILITY COMMANDS */
903 /*****************************************************************************/
905 void cmd_smtp(char *argbuf) {
912 if (CtdlAccessCheck(ac_aide)) return;
914 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
916 if (!strcasecmp(cmd, "mx")) {
917 extract_token(node, argbuf, 1, '|', sizeof node);
918 num_mxhosts = getmx(buf, node);
919 cprintf("%d %d MX hosts listed for %s\n",
920 LISTING_FOLLOWS, num_mxhosts, node);
921 for (i=0; i<num_mxhosts; ++i) {
922 extract_token(node, buf, i, '|', sizeof node);
923 cprintf("%s\n", node);
929 else if (!strcasecmp(cmd, "runqueue")) {
931 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
936 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
943 * smtp_queue_thread()
945 * Run through the queue sending out messages.
947 void smtp_do_queue(void) {
948 static int is_running = 0;
949 int num_processed = 0;
951 if (is_running) return; /* Concurrency check - only one can run */
954 syslog(LOG_INFO, "SMTP client: processing outbound queue\n");
956 if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
957 syslog(LOG_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
960 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
962 syslog(LOG_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
968 * Initialize the SMTP outbound queue
970 void smtp_init_spoolout(void) {
971 struct ctdlroom qrbuf;
974 * Create the room. This will silently fail if the room already
975 * exists, and that's perfectly ok, because we want it to exist.
977 CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
980 * Make sure it's set to be a "system room" so it doesn't show up
981 * in the <K>nown rooms list for Aides.
983 if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
984 qrbuf.QRflags2 |= QR2_SYSTEM;
985 CtdlPutRoomLock(&qrbuf);
992 CTDL_MODULE_INIT(smtp_client)
996 smtp_init_spoolout();
997 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
998 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1001 /* return our Subversion id for the Log */