]> code.citadel.org Git - citadel.git/blob - citadel/serv_smtp.c
Mostly made changes to allow client_gets to handle reading a buffer
[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 Welcome to the Citadel/UX ESMTP server at %s\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-Here's the frequency, Kenneth:\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 I could tell you more, but then I'd have to kill you.\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 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 Message accepted for delivery.\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 *icmdbuf;
692
693         time(&CC->lastcmd);
694         if (client_gets(&icmdbuf) < 1) {
695                 lprintf(3, "SMTP socket is broken.  Ending session.\n");
696                 CC->kill_me = 1;
697                 return;
698         }
699         /* Rather than fix the dynamic buffer a zillion places in here... */
700         if (strlen(icmdbuf) >= SIZ)
701           *(icmdbuf+SIZ)= '\0'; /* no SMTP command should be this big */
702         lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, icmdbuf);
703         while (strlen(icmdbuf) < 5) strcat(icmdbuf, " ");
704
705         if (SMTP->command_state == smtp_user) {
706                 smtp_get_user(icmdbuf);
707         }
708
709         else if (SMTP->command_state == smtp_password) {
710                 smtp_get_pass(icmdbuf);
711         }
712
713         else if (!strncasecmp(icmdbuf, "AUTH", 4)) {
714                 smtp_auth(&icmdbuf[5]);
715         }
716
717         else if (!strncasecmp(icmdbuf, "DATA", 4)) {
718                 smtp_data();
719         }
720
721         else if (!strncasecmp(icmdbuf, "EHLO", 4)) {
722                 smtp_hello(&icmdbuf[5], 1);
723         }
724
725         else if (!strncasecmp(icmdbuf, "EXPN", 4)) {
726                 smtp_expn(&icmdbuf[5]);
727         }
728
729         else if (!strncasecmp(icmdbuf, "HELO", 4)) {
730                 smtp_hello(&icmdbuf[5], 0);
731         }
732
733         else if (!strncasecmp(icmdbuf, "HELP", 4)) {
734                 smtp_help();
735         }
736
737         else if (!strncasecmp(icmdbuf, "MAIL", 4)) {
738                 smtp_mail(&icmdbuf[5]);
739         }
740
741         else if (!strncasecmp(icmdbuf, "NOOP", 4)) {
742                 cprintf("250 This command successfully did nothing.\r\n");
743         }
744
745         else if (!strncasecmp(icmdbuf, "QUIT", 4)) {
746                 cprintf("221 Goodbye...\r\n");
747                 CC->kill_me = 1;
748                 return;
749                 }
750
751         else if (!strncasecmp(icmdbuf, "RCPT", 4)) {
752                 smtp_rcpt(&icmdbuf[5]);
753         }
754
755         else if (!strncasecmp(icmdbuf, "RSET", 4)) {
756                 smtp_rset();
757         }
758
759         else if (!strncasecmp(icmdbuf, "VRFY", 4)) {
760                 smtp_vrfy(&icmdbuf[5]);
761         }
762
763         else {
764                 cprintf("502 I'm sorry Dave, I'm afraid I can't do that.\r\n");
765         }
766
767 }
768
769
770
771
772 /*****************************************************************************/
773 /*               SMTP CLIENT (OUTBOUND PROCESSING) STUFF                     */
774 /*****************************************************************************/
775
776
777
778 /*
779  * smtp_try()
780  *
781  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
782  *
783  */
784 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
785 {
786         int sock = (-1);
787         char mxhosts[1024];
788         int num_mxhosts;
789         int mx;
790         int i;
791         char user[SIZ], node[SIZ], name[SIZ];
792         char buf[1024];
793         char mailfrom[1024];
794         int lp, rp;
795         FILE *msg_fp = NULL;
796         size_t msg_size;
797         size_t blocksize = 0;
798         int scan_done;
799
800         /* Parse out the host portion of the recipient address */
801         process_rfc822_addr(addr, user, node, name);
802
803         lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
804                 user, node, name);
805
806         /* Load the message out of the database into a temp file */
807         msg_fp = tmpfile();
808         if (msg_fp == NULL) {
809                 *status = 4;
810                 sprintf(dsn, "Error creating temporary file");
811                 return;
812         }
813         else {
814                 CtdlRedirectOutput(msg_fp, -1);
815                 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
816                 CtdlRedirectOutput(NULL, -1);
817                 fseek(msg_fp, 0L, SEEK_END);
818                 msg_size = ftell(msg_fp);
819         }
820
821
822         /* Extract something to send later in the 'MAIL From:' command */
823         strcpy(mailfrom, "");
824         rewind(msg_fp);
825         scan_done = 0;
826         do {
827                 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
828                 if (!strncasecmp(buf, "From:", 5)) {
829                         safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
830                         striplt(mailfrom);
831                         for (i=0; i<strlen(mailfrom); ++i) {
832                                 if (!isprint(mailfrom[i])) {
833                                         strcpy(&mailfrom[i], &mailfrom[i+1]);
834                                         i=0;
835                                 }
836                         }
837
838                         /* Strip out parenthesized names */
839                         lp = (-1);
840                         rp = (-1);
841                         for (i=0; i<strlen(mailfrom); ++i) {
842                                 if (mailfrom[i] == '(') lp = i;
843                                 if (mailfrom[i] == ')') rp = i;
844                         }
845                         if ((lp>0)&&(rp>lp)) {
846                                 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
847                         }
848
849                         /* Prefer brokketized names */
850                         lp = (-1);
851                         rp = (-1);
852                         for (i=0; i<strlen(mailfrom); ++i) {
853                                 if (mailfrom[i] == '<') lp = i;
854                                 if (mailfrom[i] == '>') rp = i;
855                         }
856                         if ((lp>=0)&&(rp>lp)) {
857                                 mailfrom[rp] = 0;
858                                 strcpy(mailfrom, &mailfrom[lp]);
859                         }
860
861                         scan_done = 1;
862                 }
863         } while (scan_done == 0);
864         if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
865
866
867         /* Figure out what mail exchanger host we have to connect to */
868         num_mxhosts = getmx(mxhosts, node);
869         lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
870         if (num_mxhosts < 1) {
871                 *status = 5;
872                 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
873                 return;
874         }
875
876         for (mx=0; mx<num_mxhosts; ++mx) {
877                 extract(buf, mxhosts, mx);
878                 lprintf(9, "Trying <%s>\n", buf);
879                 sock = sock_connect(buf, "25", "tcp");
880                 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
881                 if (sock >= 0) lprintf(9, "Connected!\n");
882                 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
883                 if (sock >= 0) break;
884         }
885
886         if (sock < 0) {
887                 *status = 4;    /* dsn is already filled in */
888                 return;
889         }
890
891         /* Process the SMTP greeting from the server */
892         if (ml_sock_gets(sock, buf) < 0) {
893                 *status = 4;
894                 strcpy(dsn, "Connection broken during SMTP conversation");
895                 goto bail;
896         }
897         lprintf(9, "<%s\n", buf);
898         if (buf[0] != '2') {
899                 if (buf[0] == '4') {
900                         *status = 4;
901                         safestrncpy(dsn, &buf[4], 1023);
902                         goto bail;
903                 }
904                 else {
905                         *status = 5;
906                         safestrncpy(dsn, &buf[4], 1023);
907                         goto bail;
908                 }
909         }
910
911         /* At this point we know we are talking to a real SMTP server */
912
913         /* Do a HELO command */
914         snprintf(buf, sizeof buf, "HELO %s", config.c_fqdn);
915         lprintf(9, ">%s\n", buf);
916         sock_puts_crlf(sock, buf);
917         if (ml_sock_gets(sock, buf) < 0) {
918                 *status = 4;
919                 strcpy(dsn, "Connection broken during SMTP conversation");
920                 goto bail;
921         }
922         lprintf(9, "<%s\n", buf);
923         if (buf[0] != '2') {
924                 if (buf[0] == '4') {
925                         *status = 4;
926                         safestrncpy(dsn, &buf[4], 1023);
927                         goto bail;
928                 }
929                 else {
930                         *status = 5;
931                         safestrncpy(dsn, &buf[4], 1023);
932                         goto bail;
933                 }
934         }
935
936
937         /* HELO succeeded, now try the MAIL From: command */
938         snprintf(buf, sizeof buf, "MAIL From: <%s>", mailfrom);
939         lprintf(9, ">%s\n", buf);
940         sock_puts_crlf(sock, buf);
941         if (ml_sock_gets(sock, buf) < 0) {
942                 *status = 4;
943                 strcpy(dsn, "Connection broken during SMTP conversation");
944                 goto bail;
945         }
946         lprintf(9, "<%s\n", buf);
947         if (buf[0] != '2') {
948                 if (buf[0] == '4') {
949                         *status = 4;
950                         safestrncpy(dsn, &buf[4], 1023);
951                         goto bail;
952                 }
953                 else {
954                         *status = 5;
955                         safestrncpy(dsn, &buf[4], 1023);
956                         goto bail;
957                 }
958         }
959
960
961         /* MAIL succeeded, now try the RCPT To: command */
962         snprintf(buf, sizeof buf, "RCPT To: <%s>", addr);
963         lprintf(9, ">%s\n", buf);
964         sock_puts_crlf(sock, buf);
965         if (ml_sock_gets(sock, buf) < 0) {
966                 *status = 4;
967                 strcpy(dsn, "Connection broken during SMTP conversation");
968                 goto bail;
969         }
970         lprintf(9, "<%s\n", buf);
971         if (buf[0] != '2') {
972                 if (buf[0] == '4') {
973                         *status = 4;
974                         safestrncpy(dsn, &buf[4], 1023);
975                         goto bail;
976                 }
977                 else {
978                         *status = 5;
979                         safestrncpy(dsn, &buf[4], 1023);
980                         goto bail;
981                 }
982         }
983
984
985         /* RCPT succeeded, now try the DATA command */
986         lprintf(9, ">DATA\n");
987         sock_puts_crlf(sock, "DATA");
988         if (ml_sock_gets(sock, buf) < 0) {
989                 *status = 4;
990                 strcpy(dsn, "Connection broken during SMTP conversation");
991                 goto bail;
992         }
993         lprintf(9, "<%s\n", buf);
994         if (buf[0] != '3') {
995                 if (buf[0] == '4') {
996                         *status = 3;
997                         safestrncpy(dsn, &buf[4], 1023);
998                         goto bail;
999                 }
1000                 else {
1001                         *status = 5;
1002                         safestrncpy(dsn, &buf[4], 1023);
1003                         goto bail;
1004                 }
1005         }
1006
1007         /* If we reach this point, the server is expecting data */
1008         rewind(msg_fp);
1009         while (msg_size > 0) {
1010                 blocksize = sizeof(buf);
1011                 if (blocksize > msg_size) blocksize = msg_size;
1012                 fread(buf, blocksize, 1, msg_fp);
1013                 sock_write(sock, buf, blocksize);
1014                 msg_size -= blocksize;
1015         }
1016         if (buf[blocksize-1] != 10) {
1017                 lprintf(5, "Possible problem: message did not correctly "
1018                         "terminate. (expecting 0x10, got 0x%02x)\n",
1019                                 buf[blocksize-1]);
1020         }
1021
1022         sock_write(sock, ".\r\n", 3);
1023         if (ml_sock_gets(sock, buf) < 0) {
1024                 *status = 4;
1025                 strcpy(dsn, "Connection broken during SMTP conversation");
1026                 goto bail;
1027         }
1028         lprintf(9, "%s\n", buf);
1029         if (buf[0] != '2') {
1030                 if (buf[0] == '4') {
1031                         *status = 4;
1032                         safestrncpy(dsn, &buf[4], 1023);
1033                         goto bail;
1034                 }
1035                 else {
1036                         *status = 5;
1037                         safestrncpy(dsn, &buf[4], 1023);
1038                         goto bail;
1039                 }
1040         }
1041
1042         /* We did it! */
1043         safestrncpy(dsn, &buf[4], 1023);
1044         *status = 2;
1045
1046         lprintf(9, ">QUIT\n");
1047         sock_puts_crlf(sock, "QUIT");
1048         ml_sock_gets(sock, buf);
1049         lprintf(9, "<%s\n", buf);
1050
1051 bail:   if (msg_fp != NULL) fclose(msg_fp);
1052         sock_close(sock);
1053         return;
1054 }
1055
1056
1057
1058 /*
1059  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1060  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1061  * a "bounce" message (delivery status notification).
1062  */
1063 void smtp_do_bounce(char *instr) {
1064         int i;
1065         int lines;
1066         int status;
1067         char buf[1024];
1068         char key[1024];
1069         char addr[1024];
1070         char dsn[1024];
1071         char bounceto[1024];
1072         int num_bounces = 0;
1073         int bounce_this = 0;
1074         long bounce_msgid = (-1);
1075         time_t submitted = 0L;
1076         struct CtdlMessage *bmsg = NULL;
1077         int give_up = 0;
1078         int mes_type = 0;
1079
1080         lprintf(9, "smtp_do_bounce() called\n");
1081         strcpy(bounceto, "");
1082
1083         lines = num_tokens(instr, '\n');
1084
1085
1086         /* See if it's time to give up on delivery of this message */
1087         for (i=0; i<lines; ++i) {
1088                 extract_token(buf, instr, i, '\n');
1089                 extract(key, buf, 0);
1090                 extract(addr, buf, 1);
1091                 if (!strcasecmp(key, "submitted")) {
1092                         submitted = atol(addr);
1093                 }
1094         }
1095
1096         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1097                 give_up = 1;
1098         }
1099
1100
1101
1102         bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1103         if (bmsg == NULL) return;
1104         memset(bmsg, 0, sizeof(struct CtdlMessage));
1105
1106         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1107         bmsg->cm_anon_type = MES_NORMAL;
1108         bmsg->cm_format_type = 1;
1109         bmsg->cm_fields['A'] = strdoop("Citadel");
1110         bmsg->cm_fields['O'] = strdoop(MAILROOM);
1111         bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1112
1113         if (give_up) bmsg->cm_fields['M'] = strdoop(
1114 "A message you sent could not be delivered to some or all of its recipients\n"
1115 "due to prolonged unavailability of its destination(s).\n"
1116 "Giving up on the following addresses:\n\n"
1117 );
1118
1119         else bmsg->cm_fields['M'] = strdoop(
1120 "A message you sent could not be delivered to some or all of its recipients.\n"
1121 "The following addresses were undeliverable:\n\n"
1122 );
1123
1124         /*
1125          * Now go through the instructions checking for stuff.
1126          */
1127
1128         for (i=0; i<lines; ++i) {
1129                 extract_token(buf, instr, i, '\n');
1130                 extract(key, buf, 0);
1131                 extract(addr, buf, 1);
1132                 status = extract_int(buf, 2);
1133                 extract(dsn, buf, 3);
1134                 bounce_this = 0;
1135
1136                 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1137                         key, addr, status, dsn);
1138
1139                 if (!strcasecmp(key, "bounceto")) {
1140                         strcpy(bounceto, addr);
1141                 }
1142
1143                 if (
1144                    (!strcasecmp(key, "local"))
1145                    || (!strcasecmp(key, "remote"))
1146                    || (!strcasecmp(key, "ignet"))
1147                    || (!strcasecmp(key, "room"))
1148                 ) {
1149                         if (status == 5) bounce_this = 1;
1150                         if (give_up) bounce_this = 1;
1151                 }
1152
1153                 if (bounce_this) {
1154                         ++num_bounces;
1155
1156                         if (bmsg->cm_fields['M'] == NULL) {
1157                                 lprintf(2, "ERROR ... M field is null "
1158                                         "(%s:%d)\n", __FILE__, __LINE__);
1159                         }
1160
1161                         bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1162                                 strlen(bmsg->cm_fields['M']) + 1024 );
1163                         strcat(bmsg->cm_fields['M'], addr);
1164                         strcat(bmsg->cm_fields['M'], ": ");
1165                         strcat(bmsg->cm_fields['M'], dsn);
1166                         strcat(bmsg->cm_fields['M'], "\n");
1167
1168                         remove_token(instr, i, '\n');
1169                         --i;
1170                         --lines;
1171                 }
1172         }
1173
1174         /* Deliver the bounce if there's anything worth mentioning */
1175         lprintf(9, "num_bounces = %d\n", num_bounces);
1176         if (num_bounces > 0) {
1177
1178                 /* First try the user who sent the message */
1179                 lprintf(9, "bounce to user? <%s>\n", bounceto);
1180                 TRACE;
1181                 if (strlen(bounceto) == 0) {
1182                         lprintf(7, "No bounce address specified\n");
1183                         bounce_msgid = (-1L);
1184                 }
1185                 else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
1186                         lprintf(7, "Invalid bounce address <%s>\n", bounceto);
1187                         bounce_msgid = (-1L);
1188                 }
1189                 else {
1190                         bounce_msgid = CtdlSaveMsg(bmsg,
1191                                 bounceto,
1192                                 "", mes_type);
1193                 }
1194                 TRACE;
1195
1196                 /* Otherwise, go to the Aide> room */
1197                 lprintf(9, "bounce to room?\n");
1198                 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1199                         "", AIDEROOM,
1200                         MES_LOCAL);
1201         }
1202
1203         CtdlFreeMessage(bmsg);
1204         lprintf(9, "Done processing bounces\n");
1205 }
1206
1207
1208 /*
1209  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1210  * set of delivery instructions for completed deliveries and remove them.
1211  *
1212  * It returns the number of incomplete deliveries remaining.
1213  */
1214 int smtp_purge_completed_deliveries(char *instr) {
1215         int i;
1216         int lines;
1217         int status;
1218         char buf[1024];
1219         char key[1024];
1220         char addr[1024];
1221         char dsn[1024];
1222         int completed;
1223         int incomplete = 0;
1224
1225         lines = num_tokens(instr, '\n');
1226         for (i=0; i<lines; ++i) {
1227                 extract_token(buf, instr, i, '\n');
1228                 extract(key, buf, 0);
1229                 extract(addr, buf, 1);
1230                 status = extract_int(buf, 2);
1231                 extract(dsn, buf, 3);
1232
1233                 completed = 0;
1234
1235                 if (
1236                    (!strcasecmp(key, "local"))
1237                    || (!strcasecmp(key, "remote"))
1238                    || (!strcasecmp(key, "ignet"))
1239                    || (!strcasecmp(key, "room"))
1240                 ) {
1241                         if (status == 2) completed = 1;
1242                         else ++incomplete;
1243                 }
1244
1245                 if (completed) {
1246                         remove_token(instr, i, '\n');
1247                         --i;
1248                         --lines;
1249                 }
1250         }
1251
1252         return(incomplete);
1253 }
1254
1255
1256 /*
1257  * smtp_do_procmsg()
1258  *
1259  * Called by smtp_do_queue() to handle an individual message.
1260  */
1261 void smtp_do_procmsg(long msgnum, void *userdata) {
1262         struct CtdlMessage *msg;
1263         char *instr = NULL;
1264         char *results = NULL;
1265         int i;
1266         int lines;
1267         int status;
1268         char buf[1024];
1269         char key[1024];
1270         char addr[1024];
1271         char dsn[1024];
1272         long text_msgid = (-1);
1273         int incomplete_deliveries_remaining;
1274         time_t attempted = 0L;
1275         time_t last_attempted = 0L;
1276         time_t retry = SMTP_RETRY_INTERVAL;
1277
1278         msg = CtdlFetchMessage(msgnum);
1279         if (msg == NULL) {
1280                 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1281                 return;
1282         }
1283
1284         instr = strdoop(msg->cm_fields['M']);
1285         CtdlFreeMessage(msg);
1286
1287         /* Strip out the headers amd any other non-instruction line */
1288         lines = num_tokens(instr, '\n');
1289         for (i=0; i<lines; ++i) {
1290                 extract_token(buf, instr, i, '\n');
1291                 if (num_tokens(buf, '|') < 2) {
1292                         lprintf(9, "removing <%s>\n", buf);
1293                         remove_token(instr, i, '\n');
1294                         --lines;
1295                         --i;
1296                 }
1297         }
1298
1299         /* Learn the message ID and find out about recent delivery attempts */
1300         lines = num_tokens(instr, '\n');
1301         for (i=0; i<lines; ++i) {
1302                 extract_token(buf, instr, i, '\n');
1303                 extract(key, buf, 0);
1304                 if (!strcasecmp(key, "msgid")) {
1305                         text_msgid = extract_long(buf, 1);
1306                 }
1307                 if (!strcasecmp(key, "retry")) {
1308                         /* double the retry interval after each attempt */
1309                         retry = extract_long(buf, 1) * 2L;
1310                         remove_token(instr, i, '\n');
1311                 }
1312                 if (!strcasecmp(key, "attempted")) {
1313                         attempted = extract_long(buf, 1);
1314                         if (attempted > last_attempted)
1315                                 last_attempted = attempted;
1316                 }
1317         }
1318
1319
1320         /*
1321          * Postpone delivery if we've already tried recently.
1322          */
1323         if ( (time(NULL) - last_attempted) < retry) {
1324                 lprintf(7, "Retry time not yet reached.\n");
1325                 phree(instr);
1326                 return;
1327         }
1328
1329
1330         /*
1331          * Bail out if there's no actual message associated with this
1332          */
1333         if (text_msgid < 0L) {
1334                 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1335                 phree(instr);
1336                 return;
1337         }
1338
1339         /* Plow through the instructions looking for 'remote' directives and
1340          * a status of 0 (no delivery yet attempted) or 3 (transient errors
1341          * were experienced and it's time to try again)
1342          */
1343         lines = num_tokens(instr, '\n');
1344         for (i=0; i<lines; ++i) {
1345                 extract_token(buf, instr, i, '\n');
1346                 extract(key, buf, 0);
1347                 extract(addr, buf, 1);
1348                 status = extract_int(buf, 2);
1349                 extract(dsn, buf, 3);
1350                 if ( (!strcasecmp(key, "remote"))
1351                    && ((status==0)||(status==3)) ) {
1352                         remove_token(instr, i, '\n');
1353                         --i;
1354                         --lines;
1355                         lprintf(9, "SMTP: Trying <%s>\n", addr);
1356                         smtp_try(key, addr, &status, dsn, text_msgid);
1357                         if (status != 2) {
1358                                 if (results == NULL) {
1359                                         results = mallok(1024);
1360                                         memset(results, 0, 1024);
1361                                 }
1362                                 else {
1363                                         results = reallok(results,
1364                                                 strlen(results) + 1024);
1365                                 }
1366                                 sprintf(&results[strlen(results)],
1367                                         "%s|%s|%d|%s\n",
1368                                         key, addr, status, dsn);
1369                         }
1370                 }
1371         }
1372
1373         if (results != NULL) {
1374                 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1375                 strcat(instr, results);
1376                 phree(results);
1377         }
1378
1379
1380         /* Generate 'bounce' messages */
1381         smtp_do_bounce(instr);
1382
1383         /* Go through the delivery list, deleting completed deliveries */
1384         incomplete_deliveries_remaining = 
1385                 smtp_purge_completed_deliveries(instr);
1386
1387
1388         /*
1389          * No delivery instructions remain, so delete both the instructions
1390          * message and the message message.
1391          */
1392         if (incomplete_deliveries_remaining <= 0) {
1393                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1394                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1395         }
1396
1397
1398         /*
1399          * Uncompleted delivery instructions remain, so delete the old
1400          * instructions and replace with the updated ones.
1401          */
1402         if (incomplete_deliveries_remaining > 0) {
1403                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1404                 msg = mallok(sizeof(struct CtdlMessage));
1405                 memset(msg, 0, sizeof(struct CtdlMessage));
1406                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1407                 msg->cm_anon_type = MES_NORMAL;
1408                 msg->cm_format_type = FMT_RFC822;
1409                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1410                 snprintf(msg->cm_fields['M'],
1411                         strlen(instr)+SIZ,
1412                         "Content-type: %s\n\n%s\n"
1413                         "attempted|%ld\n"
1414                         "retry|%ld\n",
1415                         SPOOLMIME, instr, time(NULL), retry );
1416                 phree(instr);
1417                 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
1418                 CtdlFreeMessage(msg);
1419         }
1420
1421 }
1422
1423
1424
1425 /*
1426  * smtp_do_queue()
1427  * 
1428  * Run through the queue sending out messages.
1429  */
1430 void smtp_do_queue(void) {
1431         static int doing_queue = 0;
1432
1433         /*
1434          * This is a simple concurrency check to make sure only one queue run
1435          * is done at a time.  We could do this with a mutex, but since we
1436          * don't really require extremely fine granularity here, we'll do it
1437          * with a static variable instead.
1438          */
1439         if (doing_queue) return;
1440         doing_queue = 1;
1441
1442         /* 
1443          * Go ahead and run the queue
1444          */
1445         lprintf(7, "SMTP: processing outbound queue\n");
1446
1447         if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1448                 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1449                 return;
1450         }
1451         CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1452                 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1453
1454         lprintf(7, "SMTP: queue run completed\n");
1455         doing_queue = 0;
1456 }
1457
1458
1459
1460 /*****************************************************************************/
1461 /*                      MODULE INITIALIZATION STUFF                          */
1462 /*****************************************************************************/
1463
1464
1465 char *Dynamic_Module_Init(void)
1466 {
1467         SYM_SMTP = CtdlGetDynamicSymbol();
1468         SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1469
1470         CtdlRegisterServiceHook(config.c_smtp_port,     /* On the net... */
1471                                 NULL,
1472                                 smtp_greeting,
1473                                 smtp_command_loop);
1474
1475         CtdlRegisterServiceHook(0,                      /* ...and locally */
1476                                 "smtp.socket",
1477                                 smtp_greeting,
1478                                 smtp_command_loop);
1479
1480         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1481         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1482         return "$Id$";
1483 }