11 #include <sys/types.h>
20 #include "sysdep_decls.h"
21 #include "citserver.h"
25 #include "dynloader.h"
32 #include "internet_addressing.h"
36 #include "clientsocket.h"
39 struct citsmtp { /* Information about the current session */
42 struct usersupp vrfy_buffer;
46 int number_of_recipients;
50 enum { /* Command states for login authentication */
56 enum { /* Delivery modes */
61 #define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
62 #define SMTP_RECP ((char *)CtdlGetUserData(SYM_SMTP_RECP))
69 /*****************************************************************************/
70 /* SMTP SERVER (INBOUND) STUFF */
71 /*****************************************************************************/
77 * Here's where our SMTP session begins its happy day.
79 void smtp_greeting(void) {
81 strcpy(CC->cs_clientname, "SMTP session");
83 CC->cs_flags |= CS_STEALTH;
84 CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
85 CtdlAllocUserData(SYM_SMTP_RECP, 256);
86 sprintf(SMTP_RECP, "%s", "");
88 cprintf("220 Welcome to the Citadel/UX ESMTP server at %s\r\n",
94 * Implement HELO and EHLO commands.
96 void smtp_hello(char *argbuf, int is_esmtp) {
98 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
101 cprintf("250 Greetings and joyous salutations.\r\n");
104 cprintf("250-Greetings and joyous salutations.\r\n");
105 cprintf("250-HELP\r\n");
106 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
107 cprintf("250 AUTH=LOGIN\r\n");
113 * Implement HELP command.
115 void smtp_help(void) {
116 cprintf("214-Here's the frequency, Kenneth:\r\n");
117 cprintf("214- DATA\r\n");
118 cprintf("214- EHLO\r\n");
119 cprintf("214- EXPN\r\n");
120 cprintf("214- HELO\r\n");
121 cprintf("214- HELP\r\n");
122 cprintf("214- MAIL\r\n");
123 cprintf("214- NOOP\r\n");
124 cprintf("214- QUIT\r\n");
125 cprintf("214- RCPT\r\n");
126 cprintf("214- RSET\r\n");
127 cprintf("214- VRFY\r\n");
128 cprintf("214 I could tell you more, but then I'd have to kill you.\r\n");
135 void smtp_get_user(char *argbuf) {
139 decode_base64(username, argbuf);
140 lprintf(9, "Trying <%s>\n", username);
141 if (CtdlLoginExistingUser(username) == login_ok) {
142 encode_base64(buf, "Password:");
143 cprintf("334 %s\r\n", buf);
144 SMTP->command_state = smtp_password;
147 cprintf("500 No such user.\r\n");
148 SMTP->command_state = smtp_command;
156 void smtp_get_pass(char *argbuf) {
159 decode_base64(password, argbuf);
160 lprintf(9, "Trying <%s>\n", password);
161 if (CtdlTryPassword(password) == pass_ok) {
162 cprintf("235 Authentication successful.\r\n");
163 lprintf(9, "SMTP authenticated login successful\n");
164 CC->internal_pgm = 0;
165 CC->cs_flags &= ~CS_STEALTH;
168 cprintf("500 Authentication failed.\r\n");
170 SMTP->command_state = smtp_command;
177 void smtp_auth(char *argbuf) {
180 if (strncasecmp(argbuf, "login", 5) ) {
181 cprintf("550 We only support LOGIN authentication.\r\n");
185 if (strlen(argbuf) >= 7) {
186 smtp_get_user(&argbuf[6]);
190 encode_base64(buf, "Username:");
191 cprintf("334 %s\r\n", buf);
192 SMTP->command_state = smtp_user;
198 * Back end for smtp_vrfy() command
200 void smtp_vrfy_backend(struct usersupp *us, void *data) {
202 if (!fuzzy_match(us, SMTP->vrfy_match)) {
204 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
210 * Implements the VRFY (verify user name) command.
211 * Performs fuzzy match on full user names.
213 void smtp_vrfy(char *argbuf) {
214 SMTP->vrfy_count = 0;
215 strcpy(SMTP->vrfy_match, argbuf);
216 ForEachUser(smtp_vrfy_backend, NULL);
218 if (SMTP->vrfy_count < 1) {
219 cprintf("550 String does not match anything.\r\n");
221 else if (SMTP->vrfy_count == 1) {
222 cprintf("250 %s <cit%ld@%s>\r\n",
223 SMTP->vrfy_buffer.fullname,
224 SMTP->vrfy_buffer.usernum,
227 else if (SMTP->vrfy_count > 1) {
228 cprintf("553 Request ambiguous: %d users matched.\r\n",
237 * Back end for smtp_expn() command
239 void smtp_expn_backend(struct usersupp *us, void *data) {
241 if (!fuzzy_match(us, SMTP->vrfy_match)) {
243 if (SMTP->vrfy_count >= 1) {
244 cprintf("250-%s <cit%ld@%s>\r\n",
245 SMTP->vrfy_buffer.fullname,
246 SMTP->vrfy_buffer.usernum,
251 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
257 * Implements the EXPN (expand user name) command.
258 * Performs fuzzy match on full user names.
260 void smtp_expn(char *argbuf) {
261 SMTP->vrfy_count = 0;
262 strcpy(SMTP->vrfy_match, argbuf);
263 ForEachUser(smtp_expn_backend, NULL);
265 if (SMTP->vrfy_count < 1) {
266 cprintf("550 String does not match anything.\r\n");
268 else if (SMTP->vrfy_count >= 1) {
269 cprintf("250 %s <cit%ld@%s>\r\n",
270 SMTP->vrfy_buffer.fullname,
271 SMTP->vrfy_buffer.usernum,
278 * Implements the RSET (reset state) command.
279 * Currently this just zeroes out the state buffer. If pointers to data
280 * allocated with mallok() are ever placed in the state buffer, we have to
281 * be sure to phree() them first!
283 void smtp_rset(void) {
284 memset(SMTP, 0, sizeof(struct citsmtp));
285 if (CC->logged_in) logout(CC);
286 cprintf("250 Zap!\r\n");
292 * Implements the "MAIL From:" command
294 void smtp_mail(char *argbuf) {
299 if (strlen(SMTP->from) != 0) {
300 cprintf("503 Only one sender permitted\r\n");
304 if (strncasecmp(argbuf, "From:", 5)) {
305 cprintf("501 Syntax error\r\n");
309 strcpy(SMTP->from, &argbuf[5]);
312 if (strlen(SMTP->from) == 0) {
313 cprintf("501 Empty sender name is not permitted\r\n");
318 /* If this SMTP connection is from a logged-in user, make sure that
319 * the user only sends email from his/her own address.
322 cvt = convert_internet_address(user, node, SMTP->from);
323 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
324 if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
325 cprintf("550 <%s> is not your address.\r\n", SMTP->from);
326 strcpy(SMTP->from, "");
331 /* Otherwise, make sure outsiders aren't trying to forge mail from
335 cvt = convert_internet_address(user, node, SMTP->from);
336 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
337 if (CtdlHostAlias(node) == hostalias_localhost) {
338 cprintf("550 You must log in to send mail from %s\r\n",
340 strcpy(SMTP->from, "");
345 cprintf("250 Sender ok. Groovy.\r\n");
351 * Implements the "RCPT To:" command
353 void smtp_rcpt(char *argbuf) {
358 int is_spam = 0; /* FIXME implement anti-spamming */
360 if (strlen(SMTP->from) == 0) {
361 cprintf("503 MAIL first, then RCPT. Duh.\r\n");
365 if (strncasecmp(argbuf, "To:", 3)) {
366 cprintf("501 Syntax error\r\n");
370 strcpy(recp, &argbuf[3]);
374 cvt = convert_internet_address(user, node, recp);
375 sprintf(recp, "%s@%s", user, node);
378 case rfc822_address_locally_validated:
379 cprintf("250 %s is a valid recipient.\r\n", user);
380 ++SMTP->number_of_recipients;
381 CtdlReallocUserData(SYM_SMTP_RECP,
382 strlen(SMTP_RECP) + 1024 );
383 strcat(SMTP_RECP, "local|");
384 strcat(SMTP_RECP, user);
385 strcat(SMTP_RECP, "|0\n");
388 case rfc822_room_delivery:
389 cprintf("250 Delivering to room '%s'\r\n", user);
390 ++SMTP->number_of_recipients;
391 CtdlReallocUserData(SYM_SMTP_RECP,
392 strlen(SMTP_RECP) + 1024 );
393 strcat(SMTP_RECP, "room|");
394 strcat(SMTP_RECP, user);
395 strcat(SMTP_RECP, "|0|\n");
398 case rfc822_no_such_user:
399 cprintf("550 %s: no such user\r\n", recp);
402 case rfc822_address_invalid:
404 cprintf("551 Away with thee, spammer!\r\n");
407 cprintf("250 Remote recipient %s ok\r\n", recp);
408 ++SMTP->number_of_recipients;
409 CtdlReallocUserData(SYM_SMTP_RECP,
410 strlen(SMTP_RECP) + 1024 );
411 strcat(SMTP_RECP, "remote|");
412 strcat(SMTP_RECP, recp);
413 strcat(SMTP_RECP, "|0|\n");
419 cprintf("599 Unknown error\r\n");
427 * Back end for smtp_data() ... this does the actual delivery of the message
428 * Returns 0 on success, nonzero on failure
430 int smtp_message_delivery(struct CtdlMessage *msg) {
437 int successful_saves = 0; /* number of successful local saves */
438 int failed_saves = 0; /* number of failed deliveries */
439 int remote_spools = 0; /* number of copies to send out */
442 struct usersupp userbuf;
443 char *instr; /* Remote delivery instructions */
444 struct CtdlMessage *imsg;
446 lprintf(9, "smtp_message_delivery() called\n");
448 /* Fill in 'from' fields with envelope information if missing */
449 process_rfc822_addr(SMTP->from, user, node, name);
450 if (msg->cm_fields['A']==NULL) msg->cm_fields['A'] = strdoop(user);
451 if (msg->cm_fields['N']==NULL) msg->cm_fields['N'] = strdoop(node);
452 if (msg->cm_fields['H']==NULL) msg->cm_fields['H'] = strdoop(name);
454 /* Save the message in the queue */
455 msgid = CtdlSaveMsg(msg,
462 instr = mallok(1024);
463 sprintf(instr, "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
465 SPOOLMIME, msgid, time(NULL),
468 for (i=0; i<SMTP->number_of_recipients; ++i) {
469 extract_token(buf, SMTP_RECP, i, '\n');
470 extract(dtype, buf, 0);
472 /* Stuff local mailboxes */
473 if (!strcasecmp(dtype, "local")) {
474 extract(user, buf, 1);
475 if (getuser(&userbuf, user) == 0) {
476 MailboxName(room, &userbuf, MAILROOM);
477 CtdlSaveMsgPointerInRoom(room, msgid, 0);
485 /* Delivery to local non-mailbox rooms */
486 if (!strcasecmp(dtype, "room")) {
487 extract(room, buf, 1);
488 CtdlSaveMsgPointerInRoom(room, msgid, 0);
492 /* Remote delivery */
493 if (!strcasecmp(dtype, "remote")) {
494 extract(user, buf, 1);
495 instr = reallok(instr, strlen(instr) + 1024);
496 sprintf(&instr[strlen(instr)],
504 /* If there are remote spools to be done, save the instructions */
505 if (remote_spools > 0) {
506 imsg = mallok(sizeof(struct CtdlMessage));
507 memset(imsg, 0, sizeof(struct CtdlMessage));
508 imsg->cm_magic = CTDLMESSAGE_MAGIC;
509 imsg->cm_anon_type = MES_NORMAL;
510 imsg->cm_format_type = FMT_RFC822;
511 imsg->cm_fields['M'] = instr;
512 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
513 CtdlFreeMessage(imsg);
516 /* If there are no remote spools, delete the message */
518 phree(instr); /* only needed here, because CtdlSaveMsg()
519 * would free this buffer otherwise */
520 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, NULL);
523 return(failed_saves);
529 * Implements the DATA command
531 void smtp_data(void) {
533 struct CtdlMessage *msg;
537 if (strlen(SMTP->from) == 0) {
538 cprintf("503 Need MAIL command first.\r\n");
542 if (SMTP->number_of_recipients < 1) {
543 cprintf("503 Need RCPT command first.\r\n");
547 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
549 generate_rfc822_datestamp(nowstamp, time(NULL));
552 if (body != NULL) sprintf(body,
553 "Received: from %s\n"
560 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
562 cprintf("550 Unable to save message text: internal error.\r\n");
566 lprintf(9, "Converting message...\n");
567 msg = convert_internet_message(body);
569 /* If the user is locally authenticated, FORCE the From: header to
570 * show up as the real sender
573 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
574 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
575 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
576 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
577 msg->cm_fields['N'] = strdoop(config.c_nodename);
578 msg->cm_fields['H'] = strdoop(config.c_humannode);
581 retval = smtp_message_delivery(msg);
582 CtdlFreeMessage(msg);
585 cprintf("250 Message accepted for delivery.\r\n");
588 cprintf("550 Internal delivery errors: %d\r\n", retval);
596 * Main command loop for SMTP sessions.
598 void smtp_command_loop(void) {
602 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
603 if (client_gets(cmdbuf) < 1) {
604 lprintf(3, "SMTP socket is broken. Ending session.\n");
608 lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
609 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
611 if (SMTP->command_state == smtp_user) {
612 smtp_get_user(cmdbuf);
615 else if (SMTP->command_state == smtp_password) {
616 smtp_get_pass(cmdbuf);
619 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
620 smtp_auth(&cmdbuf[5]);
623 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
627 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
628 smtp_hello(&cmdbuf[5], 1);
631 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
632 smtp_expn(&cmdbuf[5]);
635 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
636 smtp_hello(&cmdbuf[5], 0);
639 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
643 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
644 smtp_mail(&cmdbuf[5]);
647 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
648 cprintf("250 This command successfully did nothing.\r\n");
651 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
652 cprintf("221 Goodbye...\r\n");
657 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
658 smtp_rcpt(&cmdbuf[5]);
661 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
665 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
666 smtp_vrfy(&cmdbuf[5]);
670 cprintf("502 I'm sorry Dave, I'm afraid I can't do that.\r\n");
678 /*****************************************************************************/
679 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
680 /*****************************************************************************/
687 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
690 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
697 char user[256], node[256], name[256];
703 size_t blocksize = 0;
706 /* Parse out the host portion of the recipient address */
707 process_rfc822_addr(addr, user, node, name);
709 if (is_ignet(node)) {
710 if (ignet_spool_to(node, msgnum) == 0) {
711 strcpy(dsn, "Delivery via Citadel network successful");
715 strcpy(dsn, "Delivery via Citadel network failed");
721 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
724 /* Load the message out of the database into a temp file */
726 if (msg_fp == NULL) {
728 sprintf(dsn, "Error creating temporary file");
732 CtdlRedirectOutput(msg_fp, -1);
733 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
734 CtdlRedirectOutput(NULL, -1);
735 fseek(msg_fp, 0L, SEEK_END);
736 msg_size = ftell(msg_fp);
740 /* Extract something to send later in the 'MAIL From:' command */
741 strcpy(mailfrom, "");
745 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
746 if (!strncasecmp(buf, "From:", 5)) {
747 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
749 for (i=0; i<strlen(mailfrom); ++i) {
750 if (!isprint(mailfrom[i])) {
751 strcpy(&mailfrom[i], &mailfrom[i+1]);
756 /* Strip out parenthesized names */
759 for (i=0; i<strlen(mailfrom); ++i) {
760 if (mailfrom[i] == '(') lp = i;
761 if (mailfrom[i] == ')') rp = i;
763 if ((lp>0)&&(rp>lp)) {
764 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
767 /* Prefer brokketized names */
770 for (i=0; i<strlen(mailfrom); ++i) {
771 if (mailfrom[i] == '<') lp = i;
772 if (mailfrom[i] == '>') rp = i;
774 if ((lp>=0)&&(rp>lp)) {
776 strcpy(mailfrom, &mailfrom[lp]);
781 } while (scan_done == 0);
782 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
785 /* Figure out what mail exchanger host we have to connect to */
786 num_mxhosts = getmx(mxhosts, node);
787 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
788 if (num_mxhosts < 1) {
790 sprintf(dsn, "No MX hosts found for <%s>", node);
794 for (mx=0; mx<num_mxhosts; ++mx) {
795 extract(buf, mxhosts, mx);
796 lprintf(9, "Trying <%s>\n", buf);
797 sock = sock_connect(buf, "25", "tcp");
798 sprintf(dsn, "Could not connect: %s", strerror(errno));
799 if (sock >= 0) lprintf(9, "Connected!\n");
800 if (sock < 0) sprintf(dsn, "%s", strerror(errno));
801 if (sock >= 0) break;
805 *status = 4; /* dsn is already filled in */
809 /* Process the SMTP greeting from the server */
810 if (sock_gets(sock, buf) < 0) {
812 strcpy(dsn, "Connection broken during SMTP conversation");
815 lprintf(9, "<%s\n", buf);
819 strcpy(dsn, &buf[4]);
824 strcpy(dsn, &buf[4]);
829 /* At this point we know we are talking to a real SMTP server */
831 /* Do a HELO command */
832 sprintf(buf, "HELO %s", config.c_fqdn);
833 lprintf(9, ">%s\n", buf);
834 sock_puts(sock, buf);
835 if (sock_gets(sock, buf) < 0) {
837 strcpy(dsn, "Connection broken during SMTP conversation");
840 lprintf(9, "<%s\n", buf);
844 strcpy(dsn, &buf[4]);
849 strcpy(dsn, &buf[4]);
855 /* HELO succeeded, now try the MAIL From: command */
856 sprintf(buf, "MAIL From: %s", mailfrom);
857 lprintf(9, ">%s\n", buf);
858 sock_puts(sock, buf);
859 if (sock_gets(sock, buf) < 0) {
861 strcpy(dsn, "Connection broken during SMTP conversation");
864 lprintf(9, "<%s\n", buf);
868 strcpy(dsn, &buf[4]);
873 strcpy(dsn, &buf[4]);
879 /* MAIL succeeded, now try the RCPT To: command */
880 sprintf(buf, "RCPT To: %s", addr);
881 lprintf(9, ">%s\n", buf);
882 sock_puts(sock, buf);
883 if (sock_gets(sock, buf) < 0) {
885 strcpy(dsn, "Connection broken during SMTP conversation");
888 lprintf(9, "<%s\n", buf);
892 strcpy(dsn, &buf[4]);
897 strcpy(dsn, &buf[4]);
903 /* RCPT succeeded, now try the DATA command */
904 lprintf(9, ">DATA\n");
905 sock_puts(sock, "DATA");
906 if (sock_gets(sock, buf) < 0) {
908 strcpy(dsn, "Connection broken during SMTP conversation");
911 lprintf(9, "<%s\n", buf);
915 strcpy(dsn, &buf[4]);
920 strcpy(dsn, &buf[4]);
925 /* If we reach this point, the server is expecting data */
927 while (msg_size > 0) {
928 blocksize = sizeof(buf);
929 if (blocksize > msg_size) blocksize = msg_size;
930 fread(buf, blocksize, 1, msg_fp);
931 sock_write(sock, buf, blocksize);
932 msg_size -= blocksize;
934 if (buf[blocksize-1] != 10) {
935 lprintf(5, "Possible problem: message did not correctly "
936 "terminate. (expecting 0x10, got 0x%02x)\n",
940 sock_write(sock, ".\r\n", 3);
941 if (sock_gets(sock, buf) < 0) {
943 strcpy(dsn, "Connection broken during SMTP conversation");
946 lprintf(9, "%s\n", buf);
950 strcpy(dsn, &buf[4]);
955 strcpy(dsn, &buf[4]);
961 strcpy(dsn, &buf[4]);
964 lprintf(9, ">QUIT\n");
965 sock_puts(sock, "QUIT");
966 sock_gets(sock, buf);
967 lprintf(9, "<%s\n", buf);
969 bail: if (msg_fp != NULL) fclose(msg_fp);
977 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
978 * instructions for "5" codes (permanent fatal errors) and produce/deliver
979 * a "bounce" message (delivery status notification).
981 void smtp_do_bounce(char *instr) {
992 long bounce_msgid = (-1);
993 time_t submitted = 0L;
994 struct CtdlMessage *bmsg = NULL;
998 lprintf(9, "smtp_do_bounce() called\n");
999 strcpy(bounceto, "");
1001 lines = num_tokens(instr, '\n');
1004 /* See if it's time to give up on delivery of this message */
1005 for (i=0; i<lines; ++i) {
1006 extract_token(buf, instr, i, '\n');
1007 extract(key, buf, 0);
1008 extract(addr, buf, 1);
1009 if (!strcasecmp(key, "submitted")) {
1010 submitted = atol(addr);
1014 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1020 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1021 if (bmsg == NULL) return;
1022 memset(bmsg, 0, sizeof(struct CtdlMessage));
1024 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1025 bmsg->cm_anon_type = MES_NORMAL;
1026 bmsg->cm_format_type = 1;
1027 bmsg->cm_fields['A'] = strdoop("Citadel");
1028 bmsg->cm_fields['O'] = strdoop(MAILROOM);
1029 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1031 if (give_up) bmsg->cm_fields['M'] = strdoop(
1032 "A message you sent could not be delivered to some or all of its recipients.\n"
1033 "The following addresses were undeliverable:\n\n"
1036 else bmsg->cm_fields['M'] = strdoop(
1037 "A message you sent could not be delivered to some or all of its recipients\n"
1038 "due to prolonged unavailability of its destination(s).\n"
1039 "Giving up on the following addresses:\n\n"
1043 * Now go through the instructions checking for stuff.
1046 for (i=0; i<lines; ++i) {
1047 extract_token(buf, instr, i, '\n');
1048 extract(key, buf, 0);
1049 extract(addr, buf, 1);
1050 status = extract_int(buf, 2);
1051 extract(dsn, buf, 3);
1054 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1055 key, addr, status, dsn);
1057 if (!strcasecmp(key, "bounceto")) {
1058 strcpy(bounceto, addr);
1062 (!strcasecmp(key, "local"))
1063 || (!strcasecmp(key, "remote"))
1064 || (!strcasecmp(key, "ignet"))
1065 || (!strcasecmp(key, "room"))
1067 if (status == 5) bounce_this = 1;
1068 if (give_up) bounce_this = 1;
1074 if (bmsg->cm_fields['M'] == NULL) {
1075 lprintf(2, "ERROR ... M field is null "
1076 "(%s:%d)\n", __FILE__, __LINE__);
1079 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1080 strlen(bmsg->cm_fields['M']) + 1024 );
1081 strcat(bmsg->cm_fields['M'], addr);
1082 strcat(bmsg->cm_fields['M'], ": ");
1083 strcat(bmsg->cm_fields['M'], dsn);
1084 strcat(bmsg->cm_fields['M'], "\n");
1086 remove_token(instr, i, '\n');
1092 /* Deliver the bounce if there's anything worth mentioning */
1093 lprintf(9, "num_bounces = %d\n", num_bounces);
1094 if (num_bounces > 0) {
1096 /* First try the user who sent the message */
1097 lprintf(9, "bounce to user? <%s>\n", bounceto);
1098 if (strlen(bounceto) == 0) {
1099 lprintf(7, "No bounce address specified\n");
1100 bounce_msgid = (-1L);
1102 else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
1103 lprintf(7, "Invalid bounce address <%s>\n", bounceto);
1104 bounce_msgid = (-1L);
1107 bounce_msgid = CtdlSaveMsg(bmsg,
1112 /* Otherwise, go to the Aide> room */
1113 lprintf(9, "bounce to room?\n");
1114 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1119 CtdlFreeMessage(bmsg);
1120 lprintf(9, "Done processing bounces\n");
1125 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1126 * set of delivery instructions for completed deliveries and remove them.
1128 * It returns the number of incomplete deliveries remaining.
1130 int smtp_purge_completed_deliveries(char *instr) {
1141 lines = num_tokens(instr, '\n');
1142 for (i=0; i<lines; ++i) {
1143 extract_token(buf, instr, i, '\n');
1144 extract(key, buf, 0);
1145 extract(addr, buf, 1);
1146 status = extract_int(buf, 2);
1147 extract(dsn, buf, 3);
1152 (!strcasecmp(key, "local"))
1153 || (!strcasecmp(key, "remote"))
1154 || (!strcasecmp(key, "ignet"))
1155 || (!strcasecmp(key, "room"))
1157 if (status == 2) completed = 1;
1162 remove_token(instr, i, '\n');
1175 * Called by smtp_do_queue() to handle an individual message.
1177 void smtp_do_procmsg(long msgnum) {
1178 struct CtdlMessage *msg;
1180 char *results = NULL;
1188 long text_msgid = (-1);
1189 int incomplete_deliveries_remaining;
1190 time_t attempted = 0L;
1191 time_t last_attempted = 0L;
1193 msg = CtdlFetchMessage(msgnum);
1195 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1199 instr = strdoop(msg->cm_fields['M']);
1200 CtdlFreeMessage(msg);
1202 /* Strip out the headers amd any other non-instruction line */
1203 lines = num_tokens(instr, '\n');
1204 for (i=0; i<lines; ++i) {
1205 extract_token(buf, instr, i, '\n');
1206 if (num_tokens(buf, '|') < 2) {
1207 lprintf(9, "removing <%s>\n", buf);
1208 remove_token(instr, i, '\n');
1214 /* Learn the message ID and find out about recent delivery attempts */
1215 lines = num_tokens(instr, '\n');
1216 for (i=0; i<lines; ++i) {
1217 extract_token(buf, instr, i, '\n');
1218 extract(key, buf, 0);
1219 if (!strcasecmp(key, "msgid")) {
1220 text_msgid = extract_long(buf, 1);
1222 if (!strcasecmp(key, "attempted")) {
1223 attempted = extract_long(buf, 1);
1224 if (attempted > last_attempted)
1225 last_attempted = attempted;
1231 * Postpone delivery if we've already tried recently.
1233 if ( (time(NULL) - last_attempted) < SMTP_RETRY_INTERVAL) {
1234 lprintf(7, "Retry time not yet reached.\n");
1241 * Bail out if there's no actual message associated with this
1243 if (text_msgid < 0L) {
1244 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1249 /* Plow through the instructions looking for 'remote' directives and
1250 * a status of 0 (no delivery yet attempted) or 3 (transient errors
1251 * were experienced and it's time to try again)
1253 lines = num_tokens(instr, '\n');
1254 for (i=0; i<lines; ++i) {
1255 extract_token(buf, instr, i, '\n');
1256 extract(key, buf, 0);
1257 extract(addr, buf, 1);
1258 status = extract_int(buf, 2);
1259 extract(dsn, buf, 3);
1260 if ( (!strcasecmp(key, "remote"))
1261 && ((status==0)||(status==3)) ) {
1262 remove_token(instr, i, '\n');
1265 lprintf(9, "SMTP: Trying <%s>\n", addr);
1266 smtp_try(key, addr, &status, dsn, text_msgid);
1268 if (results == NULL) {
1269 results = mallok(1024);
1270 memset(results, 0, 1024);
1273 results = reallok(results,
1274 strlen(results) + 1024);
1276 sprintf(&results[strlen(results)],
1278 key, addr, status, dsn);
1283 if (results != NULL) {
1284 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1285 strcat(instr, results);
1290 /* Generate 'bounce' messages */
1291 smtp_do_bounce(instr);
1293 /* Go through the delivery list, deleting completed deliveries */
1294 incomplete_deliveries_remaining =
1295 smtp_purge_completed_deliveries(instr);
1299 * No delivery instructions remain, so delete both the instructions
1300 * message and the message message.
1302 if (incomplete_deliveries_remaining <= 0) {
1303 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1304 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, NULL);
1309 * Uncompleted delivery instructions remain, so delete the old
1310 * instructions and replace with the updated ones.
1312 if (incomplete_deliveries_remaining > 0) {
1313 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1314 msg = mallok(sizeof(struct CtdlMessage));
1315 memset(msg, 0, sizeof(struct CtdlMessage));
1316 msg->cm_magic = CTDLMESSAGE_MAGIC;
1317 msg->cm_anon_type = MES_NORMAL;
1318 msg->cm_format_type = FMT_RFC822;
1319 msg->cm_fields['M'] = malloc(strlen(instr)+256);
1320 sprintf(msg->cm_fields['M'],
1321 "Content-type: %s\n\n%s\nattempted|%ld\n",
1322 SPOOLMIME, instr, time(NULL) );
1324 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
1325 CtdlFreeMessage(msg);
1335 * Run through the queue sending out messages.
1337 void smtp_do_queue(void) {
1338 static int doing_queue = 0;
1341 * This is a simple concurrency check to make sure only one queue run
1342 * is done at a time. We could do this with a mutex, but since we
1343 * don't really require extremely fine granularity here, we'll do it
1344 * with a static variable instead.
1346 if (doing_queue) return;
1350 * Go ahead and run the queue
1352 lprintf(5, "SMTP: processing outbound queue\n");
1354 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1355 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1358 CtdlForEachMessage(MSGS_ALL, 0L, SPOOLMIME, NULL, smtp_do_procmsg);
1360 lprintf(5, "SMTP: queue run completed\n");
1366 /*****************************************************************************/
1367 /* MODULE INITIALIZATION STUFF */
1368 /*****************************************************************************/
1371 char *Dynamic_Module_Init(void)
1373 SYM_SMTP = CtdlGetDynamicSymbol();
1374 SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1375 CtdlRegisterServiceHook(SMTP_PORT,
1378 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
1379 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);