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>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
39 #include "sysdep_decls.h"
40 #include "citserver.h"
44 #include "serv_extensions.h"
51 #include "internet_addressing.h"
54 #include "clientsocket.h"
55 #include "locate_host.h"
62 struct citsmtp { /* Information about the current session */
65 struct usersupp vrfy_buffer;
70 int number_of_recipients;
73 int message_originated_locally;
76 enum { /* Command states for login authentication */
82 enum { /* Delivery modes */
87 #define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
88 #define SMTP_RECPS ((char *)CtdlGetUserData(SYM_SMTP_RECPS))
89 #define SMTP_ROOMS ((char *)CtdlGetUserData(SYM_SMTP_ROOMS))
95 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
99 /*****************************************************************************/
100 /* SMTP SERVER (INBOUND) STUFF */
101 /*****************************************************************************/
107 * Here's where our SMTP session begins its happy day.
109 void smtp_greeting(void) {
111 strcpy(CC->cs_clientname, "SMTP session");
112 CC->internal_pgm = 1;
113 CC->cs_flags |= CS_STEALTH;
114 CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
115 CtdlAllocUserData(SYM_SMTP_RECPS, SIZ);
116 CtdlAllocUserData(SYM_SMTP_ROOMS, SIZ);
117 snprintf(SMTP_RECPS, SIZ, "%s", "");
118 snprintf(SMTP_ROOMS, SIZ, "%s", "");
120 cprintf("220 %s ESMTP Citadel/UX server ready.\r\n", config.c_fqdn);
125 * Implement HELO and EHLO commands.
127 void smtp_hello(char *argbuf, int is_esmtp) {
129 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
132 cprintf("250 Greetings and joyous salutations.\r\n");
135 cprintf("250-Greetings and joyous salutations.\r\n");
136 cprintf("250-HELP\r\n");
137 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
138 cprintf("250-PIPELINING\r\n");
139 cprintf("250 AUTH=LOGIN\r\n");
145 * Implement HELP command.
147 void smtp_help(void) {
148 cprintf("214-Commands accepted:\r\n");
149 cprintf("214- DATA\r\n");
150 cprintf("214- EHLO\r\n");
151 cprintf("214- EXPN\r\n");
152 cprintf("214- HELO\r\n");
153 cprintf("214- HELP\r\n");
154 cprintf("214- MAIL\r\n");
155 cprintf("214- NOOP\r\n");
156 cprintf("214- QUIT\r\n");
157 cprintf("214- RCPT\r\n");
158 cprintf("214- RSET\r\n");
159 cprintf("214- VRFY\r\n");
167 void smtp_get_user(char *argbuf) {
171 CtdlDecodeBase64(username, argbuf, SIZ);
172 lprintf(9, "Trying <%s>\n", username);
173 if (CtdlLoginExistingUser(username) == login_ok) {
174 CtdlEncodeBase64(buf, "Password:", 9);
175 cprintf("334 %s\r\n", buf);
176 SMTP->command_state = smtp_password;
179 cprintf("500 No such user.\r\n");
180 SMTP->command_state = smtp_command;
188 void smtp_get_pass(char *argbuf) {
191 CtdlDecodeBase64(password, argbuf, SIZ);
192 lprintf(9, "Trying <%s>\n", password);
193 if (CtdlTryPassword(password) == pass_ok) {
194 cprintf("235 Hello, %s\r\n", CC->usersupp.fullname);
195 lprintf(9, "SMTP authenticated login successful\n");
196 CC->internal_pgm = 0;
197 CC->cs_flags &= ~CS_STEALTH;
200 cprintf("500 Authentication failed.\r\n");
202 SMTP->command_state = smtp_command;
209 void smtp_auth(char *argbuf) {
212 if (strncasecmp(argbuf, "login", 5) ) {
213 cprintf("550 We only support LOGIN authentication.\r\n");
217 if (strlen(argbuf) >= 7) {
218 smtp_get_user(&argbuf[6]);
222 CtdlEncodeBase64(buf, "Username:", 9);
223 cprintf("334 %s\r\n", buf);
224 SMTP->command_state = smtp_user;
230 * Back end for smtp_vrfy() command
232 void smtp_vrfy_backend(struct usersupp *us, void *data) {
234 if (!fuzzy_match(us, SMTP->vrfy_match)) {
236 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
242 * Implements the VRFY (verify user name) command.
243 * Performs fuzzy match on full user names.
245 void smtp_vrfy(char *argbuf) {
246 SMTP->vrfy_count = 0;
247 strcpy(SMTP->vrfy_match, argbuf);
248 ForEachUser(smtp_vrfy_backend, NULL);
250 if (SMTP->vrfy_count < 1) {
251 cprintf("550 String does not match anything.\r\n");
253 else if (SMTP->vrfy_count == 1) {
254 cprintf("250 %s <cit%ld@%s>\r\n",
255 SMTP->vrfy_buffer.fullname,
256 SMTP->vrfy_buffer.usernum,
259 else if (SMTP->vrfy_count > 1) {
260 cprintf("553 Request ambiguous: %d users matched.\r\n",
269 * Back end for smtp_expn() command
271 void smtp_expn_backend(struct usersupp *us, void *data) {
273 if (!fuzzy_match(us, SMTP->vrfy_match)) {
275 if (SMTP->vrfy_count >= 1) {
276 cprintf("250-%s <cit%ld@%s>\r\n",
277 SMTP->vrfy_buffer.fullname,
278 SMTP->vrfy_buffer.usernum,
283 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
289 * Implements the EXPN (expand user name) command.
290 * Performs fuzzy match on full user names.
292 void smtp_expn(char *argbuf) {
293 SMTP->vrfy_count = 0;
294 strcpy(SMTP->vrfy_match, argbuf);
295 ForEachUser(smtp_expn_backend, NULL);
297 if (SMTP->vrfy_count < 1) {
298 cprintf("550 String does not match anything.\r\n");
300 else if (SMTP->vrfy_count >= 1) {
301 cprintf("250 %s <cit%ld@%s>\r\n",
302 SMTP->vrfy_buffer.fullname,
303 SMTP->vrfy_buffer.usernum,
310 * Implements the RSET (reset state) command.
311 * Currently this just zeroes out the state buffer. If pointers to data
312 * allocated with mallok() are ever placed in the state buffer, we have to
313 * be sure to phree() them first!
315 void smtp_rset(void) {
316 memset(SMTP, 0, sizeof(struct citsmtp));
320 cprintf("250 Zap!\r\n");
324 * Clear out the portions of the state buffer that need to be cleared out
325 * after the DATA command finishes.
327 void smtp_data_clear(void) {
328 strcpy(SMTP->from, "");
329 strcpy(SMTP->recipients, "");
330 SMTP->number_of_recipients = 0;
331 SMTP->delivery_mode = 0;
332 SMTP->message_originated_locally = 0;
338 * Implements the "MAIL From:" command
340 void smtp_mail(char *argbuf) {
344 struct recptypes *valid;
346 if (strlen(SMTP->from) != 0) {
347 cprintf("503 Only one sender permitted\r\n");
351 if (strncasecmp(argbuf, "From:", 5)) {
352 cprintf("501 Syntax error\r\n");
356 strcpy(SMTP->from, &argbuf[5]);
358 stripallbut(SMTP->from, '<', '>');
360 if (strlen(SMTP->from) == 0) {
361 cprintf("501 Empty sender name is not permitted\r\n");
365 /* If this SMTP connection is from a logged-in user, make sure that
366 * the user only sends email from his/her own address.
369 valid = validate_recipients(SMTP->from);
370 if ( (valid->num_local == 1) &&
371 (!strcasecmp(valid->recp_local, CC->usersupp.fullname)) ) {
372 cprintf("250 Sender ok <%s>\r\n", valid->recp_local);
373 SMTP->message_originated_locally = 1;
376 cprintf("550 <%s> is not your address.\r\n",
378 strcpy(SMTP->from, "");
385 /* Otherwise, make sure outsiders aren't trying to forge mail from
389 process_rfc822_addr(SMTP->from, user, node, name);
390 if (CtdlHostAlias(node) != hostalias_nomatch) {
391 cprintf("550 You must log in to send mail from %s\r\n",
393 strcpy(SMTP->from, "");
398 cprintf("250 Sender ok\r\n");
404 * Implements the "RCPT To:" command
406 void smtp_rcpt(char *argbuf) {
408 char message_to_spammer[SIZ];
409 struct recptypes *valid = NULL;
411 if (strlen(SMTP->from) == 0) {
412 cprintf("503 Need MAIL before RCPT\r\n");
416 if (strncasecmp(argbuf, "To:", 3)) {
417 cprintf("501 Syntax error\r\n");
421 strcpy(recp, &argbuf[3]);
423 stripallbut(recp, '<', '>');
425 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
426 cprintf("452 Too many recipients\r\n");
431 if ( (!CC->logged_in) && (!CC->is_local_socket) ) {
432 if (rbl_check(message_to_spammer)) {
433 cprintf("552 %s\r\n", message_to_spammer);
434 /* no need to phree(valid), it's not allocated yet */
439 valid = validate_recipients(recp);
440 if (valid->num_error > 0) {
441 cprintf("599 Error: %s\r\n", valid->errormsg);
446 if (valid->num_internet > 0) {
447 if (SMTP->message_originated_locally == 0) {
448 cprintf("551 Relaying denied <%s>\r\n", recp);
454 cprintf("250 RCPT ok <%s>\r\n", recp);
455 if (strlen(SMTP->recipients) > 0) {
456 strcat(SMTP->recipients, ",");
458 strcat(SMTP->recipients, recp);
459 SMTP->number_of_recipients += 1;
466 * Implements the DATA command
468 void smtp_data(void) {
470 struct CtdlMessage *msg;
473 struct recptypes *valid;
476 if (strlen(SMTP->from) == 0) {
477 cprintf("503 Need MAIL command first.\r\n");
481 if (SMTP->number_of_recipients < 1) {
482 cprintf("503 Need RCPT command first.\r\n");
486 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
488 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
492 it should be Received: from %s (real.name.dom [w.x.y.z])
494 if (body != NULL) snprintf(body, 4096,
495 "Received: from %s (%s)\n"
502 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
504 cprintf("550 Unable to save message: internal error.\r\n");
508 lprintf(9, "Converting message...\n");
509 msg = convert_internet_message(body);
511 /* If the user is locally authenticated, FORCE the From: header to
512 * show up as the real sender. Yes, this violates the RFC standard,
513 * but IT MAKES SENSE. Comment it out if you don't like this behavior.
516 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
517 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
518 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
519 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
520 msg->cm_fields['N'] = strdoop(config.c_nodename);
521 msg->cm_fields['H'] = strdoop(config.c_humannode);
524 /* Submit the message into the Citadel system. */
525 valid = validate_recipients(SMTP->recipients);
527 /* If there are modules that want to scan this message before final
528 * submission (such as virus checkers or spam filters), call them now
529 * and give them an opportunity to reject the message.
531 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
533 if (scan_errors > 0) { /* We don't want this message! */
535 if (msg->cm_fields['0'] == NULL) {
536 msg->cm_fields['0'] = strdoop(
537 "Message rejected by filter");
540 cprintf("552 %s\r\n", msg->cm_fields['0']);
543 else { /* Ok, we'll accept this message. */
544 msgnum = CtdlSubmitMsg(msg, valid, "");
546 cprintf("250 Message accepted.\r\n");
549 cprintf("550 Internal delivery error\r\n");
553 CtdlFreeMessage(msg);
555 smtp_data_clear(); /* clear out the buffers now */
562 * Main command loop for SMTP sessions.
564 void smtp_command_loop(void) {
568 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
569 if (client_gets(cmdbuf) < 1) {
570 lprintf(3, "SMTP socket is broken. Ending session.\n");
574 lprintf(5, "SMTP: %s\n", cmdbuf);
575 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
577 if (SMTP->command_state == smtp_user) {
578 smtp_get_user(cmdbuf);
581 else if (SMTP->command_state == smtp_password) {
582 smtp_get_pass(cmdbuf);
585 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
586 smtp_auth(&cmdbuf[5]);
589 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
593 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
594 smtp_hello(&cmdbuf[5], 1);
597 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
598 smtp_expn(&cmdbuf[5]);
601 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
602 smtp_hello(&cmdbuf[5], 0);
605 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
609 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
610 smtp_mail(&cmdbuf[5]);
613 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
614 cprintf("250 NOOP\r\n");
617 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
618 cprintf("221 Goodbye...\r\n");
623 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
624 smtp_rcpt(&cmdbuf[5]);
627 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
631 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
632 smtp_vrfy(&cmdbuf[5]);
636 cprintf("502 I'm afraid I can't do that.\r\n");
644 /*****************************************************************************/
645 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
646 /*****************************************************************************/
653 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
656 void smtp_try(const char *key, const char *addr, int *status,
657 char *dsn, size_t n, 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 snprintf(dsn, n, "Error creating temporary file");
687 CtdlRedirectOutput(msg_fp, -1);
688 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 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, config.c_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, sizeof 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 snprintf(&results[strlen(results)], 1024,
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,
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", CIT_OK);
1374 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1381 * Initialize the SMTP outbound queue
1383 void smtp_init_spoolout(void) {
1384 struct quickroom qrbuf;
1387 * Create the room. This will silently fail if the room already
1388 * exists, and that's perfectly ok, because we want it to exist.
1390 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0);
1393 * Make sure it's set to be a "system room" so it doesn't show up
1394 * in the <K>nown rooms list for Aides.
1396 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1397 qrbuf.QRflags2 |= QR2_SYSTEM;
1405 /*****************************************************************************/
1406 /* MODULE INITIALIZATION STUFF */
1407 /*****************************************************************************/
1410 char *serv_smtp_init(void)
1412 SYM_SMTP = CtdlGetDynamicSymbol();
1414 CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
1419 CtdlRegisterServiceHook(0, /* ...and locally */
1424 smtp_init_spoolout();
1425 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1426 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");