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