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