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);
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(char *key, char *addr, int *status, char *dsn, long msgnum)
630 char user[SIZ], node[SIZ], name[SIZ];
636 size_t blocksize = 0;
639 /* Parse out the host portion of the recipient address */
640 process_rfc822_addr(addr, user, node, name);
642 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
645 /* Load the message out of the database into a temp file */
647 if (msg_fp == NULL) {
649 sprintf(dsn, "Error creating temporary file");
653 CtdlRedirectOutput(msg_fp, -1);
654 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
655 CtdlRedirectOutput(NULL, -1);
656 fseek(msg_fp, 0L, SEEK_END);
657 msg_size = ftell(msg_fp);
661 /* Extract something to send later in the 'MAIL From:' command */
662 strcpy(mailfrom, "");
666 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
667 if (!strncasecmp(buf, "From:", 5)) {
668 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
670 for (i=0; i<strlen(mailfrom); ++i) {
671 if (!isprint(mailfrom[i])) {
672 strcpy(&mailfrom[i], &mailfrom[i+1]);
677 /* Strip out parenthesized names */
680 for (i=0; i<strlen(mailfrom); ++i) {
681 if (mailfrom[i] == '(') lp = i;
682 if (mailfrom[i] == ')') rp = i;
684 if ((lp>0)&&(rp>lp)) {
685 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
688 /* Prefer brokketized names */
691 for (i=0; i<strlen(mailfrom); ++i) {
692 if (mailfrom[i] == '<') lp = i;
693 if (mailfrom[i] == '>') rp = i;
695 if ( (lp>=0) && (rp>lp) ) {
697 strcpy(mailfrom, &mailfrom[lp]);
702 } while (scan_done == 0);
703 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
705 /* Figure out what mail exchanger host we have to connect to */
706 num_mxhosts = getmx(mxhosts, node);
707 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
708 if (num_mxhosts < 1) {
710 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
714 for (mx=0; mx<num_mxhosts; ++mx) {
715 extract(buf, mxhosts, mx);
716 lprintf(9, "Trying <%s>\n", buf);
717 sock = sock_connect(buf, "25", "tcp");
718 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
719 if (sock >= 0) lprintf(9, "Connected!\n");
720 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
721 if (sock >= 0) break;
725 *status = 4; /* dsn is already filled in */
729 /* Process the SMTP greeting from the server */
730 if (ml_sock_gets(sock, buf) < 0) {
732 strcpy(dsn, "Connection broken during SMTP conversation");
735 lprintf(9, "<%s\n", buf);
739 safestrncpy(dsn, &buf[4], 1023);
744 safestrncpy(dsn, &buf[4], 1023);
749 /* At this point we know we are talking to a real SMTP server */
751 /* Do a HELO command */
752 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
753 lprintf(9, ">%s", buf);
754 sock_write(sock, buf, strlen(buf));
755 if (ml_sock_gets(sock, buf) < 0) {
757 strcpy(dsn, "Connection broken during SMTP HELO");
760 lprintf(9, "<%s\n", buf);
764 safestrncpy(dsn, &buf[4], 1023);
769 safestrncpy(dsn, &buf[4], 1023);
775 /* HELO succeeded, now try the MAIL From: command */
776 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
777 lprintf(9, ">%s", buf);
778 sock_write(sock, buf, strlen(buf));
779 if (ml_sock_gets(sock, buf) < 0) {
781 strcpy(dsn, "Connection broken during SMTP MAIL");
784 lprintf(9, "<%s\n", buf);
788 safestrncpy(dsn, &buf[4], 1023);
793 safestrncpy(dsn, &buf[4], 1023);
799 /* MAIL succeeded, now try the RCPT To: command */
800 snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
801 lprintf(9, ">%s", buf);
802 sock_write(sock, buf, strlen(buf));
803 if (ml_sock_gets(sock, buf) < 0) {
805 strcpy(dsn, "Connection broken during SMTP RCPT");
808 lprintf(9, "<%s\n", buf);
812 safestrncpy(dsn, &buf[4], 1023);
817 safestrncpy(dsn, &buf[4], 1023);
823 /* RCPT succeeded, now try the DATA command */
824 lprintf(9, ">DATA\n");
825 sock_write(sock, "DATA\r\n", 6);
826 if (ml_sock_gets(sock, buf) < 0) {
828 strcpy(dsn, "Connection broken during SMTP DATA");
831 lprintf(9, "<%s\n", buf);
835 safestrncpy(dsn, &buf[4], 1023);
840 safestrncpy(dsn, &buf[4], 1023);
845 /* If we reach this point, the server is expecting data */
847 while (msg_size > 0) {
848 blocksize = sizeof(buf);
849 if (blocksize > msg_size) blocksize = msg_size;
850 fread(buf, blocksize, 1, msg_fp);
851 sock_write(sock, buf, blocksize);
852 msg_size -= blocksize;
854 if (buf[blocksize-1] != 10) {
855 lprintf(5, "Possible problem: message did not correctly "
856 "terminate. (expecting 0x10, got 0x%02x)\n",
860 sock_write(sock, ".\r\n", 3);
861 if (ml_sock_gets(sock, buf) < 0) {
863 strcpy(dsn, "Connection broken during SMTP message transmit");
866 lprintf(9, "%s\n", buf);
870 safestrncpy(dsn, &buf[4], 1023);
875 safestrncpy(dsn, &buf[4], 1023);
881 safestrncpy(dsn, &buf[4], 1023);
884 lprintf(9, ">QUIT\n");
885 sock_write(sock, "QUIT\r\n", 6);
886 ml_sock_gets(sock, buf);
887 lprintf(9, "<%s\n", buf);
889 bail: if (msg_fp != NULL) fclose(msg_fp);
897 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
898 * instructions for "5" codes (permanent fatal errors) and produce/deliver
899 * a "bounce" message (delivery status notification).
901 void smtp_do_bounce(char *instr) {
912 long bounce_msgid = (-1);
913 time_t submitted = 0L;
914 struct CtdlMessage *bmsg = NULL;
916 struct recptypes *valid;
917 int successful_bounce = 0;
919 lprintf(9, "smtp_do_bounce() called\n");
920 strcpy(bounceto, "");
922 lines = num_tokens(instr, '\n');
925 /* See if it's time to give up on delivery of this message */
926 for (i=0; i<lines; ++i) {
927 extract_token(buf, instr, i, '\n');
928 extract(key, buf, 0);
929 extract(addr, buf, 1);
930 if (!strcasecmp(key, "submitted")) {
931 submitted = atol(addr);
935 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
941 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
942 if (bmsg == NULL) return;
943 memset(bmsg, 0, sizeof(struct CtdlMessage));
945 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
946 bmsg->cm_anon_type = MES_NORMAL;
947 bmsg->cm_format_type = 1;
948 bmsg->cm_fields['A'] = strdoop("Citadel");
949 bmsg->cm_fields['O'] = strdoop(MAILROOM);
950 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
952 if (give_up) bmsg->cm_fields['M'] = strdoop(
953 "A message you sent could not be delivered to some or all of its recipients\n"
954 "due to prolonged unavailability of its destination(s).\n"
955 "Giving up on the following addresses:\n\n"
958 else bmsg->cm_fields['M'] = strdoop(
959 "A message you sent could not be delivered to some or all of its recipients.\n"
960 "The following addresses were undeliverable:\n\n"
964 * Now go through the instructions checking for stuff.
967 for (i=0; i<lines; ++i) {
968 extract_token(buf, instr, i, '\n');
969 extract(key, buf, 0);
970 extract(addr, buf, 1);
971 status = extract_int(buf, 2);
972 extract(dsn, buf, 3);
975 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
976 key, addr, status, dsn);
978 if (!strcasecmp(key, "bounceto")) {
979 strcpy(bounceto, addr);
983 (!strcasecmp(key, "local"))
984 || (!strcasecmp(key, "remote"))
985 || (!strcasecmp(key, "ignet"))
986 || (!strcasecmp(key, "room"))
988 if (status == 5) bounce_this = 1;
989 if (give_up) bounce_this = 1;
995 if (bmsg->cm_fields['M'] == NULL) {
996 lprintf(2, "ERROR ... M field is null "
997 "(%s:%d)\n", __FILE__, __LINE__);
1000 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1001 strlen(bmsg->cm_fields['M']) + 1024 );
1002 strcat(bmsg->cm_fields['M'], addr);
1003 strcat(bmsg->cm_fields['M'], ": ");
1004 strcat(bmsg->cm_fields['M'], dsn);
1005 strcat(bmsg->cm_fields['M'], "\n");
1007 remove_token(instr, i, '\n');
1013 /* Deliver the bounce if there's anything worth mentioning */
1014 lprintf(9, "num_bounces = %d\n", num_bounces);
1015 if (num_bounces > 0) {
1017 /* First try the user who sent the message */
1018 lprintf(9, "bounce to user? <%s>\n", bounceto);
1019 if (strlen(bounceto) == 0) {
1020 lprintf(7, "No bounce address specified\n");
1021 bounce_msgid = (-1L);
1024 /* Can we deliver the bounce to the original sender? */
1025 valid = validate_recipients(bounceto);
1026 if (valid != NULL) {
1027 if (valid->num_error == 0) {
1028 CtdlSubmitMsg(bmsg, valid, "");
1029 successful_bounce = 1;
1033 /* If not, post it in the Aide> room */
1034 if (successful_bounce == 0) {
1035 CtdlSubmitMsg(bmsg, NULL, AIDEROOM);
1038 /* Free up the memory we used */
1039 if (valid != NULL) {
1044 CtdlFreeMessage(bmsg);
1045 lprintf(9, "Done processing bounces\n");
1050 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1051 * set of delivery instructions for completed deliveries and remove them.
1053 * It returns the number of incomplete deliveries remaining.
1055 int smtp_purge_completed_deliveries(char *instr) {
1066 lines = num_tokens(instr, '\n');
1067 for (i=0; i<lines; ++i) {
1068 extract_token(buf, instr, i, '\n');
1069 extract(key, buf, 0);
1070 extract(addr, buf, 1);
1071 status = extract_int(buf, 2);
1072 extract(dsn, buf, 3);
1077 (!strcasecmp(key, "local"))
1078 || (!strcasecmp(key, "remote"))
1079 || (!strcasecmp(key, "ignet"))
1080 || (!strcasecmp(key, "room"))
1082 if (status == 2) completed = 1;
1087 remove_token(instr, i, '\n');
1100 * Called by smtp_do_queue() to handle an individual message.
1102 void smtp_do_procmsg(long msgnum, void *userdata) {
1103 struct CtdlMessage *msg;
1105 char *results = NULL;
1113 long text_msgid = (-1);
1114 int incomplete_deliveries_remaining;
1115 time_t attempted = 0L;
1116 time_t last_attempted = 0L;
1117 time_t retry = SMTP_RETRY_INTERVAL;
1119 lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1121 msg = CtdlFetchMessage(msgnum);
1123 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1127 instr = strdoop(msg->cm_fields['M']);
1128 CtdlFreeMessage(msg);
1130 /* Strip out the headers amd any other non-instruction line */
1131 lines = num_tokens(instr, '\n');
1132 for (i=0; i<lines; ++i) {
1133 extract_token(buf, instr, i, '\n');
1134 if (num_tokens(buf, '|') < 2) {
1135 remove_token(instr, i, '\n');
1141 /* Learn the message ID and find out about recent delivery attempts */
1142 lines = num_tokens(instr, '\n');
1143 for (i=0; i<lines; ++i) {
1144 extract_token(buf, instr, i, '\n');
1145 extract(key, buf, 0);
1146 if (!strcasecmp(key, "msgid")) {
1147 text_msgid = extract_long(buf, 1);
1149 if (!strcasecmp(key, "retry")) {
1150 /* double the retry interval after each attempt */
1151 retry = extract_long(buf, 1) * 2L;
1152 if (retry > SMTP_RETRY_MAX) {
1153 retry = SMTP_RETRY_MAX;
1155 remove_token(instr, i, '\n');
1157 if (!strcasecmp(key, "attempted")) {
1158 attempted = extract_long(buf, 1);
1159 if (attempted > last_attempted)
1160 last_attempted = attempted;
1165 * Postpone delivery if we've already tried recently.
1167 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1168 lprintf(7, "Retry time not yet reached.\n");
1175 * Bail out if there's no actual message associated with this
1177 if (text_msgid < 0L) {
1178 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1183 /* Plow through the instructions looking for 'remote' directives and
1184 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1185 * were experienced and it's time to try again)
1187 lines = num_tokens(instr, '\n');
1188 for (i=0; i<lines; ++i) {
1189 extract_token(buf, instr, i, '\n');
1190 extract(key, buf, 0);
1191 extract(addr, buf, 1);
1192 status = extract_int(buf, 2);
1193 extract(dsn, buf, 3);
1194 if ( (!strcasecmp(key, "remote"))
1195 && ((status==0)||(status==3)||(status==4)) ) {
1196 remove_token(instr, i, '\n');
1199 lprintf(9, "SMTP: Trying <%s>\n", addr);
1200 smtp_try(key, addr, &status, dsn, text_msgid);
1202 if (results == NULL) {
1203 results = mallok(1024);
1204 memset(results, 0, 1024);
1207 results = reallok(results,
1208 strlen(results) + 1024);
1210 sprintf(&results[strlen(results)],
1212 key, addr, status, dsn);
1217 if (results != NULL) {
1218 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1219 strcat(instr, results);
1224 /* Generate 'bounce' messages */
1225 smtp_do_bounce(instr);
1227 /* Go through the delivery list, deleting completed deliveries */
1228 incomplete_deliveries_remaining =
1229 smtp_purge_completed_deliveries(instr);
1233 * No delivery instructions remain, so delete both the instructions
1234 * message and the message message.
1236 if (incomplete_deliveries_remaining <= 0) {
1237 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1238 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1243 * Uncompleted delivery instructions remain, so delete the old
1244 * instructions and replace with the updated ones.
1246 if (incomplete_deliveries_remaining > 0) {
1247 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1248 msg = mallok(sizeof(struct CtdlMessage));
1249 memset(msg, 0, sizeof(struct CtdlMessage));
1250 msg->cm_magic = CTDLMESSAGE_MAGIC;
1251 msg->cm_anon_type = MES_NORMAL;
1252 msg->cm_format_type = FMT_RFC822;
1253 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1254 snprintf(msg->cm_fields['M'],
1256 "Content-type: %s\n\n%s\n"
1259 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1261 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1262 CtdlFreeMessage(msg);
1272 * Run through the queue sending out messages.
1274 void smtp_do_queue(void) {
1275 static int doing_queue = 0;
1278 * This is a simple concurrency check to make sure only one queue run
1279 * is done at a time. We could do this with a mutex, but since we
1280 * don't really require extremely fine granularity here, we'll do it
1281 * with a static variable instead.
1283 if (doing_queue) return;
1287 * Go ahead and run the queue
1289 lprintf(7, "SMTP: processing outbound queue\n");
1291 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1292 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1295 CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1296 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1298 lprintf(7, "SMTP: queue run completed\n");
1305 /*****************************************************************************/
1306 /* SMTP UTILITY COMMANDS */
1307 /*****************************************************************************/
1309 void cmd_smtp(char *argbuf) {
1316 if (CtdlAccessCheck(ac_aide)) return;
1318 extract(cmd, argbuf, 0);
1320 if (!strcasecmp(cmd, "mx")) {
1321 extract(node, argbuf, 1);
1322 num_mxhosts = getmx(buf, node);
1323 cprintf("%d %d MX hosts listed for %s\n",
1324 LISTING_FOLLOWS, num_mxhosts, node);
1325 for (i=0; i<num_mxhosts; ++i) {
1326 extract(node, buf, i);
1327 cprintf("%s\n", node);
1333 else if (!strcasecmp(cmd, "runqueue")) {
1335 cprintf("%d All outbound SMTP will be retried now.\n", OK);
1340 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1348 /*****************************************************************************/
1349 /* MODULE INITIALIZATION STUFF */
1350 /*****************************************************************************/
1353 char *Dynamic_Module_Init(void)
1355 SYM_SMTP = CtdlGetDynamicSymbol();
1357 CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
1362 CtdlRegisterServiceHook(0, /* ...and locally */
1367 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1368 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1369 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");