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; /* FIX 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 struct CtdlMessage *bmsg = NULL;
981 lprintf(9, "smtp_do_bounce() called\n");
982 strcpy(bounceto, "");
984 bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
985 if (bmsg == NULL) return;
986 memset(bmsg, 0, sizeof(struct CtdlMessage));
988 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
989 bmsg->cm_anon_type = MES_NORMAL;
990 bmsg->cm_format_type = 1;
991 bmsg->cm_fields['A'] = strdoop("Citadel");
992 bmsg->cm_fields['N'] = strdoop(config.c_nodename);
993 bmsg->cm_fields['M'] = strdoop(
994 "BOUNCE! BOUNCE!! BOUNCE!!!\n\n"
995 "FIX ... this message should be made to look nice and stuff.\n"
996 "In the meantime, you should be aware that the following\n"
997 "recipient addresses had permanent fatal errors:\n\n");
999 lines = num_tokens(instr, '\n');
1000 for (i=0; i<lines; ++i) {
1001 extract_token(buf, instr, i, '\n');
1002 extract(key, buf, 0);
1003 extract(addr, buf, 1);
1004 status = extract_int(buf, 2);
1005 extract(dsn, buf, 3);
1008 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1009 key, addr, status, dsn);
1011 if (!strcasecmp(key, "bounceto")) {
1012 strcpy(bounceto, addr);
1016 (!strcasecmp(key, "local"))
1017 || (!strcasecmp(key, "remote"))
1018 || (!strcasecmp(key, "ignet"))
1019 || (!strcasecmp(key, "room"))
1021 if (status == 5) bounce_this = 1;
1027 /* FIX put this back in!
1028 bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1029 strlen(bmsg->cm_fields['M'] + 1024) );
1030 strcat(bmsg->cm_fields['M'], addr);
1031 strcat(bmsg->cm_fields['M'], ": ");
1032 strcat(bmsg->cm_fields['M'], dsn);
1033 strcat(bmsg->cm_fields['M'], "\n");
1036 remove_token(instr, i, '\n');
1042 /* Deliver the bounce if there's anything worth mentioning */
1043 lprintf(9, "num_bounces = %d\n", num_bounces);
1044 if (num_bounces > 0) {
1046 /* First try the user who sent the message FIX
1047 lprintf(9, "bounce to user? <%s>\n", bounceto);
1048 if (strlen(bounceto) == 0) bounce_msgid = (-1L);
1049 else bounce_msgid = CtdlSaveMsg(bmsg,
1054 /* Otherwise, go to the Aide> room */
1055 lprintf(9, "bounce to room?\n");
1056 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1061 CtdlFreeMessage(bmsg);
1062 lprintf(9, "Done processing bounces\n");
1067 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1068 * set of delivery instructions for completed deliveries and remove them.
1070 * It returns the number of incomplete deliveries remaining.
1072 int smtp_purge_completed_deliveries(char *instr) {
1083 lines = num_tokens(instr, '\n');
1084 for (i=0; i<lines; ++i) {
1085 extract_token(buf, instr, i, '\n');
1086 extract(key, buf, 0);
1087 extract(addr, buf, 1);
1088 status = extract_int(buf, 2);
1089 extract(dsn, buf, 3);
1094 (!strcasecmp(key, "local"))
1095 || (!strcasecmp(key, "remote"))
1096 || (!strcasecmp(key, "ignet"))
1097 || (!strcasecmp(key, "room"))
1099 if (status == 2) completed = 1;
1104 remove_token(instr, i, '\n');
1117 * Called by smtp_do_queue() to handle an individual message.
1119 void smtp_do_procmsg(long msgnum) {
1120 struct CtdlMessage *msg;
1122 char *results = NULL;
1130 long text_msgid = (-1);
1131 int incomplete_deliveries_remaining;
1133 msg = CtdlFetchMessage(msgnum);
1135 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1139 instr = strdoop(msg->cm_fields['M']);
1140 CtdlFreeMessage(msg);
1142 /* Strip out the headers amd any other non-instruction line */
1143 lines = num_tokens(instr, '\n');
1144 for (i=0; i<lines; ++i) {
1145 extract_token(buf, instr, i, '\n');
1146 if (num_tokens(buf, '|') < 2) {
1147 lprintf(9, "removing <%s>\n", buf);
1148 remove_token(instr, i, '\n');
1154 /* Learn the message ID */
1155 lines = num_tokens(instr, '\n');
1156 for (i=0; i<lines; ++i) {
1157 extract_token(buf, instr, i, '\n');
1158 extract(key, buf, 0);
1159 if (!strcasecmp(key, "msgid")) {
1160 text_msgid = extract_long(buf, 1);
1164 if (text_msgid < 0L) {
1165 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1170 /* Plow through the instructions looking for 'remote' directives and
1171 * a status of 0 (no delivery yet attempted) or 3 (transient errors
1172 * were experienced and it's time to try again)
1174 lines = num_tokens(instr, '\n');
1175 for (i=0; i<lines; ++i) {
1176 extract_token(buf, instr, i, '\n');
1177 extract(key, buf, 0);
1178 extract(addr, buf, 1);
1179 status = extract_int(buf, 2);
1180 extract(dsn, buf, 3);
1181 if ( (!strcasecmp(key, "remote"))
1182 && ((status==0)||(status==3)) ) {
1183 remove_token(instr, i, '\n');
1186 lprintf(9, "SMTP: Trying <%s>\n", addr);
1187 smtp_try(key, addr, &status, dsn, text_msgid);
1189 if (results == NULL) {
1190 results = mallok(1024);
1191 memset(results, 0, 1024);
1194 results = reallok(results,
1195 strlen(results) + 1024);
1197 sprintf(&results[strlen(results)],
1199 key, addr, status, dsn);
1204 if (results != NULL) {
1205 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1206 strcat(instr, results);
1211 /* Generate 'bounce' messages */
1212 smtp_do_bounce(instr);
1214 /* Go through the delivery list, deleting completed deliveries */
1215 incomplete_deliveries_remaining =
1216 smtp_purge_completed_deliveries(instr);
1220 * No delivery instructions remain, so delete both the instructions
1221 * message and the message message.
1223 if (incomplete_deliveries_remaining <= 0) {
1224 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1225 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, NULL);
1230 * Uncompleted delivery instructions remain, so delete the old
1231 * instructions and replace with the updated ones.
1233 if (incomplete_deliveries_remaining > 0) {
1234 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
1235 msg = mallok(sizeof(struct CtdlMessage));
1236 memset(msg, 0, sizeof(struct CtdlMessage));
1237 msg->cm_magic = CTDLMESSAGE_MAGIC;
1238 msg->cm_anon_type = MES_NORMAL;
1239 msg->cm_format_type = FMT_RFC822;
1240 msg->cm_fields['M'] = malloc(strlen(instr)+256);
1241 sprintf(msg->cm_fields['M'],
1242 "Content-type: %s\n\n%s\nattempted|%ld\n",
1243 SPOOLMIME, instr, time(NULL) );
1245 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
1246 CtdlFreeMessage(msg);
1256 * Run through the queue sending out messages.
1258 void smtp_do_queue(void) {
1259 lprintf(5, "SMTP: processing outbound queue\n");
1261 if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1262 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1265 CtdlForEachMessage(MSGS_ALL, 0L, SPOOLMIME, NULL, smtp_do_procmsg);
1267 lprintf(5, "SMTP: queue run completed\n");
1272 /*****************************************************************************/
1273 /* MODULE INITIALIZATION STUFF */
1274 /*****************************************************************************/
1277 char *Dynamic_Module_Init(void)
1279 SYM_SMTP = CtdlGetDynamicSymbol();
1280 SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1281 CtdlRegisterServiceHook(SMTP_PORT,
1284 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
1285 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);