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