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