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