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 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 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) {
404 int bypass_spam_checking = 0;
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 /* Allow relaying if it's from the Internet to another Citadel node
421 * for whom we are providing directory service.
423 bypass_spam_checking = IsDirectory(recp);
426 cvt = convert_internet_address(user, node, recp);
427 snprintf(recp, sizeof recp, "%s@%s", user, node);
428 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
430 /* FIXME possible buffer overflow type of issues here. */
432 case rfc822_address_locally_validated:
433 cprintf("250 RCPT ok\r\n");
434 if (SMTP->valid.num_local > 0) {
435 strcat(SMTP->valid.recp_local, "|");
437 strcat(SMTP->valid.recp_local, user);
438 SMTP->valid.num_local += 1;
439 SMTP->number_of_recipients += 1;
442 case rfc822_room_delivery:
443 cprintf("250 RCPT ok\r\n");
444 if (SMTP->valid.num_room > 0) {
445 strcat(SMTP->valid.recp_room, "|");
447 strcat(SMTP->valid.recp_room, user);
448 SMTP->valid.num_room += 1;
449 SMTP->number_of_recipients += 1;
452 case rfc822_address_on_citadel_network:
453 cprintf("250 RCPT ok\r\n");
454 if (SMTP->valid.num_ignet > 0) {
455 strcat(SMTP->valid.recp_ignet, "|");
457 strcat(SMTP->valid.recp_ignet, user);
458 SMTP->valid.num_ignet += 1;
459 SMTP->number_of_recipients += 1;
462 case rfc822_no_such_user:
463 cprintf("550 %s: no such user\r\n", recp);
466 case rfc822_address_nonlocal:
467 if ( (SMTP->message_originated_locally == 0)
468 && (bypass_spam_checking == 0) ) {
469 cprintf("551 Relaying denied.\r\n");
472 cprintf("250 RCPT ok\r\n");
474 if (SMTP->valid.num_internet > 0) {
475 strcat(SMTP->valid.recp_internet, "|");
477 strcat(SMTP->valid.recp_internet, user);
478 SMTP->valid.num_internet += 1;
479 SMTP->number_of_recipients += 1;
485 cprintf("599 Unknown error\r\n");
492 * Implements the DATA command
494 void smtp_data(void) {
496 struct CtdlMessage *msg;
500 if (strlen(SMTP->from) == 0) {
501 cprintf("503 Need MAIL command first.\r\n");
505 if (SMTP->number_of_recipients < 1) {
506 cprintf("503 Need RCPT command first.\r\n");
510 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
512 datestring(nowstamp, time(NULL), DATESTRING_RFC822);
515 if (body != NULL) snprintf(body, 4096,
516 "Received: from %s\n"
523 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
525 cprintf("550 Unable to save message: internal error.\r\n");
529 lprintf(9, "Converting message...\n");
530 msg = convert_internet_message(body);
532 /* If the user is locally authenticated, FORCE the From: header to
533 * show up as the real sender. Yes, this violates the RFC standard,
534 * but IT MAKES SENSE. Comment it out if you don't like this behavior.
537 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
538 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
539 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
540 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
541 msg->cm_fields['N'] = strdoop(config.c_nodename);
542 msg->cm_fields['H'] = strdoop(config.c_humannode);
545 /* Submit the message into the Citadel system. */
546 msgnum = CtdlSubmitMsg(msg, &SMTP->valid, "");
547 CtdlFreeMessage(msg);
550 cprintf("250 Message accepted.\r\n");
553 cprintf("550 Internal delivery error\r\n");
556 smtp_data_clear(); /* clear out the buffers now */
563 * Main command loop for SMTP sessions.
565 void smtp_command_loop(void) {
569 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
570 if (client_gets(cmdbuf) < 1) {
571 lprintf(3, "SMTP socket is broken. Ending session.\n");
575 lprintf(5, "SMTP: %s\n", cmdbuf);
576 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
578 if (SMTP->command_state == smtp_user) {
579 smtp_get_user(cmdbuf);
582 else if (SMTP->command_state == smtp_password) {
583 smtp_get_pass(cmdbuf);
586 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
587 smtp_auth(&cmdbuf[5]);
590 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
594 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
595 smtp_hello(&cmdbuf[5], 1);
598 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
599 smtp_expn(&cmdbuf[5]);
602 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
603 smtp_hello(&cmdbuf[5], 0);
606 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
610 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
611 smtp_mail(&cmdbuf[5]);
614 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
615 cprintf("250 NOOP\r\n");
618 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
619 cprintf("221 Goodbye...\r\n");
624 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
625 smtp_rcpt(&cmdbuf[5]);
628 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
632 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
633 smtp_vrfy(&cmdbuf[5]);
637 cprintf("502 I'm afraid I can't do that.\r\n");
645 /*****************************************************************************/
646 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
647 /*****************************************************************************/
654 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
657 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
664 char user[SIZ], node[SIZ], name[SIZ];
670 size_t blocksize = 0;
673 /* Parse out the host portion of the recipient address */
674 process_rfc822_addr(addr, user, node, name);
676 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
679 /* Load the message out of the database into a temp file */
681 if (msg_fp == NULL) {
683 sprintf(dsn, "Error creating temporary file");
687 CtdlRedirectOutput(msg_fp, -1);
688 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
689 CtdlRedirectOutput(NULL, -1);
690 fseek(msg_fp, 0L, SEEK_END);
691 msg_size = ftell(msg_fp);
695 /* Extract something to send later in the 'MAIL From:' command */
696 strcpy(mailfrom, "");
700 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
701 if (!strncasecmp(buf, "From:", 5)) {
702 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
704 for (i=0; i<strlen(mailfrom); ++i) {
705 if (!isprint(mailfrom[i])) {
706 strcpy(&mailfrom[i], &mailfrom[i+1]);
711 /* Strip out parenthesized names */
714 for (i=0; i<strlen(mailfrom); ++i) {
715 if (mailfrom[i] == '(') lp = i;
716 if (mailfrom[i] == ')') rp = i;
718 if ((lp>0)&&(rp>lp)) {
719 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
722 /* Prefer brokketized names */
725 for (i=0; i<strlen(mailfrom); ++i) {
726 if (mailfrom[i] == '<') lp = i;
727 if (mailfrom[i] == '>') rp = i;
729 if ( (lp>=0) && (rp>lp) ) {
731 strcpy(mailfrom, &mailfrom[lp]);
736 } while (scan_done == 0);
737 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
739 /* Figure out what mail exchanger host we have to connect to */
740 num_mxhosts = getmx(mxhosts, node);
741 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
742 if (num_mxhosts < 1) {
744 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
748 for (mx=0; mx<num_mxhosts; ++mx) {
749 extract(buf, mxhosts, mx);
750 lprintf(9, "Trying <%s>\n", buf);
751 sock = sock_connect(buf, "25", "tcp");
752 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
753 if (sock >= 0) lprintf(9, "Connected!\n");
754 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
755 if (sock >= 0) break;
759 *status = 4; /* dsn is already filled in */
763 /* Process the SMTP greeting from the server */
764 if (ml_sock_gets(sock, buf) < 0) {
766 strcpy(dsn, "Connection broken during SMTP conversation");
769 lprintf(9, "<%s\n", buf);
773 safestrncpy(dsn, &buf[4], 1023);
778 safestrncpy(dsn, &buf[4], 1023);
783 /* At this point we know we are talking to a real SMTP server */
785 /* Do a HELO command */
786 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
787 lprintf(9, ">%s", buf);
788 sock_write(sock, buf, strlen(buf));
789 if (ml_sock_gets(sock, buf) < 0) {
791 strcpy(dsn, "Connection broken during SMTP HELO");
794 lprintf(9, "<%s\n", buf);
798 safestrncpy(dsn, &buf[4], 1023);
803 safestrncpy(dsn, &buf[4], 1023);
809 /* HELO succeeded, now try the MAIL From: command */
810 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
811 lprintf(9, ">%s", buf);
812 sock_write(sock, buf, strlen(buf));
813 if (ml_sock_gets(sock, buf) < 0) {
815 strcpy(dsn, "Connection broken during SMTP MAIL");
818 lprintf(9, "<%s\n", buf);
822 safestrncpy(dsn, &buf[4], 1023);
827 safestrncpy(dsn, &buf[4], 1023);
833 /* MAIL succeeded, now try the RCPT To: command */
834 snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
835 lprintf(9, ">%s", buf);
836 sock_write(sock, buf, strlen(buf));
837 if (ml_sock_gets(sock, buf) < 0) {
839 strcpy(dsn, "Connection broken during SMTP RCPT");
842 lprintf(9, "<%s\n", buf);
846 safestrncpy(dsn, &buf[4], 1023);
851 safestrncpy(dsn, &buf[4], 1023);
857 /* RCPT succeeded, now try the DATA command */
858 lprintf(9, ">DATA\n");
859 sock_write(sock, "DATA\r\n", 6);
860 if (ml_sock_gets(sock, buf) < 0) {
862 strcpy(dsn, "Connection broken during SMTP DATA");
865 lprintf(9, "<%s\n", buf);
869 safestrncpy(dsn, &buf[4], 1023);
874 safestrncpy(dsn, &buf[4], 1023);
879 /* If we reach this point, the server is expecting data */
881 while (msg_size > 0) {
882 blocksize = sizeof(buf);
883 if (blocksize > msg_size) blocksize = msg_size;
884 fread(buf, blocksize, 1, msg_fp);
885 sock_write(sock, buf, blocksize);
886 msg_size -= blocksize;
888 if (buf[blocksize-1] != 10) {
889 lprintf(5, "Possible problem: message did not correctly "
890 "terminate. (expecting 0x10, got 0x%02x)\n",
894 sock_write(sock, ".\r\n", 3);
895 if (ml_sock_gets(sock, buf) < 0) {
897 strcpy(dsn, "Connection broken during SMTP message transmit");
900 lprintf(9, "%s\n", buf);
904 safestrncpy(dsn, &buf[4], 1023);
909 safestrncpy(dsn, &buf[4], 1023);
915 safestrncpy(dsn, &buf[4], 1023);
918 lprintf(9, ">QUIT\n");
919 sock_write(sock, "QUIT\r\n", 6);
920 ml_sock_gets(sock, buf);
921 lprintf(9, "<%s\n", buf);
923 bail: if (msg_fp != NULL) fclose(msg_fp);
931 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
932 * instructions for "5" codes (permanent fatal errors) and produce/deliver
933 * a "bounce" message (delivery status notification).
935 void smtp_do_bounce(char *instr) {
946 long bounce_msgid = (-1);
947 time_t submitted = 0L;
948 struct CtdlMessage *bmsg = NULL;
950 struct recptypes *valid;
951 int successful_bounce = 0;
953 lprintf(9, "smtp_do_bounce() called\n");
954 strcpy(bounceto, "");
956 lines = num_tokens(instr, '\n');
959 /* See if it's time to give up on delivery of this message */
960 for (i=0; i<lines; ++i) {
961 extract_token(buf, instr, i, '\n');
962 extract(key, buf, 0);
963 extract(addr, buf, 1);
964 if (!strcasecmp(key, "submitted")) {
965 submitted = atol(addr);
969 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
975 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
976 if (bmsg == NULL) return;
977 memset(bmsg, 0, sizeof(struct CtdlMessage));
979 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
980 bmsg->cm_anon_type = MES_NORMAL;
981 bmsg->cm_format_type = 1;
982 bmsg->cm_fields['A'] = strdoop("Citadel");
983 bmsg->cm_fields['O'] = strdoop(MAILROOM);
984 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
986 if (give_up) bmsg->cm_fields['M'] = strdoop(
987 "A message you sent could not be delivered to some or all of its recipients\n"
988 "due to prolonged unavailability of its destination(s).\n"
989 "Giving up on the following addresses:\n\n"
992 else bmsg->cm_fields['M'] = strdoop(
993 "A message you sent could not be delivered to some or all of its recipients.\n"
994 "The following addresses were undeliverable:\n\n"
998 * Now go through the instructions checking for stuff.
1001 for (i=0; i<lines; ++i) {
1002 extract_token(buf, instr, i, '\n');
1003 extract(key, buf, 0);
1004 extract(addr, buf, 1);
1005 status = extract_int(buf, 2);
1006 extract(dsn, buf, 3);
1009 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1010 key, addr, status, dsn);
1012 if (!strcasecmp(key, "bounceto")) {
1013 strcpy(bounceto, addr);
1017 (!strcasecmp(key, "local"))
1018 || (!strcasecmp(key, "remote"))
1019 || (!strcasecmp(key, "ignet"))
1020 || (!strcasecmp(key, "room"))
1022 if (status == 5) bounce_this = 1;
1023 if (give_up) bounce_this = 1;
1029 if (bmsg->cm_fields['M'] == NULL) {
1030 lprintf(2, "ERROR ... M field is null "
1031 "(%s:%d)\n", __FILE__, __LINE__);
1034 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1035 strlen(bmsg->cm_fields['M']) + 1024 );
1036 strcat(bmsg->cm_fields['M'], addr);
1037 strcat(bmsg->cm_fields['M'], ": ");
1038 strcat(bmsg->cm_fields['M'], dsn);
1039 strcat(bmsg->cm_fields['M'], "\n");
1041 remove_token(instr, i, '\n');
1047 /* Deliver the bounce if there's anything worth mentioning */
1048 lprintf(9, "num_bounces = %d\n", num_bounces);
1049 if (num_bounces > 0) {
1051 /* First try the user who sent the message */
1052 lprintf(9, "bounce to user? <%s>\n", bounceto);
1053 if (strlen(bounceto) == 0) {
1054 lprintf(7, "No bounce address specified\n");
1055 bounce_msgid = (-1L);
1058 /* Can we deliver the bounce to the original sender? */
1059 valid = validate_recipients(bounceto);
1060 if (valid != NULL) {
1061 if (valid->num_error == 0) {
1062 CtdlSubmitMsg(bmsg, valid, "");
1063 successful_bounce = 1;
1067 /* If not, post it in the Aide> room */
1068 if (successful_bounce == 0) {
1069 CtdlSubmitMsg(bmsg, NULL, AIDEROOM);
1072 /* Free up the memory we used */
1073 if (valid != NULL) {
1078 CtdlFreeMessage(bmsg);
1079 lprintf(9, "Done processing bounces\n");
1084 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1085 * set of delivery instructions for completed deliveries and remove them.
1087 * It returns the number of incomplete deliveries remaining.
1089 int smtp_purge_completed_deliveries(char *instr) {
1100 lines = num_tokens(instr, '\n');
1101 for (i=0; i<lines; ++i) {
1102 extract_token(buf, instr, i, '\n');
1103 extract(key, buf, 0);
1104 extract(addr, buf, 1);
1105 status = extract_int(buf, 2);
1106 extract(dsn, buf, 3);
1111 (!strcasecmp(key, "local"))
1112 || (!strcasecmp(key, "remote"))
1113 || (!strcasecmp(key, "ignet"))
1114 || (!strcasecmp(key, "room"))
1116 if (status == 2) completed = 1;
1121 remove_token(instr, i, '\n');
1134 * Called by smtp_do_queue() to handle an individual message.
1136 void smtp_do_procmsg(long msgnum, void *userdata) {
1137 struct CtdlMessage *msg;
1139 char *results = NULL;
1147 long text_msgid = (-1);
1148 int incomplete_deliveries_remaining;
1149 time_t attempted = 0L;
1150 time_t last_attempted = 0L;
1151 time_t retry = SMTP_RETRY_INTERVAL;
1153 lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1155 msg = CtdlFetchMessage(msgnum);
1157 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1161 instr = strdoop(msg->cm_fields['M']);
1162 CtdlFreeMessage(msg);
1164 /* Strip out the headers amd any other non-instruction line */
1165 lines = num_tokens(instr, '\n');
1166 for (i=0; i<lines; ++i) {
1167 extract_token(buf, instr, i, '\n');
1168 if (num_tokens(buf, '|') < 2) {
1169 remove_token(instr, i, '\n');
1175 /* Learn the message ID and find out about recent delivery attempts */
1176 lines = num_tokens(instr, '\n');
1177 for (i=0; i<lines; ++i) {
1178 extract_token(buf, instr, i, '\n');
1179 extract(key, buf, 0);
1180 if (!strcasecmp(key, "msgid")) {
1181 text_msgid = extract_long(buf, 1);
1183 if (!strcasecmp(key, "retry")) {
1184 /* double the retry interval after each attempt */
1185 retry = extract_long(buf, 1) * 2L;
1186 if (retry > SMTP_RETRY_MAX) {
1187 retry = SMTP_RETRY_MAX;
1189 remove_token(instr, i, '\n');
1191 if (!strcasecmp(key, "attempted")) {
1192 attempted = extract_long(buf, 1);
1193 if (attempted > last_attempted)
1194 last_attempted = attempted;
1199 * Postpone delivery if we've already tried recently.
1201 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1202 lprintf(7, "Retry time not yet reached.\n");
1209 * Bail out if there's no actual message associated with this
1211 if (text_msgid < 0L) {
1212 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1217 /* Plow through the instructions looking for 'remote' directives and
1218 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1219 * were experienced and it's time to try again)
1221 lines = num_tokens(instr, '\n');
1222 for (i=0; i<lines; ++i) {
1223 extract_token(buf, instr, i, '\n');
1224 extract(key, buf, 0);
1225 extract(addr, buf, 1);
1226 status = extract_int(buf, 2);
1227 extract(dsn, buf, 3);
1228 if ( (!strcasecmp(key, "remote"))
1229 && ((status==0)||(status==3)||(status==4)) ) {
1230 remove_token(instr, i, '\n');
1233 lprintf(9, "SMTP: Trying <%s>\n", addr);
1234 smtp_try(key, addr, &status, dsn, text_msgid);
1236 if (results == NULL) {
1237 results = mallok(1024);
1238 memset(results, 0, 1024);
1241 results = reallok(results,
1242 strlen(results) + 1024);
1244 sprintf(&results[strlen(results)],
1246 key, addr, status, dsn);
1251 if (results != NULL) {
1252 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1253 strcat(instr, results);
1258 /* Generate 'bounce' messages */
1259 smtp_do_bounce(instr);
1261 /* Go through the delivery list, deleting completed deliveries */
1262 incomplete_deliveries_remaining =
1263 smtp_purge_completed_deliveries(instr);
1267 * No delivery instructions remain, so delete both the instructions
1268 * message and the message message.
1270 if (incomplete_deliveries_remaining <= 0) {
1271 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1272 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1277 * Uncompleted delivery instructions remain, so delete the old
1278 * instructions and replace with the updated ones.
1280 if (incomplete_deliveries_remaining > 0) {
1281 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1282 msg = mallok(sizeof(struct CtdlMessage));
1283 memset(msg, 0, sizeof(struct CtdlMessage));
1284 msg->cm_magic = CTDLMESSAGE_MAGIC;
1285 msg->cm_anon_type = MES_NORMAL;
1286 msg->cm_format_type = FMT_RFC822;
1287 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1288 snprintf(msg->cm_fields['M'],
1290 "Content-type: %s\n\n%s\n"
1293 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1295 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1296 CtdlFreeMessage(msg);
1306 * Run through the queue sending out messages.
1308 void smtp_do_queue(void) {
1309 static int doing_queue = 0;
1312 * This is a simple concurrency check to make sure only one queue run
1313 * is done at a time. We could do this with a mutex, but since we
1314 * don't really require extremely fine granularity here, we'll do it
1315 * with a static variable instead.
1317 if (doing_queue) return;
1321 * Go ahead and run the queue
1323 lprintf(7, "SMTP: processing outbound queue\n");
1325 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1326 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1329 CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1330 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1332 lprintf(7, "SMTP: queue run completed\n");
1339 /*****************************************************************************/
1340 /* SMTP UTILITY COMMANDS */
1341 /*****************************************************************************/
1343 void cmd_smtp(char *argbuf) {
1350 if (CtdlAccessCheck(ac_aide)) return;
1352 extract(cmd, argbuf, 0);
1354 if (!strcasecmp(cmd, "mx")) {
1355 extract(node, argbuf, 1);
1356 num_mxhosts = getmx(buf, node);
1357 cprintf("%d %d MX hosts listed for %s\n",
1358 LISTING_FOLLOWS, num_mxhosts, node);
1359 for (i=0; i<num_mxhosts; ++i) {
1360 extract(node, buf, i);
1361 cprintf("%s\n", node);
1367 else if (!strcasecmp(cmd, "runqueue")) {
1369 cprintf("%d All outbound SMTP will be retried now.\n", OK);
1374 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1382 /*****************************************************************************/
1383 /* MODULE INITIALIZATION STUFF */
1384 /*****************************************************************************/
1387 char *Dynamic_Module_Init(void)
1389 SYM_SMTP = CtdlGetDynamicSymbol();
1391 CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
1396 CtdlRegisterServiceHook(0, /* ...and locally */
1401 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1402 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1403 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");