11 #include <sys/types.h>
20 #include "sysdep_decls.h"
21 #include "citserver.h"
24 #include "dynloader.h"
31 #include "internet_addressing.h"
34 #include "clientsocket.h"
37 struct citsmtp { /* Information about the current session */
40 struct usersupp vrfy_buffer;
44 int number_of_recipients;
48 enum { /* Command states for login authentication */
54 enum { /* Delivery modes */
59 #define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
60 #define SMTP_RECP ((char *)CtdlGetUserData(SYM_SMTP_RECP))
67 /*****************************************************************************/
68 /* SMTP SERVER (INBOUND) STUFF */
69 /*****************************************************************************/
75 * Here's where our SMTP session begins its happy day.
77 void smtp_greeting(void) {
79 strcpy(CC->cs_clientname, "SMTP session");
81 CC->cs_flags |= CS_STEALTH;
82 CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
83 CtdlAllocUserData(SYM_SMTP_RECP, 256);
84 sprintf(SMTP_RECP, "%s", "");
86 cprintf("220 Welcome to the Citadel/UX ESMTP server at %s\r\n",
92 * Implement HELO and EHLO commands.
94 void smtp_hello(char *argbuf, int is_esmtp) {
96 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
99 cprintf("250 Greetings and joyous salutations.\r\n");
102 cprintf("250-Greetings and joyous salutations.\r\n");
103 cprintf("250-HELP\r\n");
104 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
105 cprintf("250 AUTH=LOGIN\r\n");
111 * Implement HELP command.
113 void smtp_help(void) {
114 cprintf("214-Here's the frequency, Kenneth:\r\n");
115 cprintf("214- DATA\r\n");
116 cprintf("214- EHLO\r\n");
117 cprintf("214- EXPN\r\n");
118 cprintf("214- HELO\r\n");
119 cprintf("214- HELP\r\n");
120 cprintf("214- MAIL\r\n");
121 cprintf("214- NOOP\r\n");
122 cprintf("214- QUIT\r\n");
123 cprintf("214- RCPT\r\n");
124 cprintf("214- RSET\r\n");
125 cprintf("214- VRFY\r\n");
126 cprintf("214 I could tell you more, but then I'd have to kill you.\r\n");
133 void smtp_get_user(char *argbuf) {
137 decode_base64(username, argbuf);
138 lprintf(9, "Trying <%s>\n", username);
139 if (CtdlLoginExistingUser(username) == login_ok) {
140 encode_base64(buf, "Password:");
141 cprintf("334 %s\r\n", buf);
142 SMTP->command_state = smtp_password;
145 cprintf("500 No such user.\r\n");
146 SMTP->command_state = smtp_command;
154 void smtp_get_pass(char *argbuf) {
157 decode_base64(password, argbuf);
158 lprintf(9, "Trying <%s>\n", password);
159 if (CtdlTryPassword(password) == pass_ok) {
160 cprintf("235 Authentication successful.\r\n");
161 lprintf(9, "SMTP authenticated login successful\n");
162 CC->internal_pgm = 0;
163 CC->cs_flags &= ~CS_STEALTH;
166 cprintf("500 Authentication failed.\r\n");
168 SMTP->command_state = smtp_command;
175 void smtp_auth(char *argbuf) {
178 if (strncasecmp(argbuf, "login", 5) ) {
179 cprintf("550 We only support LOGIN authentication.\r\n");
183 if (strlen(argbuf) >= 7) {
184 smtp_get_user(&argbuf[6]);
188 encode_base64(buf, "Username:");
189 cprintf("334 %s\r\n", buf);
190 SMTP->command_state = smtp_user;
196 * Back end for smtp_vrfy() command
198 void smtp_vrfy_backend(struct usersupp *us, void *data) {
200 if (!fuzzy_match(us, SMTP->vrfy_match)) {
202 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
208 * Implements the VRFY (verify user name) command.
209 * Performs fuzzy match on full user names.
211 void smtp_vrfy(char *argbuf) {
212 SMTP->vrfy_count = 0;
213 strcpy(SMTP->vrfy_match, argbuf);
214 ForEachUser(smtp_vrfy_backend, NULL);
216 if (SMTP->vrfy_count < 1) {
217 cprintf("550 String does not match anything.\r\n");
219 else if (SMTP->vrfy_count == 1) {
220 cprintf("250 %s <cit%ld@%s>\r\n",
221 SMTP->vrfy_buffer.fullname,
222 SMTP->vrfy_buffer.usernum,
225 else if (SMTP->vrfy_count > 1) {
226 cprintf("553 Request ambiguous: %d users matched.\r\n",
235 * Back end for smtp_expn() command
237 void smtp_expn_backend(struct usersupp *us, void *data) {
239 if (!fuzzy_match(us, SMTP->vrfy_match)) {
241 if (SMTP->vrfy_count >= 1) {
242 cprintf("250-%s <cit%ld@%s>\r\n",
243 SMTP->vrfy_buffer.fullname,
244 SMTP->vrfy_buffer.usernum,
249 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
255 * Implements the EXPN (expand user name) command.
256 * Performs fuzzy match on full user names.
258 void smtp_expn(char *argbuf) {
259 SMTP->vrfy_count = 0;
260 strcpy(SMTP->vrfy_match, argbuf);
261 ForEachUser(smtp_expn_backend, NULL);
263 if (SMTP->vrfy_count < 1) {
264 cprintf("550 String does not match anything.\r\n");
266 else if (SMTP->vrfy_count >= 1) {
267 cprintf("250 %s <cit%ld@%s>\r\n",
268 SMTP->vrfy_buffer.fullname,
269 SMTP->vrfy_buffer.usernum,
276 * Implements the RSET (reset state) command.
277 * Currently this just zeroes out the state buffer. If pointers to data
278 * allocated with mallok() are ever placed in the state buffer, we have to
279 * be sure to phree() them first!
281 void smtp_rset(void) {
282 memset(SMTP, 0, sizeof(struct citsmtp));
283 if (CC->logged_in) logout(CC);
284 cprintf("250 Zap!\r\n");
290 * Implements the "MAIL From:" command
292 void smtp_mail(char *argbuf) {
297 if (strlen(SMTP->from) != 0) {
298 cprintf("503 Only one sender permitted\r\n");
302 if (strncasecmp(argbuf, "From:", 5)) {
303 cprintf("501 Syntax error\r\n");
307 strcpy(SMTP->from, &argbuf[5]);
310 if (strlen(SMTP->from) == 0) {
311 cprintf("501 Empty sender name is not permitted\r\n");
316 /* If this SMTP connection is from a logged-in user, make sure that
317 * the user only sends email from his/her own address.
320 cvt = convert_internet_address(user, node, SMTP->from);
321 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
322 if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
323 cprintf("550 <%s> is not your address.\r\n", SMTP->from);
324 strcpy(SMTP->from, "");
329 /* Otherwise, make sure outsiders aren't trying to forge mail from
333 cvt = convert_internet_address(user, node, SMTP->from);
334 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
335 if (CtdlHostAlias(node) == hostalias_localhost) {
336 cprintf("550 You must log in to send mail from %s\r\n",
338 strcpy(SMTP->from, "");
343 cprintf("250 Sender ok. Groovy.\r\n");
349 * Implements the "RCPT To:" command
351 void smtp_rcpt(char *argbuf) {
356 int is_spam = 0; /* FIXME implement anti-spamming */
358 if (strlen(SMTP->from) == 0) {
359 cprintf("503 MAIL first, then RCPT. Duh.\r\n");
363 if (strncasecmp(argbuf, "To:", 3)) {
364 cprintf("501 Syntax error\r\n");
368 strcpy(recp, &argbuf[3]);
372 cvt = convert_internet_address(user, node, recp);
373 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;
983 lprintf(9, "smtp_do_bounce() called\n");
984 strcpy(bounceto, "");
986 lines = num_tokens(instr, '\n');
989 /* See if it's time to give up on delivery of this message */
990 for (i=0; i<lines; ++i) {
991 extract_token(buf, instr, i, '\n');
992 extract(key, buf, 0);
993 extract(addr, buf, 1);
994 if (!strcasecmp(key, "submitted")) {
995 submitted = atol(addr);
999 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1005 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1006 if (bmsg == NULL) return;
1007 memset(bmsg, 0, sizeof(struct CtdlMessage));
1009 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1010 bmsg->cm_anon_type = MES_NORMAL;
1011 bmsg->cm_format_type = 1;
1012 bmsg->cm_fields['A'] = strdoop("Citadel");
1013 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1015 if (give_up) bmsg->cm_fields['M'] = strdoop(
1016 "BOUNCE! BOUNCE!! BOUNCE!!!\n\n"
1017 "FIXME ... this message should be made to look nice and stuff.\n"
1018 "In the meantime, you should be aware that we're giving up on the\n"
1019 "following deliveries because their mail servers are fux0red and\n"
1020 "would not accept the message for a very, very long time:\n\n"
1023 else bmsg->cm_fields['M'] = strdoop(
1024 "BOUNCE! BOUNCE!! BOUNCE!!!\n\n"
1025 "FIXME ... this message should be made to look nice and stuff.\n"
1026 "In the meantime, you should be aware that the following\n"
1027 "recipient addresses had permanent fatal errors:\n\n"
1033 * Now go through the instructions checking for stuff.
1036 for (i=0; i<lines; ++i) {
1037 extract_token(buf, instr, i, '\n');
1038 extract(key, buf, 0);
1039 extract(addr, buf, 1);
1040 status = extract_int(buf, 2);
1041 extract(dsn, buf, 3);
1044 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1045 key, addr, status, dsn);
1047 if (!strcasecmp(key, "bounceto")) {
1048 strcpy(bounceto, addr);
1052 (!strcasecmp(key, "local"))
1053 || (!strcasecmp(key, "remote"))
1054 || (!strcasecmp(key, "ignet"))
1055 || (!strcasecmp(key, "room"))
1057 if (status == 5) bounce_this = 1;
1058 if (give_up) bounce_this = 1;
1064 if (bmsg->cm_fields['M'] == NULL) {
1065 lprintf(2, "ERROR ... M field is null "
1066 "(%s:%d)\n", __FILE__, __LINE__);
1069 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1070 strlen(bmsg->cm_fields['M']) + 1024 );
1071 strcat(bmsg->cm_fields['M'], addr);
1072 strcat(bmsg->cm_fields['M'], ": ");
1073 strcat(bmsg->cm_fields['M'], dsn);
1074 strcat(bmsg->cm_fields['M'], "\n");
1076 remove_token(instr, i, '\n');
1082 /* Deliver the bounce if there's anything worth mentioning */
1083 lprintf(9, "num_bounces = %d\n", num_bounces);
1084 if (num_bounces > 0) {
1086 /* First try the user who sent the message */
1087 lprintf(9, "bounce to user? <%s>\n", bounceto);
1088 if (strlen(bounceto) == 0) bounce_msgid = (-1L);
1089 else bounce_msgid = CtdlSaveMsg(bmsg,
1093 /* Otherwise, go to the Aide> room */
1094 lprintf(9, "bounce to room?\n");
1095 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1100 CtdlFreeMessage(bmsg);
1101 lprintf(9, "Done processing bounces\n");
1106 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1107 * set of delivery instructions for completed deliveries and remove them.
1109 * It returns the number of incomplete deliveries remaining.
1111 int smtp_purge_completed_deliveries(char *instr) {
1122 lines = num_tokens(instr, '\n');
1123 for (i=0; i<lines; ++i) {
1124 extract_token(buf, instr, i, '\n');
1125 extract(key, buf, 0);
1126 extract(addr, buf, 1);
1127 status = extract_int(buf, 2);
1128 extract(dsn, buf, 3);
1133 (!strcasecmp(key, "local"))
1134 || (!strcasecmp(key, "remote"))
1135 || (!strcasecmp(key, "ignet"))
1136 || (!strcasecmp(key, "room"))
1138 if (status == 2) completed = 1;
1143 remove_token(instr, i, '\n');
1156 * Called by smtp_do_queue() to handle an individual message.
1158 void smtp_do_procmsg(long msgnum) {
1159 struct CtdlMessage *msg;
1161 char *results = NULL;
1169 long text_msgid = (-1);
1170 int incomplete_deliveries_remaining;
1171 time_t attempted = 0L;
1172 time_t last_attempted = 0L;
1174 msg = CtdlFetchMessage(msgnum);
1176 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1180 instr = strdoop(msg->cm_fields['M']);
1181 CtdlFreeMessage(msg);
1183 /* Strip out the headers amd any other non-instruction line */
1184 lines = num_tokens(instr, '\n');
1185 for (i=0; i<lines; ++i) {
1186 extract_token(buf, instr, i, '\n');
1187 if (num_tokens(buf, '|') < 2) {
1188 lprintf(9, "removing <%s>\n", buf);
1189 remove_token(instr, i, '\n');
1195 /* Learn the message ID and find out about recent delivery attempts */
1196 lines = num_tokens(instr, '\n');
1197 for (i=0; i<lines; ++i) {
1198 extract_token(buf, instr, i, '\n');
1199 extract(key, buf, 0);
1200 if (!strcasecmp(key, "msgid")) {
1201 text_msgid = extract_long(buf, 1);
1203 if (!strcasecmp(key, "attempted")) {
1204 attempted = extract_long(buf, 1);
1205 if (attempted > last_attempted)
1206 last_attempted = attempted;
1212 * Postpone delivery if we've already tried recently.
1214 if ( (time(NULL) - last_attempted) < SMTP_RETRY_INTERVAL) {
1215 lprintf(7, "Retry time not yet reached.\n");
1222 * Bail out if there's no actual message associated with this
1224 if (text_msgid < 0L) {
1225 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1230 /* Plow through the instructions looking for 'remote' directives and
1231 * a status of 0 (no delivery yet attempted) or 3 (transient errors
1232 * were experienced and it's time to try again)
1234 lines = num_tokens(instr, '\n');
1235 for (i=0; i<lines; ++i) {
1236 extract_token(buf, instr, i, '\n');
1237 extract(key, buf, 0);
1238 extract(addr, buf, 1);
1239 status = extract_int(buf, 2);
1240 extract(dsn, buf, 3);
1241 if ( (!strcasecmp(key, "remote"))
1242 && ((status==0)||(status==3)) ) {
1243 remove_token(instr, i, '\n');
1246 lprintf(9, "SMTP: Trying <%s>\n", addr);
1247 smtp_try(key, addr, &status, dsn, text_msgid);
1249 if (results == NULL) {
1250 results = mallok(1024);
1251 memset(results, 0, 1024);
1254 results = reallok(results,
1255 strlen(results) + 1024);
1257 sprintf(&results[strlen(results)],
1259 key, addr, status, dsn);
1264 if (results != NULL) {
1265 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1266 strcat(instr, results);
1271 /* Generate 'bounce' messages */
1272 smtp_do_bounce(instr);
1274 /* Go through the delivery list, deleting completed deliveries */
1275 incomplete_deliveries_remaining =
1276 smtp_purge_completed_deliveries(instr);
1280 * No delivery instructions remain, so delete both the instructions
1281 * message and the message message.
1283 if (incomplete_deliveries_remaining <= 0) {
1284 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1285 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, NULL);
1290 * Uncompleted delivery instructions remain, so delete the old
1291 * instructions and replace with the updated ones.
1293 if (incomplete_deliveries_remaining > 0) {
1294 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1295 msg = mallok(sizeof(struct CtdlMessage));
1296 memset(msg, 0, sizeof(struct CtdlMessage));
1297 msg->cm_magic = CTDLMESSAGE_MAGIC;
1298 msg->cm_anon_type = MES_NORMAL;
1299 msg->cm_format_type = FMT_RFC822;
1300 msg->cm_fields['M'] = malloc(strlen(instr)+256);
1301 sprintf(msg->cm_fields['M'],
1302 "Content-type: %s\n\n%s\nattempted|%ld\n",
1303 SPOOLMIME, instr, time(NULL) );
1305 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
1306 CtdlFreeMessage(msg);
1316 * Run through the queue sending out messages.
1318 void smtp_do_queue(void) {
1319 lprintf(5, "SMTP: processing outbound queue\n");
1321 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1322 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1325 CtdlForEachMessage(MSGS_ALL, 0L, SPOOLMIME, NULL, smtp_do_procmsg);
1327 lprintf(5, "SMTP: queue run completed\n");
1332 /*****************************************************************************/
1333 /* MODULE INITIALIZATION STUFF */
1334 /*****************************************************************************/
1337 char *Dynamic_Module_Init(void)
1339 SYM_SMTP = CtdlGetDynamicSymbol();
1340 SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1341 CtdlRegisterServiceHook(SMTP_PORT,
1344 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
1345 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);