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