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