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);
375 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, 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_on_citadel_network:
403 cprintf("250 %s is on the local network\r\n", recp);
404 ++SMTP->number_of_recipients;
405 CtdlReallocUserData(SYM_SMTP_RECP,
406 strlen(SMTP_RECP) + 1024 );
407 strcat(SMTP_RECP, "ignet|");
408 strcat(SMTP_RECP, user);
409 strcat(SMTP_RECP, "|");
410 strcat(SMTP_RECP, node);
411 strcat(SMTP_RECP, "|0|\n");
414 case rfc822_address_nonlocal:
416 cprintf("551 Away with thee, spammer!\r\n");
419 cprintf("250 Remote recipient %s ok\r\n", recp);
420 ++SMTP->number_of_recipients;
421 CtdlReallocUserData(SYM_SMTP_RECP,
422 strlen(SMTP_RECP) + 1024 );
423 strcat(SMTP_RECP, "remote|");
424 strcat(SMTP_RECP, recp);
425 strcat(SMTP_RECP, "|0|\n");
431 cprintf("599 Unknown error\r\n");
437 * Send a message out through the local network
438 * (This is kind of ugly. IGnet should be done using clean server-to-server
439 * code instead of the old style spool.)
441 void smtp_deliver_ignet(struct CtdlMessage *msg, char *user, char *room) {
443 char *hold_R, *hold_D;
446 hold_R = msg->cm_fields['R'];
447 hold_D = msg->cm_fields['D'];
448 msg->cm_fields['R'] = user;
449 msg->cm_fields['D'] = room;
451 serialize_message(&smr, msg);
453 msg->cm_fields['R'] = hold_R;
454 msg->cm_fields['D'] = hold_D;
457 fp = fopen(tmpnam("./network/spoolin/"), "wb");
459 fwrite(smr.ser, smr.len, 1, fp);
470 * Back end for smtp_data() ... this does the actual delivery of the message
471 * Returns 0 on success, nonzero on failure
473 int smtp_message_delivery(struct CtdlMessage *msg) {
480 int successful_saves = 0; /* number of successful local saves */
481 int failed_saves = 0; /* number of failed deliveries */
482 int remote_spools = 0; /* number of copies to send out */
485 struct usersupp userbuf;
486 char *instr; /* Remote delivery instructions */
487 struct CtdlMessage *imsg;
489 lprintf(9, "smtp_message_delivery() called\n");
491 /* Fill in 'from' fields with envelope information if missing */
492 process_rfc822_addr(SMTP->from, user, node, name);
493 if (msg->cm_fields['A']==NULL) msg->cm_fields['A'] = strdoop(user);
494 if (msg->cm_fields['N']==NULL) msg->cm_fields['N'] = strdoop(node);
495 if (msg->cm_fields['H']==NULL) msg->cm_fields['H'] = strdoop(name);
497 /* Save the message in the queue */
498 msgid = CtdlSaveMsg(msg,
505 instr = mallok(1024);
506 sprintf(instr, "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
508 SPOOLMIME, msgid, time(NULL),
511 for (i=0; i<SMTP->number_of_recipients; ++i) {
512 extract_token(buf, SMTP_RECP, i, '\n');
513 extract(dtype, buf, 0);
515 /* Stuff local mailboxes */
516 if (!strcasecmp(dtype, "local")) {
517 extract(user, buf, 1);
518 if (getuser(&userbuf, user) == 0) {
519 MailboxName(room, &userbuf, MAILROOM);
520 CtdlSaveMsgPointerInRoom(room, msgid, 0);
528 /* Delivery to local non-mailbox rooms */
529 if (!strcasecmp(dtype, "room")) {
530 extract(room, buf, 1);
531 CtdlSaveMsgPointerInRoom(room, msgid, 0);
535 /* Delivery over the local Citadel network (IGnet) */
536 if (!strcasecmp(dtype, "ignet")) {
537 smtp_deliver_ignet(msg, user, room);
540 /* Remote delivery */
541 if (!strcasecmp(dtype, "remote")) {
542 extract(user, buf, 1);
543 instr = reallok(instr, strlen(instr) + 1024);
544 sprintf(&instr[strlen(instr)],
552 /* If there are remote spools to be done, save the instructions */
553 if (remote_spools > 0) {
554 imsg = mallok(sizeof(struct CtdlMessage));
555 memset(imsg, 0, sizeof(struct CtdlMessage));
556 imsg->cm_magic = CTDLMESSAGE_MAGIC;
557 imsg->cm_anon_type = MES_NORMAL;
558 imsg->cm_format_type = FMT_RFC822;
559 imsg->cm_fields['M'] = instr;
560 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
561 CtdlFreeMessage(imsg);
564 /* If there are no remote spools, delete the message */
566 phree(instr); /* only needed here, because CtdlSaveMsg()
567 * would free this buffer otherwise */
568 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, NULL);
571 return(failed_saves);
577 * Implements the DATA command
579 void smtp_data(void) {
581 struct CtdlMessage *msg;
585 if (strlen(SMTP->from) == 0) {
586 cprintf("503 Need MAIL command first.\r\n");
590 if (SMTP->number_of_recipients < 1) {
591 cprintf("503 Need RCPT command first.\r\n");
595 cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
597 generate_rfc822_datestamp(nowstamp, time(NULL));
600 if (body != NULL) sprintf(body,
601 "Received: from %s\n"
608 body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
610 cprintf("550 Unable to save message text: internal error.\r\n");
614 lprintf(9, "Converting message...\n");
615 msg = convert_internet_message(body);
617 /* If the user is locally authenticated, FORCE the From: header to
618 * show up as the real sender
621 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
622 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
623 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
624 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
625 msg->cm_fields['N'] = strdoop(config.c_nodename);
626 msg->cm_fields['H'] = strdoop(config.c_humannode);
629 retval = smtp_message_delivery(msg);
630 CtdlFreeMessage(msg);
633 cprintf("250 Message accepted for delivery.\r\n");
636 cprintf("550 Internal delivery errors: %d\r\n", retval);
644 * Main command loop for SMTP sessions.
646 void smtp_command_loop(void) {
650 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
651 if (client_gets(cmdbuf) < 1) {
652 lprintf(3, "SMTP socket is broken. Ending session.\n");
656 lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
657 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
659 if (SMTP->command_state == smtp_user) {
660 smtp_get_user(cmdbuf);
663 else if (SMTP->command_state == smtp_password) {
664 smtp_get_pass(cmdbuf);
667 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
668 smtp_auth(&cmdbuf[5]);
671 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
675 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
676 smtp_hello(&cmdbuf[5], 1);
679 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
680 smtp_expn(&cmdbuf[5]);
683 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
684 smtp_hello(&cmdbuf[5], 0);
687 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
691 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
692 smtp_mail(&cmdbuf[5]);
695 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
696 cprintf("250 This command successfully did nothing.\r\n");
699 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
700 cprintf("221 Goodbye...\r\n");
705 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
706 smtp_rcpt(&cmdbuf[5]);
709 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
713 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
714 smtp_vrfy(&cmdbuf[5]);
718 cprintf("502 I'm sorry Dave, I'm afraid I can't do that.\r\n");
726 /*****************************************************************************/
727 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
728 /*****************************************************************************/
735 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
738 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
745 char user[256], node[256], name[256];
751 size_t blocksize = 0;
754 /* Parse out the host portion of the recipient address */
755 process_rfc822_addr(addr, user, node, name);
757 lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
760 /* Load the message out of the database into a temp file */
762 if (msg_fp == NULL) {
764 sprintf(dsn, "Error creating temporary file");
768 CtdlRedirectOutput(msg_fp, -1);
769 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
770 CtdlRedirectOutput(NULL, -1);
771 fseek(msg_fp, 0L, SEEK_END);
772 msg_size = ftell(msg_fp);
776 /* Extract something to send later in the 'MAIL From:' command */
777 strcpy(mailfrom, "");
781 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
782 if (!strncasecmp(buf, "From:", 5)) {
783 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
785 for (i=0; i<strlen(mailfrom); ++i) {
786 if (!isprint(mailfrom[i])) {
787 strcpy(&mailfrom[i], &mailfrom[i+1]);
792 /* Strip out parenthesized names */
795 for (i=0; i<strlen(mailfrom); ++i) {
796 if (mailfrom[i] == '(') lp = i;
797 if (mailfrom[i] == ')') rp = i;
799 if ((lp>0)&&(rp>lp)) {
800 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
803 /* Prefer brokketized names */
806 for (i=0; i<strlen(mailfrom); ++i) {
807 if (mailfrom[i] == '<') lp = i;
808 if (mailfrom[i] == '>') rp = i;
810 if ((lp>=0)&&(rp>lp)) {
812 strcpy(mailfrom, &mailfrom[lp]);
817 } while (scan_done == 0);
818 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
821 /* Figure out what mail exchanger host we have to connect to */
822 num_mxhosts = getmx(mxhosts, node);
823 lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
824 if (num_mxhosts < 1) {
826 sprintf(dsn, "No MX hosts found for <%s>", node);
830 for (mx=0; mx<num_mxhosts; ++mx) {
831 extract(buf, mxhosts, mx);
832 lprintf(9, "Trying <%s>\n", buf);
833 sock = sock_connect(buf, "25", "tcp");
834 sprintf(dsn, "Could not connect: %s", strerror(errno));
835 if (sock >= 0) lprintf(9, "Connected!\n");
836 if (sock < 0) sprintf(dsn, "%s", strerror(errno));
837 if (sock >= 0) break;
841 *status = 4; /* dsn is already filled in */
845 /* Process the SMTP greeting from the server */
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]);
865 /* At this point we know we are talking to a real SMTP server */
867 /* Do a HELO command */
868 sprintf(buf, "HELO %s", config.c_fqdn);
869 lprintf(9, ">%s\n", buf);
870 sock_puts(sock, buf);
871 if (sock_gets(sock, buf) < 0) {
873 strcpy(dsn, "Connection broken during SMTP conversation");
876 lprintf(9, "<%s\n", buf);
880 strcpy(dsn, &buf[4]);
885 strcpy(dsn, &buf[4]);
891 /* HELO succeeded, now try the MAIL From: command */
892 sprintf(buf, "MAIL From: %s", mailfrom);
893 lprintf(9, ">%s\n", buf);
894 sock_puts(sock, buf);
895 if (sock_gets(sock, buf) < 0) {
897 strcpy(dsn, "Connection broken during SMTP conversation");
900 lprintf(9, "<%s\n", buf);
904 strcpy(dsn, &buf[4]);
909 strcpy(dsn, &buf[4]);
915 /* MAIL succeeded, now try the RCPT To: command */
916 sprintf(buf, "RCPT To: %s", addr);
917 lprintf(9, ">%s\n", buf);
918 sock_puts(sock, buf);
919 if (sock_gets(sock, buf) < 0) {
921 strcpy(dsn, "Connection broken during SMTP conversation");
924 lprintf(9, "<%s\n", buf);
928 strcpy(dsn, &buf[4]);
933 strcpy(dsn, &buf[4]);
939 /* RCPT succeeded, now try the DATA command */
940 lprintf(9, ">DATA\n");
941 sock_puts(sock, "DATA");
942 if (sock_gets(sock, buf) < 0) {
944 strcpy(dsn, "Connection broken during SMTP conversation");
947 lprintf(9, "<%s\n", buf);
951 strcpy(dsn, &buf[4]);
956 strcpy(dsn, &buf[4]);
961 /* If we reach this point, the server is expecting data */
963 while (msg_size > 0) {
964 blocksize = sizeof(buf);
965 if (blocksize > msg_size) blocksize = msg_size;
966 fread(buf, blocksize, 1, msg_fp);
967 sock_write(sock, buf, blocksize);
968 msg_size -= blocksize;
970 if (buf[blocksize-1] != 10) {
971 lprintf(5, "Possible problem: message did not correctly "
972 "terminate. (expecting 0x10, got 0x%02x)\n",
976 sock_write(sock, ".\r\n", 3);
977 if (sock_gets(sock, buf) < 0) {
979 strcpy(dsn, "Connection broken during SMTP conversation");
982 lprintf(9, "%s\n", buf);
986 strcpy(dsn, &buf[4]);
991 strcpy(dsn, &buf[4]);
997 strcpy(dsn, &buf[4]);
1000 lprintf(9, ">QUIT\n");
1001 sock_puts(sock, "QUIT");
1002 sock_gets(sock, buf);
1003 lprintf(9, "<%s\n", buf);
1005 bail: if (msg_fp != NULL) fclose(msg_fp);
1013 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1014 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1015 * a "bounce" message (delivery status notification).
1017 void smtp_do_bounce(char *instr) {
1025 char bounceto[1024];
1026 int num_bounces = 0;
1027 int bounce_this = 0;
1028 long bounce_msgid = (-1);
1029 time_t submitted = 0L;
1030 struct CtdlMessage *bmsg = NULL;
1034 lprintf(9, "smtp_do_bounce() called\n");
1035 strcpy(bounceto, "");
1037 lines = num_tokens(instr, '\n');
1040 /* See if it's time to give up on delivery of this message */
1041 for (i=0; i<lines; ++i) {
1042 extract_token(buf, instr, i, '\n');
1043 extract(key, buf, 0);
1044 extract(addr, buf, 1);
1045 if (!strcasecmp(key, "submitted")) {
1046 submitted = atol(addr);
1050 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1056 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1057 if (bmsg == NULL) return;
1058 memset(bmsg, 0, sizeof(struct CtdlMessage));
1060 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1061 bmsg->cm_anon_type = MES_NORMAL;
1062 bmsg->cm_format_type = 1;
1063 bmsg->cm_fields['A'] = strdoop("Citadel");
1064 bmsg->cm_fields['O'] = strdoop(MAILROOM);
1065 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1067 if (give_up) bmsg->cm_fields['M'] = strdoop(
1068 "A message you sent could not be delivered to some or all of its recipients.\n"
1069 "The following addresses were undeliverable:\n\n"
1072 else bmsg->cm_fields['M'] = strdoop(
1073 "A message you sent could not be delivered to some or all of its recipients\n"
1074 "due to prolonged unavailability of its destination(s).\n"
1075 "Giving up on the following addresses:\n\n"
1079 * Now go through the instructions checking for stuff.
1082 for (i=0; i<lines; ++i) {
1083 extract_token(buf, instr, i, '\n');
1084 extract(key, buf, 0);
1085 extract(addr, buf, 1);
1086 status = extract_int(buf, 2);
1087 extract(dsn, buf, 3);
1090 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1091 key, addr, status, dsn);
1093 if (!strcasecmp(key, "bounceto")) {
1094 strcpy(bounceto, addr);
1098 (!strcasecmp(key, "local"))
1099 || (!strcasecmp(key, "remote"))
1100 || (!strcasecmp(key, "ignet"))
1101 || (!strcasecmp(key, "room"))
1103 if (status == 5) bounce_this = 1;
1104 if (give_up) bounce_this = 1;
1110 if (bmsg->cm_fields['M'] == NULL) {
1111 lprintf(2, "ERROR ... M field is null "
1112 "(%s:%d)\n", __FILE__, __LINE__);
1115 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1116 strlen(bmsg->cm_fields['M']) + 1024 );
1117 strcat(bmsg->cm_fields['M'], addr);
1118 strcat(bmsg->cm_fields['M'], ": ");
1119 strcat(bmsg->cm_fields['M'], dsn);
1120 strcat(bmsg->cm_fields['M'], "\n");
1122 remove_token(instr, i, '\n');
1128 /* Deliver the bounce if there's anything worth mentioning */
1129 lprintf(9, "num_bounces = %d\n", num_bounces);
1130 if (num_bounces > 0) {
1132 /* First try the user who sent the message */
1133 lprintf(9, "bounce to user? <%s>\n", bounceto);
1134 if (strlen(bounceto) == 0) {
1135 lprintf(7, "No bounce address specified\n");
1136 bounce_msgid = (-1L);
1138 else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
1139 lprintf(7, "Invalid bounce address <%s>\n", bounceto);
1140 bounce_msgid = (-1L);
1143 bounce_msgid = CtdlSaveMsg(bmsg,
1148 /* Otherwise, go to the Aide> room */
1149 lprintf(9, "bounce to room?\n");
1150 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1155 CtdlFreeMessage(bmsg);
1156 lprintf(9, "Done processing bounces\n");
1161 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1162 * set of delivery instructions for completed deliveries and remove them.
1164 * It returns the number of incomplete deliveries remaining.
1166 int smtp_purge_completed_deliveries(char *instr) {
1177 lines = num_tokens(instr, '\n');
1178 for (i=0; i<lines; ++i) {
1179 extract_token(buf, instr, i, '\n');
1180 extract(key, buf, 0);
1181 extract(addr, buf, 1);
1182 status = extract_int(buf, 2);
1183 extract(dsn, buf, 3);
1188 (!strcasecmp(key, "local"))
1189 || (!strcasecmp(key, "remote"))
1190 || (!strcasecmp(key, "ignet"))
1191 || (!strcasecmp(key, "room"))
1193 if (status == 2) completed = 1;
1198 remove_token(instr, i, '\n');
1211 * Called by smtp_do_queue() to handle an individual message.
1213 void smtp_do_procmsg(long msgnum) {
1214 struct CtdlMessage *msg;
1216 char *results = NULL;
1224 long text_msgid = (-1);
1225 int incomplete_deliveries_remaining;
1226 time_t attempted = 0L;
1227 time_t last_attempted = 0L;
1229 msg = CtdlFetchMessage(msgnum);
1231 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1235 instr = strdoop(msg->cm_fields['M']);
1236 CtdlFreeMessage(msg);
1238 /* Strip out the headers amd any other non-instruction line */
1239 lines = num_tokens(instr, '\n');
1240 for (i=0; i<lines; ++i) {
1241 extract_token(buf, instr, i, '\n');
1242 if (num_tokens(buf, '|') < 2) {
1243 lprintf(9, "removing <%s>\n", buf);
1244 remove_token(instr, i, '\n');
1250 /* Learn the message ID and find out about recent delivery attempts */
1251 lines = num_tokens(instr, '\n');
1252 for (i=0; i<lines; ++i) {
1253 extract_token(buf, instr, i, '\n');
1254 extract(key, buf, 0);
1255 if (!strcasecmp(key, "msgid")) {
1256 text_msgid = extract_long(buf, 1);
1258 if (!strcasecmp(key, "attempted")) {
1259 attempted = extract_long(buf, 1);
1260 if (attempted > last_attempted)
1261 last_attempted = attempted;
1267 * Postpone delivery if we've already tried recently.
1269 if ( (time(NULL) - last_attempted) < SMTP_RETRY_INTERVAL) {
1270 lprintf(7, "Retry time not yet reached.\n");
1277 * Bail out if there's no actual message associated with this
1279 if (text_msgid < 0L) {
1280 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1285 /* Plow through the instructions looking for 'remote' directives and
1286 * a status of 0 (no delivery yet attempted) or 3 (transient errors
1287 * were experienced and it's time to try again)
1289 lines = num_tokens(instr, '\n');
1290 for (i=0; i<lines; ++i) {
1291 extract_token(buf, instr, i, '\n');
1292 extract(key, buf, 0);
1293 extract(addr, buf, 1);
1294 status = extract_int(buf, 2);
1295 extract(dsn, buf, 3);
1296 if ( (!strcasecmp(key, "remote"))
1297 && ((status==0)||(status==3)) ) {
1298 remove_token(instr, i, '\n');
1301 lprintf(9, "SMTP: Trying <%s>\n", addr);
1302 smtp_try(key, addr, &status, dsn, text_msgid);
1304 if (results == NULL) {
1305 results = mallok(1024);
1306 memset(results, 0, 1024);
1309 results = reallok(results,
1310 strlen(results) + 1024);
1312 sprintf(&results[strlen(results)],
1314 key, addr, status, dsn);
1319 if (results != NULL) {
1320 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1321 strcat(instr, results);
1326 /* Generate 'bounce' messages */
1327 smtp_do_bounce(instr);
1329 /* Go through the delivery list, deleting completed deliveries */
1330 incomplete_deliveries_remaining =
1331 smtp_purge_completed_deliveries(instr);
1335 * No delivery instructions remain, so delete both the instructions
1336 * message and the message message.
1338 if (incomplete_deliveries_remaining <= 0) {
1339 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1340 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, NULL);
1345 * Uncompleted delivery instructions remain, so delete the old
1346 * instructions and replace with the updated ones.
1348 if (incomplete_deliveries_remaining > 0) {
1349 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1350 msg = mallok(sizeof(struct CtdlMessage));
1351 memset(msg, 0, sizeof(struct CtdlMessage));
1352 msg->cm_magic = CTDLMESSAGE_MAGIC;
1353 msg->cm_anon_type = MES_NORMAL;
1354 msg->cm_format_type = FMT_RFC822;
1355 msg->cm_fields['M'] = malloc(strlen(instr)+256);
1356 sprintf(msg->cm_fields['M'],
1357 "Content-type: %s\n\n%s\nattempted|%ld\n",
1358 SPOOLMIME, instr, time(NULL) );
1360 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
1361 CtdlFreeMessage(msg);
1371 * Run through the queue sending out messages.
1373 void smtp_do_queue(void) {
1374 static int doing_queue = 0;
1377 * This is a simple concurrency check to make sure only one queue run
1378 * is done at a time. We could do this with a mutex, but since we
1379 * don't really require extremely fine granularity here, we'll do it
1380 * with a static variable instead.
1382 if (doing_queue) return;
1386 * Go ahead and run the queue
1388 lprintf(5, "SMTP: processing outbound queue\n");
1390 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1391 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1394 CtdlForEachMessage(MSGS_ALL, 0L, SPOOLMIME, NULL, smtp_do_procmsg);
1396 lprintf(5, "SMTP: queue run completed\n");
1402 /*****************************************************************************/
1403 /* MODULE INITIALIZATION STUFF */
1404 /*****************************************************************************/
1407 char *Dynamic_Module_Init(void)
1409 SYM_SMTP = CtdlGetDynamicSymbol();
1410 SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1411 CtdlRegisterServiceHook(SMTP_PORT,
1414 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
1415 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);