]> code.citadel.org Git - citadel.git/blob - citadel/serv_smtp.c
* serv_smtp.c: validated and documented compliance with a big pile of RFC's
[citadel.git] / citadel / serv_smtp.c
1 /*
2  * $Id$
3  *
4  * This module is an SMTP and ESMTP implementation for the Citadel system.
5  * It is compliant with all of the following:
6  *
7  * RFC  821 - Simple Mail Transfer Protocol
8  * RFC  876 - Survey of SMTP Implementations
9  * RFC 1047 - Duplicate messages and SMTP
10  * RFC 1854 - command pipelining
11  * RFC 1869 - Extended Simple Mail Transfer Protocol
12  * RFC 1870 - SMTP Service Extension for Message Size Declaration
13  * RFC 1893 - Enhanced Mail System Status Codes
14  * RFC 2033 - Local Mail Transfer Protocol
15  * RFC 2034 - SMTP Service Extension for Returning Enhanced Error Codes
16  * RFC 2197 - SMTP Service Extension for Command Pipelining
17  * RFC 2554 - SMTP Service Extension for Authentication
18  * RFC 2821 - Simple Mail Transfer Protocol
19  * RFC 2822 - Internet Message Format
20  * RFC 2920 - SMTP Service Extension for Command Pipelining
21  *
22  */
23
24 #include "sysdep.h"
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <pwd.h>
31 #include <errno.h>
32 #include <sys/types.h>
33
34 #if TIME_WITH_SYS_TIME
35 # include <sys/time.h>
36 # include <time.h>
37 #else
38 # if HAVE_SYS_TIME_H
39 #  include <sys/time.h>
40 # else
41 #  include <time.h>
42 # endif
43 #endif
44
45 #include <sys/wait.h>
46 #include <ctype.h>
47 #include <string.h>
48 #include <limits.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
52 #include "citadel.h"
53 #include "server.h"
54 #include "sysdep_decls.h"
55 #include "citserver.h"
56 #include "support.h"
57 #include "config.h"
58 #include "control.h"
59 #include "serv_extensions.h"
60 #include "room_ops.h"
61 #include "user_ops.h"
62 #include "policy.h"
63 #include "database.h"
64 #include "msgbase.h"
65 #include "tools.h"
66 #include "internet_addressing.h"
67 #include "genstamp.h"
68 #include "domain.h"
69 #include "clientsocket.h"
70 #include "locate_host.h"
71
72
73 #ifndef HAVE_SNPRINTF
74 #include "snprintf.h"
75 #endif
76
77 struct citsmtp {                /* Information about the current session */
78         int command_state;
79         char helo_node[SIZ];
80         struct ctdluser vrfy_buffer;
81         int vrfy_count;
82         char vrfy_match[SIZ];
83         char from[SIZ];
84         char recipients[SIZ];
85         int number_of_recipients;
86         int delivery_mode;
87         int message_originated_locally;
88         int is_lmtp;
89 };
90
91 enum {                          /* Command states for login authentication */
92         smtp_command,
93         smtp_user,
94         smtp_password
95 };
96
97 enum {                          /* Delivery modes */
98         smtp_deliver_local,
99         smtp_deliver_remote
100 };
101
102 #define SMTP            ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
103 #define SMTP_RECPS      ((char *)CtdlGetUserData(SYM_SMTP_RECPS))
104 #define SMTP_ROOMS      ((char *)CtdlGetUserData(SYM_SMTP_ROOMS))
105
106
107 int run_queue_now = 0;  /* Set to 1 to ignore SMTP send retry times */
108
109
110
111 /*****************************************************************************/
112 /*                      SMTP SERVER (INBOUND) STUFF                          */
113 /*****************************************************************************/
114
115
116
117
118 /*
119  * Here's where our SMTP session begins its happy day.
120  */
121 void smtp_greeting(void) {
122
123         strcpy(CC->cs_clientname, "SMTP session");
124         CC->internal_pgm = 1;
125         CC->cs_flags |= CS_STEALTH;
126         CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
127         CtdlAllocUserData(SYM_SMTP_RECPS, SIZ);
128         CtdlAllocUserData(SYM_SMTP_ROOMS, SIZ);
129         snprintf(SMTP_RECPS, SIZ, "%s", "");
130         snprintf(SMTP_ROOMS, SIZ, "%s", "");
131
132         cprintf("220 %s ESMTP Citadel/UX server ready.\r\n", config.c_fqdn);
133 }
134
135 /*
136  * LMTP is like SMTP but with some extra bonus footage added.
137  */
138 void lmtp_greeting(void) {
139         smtp_greeting();
140         SMTP->is_lmtp = 1;
141 }
142
143
144 /*
145  * Implement HELO and EHLO commands.
146  *
147  * which_command:  0=HELO, 1=EHLO, 2=LHLO
148  */
149 void smtp_hello(char *argbuf, int which_command) {
150
151         safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
152
153         if ( (which_command != 2) && (SMTP->is_lmtp) ) {
154                 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
155                 return;
156         }
157
158         if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
159                 cprintf("500 LHLO is only allowed when running LMTP\r\n");
160                 return;
161         }
162
163         if (which_command == 0) {
164                 cprintf("250 Greetings and joyous salutations.\r\n");
165         }
166         else {
167                 cprintf("250-Greetings and joyous salutations.\r\n");
168                 cprintf("250-HELP\r\n");
169                 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
170                 cprintf("250-PIPELINING\r\n");
171                 cprintf("250-AUTH=LOGIN\r\n");
172                 cprintf("250 ENHANCEDSTATUSCODES\r\n");
173         }
174 }
175
176
177 /*
178  * Implement HELP command.
179  */
180 void smtp_help(void) {
181         cprintf("214-Commands accepted:\r\n");
182         cprintf("214-    DATA\r\n");
183         cprintf("214-    EHLO\r\n");
184         cprintf("214-    EXPN\r\n");
185         cprintf("214-    HELO\r\n");
186         cprintf("214-    HELP\r\n");
187         cprintf("214-    MAIL\r\n");
188         cprintf("214-    NOOP\r\n");
189         cprintf("214-    QUIT\r\n");
190         cprintf("214-    RCPT\r\n");
191         cprintf("214-    RSET\r\n");
192         cprintf("214-    VRFY\r\n");
193         cprintf("214     \r\n");
194 }
195
196
197 /*
198  *
199  */
200 void smtp_get_user(char *argbuf) {
201         char buf[SIZ];
202         char username[SIZ];
203
204         CtdlDecodeBase64(username, argbuf, SIZ);
205         lprintf(9, "Trying <%s>\n", username);
206         if (CtdlLoginExistingUser(username) == login_ok) {
207                 CtdlEncodeBase64(buf, "Password:", 9);
208                 cprintf("334 %s\r\n", buf);
209                 SMTP->command_state = smtp_password;
210         }
211         else {
212                 cprintf("500 5.7.0 No such user.\r\n");
213                 SMTP->command_state = smtp_command;
214         }
215 }
216
217
218 /*
219  *
220  */
221 void smtp_get_pass(char *argbuf) {
222         char password[SIZ];
223
224         CtdlDecodeBase64(password, argbuf, SIZ);
225         lprintf(9, "Trying <%s>\n", password);
226         if (CtdlTryPassword(password) == pass_ok) {
227                 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
228                 lprintf(9, "SMTP authenticated login successful\n");
229                 CC->internal_pgm = 0;
230                 CC->cs_flags &= ~CS_STEALTH;
231         }
232         else {
233                 cprintf("535 5.7.0 Authentication failed.\r\n");
234         }
235         SMTP->command_state = smtp_command;
236 }
237
238
239 /*
240  *
241  */
242 void smtp_auth(char *argbuf) {
243         char buf[SIZ];
244
245         if (strncasecmp(argbuf, "login", 5) ) {
246                 cprintf("504 5.7.4 We only support LOGIN authentication.\r\n");
247                 return;
248         }
249
250         if (strlen(argbuf) >= 7) {
251                 smtp_get_user(&argbuf[6]);
252         }
253
254         else {
255                 CtdlEncodeBase64(buf, "Username:", 9);
256                 cprintf("334 %s\r\n", buf);
257                 SMTP->command_state = smtp_user;
258         }
259 }
260
261
262 /*
263  * Back end for smtp_vrfy() command
264  */
265 void smtp_vrfy_backend(struct ctdluser *us, void *data) {
266
267         if (!fuzzy_match(us, SMTP->vrfy_match)) {
268                 ++SMTP->vrfy_count;
269                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
270         }
271 }
272
273
274 /* 
275  * Implements the VRFY (verify user name) command.
276  * Performs fuzzy match on full user names.
277  */
278 void smtp_vrfy(char *argbuf) {
279         SMTP->vrfy_count = 0;
280         strcpy(SMTP->vrfy_match, argbuf);
281         ForEachUser(smtp_vrfy_backend, NULL);
282
283         if (SMTP->vrfy_count < 1) {
284                 cprintf("550 5.1.1 String does not match anything.\r\n");
285         }
286         else if (SMTP->vrfy_count == 1) {
287                 cprintf("250 %s <cit%ld@%s>\r\n",
288                         SMTP->vrfy_buffer.fullname,
289                         SMTP->vrfy_buffer.usernum,
290                         config.c_fqdn);
291         }
292         else if (SMTP->vrfy_count > 1) {
293                 cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
294                         SMTP->vrfy_count);
295         }
296
297 }
298
299
300
301 /*
302  * Back end for smtp_expn() command
303  */
304 void smtp_expn_backend(struct ctdluser *us, void *data) {
305
306         if (!fuzzy_match(us, SMTP->vrfy_match)) {
307
308                 if (SMTP->vrfy_count >= 1) {
309                         cprintf("250-%s <cit%ld@%s>\r\n",
310                                 SMTP->vrfy_buffer.fullname,
311                                 SMTP->vrfy_buffer.usernum,
312                                 config.c_fqdn);
313                 }
314
315                 ++SMTP->vrfy_count;
316                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
317         }
318 }
319
320
321 /* 
322  * Implements the EXPN (expand user name) command.
323  * Performs fuzzy match on full user names.
324  */
325 void smtp_expn(char *argbuf) {
326         SMTP->vrfy_count = 0;
327         strcpy(SMTP->vrfy_match, argbuf);
328         ForEachUser(smtp_expn_backend, NULL);
329
330         if (SMTP->vrfy_count < 1) {
331                 cprintf("550 5.1.1 String does not match anything.\r\n");
332         }
333         else if (SMTP->vrfy_count >= 1) {
334                 cprintf("250 %s <cit%ld@%s>\r\n",
335                         SMTP->vrfy_buffer.fullname,
336                         SMTP->vrfy_buffer.usernum,
337                         config.c_fqdn);
338         }
339 }
340
341
342 /*
343  * Implements the RSET (reset state) command.
344  * Currently this just zeroes out the state buffer.  If pointers to data
345  * allocated with mallok() are ever placed in the state buffer, we have to
346  * be sure to phree() them first!
347  */
348 void smtp_rset(void) {
349         int is_lmtp;
350
351         /*
352          * Our entire SMTP state is discarded when a RSET command is issued,
353          * but we need to preserve this one little piece of information, so
354          * we save it for later.
355          */
356         is_lmtp = SMTP->is_lmtp;
357
358         memset(SMTP, 0, sizeof(struct citsmtp));
359
360         /*
361          * It is somewhat ambiguous whether we want to log out when a RSET
362          * command is issued.  Here's the code to do it.  It is commented out
363          * because some clients (such as Pine) issue RSET commands before
364          * each message, but still expect to be logged in.
365          *
366          * if (CC->logged_in) {
367          *      logout(CC);
368          * }
369          */
370
371         /*
372          * Reinstate this little piece of information we saved (see above).
373          */
374         SMTP->is_lmtp = is_lmtp;
375
376         cprintf("250 2.0.0 Zap!\r\n");
377 }
378
379 /*
380  * Clear out the portions of the state buffer that need to be cleared out
381  * after the DATA command finishes.
382  */
383 void smtp_data_clear(void) {
384         strcpy(SMTP->from, "");
385         strcpy(SMTP->recipients, "");
386         SMTP->number_of_recipients = 0;
387         SMTP->delivery_mode = 0;
388         SMTP->message_originated_locally = 0;
389 }
390
391
392
393 /*
394  * Implements the "MAIL From:" command
395  */
396 void smtp_mail(char *argbuf) {
397         char user[SIZ];
398         char node[SIZ];
399         char name[SIZ];
400
401         if (strlen(SMTP->from) != 0) {
402                 cprintf("503 5.1.0 Only one sender permitted\r\n");
403                 return;
404         }
405
406         if (strncasecmp(argbuf, "From:", 5)) {
407                 cprintf("501 5.1.7 Syntax error\r\n");
408                 return;
409         }
410
411         strcpy(SMTP->from, &argbuf[5]);
412         striplt(SMTP->from);
413         stripallbut(SMTP->from, '<', '>');
414
415         /* We used to reject empty sender names, until it was brought to our
416          * attention that RFC1123 5.2.9 requires that this be allowed.  So now
417          * we allow it, but replace the empty string with a fake
418          * address so we don't have to contend with the empty string causing
419          * other code to fail when it's expecting something there.
420          */
421         if (strlen(SMTP->from) == 0) {
422                 strcpy(SMTP->from, "someone@somewhere.org");
423         }
424
425         /* If this SMTP connection is from a logged-in user, force the 'from'
426          * to be the user's Internet e-mail address as Citadel knows it.
427          */
428         if (CC->logged_in) {
429                 strcpy(SMTP->from, CC->cs_inet_email);
430                 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
431                 SMTP->message_originated_locally = 1;
432                 return;
433         }
434
435         else if (SMTP->is_lmtp) {
436                 /* Bypass forgery checking for LMTP */
437         }
438
439         /* Otherwise, make sure outsiders aren't trying to forge mail from
440          * this system.
441          */
442         else {
443                 process_rfc822_addr(SMTP->from, user, node, name);
444                 if (CtdlHostAlias(node) != hostalias_nomatch) {
445                         cprintf("550 5.1.8 "
446                                 "You must log in to send mail from %s\r\n",
447                                 node);
448                         strcpy(SMTP->from, "");
449                         return;
450                 }
451         }
452
453         cprintf("250 2.0.0 Sender ok\r\n");
454 }
455
456
457
458 /*
459  * Implements the "RCPT To:" command
460  */
461 void smtp_rcpt(char *argbuf) {
462         char recp[SIZ];
463         char message_to_spammer[SIZ];
464         struct recptypes *valid = NULL;
465
466         if (strlen(SMTP->from) == 0) {
467                 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
468                 return;
469         }
470
471         if (strncasecmp(argbuf, "To:", 3)) {
472                 cprintf("501 5.1.7 Syntax error\r\n");
473                 return;
474         }
475
476         strcpy(recp, &argbuf[3]);
477         striplt(recp);
478         stripallbut(recp, '<', '>');
479
480         if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
481                 cprintf("452 4.5.3 Too many recipients\r\n");
482                 return;
483         }
484
485         /* RBL check */
486         if ( (!CC->logged_in)
487            && (!SMTP->is_lmtp) ) {
488                 if (rbl_check(message_to_spammer)) {
489                         cprintf("550 %s\r\n", message_to_spammer);
490                         /* no need to phree(valid), it's not allocated yet */
491                         return;
492                 }
493         }
494
495         valid = validate_recipients(recp);
496         if (valid->num_error > 0) {
497                 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
498                 phree(valid);
499                 return;
500         }
501
502         if (valid->num_internet > 0) {
503                 if ( (SMTP->message_originated_locally == 0)
504                    && (SMTP->is_lmtp == 0) ) {
505                         cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
506                         phree(valid);
507                         return;
508                 }
509         }
510
511         cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
512         if (strlen(SMTP->recipients) > 0) {
513                 strcat(SMTP->recipients, ",");
514         }
515         strcat(SMTP->recipients, recp);
516         SMTP->number_of_recipients += 1;
517 }
518
519
520
521
522 /*
523  * Implements the DATA command
524  */
525 void smtp_data(void) {
526         char *body;
527         struct CtdlMessage *msg;
528         long msgnum;
529         char nowstamp[SIZ];
530         struct recptypes *valid;
531         int scan_errors;
532         int i;
533         char result[SIZ];
534
535         if (strlen(SMTP->from) == 0) {
536                 cprintf("503 5.5.1 Need MAIL command first.\r\n");
537                 return;
538         }
539
540         if (SMTP->number_of_recipients < 1) {
541                 cprintf("503 5.5.1 Need RCPT command first.\r\n");
542                 return;
543         }
544
545         cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
546         
547         datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
548         body = mallok(4096);
549
550         if (body != NULL) snprintf(body, 4096,
551                 "Received: from %s (%s [%s])\n"
552                 "       by %s; %s\n",
553                         SMTP->helo_node,
554                         CC->cs_host,
555                         CC->cs_addr,
556                         config.c_fqdn,
557                         nowstamp);
558         
559         body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
560         if (body == NULL) {
561                 cprintf("550 5.6.5 "
562                         "Unable to save message: internal error.\r\n");
563                 return;
564         }
565
566         lprintf(9, "Converting message...\n");
567         msg = convert_internet_message(body);
568
569         /* If the user is locally authenticated, FORCE the From: header to
570          * show up as the real sender.  Yes, this violates the RFC standard,
571          * but IT MAKES SENSE.  If you prefer strict RFC adherence over
572          * common sense, you can disable this in the configuration.
573          *
574          * We also set the "message room name" ('O' field) to MAILROOM
575          * (which is Mail> on most systems) to prevent it from getting set
576          * to something ugly like "0000058008.Sent Items>" when the message
577          * is read with a Citadel client.
578          */
579         if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
580                 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
581                 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
582                 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
583                 if (msg->cm_fields['F'] != NULL) phree(msg->cm_fields['F']);
584                 if (msg->cm_fields['O'] != NULL) phree(msg->cm_fields['O']);
585                 msg->cm_fields['A'] = strdoop(CC->user.fullname);
586                 msg->cm_fields['N'] = strdoop(config.c_nodename);
587                 msg->cm_fields['H'] = strdoop(config.c_humannode);
588                 msg->cm_fields['F'] = strdoop(CC->cs_inet_email);
589                 msg->cm_fields['O'] = strdoop(MAILROOM);
590         }
591
592         /* Submit the message into the Citadel system. */
593         valid = validate_recipients(SMTP->recipients);
594
595         /* If there are modules that want to scan this message before final
596          * submission (such as virus checkers or spam filters), call them now
597          * and give them an opportunity to reject the message.
598          */
599         scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
600
601         if (scan_errors > 0) {  /* We don't want this message! */
602
603                 if (msg->cm_fields['0'] == NULL) {
604                         msg->cm_fields['0'] = strdoop(
605                                 "5.7.1 Message rejected by filter");
606                 }
607
608                 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
609         }
610         
611         else {                  /* Ok, we'll accept this message. */
612                 msgnum = CtdlSubmitMsg(msg, valid, "");
613                 if (msgnum > 0L) {
614                         sprintf(result, "250 2.0.0 Message accepted.\r\n");
615                 }
616                 else {
617                         sprintf(result, "550 5.5.0 Internal delivery error\r\n");
618                 }
619         }
620
621         /* For SMTP and ESTMP, just print the result message.  For LMTP, we
622          * have to print one result message for each recipient.  Since there
623          * is nothing in Citadel which would cause different recipients to
624          * have different results, we can get away with just spitting out the
625          * same message once for each recipient.
626          */
627         if (SMTP->is_lmtp) {
628                 for (i=0; i<SMTP->number_of_recipients; ++i) {
629                         cprintf("%s", result);
630                 }
631         }
632         else {
633                 cprintf("%s", result);
634         }
635
636         CtdlFreeMessage(msg);
637         phree(valid);
638         smtp_data_clear();      /* clear out the buffers now */
639 }
640
641
642
643
644 /* 
645  * Main command loop for SMTP sessions.
646  */
647 void smtp_command_loop(void) {
648         char cmdbuf[SIZ];
649
650         time(&CC->lastcmd);
651         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
652         if (client_gets(cmdbuf) < 1) {
653                 lprintf(3, "SMTP socket is broken.  Ending session.\n");
654                 CC->kill_me = 1;
655                 return;
656         }
657         lprintf(5, "SMTP: %s\n", cmdbuf);
658         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
659
660         lprintf(9, "CC->logged_in = %d\n", CC->logged_in);
661
662         if (SMTP->command_state == smtp_user) {
663                 smtp_get_user(cmdbuf);
664         }
665
666         else if (SMTP->command_state == smtp_password) {
667                 smtp_get_pass(cmdbuf);
668         }
669
670         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
671                 smtp_auth(&cmdbuf[5]);
672         }
673
674         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
675                 smtp_data();
676         }
677
678         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
679                 smtp_expn(&cmdbuf[5]);
680         }
681
682         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
683                 smtp_hello(&cmdbuf[5], 0);
684         }
685
686         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
687                 smtp_hello(&cmdbuf[5], 1);
688         }
689
690         else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
691                 smtp_hello(&cmdbuf[5], 2);
692         }
693
694         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
695                 smtp_help();
696         }
697
698         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
699                 smtp_mail(&cmdbuf[5]);
700         }
701
702         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
703                 cprintf("250 NOOP\r\n");
704         }
705
706         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
707                 cprintf("221 Goodbye...\r\n");
708                 CC->kill_me = 1;
709                 return;
710         }
711
712         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
713                 smtp_rcpt(&cmdbuf[5]);
714         }
715
716         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
717                 smtp_rset();
718         }
719
720         else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
721                 smtp_vrfy(&cmdbuf[5]);
722         }
723
724         else {
725                 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
726         }
727
728 }
729
730
731
732
733 /*****************************************************************************/
734 /*               SMTP CLIENT (OUTBOUND PROCESSING) STUFF                     */
735 /*****************************************************************************/
736
737
738
739 /*
740  * smtp_try()
741  *
742  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
743  *
744  */
745 void smtp_try(const char *key, const char *addr, int *status,
746               char *dsn, size_t n, long msgnum)
747 {
748         int sock = (-1);
749         char mxhosts[1024];
750         int num_mxhosts;
751         int mx;
752         int i;
753         char user[SIZ], node[SIZ], name[SIZ];
754         char buf[1024];
755         char mailfrom[1024];
756         int lp, rp;
757         FILE *msg_fp = NULL;
758         size_t msg_size;
759         size_t blocksize = 0;
760         int scan_done;
761
762         /* Parse out the host portion of the recipient address */
763         process_rfc822_addr(addr, user, node, name);
764
765         lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
766                 user, node, name);
767
768         /* Load the message out of the database into a temp file */
769         msg_fp = tmpfile();
770         if (msg_fp == NULL) {
771                 *status = 4;
772                 snprintf(dsn, n, "Error creating temporary file");
773                 return;
774         }
775         else {
776                 CtdlRedirectOutput(msg_fp, -1);
777                 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1);
778                 CtdlRedirectOutput(NULL, -1);
779                 fseek(msg_fp, 0L, SEEK_END);
780                 msg_size = ftell(msg_fp);
781         }
782
783
784         /* Extract something to send later in the 'MAIL From:' command */
785         strcpy(mailfrom, "");
786         rewind(msg_fp);
787         scan_done = 0;
788         do {
789                 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
790                 if (!strncasecmp(buf, "From:", 5)) {
791                         safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
792                         striplt(mailfrom);
793                         for (i=0; i<strlen(mailfrom); ++i) {
794                                 if (!isprint(mailfrom[i])) {
795                                         strcpy(&mailfrom[i], &mailfrom[i+1]);
796                                         i=0;
797                                 }
798                         }
799
800                         /* Strip out parenthesized names */
801                         lp = (-1);
802                         rp = (-1);
803                         for (i=0; i<strlen(mailfrom); ++i) {
804                                 if (mailfrom[i] == '(') lp = i;
805                                 if (mailfrom[i] == ')') rp = i;
806                         }
807                         if ((lp>0)&&(rp>lp)) {
808                                 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
809                         }
810
811                         /* Prefer brokketized names */
812                         lp = (-1);
813                         rp = (-1);
814                         for (i=0; i<strlen(mailfrom); ++i) {
815                                 if (mailfrom[i] == '<') lp = i;
816                                 if (mailfrom[i] == '>') rp = i;
817                         }
818                         if ( (lp>=0) && (rp>lp) ) {
819                                 mailfrom[rp] = 0;
820                                 strcpy(mailfrom, &mailfrom[lp]);
821                         }
822
823                         scan_done = 1;
824                 }
825         } while (scan_done == 0);
826         if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
827
828         /* Figure out what mail exchanger host we have to connect to */
829         num_mxhosts = getmx(mxhosts, node);
830         lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
831         if (num_mxhosts < 1) {
832                 *status = 5;
833                 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
834                 return;
835         }
836
837         sock = (-1);
838         for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
839                 extract(buf, mxhosts, mx);
840                 lprintf(9, "Trying <%s>\n", buf);
841                 sock = sock_connect(buf, "25", "tcp");
842                 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
843                 if (sock >= 0) lprintf(9, "Connected!\n");
844                 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
845         }
846
847         if (sock < 0) {
848                 *status = 4;    /* dsn is already filled in */
849                 return;
850         }
851
852         /* Process the SMTP greeting from the server */
853         if (ml_sock_gets(sock, buf) < 0) {
854                 *status = 4;
855                 strcpy(dsn, "Connection broken during SMTP conversation");
856                 goto bail;
857         }
858         lprintf(9, "<%s\n", buf);
859         if (buf[0] != '2') {
860                 if (buf[0] == '4') {
861                         *status = 4;
862                         safestrncpy(dsn, &buf[4], 1023);
863                         goto bail;
864                 }
865                 else {
866                         *status = 5;
867                         safestrncpy(dsn, &buf[4], 1023);
868                         goto bail;
869                 }
870         }
871
872         /* At this point we know we are talking to a real SMTP server */
873
874         /* Do a HELO command */
875         snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
876         lprintf(9, ">%s", buf);
877         sock_write(sock, buf, strlen(buf));
878         if (ml_sock_gets(sock, buf) < 0) {
879                 *status = 4;
880                 strcpy(dsn, "Connection broken during SMTP HELO");
881                 goto bail;
882         }
883         lprintf(9, "<%s\n", buf);
884         if (buf[0] != '2') {
885                 if (buf[0] == '4') {
886                         *status = 4;
887                         safestrncpy(dsn, &buf[4], 1023);
888                         goto bail;
889                 }
890                 else {
891                         *status = 5;
892                         safestrncpy(dsn, &buf[4], 1023);
893                         goto bail;
894                 }
895         }
896
897
898         /* HELO succeeded, now try the MAIL From: command */
899         snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
900         lprintf(9, ">%s", buf);
901         sock_write(sock, buf, strlen(buf));
902         if (ml_sock_gets(sock, buf) < 0) {
903                 *status = 4;
904                 strcpy(dsn, "Connection broken during SMTP MAIL");
905                 goto bail;
906         }
907         lprintf(9, "<%s\n", buf);
908         if (buf[0] != '2') {
909                 if (buf[0] == '4') {
910                         *status = 4;
911                         safestrncpy(dsn, &buf[4], 1023);
912                         goto bail;
913                 }
914                 else {
915                         *status = 5;
916                         safestrncpy(dsn, &buf[4], 1023);
917                         goto bail;
918                 }
919         }
920
921
922         /* MAIL succeeded, now try the RCPT To: command */
923         snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
924         lprintf(9, ">%s", buf);
925         sock_write(sock, buf, strlen(buf));
926         if (ml_sock_gets(sock, buf) < 0) {
927                 *status = 4;
928                 strcpy(dsn, "Connection broken during SMTP RCPT");
929                 goto bail;
930         }
931         lprintf(9, "<%s\n", buf);
932         if (buf[0] != '2') {
933                 if (buf[0] == '4') {
934                         *status = 4;
935                         safestrncpy(dsn, &buf[4], 1023);
936                         goto bail;
937                 }
938                 else {
939                         *status = 5;
940                         safestrncpy(dsn, &buf[4], 1023);
941                         goto bail;
942                 }
943         }
944
945
946         /* RCPT succeeded, now try the DATA command */
947         lprintf(9, ">DATA\n");
948         sock_write(sock, "DATA\r\n", 6);
949         if (ml_sock_gets(sock, buf) < 0) {
950                 *status = 4;
951                 strcpy(dsn, "Connection broken during SMTP DATA");
952                 goto bail;
953         }
954         lprintf(9, "<%s\n", buf);
955         if (buf[0] != '3') {
956                 if (buf[0] == '4') {
957                         *status = 3;
958                         safestrncpy(dsn, &buf[4], 1023);
959                         goto bail;
960                 }
961                 else {
962                         *status = 5;
963                         safestrncpy(dsn, &buf[4], 1023);
964                         goto bail;
965                 }
966         }
967
968         /* If we reach this point, the server is expecting data */
969         rewind(msg_fp);
970         while (msg_size > 0) {
971                 blocksize = sizeof(buf);
972                 if (blocksize > msg_size) blocksize = msg_size;
973                 fread(buf, blocksize, 1, msg_fp);
974                 sock_write(sock, buf, blocksize);
975                 msg_size -= blocksize;
976         }
977         if (buf[blocksize-1] != 10) {
978                 lprintf(5, "Possible problem: message did not correctly "
979                         "terminate. (expecting 0x10, got 0x%02x)\n",
980                                 buf[blocksize-1]);
981         }
982
983         sock_write(sock, ".\r\n", 3);
984         if (ml_sock_gets(sock, buf) < 0) {
985                 *status = 4;
986                 strcpy(dsn, "Connection broken during SMTP message transmit");
987                 goto bail;
988         }
989         lprintf(9, "%s\n", buf);
990         if (buf[0] != '2') {
991                 if (buf[0] == '4') {
992                         *status = 4;
993                         safestrncpy(dsn, &buf[4], 1023);
994                         goto bail;
995                 }
996                 else {
997                         *status = 5;
998                         safestrncpy(dsn, &buf[4], 1023);
999                         goto bail;
1000                 }
1001         }
1002
1003         /* We did it! */
1004         safestrncpy(dsn, &buf[4], 1023);
1005         *status = 2;
1006
1007         lprintf(9, ">QUIT\n");
1008         sock_write(sock, "QUIT\r\n", 6);
1009         ml_sock_gets(sock, buf);
1010         lprintf(9, "<%s\n", buf);
1011
1012 bail:   if (msg_fp != NULL) fclose(msg_fp);
1013         sock_close(sock);
1014         return;
1015 }
1016
1017
1018
1019 /*
1020  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1021  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1022  * a "bounce" message (delivery status notification).
1023  */
1024 void smtp_do_bounce(char *instr) {
1025         int i;
1026         int lines;
1027         int status;
1028         char buf[1024];
1029         char key[1024];
1030         char addr[1024];
1031         char dsn[1024];
1032         char bounceto[1024];
1033         int num_bounces = 0;
1034         int bounce_this = 0;
1035         long bounce_msgid = (-1);
1036         time_t submitted = 0L;
1037         struct CtdlMessage *bmsg = NULL;
1038         int give_up = 0;
1039         struct recptypes *valid;
1040         int successful_bounce = 0;
1041
1042         lprintf(9, "smtp_do_bounce() called\n");
1043         strcpy(bounceto, "");
1044
1045         lines = num_tokens(instr, '\n');
1046
1047
1048         /* See if it's time to give up on delivery of this message */
1049         for (i=0; i<lines; ++i) {
1050                 extract_token(buf, instr, i, '\n');
1051                 extract(key, buf, 0);
1052                 extract(addr, buf, 1);
1053                 if (!strcasecmp(key, "submitted")) {
1054                         submitted = atol(addr);
1055                 }
1056         }
1057
1058         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1059                 give_up = 1;
1060         }
1061
1062
1063
1064         bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1065         if (bmsg == NULL) return;
1066         memset(bmsg, 0, sizeof(struct CtdlMessage));
1067
1068         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1069         bmsg->cm_anon_type = MES_NORMAL;
1070         bmsg->cm_format_type = 1;
1071         bmsg->cm_fields['A'] = strdoop("Citadel");
1072         bmsg->cm_fields['O'] = strdoop(MAILROOM);
1073         bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1074
1075         if (give_up) bmsg->cm_fields['M'] = strdoop(
1076 "A message you sent could not be delivered to some or all of its recipients\n"
1077 "due to prolonged unavailability of its destination(s).\n"
1078 "Giving up on the following addresses:\n\n"
1079 );
1080
1081         else bmsg->cm_fields['M'] = strdoop(
1082 "A message you sent could not be delivered to some or all of its recipients.\n"
1083 "The following addresses were undeliverable:\n\n"
1084 );
1085
1086         /*
1087          * Now go through the instructions checking for stuff.
1088          */
1089         for (i=0; i<lines; ++i) {
1090                 extract_token(buf, instr, i, '\n');
1091                 extract(key, buf, 0);
1092                 extract(addr, buf, 1);
1093                 status = extract_int(buf, 2);
1094                 extract(dsn, buf, 3);
1095                 bounce_this = 0;
1096
1097                 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1098                         key, addr, status, dsn);
1099
1100                 if (!strcasecmp(key, "bounceto")) {
1101                         strcpy(bounceto, addr);
1102                 }
1103
1104                 if (
1105                    (!strcasecmp(key, "local"))
1106                    || (!strcasecmp(key, "remote"))
1107                    || (!strcasecmp(key, "ignet"))
1108                    || (!strcasecmp(key, "room"))
1109                 ) {
1110                         if (status == 5) bounce_this = 1;
1111                         if (give_up) bounce_this = 1;
1112                 }
1113
1114                 if (bounce_this) {
1115                         ++num_bounces;
1116
1117                         if (bmsg->cm_fields['M'] == NULL) {
1118                                 lprintf(2, "ERROR ... M field is null "
1119                                         "(%s:%d)\n", __FILE__, __LINE__);
1120                         }
1121
1122                         bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1123                                 strlen(bmsg->cm_fields['M']) + 1024 );
1124                         strcat(bmsg->cm_fields['M'], addr);
1125                         strcat(bmsg->cm_fields['M'], ": ");
1126                         strcat(bmsg->cm_fields['M'], dsn);
1127                         strcat(bmsg->cm_fields['M'], "\n");
1128
1129                         remove_token(instr, i, '\n');
1130                         --i;
1131                         --lines;
1132                 }
1133         }
1134
1135         /* Deliver the bounce if there's anything worth mentioning */
1136         lprintf(9, "num_bounces = %d\n", num_bounces);
1137         if (num_bounces > 0) {
1138
1139                 /* First try the user who sent the message */
1140                 lprintf(9, "bounce to user? <%s>\n", bounceto);
1141                 if (strlen(bounceto) == 0) {
1142                         lprintf(7, "No bounce address specified\n");
1143                         bounce_msgid = (-1L);
1144                 }
1145
1146                 /* Can we deliver the bounce to the original sender? */
1147                 valid = validate_recipients(bounceto);
1148                 if (valid != NULL) {
1149                         if (valid->num_error == 0) {
1150                                 CtdlSubmitMsg(bmsg, valid, "");
1151                                 successful_bounce = 1;
1152                         }
1153                 }
1154
1155                 /* If not, post it in the Aide> room */
1156                 if (successful_bounce == 0) {
1157                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1158                 }
1159
1160                 /* Free up the memory we used */
1161                 if (valid != NULL) {
1162                         phree(valid);
1163                 }
1164         }
1165
1166         CtdlFreeMessage(bmsg);
1167         lprintf(9, "Done processing bounces\n");
1168 }
1169
1170
1171 /*
1172  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1173  * set of delivery instructions for completed deliveries and remove them.
1174  *
1175  * It returns the number of incomplete deliveries remaining.
1176  */
1177 int smtp_purge_completed_deliveries(char *instr) {
1178         int i;
1179         int lines;
1180         int status;
1181         char buf[1024];
1182         char key[1024];
1183         char addr[1024];
1184         char dsn[1024];
1185         int completed;
1186         int incomplete = 0;
1187
1188         lines = num_tokens(instr, '\n');
1189         for (i=0; i<lines; ++i) {
1190                 extract_token(buf, instr, i, '\n');
1191                 extract(key, buf, 0);
1192                 extract(addr, buf, 1);
1193                 status = extract_int(buf, 2);
1194                 extract(dsn, buf, 3);
1195
1196                 completed = 0;
1197
1198                 if (
1199                    (!strcasecmp(key, "local"))
1200                    || (!strcasecmp(key, "remote"))
1201                    || (!strcasecmp(key, "ignet"))
1202                    || (!strcasecmp(key, "room"))
1203                 ) {
1204                         if (status == 2) completed = 1;
1205                         else ++incomplete;
1206                 }
1207
1208                 if (completed) {
1209                         remove_token(instr, i, '\n');
1210                         --i;
1211                         --lines;
1212                 }
1213         }
1214
1215         return(incomplete);
1216 }
1217
1218
1219 /*
1220  * smtp_do_procmsg()
1221  *
1222  * Called by smtp_do_queue() to handle an individual message.
1223  */
1224 void smtp_do_procmsg(long msgnum, void *userdata) {
1225         struct CtdlMessage *msg;
1226         char *instr = NULL;
1227         char *results = NULL;
1228         int i;
1229         int lines;
1230         int status;
1231         char buf[1024];
1232         char key[1024];
1233         char addr[1024];
1234         char dsn[1024];
1235         long text_msgid = (-1);
1236         int incomplete_deliveries_remaining;
1237         time_t attempted = 0L;
1238         time_t last_attempted = 0L;
1239         time_t retry = SMTP_RETRY_INTERVAL;
1240
1241         lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1242
1243         msg = CtdlFetchMessage(msgnum);
1244         if (msg == NULL) {
1245                 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1246                 return;
1247         }
1248
1249         instr = strdoop(msg->cm_fields['M']);
1250         CtdlFreeMessage(msg);
1251
1252         /* Strip out the headers amd any other non-instruction line */
1253         lines = num_tokens(instr, '\n');
1254         for (i=0; i<lines; ++i) {
1255                 extract_token(buf, instr, i, '\n');
1256                 if (num_tokens(buf, '|') < 2) {
1257                         remove_token(instr, i, '\n');
1258                         --lines;
1259                         --i;
1260                 }
1261         }
1262
1263         /* Learn the message ID and find out about recent delivery attempts */
1264         lines = num_tokens(instr, '\n');
1265         for (i=0; i<lines; ++i) {
1266                 extract_token(buf, instr, i, '\n');
1267                 extract(key, buf, 0);
1268                 if (!strcasecmp(key, "msgid")) {
1269                         text_msgid = extract_long(buf, 1);
1270                 }
1271                 if (!strcasecmp(key, "retry")) {
1272                         /* double the retry interval after each attempt */
1273                         retry = extract_long(buf, 1) * 2L;
1274                         if (retry > SMTP_RETRY_MAX) {
1275                                 retry = SMTP_RETRY_MAX;
1276                         }
1277                         remove_token(instr, i, '\n');
1278                 }
1279                 if (!strcasecmp(key, "attempted")) {
1280                         attempted = extract_long(buf, 1);
1281                         if (attempted > last_attempted)
1282                                 last_attempted = attempted;
1283                 }
1284         }
1285
1286         /*
1287          * Postpone delivery if we've already tried recently.
1288          */
1289         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1290                 lprintf(7, "Retry time not yet reached.\n");
1291                 phree(instr);
1292                 return;
1293         }
1294
1295
1296         /*
1297          * Bail out if there's no actual message associated with this
1298          */
1299         if (text_msgid < 0L) {
1300                 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1301                 phree(instr);
1302                 return;
1303         }
1304
1305         /* Plow through the instructions looking for 'remote' directives and
1306          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1307          * were experienced and it's time to try again)
1308          */
1309         lines = num_tokens(instr, '\n');
1310         for (i=0; i<lines; ++i) {
1311                 extract_token(buf, instr, i, '\n');
1312                 extract(key, buf, 0);
1313                 extract(addr, buf, 1);
1314                 status = extract_int(buf, 2);
1315                 extract(dsn, buf, 3);
1316                 if ( (!strcasecmp(key, "remote"))
1317                    && ((status==0)||(status==3)||(status==4)) ) {
1318
1319                         /* Remove this "remote" instruction from the set,
1320                          * but replace the set's final newline if
1321                          * remove_token() stripped it.  It has to be there.
1322                          */
1323                         remove_token(instr, i, '\n');
1324                         if (instr[strlen(instr)-1] != '\n') {
1325                                 strcat(instr, "\n");
1326                         }
1327
1328                         --i;
1329                         --lines;
1330                         lprintf(9, "SMTP: Trying <%s>\n", addr);
1331                         smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1332                         if (status != 2) {
1333                                 if (results == NULL) {
1334                                         results = mallok(1024);
1335                                         memset(results, 0, 1024);
1336                                 }
1337                                 else {
1338                                         results = reallok(results,
1339                                                 strlen(results) + 1024);
1340                                 }
1341                                 snprintf(&results[strlen(results)], 1024,
1342                                         "%s|%s|%d|%s\n",
1343                                         key, addr, status, dsn);
1344                         }
1345                 }
1346         }
1347
1348         if (results != NULL) {
1349                 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1350                 strcat(instr, results);
1351                 phree(results);
1352         }
1353
1354
1355         /* Generate 'bounce' messages */
1356         smtp_do_bounce(instr);
1357
1358         /* Go through the delivery list, deleting completed deliveries */
1359         incomplete_deliveries_remaining = 
1360                 smtp_purge_completed_deliveries(instr);
1361
1362
1363         /*
1364          * No delivery instructions remain, so delete both the instructions
1365          * message and the message message.
1366          */
1367         if (incomplete_deliveries_remaining <= 0) {
1368                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1369                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1370         }
1371
1372
1373         /*
1374          * Uncompleted delivery instructions remain, so delete the old
1375          * instructions and replace with the updated ones.
1376          */
1377         if (incomplete_deliveries_remaining > 0) {
1378                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1379                 msg = mallok(sizeof(struct CtdlMessage));
1380                 memset(msg, 0, sizeof(struct CtdlMessage));
1381                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1382                 msg->cm_anon_type = MES_NORMAL;
1383                 msg->cm_format_type = FMT_RFC822;
1384                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1385                 snprintf(msg->cm_fields['M'],
1386                         strlen(instr)+SIZ,
1387                         "Content-type: %s\n\n%s\n"
1388                         "attempted|%ld\n"
1389                         "retry|%ld\n",
1390                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1391                 phree(instr);
1392                 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1393                 CtdlFreeMessage(msg);
1394         }
1395
1396 }
1397
1398
1399
1400 /*
1401  * smtp_do_queue()
1402  * 
1403  * Run through the queue sending out messages.
1404  */
1405 void smtp_do_queue(void) {
1406         static int doing_queue = 0;
1407
1408         /*
1409          * This is a simple concurrency check to make sure only one queue run
1410          * is done at a time.  We could do this with a mutex, but since we
1411          * don't really require extremely fine granularity here, we'll do it
1412          * with a static variable instead.
1413          */
1414         if (doing_queue) return;
1415         doing_queue = 1;
1416
1417         /* 
1418          * Go ahead and run the queue
1419          */
1420         lprintf(7, "SMTP: processing outbound queue\n");
1421
1422         if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1423                 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1424                 return;
1425         }
1426         CtdlForEachMessage(MSGS_ALL, 0L,
1427                 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1428
1429         lprintf(7, "SMTP: queue run completed\n");
1430         run_queue_now = 0;
1431         doing_queue = 0;
1432 }
1433
1434
1435
1436 /*****************************************************************************/
1437 /*                          SMTP UTILITY COMMANDS                            */
1438 /*****************************************************************************/
1439
1440 void cmd_smtp(char *argbuf) {
1441         char cmd[SIZ];
1442         char node[SIZ];
1443         char buf[SIZ];
1444         int i;
1445         int num_mxhosts;
1446
1447         if (CtdlAccessCheck(ac_aide)) return;
1448
1449         extract(cmd, argbuf, 0);
1450
1451         if (!strcasecmp(cmd, "mx")) {
1452                 extract(node, argbuf, 1);
1453                 num_mxhosts = getmx(buf, node);
1454                 cprintf("%d %d MX hosts listed for %s\n",
1455                         LISTING_FOLLOWS, num_mxhosts, node);
1456                 for (i=0; i<num_mxhosts; ++i) {
1457                         extract(node, buf, i);
1458                         cprintf("%s\n", node);
1459                 }
1460                 cprintf("000\n");
1461                 return;
1462         }
1463
1464         else if (!strcasecmp(cmd, "runqueue")) {
1465                 run_queue_now = 1;
1466                 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1467                 return;
1468         }
1469
1470         else {
1471                 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1472         }
1473
1474 }
1475
1476
1477 /*
1478  * Initialize the SMTP outbound queue
1479  */
1480 void smtp_init_spoolout(void) {
1481         struct ctdlroom qrbuf;
1482
1483         /*
1484          * Create the room.  This will silently fail if the room already
1485          * exists, and that's perfectly ok, because we want it to exist.
1486          */
1487         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0);
1488
1489         /*
1490          * Make sure it's set to be a "system room" so it doesn't show up
1491          * in the <K>nown rooms list for Aides.
1492          */
1493         if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1494                 qrbuf.QRflags2 |= QR2_SYSTEM;
1495                 lputroom(&qrbuf);
1496         }
1497 }
1498
1499
1500
1501
1502 /*****************************************************************************/
1503 /*                      MODULE INITIALIZATION STUFF                          */
1504 /*****************************************************************************/
1505
1506
1507 char *serv_smtp_init(void)
1508 {
1509         CtdlRegisterServiceHook(config.c_smtp_port,     /* On the net... */
1510                                 NULL,
1511                                 smtp_greeting,
1512                                 smtp_command_loop);
1513
1514         CtdlRegisterServiceHook(0,                      /* ...and locally */
1515                                 "lmtp.socket",
1516                                 lmtp_greeting,
1517                                 smtp_command_loop);
1518
1519         smtp_init_spoolout();
1520         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1521         CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1522         return "$Id$";
1523 }