b7e78ea8cd379b7b077462086a6774917ccf3248
[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         TRACE;
388         alias(recp);
389         TRACE;
390
391         cvt = convert_internet_address(user, node, recp);
392         snprintf(recp, sizeof recp, "%s@%s", user, node);
393         lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
394
395         switch(cvt) {
396                 case rfc822_address_locally_validated:
397                         cprintf("250 %s is a valid recipient.\r\n", user);
398                         ++SMTP->number_of_recipients;
399                         CtdlReallocUserData(SYM_SMTP_RECP,
400                                 strlen(SMTP_RECP) + 1024 );
401                         strcat(SMTP_RECP, "local|");
402                         strcat(SMTP_RECP, user);
403                         strcat(SMTP_RECP, "|0\n");
404                         return;
405
406                 case rfc822_room_delivery:
407                         cprintf("250 Delivering to room '%s'\r\n", user);
408                         ++SMTP->number_of_recipients;
409                         CtdlReallocUserData(SYM_SMTP_RECP,
410                                 strlen(SMTP_RECP) + 1024 );
411                         strcat(SMTP_RECP, "room|");
412                         strcat(SMTP_RECP, user);
413                         strcat(SMTP_RECP, "|0|\n");
414                         return;
415
416                 case rfc822_no_such_user:
417                         cprintf("550 %s: no such user\r\n", recp);
418                         return;
419
420                 case rfc822_address_on_citadel_network:
421                         cprintf("250 %s is on the local network\r\n", recp);
422                         ++SMTP->number_of_recipients;
423                         CtdlReallocUserData(SYM_SMTP_RECP,
424                                 strlen(SMTP_RECP) + 1024 );
425                         strcat(SMTP_RECP, "ignet|");
426                         strcat(SMTP_RECP, user);
427                         strcat(SMTP_RECP, "|");
428                         strcat(SMTP_RECP, node);
429                         strcat(SMTP_RECP, "|0|\n");
430                         return;
431
432                 case rfc822_address_nonlocal:
433                         if (SMTP->message_originated_locally == 0) {
434                                 cprintf("551 Relaying denied\r\n");
435                         }
436                         else {
437                                 cprintf("250 Remote recipient %s ok\r\n", recp);
438                                 ++SMTP->number_of_recipients;
439                                 CtdlReallocUserData(SYM_SMTP_RECP,
440                                         strlen(SMTP_RECP) + 1024 );
441                                 strcat(SMTP_RECP, "remote|");
442                                 strcat(SMTP_RECP, recp);
443                                 strcat(SMTP_RECP, "|0|\n");
444                                 return;
445                         }
446                         return;
447         }
448
449         cprintf("599 Unknown error\r\n");
450 }
451
452
453
454 /*
455  * Send a message out through the local network
456  * (This is kind of ugly.  IGnet should be done using clean server-to-server
457  * code instead of the old style spool.)
458  */
459 void smtp_deliver_ignet(struct CtdlMessage *msg, char *user, char *dest) {
460         struct ser_ret smr;
461         char *hold_R, *hold_D, *hold_O;
462         FILE *fp;
463         char filename[256];
464         static int seq = 0;
465
466         lprintf(9, "smtp_deliver_ignet(msg, %s, %s)\n", user, dest);
467
468         hold_R = msg->cm_fields['R'];
469         hold_D = msg->cm_fields['D'];
470         hold_O = msg->cm_fields['O'];
471         msg->cm_fields['R'] = user;
472         msg->cm_fields['D'] = dest;
473         msg->cm_fields['O'] = MAILROOM;
474
475         serialize_message(&smr, msg);
476
477         msg->cm_fields['R'] = hold_R;
478         msg->cm_fields['D'] = hold_D;
479         msg->cm_fields['O'] = hold_O;
480
481         if (smr.len != 0) {
482                 snprintf(filename, sizeof filename,
483                         "./network/spoolin/%s.%04x.%04x",
484                         dest, getpid(), ++seq);
485                 lprintf(9, "spool file name is <%s>\n", filename);
486                 fp = fopen(filename, "wb");
487                 if (fp != NULL) {
488                         fwrite(smr.ser, smr.len, 1, fp);
489                         fclose(fp);
490                 }
491                 phree(smr.ser);
492         }
493
494 }
495
496
497
498 /*
499  * Back end for smtp_data()  ... this does the actual delivery of the message
500  * Returns 0 on success, nonzero on failure
501  */
502 int smtp_message_delivery(struct CtdlMessage *msg) {
503         char user[1024];
504         char node[1024];
505         char name[1024];
506         char buf[1024];
507         char dtype[1024];
508         char room[1024];
509         int successful_saves = 0;       /* number of successful local saves */
510         int failed_saves = 0;           /* number of failed deliveries */
511         int remote_spools = 0;          /* number of copies to send out */
512         long msgid = (-1L);
513         int i;
514         struct usersupp userbuf;
515         char *instr;                    /* Remote delivery instructions */
516         struct CtdlMessage *imsg;
517
518         lprintf(9, "smtp_message_delivery() called\n");
519
520         /* Fill in 'from' fields with envelope information if missing */
521         process_rfc822_addr(SMTP->from, user, node, name);
522         if (msg->cm_fields['A']==NULL) msg->cm_fields['A'] = strdoop(user);
523         if (msg->cm_fields['N']==NULL) msg->cm_fields['N'] = strdoop(node);
524         if (msg->cm_fields['H']==NULL) msg->cm_fields['H'] = strdoop(name);
525         if (msg->cm_fields['O']==NULL) msg->cm_fields['O'] = strdoop(MAILROOM);
526
527         /* Save the message in the queue */
528         msgid = CtdlSaveMsg(msg,
529                 "",
530                 SMTP_SPOOLOUT_ROOM,
531                 MES_LOCAL);
532         ++successful_saves;
533
534         instr = mallok(1024);
535         snprintf(instr, 1024,
536                         "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
537                         "bounceto|%s\n",
538                 SPOOLMIME, msgid, time(NULL),
539                 SMTP->from );
540
541         for (i=0; i<SMTP->number_of_recipients; ++i) {
542                 extract_token(buf, SMTP_RECP, i, '\n');
543                 extract(dtype, buf, 0);
544
545                 /* Stuff local mailboxes */
546                 if (!strcasecmp(dtype, "local")) {
547                         extract(user, buf, 1);
548                         if (getuser(&userbuf, user) == 0) {
549                                 MailboxName(room, &userbuf, MAILROOM);
550                                 CtdlSaveMsgPointerInRoom(room, msgid, 0);
551                                 ++successful_saves;
552                         }
553                         else {
554                                 ++failed_saves;
555                         }
556                 }
557
558                 /* Delivery to local non-mailbox rooms */
559                 if (!strcasecmp(dtype, "room")) {
560                         extract(room, buf, 1);
561                         CtdlSaveMsgPointerInRoom(room, msgid, 0);
562                         ++successful_saves;
563                 }
564
565                 /* Delivery over the local Citadel network (IGnet) */
566                 if (!strcasecmp(dtype, "ignet")) {
567                         extract(user, buf, 1);
568                         extract(node, buf, 2);
569                         smtp_deliver_ignet(msg, user, node);
570                 }
571
572                 /* Remote delivery */
573                 if (!strcasecmp(dtype, "remote")) {
574                         extract(user, buf, 1);
575                         instr = reallok(instr, strlen(instr) + 1024);
576                         snprintf(&instr[strlen(instr)],
577                                 strlen(instr) + 1024,
578                                 "remote|%s|0\n",
579                                 user);
580                         ++remote_spools;
581                 }
582
583         }
584
585         /* If there are remote spools to be done, save the instructions */
586         if (remote_spools > 0) {
587                 imsg = mallok(sizeof(struct CtdlMessage));
588                 memset(imsg, 0, sizeof(struct CtdlMessage));
589                 imsg->cm_magic = CTDLMESSAGE_MAGIC;
590                 imsg->cm_anon_type = MES_NORMAL;
591                 imsg->cm_format_type = FMT_RFC822;
592                 imsg->cm_fields['M'] = instr;
593                 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
594                 CtdlFreeMessage(imsg);
595         }
596
597         /* If there are no remote spools, delete the message */ 
598         else {
599                 phree(instr);   /* only needed here, because CtdlSaveMsg()
600                                  * would free this buffer otherwise */
601                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, ""); 
602         }
603
604         return(failed_saves);
605 }
606
607
608
609 /*
610  * Implements the DATA command
611  */
612 void smtp_data(void) {
613         char *body;
614         struct CtdlMessage *msg;
615         int retval;
616         char nowstamp[256];
617
618         if (strlen(SMTP->from) == 0) {
619                 cprintf("503 Need MAIL command first.\r\n");
620                 return;
621         }
622
623         if (SMTP->number_of_recipients < 1) {
624                 cprintf("503 Need RCPT command first.\r\n");
625                 return;
626         }
627
628         cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
629         
630         generate_rfc822_datestamp(nowstamp, time(NULL));
631         body = mallok(4096);
632
633         if (body != NULL) snprintf(body, 4096,
634                 "Received: from %s\n"
635                 "       by %s;\n"
636                 "       %s\n",
637                         SMTP->helo_node,
638                         config.c_fqdn,
639                         nowstamp);
640         
641         body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
642         if (body == NULL) {
643                 cprintf("550 Unable to save message text: internal error.\r\n");
644                 return;
645         }
646
647         lprintf(9, "Converting message...\n");
648         msg = convert_internet_message(body);
649
650         /* If the user is locally authenticated, FORCE the From: header to
651          * show up as the real sender
652          */
653         if (CC->logged_in) {
654                 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
655                 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
656                 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
657                 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
658                 msg->cm_fields['N'] = strdoop(config.c_nodename);
659                 msg->cm_fields['H'] = strdoop(config.c_humannode);
660         }
661
662         retval = smtp_message_delivery(msg);
663         CtdlFreeMessage(msg);
664
665         if (!retval) {
666                 cprintf("250 Message accepted for delivery.\r\n");
667         }
668         else {
669                 cprintf("550 Internal delivery errors: %d\r\n", retval);
670         }
671
672         smtp_data_clear();      /* clear out the buffers now */
673 }
674
675
676
677
678 /* 
679  * Main command loop for SMTP sessions.
680  */
681 void smtp_command_loop(void) {
682         char cmdbuf[256];
683
684         time(&CC->lastcmd);
685         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
686         if (client_gets(cmdbuf) < 1) {
687                 lprintf(3, "SMTP socket is broken.  Ending session.\n");
688                 CC->kill_me = 1;
689                 return;
690         }
691         lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
692         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
693
694         if (SMTP->command_state == smtp_user) {
695                 smtp_get_user(cmdbuf);
696         }
697
698         else if (SMTP->command_state == smtp_password) {
699                 smtp_get_pass(cmdbuf);
700         }
701
702         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
703                 smtp_auth(&cmdbuf[5]);
704         }
705
706         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
707                 smtp_data();
708         }
709
710         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
711                 smtp_hello(&cmdbuf[5], 1);
712         }
713
714         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
715                 smtp_expn(&cmdbuf[5]);
716         }
717
718         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
719                 smtp_hello(&cmdbuf[5], 0);
720         }
721
722         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
723                 smtp_help();
724         }
725
726         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
727                 smtp_mail(&cmdbuf[5]);
728         }
729
730         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
731                 cprintf("250 This command successfully did nothing.\r\n");
732         }
733
734         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
735                 cprintf("221 Goodbye...\r\n");
736                 CC->kill_me = 1;
737                 return;
738                 }
739
740         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
741                 smtp_rcpt(&cmdbuf[5]);
742         }
743
744         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
745                 smtp_rset();
746         }
747
748         else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
749                 smtp_vrfy(&cmdbuf[5]);
750         }
751
752         else {
753                 cprintf("502 I'm sorry Dave, I'm afraid I can't do that.\r\n");
754         }
755
756 }
757
758
759
760
761 /*****************************************************************************/
762 /*               SMTP CLIENT (OUTBOUND PROCESSING) STUFF                     */
763 /*****************************************************************************/
764
765
766
767 /*
768  * smtp_try()
769  *
770  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
771  *
772  */
773 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
774 {
775         int sock = (-1);
776         char mxhosts[1024];
777         int num_mxhosts;
778         int mx;
779         int i;
780         char user[256], node[256], name[256];
781         char buf[1024];
782         char mailfrom[1024];
783         int lp, rp;
784         FILE *msg_fp = NULL;
785         size_t msg_size;
786         size_t blocksize = 0;
787         int scan_done;
788
789         /* Parse out the host portion of the recipient address */
790         process_rfc822_addr(addr, user, node, name);
791
792         lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
793                 user, node, name);
794
795         /* Load the message out of the database into a temp file */
796         msg_fp = tmpfile();
797         if (msg_fp == NULL) {
798                 *status = 4;
799                 sprintf(dsn, "Error creating temporary file");
800                 return;
801         }
802         else {
803                 CtdlRedirectOutput(msg_fp, -1);
804                 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
805                 CtdlRedirectOutput(NULL, -1);
806                 fseek(msg_fp, 0L, SEEK_END);
807                 msg_size = ftell(msg_fp);
808         }
809
810
811         /* Extract something to send later in the 'MAIL From:' command */
812         strcpy(mailfrom, "");
813         rewind(msg_fp);
814         scan_done = 0;
815         do {
816                 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
817                 if (!strncasecmp(buf, "From:", 5)) {
818                         safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
819                         striplt(mailfrom);
820                         for (i=0; i<strlen(mailfrom); ++i) {
821                                 if (!isprint(mailfrom[i])) {
822                                         strcpy(&mailfrom[i], &mailfrom[i+1]);
823                                         i=0;
824                                 }
825                         }
826
827                         /* Strip out parenthesized names */
828                         lp = (-1);
829                         rp = (-1);
830                         for (i=0; i<strlen(mailfrom); ++i) {
831                                 if (mailfrom[i] == '(') lp = i;
832                                 if (mailfrom[i] == ')') rp = i;
833                         }
834                         if ((lp>0)&&(rp>lp)) {
835                                 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
836                         }
837
838                         /* Prefer brokketized names */
839                         lp = (-1);
840                         rp = (-1);
841                         for (i=0; i<strlen(mailfrom); ++i) {
842                                 if (mailfrom[i] == '<') lp = i;
843                                 if (mailfrom[i] == '>') rp = i;
844                         }
845                         if ((lp>=0)&&(rp>lp)) {
846                                 mailfrom[rp] = 0;
847                                 strcpy(mailfrom, &mailfrom[lp]);
848                         }
849
850                         scan_done = 1;
851                 }
852         } while (scan_done == 0);
853         if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
854
855
856         /* Figure out what mail exchanger host we have to connect to */
857         num_mxhosts = getmx(mxhosts, node);
858         lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
859         if (num_mxhosts < 1) {
860                 *status = 5;
861                 snprintf(dsn, 256, "No MX hosts found for <%s>", node);
862                 return;
863         }
864
865         for (mx=0; mx<num_mxhosts; ++mx) {
866                 extract(buf, mxhosts, mx);
867                 lprintf(9, "Trying <%s>\n", buf);
868                 sock = sock_connect(buf, "25", "tcp");
869                 snprintf(dsn, 256, "Could not connect: %s", strerror(errno));
870                 if (sock >= 0) lprintf(9, "Connected!\n");
871                 if (sock < 0) snprintf(dsn, 256, "%s", strerror(errno));
872                 if (sock >= 0) break;
873         }
874
875         if (sock < 0) {
876                 *status = 4;    /* dsn is already filled in */
877                 return;
878         }
879
880         /* Process the SMTP greeting from the server */
881         if (ml_sock_gets(sock, buf) < 0) {
882                 *status = 4;
883                 strcpy(dsn, "Connection broken during SMTP conversation");
884                 goto bail;
885         }
886         lprintf(9, "<%s\n", buf);
887         if (buf[0] != '2') {
888                 if (buf[0] == '4') {
889                         *status = 4;
890                         safestrncpy(dsn, &buf[4], 1023);
891                         goto bail;
892                 }
893                 else {
894                         *status = 5;
895                         safestrncpy(dsn, &buf[4], 1023);
896                         goto bail;
897                 }
898         }
899
900         /* At this point we know we are talking to a real SMTP server */
901
902         /* Do a HELO command */
903         snprintf(buf, sizeof buf, "HELO %s", config.c_fqdn);
904         lprintf(9, ">%s\n", buf);
905         sock_puts_crlf(sock, buf);
906         if (ml_sock_gets(sock, buf) < 0) {
907                 *status = 4;
908                 strcpy(dsn, "Connection broken during SMTP conversation");
909                 goto bail;
910         }
911         lprintf(9, "<%s\n", buf);
912         if (buf[0] != '2') {
913                 if (buf[0] == '4') {
914                         *status = 4;
915                         safestrncpy(dsn, &buf[4], 1023);
916                         goto bail;
917                 }
918                 else {
919                         *status = 5;
920                         safestrncpy(dsn, &buf[4], 1023);
921                         goto bail;
922                 }
923         }
924
925
926         /* HELO succeeded, now try the MAIL From: command */
927         snprintf(buf, sizeof buf, "MAIL From: <%s>", mailfrom);
928         lprintf(9, ">%s\n", buf);
929         sock_puts_crlf(sock, buf);
930         if (ml_sock_gets(sock, buf) < 0) {
931                 *status = 4;
932                 strcpy(dsn, "Connection broken during SMTP conversation");
933                 goto bail;
934         }
935         lprintf(9, "<%s\n", buf);
936         if (buf[0] != '2') {
937                 if (buf[0] == '4') {
938                         *status = 4;
939                         safestrncpy(dsn, &buf[4], 1023);
940                         goto bail;
941                 }
942                 else {
943                         *status = 5;
944                         safestrncpy(dsn, &buf[4], 1023);
945                         goto bail;
946                 }
947         }
948
949
950         /* MAIL succeeded, now try the RCPT To: command */
951         snprintf(buf, sizeof buf, "RCPT To: <%s>", addr);
952         lprintf(9, ">%s\n", buf);
953         sock_puts_crlf(sock, buf);
954         if (ml_sock_gets(sock, buf) < 0) {
955                 *status = 4;
956                 strcpy(dsn, "Connection broken during SMTP conversation");
957                 goto bail;
958         }
959         lprintf(9, "<%s\n", buf);
960         if (buf[0] != '2') {
961                 if (buf[0] == '4') {
962                         *status = 4;
963                         safestrncpy(dsn, &buf[4], 1023);
964                         goto bail;
965                 }
966                 else {
967                         *status = 5;
968                         safestrncpy(dsn, &buf[4], 1023);
969                         goto bail;
970                 }
971         }
972
973
974         /* RCPT succeeded, now try the DATA command */
975         lprintf(9, ">DATA\n");
976         sock_puts_crlf(sock, "DATA");
977         if (ml_sock_gets(sock, buf) < 0) {
978                 *status = 4;
979                 strcpy(dsn, "Connection broken during SMTP conversation");
980                 goto bail;
981         }
982         lprintf(9, "<%s\n", buf);
983         if (buf[0] != '3') {
984                 if (buf[0] == '4') {
985                         *status = 3;
986                         safestrncpy(dsn, &buf[4], 1023);
987                         goto bail;
988                 }
989                 else {
990                         *status = 5;
991                         safestrncpy(dsn, &buf[4], 1023);
992                         goto bail;
993                 }
994         }
995
996         /* If we reach this point, the server is expecting data */
997         rewind(msg_fp);
998         while (msg_size > 0) {
999                 blocksize = sizeof(buf);
1000                 if (blocksize > msg_size) blocksize = msg_size;
1001                 fread(buf, blocksize, 1, msg_fp);
1002                 sock_write(sock, buf, blocksize);
1003                 msg_size -= blocksize;
1004         }
1005         if (buf[blocksize-1] != 10) {
1006                 lprintf(5, "Possible problem: message did not correctly "
1007                         "terminate. (expecting 0x10, got 0x%02x)\n",
1008                                 buf[blocksize-1]);
1009         }
1010
1011         sock_write(sock, ".\r\n", 3);
1012         if (ml_sock_gets(sock, buf) < 0) {
1013                 *status = 4;
1014                 strcpy(dsn, "Connection broken during SMTP conversation");
1015                 goto bail;
1016         }
1017         lprintf(9, "%s\n", buf);
1018         if (buf[0] != '2') {
1019                 if (buf[0] == '4') {
1020                         *status = 4;
1021                         safestrncpy(dsn, &buf[4], 1023);
1022                         goto bail;
1023                 }
1024                 else {
1025                         *status = 5;
1026                         safestrncpy(dsn, &buf[4], 1023);
1027                         goto bail;
1028                 }
1029         }
1030
1031         /* We did it! */
1032         safestrncpy(dsn, &buf[4], 1023);
1033         *status = 2;
1034
1035         lprintf(9, ">QUIT\n");
1036         sock_puts_crlf(sock, "QUIT");
1037         ml_sock_gets(sock, buf);
1038         lprintf(9, "<%s\n", buf);
1039
1040 bail:   if (msg_fp != NULL) fclose(msg_fp);
1041         sock_close(sock);
1042         return;
1043 }
1044
1045
1046
1047 /*
1048  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1049  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1050  * a "bounce" message (delivery status notification).
1051  */
1052 void smtp_do_bounce(char *instr) {
1053         int i;
1054         int lines;
1055         int status;
1056         char buf[1024];
1057         char key[1024];
1058         char addr[1024];
1059         char dsn[1024];
1060         char bounceto[1024];
1061         int num_bounces = 0;
1062         int bounce_this = 0;
1063         long bounce_msgid = (-1);
1064         time_t submitted = 0L;
1065         struct CtdlMessage *bmsg = NULL;
1066         int give_up = 0;
1067         int mes_type = 0;
1068
1069         lprintf(9, "smtp_do_bounce() called\n");
1070         strcpy(bounceto, "");
1071
1072         lines = num_tokens(instr, '\n');
1073
1074
1075         /* See if it's time to give up on delivery of this message */
1076         for (i=0; i<lines; ++i) {
1077                 extract_token(buf, instr, i, '\n');
1078                 extract(key, buf, 0);
1079                 extract(addr, buf, 1);
1080                 if (!strcasecmp(key, "submitted")) {
1081                         submitted = atol(addr);
1082                 }
1083         }
1084
1085         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1086                 give_up = 1;
1087         }
1088
1089
1090
1091         bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1092         if (bmsg == NULL) return;
1093         memset(bmsg, 0, sizeof(struct CtdlMessage));
1094
1095         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1096         bmsg->cm_anon_type = MES_NORMAL;
1097         bmsg->cm_format_type = 1;
1098         bmsg->cm_fields['A'] = strdoop("Citadel");
1099         bmsg->cm_fields['O'] = strdoop(MAILROOM);
1100         bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1101
1102         if (give_up) bmsg->cm_fields['M'] = strdoop(
1103 "A message you sent could not be delivered to some or all of its recipients\n"
1104 "due to prolonged unavailability of its destination(s).\n"
1105 "Giving up on the following addresses:\n\n"
1106 );
1107
1108         else bmsg->cm_fields['M'] = strdoop(
1109 "A message you sent could not be delivered to some or all of its recipients.\n"
1110 "The following addresses were undeliverable:\n\n"
1111 );
1112
1113         /*
1114          * Now go through the instructions checking for stuff.
1115          */
1116
1117         for (i=0; i<lines; ++i) {
1118                 extract_token(buf, instr, i, '\n');
1119                 extract(key, buf, 0);
1120                 extract(addr, buf, 1);
1121                 status = extract_int(buf, 2);
1122                 extract(dsn, buf, 3);
1123                 bounce_this = 0;
1124
1125                 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1126                         key, addr, status, dsn);
1127
1128                 if (!strcasecmp(key, "bounceto")) {
1129                         strcpy(bounceto, addr);
1130                 }
1131
1132                 if (
1133                    (!strcasecmp(key, "local"))
1134                    || (!strcasecmp(key, "remote"))
1135                    || (!strcasecmp(key, "ignet"))
1136                    || (!strcasecmp(key, "room"))
1137                 ) {
1138                         if (status == 5) bounce_this = 1;
1139                         if (give_up) bounce_this = 1;
1140                 }
1141
1142                 if (bounce_this) {
1143                         ++num_bounces;
1144
1145                         if (bmsg->cm_fields['M'] == NULL) {
1146                                 lprintf(2, "ERROR ... M field is null "
1147                                         "(%s:%d)\n", __FILE__, __LINE__);
1148                         }
1149
1150                         bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1151                                 strlen(bmsg->cm_fields['M']) + 1024 );
1152                         strcat(bmsg->cm_fields['M'], addr);
1153                         strcat(bmsg->cm_fields['M'], ": ");
1154                         strcat(bmsg->cm_fields['M'], dsn);
1155                         strcat(bmsg->cm_fields['M'], "\n");
1156
1157                         remove_token(instr, i, '\n');
1158                         --i;
1159                         --lines;
1160                 }
1161         }
1162
1163         /* Deliver the bounce if there's anything worth mentioning */
1164         lprintf(9, "num_bounces = %d\n", num_bounces);
1165         if (num_bounces > 0) {
1166
1167                 /* First try the user who sent the message */
1168                 lprintf(9, "bounce to user? <%s>\n", bounceto);
1169                 TRACE;
1170                 if (strlen(bounceto) == 0) {
1171                         lprintf(7, "No bounce address specified\n");
1172                         bounce_msgid = (-1L);
1173                 }
1174                 else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
1175                         lprintf(7, "Invalid bounce address <%s>\n", bounceto);
1176                         bounce_msgid = (-1L);
1177                 }
1178                 else {
1179                         bounce_msgid = CtdlSaveMsg(bmsg,
1180                                 bounceto,
1181                                 "", mes_type);
1182                 }
1183                 TRACE;
1184
1185                 /* Otherwise, go to the Aide> room */
1186                 lprintf(9, "bounce to room?\n");
1187                 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1188                         "", AIDEROOM,
1189                         MES_LOCAL);
1190         }
1191
1192         CtdlFreeMessage(bmsg);
1193         lprintf(9, "Done processing bounces\n");
1194 }
1195
1196
1197 /*
1198  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1199  * set of delivery instructions for completed deliveries and remove them.
1200  *
1201  * It returns the number of incomplete deliveries remaining.
1202  */
1203 int smtp_purge_completed_deliveries(char *instr) {
1204         int i;
1205         int lines;
1206         int status;
1207         char buf[1024];
1208         char key[1024];
1209         char addr[1024];
1210         char dsn[1024];
1211         int completed;
1212         int incomplete = 0;
1213
1214         lines = num_tokens(instr, '\n');
1215         for (i=0; i<lines; ++i) {
1216                 extract_token(buf, instr, i, '\n');
1217                 extract(key, buf, 0);
1218                 extract(addr, buf, 1);
1219                 status = extract_int(buf, 2);
1220                 extract(dsn, buf, 3);
1221
1222                 completed = 0;
1223
1224                 if (
1225                    (!strcasecmp(key, "local"))
1226                    || (!strcasecmp(key, "remote"))
1227                    || (!strcasecmp(key, "ignet"))
1228                    || (!strcasecmp(key, "room"))
1229                 ) {
1230                         if (status == 2) completed = 1;
1231                         else ++incomplete;
1232                 }
1233
1234                 if (completed) {
1235                         remove_token(instr, i, '\n');
1236                         --i;
1237                         --lines;
1238                 }
1239         }
1240
1241         return(incomplete);
1242 }
1243
1244
1245 /*
1246  * smtp_do_procmsg()
1247  *
1248  * Called by smtp_do_queue() to handle an individual message.
1249  */
1250 void smtp_do_procmsg(long msgnum) {
1251         struct CtdlMessage *msg;
1252         char *instr = NULL;
1253         char *results = NULL;
1254         int i;
1255         int lines;
1256         int status;
1257         char buf[1024];
1258         char key[1024];
1259         char addr[1024];
1260         char dsn[1024];
1261         long text_msgid = (-1);
1262         int incomplete_deliveries_remaining;
1263         time_t attempted = 0L;
1264         time_t last_attempted = 0L;
1265         time_t retry = SMTP_RETRY_INTERVAL;
1266
1267         msg = CtdlFetchMessage(msgnum);
1268         if (msg == NULL) {
1269                 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1270                 return;
1271         }
1272
1273         instr = strdoop(msg->cm_fields['M']);
1274         CtdlFreeMessage(msg);
1275
1276         /* Strip out the headers amd any other non-instruction line */
1277         lines = num_tokens(instr, '\n');
1278         for (i=0; i<lines; ++i) {
1279                 extract_token(buf, instr, i, '\n');
1280                 if (num_tokens(buf, '|') < 2) {
1281                         lprintf(9, "removing <%s>\n", buf);
1282                         remove_token(instr, i, '\n');
1283                         --lines;
1284                         --i;
1285                 }
1286         }
1287
1288         /* Learn the message ID and find out about recent delivery attempts */
1289         lines = num_tokens(instr, '\n');
1290         for (i=0; i<lines; ++i) {
1291                 extract_token(buf, instr, i, '\n');
1292                 extract(key, buf, 0);
1293                 if (!strcasecmp(key, "msgid")) {
1294                         text_msgid = extract_long(buf, 1);
1295                 }
1296                 if (!strcasecmp(key, "retry")) {
1297                         /* double the retry interval after each attempt */
1298                         retry = extract_long(buf, 1) * 2L;
1299                         remove_token(instr, i, '\n');
1300                 }
1301                 if (!strcasecmp(key, "attempted")) {
1302                         attempted = extract_long(buf, 1);
1303                         if (attempted > last_attempted)
1304                                 last_attempted = attempted;
1305                 }
1306         }
1307
1308
1309         /*
1310          * Postpone delivery if we've already tried recently.
1311          */
1312         if ( (time(NULL) - last_attempted) < retry) {
1313                 lprintf(7, "Retry time not yet reached.\n");
1314                 phree(instr);
1315                 return;
1316         }
1317
1318
1319         /*
1320          * Bail out if there's no actual message associated with this
1321          */
1322         if (text_msgid < 0L) {
1323                 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1324                 phree(instr);
1325                 return;
1326         }
1327
1328         /* Plow through the instructions looking for 'remote' directives and
1329          * a status of 0 (no delivery yet attempted) or 3 (transient errors
1330          * were experienced and it's time to try again)
1331          */
1332         lines = num_tokens(instr, '\n');
1333         for (i=0; i<lines; ++i) {
1334                 extract_token(buf, instr, i, '\n');
1335                 extract(key, buf, 0);
1336                 extract(addr, buf, 1);
1337                 status = extract_int(buf, 2);
1338                 extract(dsn, buf, 3);
1339                 if ( (!strcasecmp(key, "remote"))
1340                    && ((status==0)||(status==3)) ) {
1341                         remove_token(instr, i, '\n');
1342                         --i;
1343                         --lines;
1344                         lprintf(9, "SMTP: Trying <%s>\n", addr);
1345                         smtp_try(key, addr, &status, dsn, text_msgid);
1346                         if (status != 2) {
1347                                 if (results == NULL) {
1348                                         results = mallok(1024);
1349                                         memset(results, 0, 1024);
1350                                 }
1351                                 else {
1352                                         results = reallok(results,
1353                                                 strlen(results) + 1024);
1354                                 }
1355                                 sprintf(&results[strlen(results)],
1356                                         "%s|%s|%d|%s\n",
1357                                         key, addr, status, dsn);
1358                         }
1359                 }
1360         }
1361
1362         if (results != NULL) {
1363                 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1364                 strcat(instr, results);
1365                 phree(results);
1366         }
1367
1368
1369         /* Generate 'bounce' messages */
1370         smtp_do_bounce(instr);
1371
1372         /* Go through the delivery list, deleting completed deliveries */
1373         incomplete_deliveries_remaining = 
1374                 smtp_purge_completed_deliveries(instr);
1375
1376
1377         /*
1378          * No delivery instructions remain, so delete both the instructions
1379          * message and the message message.
1380          */
1381         if (incomplete_deliveries_remaining <= 0) {
1382                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1383                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1384         }
1385
1386
1387         /*
1388          * Uncompleted delivery instructions remain, so delete the old
1389          * instructions and replace with the updated ones.
1390          */
1391         if (incomplete_deliveries_remaining > 0) {
1392                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1393                 msg = mallok(sizeof(struct CtdlMessage));
1394                 memset(msg, 0, sizeof(struct CtdlMessage));
1395                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1396                 msg->cm_anon_type = MES_NORMAL;
1397                 msg->cm_format_type = FMT_RFC822;
1398                 msg->cm_fields['M'] = malloc(strlen(instr)+256);
1399                 snprintf(msg->cm_fields['M'],
1400                         strlen(instr)+256,
1401                         "Content-type: %s\n\n%s\n"
1402                         "attempted|%ld\n"
1403                         "retry|%ld\n",
1404                         SPOOLMIME, instr, time(NULL), retry );
1405                 phree(instr);
1406                 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
1407                 CtdlFreeMessage(msg);
1408         }
1409
1410 }
1411
1412
1413
1414 /*
1415  * smtp_do_queue()
1416  * 
1417  * Run through the queue sending out messages.
1418  */
1419 void smtp_do_queue(void) {
1420         static int doing_queue = 0;
1421
1422         /*
1423          * This is a simple concurrency check to make sure only one queue run
1424          * is done at a time.  We could do this with a mutex, but since we
1425          * don't really require extremely fine granularity here, we'll do it
1426          * with a static variable instead.
1427          */
1428         if (doing_queue) return;
1429         doing_queue = 1;
1430
1431         /* 
1432          * Go ahead and run the queue
1433          */
1434         lprintf(7, "SMTP: processing outbound queue\n");
1435
1436         if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1437                 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1438                 return;
1439         }
1440         CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1441                 SPOOLMIME, NULL, smtp_do_procmsg);
1442
1443         lprintf(7, "SMTP: queue run completed\n");
1444         doing_queue = 0;
1445 }
1446
1447
1448
1449 /*****************************************************************************/
1450 /*                      MODULE INITIALIZATION STUFF                          */
1451 /*****************************************************************************/
1452
1453
1454 char *Dynamic_Module_Init(void)
1455 {
1456         SYM_SMTP = CtdlGetDynamicSymbol();
1457         SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1458
1459         CtdlRegisterServiceHook(config.c_smtp_port,     /* On the net... */
1460                                 NULL,
1461                                 smtp_greeting,
1462                                 smtp_command_loop);
1463
1464         CtdlRegisterServiceHook(0,                      /* ...and locally */
1465                                 "smtp.socket",
1466                                 smtp_greeting,
1467                                 smtp_command_loop);
1468
1469         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
1470         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1471         return "$Id$";
1472 }