11 #include <sys/types.h>
20 #include "sysdep_decls.h"
21 #include "citserver.h"
25 #include "dynloader.h"
32 #include "internet_addressing.h"
35 #include "clientsocket.h"
38 struct citsmtp { /* Information about the current session */
41 struct usersupp vrfy_buffer;
45 int number_of_recipients;
49 enum { /* Command states for login authentication */
55 enum { /* Delivery modes */
60 #define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
61 #define SMTP_RECP ((char *)CtdlGetUserData(SYM_SMTP_RECP))
68 /*****************************************************************************/
69 /* SMTP SERVER (INBOUND) STUFF */
70 /*****************************************************************************/
76 * Here's where our SMTP session begins its happy day.
78 void smtp_greeting(void) {
80 strcpy(CC->cs_clientname, "SMTP session");
82 CC->cs_flags |= CS_STEALTH;
83 CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
84 CtdlAllocUserData(SYM_SMTP_RECP, 256);
85 sprintf(SMTP_RECP, "%s", "");
87 cprintf("220 Welcome to the Citadel/UX ESMTP server at %s\r\n",
93 * Implement HELO and EHLO commands.
95 void smtp_hello(char *argbuf, int is_esmtp) {
97 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
100 cprintf("250 Greetings and joyous salutations.\r\n");
103 cprintf("250-Greetings and joyous salutations.\r\n");
104 cprintf("250-HELP\r\n");
105 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
106 cprintf("250 AUTH=LOGIN\r\n");
112 * Implement HELP command.
114 void smtp_help(void) {
115 cprintf("214-Here's the frequency, Kenneth:\r\n");
116 cprintf("214- DATA\r\n");
117 cprintf("214- EHLO\r\n");
118 cprintf("214- EXPN\r\n");
119 cprintf("214- HELO\r\n");
120 cprintf("214- HELP\r\n");
121 cprintf("214- MAIL\r\n");
122 cprintf("214- NOOP\r\n");
123 cprintf("214- QUIT\r\n");
124 cprintf("214- RCPT\r\n");
125 cprintf("214- RSET\r\n");
126 cprintf("214- VRFY\r\n");
127 cprintf("214 I could tell you more, but then I'd have to kill you.\r\n");
134 void smtp_get_user(char *argbuf) {
138 decode_base64(username, argbuf);
139 lprintf(9, "Trying <%s>\n", username);
140 if (CtdlLoginExistingUser(username) == login_ok) {
141 encode_base64(buf, "Password:");
142 cprintf("334 %s\r\n", buf);
143 SMTP->command_state = smtp_password;
146 cprintf("500 No such user.\r\n");
147 SMTP->command_state = smtp_command;
155 void smtp_get_pass(char *argbuf) {
158 decode_base64(password, argbuf);
159 lprintf(9, "Trying <%s>\n", password);
160 if (CtdlTryPassword(password) == pass_ok) {
161 cprintf("235 Authentication successful.\r\n");
162 lprintf(9, "SMTP authenticated login successful\n");
163 CC->internal_pgm = 0;
164 CC->cs_flags &= ~CS_STEALTH;
167 cprintf("500 Authentication failed.\r\n");
169 SMTP->command_state = smtp_command;
176 void smtp_auth(char *argbuf) {
179 if (strncasecmp(argbuf, "login", 5) ) {
180 cprintf("550 We only support LOGIN authentication.\r\n");
184 if (strlen(argbuf) >= 7) {
185 smtp_get_user(&argbuf[6]);
189 encode_base64(buf, "Username:");
190 cprintf("334 %s\r\n", buf);
191 SMTP->command_state = smtp_user;
197 * Back end for smtp_vrfy() command
199 void smtp_vrfy_backend(struct usersupp *us, void *data) {
201 if (!fuzzy_match(us, SMTP->vrfy_match)) {
203 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
209 * Implements the VRFY (verify user name) command.
210 * Performs fuzzy match on full user names.
212 void smtp_vrfy(char *argbuf) {
213 SMTP->vrfy_count = 0;
214 strcpy(SMTP->vrfy_match, argbuf);
215 ForEachUser(smtp_vrfy_backend, NULL);
217 if (SMTP->vrfy_count < 1) {
218 cprintf("550 String does not match anything.\r\n");
220 else if (SMTP->vrfy_count == 1) {
221 cprintf("250 %s <cit%ld@%s>\r\n",
222 SMTP->vrfy_buffer.fullname,
223 SMTP->vrfy_buffer.usernum,
226 else if (SMTP->vrfy_count > 1) {
227 cprintf("553 Request ambiguous: %d users matched.\r\n",
236 * Back end for smtp_expn() command
238 void smtp_expn_backend(struct usersupp *us, void *data) {
240 if (!fuzzy_match(us, SMTP->vrfy_match)) {
242 if (SMTP->vrfy_count >= 1) {
243 cprintf("250-%s <cit%ld@%s>\r\n",
244 SMTP->vrfy_buffer.fullname,
245 SMTP->vrfy_buffer.usernum,
250 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
256 * Implements the EXPN (expand user name) command.
257 * Performs fuzzy match on full user names.
259 void smtp_expn(char *argbuf) {
260 SMTP->vrfy_count = 0;
261 strcpy(SMTP->vrfy_match, argbuf);
262 ForEachUser(smtp_expn_backend, NULL);
264 if (SMTP->vrfy_count < 1) {
265 cprintf("550 String does not match anything.\r\n");
267 else if (SMTP->vrfy_count >= 1) {
268 cprintf("250 %s <cit%ld@%s>\r\n",
269 SMTP->vrfy_buffer.fullname,
270 SMTP->vrfy_buffer.usernum,
277 * Implements the RSET (reset state) command.
278 * Currently this just zeroes out the state buffer. If pointers to data
279 * allocated with mallok() are ever placed in the state buffer, we have to
280 * be sure to phree() them first!
282 void smtp_rset(void) {
283 memset(SMTP, 0, sizeof(struct citsmtp));
284 if (CC->logged_in) logout(CC);
285 cprintf("250 Zap!\r\n");
291 * Implements the "MAIL From:" command
293 void smtp_mail(char *argbuf) {
298 if (strlen(SMTP->from) != 0) {
299 cprintf("503 Only one sender permitted\r\n");
303 if (strncasecmp(argbuf, "From:", 5)) {
304 cprintf("501 Syntax error\r\n");
308 strcpy(SMTP->from, &argbuf[5]);
311 if (strlen(SMTP->from) == 0) {
312 cprintf("501 Empty sender name is not permitted\r\n");
317 /* If this SMTP connection is from a logged-in user, make sure that
318 * the user only sends email from his/her own address.
321 cvt = convert_internet_address(user, node, SMTP->from);
322 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
323 if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
324 cprintf("550 <%s> is not your address.\r\n", SMTP->from);
325 strcpy(SMTP->from, "");
330 /* Otherwise, make sure outsiders aren't trying to forge mail from
334 cvt = convert_internet_address(user, node, SMTP->from);
335 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
336 if (CtdlHostAlias(node) == hostalias_localhost) {
337 cprintf("550 You must log in to send mail from %s\r\n",
339 strcpy(SMTP->from, "");
344 cprintf("250 Sender ok. Groovy.\r\n");
350 * Implements the "RCPT To:" command
352 void smtp_rcpt(char *argbuf) {
357 int is_spam = 0; /* FIXME implement anti-spamming */
359 if (strlen(SMTP->from) == 0) {
360 cprintf("503 MAIL first, then RCPT. Duh.\r\n");
364 if (strncasecmp(argbuf, "To:", 3)) {
365 cprintf("501 Syntax error\r\n");
369 strcpy(recp, &argbuf[3]);
373 cvt = convert_internet_address(user, node, recp);
374 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);
708 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
711 /* Load the message out of the database into a temp file */
713 if (msg_fp == NULL) {
715 sprintf(dsn, "Error creating temporary file");
719 CtdlRedirectOutput(msg_fp, -1);
720 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
721 CtdlRedirectOutput(NULL, -1);
722 fseek(msg_fp, 0L, SEEK_END);
723 msg_size = ftell(msg_fp);
727 /* Extract something to send later in the 'MAIL From:' command */
728 strcpy(mailfrom, "");
732 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
733 if (!strncasecmp(buf, "From:", 5)) {
734 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
736 for (i=0; i<strlen(mailfrom); ++i) {
737 if (!isprint(mailfrom[i])) {
738 strcpy(&mailfrom[i], &mailfrom[i+1]);
743 /* Strip out parenthesized names */
746 for (i=0; i<strlen(mailfrom); ++i) {
747 if (mailfrom[i] == '(') lp = i;
748 if (mailfrom[i] == ')') rp = i;
750 if ((lp>0)&&(rp>lp)) {
751 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
754 /* Prefer brokketized names */
757 for (i=0; i<strlen(mailfrom); ++i) {
758 if (mailfrom[i] == '<') lp = i;
759 if (mailfrom[i] == '>') rp = i;
761 if ((lp>=0)&&(rp>lp)) {
763 strcpy(mailfrom, &mailfrom[lp]);
768 } while (scan_done == 0);
769 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
772 /* Figure out what mail exchanger host we have to connect to */
773 num_mxhosts = getmx(mxhosts, node);
774 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
775 if (num_mxhosts < 1) {
777 sprintf(dsn, "No MX hosts found for <%s>", node);
781 for (mx=0; mx<num_mxhosts; ++mx) {
782 extract(buf, mxhosts, mx);
783 lprintf(9, "Trying <%s>\n", buf);
784 sock = sock_connect(buf, "25", "tcp");
785 sprintf(dsn, "Could not connect: %s", strerror(errno));
786 if (sock >= 0) lprintf(9, "Connected!\n");
787 if (sock < 0) sprintf(dsn, "%s", strerror(errno));
788 if (sock >= 0) break;
792 *status = 4; /* dsn is already filled in */
796 /* Process the SMTP greeting from the server */
797 if (sock_gets(sock, buf) < 0) {
799 strcpy(dsn, "Connection broken during SMTP conversation");
802 lprintf(9, "<%s\n", buf);
806 strcpy(dsn, &buf[4]);
811 strcpy(dsn, &buf[4]);
816 /* At this point we know we are talking to a real SMTP server */
818 /* Do a HELO command */
819 sprintf(buf, "HELO %s", config.c_fqdn);
820 lprintf(9, ">%s\n", buf);
821 sock_puts(sock, buf);
822 if (sock_gets(sock, buf) < 0) {
824 strcpy(dsn, "Connection broken during SMTP conversation");
827 lprintf(9, "<%s\n", buf);
831 strcpy(dsn, &buf[4]);
836 strcpy(dsn, &buf[4]);
842 /* HELO succeeded, now try the MAIL From: command */
843 sprintf(buf, "MAIL From: %s", mailfrom);
844 lprintf(9, ">%s\n", buf);
845 sock_puts(sock, buf);
846 if (sock_gets(sock, buf) < 0) {
848 strcpy(dsn, "Connection broken during SMTP conversation");
851 lprintf(9, "<%s\n", buf);
855 strcpy(dsn, &buf[4]);
860 strcpy(dsn, &buf[4]);
866 /* MAIL succeeded, now try the RCPT To: command */
867 sprintf(buf, "RCPT To: %s", addr);
868 lprintf(9, ">%s\n", buf);
869 sock_puts(sock, buf);
870 if (sock_gets(sock, buf) < 0) {
872 strcpy(dsn, "Connection broken during SMTP conversation");
875 lprintf(9, "<%s\n", buf);
879 strcpy(dsn, &buf[4]);
884 strcpy(dsn, &buf[4]);
890 /* RCPT succeeded, now try the DATA command */
891 lprintf(9, ">DATA\n");
892 sock_puts(sock, "DATA");
893 if (sock_gets(sock, buf) < 0) {
895 strcpy(dsn, "Connection broken during SMTP conversation");
898 lprintf(9, "<%s\n", buf);
902 strcpy(dsn, &buf[4]);
907 strcpy(dsn, &buf[4]);
912 /* If we reach this point, the server is expecting data */
914 while (msg_size > 0) {
915 blocksize = sizeof(buf);
916 if (blocksize > msg_size) blocksize = msg_size;
917 fread(buf, blocksize, 1, msg_fp);
918 sock_write(sock, buf, blocksize);
919 msg_size -= blocksize;
921 if (buf[blocksize-1] != 10) {
922 lprintf(5, "Possible problem: message did not correctly "
923 "terminate. (expecting 0x10, got 0x%02x)\n",
927 sock_write(sock, ".\r\n", 3);
928 if (sock_gets(sock, buf) < 0) {
930 strcpy(dsn, "Connection broken during SMTP conversation");
933 lprintf(9, "%s\n", buf);
937 strcpy(dsn, &buf[4]);
942 strcpy(dsn, &buf[4]);
948 strcpy(dsn, &buf[4]);
951 lprintf(9, ">QUIT\n");
952 sock_puts(sock, "QUIT");
953 sock_gets(sock, buf);
954 lprintf(9, "<%s\n", buf);
956 bail: if (msg_fp != NULL) fclose(msg_fp);
964 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
965 * instructions for "5" codes (permanent fatal errors) and produce/deliver
966 * a "bounce" message (delivery status notification).
968 void smtp_do_bounce(char *instr) {
979 long bounce_msgid = (-1);
980 time_t submitted = 0L;
981 struct CtdlMessage *bmsg = NULL;
985 lprintf(9, "smtp_do_bounce() called\n");
986 strcpy(bounceto, "");
988 lines = num_tokens(instr, '\n');
991 /* See if it's time to give up on delivery of this message */
992 for (i=0; i<lines; ++i) {
993 extract_token(buf, instr, i, '\n');
994 extract(key, buf, 0);
995 extract(addr, buf, 1);
996 if (!strcasecmp(key, "submitted")) {
997 submitted = atol(addr);
1001 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1007 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1008 if (bmsg == NULL) return;
1009 memset(bmsg, 0, sizeof(struct CtdlMessage));
1011 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1012 bmsg->cm_anon_type = MES_NORMAL;
1013 bmsg->cm_format_type = 1;
1014 bmsg->cm_fields['A'] = strdoop("Citadel");
1015 bmsg->cm_fields['O'] = strdoop(MAILROOM);
1016 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1018 if (give_up) bmsg->cm_fields['M'] = strdoop(
1019 "A message you sent could not be delivered to some or all of its recipients.\n"
1020 "The following addresses were undeliverable:\n\n"
1023 else bmsg->cm_fields['M'] = strdoop(
1024 "A message you sent could not be delivered to some or all of its recipients\n"
1025 "due to prolonged unavailability of its destination(s).\n"
1026 "Giving up on the following addresses:\n\n"
1030 * Now go through the instructions checking for stuff.
1033 for (i=0; i<lines; ++i) {
1034 extract_token(buf, instr, i, '\n');
1035 extract(key, buf, 0);
1036 extract(addr, buf, 1);
1037 status = extract_int(buf, 2);
1038 extract(dsn, buf, 3);
1041 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1042 key, addr, status, dsn);
1044 if (!strcasecmp(key, "bounceto")) {
1045 strcpy(bounceto, addr);
1049 (!strcasecmp(key, "local"))
1050 || (!strcasecmp(key, "remote"))
1051 || (!strcasecmp(key, "ignet"))
1052 || (!strcasecmp(key, "room"))
1054 if (status == 5) bounce_this = 1;
1055 if (give_up) bounce_this = 1;
1061 if (bmsg->cm_fields['M'] == NULL) {
1062 lprintf(2, "ERROR ... M field is null "
1063 "(%s:%d)\n", __FILE__, __LINE__);
1066 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1067 strlen(bmsg->cm_fields['M']) + 1024 );
1068 strcat(bmsg->cm_fields['M'], addr);
1069 strcat(bmsg->cm_fields['M'], ": ");
1070 strcat(bmsg->cm_fields['M'], dsn);
1071 strcat(bmsg->cm_fields['M'], "\n");
1073 remove_token(instr, i, '\n');
1079 /* Deliver the bounce if there's anything worth mentioning */
1080 lprintf(9, "num_bounces = %d\n", num_bounces);
1081 if (num_bounces > 0) {
1083 /* First try the user who sent the message */
1084 lprintf(9, "bounce to user? <%s>\n", bounceto);
1085 if (strlen(bounceto) == 0) {
1086 lprintf(7, "No bounce address specified\n");
1087 bounce_msgid = (-1L);
1089 else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
1090 lprintf(7, "Invalid bounce address <%s>\n", bounceto);
1091 bounce_msgid = (-1L);
1094 bounce_msgid = CtdlSaveMsg(bmsg,
1099 /* Otherwise, go to the Aide> room */
1100 lprintf(9, "bounce to room?\n");
1101 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1106 CtdlFreeMessage(bmsg);
1107 lprintf(9, "Done processing bounces\n");
1112 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1113 * set of delivery instructions for completed deliveries and remove them.
1115 * It returns the number of incomplete deliveries remaining.
1117 int smtp_purge_completed_deliveries(char *instr) {
1128 lines = num_tokens(instr, '\n');
1129 for (i=0; i<lines; ++i) {
1130 extract_token(buf, instr, i, '\n');
1131 extract(key, buf, 0);
1132 extract(addr, buf, 1);
1133 status = extract_int(buf, 2);
1134 extract(dsn, buf, 3);
1139 (!strcasecmp(key, "local"))
1140 || (!strcasecmp(key, "remote"))
1141 || (!strcasecmp(key, "ignet"))
1142 || (!strcasecmp(key, "room"))
1144 if (status == 2) completed = 1;
1149 remove_token(instr, i, '\n');
1162 * Called by smtp_do_queue() to handle an individual message.
1164 void smtp_do_procmsg(long msgnum) {
1165 struct CtdlMessage *msg;
1167 char *results = NULL;
1175 long text_msgid = (-1);
1176 int incomplete_deliveries_remaining;
1177 time_t attempted = 0L;
1178 time_t last_attempted = 0L;
1180 msg = CtdlFetchMessage(msgnum);
1182 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1186 instr = strdoop(msg->cm_fields['M']);
1187 CtdlFreeMessage(msg);
1189 /* Strip out the headers amd any other non-instruction line */
1190 lines = num_tokens(instr, '\n');
1191 for (i=0; i<lines; ++i) {
1192 extract_token(buf, instr, i, '\n');
1193 if (num_tokens(buf, '|') < 2) {
1194 lprintf(9, "removing <%s>\n", buf);
1195 remove_token(instr, i, '\n');
1201 /* Learn the message ID and find out about recent delivery attempts */
1202 lines = num_tokens(instr, '\n');
1203 for (i=0; i<lines; ++i) {
1204 extract_token(buf, instr, i, '\n');
1205 extract(key, buf, 0);
1206 if (!strcasecmp(key, "msgid")) {
1207 text_msgid = extract_long(buf, 1);
1209 if (!strcasecmp(key, "attempted")) {
1210 attempted = extract_long(buf, 1);
1211 if (attempted > last_attempted)
1212 last_attempted = attempted;
1218 * Postpone delivery if we've already tried recently.
1220 if ( (time(NULL) - last_attempted) < SMTP_RETRY_INTERVAL) {
1221 lprintf(7, "Retry time not yet reached.\n");
1228 * Bail out if there's no actual message associated with this
1230 if (text_msgid < 0L) {
1231 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1236 /* Plow through the instructions looking for 'remote' directives and
1237 * a status of 0 (no delivery yet attempted) or 3 (transient errors
1238 * were experienced and it's time to try again)
1240 lines = num_tokens(instr, '\n');
1241 for (i=0; i<lines; ++i) {
1242 extract_token(buf, instr, i, '\n');
1243 extract(key, buf, 0);
1244 extract(addr, buf, 1);
1245 status = extract_int(buf, 2);
1246 extract(dsn, buf, 3);
1247 if ( (!strcasecmp(key, "remote"))
1248 && ((status==0)||(status==3)) ) {
1249 remove_token(instr, i, '\n');
1252 lprintf(9, "SMTP: Trying <%s>\n", addr);
1253 smtp_try(key, addr, &status, dsn, text_msgid);
1255 if (results == NULL) {
1256 results = mallok(1024);
1257 memset(results, 0, 1024);
1260 results = reallok(results,
1261 strlen(results) + 1024);
1263 sprintf(&results[strlen(results)],
1265 key, addr, status, dsn);
1270 if (results != NULL) {
1271 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1272 strcat(instr, results);
1277 /* Generate 'bounce' messages */
1278 smtp_do_bounce(instr);
1280 /* Go through the delivery list, deleting completed deliveries */
1281 incomplete_deliveries_remaining =
1282 smtp_purge_completed_deliveries(instr);
1286 * No delivery instructions remain, so delete both the instructions
1287 * message and the message message.
1289 if (incomplete_deliveries_remaining <= 0) {
1290 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1291 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, NULL);
1296 * Uncompleted delivery instructions remain, so delete the old
1297 * instructions and replace with the updated ones.
1299 if (incomplete_deliveries_remaining > 0) {
1300 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1301 msg = mallok(sizeof(struct CtdlMessage));
1302 memset(msg, 0, sizeof(struct CtdlMessage));
1303 msg->cm_magic = CTDLMESSAGE_MAGIC;
1304 msg->cm_anon_type = MES_NORMAL;
1305 msg->cm_format_type = FMT_RFC822;
1306 msg->cm_fields['M'] = malloc(strlen(instr)+256);
1307 sprintf(msg->cm_fields['M'],
1308 "Content-type: %s\n\n%s\nattempted|%ld\n",
1309 SPOOLMIME, instr, time(NULL) );
1311 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
1312 CtdlFreeMessage(msg);
1322 * Run through the queue sending out messages.
1324 void smtp_do_queue(void) {
1325 lprintf(5, "SMTP: processing outbound queue\n");
1327 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1328 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1331 CtdlForEachMessage(MSGS_ALL, 0L, SPOOLMIME, NULL, smtp_do_procmsg);
1333 lprintf(5, "SMTP: queue run completed\n");
1338 /*****************************************************************************/
1339 /* MODULE INITIALIZATION STUFF */
1340 /*****************************************************************************/
1343 char *Dynamic_Module_Init(void)
1345 SYM_SMTP = CtdlGetDynamicSymbol();
1346 SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1347 CtdlRegisterServiceHook(SMTP_PORT,
1350 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
1351 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);