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