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 sprintf(SMTP_RECPS, "%s", "");
114 sprintf(SMTP_ROOMS, "%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, time(NULL), DATESTRING_RFC822);
476 if (body != NULL) snprintf(body, 4096,
477 "Received: from %s\n"
484 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
486 cprintf("550 Unable to save message: internal error.\r\n");
490 lprintf(9, "Converting message...\n");
491 msg = convert_internet_message(body);
493 /* If the user is locally authenticated, FORCE the From: header to
494 * show up as the real sender. Yes, this violates the RFC standard,
495 * but IT MAKES SENSE. Comment it out if you don't like this behavior.
498 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
499 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
500 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
501 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
502 msg->cm_fields['N'] = strdoop(config.c_nodename);
503 msg->cm_fields['H'] = strdoop(config.c_humannode);
506 /* Submit the message into the Citadel system. */
507 valid = validate_recipients(SMTP->recipients);
508 msgnum = CtdlSubmitMsg(msg, valid, "");
509 CtdlFreeMessage(msg);
513 cprintf("250 Message accepted.\r\n");
516 cprintf("550 Internal delivery error\r\n");
519 smtp_data_clear(); /* clear out the buffers now */
526 * Main command loop for SMTP sessions.
528 void smtp_command_loop(void) {
532 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
533 if (client_gets(cmdbuf) < 1) {
534 lprintf(3, "SMTP socket is broken. Ending session.\n");
538 lprintf(5, "SMTP: %s\n", cmdbuf);
539 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
541 if (SMTP->command_state == smtp_user) {
542 smtp_get_user(cmdbuf);
545 else if (SMTP->command_state == smtp_password) {
546 smtp_get_pass(cmdbuf);
549 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
550 smtp_auth(&cmdbuf[5]);
553 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
557 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
558 smtp_hello(&cmdbuf[5], 1);
561 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
562 smtp_expn(&cmdbuf[5]);
565 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
566 smtp_hello(&cmdbuf[5], 0);
569 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
573 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
574 smtp_mail(&cmdbuf[5]);
577 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
578 cprintf("250 NOOP\r\n");
581 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
582 cprintf("221 Goodbye...\r\n");
587 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
588 smtp_rcpt(&cmdbuf[5]);
591 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
595 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
596 smtp_vrfy(&cmdbuf[5]);
600 cprintf("502 I'm afraid I can't do that.\r\n");
608 /*****************************************************************************/
609 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
610 /*****************************************************************************/
617 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
620 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
627 char user[SIZ], node[SIZ], name[SIZ];
633 size_t blocksize = 0;
636 /* Parse out the host portion of the recipient address */
637 process_rfc822_addr(addr, user, node, name);
639 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
642 /* Load the message out of the database into a temp file */
644 if (msg_fp == NULL) {
646 sprintf(dsn, "Error creating temporary file");
650 CtdlRedirectOutput(msg_fp, -1);
651 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
652 CtdlRedirectOutput(NULL, -1);
653 fseek(msg_fp, 0L, SEEK_END);
654 msg_size = ftell(msg_fp);
658 /* Extract something to send later in the 'MAIL From:' command */
659 strcpy(mailfrom, "");
663 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
664 if (!strncasecmp(buf, "From:", 5)) {
665 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
667 for (i=0; i<strlen(mailfrom); ++i) {
668 if (!isprint(mailfrom[i])) {
669 strcpy(&mailfrom[i], &mailfrom[i+1]);
674 /* Strip out parenthesized names */
677 for (i=0; i<strlen(mailfrom); ++i) {
678 if (mailfrom[i] == '(') lp = i;
679 if (mailfrom[i] == ')') rp = i;
681 if ((lp>0)&&(rp>lp)) {
682 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
685 /* Prefer brokketized names */
688 for (i=0; i<strlen(mailfrom); ++i) {
689 if (mailfrom[i] == '<') lp = i;
690 if (mailfrom[i] == '>') rp = i;
692 if ( (lp>=0) && (rp>lp) ) {
694 strcpy(mailfrom, &mailfrom[lp]);
699 } while (scan_done == 0);
700 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
702 /* Figure out what mail exchanger host we have to connect to */
703 num_mxhosts = getmx(mxhosts, node);
704 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
705 if (num_mxhosts < 1) {
707 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
711 for (mx=0; mx<num_mxhosts; ++mx) {
712 extract(buf, mxhosts, mx);
713 lprintf(9, "Trying <%s>\n", buf);
714 sock = sock_connect(buf, "25", "tcp");
715 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
716 if (sock >= 0) lprintf(9, "Connected!\n");
717 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
718 if (sock >= 0) break;
722 *status = 4; /* dsn is already filled in */
726 /* Process the SMTP greeting from the server */
727 if (ml_sock_gets(sock, buf) < 0) {
729 strcpy(dsn, "Connection broken during SMTP conversation");
732 lprintf(9, "<%s\n", buf);
736 safestrncpy(dsn, &buf[4], 1023);
741 safestrncpy(dsn, &buf[4], 1023);
746 /* At this point we know we are talking to a real SMTP server */
748 /* Do a HELO command */
749 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
750 lprintf(9, ">%s", buf);
751 sock_write(sock, buf, strlen(buf));
752 if (ml_sock_gets(sock, buf) < 0) {
754 strcpy(dsn, "Connection broken during SMTP HELO");
757 lprintf(9, "<%s\n", buf);
761 safestrncpy(dsn, &buf[4], 1023);
766 safestrncpy(dsn, &buf[4], 1023);
772 /* HELO succeeded, now try the MAIL From: command */
773 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
774 lprintf(9, ">%s", buf);
775 sock_write(sock, buf, strlen(buf));
776 if (ml_sock_gets(sock, buf) < 0) {
778 strcpy(dsn, "Connection broken during SMTP MAIL");
781 lprintf(9, "<%s\n", buf);
785 safestrncpy(dsn, &buf[4], 1023);
790 safestrncpy(dsn, &buf[4], 1023);
796 /* MAIL succeeded, now try the RCPT To: command */
797 snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
798 lprintf(9, ">%s", buf);
799 sock_write(sock, buf, strlen(buf));
800 if (ml_sock_gets(sock, buf) < 0) {
802 strcpy(dsn, "Connection broken during SMTP RCPT");
805 lprintf(9, "<%s\n", buf);
809 safestrncpy(dsn, &buf[4], 1023);
814 safestrncpy(dsn, &buf[4], 1023);
820 /* RCPT succeeded, now try the DATA command */
821 lprintf(9, ">DATA\n");
822 sock_write(sock, "DATA\r\n", 6);
823 if (ml_sock_gets(sock, buf) < 0) {
825 strcpy(dsn, "Connection broken during SMTP DATA");
828 lprintf(9, "<%s\n", buf);
832 safestrncpy(dsn, &buf[4], 1023);
837 safestrncpy(dsn, &buf[4], 1023);
842 /* If we reach this point, the server is expecting data */
844 while (msg_size > 0) {
845 blocksize = sizeof(buf);
846 if (blocksize > msg_size) blocksize = msg_size;
847 fread(buf, blocksize, 1, msg_fp);
848 sock_write(sock, buf, blocksize);
849 msg_size -= blocksize;
851 if (buf[blocksize-1] != 10) {
852 lprintf(5, "Possible problem: message did not correctly "
853 "terminate. (expecting 0x10, got 0x%02x)\n",
857 sock_write(sock, ".\r\n", 3);
858 if (ml_sock_gets(sock, buf) < 0) {
860 strcpy(dsn, "Connection broken during SMTP message transmit");
863 lprintf(9, "%s\n", buf);
867 safestrncpy(dsn, &buf[4], 1023);
872 safestrncpy(dsn, &buf[4], 1023);
878 safestrncpy(dsn, &buf[4], 1023);
881 lprintf(9, ">QUIT\n");
882 sock_write(sock, "QUIT\r\n", 6);
883 ml_sock_gets(sock, buf);
884 lprintf(9, "<%s\n", buf);
886 bail: if (msg_fp != NULL) fclose(msg_fp);
894 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
895 * instructions for "5" codes (permanent fatal errors) and produce/deliver
896 * a "bounce" message (delivery status notification).
898 void smtp_do_bounce(char *instr) {
909 long bounce_msgid = (-1);
910 time_t submitted = 0L;
911 struct CtdlMessage *bmsg = NULL;
913 struct recptypes *valid;
914 int successful_bounce = 0;
916 lprintf(9, "smtp_do_bounce() called\n");
917 strcpy(bounceto, "");
919 lines = num_tokens(instr, '\n');
922 /* See if it's time to give up on delivery of this message */
923 for (i=0; i<lines; ++i) {
924 extract_token(buf, instr, i, '\n');
925 extract(key, buf, 0);
926 extract(addr, buf, 1);
927 if (!strcasecmp(key, "submitted")) {
928 submitted = atol(addr);
932 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
938 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
939 if (bmsg == NULL) return;
940 memset(bmsg, 0, sizeof(struct CtdlMessage));
942 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
943 bmsg->cm_anon_type = MES_NORMAL;
944 bmsg->cm_format_type = 1;
945 bmsg->cm_fields['A'] = strdoop("Citadel");
946 bmsg->cm_fields['O'] = strdoop(MAILROOM);
947 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
949 if (give_up) bmsg->cm_fields['M'] = strdoop(
950 "A message you sent could not be delivered to some or all of its recipients\n"
951 "due to prolonged unavailability of its destination(s).\n"
952 "Giving up on the following addresses:\n\n"
955 else bmsg->cm_fields['M'] = strdoop(
956 "A message you sent could not be delivered to some or all of its recipients.\n"
957 "The following addresses were undeliverable:\n\n"
961 * Now go through the instructions checking for stuff.
964 for (i=0; i<lines; ++i) {
965 extract_token(buf, instr, i, '\n');
966 extract(key, buf, 0);
967 extract(addr, buf, 1);
968 status = extract_int(buf, 2);
969 extract(dsn, buf, 3);
972 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
973 key, addr, status, dsn);
975 if (!strcasecmp(key, "bounceto")) {
976 strcpy(bounceto, addr);
980 (!strcasecmp(key, "local"))
981 || (!strcasecmp(key, "remote"))
982 || (!strcasecmp(key, "ignet"))
983 || (!strcasecmp(key, "room"))
985 if (status == 5) bounce_this = 1;
986 if (give_up) bounce_this = 1;
992 if (bmsg->cm_fields['M'] == NULL) {
993 lprintf(2, "ERROR ... M field is null "
994 "(%s:%d)\n", __FILE__, __LINE__);
997 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
998 strlen(bmsg->cm_fields['M']) + 1024 );
999 strcat(bmsg->cm_fields['M'], addr);
1000 strcat(bmsg->cm_fields['M'], ": ");
1001 strcat(bmsg->cm_fields['M'], dsn);
1002 strcat(bmsg->cm_fields['M'], "\n");
1004 remove_token(instr, i, '\n');
1010 /* Deliver the bounce if there's anything worth mentioning */
1011 lprintf(9, "num_bounces = %d\n", num_bounces);
1012 if (num_bounces > 0) {
1014 /* First try the user who sent the message */
1015 lprintf(9, "bounce to user? <%s>\n", bounceto);
1016 if (strlen(bounceto) == 0) {
1017 lprintf(7, "No bounce address specified\n");
1018 bounce_msgid = (-1L);
1021 /* Can we deliver the bounce to the original sender? */
1022 valid = validate_recipients(bounceto);
1023 if (valid != NULL) {
1024 if (valid->num_error == 0) {
1025 CtdlSubmitMsg(bmsg, valid, "");
1026 successful_bounce = 1;
1030 /* If not, post it in the Aide> room */
1031 if (successful_bounce == 0) {
1032 CtdlSubmitMsg(bmsg, NULL, AIDEROOM);
1035 /* Free up the memory we used */
1036 if (valid != NULL) {
1041 CtdlFreeMessage(bmsg);
1042 lprintf(9, "Done processing bounces\n");
1047 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1048 * set of delivery instructions for completed deliveries and remove them.
1050 * It returns the number of incomplete deliveries remaining.
1052 int smtp_purge_completed_deliveries(char *instr) {
1063 lines = num_tokens(instr, '\n');
1064 for (i=0; i<lines; ++i) {
1065 extract_token(buf, instr, i, '\n');
1066 extract(key, buf, 0);
1067 extract(addr, buf, 1);
1068 status = extract_int(buf, 2);
1069 extract(dsn, buf, 3);
1074 (!strcasecmp(key, "local"))
1075 || (!strcasecmp(key, "remote"))
1076 || (!strcasecmp(key, "ignet"))
1077 || (!strcasecmp(key, "room"))
1079 if (status == 2) completed = 1;
1084 remove_token(instr, i, '\n');
1097 * Called by smtp_do_queue() to handle an individual message.
1099 void smtp_do_procmsg(long msgnum, void *userdata) {
1100 struct CtdlMessage *msg;
1102 char *results = NULL;
1110 long text_msgid = (-1);
1111 int incomplete_deliveries_remaining;
1112 time_t attempted = 0L;
1113 time_t last_attempted = 0L;
1114 time_t retry = SMTP_RETRY_INTERVAL;
1116 lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1118 msg = CtdlFetchMessage(msgnum);
1120 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1124 instr = strdoop(msg->cm_fields['M']);
1125 CtdlFreeMessage(msg);
1127 /* Strip out the headers amd any other non-instruction line */
1128 lines = num_tokens(instr, '\n');
1129 for (i=0; i<lines; ++i) {
1130 extract_token(buf, instr, i, '\n');
1131 if (num_tokens(buf, '|') < 2) {
1132 remove_token(instr, i, '\n');
1138 /* Learn the message ID and find out about recent delivery attempts */
1139 lines = num_tokens(instr, '\n');
1140 for (i=0; i<lines; ++i) {
1141 extract_token(buf, instr, i, '\n');
1142 extract(key, buf, 0);
1143 if (!strcasecmp(key, "msgid")) {
1144 text_msgid = extract_long(buf, 1);
1146 if (!strcasecmp(key, "retry")) {
1147 /* double the retry interval after each attempt */
1148 retry = extract_long(buf, 1) * 2L;
1149 if (retry > SMTP_RETRY_MAX) {
1150 retry = SMTP_RETRY_MAX;
1152 remove_token(instr, i, '\n');
1154 if (!strcasecmp(key, "attempted")) {
1155 attempted = extract_long(buf, 1);
1156 if (attempted > last_attempted)
1157 last_attempted = attempted;
1162 * Postpone delivery if we've already tried recently.
1164 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1165 lprintf(7, "Retry time not yet reached.\n");
1172 * Bail out if there's no actual message associated with this
1174 if (text_msgid < 0L) {
1175 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1180 /* Plow through the instructions looking for 'remote' directives and
1181 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1182 * were experienced and it's time to try again)
1184 lines = num_tokens(instr, '\n');
1185 for (i=0; i<lines; ++i) {
1186 extract_token(buf, instr, i, '\n');
1187 extract(key, buf, 0);
1188 extract(addr, buf, 1);
1189 status = extract_int(buf, 2);
1190 extract(dsn, buf, 3);
1191 if ( (!strcasecmp(key, "remote"))
1192 && ((status==0)||(status==3)||(status==4)) ) {
1193 remove_token(instr, i, '\n');
1196 lprintf(9, "SMTP: Trying <%s>\n", addr);
1197 smtp_try(key, addr, &status, dsn, text_msgid);
1199 if (results == NULL) {
1200 results = mallok(1024);
1201 memset(results, 0, 1024);
1204 results = reallok(results,
1205 strlen(results) + 1024);
1207 sprintf(&results[strlen(results)],
1209 key, addr, status, dsn);
1214 if (results != NULL) {
1215 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1216 strcat(instr, results);
1221 /* Generate 'bounce' messages */
1222 smtp_do_bounce(instr);
1224 /* Go through the delivery list, deleting completed deliveries */
1225 incomplete_deliveries_remaining =
1226 smtp_purge_completed_deliveries(instr);
1230 * No delivery instructions remain, so delete both the instructions
1231 * message and the message message.
1233 if (incomplete_deliveries_remaining <= 0) {
1234 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1235 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1240 * Uncompleted delivery instructions remain, so delete the old
1241 * instructions and replace with the updated ones.
1243 if (incomplete_deliveries_remaining > 0) {
1244 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1245 msg = mallok(sizeof(struct CtdlMessage));
1246 memset(msg, 0, sizeof(struct CtdlMessage));
1247 msg->cm_magic = CTDLMESSAGE_MAGIC;
1248 msg->cm_anon_type = MES_NORMAL;
1249 msg->cm_format_type = FMT_RFC822;
1250 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1251 snprintf(msg->cm_fields['M'],
1253 "Content-type: %s\n\n%s\n"
1256 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1258 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1259 CtdlFreeMessage(msg);
1269 * Run through the queue sending out messages.
1271 void smtp_do_queue(void) {
1272 static int doing_queue = 0;
1275 * This is a simple concurrency check to make sure only one queue run
1276 * is done at a time. We could do this with a mutex, but since we
1277 * don't really require extremely fine granularity here, we'll do it
1278 * with a static variable instead.
1280 if (doing_queue) return;
1284 * Go ahead and run the queue
1286 lprintf(7, "SMTP: processing outbound queue\n");
1288 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1289 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1292 CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1293 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1295 lprintf(7, "SMTP: queue run completed\n");
1302 /*****************************************************************************/
1303 /* SMTP UTILITY COMMANDS */
1304 /*****************************************************************************/
1306 void cmd_smtp(char *argbuf) {
1313 if (CtdlAccessCheck(ac_aide)) return;
1315 extract(cmd, argbuf, 0);
1317 if (!strcasecmp(cmd, "mx")) {
1318 extract(node, argbuf, 1);
1319 num_mxhosts = getmx(buf, node);
1320 cprintf("%d %d MX hosts listed for %s\n",
1321 LISTING_FOLLOWS, num_mxhosts, node);
1322 for (i=0; i<num_mxhosts; ++i) {
1323 extract(node, buf, i);
1324 cprintf("%s\n", node);
1330 else if (!strcasecmp(cmd, "runqueue")) {
1332 cprintf("%d All outbound SMTP will be retried now.\n", OK);
1337 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1345 /*****************************************************************************/
1346 /* MODULE INITIALIZATION STUFF */
1347 /*****************************************************************************/
1350 char *Dynamic_Module_Init(void)
1352 SYM_SMTP = CtdlGetDynamicSymbol();
1354 CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
1359 CtdlRegisterServiceHook(0, /* ...and locally */
1364 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1365 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1366 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");