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