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