]> code.citadel.org Git - citadel.git/blob - citadel/serv_smtp.c
* mail stuff
[citadel.git] / citadel / serv_smtp.c
1 /*
2  * $Id$
3  *
4  * An implementation of RFC821 (Simple Mail Transfer Protocol) for the
5  * Citadel system.
6  *
7  */
8
9 #include "sysdep.h"
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <pwd.h>
16 #include <errno.h>
17 #include <sys/types.h>
18
19 #if TIME_WITH_SYS_TIME
20 # include <sys/time.h>
21 # include <time.h>
22 #else
23 # if HAVE_SYS_TIME_H
24 #  include <sys/time.h>
25 # else
26 #  include <time.h>
27 # endif
28 #endif
29
30 #include <sys/wait.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <limits.h>
34 #include "citadel.h"
35 #include "server.h"
36 #include "sysdep_decls.h"
37 #include "citserver.h"
38 #include "support.h"
39 #include "config.h"
40 #include "control.h"
41 #include "dynloader.h"
42 #include "room_ops.h"
43 #include "user_ops.h"
44 #include "policy.h"
45 #include "database.h"
46 #include "msgbase.h"
47 #include "tools.h"
48 #include "internet_addressing.h"
49 #include "genstamp.h"
50 #include "domain.h"
51 #include "clientsocket.h"
52
53
54 #ifndef HAVE_SNPRINTF
55 #include "snprintf.h"
56 #endif
57
58 struct citsmtp {                /* Information about the current session */
59         int command_state;
60         char helo_node[SIZ];
61         struct usersupp vrfy_buffer;
62         int vrfy_count;
63         char vrfy_match[SIZ];
64         char from[SIZ];
65         int number_of_recipients;
66         int number_of_rooms;
67         int delivery_mode;
68         int message_originated_locally;
69         struct recptypes *valid;
70 };
71
72 enum {                          /* Command states for login authentication */
73         smtp_command,
74         smtp_user,
75         smtp_password
76 };
77
78 enum {                          /* Delivery modes */
79         smtp_deliver_local,
80         smtp_deliver_remote
81 };
82
83 #define SMTP            ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
84 #define SMTP_RECPS      ((char *)CtdlGetUserData(SYM_SMTP_RECPS))
85 #define SMTP_ROOMS      ((char *)CtdlGetUserData(SYM_SMTP_ROOMS))
86
87 long SYM_SMTP;
88 long SYM_SMTP_RECPS;
89 long SYM_SMTP_ROOMS;
90
91 int run_queue_now = 0;  /* Set to 1 to ignore SMTP send retry times */
92
93
94
95 /*****************************************************************************/
96 /*                      SMTP SERVER (INBOUND) STUFF                          */
97 /*****************************************************************************/
98
99
100
101
102 /*
103  * Here's where our SMTP session begins its happy day.
104  */
105 void smtp_greeting(void) {
106
107         strcpy(CC->cs_clientname, "SMTP session");
108         CC->internal_pgm = 1;
109         CC->cs_flags |= CS_STEALTH;
110         CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
111         CtdlAllocUserData(SYM_SMTP_RECPS, SIZ);
112         CtdlAllocUserData(SYM_SMTP_ROOMS, SIZ);
113         sprintf(SMTP_RECPS, "%s", "");
114         sprintf(SMTP_ROOMS, "%s", "");
115
116         cprintf("220 %s ESMTP Citadel/UX server ready.\r\n", config.c_fqdn);
117 }
118
119
120 /*
121  * Implement HELO and EHLO commands.
122  */
123 void smtp_hello(char *argbuf, int is_esmtp) {
124
125         safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
126
127         if (!is_esmtp) {
128                 cprintf("250 Greetings and joyous salutations.\r\n");
129         }
130         else {
131                 cprintf("250-Greetings and joyous salutations.\r\n");
132                 cprintf("250-HELP\r\n");
133                 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
134                 cprintf("250-PIPELINING\r\n");
135                 cprintf("250 AUTH=LOGIN\r\n");
136         }
137 }
138
139
140 /*
141  * Implement HELP command.
142  */
143 void smtp_help(void) {
144         cprintf("214-Commands accepted:\r\n");
145         cprintf("214-    DATA\r\n");
146         cprintf("214-    EHLO\r\n");
147         cprintf("214-    EXPN\r\n");
148         cprintf("214-    HELO\r\n");
149         cprintf("214-    HELP\r\n");
150         cprintf("214-    MAIL\r\n");
151         cprintf("214-    NOOP\r\n");
152         cprintf("214-    QUIT\r\n");
153         cprintf("214-    RCPT\r\n");
154         cprintf("214-    RSET\r\n");
155         cprintf("214-    VRFY\r\n");
156         cprintf("214     \r\n");
157 }
158
159
160 /*
161  *
162  */
163 void smtp_get_user(char *argbuf) {
164         char buf[SIZ];
165         char username[SIZ];
166
167         decode_base64(username, argbuf, SIZ);
168         lprintf(9, "Trying <%s>\n", username);
169         if (CtdlLoginExistingUser(username) == login_ok) {
170                 encode_base64(buf, "Password:");
171                 cprintf("334 %s\r\n", buf);
172                 SMTP->command_state = smtp_password;
173         }
174         else {
175                 cprintf("500 No such user.\r\n");
176                 SMTP->command_state = smtp_command;
177         }
178 }
179
180
181 /*
182  *
183  */
184 void smtp_get_pass(char *argbuf) {
185         char password[SIZ];
186
187         decode_base64(password, argbuf, SIZ);
188         lprintf(9, "Trying <%s>\n", password);
189         if (CtdlTryPassword(password) == pass_ok) {
190                 cprintf("235 Authentication successful.\r\n");
191                 lprintf(9, "SMTP authenticated login successful\n");
192                 CC->internal_pgm = 0;
193                 CC->cs_flags &= ~CS_STEALTH;
194         }
195         else {
196                 cprintf("500 Authentication failed.\r\n");
197         }
198         SMTP->command_state = smtp_command;
199 }
200
201
202 /*
203  *
204  */
205 void smtp_auth(char *argbuf) {
206         char buf[SIZ];
207
208         if (strncasecmp(argbuf, "login", 5) ) {
209                 cprintf("550 We only support LOGIN authentication.\r\n");
210                 return;
211         }
212
213         if (strlen(argbuf) >= 7) {
214                 smtp_get_user(&argbuf[6]);
215         }
216
217         else {
218                 encode_base64(buf, "Username:");
219                 cprintf("334 %s\r\n", buf);
220                 SMTP->command_state = smtp_user;
221         }
222 }
223
224
225 /*
226  * Back end for smtp_vrfy() command
227  */
228 void smtp_vrfy_backend(struct usersupp *us, void *data) {
229
230         if (!fuzzy_match(us, SMTP->vrfy_match)) {
231                 ++SMTP->vrfy_count;
232                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
233         }
234 }
235
236
237 /* 
238  * Implements the VRFY (verify user name) command.
239  * Performs fuzzy match on full user names.
240  */
241 void smtp_vrfy(char *argbuf) {
242         SMTP->vrfy_count = 0;
243         strcpy(SMTP->vrfy_match, argbuf);
244         ForEachUser(smtp_vrfy_backend, NULL);
245
246         if (SMTP->vrfy_count < 1) {
247                 cprintf("550 String does not match anything.\r\n");
248         }
249         else if (SMTP->vrfy_count == 1) {
250                 cprintf("250 %s <cit%ld@%s>\r\n",
251                         SMTP->vrfy_buffer.fullname,
252                         SMTP->vrfy_buffer.usernum,
253                         config.c_fqdn);
254         }
255         else if (SMTP->vrfy_count > 1) {
256                 cprintf("553 Request ambiguous: %d users matched.\r\n",
257                         SMTP->vrfy_count);
258         }
259
260 }
261
262
263
264 /*
265  * Back end for smtp_expn() command
266  */
267 void smtp_expn_backend(struct usersupp *us, void *data) {
268
269         if (!fuzzy_match(us, SMTP->vrfy_match)) {
270
271                 if (SMTP->vrfy_count >= 1) {
272                         cprintf("250-%s <cit%ld@%s>\r\n",
273                                 SMTP->vrfy_buffer.fullname,
274                                 SMTP->vrfy_buffer.usernum,
275                                 config.c_fqdn);
276                 }
277
278                 ++SMTP->vrfy_count;
279                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
280         }
281 }
282
283
284 /* 
285  * Implements the EXPN (expand user name) command.
286  * Performs fuzzy match on full user names.
287  */
288 void smtp_expn(char *argbuf) {
289         SMTP->vrfy_count = 0;
290         strcpy(SMTP->vrfy_match, argbuf);
291         ForEachUser(smtp_expn_backend, NULL);
292
293         if (SMTP->vrfy_count < 1) {
294                 cprintf("550 String does not match anything.\r\n");
295         }
296         else if (SMTP->vrfy_count >= 1) {
297                 cprintf("250 %s <cit%ld@%s>\r\n",
298                         SMTP->vrfy_buffer.fullname,
299                         SMTP->vrfy_buffer.usernum,
300                         config.c_fqdn);
301         }
302 }
303
304
305 /*
306  * Implements the RSET (reset state) command.
307  * Currently this just zeroes out the state buffer.  If pointers to data
308  * allocated with mallok() are ever placed in the state buffer, we have to
309  * be sure to phree() them first!
310  */
311 void smtp_rset(void) {
312         memset(SMTP, 0, sizeof(struct citsmtp));
313         if (CC->logged_in) {
314                 logout(CC);
315         }
316         cprintf("250 Zap!\r\n");
317 }
318
319 /*
320  * Clear out the portions of the state buffer that need to be cleared out
321  * after the DATA command finishes.
322  */
323 void smtp_data_clear(void) {
324         strcpy(SMTP->from, "");
325         SMTP->number_of_recipients = 0;
326         SMTP->delivery_mode = 0;
327         SMTP->message_originated_locally = 0;
328         memset(SMTP->valid, 0, sizeof(struct recptypes));
329 }
330
331
332
333 /*
334  * Implements the "MAIL From:" command
335  */
336 void smtp_mail(char *argbuf) {
337         char user[SIZ];
338         char node[SIZ];
339         int cvt;
340
341         if (strlen(SMTP->from) != 0) {
342                 cprintf("503 Only one sender permitted\r\n");
343                 return;
344         }
345
346         if (strncasecmp(argbuf, "From:", 5)) {
347                 cprintf("501 Syntax error\r\n");
348                 return;
349         }
350
351         strcpy(SMTP->from, &argbuf[5]);
352         striplt(SMTP->from);
353
354         if (strlen(SMTP->from) == 0) {
355                 cprintf("501 Empty sender name is not permitted\r\n");
356                 return;
357         }
358
359
360         /* If this SMTP connection is from a logged-in user, make sure that
361          * the user only sends email from his/her own address.
362          */
363         if (CC->logged_in) {
364                 cvt = convert_internet_address(user, node, SMTP->from);
365                 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
366                 if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
367                         cprintf("550 <%s> is not your address.\r\n",
368                                 SMTP->from);
369                         strcpy(SMTP->from, "");
370                         return;
371                 }
372                 else {
373                         SMTP->message_originated_locally = 1;
374                 }
375         }
376
377         /* Otherwise, make sure outsiders aren't trying to forge mail from
378          * this system.
379          */
380         else {
381                 cvt = convert_internet_address(user, node, SMTP->from);
382                 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
383                 if (CtdlHostAlias(node) == hostalias_localhost) {
384                         cprintf("550 You must log in to send mail from %s\r\n",
385                                 node);
386                         strcpy(SMTP->from, "");
387                         return;
388                 }
389         }
390
391         cprintf("250 Sender ok\r\n");
392 }
393
394
395
396 /*
397  * Implements the "RCPT To:" command
398  */
399 void smtp_rcpt(char *argbuf) {
400         int cvt;
401         char user[SIZ];
402         char node[SIZ];
403         char recp[SIZ];
404
405         if (strlen(SMTP->from) == 0) {
406                 cprintf("503 Need MAIL before RCPT\r\n");
407                 return;
408         }
409
410         if (strncasecmp(argbuf, "To:", 3)) {
411                 cprintf("501 Syntax error\r\n");
412                 return;
413         }
414
415         strcpy(recp, &argbuf[3]);
416         striplt(recp);
417         alias(recp);
418
419         cvt = convert_internet_address(user, node, recp);
420         snprintf(recp, sizeof recp, "%s@%s", user, node);
421         lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
422
423         switch(cvt) {
424                 case rfc822_address_locally_validated:
425                         SMTP->valid.num_local += 1;
426                         strcat
427                         return;
428                 case rfc822_address_on_citadel_network:
429                         cprintf("250 %s is a valid recipient.\r\n", user);
430                         CtdlReallocUserData(SYM_SMTP_RECPS,
431                                 strlen(SMTP_RECPS) + 1024 );
432                         if (strlen(SMTP_RECPS) > 0) {
433                                 strcat(SMTP_RECPS, "|");
434                         }
435                         strcat(SMTP_RECPS, user);
436                         return;
437
438                 case rfc822_room_delivery:
439                         cprintf("250 Delivering to room '%s'\r\n", user);
440                         CtdlReallocUserData(SYM_SMTP_ROOMS,
441                                 strlen(SMTP_ROOMS) + 1024 );
442                         if (strlen(SMTP_ROOMS) > 0) {
443                                 strcat(SMTP_ROOMS, "|");
444                         }
445                         strcat(SMTP_RECPS, user);
446                         return;
447
448                 case rfc822_no_such_user:
449                         cprintf("550 %s: no such user\r\n", recp);
450                         return;
451
452                 case rfc822_address_nonlocal:
453                         if (SMTP->message_originated_locally == 0) {
454                                 cprintf("551 Relaying denied.\r\n");
455                         }
456                         else {
457                                 cprintf("250 Remote recipient %s ok\r\n", recp);
458                                 CtdlReallocUserData(SYM_SMTP_RECPS,
459                                         strlen(SMTP_RECPS) + 1024 );
460                                 if (strlen(SMTP_RECPS) > 0) {
461                                         strcat(SMTP_RECPS, "|");
462                                 }
463                                 strcat(SMTP_RECPS, user);
464                                 return;
465                         }
466                         return;
467         }
468
469         cprintf("599 Unknown error\r\n");
470 }
471
472
473
474 /*
475  * Back end for smtp_data()  ... this does the actual delivery of the message
476  * Returns 0 on success, nonzero on failure
477  */
478 int smtp_message_delivery(struct CtdlMessage *msg) {
479         char user[1024];
480         char node[1024];
481         char name[1024];
482         char buf[1024];
483         char dtype[1024];
484         char room[1024];
485         int successful_saves = 0;       /* number of successful local saves */
486         int failed_saves = 0;           /* number of failed deliveries */
487         int remote_spools = 0;          /* number of copies to send out */
488         long msgid = (-1L);
489         int i;
490         struct usersupp userbuf;
491         char *instr;                    /* Remote delivery instructions */
492         struct CtdlMessage *imsg;
493
494         lprintf(9, "smtp_message_delivery() called\n");
495
496         /* Fill in 'from' fields with envelope information if missing */
497         process_rfc822_addr(SMTP->from, user, node, name);
498         if (msg->cm_fields['A']==NULL) msg->cm_fields['A'] = strdoop(user);
499         if (msg->cm_fields['N']==NULL) msg->cm_fields['N'] = strdoop(node);
500         if (msg->cm_fields['H']==NULL) msg->cm_fields['H'] = strdoop(name);
501         if (msg->cm_fields['O']==NULL) msg->cm_fields['O'] = strdoop(MAILROOM);
502
503         /* Save the message in the queue */
504         msgid = CtdlSubmitMsg(msg,
505                 NULL,
506                 SMTP_SPOOLOUT_ROOM);
507         ++successful_saves;
508
509         valid = validate_recipients(char *recipients) ;
510
511         for (i=0; i<SMTP->number_of_recipients; ++i) {
512                 extract_token(buf, SMTP_RECP, i, '\n');
513                 extract(dtype, buf, 0);
514
515                 /* Stuff local mailboxes */
516                 if (!strcasecmp(dtype, "local")) {
517                         extract(user, buf, 1);
518                         if (getuser(&userbuf, user) == 0) {
519                                 MailboxName(room, &userbuf, MAILROOM);
520                                 CtdlSaveMsgPointerInRoom(room, msgid, 0);
521                                 ++successful_saves;
522                         }
523                         else {
524                                 ++failed_saves;
525                         }
526                 }
527
528                 /* Delivery to local non-mailbox rooms */
529                 if (!strcasecmp(dtype, "room")) {
530                         extract(room, buf, 1);
531                         CtdlSaveMsgPointerInRoom(room, msgid, 0);
532                         ++successful_saves;
533                 }
534
535                 /* Delivery over the local Citadel network (IGnet) */
536                 if (!strcasecmp(dtype, "ignet")) {
537                         extract(user, buf, 1);
538                         extract(node, buf, 2);
539                         smtp_deliver_ignet(msg, user, node);
540                 }
541
542                 /* Remote delivery */
543                 if (!strcasecmp(dtype, "remote")) {
544                         extract(user, buf, 1);
545                         instr = reallok(instr, strlen(instr) + 1024);
546                         snprintf(&instr[strlen(instr)],
547                                 strlen(instr) + 1024,
548                                 "remote|%s|0\n",
549                                 user);
550                         ++remote_spools;
551                 }
552
553         }
554
555         /* If there are remote spools to be done, save the instructions */
556         if (remote_spools > 0) {
557                 imsg = mallok(sizeof(struct CtdlMessage));
558                 memset(imsg, 0, sizeof(struct CtdlMessage));
559                 imsg->cm_magic = CTDLMESSAGE_MAGIC;
560                 imsg->cm_anon_type = MES_NORMAL;
561                 imsg->cm_format_type = FMT_RFC822;
562                 imsg->cm_fields['M'] = instr;
563                 CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
564                 CtdlFreeMessage(imsg);
565         }
566
567         /* If there are no remote spools, delete the message */ 
568         else {
569                 phree(instr);   /* only needed here, because CtdlSubmitMsg()
570                                  * would free this buffer otherwise */
571                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, ""); 
572         }
573
574         return(failed_saves);
575 }
576
577
578
579 /*
580  * Implements the DATA command
581  */
582 void smtp_data(void) {
583         char *body;
584         struct CtdlMessage *msg;
585         int retval;
586         char nowstamp[SIZ];
587
588         if (strlen(SMTP->from) == 0) {
589                 cprintf("503 Need MAIL command first.\r\n");
590                 return;
591         }
592
593         if (SMTP->number_of_recipients < 1) {
594                 cprintf("503 Need RCPT command first.\r\n");
595                 return;
596         }
597
598         cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
599         
600         datestring(nowstamp, time(NULL), DATESTRING_RFC822);
601         body = mallok(4096);
602
603         if (body != NULL) snprintf(body, 4096,
604                 "Received: from %s\n"
605                 "       by %s;\n"
606                 "       %s\n",
607                         SMTP->helo_node,
608                         config.c_fqdn,
609                         nowstamp);
610         
611         body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
612         if (body == NULL) {
613                 cprintf("550 Unable to save message: internal error.\r\n");
614                 return;
615         }
616
617         lprintf(9, "Converting message...\n");
618         msg = convert_internet_message(body);
619
620         /* If the user is locally authenticated, FORCE the From: header to
621          * show up as the real sender.  Yes, this violates the RFC standard,
622          * but IT MAKES SENSE.  Comment it out if you don't like this behavior.
623          */
624         if (CC->logged_in) {
625                 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
626                 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
627                 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
628                 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
629                 msg->cm_fields['N'] = strdoop(config.c_nodename);
630                 msg->cm_fields['H'] = strdoop(config.c_humannode);
631         }
632
633         /* Submit the message into the Citadel system. */
634         retval = smtp_message_delivery(msg);
635         CtdlFreeMessage(msg);
636
637         if (!retval) {
638                 cprintf("250 Message accepted.\r\n");
639         }
640         else {
641                 cprintf("550 Internal delivery errors: %d\r\n", retval);
642         }
643
644         smtp_data_clear();      /* clear out the buffers now */
645 }
646
647
648
649
650 /* 
651  * Main command loop for SMTP sessions.
652  */
653 void smtp_command_loop(void) {
654         char cmdbuf[SIZ];
655
656         time(&CC->lastcmd);
657         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
658         if (client_gets(cmdbuf) < 1) {
659                 lprintf(3, "SMTP socket is broken.  Ending session.\n");
660                 CC->kill_me = 1;
661                 return;
662         }
663         lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
664         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
665
666         if (SMTP->command_state == smtp_user) {
667                 smtp_get_user(cmdbuf);
668         }
669
670         else if (SMTP->command_state == smtp_password) {
671                 smtp_get_pass(cmdbuf);
672         }
673
674         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
675                 smtp_auth(&cmdbuf[5]);
676         }
677
678         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
679                 smtp_data();
680         }
681
682         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
683                 smtp_hello(&cmdbuf[5], 1);
684         }
685
686         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
687                 smtp_expn(&cmdbuf[5]);
688         }
689
690         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
691                 smtp_hello(&cmdbuf[5], 0);
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 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(char *key, char *addr, int *status, char *dsn, long msgnum)
746 {
747         int sock = (-1);
748         char mxhosts[1024];
749         int num_mxhosts;
750         int mx;
751         int i;
752         char user[SIZ], node[SIZ], name[SIZ];
753         char buf[1024];
754         char mailfrom[1024];
755         int lp, rp;
756         FILE *msg_fp = NULL;
757         size_t msg_size;
758         size_t blocksize = 0;
759         int scan_done;
760
761         /* Parse out the host portion of the recipient address */
762         process_rfc822_addr(addr, user, node, name);
763
764         lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
765                 user, node, name);
766
767         /* Load the message out of the database into a temp file */
768         msg_fp = tmpfile();
769         if (msg_fp == NULL) {
770                 *status = 4;
771                 sprintf(dsn, "Error creating temporary file");
772                 return;
773         }
774         else {
775                 CtdlRedirectOutput(msg_fp, -1);
776                 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
777                 CtdlRedirectOutput(NULL, -1);
778                 fseek(msg_fp, 0L, SEEK_END);
779                 msg_size = ftell(msg_fp);
780         }
781
782
783         /* Extract something to send later in the 'MAIL From:' command */
784         strcpy(mailfrom, "");
785         rewind(msg_fp);
786         scan_done = 0;
787         do {
788                 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
789                 if (!strncasecmp(buf, "From:", 5)) {
790                         safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
791                         striplt(mailfrom);
792                         for (i=0; i<strlen(mailfrom); ++i) {
793                                 if (!isprint(mailfrom[i])) {
794                                         strcpy(&mailfrom[i], &mailfrom[i+1]);
795                                         i=0;
796                                 }
797                         }
798
799                         /* Strip out parenthesized names */
800                         lp = (-1);
801                         rp = (-1);
802                         for (i=0; i<strlen(mailfrom); ++i) {
803                                 if (mailfrom[i] == '(') lp = i;
804                                 if (mailfrom[i] == ')') rp = i;
805                         }
806                         if ((lp>0)&&(rp>lp)) {
807                                 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
808                         }
809
810                         /* Prefer brokketized names */
811                         lp = (-1);
812                         rp = (-1);
813                         for (i=0; i<strlen(mailfrom); ++i) {
814                                 if (mailfrom[i] == '<') lp = i;
815                                 if (mailfrom[i] == '>') rp = i;
816                         }
817                         if ((lp>=0)&&(rp>lp)) {
818                                 mailfrom[rp] = 0;
819                                 strcpy(mailfrom, &mailfrom[lp]);
820                         }
821
822                         scan_done = 1;
823                 }
824         } while (scan_done == 0);
825         if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
826
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         for (mx=0; mx<num_mxhosts; ++mx) {
838                 extract(buf, mxhosts, mx);
839                 lprintf(9, "Trying <%s>\n", buf);
840                 sock = sock_connect(buf, "25", "tcp");
841                 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
842                 if (sock >= 0) lprintf(9, "Connected!\n");
843                 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
844                 if (sock >= 0) break;
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         int mes_type = 0;
1040
1041         lprintf(9, "smtp_do_bounce() called\n");
1042         strcpy(bounceto, "");
1043
1044         lines = num_tokens(instr, '\n');
1045
1046
1047         /* See if it's time to give up on delivery of this message */
1048         for (i=0; i<lines; ++i) {
1049                 extract_token(buf, instr, i, '\n');
1050                 extract(key, buf, 0);
1051                 extract(addr, buf, 1);
1052                 if (!strcasecmp(key, "submitted")) {
1053                         submitted = atol(addr);
1054                 }
1055         }
1056
1057         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1058                 give_up = 1;
1059         }
1060
1061
1062
1063         bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1064         if (bmsg == NULL) return;
1065         memset(bmsg, 0, sizeof(struct CtdlMessage));
1066
1067         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1068         bmsg->cm_anon_type = MES_NORMAL;
1069         bmsg->cm_format_type = 1;
1070         bmsg->cm_fields['A'] = strdoop("Citadel");
1071         bmsg->cm_fields['O'] = strdoop(MAILROOM);
1072         bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1073
1074         if (give_up) bmsg->cm_fields['M'] = strdoop(
1075 "A message you sent could not be delivered to some or all of its recipients\n"
1076 "due to prolonged unavailability of its destination(s).\n"
1077 "Giving up on the following addresses:\n\n"
1078 );
1079
1080         else bmsg->cm_fields['M'] = strdoop(
1081 "A message you sent could not be delivered to some or all of its recipients.\n"
1082 "The following addresses were undeliverable:\n\n"
1083 );
1084
1085         /*
1086          * Now go through the instructions checking for stuff.
1087          */
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                 TRACE;
1142                 if (strlen(bounceto) == 0) {
1143                         lprintf(7, "No bounce address specified\n");
1144                         bounce_msgid = (-1L);
1145                 }
1146 /* FIXME this won't work
1147                 else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
1148                         lprintf(7, "Invalid bounce address <%s>\n", bounceto);
1149                         bounce_msgid = (-1L);
1150                 }
1151                 else {
1152                         bounce_msgid = CtdlSubmitMsg(bmsg,
1153                                 bounceto,
1154                                 "", mes_type);
1155                 }
1156  */
1157                 TRACE;
1158
1159                 /* Otherwise, go to the Aide> room */
1160                 lprintf(9, "bounce to room?\n");
1161                 if (bounce_msgid < 0L) bounce_msgid = CtdlSubmitMsg(bmsg,
1162                         NULL, AIDEROOM);
1163         }
1164
1165         CtdlFreeMessage(bmsg);
1166         lprintf(9, "Done processing bounces\n");
1167 }
1168
1169
1170 /*
1171  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1172  * set of delivery instructions for completed deliveries and remove them.
1173  *
1174  * It returns the number of incomplete deliveries remaining.
1175  */
1176 int smtp_purge_completed_deliveries(char *instr) {
1177         int i;
1178         int lines;
1179         int status;
1180         char buf[1024];
1181         char key[1024];
1182         char addr[1024];
1183         char dsn[1024];
1184         int completed;
1185         int incomplete = 0;
1186
1187         lines = num_tokens(instr, '\n');
1188         for (i=0; i<lines; ++i) {
1189                 extract_token(buf, instr, i, '\n');
1190                 extract(key, buf, 0);
1191                 extract(addr, buf, 1);
1192                 status = extract_int(buf, 2);
1193                 extract(dsn, buf, 3);
1194
1195                 completed = 0;
1196
1197                 if (
1198                    (!strcasecmp(key, "local"))
1199                    || (!strcasecmp(key, "remote"))
1200                    || (!strcasecmp(key, "ignet"))
1201                    || (!strcasecmp(key, "room"))
1202                 ) {
1203                         if (status == 2) completed = 1;
1204                         else ++incomplete;
1205                 }
1206
1207                 if (completed) {
1208                         remove_token(instr, i, '\n');
1209                         --i;
1210                         --lines;
1211                 }
1212         }
1213
1214         return(incomplete);
1215 }
1216
1217
1218 /*
1219  * smtp_do_procmsg()
1220  *
1221  * Called by smtp_do_queue() to handle an individual message.
1222  */
1223 void smtp_do_procmsg(long msgnum, void *userdata) {
1224         struct CtdlMessage *msg;
1225         char *instr = NULL;
1226         char *results = NULL;
1227         int i;
1228         int lines;
1229         int status;
1230         char buf[1024];
1231         char key[1024];
1232         char addr[1024];
1233         char dsn[1024];
1234         long text_msgid = (-1);
1235         int incomplete_deliveries_remaining;
1236         time_t attempted = 0L;
1237         time_t last_attempted = 0L;
1238         time_t retry = SMTP_RETRY_INTERVAL;
1239
1240         lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1241
1242         msg = CtdlFetchMessage(msgnum);
1243         if (msg == NULL) {
1244                 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1245                 return;
1246         }
1247
1248         instr = strdoop(msg->cm_fields['M']);
1249         CtdlFreeMessage(msg);
1250
1251         /* Strip out the headers amd any other non-instruction line */
1252         lines = num_tokens(instr, '\n');
1253         for (i=0; i<lines; ++i) {
1254                 extract_token(buf, instr, i, '\n');
1255                 if (num_tokens(buf, '|') < 2) {
1256                         remove_token(instr, i, '\n');
1257                         --lines;
1258                         --i;
1259                 }
1260         }
1261
1262         /* Learn the message ID and find out about recent delivery attempts */
1263         lines = num_tokens(instr, '\n');
1264         for (i=0; i<lines; ++i) {
1265                 extract_token(buf, instr, i, '\n');
1266                 extract(key, buf, 0);
1267                 if (!strcasecmp(key, "msgid")) {
1268                         text_msgid = extract_long(buf, 1);
1269                 }
1270                 if (!strcasecmp(key, "retry")) {
1271                         /* double the retry interval after each attempt */
1272                         retry = extract_long(buf, 1) * 2L;
1273                         if (retry > SMTP_RETRY_MAX) {
1274                                 retry = SMTP_RETRY_MAX;
1275                         }
1276                         remove_token(instr, i, '\n');
1277                 }
1278                 if (!strcasecmp(key, "attempted")) {
1279                         attempted = extract_long(buf, 1);
1280                         if (attempted > last_attempted)
1281                                 last_attempted = attempted;
1282                 }
1283         }
1284
1285         /*
1286          * Postpone delivery if we've already tried recently.
1287          */
1288         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1289                 lprintf(7, "Retry time not yet reached.\n");
1290                 phree(instr);
1291                 return;
1292         }
1293
1294
1295         /*
1296          * Bail out if there's no actual message associated with this
1297          */
1298         if (text_msgid < 0L) {
1299                 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1300                 phree(instr);
1301                 return;
1302         }
1303
1304         /* Plow through the instructions looking for 'remote' directives and
1305          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1306          * were experienced and it's time to try again)
1307          */
1308         lines = num_tokens(instr, '\n');
1309         for (i=0; i<lines; ++i) {
1310                 extract_token(buf, instr, i, '\n');
1311                 extract(key, buf, 0);
1312                 extract(addr, buf, 1);
1313                 status = extract_int(buf, 2);
1314                 extract(dsn, buf, 3);
1315                 if ( (!strcasecmp(key, "remote"))
1316                    && ((status==0)||(status==3)||(status==4)) ) {
1317                         remove_token(instr, i, '\n');
1318                         --i;
1319                         --lines;
1320                         lprintf(9, "SMTP: Trying <%s>\n", addr);
1321                         smtp_try(key, addr, &status, 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                                 sprintf(&results[strlen(results)],
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->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1413                 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1414                 return;
1415         }
1416         CtdlForEachMessage(MSGS_ALL, 0L, (-127),
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", OK);
1457                 return;
1458         }
1459
1460         else {
1461                 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1462         }
1463
1464 }
1465
1466
1467
1468
1469 /*****************************************************************************/
1470 /*                      MODULE INITIALIZATION STUFF                          */
1471 /*****************************************************************************/
1472
1473
1474 char *Dynamic_Module_Init(void)
1475 {
1476         SYM_SMTP = CtdlGetDynamicSymbol();
1477         SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1478
1479         CtdlRegisterServiceHook(config.c_smtp_port,     /* On the net... */
1480                                 NULL,
1481                                 smtp_greeting,
1482                                 smtp_command_loop);
1483
1484         CtdlRegisterServiceHook(0,                      /* ...and locally */
1485                                 "smtp.socket",
1486                                 smtp_greeting,
1487                                 smtp_command_loop);
1488
1489         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1490         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1491         CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1492         return "$Id$";
1493 }