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) {
341 if (strlen(SMTP->from) != 0) {
342 cprintf("503 Only one sender permitted\r\n");
346 if (strncasecmp(argbuf, "From:", 5)) {
347 cprintf("501 Syntax error\r\n");
351 strcpy(SMTP->from, &argbuf[5]);
354 if (strlen(SMTP->from) == 0) {
355 cprintf("501 Empty sender name is not permitted\r\n");
360 /* If this SMTP connection is from a logged-in user, make sure that
361 * the user only sends email from his/her own address.
364 cvt = convert_internet_address(user, node, SMTP->from);
365 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
366 if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
367 cprintf("550 <%s> is not your address.\r\n",
369 strcpy(SMTP->from, "");
373 SMTP->message_originated_locally = 1;
377 /* Otherwise, make sure outsiders aren't trying to forge mail from
381 cvt = convert_internet_address(user, node, SMTP->from);
382 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
383 if (CtdlHostAlias(node) == hostalias_localhost) {
384 cprintf("550 You must log in to send mail from %s\r\n",
386 strcpy(SMTP->from, "");
391 cprintf("250 Sender ok\r\n");
397 * Implements the "RCPT To:" command
399 void smtp_rcpt(char *argbuf) {
401 struct recptypes *valid;
403 if (strlen(SMTP->from) == 0) {
404 cprintf("503 Need MAIL before RCPT\r\n");
408 if (strncasecmp(argbuf, "To:", 3)) {
409 cprintf("501 Syntax error\r\n");
413 strcpy(recp, &argbuf[3]);
415 stripallbut(recp, '<', '>');
417 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
418 cprintf("452 Too many recipients\r\n");
422 valid = validate_recipients(recp);
423 if (valid->num_error > 0) {
424 cprintf("599 Error: %s\r\n", valid->errormsg);
429 if (valid->num_internet > 0) {
430 if (SMTP->message_originated_locally == 0) {
431 cprintf("551 Relaying denied <%s>\r\n", recp);
437 cprintf("250 RCPT ok <%s>\r\n", recp);
438 if (strlen(SMTP->recipients) > 0) {
439 strcat(SMTP->recipients, ",");
441 strcat(SMTP->recipients, recp);
442 SMTP->number_of_recipients += 1;
449 * Implements the DATA command
451 void smtp_data(void) {
453 struct CtdlMessage *msg;
456 struct recptypes *valid;
458 if (strlen(SMTP->from) == 0) {
459 cprintf("503 Need MAIL command first.\r\n");
463 if (SMTP->number_of_recipients < 1) {
464 cprintf("503 Need RCPT command first.\r\n");
468 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
470 datestring(nowstamp, time(NULL), DATESTRING_RFC822);
473 if (body != NULL) snprintf(body, 4096,
474 "Received: from %s\n"
481 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
483 cprintf("550 Unable to save message: internal error.\r\n");
487 lprintf(9, "Converting message...\n");
488 msg = convert_internet_message(body);
490 /* If the user is locally authenticated, FORCE the From: header to
491 * show up as the real sender. Yes, this violates the RFC standard,
492 * but IT MAKES SENSE. Comment it out if you don't like this behavior.
495 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
496 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
497 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
498 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
499 msg->cm_fields['N'] = strdoop(config.c_nodename);
500 msg->cm_fields['H'] = strdoop(config.c_humannode);
503 /* Submit the message into the Citadel system. */
504 valid = validate_recipients(SMTP->recipients);
505 msgnum = CtdlSubmitMsg(msg, valid, "");
506 CtdlFreeMessage(msg);
510 cprintf("250 Message accepted.\r\n");
513 cprintf("550 Internal delivery error\r\n");
516 smtp_data_clear(); /* clear out the buffers now */
523 * Main command loop for SMTP sessions.
525 void smtp_command_loop(void) {
529 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
530 if (client_gets(cmdbuf) < 1) {
531 lprintf(3, "SMTP socket is broken. Ending session.\n");
535 lprintf(5, "SMTP: %s\n", cmdbuf);
536 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
538 if (SMTP->command_state == smtp_user) {
539 smtp_get_user(cmdbuf);
542 else if (SMTP->command_state == smtp_password) {
543 smtp_get_pass(cmdbuf);
546 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
547 smtp_auth(&cmdbuf[5]);
550 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
554 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
555 smtp_hello(&cmdbuf[5], 1);
558 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
559 smtp_expn(&cmdbuf[5]);
562 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
563 smtp_hello(&cmdbuf[5], 0);
566 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
570 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
571 smtp_mail(&cmdbuf[5]);
574 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
575 cprintf("250 NOOP\r\n");
578 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
579 cprintf("221 Goodbye...\r\n");
584 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
585 smtp_rcpt(&cmdbuf[5]);
588 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
592 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
593 smtp_vrfy(&cmdbuf[5]);
597 cprintf("502 I'm afraid I can't do that.\r\n");
605 /*****************************************************************************/
606 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
607 /*****************************************************************************/
614 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
617 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
624 char user[SIZ], node[SIZ], name[SIZ];
630 size_t blocksize = 0;
633 /* Parse out the host portion of the recipient address */
634 process_rfc822_addr(addr, user, node, name);
636 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
639 /* Load the message out of the database into a temp file */
641 if (msg_fp == NULL) {
643 sprintf(dsn, "Error creating temporary file");
647 CtdlRedirectOutput(msg_fp, -1);
648 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
649 CtdlRedirectOutput(NULL, -1);
650 fseek(msg_fp, 0L, SEEK_END);
651 msg_size = ftell(msg_fp);
655 /* Extract something to send later in the 'MAIL From:' command */
656 strcpy(mailfrom, "");
660 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
661 if (!strncasecmp(buf, "From:", 5)) {
662 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
664 for (i=0; i<strlen(mailfrom); ++i) {
665 if (!isprint(mailfrom[i])) {
666 strcpy(&mailfrom[i], &mailfrom[i+1]);
671 /* Strip out parenthesized names */
674 for (i=0; i<strlen(mailfrom); ++i) {
675 if (mailfrom[i] == '(') lp = i;
676 if (mailfrom[i] == ')') rp = i;
678 if ((lp>0)&&(rp>lp)) {
679 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
682 /* Prefer brokketized names */
685 for (i=0; i<strlen(mailfrom); ++i) {
686 if (mailfrom[i] == '<') lp = i;
687 if (mailfrom[i] == '>') rp = i;
689 if ( (lp>=0) && (rp>lp) ) {
691 strcpy(mailfrom, &mailfrom[lp]);
696 } while (scan_done == 0);
697 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
699 /* Figure out what mail exchanger host we have to connect to */
700 num_mxhosts = getmx(mxhosts, node);
701 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
702 if (num_mxhosts < 1) {
704 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
708 for (mx=0; mx<num_mxhosts; ++mx) {
709 extract(buf, mxhosts, mx);
710 lprintf(9, "Trying <%s>\n", buf);
711 sock = sock_connect(buf, "25", "tcp");
712 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
713 if (sock >= 0) lprintf(9, "Connected!\n");
714 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
715 if (sock >= 0) break;
719 *status = 4; /* dsn is already filled in */
723 /* Process the SMTP greeting from the server */
724 if (ml_sock_gets(sock, buf) < 0) {
726 strcpy(dsn, "Connection broken during SMTP conversation");
729 lprintf(9, "<%s\n", buf);
733 safestrncpy(dsn, &buf[4], 1023);
738 safestrncpy(dsn, &buf[4], 1023);
743 /* At this point we know we are talking to a real SMTP server */
745 /* Do a HELO command */
746 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
747 lprintf(9, ">%s", buf);
748 sock_write(sock, buf, strlen(buf));
749 if (ml_sock_gets(sock, buf) < 0) {
751 strcpy(dsn, "Connection broken during SMTP HELO");
754 lprintf(9, "<%s\n", buf);
758 safestrncpy(dsn, &buf[4], 1023);
763 safestrncpy(dsn, &buf[4], 1023);
769 /* HELO succeeded, now try the MAIL From: command */
770 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
771 lprintf(9, ">%s", buf);
772 sock_write(sock, buf, strlen(buf));
773 if (ml_sock_gets(sock, buf) < 0) {
775 strcpy(dsn, "Connection broken during SMTP MAIL");
778 lprintf(9, "<%s\n", buf);
782 safestrncpy(dsn, &buf[4], 1023);
787 safestrncpy(dsn, &buf[4], 1023);
793 /* MAIL succeeded, now try the RCPT To: command */
794 snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
795 lprintf(9, ">%s", buf);
796 sock_write(sock, buf, strlen(buf));
797 if (ml_sock_gets(sock, buf) < 0) {
799 strcpy(dsn, "Connection broken during SMTP RCPT");
802 lprintf(9, "<%s\n", buf);
806 safestrncpy(dsn, &buf[4], 1023);
811 safestrncpy(dsn, &buf[4], 1023);
817 /* RCPT succeeded, now try the DATA command */
818 lprintf(9, ">DATA\n");
819 sock_write(sock, "DATA\r\n", 6);
820 if (ml_sock_gets(sock, buf) < 0) {
822 strcpy(dsn, "Connection broken during SMTP DATA");
825 lprintf(9, "<%s\n", buf);
829 safestrncpy(dsn, &buf[4], 1023);
834 safestrncpy(dsn, &buf[4], 1023);
839 /* If we reach this point, the server is expecting data */
841 while (msg_size > 0) {
842 blocksize = sizeof(buf);
843 if (blocksize > msg_size) blocksize = msg_size;
844 fread(buf, blocksize, 1, msg_fp);
845 sock_write(sock, buf, blocksize);
846 msg_size -= blocksize;
848 if (buf[blocksize-1] != 10) {
849 lprintf(5, "Possible problem: message did not correctly "
850 "terminate. (expecting 0x10, got 0x%02x)\n",
854 sock_write(sock, ".\r\n", 3);
855 if (ml_sock_gets(sock, buf) < 0) {
857 strcpy(dsn, "Connection broken during SMTP message transmit");
860 lprintf(9, "%s\n", buf);
864 safestrncpy(dsn, &buf[4], 1023);
869 safestrncpy(dsn, &buf[4], 1023);
875 safestrncpy(dsn, &buf[4], 1023);
878 lprintf(9, ">QUIT\n");
879 sock_write(sock, "QUIT\r\n", 6);
880 ml_sock_gets(sock, buf);
881 lprintf(9, "<%s\n", buf);
883 bail: if (msg_fp != NULL) fclose(msg_fp);
891 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
892 * instructions for "5" codes (permanent fatal errors) and produce/deliver
893 * a "bounce" message (delivery status notification).
895 void smtp_do_bounce(char *instr) {
906 long bounce_msgid = (-1);
907 time_t submitted = 0L;
908 struct CtdlMessage *bmsg = NULL;
910 struct recptypes *valid;
911 int successful_bounce = 0;
913 lprintf(9, "smtp_do_bounce() called\n");
914 strcpy(bounceto, "");
916 lines = num_tokens(instr, '\n');
919 /* See if it's time to give up on delivery of this message */
920 for (i=0; i<lines; ++i) {
921 extract_token(buf, instr, i, '\n');
922 extract(key, buf, 0);
923 extract(addr, buf, 1);
924 if (!strcasecmp(key, "submitted")) {
925 submitted = atol(addr);
929 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
935 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
936 if (bmsg == NULL) return;
937 memset(bmsg, 0, sizeof(struct CtdlMessage));
939 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
940 bmsg->cm_anon_type = MES_NORMAL;
941 bmsg->cm_format_type = 1;
942 bmsg->cm_fields['A'] = strdoop("Citadel");
943 bmsg->cm_fields['O'] = strdoop(MAILROOM);
944 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
946 if (give_up) bmsg->cm_fields['M'] = strdoop(
947 "A message you sent could not be delivered to some or all of its recipients\n"
948 "due to prolonged unavailability of its destination(s).\n"
949 "Giving up on the following addresses:\n\n"
952 else bmsg->cm_fields['M'] = strdoop(
953 "A message you sent could not be delivered to some or all of its recipients.\n"
954 "The following addresses were undeliverable:\n\n"
958 * Now go through the instructions checking for stuff.
961 for (i=0; i<lines; ++i) {
962 extract_token(buf, instr, i, '\n');
963 extract(key, buf, 0);
964 extract(addr, buf, 1);
965 status = extract_int(buf, 2);
966 extract(dsn, buf, 3);
969 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
970 key, addr, status, dsn);
972 if (!strcasecmp(key, "bounceto")) {
973 strcpy(bounceto, addr);
977 (!strcasecmp(key, "local"))
978 || (!strcasecmp(key, "remote"))
979 || (!strcasecmp(key, "ignet"))
980 || (!strcasecmp(key, "room"))
982 if (status == 5) bounce_this = 1;
983 if (give_up) bounce_this = 1;
989 if (bmsg->cm_fields['M'] == NULL) {
990 lprintf(2, "ERROR ... M field is null "
991 "(%s:%d)\n", __FILE__, __LINE__);
994 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
995 strlen(bmsg->cm_fields['M']) + 1024 );
996 strcat(bmsg->cm_fields['M'], addr);
997 strcat(bmsg->cm_fields['M'], ": ");
998 strcat(bmsg->cm_fields['M'], dsn);
999 strcat(bmsg->cm_fields['M'], "\n");
1001 remove_token(instr, i, '\n');
1007 /* Deliver the bounce if there's anything worth mentioning */
1008 lprintf(9, "num_bounces = %d\n", num_bounces);
1009 if (num_bounces > 0) {
1011 /* First try the user who sent the message */
1012 lprintf(9, "bounce to user? <%s>\n", bounceto);
1013 if (strlen(bounceto) == 0) {
1014 lprintf(7, "No bounce address specified\n");
1015 bounce_msgid = (-1L);
1018 /* Can we deliver the bounce to the original sender? */
1019 valid = validate_recipients(bounceto);
1020 if (valid != NULL) {
1021 if (valid->num_error == 0) {
1022 CtdlSubmitMsg(bmsg, valid, "");
1023 successful_bounce = 1;
1027 /* If not, post it in the Aide> room */
1028 if (successful_bounce == 0) {
1029 CtdlSubmitMsg(bmsg, NULL, AIDEROOM);
1032 /* Free up the memory we used */
1033 if (valid != NULL) {
1038 CtdlFreeMessage(bmsg);
1039 lprintf(9, "Done processing bounces\n");
1044 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1045 * set of delivery instructions for completed deliveries and remove them.
1047 * It returns the number of incomplete deliveries remaining.
1049 int smtp_purge_completed_deliveries(char *instr) {
1060 lines = num_tokens(instr, '\n');
1061 for (i=0; i<lines; ++i) {
1062 extract_token(buf, instr, i, '\n');
1063 extract(key, buf, 0);
1064 extract(addr, buf, 1);
1065 status = extract_int(buf, 2);
1066 extract(dsn, buf, 3);
1071 (!strcasecmp(key, "local"))
1072 || (!strcasecmp(key, "remote"))
1073 || (!strcasecmp(key, "ignet"))
1074 || (!strcasecmp(key, "room"))
1076 if (status == 2) completed = 1;
1081 remove_token(instr, i, '\n');
1094 * Called by smtp_do_queue() to handle an individual message.
1096 void smtp_do_procmsg(long msgnum, void *userdata) {
1097 struct CtdlMessage *msg;
1099 char *results = NULL;
1107 long text_msgid = (-1);
1108 int incomplete_deliveries_remaining;
1109 time_t attempted = 0L;
1110 time_t last_attempted = 0L;
1111 time_t retry = SMTP_RETRY_INTERVAL;
1113 lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1115 msg = CtdlFetchMessage(msgnum);
1117 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1121 instr = strdoop(msg->cm_fields['M']);
1122 CtdlFreeMessage(msg);
1124 /* Strip out the headers amd any other non-instruction line */
1125 lines = num_tokens(instr, '\n');
1126 for (i=0; i<lines; ++i) {
1127 extract_token(buf, instr, i, '\n');
1128 if (num_tokens(buf, '|') < 2) {
1129 remove_token(instr, i, '\n');
1135 /* Learn the message ID and find out about recent delivery attempts */
1136 lines = num_tokens(instr, '\n');
1137 for (i=0; i<lines; ++i) {
1138 extract_token(buf, instr, i, '\n');
1139 extract(key, buf, 0);
1140 if (!strcasecmp(key, "msgid")) {
1141 text_msgid = extract_long(buf, 1);
1143 if (!strcasecmp(key, "retry")) {
1144 /* double the retry interval after each attempt */
1145 retry = extract_long(buf, 1) * 2L;
1146 if (retry > SMTP_RETRY_MAX) {
1147 retry = SMTP_RETRY_MAX;
1149 remove_token(instr, i, '\n');
1151 if (!strcasecmp(key, "attempted")) {
1152 attempted = extract_long(buf, 1);
1153 if (attempted > last_attempted)
1154 last_attempted = attempted;
1159 * Postpone delivery if we've already tried recently.
1161 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1162 lprintf(7, "Retry time not yet reached.\n");
1169 * Bail out if there's no actual message associated with this
1171 if (text_msgid < 0L) {
1172 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1177 /* Plow through the instructions looking for 'remote' directives and
1178 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1179 * were experienced and it's time to try again)
1181 lines = num_tokens(instr, '\n');
1182 for (i=0; i<lines; ++i) {
1183 extract_token(buf, instr, i, '\n');
1184 extract(key, buf, 0);
1185 extract(addr, buf, 1);
1186 status = extract_int(buf, 2);
1187 extract(dsn, buf, 3);
1188 if ( (!strcasecmp(key, "remote"))
1189 && ((status==0)||(status==3)||(status==4)) ) {
1190 remove_token(instr, i, '\n');
1193 lprintf(9, "SMTP: Trying <%s>\n", addr);
1194 smtp_try(key, addr, &status, dsn, text_msgid);
1196 if (results == NULL) {
1197 results = mallok(1024);
1198 memset(results, 0, 1024);
1201 results = reallok(results,
1202 strlen(results) + 1024);
1204 sprintf(&results[strlen(results)],
1206 key, addr, status, dsn);
1211 if (results != NULL) {
1212 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1213 strcat(instr, results);
1218 /* Generate 'bounce' messages */
1219 smtp_do_bounce(instr);
1221 /* Go through the delivery list, deleting completed deliveries */
1222 incomplete_deliveries_remaining =
1223 smtp_purge_completed_deliveries(instr);
1227 * No delivery instructions remain, so delete both the instructions
1228 * message and the message message.
1230 if (incomplete_deliveries_remaining <= 0) {
1231 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1232 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1237 * Uncompleted delivery instructions remain, so delete the old
1238 * instructions and replace with the updated ones.
1240 if (incomplete_deliveries_remaining > 0) {
1241 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1242 msg = mallok(sizeof(struct CtdlMessage));
1243 memset(msg, 0, sizeof(struct CtdlMessage));
1244 msg->cm_magic = CTDLMESSAGE_MAGIC;
1245 msg->cm_anon_type = MES_NORMAL;
1246 msg->cm_format_type = FMT_RFC822;
1247 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1248 snprintf(msg->cm_fields['M'],
1250 "Content-type: %s\n\n%s\n"
1253 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1255 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1256 CtdlFreeMessage(msg);
1266 * Run through the queue sending out messages.
1268 void smtp_do_queue(void) {
1269 static int doing_queue = 0;
1272 * This is a simple concurrency check to make sure only one queue run
1273 * is done at a time. We could do this with a mutex, but since we
1274 * don't really require extremely fine granularity here, we'll do it
1275 * with a static variable instead.
1277 if (doing_queue) return;
1281 * Go ahead and run the queue
1283 lprintf(7, "SMTP: processing outbound queue\n");
1285 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1286 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1289 CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1290 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1292 lprintf(7, "SMTP: queue run completed\n");
1299 /*****************************************************************************/
1300 /* SMTP UTILITY COMMANDS */
1301 /*****************************************************************************/
1303 void cmd_smtp(char *argbuf) {
1310 if (CtdlAccessCheck(ac_aide)) return;
1312 extract(cmd, argbuf, 0);
1314 if (!strcasecmp(cmd, "mx")) {
1315 extract(node, argbuf, 1);
1316 num_mxhosts = getmx(buf, node);
1317 cprintf("%d %d MX hosts listed for %s\n",
1318 LISTING_FOLLOWS, num_mxhosts, node);
1319 for (i=0; i<num_mxhosts; ++i) {
1320 extract(node, buf, i);
1321 cprintf("%s\n", node);
1327 else if (!strcasecmp(cmd, "runqueue")) {
1329 cprintf("%d All outbound SMTP will be retried now.\n", OK);
1334 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1342 /*****************************************************************************/
1343 /* MODULE INITIALIZATION STUFF */
1344 /*****************************************************************************/
1347 char *Dynamic_Module_Init(void)
1349 SYM_SMTP = CtdlGetDynamicSymbol();
1351 CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
1356 CtdlRegisterServiceHook(0, /* ...and locally */
1361 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1362 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1363 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");