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) {
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 alias_type = alias(recp);
421 cvt = convert_internet_address(user, node, recp);
422 if (alias_type == MES_IGNET) {
423 cvt = rfc822_address_on_citadel_network;
425 snprintf(recp, sizeof recp, "%s@%s", user, node);
426 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
428 /* FIXME possible buffer overflow type of issues here. */
430 case rfc822_address_locally_validated:
431 cprintf("250 RCPT ok local\r\n");
432 if (SMTP->valid.num_local > 0) {
433 strcat(SMTP->valid.recp_local, "|");
435 strcat(SMTP->valid.recp_local, user);
436 SMTP->valid.num_local += 1;
437 SMTP->number_of_recipients += 1;
440 case rfc822_room_delivery:
441 cprintf("250 RCPT ok room\r\n");
442 if (SMTP->valid.num_room > 0) {
443 strcat(SMTP->valid.recp_room, "|");
445 strcat(SMTP->valid.recp_room, user);
446 SMTP->valid.num_room += 1;
447 SMTP->number_of_recipients += 1;
450 case rfc822_address_on_citadel_network:
451 cprintf("250 RCPT ok ignet <%s>\r\n", recp);
452 if (SMTP->valid.num_ignet > 0) {
453 strcat(SMTP->valid.recp_ignet, "|");
455 strcat(SMTP->valid.recp_ignet, user);
456 SMTP->valid.num_ignet += 1;
457 SMTP->number_of_recipients += 1;
460 case rfc822_no_such_user:
461 cprintf("550 %s: no such user\r\n", recp);
464 case rfc822_address_nonlocal:
465 if (SMTP->message_originated_locally == 0) {
466 cprintf("551 Relaying denied <%s>\r\n", recp);
469 cprintf("250 RCPT ok <%s>\r\n", recp);
471 if (SMTP->valid.num_internet > 0) {
472 strcat(SMTP->valid.recp_internet, "|");
474 strcat(SMTP->valid.recp_internet, user);
475 SMTP->valid.num_internet += 1;
476 SMTP->number_of_recipients += 1;
482 cprintf("599 Unknown error\r\n");
489 * Implements the DATA command
491 void smtp_data(void) {
493 struct CtdlMessage *msg;
497 if (strlen(SMTP->from) == 0) {
498 cprintf("503 Need MAIL command first.\r\n");
502 if (SMTP->number_of_recipients < 1) {
503 cprintf("503 Need RCPT command first.\r\n");
507 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
509 datestring(nowstamp, time(NULL), DATESTRING_RFC822);
512 if (body != NULL) snprintf(body, 4096,
513 "Received: from %s\n"
520 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
522 cprintf("550 Unable to save message: internal error.\r\n");
526 lprintf(9, "Converting message...\n");
527 msg = convert_internet_message(body);
529 /* If the user is locally authenticated, FORCE the From: header to
530 * show up as the real sender. Yes, this violates the RFC standard,
531 * but IT MAKES SENSE. Comment it out if you don't like this behavior.
534 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
535 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
536 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
537 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
538 msg->cm_fields['N'] = strdoop(config.c_nodename);
539 msg->cm_fields['H'] = strdoop(config.c_humannode);
542 /* Submit the message into the Citadel system. */
543 msgnum = CtdlSubmitMsg(msg, &SMTP->valid, "");
544 CtdlFreeMessage(msg);
547 cprintf("250 Message accepted.\r\n");
550 cprintf("550 Internal delivery error\r\n");
553 smtp_data_clear(); /* clear out the buffers now */
560 * Main command loop for SMTP sessions.
562 void smtp_command_loop(void) {
566 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
567 if (client_gets(cmdbuf) < 1) {
568 lprintf(3, "SMTP socket is broken. Ending session.\n");
572 lprintf(5, "SMTP: %s\n", cmdbuf);
573 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
575 if (SMTP->command_state == smtp_user) {
576 smtp_get_user(cmdbuf);
579 else if (SMTP->command_state == smtp_password) {
580 smtp_get_pass(cmdbuf);
583 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
584 smtp_auth(&cmdbuf[5]);
587 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
591 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
592 smtp_hello(&cmdbuf[5], 1);
595 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
596 smtp_expn(&cmdbuf[5]);
599 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
600 smtp_hello(&cmdbuf[5], 0);
603 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
607 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
608 smtp_mail(&cmdbuf[5]);
611 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
612 cprintf("250 NOOP\r\n");
615 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
616 cprintf("221 Goodbye...\r\n");
621 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
622 smtp_rcpt(&cmdbuf[5]);
625 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
629 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
630 smtp_vrfy(&cmdbuf[5]);
634 cprintf("502 I'm afraid I can't do that.\r\n");
642 /*****************************************************************************/
643 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
644 /*****************************************************************************/
651 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
654 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
661 char user[SIZ], node[SIZ], name[SIZ];
667 size_t blocksize = 0;
670 /* Parse out the host portion of the recipient address */
671 process_rfc822_addr(addr, user, node, name);
673 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
676 /* Load the message out of the database into a temp file */
678 if (msg_fp == NULL) {
680 sprintf(dsn, "Error creating temporary file");
684 CtdlRedirectOutput(msg_fp, -1);
685 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
686 CtdlRedirectOutput(NULL, -1);
687 fseek(msg_fp, 0L, SEEK_END);
688 msg_size = ftell(msg_fp);
692 /* Extract something to send later in the 'MAIL From:' command */
693 strcpy(mailfrom, "");
697 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
698 if (!strncasecmp(buf, "From:", 5)) {
699 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
701 for (i=0; i<strlen(mailfrom); ++i) {
702 if (!isprint(mailfrom[i])) {
703 strcpy(&mailfrom[i], &mailfrom[i+1]);
708 /* Strip out parenthesized names */
711 for (i=0; i<strlen(mailfrom); ++i) {
712 if (mailfrom[i] == '(') lp = i;
713 if (mailfrom[i] == ')') rp = i;
715 if ((lp>0)&&(rp>lp)) {
716 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
719 /* Prefer brokketized names */
722 for (i=0; i<strlen(mailfrom); ++i) {
723 if (mailfrom[i] == '<') lp = i;
724 if (mailfrom[i] == '>') rp = i;
726 if ( (lp>=0) && (rp>lp) ) {
728 strcpy(mailfrom, &mailfrom[lp]);
733 } while (scan_done == 0);
734 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
736 /* Figure out what mail exchanger host we have to connect to */
737 num_mxhosts = getmx(mxhosts, node);
738 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
739 if (num_mxhosts < 1) {
741 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
745 for (mx=0; mx<num_mxhosts; ++mx) {
746 extract(buf, mxhosts, mx);
747 lprintf(9, "Trying <%s>\n", buf);
748 sock = sock_connect(buf, "25", "tcp");
749 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
750 if (sock >= 0) lprintf(9, "Connected!\n");
751 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
752 if (sock >= 0) break;
756 *status = 4; /* dsn is already filled in */
760 /* Process the SMTP greeting from the server */
761 if (ml_sock_gets(sock, buf) < 0) {
763 strcpy(dsn, "Connection broken during SMTP conversation");
766 lprintf(9, "<%s\n", buf);
770 safestrncpy(dsn, &buf[4], 1023);
775 safestrncpy(dsn, &buf[4], 1023);
780 /* At this point we know we are talking to a real SMTP server */
782 /* Do a HELO command */
783 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
784 lprintf(9, ">%s", buf);
785 sock_write(sock, buf, strlen(buf));
786 if (ml_sock_gets(sock, buf) < 0) {
788 strcpy(dsn, "Connection broken during SMTP HELO");
791 lprintf(9, "<%s\n", buf);
795 safestrncpy(dsn, &buf[4], 1023);
800 safestrncpy(dsn, &buf[4], 1023);
806 /* HELO succeeded, now try the MAIL From: command */
807 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
808 lprintf(9, ">%s", buf);
809 sock_write(sock, buf, strlen(buf));
810 if (ml_sock_gets(sock, buf) < 0) {
812 strcpy(dsn, "Connection broken during SMTP MAIL");
815 lprintf(9, "<%s\n", buf);
819 safestrncpy(dsn, &buf[4], 1023);
824 safestrncpy(dsn, &buf[4], 1023);
830 /* MAIL succeeded, now try the RCPT To: command */
831 snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
832 lprintf(9, ">%s", buf);
833 sock_write(sock, buf, strlen(buf));
834 if (ml_sock_gets(sock, buf) < 0) {
836 strcpy(dsn, "Connection broken during SMTP RCPT");
839 lprintf(9, "<%s\n", buf);
843 safestrncpy(dsn, &buf[4], 1023);
848 safestrncpy(dsn, &buf[4], 1023);
854 /* RCPT succeeded, now try the DATA command */
855 lprintf(9, ">DATA\n");
856 sock_write(sock, "DATA\r\n", 6);
857 if (ml_sock_gets(sock, buf) < 0) {
859 strcpy(dsn, "Connection broken during SMTP DATA");
862 lprintf(9, "<%s\n", buf);
866 safestrncpy(dsn, &buf[4], 1023);
871 safestrncpy(dsn, &buf[4], 1023);
876 /* If we reach this point, the server is expecting data */
878 while (msg_size > 0) {
879 blocksize = sizeof(buf);
880 if (blocksize > msg_size) blocksize = msg_size;
881 fread(buf, blocksize, 1, msg_fp);
882 sock_write(sock, buf, blocksize);
883 msg_size -= blocksize;
885 if (buf[blocksize-1] != 10) {
886 lprintf(5, "Possible problem: message did not correctly "
887 "terminate. (expecting 0x10, got 0x%02x)\n",
891 sock_write(sock, ".\r\n", 3);
892 if (ml_sock_gets(sock, buf) < 0) {
894 strcpy(dsn, "Connection broken during SMTP message transmit");
897 lprintf(9, "%s\n", buf);
901 safestrncpy(dsn, &buf[4], 1023);
906 safestrncpy(dsn, &buf[4], 1023);
912 safestrncpy(dsn, &buf[4], 1023);
915 lprintf(9, ">QUIT\n");
916 sock_write(sock, "QUIT\r\n", 6);
917 ml_sock_gets(sock, buf);
918 lprintf(9, "<%s\n", buf);
920 bail: if (msg_fp != NULL) fclose(msg_fp);
928 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
929 * instructions for "5" codes (permanent fatal errors) and produce/deliver
930 * a "bounce" message (delivery status notification).
932 void smtp_do_bounce(char *instr) {
943 long bounce_msgid = (-1);
944 time_t submitted = 0L;
945 struct CtdlMessage *bmsg = NULL;
947 struct recptypes *valid;
948 int successful_bounce = 0;
950 lprintf(9, "smtp_do_bounce() called\n");
951 strcpy(bounceto, "");
953 lines = num_tokens(instr, '\n');
956 /* See if it's time to give up on delivery of this message */
957 for (i=0; i<lines; ++i) {
958 extract_token(buf, instr, i, '\n');
959 extract(key, buf, 0);
960 extract(addr, buf, 1);
961 if (!strcasecmp(key, "submitted")) {
962 submitted = atol(addr);
966 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
972 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
973 if (bmsg == NULL) return;
974 memset(bmsg, 0, sizeof(struct CtdlMessage));
976 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
977 bmsg->cm_anon_type = MES_NORMAL;
978 bmsg->cm_format_type = 1;
979 bmsg->cm_fields['A'] = strdoop("Citadel");
980 bmsg->cm_fields['O'] = strdoop(MAILROOM);
981 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
983 if (give_up) bmsg->cm_fields['M'] = strdoop(
984 "A message you sent could not be delivered to some or all of its recipients\n"
985 "due to prolonged unavailability of its destination(s).\n"
986 "Giving up on the following addresses:\n\n"
989 else bmsg->cm_fields['M'] = strdoop(
990 "A message you sent could not be delivered to some or all of its recipients.\n"
991 "The following addresses were undeliverable:\n\n"
995 * Now go through the instructions checking for stuff.
998 for (i=0; i<lines; ++i) {
999 extract_token(buf, instr, i, '\n');
1000 extract(key, buf, 0);
1001 extract(addr, buf, 1);
1002 status = extract_int(buf, 2);
1003 extract(dsn, buf, 3);
1006 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1007 key, addr, status, dsn);
1009 if (!strcasecmp(key, "bounceto")) {
1010 strcpy(bounceto, addr);
1014 (!strcasecmp(key, "local"))
1015 || (!strcasecmp(key, "remote"))
1016 || (!strcasecmp(key, "ignet"))
1017 || (!strcasecmp(key, "room"))
1019 if (status == 5) bounce_this = 1;
1020 if (give_up) bounce_this = 1;
1026 if (bmsg->cm_fields['M'] == NULL) {
1027 lprintf(2, "ERROR ... M field is null "
1028 "(%s:%d)\n", __FILE__, __LINE__);
1031 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1032 strlen(bmsg->cm_fields['M']) + 1024 );
1033 strcat(bmsg->cm_fields['M'], addr);
1034 strcat(bmsg->cm_fields['M'], ": ");
1035 strcat(bmsg->cm_fields['M'], dsn);
1036 strcat(bmsg->cm_fields['M'], "\n");
1038 remove_token(instr, i, '\n');
1044 /* Deliver the bounce if there's anything worth mentioning */
1045 lprintf(9, "num_bounces = %d\n", num_bounces);
1046 if (num_bounces > 0) {
1048 /* First try the user who sent the message */
1049 lprintf(9, "bounce to user? <%s>\n", bounceto);
1050 if (strlen(bounceto) == 0) {
1051 lprintf(7, "No bounce address specified\n");
1052 bounce_msgid = (-1L);
1055 /* Can we deliver the bounce to the original sender? */
1056 valid = validate_recipients(bounceto);
1057 if (valid != NULL) {
1058 if (valid->num_error == 0) {
1059 CtdlSubmitMsg(bmsg, valid, "");
1060 successful_bounce = 1;
1064 /* If not, post it in the Aide> room */
1065 if (successful_bounce == 0) {
1066 CtdlSubmitMsg(bmsg, NULL, AIDEROOM);
1069 /* Free up the memory we used */
1070 if (valid != NULL) {
1075 CtdlFreeMessage(bmsg);
1076 lprintf(9, "Done processing bounces\n");
1081 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1082 * set of delivery instructions for completed deliveries and remove them.
1084 * It returns the number of incomplete deliveries remaining.
1086 int smtp_purge_completed_deliveries(char *instr) {
1097 lines = num_tokens(instr, '\n');
1098 for (i=0; i<lines; ++i) {
1099 extract_token(buf, instr, i, '\n');
1100 extract(key, buf, 0);
1101 extract(addr, buf, 1);
1102 status = extract_int(buf, 2);
1103 extract(dsn, buf, 3);
1108 (!strcasecmp(key, "local"))
1109 || (!strcasecmp(key, "remote"))
1110 || (!strcasecmp(key, "ignet"))
1111 || (!strcasecmp(key, "room"))
1113 if (status == 2) completed = 1;
1118 remove_token(instr, i, '\n');
1131 * Called by smtp_do_queue() to handle an individual message.
1133 void smtp_do_procmsg(long msgnum, void *userdata) {
1134 struct CtdlMessage *msg;
1136 char *results = NULL;
1144 long text_msgid = (-1);
1145 int incomplete_deliveries_remaining;
1146 time_t attempted = 0L;
1147 time_t last_attempted = 0L;
1148 time_t retry = SMTP_RETRY_INTERVAL;
1150 lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1152 msg = CtdlFetchMessage(msgnum);
1154 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1158 instr = strdoop(msg->cm_fields['M']);
1159 CtdlFreeMessage(msg);
1161 /* Strip out the headers amd any other non-instruction line */
1162 lines = num_tokens(instr, '\n');
1163 for (i=0; i<lines; ++i) {
1164 extract_token(buf, instr, i, '\n');
1165 if (num_tokens(buf, '|') < 2) {
1166 remove_token(instr, i, '\n');
1172 /* Learn the message ID and find out about recent delivery attempts */
1173 lines = num_tokens(instr, '\n');
1174 for (i=0; i<lines; ++i) {
1175 extract_token(buf, instr, i, '\n');
1176 extract(key, buf, 0);
1177 if (!strcasecmp(key, "msgid")) {
1178 text_msgid = extract_long(buf, 1);
1180 if (!strcasecmp(key, "retry")) {
1181 /* double the retry interval after each attempt */
1182 retry = extract_long(buf, 1) * 2L;
1183 if (retry > SMTP_RETRY_MAX) {
1184 retry = SMTP_RETRY_MAX;
1186 remove_token(instr, i, '\n');
1188 if (!strcasecmp(key, "attempted")) {
1189 attempted = extract_long(buf, 1);
1190 if (attempted > last_attempted)
1191 last_attempted = attempted;
1196 * Postpone delivery if we've already tried recently.
1198 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1199 lprintf(7, "Retry time not yet reached.\n");
1206 * Bail out if there's no actual message associated with this
1208 if (text_msgid < 0L) {
1209 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1214 /* Plow through the instructions looking for 'remote' directives and
1215 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1216 * were experienced and it's time to try again)
1218 lines = num_tokens(instr, '\n');
1219 for (i=0; i<lines; ++i) {
1220 extract_token(buf, instr, i, '\n');
1221 extract(key, buf, 0);
1222 extract(addr, buf, 1);
1223 status = extract_int(buf, 2);
1224 extract(dsn, buf, 3);
1225 if ( (!strcasecmp(key, "remote"))
1226 && ((status==0)||(status==3)||(status==4)) ) {
1227 remove_token(instr, i, '\n');
1230 lprintf(9, "SMTP: Trying <%s>\n", addr);
1231 smtp_try(key, addr, &status, dsn, text_msgid);
1233 if (results == NULL) {
1234 results = mallok(1024);
1235 memset(results, 0, 1024);
1238 results = reallok(results,
1239 strlen(results) + 1024);
1241 sprintf(&results[strlen(results)],
1243 key, addr, status, dsn);
1248 if (results != NULL) {
1249 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1250 strcat(instr, results);
1255 /* Generate 'bounce' messages */
1256 smtp_do_bounce(instr);
1258 /* Go through the delivery list, deleting completed deliveries */
1259 incomplete_deliveries_remaining =
1260 smtp_purge_completed_deliveries(instr);
1264 * No delivery instructions remain, so delete both the instructions
1265 * message and the message message.
1267 if (incomplete_deliveries_remaining <= 0) {
1268 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1269 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1274 * Uncompleted delivery instructions remain, so delete the old
1275 * instructions and replace with the updated ones.
1277 if (incomplete_deliveries_remaining > 0) {
1278 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1279 msg = mallok(sizeof(struct CtdlMessage));
1280 memset(msg, 0, sizeof(struct CtdlMessage));
1281 msg->cm_magic = CTDLMESSAGE_MAGIC;
1282 msg->cm_anon_type = MES_NORMAL;
1283 msg->cm_format_type = FMT_RFC822;
1284 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1285 snprintf(msg->cm_fields['M'],
1287 "Content-type: %s\n\n%s\n"
1290 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1292 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1293 CtdlFreeMessage(msg);
1303 * Run through the queue sending out messages.
1305 void smtp_do_queue(void) {
1306 static int doing_queue = 0;
1309 * This is a simple concurrency check to make sure only one queue run
1310 * is done at a time. We could do this with a mutex, but since we
1311 * don't really require extremely fine granularity here, we'll do it
1312 * with a static variable instead.
1314 if (doing_queue) return;
1318 * Go ahead and run the queue
1320 lprintf(7, "SMTP: processing outbound queue\n");
1322 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1323 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1326 CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1327 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1329 lprintf(7, "SMTP: queue run completed\n");
1336 /*****************************************************************************/
1337 /* SMTP UTILITY COMMANDS */
1338 /*****************************************************************************/
1340 void cmd_smtp(char *argbuf) {
1347 if (CtdlAccessCheck(ac_aide)) return;
1349 extract(cmd, argbuf, 0);
1351 if (!strcasecmp(cmd, "mx")) {
1352 extract(node, argbuf, 1);
1353 num_mxhosts = getmx(buf, node);
1354 cprintf("%d %d MX hosts listed for %s\n",
1355 LISTING_FOLLOWS, num_mxhosts, node);
1356 for (i=0; i<num_mxhosts; ++i) {
1357 extract(node, buf, i);
1358 cprintf("%s\n", node);
1364 else if (!strcasecmp(cmd, "runqueue")) {
1366 cprintf("%d All outbound SMTP will be retried now.\n", OK);
1371 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1379 /*****************************************************************************/
1380 /* MODULE INITIALIZATION STUFF */
1381 /*****************************************************************************/
1384 char *Dynamic_Module_Init(void)
1386 SYM_SMTP = CtdlGetDynamicSymbol();
1388 CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
1393 CtdlRegisterServiceHook(0, /* ...and locally */
1398 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1399 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1400 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");