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