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