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);
377 case rfc822_address_locally_validated:
378 cprintf("250 %s is a valid recipient.\r\n", user);
379 ++SMTP->number_of_recipients;
380 CtdlReallocUserData(SYM_SMTP_RECP,
381 strlen(SMTP_RECP) + 1024 );
382 strcat(SMTP_RECP, "local|");
383 strcat(SMTP_RECP, user);
384 strcat(SMTP_RECP, "|0\n");
387 case rfc822_room_delivery:
388 cprintf("250 Delivering to room '%s'\r\n", user);
389 ++SMTP->number_of_recipients;
390 CtdlReallocUserData(SYM_SMTP_RECP,
391 strlen(SMTP_RECP) + 1024 );
392 strcat(SMTP_RECP, "room|");
393 strcat(SMTP_RECP, user);
394 strcat(SMTP_RECP, "|0|\n");
397 case rfc822_no_such_user:
398 cprintf("550 %s: no such user\r\n", recp);
401 case rfc822_address_invalid:
403 cprintf("551 Away with thee, spammer!\r\n");
406 cprintf("250 Remote recipient %s ok\r\n", recp);
407 ++SMTP->number_of_recipients;
408 CtdlReallocUserData(SYM_SMTP_RECP,
409 strlen(SMTP_RECP) + 1024 );
410 strcat(SMTP_RECP, "remote|");
411 strcat(SMTP_RECP, recp);
412 strcat(SMTP_RECP, "|0|\n");
418 cprintf("599 Unknown error\r\n");
426 * Back end for smtp_data() ... this does the actual delivery of the message
427 * Returns 0 on success, nonzero on failure
429 int smtp_message_delivery(struct CtdlMessage *msg) {
436 int successful_saves = 0; /* number of successful local saves */
437 int failed_saves = 0; /* number of failed deliveries */
438 int remote_spools = 0; /* number of copies to send out */
441 struct usersupp userbuf;
442 char *instr; /* Remote delivery instructions */
443 struct CtdlMessage *imsg;
445 lprintf(9, "smtp_message_delivery() called\n");
447 /* Fill in 'from' fields with envelope information if missing */
448 process_rfc822_addr(SMTP->from, user, node, name);
449 if (msg->cm_fields['A']==NULL) msg->cm_fields['A'] = strdoop(user);
450 if (msg->cm_fields['N']==NULL) msg->cm_fields['N'] = strdoop(node);
451 if (msg->cm_fields['H']==NULL) msg->cm_fields['H'] = strdoop(name);
453 /* Save the message in the queue */
454 msgid = CtdlSaveMsg(msg,
461 instr = mallok(1024);
462 sprintf(instr, "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
464 SPOOLMIME, msgid, time(NULL),
467 for (i=0; i<SMTP->number_of_recipients; ++i) {
468 extract_token(buf, SMTP_RECP, i, '\n');
469 extract(dtype, buf, 0);
471 /* Stuff local mailboxes */
472 if (!strcasecmp(dtype, "local")) {
473 extract(user, buf, 1);
474 if (getuser(&userbuf, user) == 0) {
475 MailboxName(room, &userbuf, MAILROOM);
476 CtdlSaveMsgPointerInRoom(room, msgid, 0);
484 /* Delivery to local non-mailbox rooms */
485 if (!strcasecmp(dtype, "room")) {
486 extract(room, buf, 1);
487 CtdlSaveMsgPointerInRoom(room, msgid, 0);
491 /* Remote delivery */
492 if (!strcasecmp(dtype, "remote")) {
493 extract(user, buf, 1);
494 instr = reallok(instr, strlen(instr) + 1024);
495 sprintf(&instr[strlen(instr)],
503 /* If there are remote spools to be done, save the instructions */
504 if (remote_spools > 0) {
505 imsg = mallok(sizeof(struct CtdlMessage));
506 memset(imsg, 0, sizeof(struct CtdlMessage));
507 imsg->cm_magic = CTDLMESSAGE_MAGIC;
508 imsg->cm_anon_type = MES_NORMAL;
509 imsg->cm_format_type = FMT_RFC822;
510 imsg->cm_fields['M'] = instr;
511 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
512 CtdlFreeMessage(imsg);
515 /* If there are no remote spools, delete the message */
517 phree(instr); /* only needed here, because CtdlSaveMsg()
518 * would free this buffer otherwise */
519 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, NULL);
522 return(failed_saves);
528 * Implements the DATA command
530 void smtp_data(void) {
532 struct CtdlMessage *msg;
536 if (strlen(SMTP->from) == 0) {
537 cprintf("503 Need MAIL command first.\r\n");
541 if (SMTP->number_of_recipients < 1) {
542 cprintf("503 Need RCPT command first.\r\n");
546 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
548 generate_rfc822_datestamp(nowstamp, time(NULL));
551 if (body != NULL) sprintf(body,
552 "Received: from %s\n"
559 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
561 cprintf("550 Unable to save message text: internal error.\r\n");
565 lprintf(9, "Converting message...\n");
566 msg = convert_internet_message(body);
568 /* If the user is locally authenticated, FORCE the From: header to
569 * show up as the real sender
572 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
573 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
574 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
575 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
576 msg->cm_fields['N'] = strdoop(config.c_nodename);
577 msg->cm_fields['H'] = strdoop(config.c_humannode);
580 retval = smtp_message_delivery(msg);
581 CtdlFreeMessage(msg);
584 cprintf("250 Message accepted for delivery.\r\n");
587 cprintf("550 Internal delivery errors: %d\r\n", retval);
595 * Main command loop for SMTP sessions.
597 void smtp_command_loop(void) {
601 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
602 if (client_gets(cmdbuf) < 1) {
603 lprintf(3, "SMTP socket is broken. Ending session.\n");
607 lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
608 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
610 if (SMTP->command_state == smtp_user) {
611 smtp_get_user(cmdbuf);
614 else if (SMTP->command_state == smtp_password) {
615 smtp_get_pass(cmdbuf);
618 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
619 smtp_auth(&cmdbuf[5]);
622 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
626 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
627 smtp_hello(&cmdbuf[5], 1);
630 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
631 smtp_expn(&cmdbuf[5]);
634 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
635 smtp_hello(&cmdbuf[5], 0);
638 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
642 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
643 smtp_mail(&cmdbuf[5]);
646 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
647 cprintf("250 This command successfully did nothing.\r\n");
650 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
651 cprintf("221 Goodbye...\r\n");
656 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
657 smtp_rcpt(&cmdbuf[5]);
660 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
664 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
665 smtp_vrfy(&cmdbuf[5]);
669 cprintf("502 I'm sorry Dave, I'm afraid I can't do that.\r\n");
677 /*****************************************************************************/
678 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
679 /*****************************************************************************/
686 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
689 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
696 char user[256], node[256], name[256];
702 size_t blocksize = 0;
705 /* Parse out the host portion of the recipient address */
706 process_rfc822_addr(addr, user, node, name);
707 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
710 /* Load the message out of the database into a temp file */
712 if (msg_fp == NULL) {
714 sprintf(dsn, "Error creating temporary file");
718 CtdlRedirectOutput(msg_fp, -1);
719 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
720 CtdlRedirectOutput(NULL, -1);
721 fseek(msg_fp, 0L, SEEK_END);
722 msg_size = ftell(msg_fp);
726 /* Extract something to send later in the 'MAIL From:' command */
727 strcpy(mailfrom, "");
731 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
732 if (!strncasecmp(buf, "From:", 5)) {
733 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
735 for (i=0; i<strlen(mailfrom); ++i) {
736 if (!isprint(mailfrom[i])) {
737 strcpy(&mailfrom[i], &mailfrom[i+1]);
742 /* Strip out parenthesized names */
745 for (i=0; i<strlen(mailfrom); ++i) {
746 if (mailfrom[i] == '(') lp = i;
747 if (mailfrom[i] == ')') rp = i;
749 if ((lp>0)&&(rp>lp)) {
750 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
753 /* Prefer brokketized names */
756 for (i=0; i<strlen(mailfrom); ++i) {
757 if (mailfrom[i] == '<') lp = i;
758 if (mailfrom[i] == '>') rp = i;
760 if ((lp>=0)&&(rp>lp)) {
762 strcpy(mailfrom, &mailfrom[lp]);
767 } while (scan_done == 0);
768 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
771 /* Figure out what mail exchanger host we have to connect to */
772 num_mxhosts = getmx(mxhosts, node);
773 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
774 if (num_mxhosts < 1) {
776 sprintf(dsn, "No MX hosts found for <%s>", node);
780 for (mx=0; mx<num_mxhosts; ++mx) {
781 extract(buf, mxhosts, mx);
782 lprintf(9, "Trying <%s>\n", buf);
783 sock = sock_connect(buf, "25", "tcp");
784 sprintf(dsn, "Could not connect: %s", strerror(errno));
785 if (sock >= 0) lprintf(9, "Connected!\n");
786 if (sock < 0) sprintf(dsn, "%s", strerror(errno));
787 if (sock >= 0) break;
791 *status = 4; /* dsn is already filled in */
795 /* Process the SMTP greeting from the server */
796 if (sock_gets(sock, buf) < 0) {
798 strcpy(dsn, "Connection broken during SMTP conversation");
801 lprintf(9, "<%s\n", buf);
805 strcpy(dsn, &buf[4]);
810 strcpy(dsn, &buf[4]);
815 /* At this point we know we are talking to a real SMTP server */
817 /* Do a HELO command */
818 sprintf(buf, "HELO %s", config.c_fqdn);
819 lprintf(9, ">%s\n", buf);
820 sock_puts(sock, buf);
821 if (sock_gets(sock, buf) < 0) {
823 strcpy(dsn, "Connection broken during SMTP conversation");
826 lprintf(9, "<%s\n", buf);
830 strcpy(dsn, &buf[4]);
835 strcpy(dsn, &buf[4]);
841 /* HELO succeeded, now try the MAIL From: command */
842 sprintf(buf, "MAIL From: %s", mailfrom);
843 lprintf(9, ">%s\n", buf);
844 sock_puts(sock, buf);
845 if (sock_gets(sock, buf) < 0) {
847 strcpy(dsn, "Connection broken during SMTP conversation");
850 lprintf(9, "<%s\n", buf);
854 strcpy(dsn, &buf[4]);
859 strcpy(dsn, &buf[4]);
865 /* MAIL succeeded, now try the RCPT To: command */
866 sprintf(buf, "RCPT To: %s", addr);
867 lprintf(9, ">%s\n", buf);
868 sock_puts(sock, buf);
869 if (sock_gets(sock, buf) < 0) {
871 strcpy(dsn, "Connection broken during SMTP conversation");
874 lprintf(9, "<%s\n", buf);
878 strcpy(dsn, &buf[4]);
883 strcpy(dsn, &buf[4]);
889 /* RCPT succeeded, now try the DATA command */
890 lprintf(9, ">DATA\n");
891 sock_puts(sock, "DATA");
892 if (sock_gets(sock, buf) < 0) {
894 strcpy(dsn, "Connection broken during SMTP conversation");
897 lprintf(9, "<%s\n", buf);
901 strcpy(dsn, &buf[4]);
906 strcpy(dsn, &buf[4]);
911 /* If we reach this point, the server is expecting data */
913 while (msg_size > 0) {
914 blocksize = sizeof(buf);
915 if (blocksize > msg_size) blocksize = msg_size;
916 fread(buf, blocksize, 1, msg_fp);
917 sock_write(sock, buf, blocksize);
918 msg_size -= blocksize;
920 if (buf[blocksize-1] != 10) {
921 lprintf(5, "Possible problem: message did not correctly "
922 "terminate. (expecting 0x10, got 0x%02x)\n",
926 sock_write(sock, ".\r\n", 3);
927 if (sock_gets(sock, buf) < 0) {
929 strcpy(dsn, "Connection broken during SMTP conversation");
932 lprintf(9, "%s\n", buf);
936 strcpy(dsn, &buf[4]);
941 strcpy(dsn, &buf[4]);
947 strcpy(dsn, &buf[4]);
950 lprintf(9, ">QUIT\n");
951 sock_puts(sock, "QUIT");
952 sock_gets(sock, buf);
953 lprintf(9, "<%s\n", buf);
955 bail: if (msg_fp != NULL) fclose(msg_fp);
963 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
964 * instructions for "5" codes (permanent fatal errors) and produce/deliver
965 * a "bounce" message (delivery status notification).
967 void smtp_do_bounce(char *instr) {
978 long bounce_msgid = (-1);
979 time_t submitted = 0L;
980 struct CtdlMessage *bmsg = NULL;
984 lprintf(9, "smtp_do_bounce() called\n");
985 strcpy(bounceto, "");
987 lines = num_tokens(instr, '\n');
990 /* See if it's time to give up on delivery of this message */
991 for (i=0; i<lines; ++i) {
992 extract_token(buf, instr, i, '\n');
993 extract(key, buf, 0);
994 extract(addr, buf, 1);
995 if (!strcasecmp(key, "submitted")) {
996 submitted = atol(addr);
1000 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1006 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1007 if (bmsg == NULL) return;
1008 memset(bmsg, 0, sizeof(struct CtdlMessage));
1010 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1011 bmsg->cm_anon_type = MES_NORMAL;
1012 bmsg->cm_format_type = 1;
1013 bmsg->cm_fields['A'] = strdoop("Citadel");
1014 bmsg->cm_fields['O'] = strdoop(MAILROOM);
1015 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1017 if (give_up) bmsg->cm_fields['M'] = strdoop(
1018 "A message you sent could not be delivered to some or all of its recipients.\n"
1019 "The following addresses were undeliverable:\n\n"
1022 else bmsg->cm_fields['M'] = strdoop(
1023 "A message you sent could not be delivered to some or all of its recipients\n"
1024 "due to prolonged unavailability of its destination(s).\n"
1025 "Giving up on the following addresses:\n\n"
1029 * Now go through the instructions checking for stuff.
1032 for (i=0; i<lines; ++i) {
1033 extract_token(buf, instr, i, '\n');
1034 extract(key, buf, 0);
1035 extract(addr, buf, 1);
1036 status = extract_int(buf, 2);
1037 extract(dsn, buf, 3);
1040 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1041 key, addr, status, dsn);
1043 if (!strcasecmp(key, "bounceto")) {
1044 strcpy(bounceto, addr);
1048 (!strcasecmp(key, "local"))
1049 || (!strcasecmp(key, "remote"))
1050 || (!strcasecmp(key, "ignet"))
1051 || (!strcasecmp(key, "room"))
1053 if (status == 5) bounce_this = 1;
1054 if (give_up) bounce_this = 1;
1060 if (bmsg->cm_fields['M'] == NULL) {
1061 lprintf(2, "ERROR ... M field is null "
1062 "(%s:%d)\n", __FILE__, __LINE__);
1065 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1066 strlen(bmsg->cm_fields['M']) + 1024 );
1067 strcat(bmsg->cm_fields['M'], addr);
1068 strcat(bmsg->cm_fields['M'], ": ");
1069 strcat(bmsg->cm_fields['M'], dsn);
1070 strcat(bmsg->cm_fields['M'], "\n");
1072 remove_token(instr, i, '\n');
1078 /* Deliver the bounce if there's anything worth mentioning */
1079 lprintf(9, "num_bounces = %d\n", num_bounces);
1080 if (num_bounces > 0) {
1082 /* First try the user who sent the message */
1083 lprintf(9, "bounce to user? <%s>\n", bounceto);
1084 if (strlen(bounceto) == 0) {
1085 lprintf(7, "No bounce address specified\n");
1086 bounce_msgid = (-1L);
1088 else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
1089 lprintf(7, "Invalid bounce address <%s>\n", bounceto);
1090 bounce_msgid = (-1L);
1093 bounce_msgid = CtdlSaveMsg(bmsg,
1098 /* Otherwise, go to the Aide> room */
1099 lprintf(9, "bounce to room?\n");
1100 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1105 CtdlFreeMessage(bmsg);
1106 lprintf(9, "Done processing bounces\n");
1111 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1112 * set of delivery instructions for completed deliveries and remove them.
1114 * It returns the number of incomplete deliveries remaining.
1116 int smtp_purge_completed_deliveries(char *instr) {
1127 lines = num_tokens(instr, '\n');
1128 for (i=0; i<lines; ++i) {
1129 extract_token(buf, instr, i, '\n');
1130 extract(key, buf, 0);
1131 extract(addr, buf, 1);
1132 status = extract_int(buf, 2);
1133 extract(dsn, buf, 3);
1138 (!strcasecmp(key, "local"))
1139 || (!strcasecmp(key, "remote"))
1140 || (!strcasecmp(key, "ignet"))
1141 || (!strcasecmp(key, "room"))
1143 if (status == 2) completed = 1;
1148 remove_token(instr, i, '\n');
1161 * Called by smtp_do_queue() to handle an individual message.
1163 void smtp_do_procmsg(long msgnum) {
1164 struct CtdlMessage *msg;
1166 char *results = NULL;
1174 long text_msgid = (-1);
1175 int incomplete_deliveries_remaining;
1176 time_t attempted = 0L;
1177 time_t last_attempted = 0L;
1179 msg = CtdlFetchMessage(msgnum);
1181 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1185 instr = strdoop(msg->cm_fields['M']);
1186 CtdlFreeMessage(msg);
1188 /* Strip out the headers amd any other non-instruction line */
1189 lines = num_tokens(instr, '\n');
1190 for (i=0; i<lines; ++i) {
1191 extract_token(buf, instr, i, '\n');
1192 if (num_tokens(buf, '|') < 2) {
1193 lprintf(9, "removing <%s>\n", buf);
1194 remove_token(instr, i, '\n');
1200 /* Learn the message ID and find out about recent delivery attempts */
1201 lines = num_tokens(instr, '\n');
1202 for (i=0; i<lines; ++i) {
1203 extract_token(buf, instr, i, '\n');
1204 extract(key, buf, 0);
1205 if (!strcasecmp(key, "msgid")) {
1206 text_msgid = extract_long(buf, 1);
1208 if (!strcasecmp(key, "attempted")) {
1209 attempted = extract_long(buf, 1);
1210 if (attempted > last_attempted)
1211 last_attempted = attempted;
1217 * Postpone delivery if we've already tried recently.
1219 if ( (time(NULL) - last_attempted) < SMTP_RETRY_INTERVAL) {
1220 lprintf(7, "Retry time not yet reached.\n");
1227 * Bail out if there's no actual message associated with this
1229 if (text_msgid < 0L) {
1230 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1235 /* Plow through the instructions looking for 'remote' directives and
1236 * a status of 0 (no delivery yet attempted) or 3 (transient errors
1237 * were experienced and it's time to try again)
1239 lines = num_tokens(instr, '\n');
1240 for (i=0; i<lines; ++i) {
1241 extract_token(buf, instr, i, '\n');
1242 extract(key, buf, 0);
1243 extract(addr, buf, 1);
1244 status = extract_int(buf, 2);
1245 extract(dsn, buf, 3);
1246 if ( (!strcasecmp(key, "remote"))
1247 && ((status==0)||(status==3)) ) {
1248 remove_token(instr, i, '\n');
1251 lprintf(9, "SMTP: Trying <%s>\n", addr);
1252 smtp_try(key, addr, &status, dsn, text_msgid);
1254 if (results == NULL) {
1255 results = mallok(1024);
1256 memset(results, 0, 1024);
1259 results = reallok(results,
1260 strlen(results) + 1024);
1262 sprintf(&results[strlen(results)],
1264 key, addr, status, dsn);
1269 if (results != NULL) {
1270 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1271 strcat(instr, results);
1276 /* Generate 'bounce' messages */
1277 smtp_do_bounce(instr);
1279 /* Go through the delivery list, deleting completed deliveries */
1280 incomplete_deliveries_remaining =
1281 smtp_purge_completed_deliveries(instr);
1285 * No delivery instructions remain, so delete both the instructions
1286 * message and the message message.
1288 if (incomplete_deliveries_remaining <= 0) {
1289 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1290 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, NULL);
1295 * Uncompleted delivery instructions remain, so delete the old
1296 * instructions and replace with the updated ones.
1298 if (incomplete_deliveries_remaining > 0) {
1299 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1300 msg = mallok(sizeof(struct CtdlMessage));
1301 memset(msg, 0, sizeof(struct CtdlMessage));
1302 msg->cm_magic = CTDLMESSAGE_MAGIC;
1303 msg->cm_anon_type = MES_NORMAL;
1304 msg->cm_format_type = FMT_RFC822;
1305 msg->cm_fields['M'] = malloc(strlen(instr)+256);
1306 sprintf(msg->cm_fields['M'],
1307 "Content-type: %s\n\n%s\nattempted|%ld\n",
1308 SPOOLMIME, instr, time(NULL) );
1310 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
1311 CtdlFreeMessage(msg);
1321 * Run through the queue sending out messages.
1323 void smtp_do_queue(void) {
1324 lprintf(5, "SMTP: processing outbound queue\n");
1326 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1327 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1330 CtdlForEachMessage(MSGS_ALL, 0L, SPOOLMIME, NULL, smtp_do_procmsg);
1332 lprintf(5, "SMTP: queue run completed\n");
1337 /*****************************************************************************/
1338 /* MODULE INITIALIZATION STUFF */
1339 /*****************************************************************************/
1342 char *Dynamic_Module_Init(void)
1344 SYM_SMTP = CtdlGetDynamicSymbol();
1345 SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1346 CtdlRegisterServiceHook(SMTP_PORT,
1349 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
1350 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);