4 * An implementation of RFC821 (Simple Mail Transfer Protocol) for the
17 #include <sys/types.h>
19 #if TIME_WITH_SYS_TIME
20 # include <sys/time.h>
24 # include <sys/time.h>
36 #include "sysdep_decls.h"
37 #include "citserver.h"
41 #include "dynloader.h"
48 #include "internet_addressing.h"
51 #include "clientsocket.h"
58 struct citsmtp { /* Information about the current session */
61 struct usersupp vrfy_buffer;
66 int number_of_recipients;
69 int message_originated_locally;
72 enum { /* Command states for login authentication */
78 enum { /* Delivery modes */
83 #define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
84 #define SMTP_RECPS ((char *)CtdlGetUserData(SYM_SMTP_RECPS))
85 #define SMTP_ROOMS ((char *)CtdlGetUserData(SYM_SMTP_ROOMS))
91 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
95 /*****************************************************************************/
96 /* SMTP SERVER (INBOUND) STUFF */
97 /*****************************************************************************/
103 * Here's where our SMTP session begins its happy day.
105 void smtp_greeting(void) {
107 strcpy(CC->cs_clientname, "SMTP session");
108 CC->internal_pgm = 1;
109 CC->cs_flags |= CS_STEALTH;
110 CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
111 CtdlAllocUserData(SYM_SMTP_RECPS, SIZ);
112 CtdlAllocUserData(SYM_SMTP_ROOMS, SIZ);
113 snprintf(SMTP_RECPS, SIZ, "%s", "");
114 snprintf(SMTP_ROOMS, SIZ, "%s", "");
116 cprintf("220 %s ESMTP Citadel/UX server ready.\r\n", config.c_fqdn);
121 * Implement HELO and EHLO commands.
123 void smtp_hello(char *argbuf, int is_esmtp) {
125 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
128 cprintf("250 Greetings and joyous salutations.\r\n");
131 cprintf("250-Greetings and joyous salutations.\r\n");
132 cprintf("250-HELP\r\n");
133 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
134 cprintf("250-PIPELINING\r\n");
135 cprintf("250 AUTH=LOGIN\r\n");
141 * Implement HELP command.
143 void smtp_help(void) {
144 cprintf("214-Commands accepted:\r\n");
145 cprintf("214- DATA\r\n");
146 cprintf("214- EHLO\r\n");
147 cprintf("214- EXPN\r\n");
148 cprintf("214- HELO\r\n");
149 cprintf("214- HELP\r\n");
150 cprintf("214- MAIL\r\n");
151 cprintf("214- NOOP\r\n");
152 cprintf("214- QUIT\r\n");
153 cprintf("214- RCPT\r\n");
154 cprintf("214- RSET\r\n");
155 cprintf("214- VRFY\r\n");
163 void smtp_get_user(char *argbuf) {
167 decode_base64(username, argbuf, SIZ);
168 lprintf(9, "Trying <%s>\n", username);
169 if (CtdlLoginExistingUser(username) == login_ok) {
170 encode_base64(buf, "Password:");
171 cprintf("334 %s\r\n", buf);
172 SMTP->command_state = smtp_password;
175 cprintf("500 No such user.\r\n");
176 SMTP->command_state = smtp_command;
184 void smtp_get_pass(char *argbuf) {
187 decode_base64(password, argbuf, SIZ);
188 lprintf(9, "Trying <%s>\n", password);
189 if (CtdlTryPassword(password) == pass_ok) {
190 cprintf("235 Hello, %s\r\n", CC->usersupp.fullname);
191 lprintf(9, "SMTP authenticated login successful\n");
192 CC->internal_pgm = 0;
193 CC->cs_flags &= ~CS_STEALTH;
196 cprintf("500 Authentication failed.\r\n");
198 SMTP->command_state = smtp_command;
205 void smtp_auth(char *argbuf) {
208 if (strncasecmp(argbuf, "login", 5) ) {
209 cprintf("550 We only support LOGIN authentication.\r\n");
213 if (strlen(argbuf) >= 7) {
214 smtp_get_user(&argbuf[6]);
218 encode_base64(buf, "Username:");
219 cprintf("334 %s\r\n", buf);
220 SMTP->command_state = smtp_user;
226 * Back end for smtp_vrfy() command
228 void smtp_vrfy_backend(struct usersupp *us, void *data) {
230 if (!fuzzy_match(us, SMTP->vrfy_match)) {
232 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
238 * Implements the VRFY (verify user name) command.
239 * Performs fuzzy match on full user names.
241 void smtp_vrfy(char *argbuf) {
242 SMTP->vrfy_count = 0;
243 strcpy(SMTP->vrfy_match, argbuf);
244 ForEachUser(smtp_vrfy_backend, NULL);
246 if (SMTP->vrfy_count < 1) {
247 cprintf("550 String does not match anything.\r\n");
249 else if (SMTP->vrfy_count == 1) {
250 cprintf("250 %s <cit%ld@%s>\r\n",
251 SMTP->vrfy_buffer.fullname,
252 SMTP->vrfy_buffer.usernum,
255 else if (SMTP->vrfy_count > 1) {
256 cprintf("553 Request ambiguous: %d users matched.\r\n",
265 * Back end for smtp_expn() command
267 void smtp_expn_backend(struct usersupp *us, void *data) {
269 if (!fuzzy_match(us, SMTP->vrfy_match)) {
271 if (SMTP->vrfy_count >= 1) {
272 cprintf("250-%s <cit%ld@%s>\r\n",
273 SMTP->vrfy_buffer.fullname,
274 SMTP->vrfy_buffer.usernum,
279 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
285 * Implements the EXPN (expand user name) command.
286 * Performs fuzzy match on full user names.
288 void smtp_expn(char *argbuf) {
289 SMTP->vrfy_count = 0;
290 strcpy(SMTP->vrfy_match, argbuf);
291 ForEachUser(smtp_expn_backend, NULL);
293 if (SMTP->vrfy_count < 1) {
294 cprintf("550 String does not match anything.\r\n");
296 else if (SMTP->vrfy_count >= 1) {
297 cprintf("250 %s <cit%ld@%s>\r\n",
298 SMTP->vrfy_buffer.fullname,
299 SMTP->vrfy_buffer.usernum,
306 * Implements the RSET (reset state) command.
307 * Currently this just zeroes out the state buffer. If pointers to data
308 * allocated with mallok() are ever placed in the state buffer, we have to
309 * be sure to phree() them first!
311 void smtp_rset(void) {
312 memset(SMTP, 0, sizeof(struct citsmtp));
316 cprintf("250 Zap!\r\n");
320 * Clear out the portions of the state buffer that need to be cleared out
321 * after the DATA command finishes.
323 void smtp_data_clear(void) {
324 strcpy(SMTP->from, "");
325 strcpy(SMTP->recipients, "");
326 SMTP->number_of_recipients = 0;
327 SMTP->delivery_mode = 0;
328 SMTP->message_originated_locally = 0;
334 * Implements the "MAIL From:" command
336 void smtp_mail(char *argbuf) {
340 struct recptypes *valid;
342 if (strlen(SMTP->from) != 0) {
343 cprintf("503 Only one sender permitted\r\n");
347 if (strncasecmp(argbuf, "From:", 5)) {
348 cprintf("501 Syntax error\r\n");
352 strcpy(SMTP->from, &argbuf[5]);
354 stripallbut(SMTP->from, '<', '>');
356 if (strlen(SMTP->from) == 0) {
357 cprintf("501 Empty sender name is not permitted\r\n");
361 /* If this SMTP connection is from a logged-in user, make sure that
362 * the user only sends email from his/her own address.
365 valid = validate_recipients(SMTP->from);
366 if ( (valid->num_local == 1) &&
367 (!strcasecmp(valid->recp_local, CC->usersupp.fullname)) ) {
368 cprintf("250 Sender ok <%s>\r\n", valid->recp_local);
369 SMTP->message_originated_locally = 1;
372 cprintf("550 <%s> is not your address.\r\n",
374 strcpy(SMTP->from, "");
381 /* Otherwise, make sure outsiders aren't trying to forge mail from
385 process_rfc822_addr(SMTP->from, user, node, name);
386 if (CtdlHostAlias(node) != hostalias_nomatch) {
387 cprintf("550 You must log in to send mail from %s\r\n",
389 strcpy(SMTP->from, "");
394 cprintf("250 Sender ok\r\n");
400 * Implements the "RCPT To:" command
402 void smtp_rcpt(char *argbuf) {
404 struct recptypes *valid;
406 if (strlen(SMTP->from) == 0) {
407 cprintf("503 Need MAIL before RCPT\r\n");
411 if (strncasecmp(argbuf, "To:", 3)) {
412 cprintf("501 Syntax error\r\n");
416 strcpy(recp, &argbuf[3]);
418 stripallbut(recp, '<', '>');
420 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
421 cprintf("452 Too many recipients\r\n");
425 valid = validate_recipients(recp);
426 if (valid->num_error > 0) {
427 cprintf("599 Error: %s\r\n", valid->errormsg);
432 if (valid->num_internet > 0) {
433 if (SMTP->message_originated_locally == 0) {
434 cprintf("551 Relaying denied <%s>\r\n", recp);
440 cprintf("250 RCPT ok <%s>\r\n", recp);
441 if (strlen(SMTP->recipients) > 0) {
442 strcat(SMTP->recipients, ",");
444 strcat(SMTP->recipients, recp);
445 SMTP->number_of_recipients += 1;
452 * Implements the DATA command
454 void smtp_data(void) {
456 struct CtdlMessage *msg;
459 struct recptypes *valid;
461 if (strlen(SMTP->from) == 0) {
462 cprintf("503 Need MAIL command first.\r\n");
466 if (SMTP->number_of_recipients < 1) {
467 cprintf("503 Need RCPT command first.\r\n");
471 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
473 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
477 it should be Received: from %s (real.name.dom [w.x.y.z])
479 if (body != NULL) snprintf(body, 4096,
480 "Received: from %s (%s)\n"
487 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
489 cprintf("550 Unable to save message: internal error.\r\n");
493 lprintf(9, "Converting message...\n");
494 msg = convert_internet_message(body);
496 /* If the user is locally authenticated, FORCE the From: header to
497 * show up as the real sender. Yes, this violates the RFC standard,
498 * but IT MAKES SENSE. Comment it out if you don't like this behavior.
501 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
502 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
503 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
504 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
505 msg->cm_fields['N'] = strdoop(config.c_nodename);
506 msg->cm_fields['H'] = strdoop(config.c_humannode);
509 /* Submit the message into the Citadel system. */
510 valid = validate_recipients(SMTP->recipients);
511 msgnum = CtdlSubmitMsg(msg, valid, "");
512 CtdlFreeMessage(msg);
516 cprintf("250 Message accepted.\r\n");
519 cprintf("550 Internal delivery error\r\n");
522 smtp_data_clear(); /* clear out the buffers now */
529 * Main command loop for SMTP sessions.
531 void smtp_command_loop(void) {
535 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
536 if (client_gets(cmdbuf) < 1) {
537 lprintf(3, "SMTP socket is broken. Ending session.\n");
541 lprintf(5, "SMTP: %s\n", cmdbuf);
542 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
544 if (SMTP->command_state == smtp_user) {
545 smtp_get_user(cmdbuf);
548 else if (SMTP->command_state == smtp_password) {
549 smtp_get_pass(cmdbuf);
552 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
553 smtp_auth(&cmdbuf[5]);
556 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
560 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
561 smtp_hello(&cmdbuf[5], 1);
564 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
565 smtp_expn(&cmdbuf[5]);
568 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
569 smtp_hello(&cmdbuf[5], 0);
572 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
576 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
577 smtp_mail(&cmdbuf[5]);
580 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
581 cprintf("250 NOOP\r\n");
584 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
585 cprintf("221 Goodbye...\r\n");
590 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
591 smtp_rcpt(&cmdbuf[5]);
594 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
598 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
599 smtp_vrfy(&cmdbuf[5]);
603 cprintf("502 I'm afraid I can't do that.\r\n");
611 /*****************************************************************************/
612 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
613 /*****************************************************************************/
620 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
623 void smtp_try(const char *key, const char *addr, int *status,
624 char *dsn, size_t n, long msgnum)
631 char user[SIZ], node[SIZ], name[SIZ];
637 size_t blocksize = 0;
640 /* Parse out the host portion of the recipient address */
641 process_rfc822_addr(addr, user, node, name);
643 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
646 /* Load the message out of the database into a temp file */
648 if (msg_fp == NULL) {
650 snprintf(dsn, n, "Error creating temporary file");
654 CtdlRedirectOutput(msg_fp, -1);
655 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
656 CtdlRedirectOutput(NULL, -1);
657 fseek(msg_fp, 0L, SEEK_END);
658 msg_size = ftell(msg_fp);
662 /* Extract something to send later in the 'MAIL From:' command */
663 strcpy(mailfrom, "");
667 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
668 if (!strncasecmp(buf, "From:", 5)) {
669 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
671 for (i=0; i<strlen(mailfrom); ++i) {
672 if (!isprint(mailfrom[i])) {
673 strcpy(&mailfrom[i], &mailfrom[i+1]);
678 /* Strip out parenthesized names */
681 for (i=0; i<strlen(mailfrom); ++i) {
682 if (mailfrom[i] == '(') lp = i;
683 if (mailfrom[i] == ')') rp = i;
685 if ((lp>0)&&(rp>lp)) {
686 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
689 /* Prefer brokketized names */
692 for (i=0; i<strlen(mailfrom); ++i) {
693 if (mailfrom[i] == '<') lp = i;
694 if (mailfrom[i] == '>') rp = i;
696 if ( (lp>=0) && (rp>lp) ) {
698 strcpy(mailfrom, &mailfrom[lp]);
703 } while (scan_done == 0);
704 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
706 /* Figure out what mail exchanger host we have to connect to */
707 num_mxhosts = getmx(mxhosts, node);
708 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
709 if (num_mxhosts < 1) {
711 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
715 for (mx=0; mx<num_mxhosts; ++mx) {
716 extract(buf, mxhosts, mx);
717 lprintf(9, "Trying <%s>\n", buf);
718 sock = sock_connect(buf, "25", "tcp");
719 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
720 if (sock >= 0) lprintf(9, "Connected!\n");
721 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
722 if (sock >= 0) break;
726 *status = 4; /* dsn is already filled in */
730 /* Process the SMTP greeting from the server */
731 if (ml_sock_gets(sock, buf) < 0) {
733 strcpy(dsn, "Connection broken during SMTP conversation");
736 lprintf(9, "<%s\n", buf);
740 safestrncpy(dsn, &buf[4], 1023);
745 safestrncpy(dsn, &buf[4], 1023);
750 /* At this point we know we are talking to a real SMTP server */
752 /* Do a HELO command */
753 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
754 lprintf(9, ">%s", buf);
755 sock_write(sock, buf, strlen(buf));
756 if (ml_sock_gets(sock, buf) < 0) {
758 strcpy(dsn, "Connection broken during SMTP HELO");
761 lprintf(9, "<%s\n", buf);
765 safestrncpy(dsn, &buf[4], 1023);
770 safestrncpy(dsn, &buf[4], 1023);
776 /* HELO succeeded, now try the MAIL From: command */
777 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
778 lprintf(9, ">%s", buf);
779 sock_write(sock, buf, strlen(buf));
780 if (ml_sock_gets(sock, buf) < 0) {
782 strcpy(dsn, "Connection broken during SMTP MAIL");
785 lprintf(9, "<%s\n", buf);
789 safestrncpy(dsn, &buf[4], 1023);
794 safestrncpy(dsn, &buf[4], 1023);
800 /* MAIL succeeded, now try the RCPT To: command */
801 snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
802 lprintf(9, ">%s", buf);
803 sock_write(sock, buf, strlen(buf));
804 if (ml_sock_gets(sock, buf) < 0) {
806 strcpy(dsn, "Connection broken during SMTP RCPT");
809 lprintf(9, "<%s\n", buf);
813 safestrncpy(dsn, &buf[4], 1023);
818 safestrncpy(dsn, &buf[4], 1023);
824 /* RCPT succeeded, now try the DATA command */
825 lprintf(9, ">DATA\n");
826 sock_write(sock, "DATA\r\n", 6);
827 if (ml_sock_gets(sock, buf) < 0) {
829 strcpy(dsn, "Connection broken during SMTP DATA");
832 lprintf(9, "<%s\n", buf);
836 safestrncpy(dsn, &buf[4], 1023);
841 safestrncpy(dsn, &buf[4], 1023);
846 /* If we reach this point, the server is expecting data */
848 while (msg_size > 0) {
849 blocksize = sizeof(buf);
850 if (blocksize > msg_size) blocksize = msg_size;
851 fread(buf, blocksize, 1, msg_fp);
852 sock_write(sock, buf, blocksize);
853 msg_size -= blocksize;
855 if (buf[blocksize-1] != 10) {
856 lprintf(5, "Possible problem: message did not correctly "
857 "terminate. (expecting 0x10, got 0x%02x)\n",
861 sock_write(sock, ".\r\n", 3);
862 if (ml_sock_gets(sock, buf) < 0) {
864 strcpy(dsn, "Connection broken during SMTP message transmit");
867 lprintf(9, "%s\n", buf);
871 safestrncpy(dsn, &buf[4], 1023);
876 safestrncpy(dsn, &buf[4], 1023);
882 safestrncpy(dsn, &buf[4], 1023);
885 lprintf(9, ">QUIT\n");
886 sock_write(sock, "QUIT\r\n", 6);
887 ml_sock_gets(sock, buf);
888 lprintf(9, "<%s\n", buf);
890 bail: if (msg_fp != NULL) fclose(msg_fp);
898 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
899 * instructions for "5" codes (permanent fatal errors) and produce/deliver
900 * a "bounce" message (delivery status notification).
902 void smtp_do_bounce(char *instr) {
913 long bounce_msgid = (-1);
914 time_t submitted = 0L;
915 struct CtdlMessage *bmsg = NULL;
917 struct recptypes *valid;
918 int successful_bounce = 0;
920 lprintf(9, "smtp_do_bounce() called\n");
921 strcpy(bounceto, "");
923 lines = num_tokens(instr, '\n');
926 /* See if it's time to give up on delivery of this message */
927 for (i=0; i<lines; ++i) {
928 extract_token(buf, instr, i, '\n');
929 extract(key, buf, 0);
930 extract(addr, buf, 1);
931 if (!strcasecmp(key, "submitted")) {
932 submitted = atol(addr);
936 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
942 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
943 if (bmsg == NULL) return;
944 memset(bmsg, 0, sizeof(struct CtdlMessage));
946 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
947 bmsg->cm_anon_type = MES_NORMAL;
948 bmsg->cm_format_type = 1;
949 bmsg->cm_fields['A'] = strdoop("Citadel");
950 bmsg->cm_fields['O'] = strdoop(MAILROOM);
951 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
953 if (give_up) bmsg->cm_fields['M'] = strdoop(
954 "A message you sent could not be delivered to some or all of its recipients\n"
955 "due to prolonged unavailability of its destination(s).\n"
956 "Giving up on the following addresses:\n\n"
959 else bmsg->cm_fields['M'] = strdoop(
960 "A message you sent could not be delivered to some or all of its recipients.\n"
961 "The following addresses were undeliverable:\n\n"
965 * Now go through the instructions checking for stuff.
968 for (i=0; i<lines; ++i) {
969 extract_token(buf, instr, i, '\n');
970 extract(key, buf, 0);
971 extract(addr, buf, 1);
972 status = extract_int(buf, 2);
973 extract(dsn, buf, 3);
976 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
977 key, addr, status, dsn);
979 if (!strcasecmp(key, "bounceto")) {
980 strcpy(bounceto, addr);
984 (!strcasecmp(key, "local"))
985 || (!strcasecmp(key, "remote"))
986 || (!strcasecmp(key, "ignet"))
987 || (!strcasecmp(key, "room"))
989 if (status == 5) bounce_this = 1;
990 if (give_up) bounce_this = 1;
996 if (bmsg->cm_fields['M'] == NULL) {
997 lprintf(2, "ERROR ... M field is null "
998 "(%s:%d)\n", __FILE__, __LINE__);
1001 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1002 strlen(bmsg->cm_fields['M']) + 1024 );
1003 strcat(bmsg->cm_fields['M'], addr);
1004 strcat(bmsg->cm_fields['M'], ": ");
1005 strcat(bmsg->cm_fields['M'], dsn);
1006 strcat(bmsg->cm_fields['M'], "\n");
1008 remove_token(instr, i, '\n');
1014 /* Deliver the bounce if there's anything worth mentioning */
1015 lprintf(9, "num_bounces = %d\n", num_bounces);
1016 if (num_bounces > 0) {
1018 /* First try the user who sent the message */
1019 lprintf(9, "bounce to user? <%s>\n", bounceto);
1020 if (strlen(bounceto) == 0) {
1021 lprintf(7, "No bounce address specified\n");
1022 bounce_msgid = (-1L);
1025 /* Can we deliver the bounce to the original sender? */
1026 valid = validate_recipients(bounceto);
1027 if (valid != NULL) {
1028 if (valid->num_error == 0) {
1029 CtdlSubmitMsg(bmsg, valid, "");
1030 successful_bounce = 1;
1034 /* If not, post it in the Aide> room */
1035 if (successful_bounce == 0) {
1036 CtdlSubmitMsg(bmsg, NULL, AIDEROOM);
1039 /* Free up the memory we used */
1040 if (valid != NULL) {
1045 CtdlFreeMessage(bmsg);
1046 lprintf(9, "Done processing bounces\n");
1051 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1052 * set of delivery instructions for completed deliveries and remove them.
1054 * It returns the number of incomplete deliveries remaining.
1056 int smtp_purge_completed_deliveries(char *instr) {
1067 lines = num_tokens(instr, '\n');
1068 for (i=0; i<lines; ++i) {
1069 extract_token(buf, instr, i, '\n');
1070 extract(key, buf, 0);
1071 extract(addr, buf, 1);
1072 status = extract_int(buf, 2);
1073 extract(dsn, buf, 3);
1078 (!strcasecmp(key, "local"))
1079 || (!strcasecmp(key, "remote"))
1080 || (!strcasecmp(key, "ignet"))
1081 || (!strcasecmp(key, "room"))
1083 if (status == 2) completed = 1;
1088 remove_token(instr, i, '\n');
1101 * Called by smtp_do_queue() to handle an individual message.
1103 void smtp_do_procmsg(long msgnum, void *userdata) {
1104 struct CtdlMessage *msg;
1106 char *results = NULL;
1114 long text_msgid = (-1);
1115 int incomplete_deliveries_remaining;
1116 time_t attempted = 0L;
1117 time_t last_attempted = 0L;
1118 time_t retry = SMTP_RETRY_INTERVAL;
1120 lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1122 msg = CtdlFetchMessage(msgnum);
1124 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1128 instr = strdoop(msg->cm_fields['M']);
1129 CtdlFreeMessage(msg);
1131 /* Strip out the headers amd any other non-instruction line */
1132 lines = num_tokens(instr, '\n');
1133 for (i=0; i<lines; ++i) {
1134 extract_token(buf, instr, i, '\n');
1135 if (num_tokens(buf, '|') < 2) {
1136 remove_token(instr, i, '\n');
1142 /* Learn the message ID and find out about recent delivery attempts */
1143 lines = num_tokens(instr, '\n');
1144 for (i=0; i<lines; ++i) {
1145 extract_token(buf, instr, i, '\n');
1146 extract(key, buf, 0);
1147 if (!strcasecmp(key, "msgid")) {
1148 text_msgid = extract_long(buf, 1);
1150 if (!strcasecmp(key, "retry")) {
1151 /* double the retry interval after each attempt */
1152 retry = extract_long(buf, 1) * 2L;
1153 if (retry > SMTP_RETRY_MAX) {
1154 retry = SMTP_RETRY_MAX;
1156 remove_token(instr, i, '\n');
1158 if (!strcasecmp(key, "attempted")) {
1159 attempted = extract_long(buf, 1);
1160 if (attempted > last_attempted)
1161 last_attempted = attempted;
1166 * Postpone delivery if we've already tried recently.
1168 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1169 lprintf(7, "Retry time not yet reached.\n");
1176 * Bail out if there's no actual message associated with this
1178 if (text_msgid < 0L) {
1179 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1184 /* Plow through the instructions looking for 'remote' directives and
1185 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1186 * were experienced and it's time to try again)
1188 lines = num_tokens(instr, '\n');
1189 for (i=0; i<lines; ++i) {
1190 extract_token(buf, instr, i, '\n');
1191 extract(key, buf, 0);
1192 extract(addr, buf, 1);
1193 status = extract_int(buf, 2);
1194 extract(dsn, buf, 3);
1195 if ( (!strcasecmp(key, "remote"))
1196 && ((status==0)||(status==3)||(status==4)) ) {
1197 remove_token(instr, i, '\n');
1200 lprintf(9, "SMTP: Trying <%s>\n", addr);
1201 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1203 if (results == NULL) {
1204 results = mallok(1024);
1205 memset(results, 0, 1024);
1208 results = reallok(results,
1209 strlen(results) + 1024);
1211 snprintf(&results[strlen(results)], 1024,
1213 key, addr, status, dsn);
1218 if (results != NULL) {
1219 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1220 strcat(instr, results);
1225 /* Generate 'bounce' messages */
1226 smtp_do_bounce(instr);
1228 /* Go through the delivery list, deleting completed deliveries */
1229 incomplete_deliveries_remaining =
1230 smtp_purge_completed_deliveries(instr);
1234 * No delivery instructions remain, so delete both the instructions
1235 * message and the message message.
1237 if (incomplete_deliveries_remaining <= 0) {
1238 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1239 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1244 * Uncompleted delivery instructions remain, so delete the old
1245 * instructions and replace with the updated ones.
1247 if (incomplete_deliveries_remaining > 0) {
1248 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1249 msg = mallok(sizeof(struct CtdlMessage));
1250 memset(msg, 0, sizeof(struct CtdlMessage));
1251 msg->cm_magic = CTDLMESSAGE_MAGIC;
1252 msg->cm_anon_type = MES_NORMAL;
1253 msg->cm_format_type = FMT_RFC822;
1254 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1255 snprintf(msg->cm_fields['M'],
1257 "Content-type: %s\n\n%s\n"
1260 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1262 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1263 CtdlFreeMessage(msg);
1273 * Run through the queue sending out messages.
1275 void smtp_do_queue(void) {
1276 static int doing_queue = 0;
1279 * This is a simple concurrency check to make sure only one queue run
1280 * is done at a time. We could do this with a mutex, but since we
1281 * don't really require extremely fine granularity here, we'll do it
1282 * with a static variable instead.
1284 if (doing_queue) return;
1288 * Go ahead and run the queue
1290 lprintf(7, "SMTP: processing outbound queue\n");
1292 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1293 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1296 CtdlForEachMessage(MSGS_ALL, 0L,
1297 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1299 lprintf(7, "SMTP: queue run completed\n");
1306 /*****************************************************************************/
1307 /* SMTP UTILITY COMMANDS */
1308 /*****************************************************************************/
1310 void cmd_smtp(char *argbuf) {
1317 if (CtdlAccessCheck(ac_aide)) return;
1319 extract(cmd, argbuf, 0);
1321 if (!strcasecmp(cmd, "mx")) {
1322 extract(node, argbuf, 1);
1323 num_mxhosts = getmx(buf, node);
1324 cprintf("%d %d MX hosts listed for %s\n",
1325 LISTING_FOLLOWS, num_mxhosts, node);
1326 for (i=0; i<num_mxhosts; ++i) {
1327 extract(node, buf, i);
1328 cprintf("%s\n", node);
1334 else if (!strcasecmp(cmd, "runqueue")) {
1336 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1341 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1349 /*****************************************************************************/
1350 /* MODULE INITIALIZATION STUFF */
1351 /*****************************************************************************/
1354 char *Dynamic_Module_Init(void)
1356 SYM_SMTP = CtdlGetDynamicSymbol();
1358 CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
1363 CtdlRegisterServiceHook(0, /* ...and locally */
1368 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0);
1369 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1370 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");