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;
65 int number_of_recipients;
68 int message_originated_locally;
69 struct recptypes valid;
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 Authentication successful.\r\n");
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 SMTP->number_of_recipients = 0;
326 SMTP->delivery_mode = 0;
327 SMTP->message_originated_locally = 0;
328 memset(&SMTP->valid, 0, sizeof(struct recptypes));
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) {
405 if (strlen(SMTP->from) == 0) {
406 cprintf("503 Need MAIL before RCPT\r\n");
410 if (strncasecmp(argbuf, "To:", 3)) {
411 cprintf("501 Syntax error\r\n");
415 strcpy(recp, &argbuf[3]);
417 stripallbut(recp, '<', '>');
419 cvt = convert_internet_address(user, node, recp);
420 snprintf(recp, sizeof recp, "%s@%s", user, node);
421 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
423 /* FIXME possible buffer overflow type of issues here. */
425 case rfc822_address_locally_validated:
426 cprintf("250 %s is a valid local user.\r\n", user);
427 if (SMTP->valid.num_local > 0) {
428 strcat(SMTP->valid.recp_local, "|");
430 strcat(SMTP->valid.recp_local, user);
431 SMTP->valid.num_local += 1;
432 SMTP->number_of_recipients += 1;
435 case rfc822_room_delivery:
436 cprintf("250 Delivering to room '%s'\r\n", user);
437 if (SMTP->valid.num_room > 0) {
438 strcat(SMTP->valid.recp_room, "|");
440 strcat(SMTP->valid.recp_room, user);
441 SMTP->valid.num_room += 1;
442 SMTP->number_of_recipients += 1;
445 case rfc822_address_on_citadel_network:
446 cprintf("250 '%s' is a valid network user.\r\n", user);
447 if (SMTP->valid.num_ignet > 0) {
448 strcat(SMTP->valid.recp_ignet, "|");
450 strcat(SMTP->valid.recp_ignet, user);
451 SMTP->valid.num_ignet += 1;
452 SMTP->number_of_recipients += 1;
455 case rfc822_no_such_user:
456 cprintf("550 %s: no such user\r\n", recp);
459 case rfc822_address_nonlocal:
460 if (SMTP->message_originated_locally == 0) {
461 cprintf("551 Relaying denied.\r\n");
464 cprintf("250 Remote recipient %s ok\r\n", recp);
466 if (SMTP->valid.num_internet > 0) {
467 strcat(SMTP->valid.recp_internet, "|");
469 strcat(SMTP->valid.recp_internet, user);
470 SMTP->valid.num_internet += 1;
471 SMTP->number_of_recipients += 1;
477 cprintf("599 Unknown error\r\n");
484 * Implements the DATA command
486 void smtp_data(void) {
488 struct CtdlMessage *msg;
492 if (strlen(SMTP->from) == 0) {
493 cprintf("503 Need MAIL command first.\r\n");
497 if (SMTP->number_of_recipients < 1) {
498 cprintf("503 Need RCPT command first.\r\n");
502 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
504 datestring(nowstamp, time(NULL), DATESTRING_RFC822);
507 if (body != NULL) snprintf(body, 4096,
508 "Received: from %s\n"
515 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
517 cprintf("550 Unable to save message: internal error.\r\n");
521 lprintf(9, "Converting message...\n");
522 msg = convert_internet_message(body);
524 /* If the user is locally authenticated, FORCE the From: header to
525 * show up as the real sender. Yes, this violates the RFC standard,
526 * but IT MAKES SENSE. Comment it out if you don't like this behavior.
529 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
530 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
531 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
532 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
533 msg->cm_fields['N'] = strdoop(config.c_nodename);
534 msg->cm_fields['H'] = strdoop(config.c_humannode);
537 /* Submit the message into the Citadel system. */
538 msgnum = CtdlSubmitMsg(msg, &SMTP->valid, "");
539 CtdlFreeMessage(msg);
542 cprintf("250 Message accepted.\r\n");
545 cprintf("550 Internal delivery error\r\n");
548 smtp_data_clear(); /* clear out the buffers now */
555 * Main command loop for SMTP sessions.
557 void smtp_command_loop(void) {
561 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
562 if (client_gets(cmdbuf) < 1) {
563 lprintf(3, "SMTP socket is broken. Ending session.\n");
567 lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
568 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
570 if (SMTP->command_state == smtp_user) {
571 smtp_get_user(cmdbuf);
574 else if (SMTP->command_state == smtp_password) {
575 smtp_get_pass(cmdbuf);
578 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
579 smtp_auth(&cmdbuf[5]);
582 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
586 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
587 smtp_hello(&cmdbuf[5], 1);
590 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
591 smtp_expn(&cmdbuf[5]);
594 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
595 smtp_hello(&cmdbuf[5], 0);
598 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
602 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
603 smtp_mail(&cmdbuf[5]);
606 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
607 cprintf("250 NOOP\r\n");
610 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
611 cprintf("221 Goodbye...\r\n");
616 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
617 smtp_rcpt(&cmdbuf[5]);
620 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
624 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
625 smtp_vrfy(&cmdbuf[5]);
629 cprintf("502 I'm afraid I can't do that.\r\n");
637 /*****************************************************************************/
638 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
639 /*****************************************************************************/
646 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
649 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
656 char user[SIZ], node[SIZ], name[SIZ];
662 size_t blocksize = 0;
665 /* Parse out the host portion of the recipient address */
666 process_rfc822_addr(addr, user, node, name);
668 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
671 /* Load the message out of the database into a temp file */
673 if (msg_fp == NULL) {
675 sprintf(dsn, "Error creating temporary file");
679 CtdlRedirectOutput(msg_fp, -1);
680 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
681 CtdlRedirectOutput(NULL, -1);
682 fseek(msg_fp, 0L, SEEK_END);
683 msg_size = ftell(msg_fp);
687 /* Extract something to send later in the 'MAIL From:' command */
688 strcpy(mailfrom, "");
692 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
693 if (!strncasecmp(buf, "From:", 5)) {
694 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
696 for (i=0; i<strlen(mailfrom); ++i) {
697 if (!isprint(mailfrom[i])) {
698 strcpy(&mailfrom[i], &mailfrom[i+1]);
703 /* Strip out parenthesized names */
706 for (i=0; i<strlen(mailfrom); ++i) {
707 if (mailfrom[i] == '(') lp = i;
708 if (mailfrom[i] == ')') rp = i;
710 if ((lp>0)&&(rp>lp)) {
711 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
714 /* Prefer brokketized names */
717 for (i=0; i<strlen(mailfrom); ++i) {
718 if (mailfrom[i] == '<') lp = i;
719 if (mailfrom[i] == '>') rp = i;
721 if ( (lp>=0) && (rp>lp) ) {
723 strcpy(mailfrom, &mailfrom[lp]);
728 } while (scan_done == 0);
729 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
731 /* Figure out what mail exchanger host we have to connect to */
732 num_mxhosts = getmx(mxhosts, node);
733 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
734 if (num_mxhosts < 1) {
736 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
740 for (mx=0; mx<num_mxhosts; ++mx) {
741 extract(buf, mxhosts, mx);
742 lprintf(9, "Trying <%s>\n", buf);
743 sock = sock_connect(buf, "25", "tcp");
744 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
745 if (sock >= 0) lprintf(9, "Connected!\n");
746 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
747 if (sock >= 0) break;
751 *status = 4; /* dsn is already filled in */
755 /* Process the SMTP greeting from the server */
756 if (ml_sock_gets(sock, buf) < 0) {
758 strcpy(dsn, "Connection broken during SMTP conversation");
761 lprintf(9, "<%s\n", buf);
765 safestrncpy(dsn, &buf[4], 1023);
770 safestrncpy(dsn, &buf[4], 1023);
775 /* At this point we know we are talking to a real SMTP server */
777 /* Do a HELO command */
778 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
779 lprintf(9, ">%s", buf);
780 sock_write(sock, buf, strlen(buf));
781 if (ml_sock_gets(sock, buf) < 0) {
783 strcpy(dsn, "Connection broken during SMTP HELO");
786 lprintf(9, "<%s\n", buf);
790 safestrncpy(dsn, &buf[4], 1023);
795 safestrncpy(dsn, &buf[4], 1023);
801 /* HELO succeeded, now try the MAIL From: command */
802 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
803 lprintf(9, ">%s", buf);
804 sock_write(sock, buf, strlen(buf));
805 if (ml_sock_gets(sock, buf) < 0) {
807 strcpy(dsn, "Connection broken during SMTP MAIL");
810 lprintf(9, "<%s\n", buf);
814 safestrncpy(dsn, &buf[4], 1023);
819 safestrncpy(dsn, &buf[4], 1023);
825 /* MAIL succeeded, now try the RCPT To: command */
826 snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
827 lprintf(9, ">%s", buf);
828 sock_write(sock, buf, strlen(buf));
829 if (ml_sock_gets(sock, buf) < 0) {
831 strcpy(dsn, "Connection broken during SMTP RCPT");
834 lprintf(9, "<%s\n", buf);
838 safestrncpy(dsn, &buf[4], 1023);
843 safestrncpy(dsn, &buf[4], 1023);
849 /* RCPT succeeded, now try the DATA command */
850 lprintf(9, ">DATA\n");
851 sock_write(sock, "DATA\r\n", 6);
852 if (ml_sock_gets(sock, buf) < 0) {
854 strcpy(dsn, "Connection broken during SMTP DATA");
857 lprintf(9, "<%s\n", buf);
861 safestrncpy(dsn, &buf[4], 1023);
866 safestrncpy(dsn, &buf[4], 1023);
871 /* If we reach this point, the server is expecting data */
873 while (msg_size > 0) {
874 blocksize = sizeof(buf);
875 if (blocksize > msg_size) blocksize = msg_size;
876 fread(buf, blocksize, 1, msg_fp);
877 sock_write(sock, buf, blocksize);
878 msg_size -= blocksize;
880 if (buf[blocksize-1] != 10) {
881 lprintf(5, "Possible problem: message did not correctly "
882 "terminate. (expecting 0x10, got 0x%02x)\n",
886 sock_write(sock, ".\r\n", 3);
887 if (ml_sock_gets(sock, buf) < 0) {
889 strcpy(dsn, "Connection broken during SMTP message transmit");
892 lprintf(9, "%s\n", buf);
896 safestrncpy(dsn, &buf[4], 1023);
901 safestrncpy(dsn, &buf[4], 1023);
907 safestrncpy(dsn, &buf[4], 1023);
910 lprintf(9, ">QUIT\n");
911 sock_write(sock, "QUIT\r\n", 6);
912 ml_sock_gets(sock, buf);
913 lprintf(9, "<%s\n", buf);
915 bail: if (msg_fp != NULL) fclose(msg_fp);
923 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
924 * instructions for "5" codes (permanent fatal errors) and produce/deliver
925 * a "bounce" message (delivery status notification).
927 void smtp_do_bounce(char *instr) {
938 long bounce_msgid = (-1);
939 time_t submitted = 0L;
940 struct CtdlMessage *bmsg = NULL;
942 struct recptypes *valid;
943 int successful_bounce = 0;
945 lprintf(9, "smtp_do_bounce() called\n");
946 strcpy(bounceto, "");
948 lines = num_tokens(instr, '\n');
951 /* See if it's time to give up on delivery of this message */
952 for (i=0; i<lines; ++i) {
953 extract_token(buf, instr, i, '\n');
954 extract(key, buf, 0);
955 extract(addr, buf, 1);
956 if (!strcasecmp(key, "submitted")) {
957 submitted = atol(addr);
961 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
967 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
968 if (bmsg == NULL) return;
969 memset(bmsg, 0, sizeof(struct CtdlMessage));
971 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
972 bmsg->cm_anon_type = MES_NORMAL;
973 bmsg->cm_format_type = 1;
974 bmsg->cm_fields['A'] = strdoop("Citadel");
975 bmsg->cm_fields['O'] = strdoop(MAILROOM);
976 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
978 if (give_up) bmsg->cm_fields['M'] = strdoop(
979 "A message you sent could not be delivered to some or all of its recipients\n"
980 "due to prolonged unavailability of its destination(s).\n"
981 "Giving up on the following addresses:\n\n"
984 else bmsg->cm_fields['M'] = strdoop(
985 "A message you sent could not be delivered to some or all of its recipients.\n"
986 "The following addresses were undeliverable:\n\n"
990 * Now go through the instructions checking for stuff.
993 for (i=0; i<lines; ++i) {
994 extract_token(buf, instr, i, '\n');
995 extract(key, buf, 0);
996 extract(addr, buf, 1);
997 status = extract_int(buf, 2);
998 extract(dsn, buf, 3);
1001 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1002 key, addr, status, dsn);
1004 if (!strcasecmp(key, "bounceto")) {
1005 strcpy(bounceto, addr);
1009 (!strcasecmp(key, "local"))
1010 || (!strcasecmp(key, "remote"))
1011 || (!strcasecmp(key, "ignet"))
1012 || (!strcasecmp(key, "room"))
1014 if (status == 5) bounce_this = 1;
1015 if (give_up) bounce_this = 1;
1021 if (bmsg->cm_fields['M'] == NULL) {
1022 lprintf(2, "ERROR ... M field is null "
1023 "(%s:%d)\n", __FILE__, __LINE__);
1026 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1027 strlen(bmsg->cm_fields['M']) + 1024 );
1028 strcat(bmsg->cm_fields['M'], addr);
1029 strcat(bmsg->cm_fields['M'], ": ");
1030 strcat(bmsg->cm_fields['M'], dsn);
1031 strcat(bmsg->cm_fields['M'], "\n");
1033 remove_token(instr, i, '\n');
1039 /* Deliver the bounce if there's anything worth mentioning */
1040 lprintf(9, "num_bounces = %d\n", num_bounces);
1041 if (num_bounces > 0) {
1043 /* First try the user who sent the message */
1044 lprintf(9, "bounce to user? <%s>\n", bounceto);
1046 if (strlen(bounceto) == 0) {
1047 lprintf(7, "No bounce address specified\n");
1048 bounce_msgid = (-1L);
1051 /* Can we deliver the bounce to the original sender? */
1052 valid = validate_recipients(bounceto);
1053 if (valid != NULL) {
1054 if (valid->num_error == 0) {
1055 CtdlSubmitMsg(bmsg, valid, "");
1056 successful_bounce = 1;
1060 /* If not, post it in the Aide> room */
1061 if (successful_bounce == 0) {
1062 CtdlSubmitMsg(bmsg, NULL, AIDEROOM);
1065 /* Free up the memory we used */
1066 if (valid != NULL) {
1071 CtdlFreeMessage(bmsg);
1072 lprintf(9, "Done processing bounces\n");
1077 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1078 * set of delivery instructions for completed deliveries and remove them.
1080 * It returns the number of incomplete deliveries remaining.
1082 int smtp_purge_completed_deliveries(char *instr) {
1093 lines = num_tokens(instr, '\n');
1094 for (i=0; i<lines; ++i) {
1095 extract_token(buf, instr, i, '\n');
1096 extract(key, buf, 0);
1097 extract(addr, buf, 1);
1098 status = extract_int(buf, 2);
1099 extract(dsn, buf, 3);
1104 (!strcasecmp(key, "local"))
1105 || (!strcasecmp(key, "remote"))
1106 || (!strcasecmp(key, "ignet"))
1107 || (!strcasecmp(key, "room"))
1109 if (status == 2) completed = 1;
1114 remove_token(instr, i, '\n');
1127 * Called by smtp_do_queue() to handle an individual message.
1129 void smtp_do_procmsg(long msgnum, void *userdata) {
1130 struct CtdlMessage *msg;
1132 char *results = NULL;
1140 long text_msgid = (-1);
1141 int incomplete_deliveries_remaining;
1142 time_t attempted = 0L;
1143 time_t last_attempted = 0L;
1144 time_t retry = SMTP_RETRY_INTERVAL;
1146 lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1148 msg = CtdlFetchMessage(msgnum);
1150 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1154 instr = strdoop(msg->cm_fields['M']);
1155 CtdlFreeMessage(msg);
1157 /* Strip out the headers amd any other non-instruction line */
1158 lines = num_tokens(instr, '\n');
1159 for (i=0; i<lines; ++i) {
1160 extract_token(buf, instr, i, '\n');
1161 if (num_tokens(buf, '|') < 2) {
1162 remove_token(instr, i, '\n');
1168 /* Learn the message ID and find out about recent delivery attempts */
1169 lines = num_tokens(instr, '\n');
1170 for (i=0; i<lines; ++i) {
1171 extract_token(buf, instr, i, '\n');
1172 extract(key, buf, 0);
1173 if (!strcasecmp(key, "msgid")) {
1174 text_msgid = extract_long(buf, 1);
1176 if (!strcasecmp(key, "retry")) {
1177 /* double the retry interval after each attempt */
1178 retry = extract_long(buf, 1) * 2L;
1179 if (retry > SMTP_RETRY_MAX) {
1180 retry = SMTP_RETRY_MAX;
1182 remove_token(instr, i, '\n');
1184 if (!strcasecmp(key, "attempted")) {
1185 attempted = extract_long(buf, 1);
1186 if (attempted > last_attempted)
1187 last_attempted = attempted;
1192 * Postpone delivery if we've already tried recently.
1194 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1195 lprintf(7, "Retry time not yet reached.\n");
1202 * Bail out if there's no actual message associated with this
1204 if (text_msgid < 0L) {
1205 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1210 /* Plow through the instructions looking for 'remote' directives and
1211 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1212 * were experienced and it's time to try again)
1214 lines = num_tokens(instr, '\n');
1215 for (i=0; i<lines; ++i) {
1216 extract_token(buf, instr, i, '\n');
1217 extract(key, buf, 0);
1218 extract(addr, buf, 1);
1219 status = extract_int(buf, 2);
1220 extract(dsn, buf, 3);
1221 if ( (!strcasecmp(key, "remote"))
1222 && ((status==0)||(status==3)||(status==4)) ) {
1223 remove_token(instr, i, '\n');
1226 lprintf(9, "SMTP: Trying <%s>\n", addr);
1227 smtp_try(key, addr, &status, dsn, text_msgid);
1229 if (results == NULL) {
1230 results = mallok(1024);
1231 memset(results, 0, 1024);
1234 results = reallok(results,
1235 strlen(results) + 1024);
1237 sprintf(&results[strlen(results)],
1239 key, addr, status, dsn);
1244 if (results != NULL) {
1245 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1246 strcat(instr, results);
1251 /* Generate 'bounce' messages */
1252 smtp_do_bounce(instr);
1254 /* Go through the delivery list, deleting completed deliveries */
1255 incomplete_deliveries_remaining =
1256 smtp_purge_completed_deliveries(instr);
1260 * No delivery instructions remain, so delete both the instructions
1261 * message and the message message.
1263 if (incomplete_deliveries_remaining <= 0) {
1264 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1265 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1270 * Uncompleted delivery instructions remain, so delete the old
1271 * instructions and replace with the updated ones.
1273 if (incomplete_deliveries_remaining > 0) {
1274 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1275 msg = mallok(sizeof(struct CtdlMessage));
1276 memset(msg, 0, sizeof(struct CtdlMessage));
1277 msg->cm_magic = CTDLMESSAGE_MAGIC;
1278 msg->cm_anon_type = MES_NORMAL;
1279 msg->cm_format_type = FMT_RFC822;
1280 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1281 snprintf(msg->cm_fields['M'],
1283 "Content-type: %s\n\n%s\n"
1286 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1288 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1289 CtdlFreeMessage(msg);
1299 * Run through the queue sending out messages.
1301 void smtp_do_queue(void) {
1302 static int doing_queue = 0;
1305 * This is a simple concurrency check to make sure only one queue run
1306 * is done at a time. We could do this with a mutex, but since we
1307 * don't really require extremely fine granularity here, we'll do it
1308 * with a static variable instead.
1310 if (doing_queue) return;
1314 * Go ahead and run the queue
1316 lprintf(7, "SMTP: processing outbound queue\n");
1318 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1319 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1322 CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1323 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1325 lprintf(7, "SMTP: queue run completed\n");
1332 /*****************************************************************************/
1333 /* SMTP UTILITY COMMANDS */
1334 /*****************************************************************************/
1336 void cmd_smtp(char *argbuf) {
1343 if (CtdlAccessCheck(ac_aide)) return;
1345 extract(cmd, argbuf, 0);
1347 if (!strcasecmp(cmd, "mx")) {
1348 extract(node, argbuf, 1);
1349 num_mxhosts = getmx(buf, node);
1350 cprintf("%d %d MX hosts listed for %s\n",
1351 LISTING_FOLLOWS, num_mxhosts, node);
1352 for (i=0; i<num_mxhosts; ++i) {
1353 extract(node, buf, i);
1354 cprintf("%s\n", node);
1360 else if (!strcasecmp(cmd, "runqueue")) {
1362 cprintf("%d All outbound SMTP will be retried now.\n", OK);
1367 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1375 /*****************************************************************************/
1376 /* MODULE INITIALIZATION STUFF */
1377 /*****************************************************************************/
1380 char *Dynamic_Module_Init(void)
1382 SYM_SMTP = CtdlGetDynamicSymbol();
1384 CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
1389 CtdlRegisterServiceHook(0, /* ...and locally */
1394 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1395 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1396 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");