]> code.citadel.org Git - citadel.git/blob - citadel/serv_smtp.c
* Support POP3 over TLS
[citadel.git] / citadel / serv_smtp.c
1 /*
2  * $Id$
3  *
4  * This module is an SMTP and ESMTP implementation for the Citadel system.
5  * It is compliant with all of the following:
6  *
7  * RFC  821 - Simple Mail Transfer Protocol
8  * RFC  876 - Survey of SMTP Implementations
9  * RFC 1047 - Duplicate messages and SMTP
10  * RFC 1854 - command pipelining
11  * RFC 1869 - Extended Simple Mail Transfer Protocol
12  * RFC 1870 - SMTP Service Extension for Message Size Declaration
13  * RFC 1893 - Enhanced Mail System Status Codes
14  * RFC 2033 - Local Mail Transfer Protocol
15  * RFC 2034 - SMTP Service Extension for Returning Enhanced Error Codes
16  * RFC 2197 - SMTP Service Extension for Command Pipelining
17  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
18  * RFC 2554 - SMTP Service Extension for Authentication
19  * RFC 2821 - Simple Mail Transfer Protocol
20  * RFC 2822 - Internet Message Format
21  * RFC 2920 - SMTP Service Extension for Command Pipelining
22  *
23  */
24
25 #include "sysdep.h"
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <signal.h>
31 #include <pwd.h>
32 #include <errno.h>
33 #include <sys/types.h>
34
35 #if TIME_WITH_SYS_TIME
36 # include <sys/time.h>
37 # include <time.h>
38 #else
39 # if HAVE_SYS_TIME_H
40 #  include <sys/time.h>
41 # else
42 #  include <time.h>
43 # endif
44 #endif
45
46 #include <sys/wait.h>
47 #include <ctype.h>
48 #include <string.h>
49 #include <limits.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <arpa/inet.h>
53 #include "citadel.h"
54 #include "server.h"
55 #include "sysdep_decls.h"
56 #include "citserver.h"
57 #include "support.h"
58 #include "config.h"
59 #include "control.h"
60 #include "serv_extensions.h"
61 #include "room_ops.h"
62 #include "user_ops.h"
63 #include "policy.h"
64 #include "database.h"
65 #include "msgbase.h"
66 #include "tools.h"
67 #include "internet_addressing.h"
68 #include "genstamp.h"
69 #include "domain.h"
70 #include "clientsocket.h"
71 #include "locate_host.h"
72
73 #ifdef HAVE_OPENSSL
74 #include "serv_crypto.h"
75 #endif
76
77
78
79 #ifndef HAVE_SNPRINTF
80 #include "snprintf.h"
81 #endif
82
83 struct citsmtp {                /* Information about the current session */
84         int command_state;
85         char helo_node[SIZ];
86         struct ctdluser vrfy_buffer;
87         int vrfy_count;
88         char vrfy_match[SIZ];
89         char from[SIZ];
90         char recipients[SIZ];
91         int number_of_recipients;
92         int delivery_mode;
93         int message_originated_locally;
94         int is_lmtp;
95 };
96
97 enum {                          /* Command states for login authentication */
98         smtp_command,
99         smtp_user,
100         smtp_password
101 };
102
103 enum {                          /* Delivery modes */
104         smtp_deliver_local,
105         smtp_deliver_remote
106 };
107
108 #define SMTP            ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
109 #define SMTP_RECPS      ((char *)CtdlGetUserData(SYM_SMTP_RECPS))
110 #define SMTP_ROOMS      ((char *)CtdlGetUserData(SYM_SMTP_ROOMS))
111
112
113 int run_queue_now = 0;  /* Set to 1 to ignore SMTP send retry times */
114
115
116
117 /*****************************************************************************/
118 /*                      SMTP SERVER (INBOUND) STUFF                          */
119 /*****************************************************************************/
120
121
122
123
124 /*
125  * Here's where our SMTP session begins its happy day.
126  */
127 void smtp_greeting(void) {
128
129         strcpy(CC->cs_clientname, "SMTP session");
130         CC->internal_pgm = 1;
131         CC->cs_flags |= CS_STEALTH;
132         CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
133         CtdlAllocUserData(SYM_SMTP_RECPS, SIZ);
134         CtdlAllocUserData(SYM_SMTP_ROOMS, SIZ);
135         snprintf(SMTP_RECPS, SIZ, "%s", "");
136         snprintf(SMTP_ROOMS, SIZ, "%s", "");
137
138         cprintf("220 %s ESMTP Citadel/UX server ready.\r\n", config.c_fqdn);
139 }
140
141 /*
142  * LMTP is like SMTP but with some extra bonus footage added.
143  */
144 void lmtp_greeting(void) {
145         smtp_greeting();
146         SMTP->is_lmtp = 1;
147 }
148
149
150 /*
151  * Implement HELO and EHLO commands.
152  *
153  * which_command:  0=HELO, 1=EHLO, 2=LHLO
154  */
155 void smtp_hello(char *argbuf, int which_command) {
156
157         safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
158
159         if ( (which_command != 2) && (SMTP->is_lmtp) ) {
160                 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
161                 return;
162         }
163
164         if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
165                 cprintf("500 LHLO is only allowed when running LMTP\r\n");
166                 return;
167         }
168
169         if (which_command == 0) {
170                 cprintf("250 Hello %s (%s [%s])\r\n",
171                         SMTP->helo_node,
172                         CC->cs_host,
173                         CC->cs_addr
174                 );
175         }
176         else {
177                 if (which_command == 1) {
178                         cprintf("250-Hello %s (%s [%s])\r\n",
179                                 SMTP->helo_node,
180                                 CC->cs_host,
181                                 CC->cs_addr
182                         );
183                 }
184                 else {
185                         cprintf("250-Greetings and joyous salutations.\r\n");
186                 }
187                 cprintf("250-HELP\r\n");
188                 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
189                 cprintf("250-PIPELINING\r\n");
190                 cprintf("250-AUTH=LOGIN\r\n");
191 #ifdef HAVE_OPENSSL
192                 cprintf("250-STARTTLS\r\n");
193 #endif
194                 cprintf("250 ENHANCEDSTATUSCODES\r\n");
195         }
196 }
197
198
199
200 /*
201  * Implement HELP command.
202  */
203 void smtp_help(void) {
204         cprintf("214-Commands accepted:\r\n");
205         cprintf("214-    DATA\r\n");
206         cprintf("214-    EHLO\r\n");
207         cprintf("214-    EXPN\r\n");
208         cprintf("214-    HELO\r\n");
209         cprintf("214-    HELP\r\n");
210         cprintf("214-    MAIL\r\n");
211         cprintf("214-    NOOP\r\n");
212         cprintf("214-    QUIT\r\n");
213         cprintf("214-    RCPT\r\n");
214         cprintf("214-    RSET\r\n");
215         cprintf("214-    VRFY\r\n");
216         cprintf("214     \r\n");
217 }
218
219
220 /*
221  *
222  */
223 void smtp_get_user(char *argbuf) {
224         char buf[SIZ];
225         char username[SIZ];
226
227         CtdlDecodeBase64(username, argbuf, SIZ);
228         lprintf(9, "Trying <%s>\n", username);
229         if (CtdlLoginExistingUser(username) == login_ok) {
230                 CtdlEncodeBase64(buf, "Password:", 9);
231                 cprintf("334 %s\r\n", buf);
232                 SMTP->command_state = smtp_password;
233         }
234         else {
235                 cprintf("500 5.7.0 No such user.\r\n");
236                 SMTP->command_state = smtp_command;
237         }
238 }
239
240
241 /*
242  *
243  */
244 void smtp_get_pass(char *argbuf) {
245         char password[SIZ];
246
247         CtdlDecodeBase64(password, argbuf, SIZ);
248         lprintf(9, "Trying <%s>\n", password);
249         if (CtdlTryPassword(password) == pass_ok) {
250                 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
251                 lprintf(9, "SMTP authenticated login successful\n");
252                 CC->internal_pgm = 0;
253                 CC->cs_flags &= ~CS_STEALTH;
254         }
255         else {
256                 cprintf("535 5.7.0 Authentication failed.\r\n");
257         }
258         SMTP->command_state = smtp_command;
259 }
260
261
262 /*
263  *
264  */
265 void smtp_auth(char *argbuf) {
266         char buf[SIZ];
267
268         if (strncasecmp(argbuf, "login", 5) ) {
269                 cprintf("504 5.7.4 We only support LOGIN authentication.\r\n");
270                 return;
271         }
272
273         if (strlen(argbuf) >= 7) {
274                 smtp_get_user(&argbuf[6]);
275         }
276
277         else {
278                 CtdlEncodeBase64(buf, "Username:", 9);
279                 cprintf("334 %s\r\n", buf);
280                 SMTP->command_state = smtp_user;
281         }
282 }
283
284
285 /*
286  * Back end for smtp_vrfy() command
287  */
288 void smtp_vrfy_backend(struct ctdluser *us, void *data) {
289
290         if (!fuzzy_match(us, SMTP->vrfy_match)) {
291                 ++SMTP->vrfy_count;
292                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
293         }
294 }
295
296
297 /* 
298  * Implements the VRFY (verify user name) command.
299  * Performs fuzzy match on full user names.
300  */
301 void smtp_vrfy(char *argbuf) {
302         SMTP->vrfy_count = 0;
303         strcpy(SMTP->vrfy_match, argbuf);
304         ForEachUser(smtp_vrfy_backend, NULL);
305
306         if (SMTP->vrfy_count < 1) {
307                 cprintf("550 5.1.1 String does not match anything.\r\n");
308         }
309         else if (SMTP->vrfy_count == 1) {
310                 cprintf("250 %s <cit%ld@%s>\r\n",
311                         SMTP->vrfy_buffer.fullname,
312                         SMTP->vrfy_buffer.usernum,
313                         config.c_fqdn);
314         }
315         else if (SMTP->vrfy_count > 1) {
316                 cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
317                         SMTP->vrfy_count);
318         }
319
320 }
321
322
323
324 /*
325  * Back end for smtp_expn() command
326  */
327 void smtp_expn_backend(struct ctdluser *us, void *data) {
328
329         if (!fuzzy_match(us, SMTP->vrfy_match)) {
330
331                 if (SMTP->vrfy_count >= 1) {
332                         cprintf("250-%s <cit%ld@%s>\r\n",
333                                 SMTP->vrfy_buffer.fullname,
334                                 SMTP->vrfy_buffer.usernum,
335                                 config.c_fqdn);
336                 }
337
338                 ++SMTP->vrfy_count;
339                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
340         }
341 }
342
343
344 /* 
345  * Implements the EXPN (expand user name) command.
346  * Performs fuzzy match on full user names.
347  */
348 void smtp_expn(char *argbuf) {
349         SMTP->vrfy_count = 0;
350         strcpy(SMTP->vrfy_match, argbuf);
351         ForEachUser(smtp_expn_backend, NULL);
352
353         if (SMTP->vrfy_count < 1) {
354                 cprintf("550 5.1.1 String does not match anything.\r\n");
355         }
356         else if (SMTP->vrfy_count >= 1) {
357                 cprintf("250 %s <cit%ld@%s>\r\n",
358                         SMTP->vrfy_buffer.fullname,
359                         SMTP->vrfy_buffer.usernum,
360                         config.c_fqdn);
361         }
362 }
363
364
365 /*
366  * Implements the RSET (reset state) command.
367  * Currently this just zeroes out the state buffer.  If pointers to data
368  * allocated with mallok() are ever placed in the state buffer, we have to
369  * be sure to phree() them first!
370  *
371  * Set do_response to nonzero to output the SMTP RSET response code.
372  */
373 void smtp_rset(int do_response) {
374         int is_lmtp;
375
376         /*
377          * Our entire SMTP state is discarded when a RSET command is issued,
378          * but we need to preserve this one little piece of information, so
379          * we save it for later.
380          */
381         is_lmtp = SMTP->is_lmtp;
382
383         memset(SMTP, 0, sizeof(struct citsmtp));
384
385         /*
386          * It is somewhat ambiguous whether we want to log out when a RSET
387          * command is issued.  Here's the code to do it.  It is commented out
388          * because some clients (such as Pine) issue RSET commands before
389          * each message, but still expect to be logged in.
390          *
391          * if (CC->logged_in) {
392          *      logout(CC);
393          * }
394          */
395
396         /*
397          * Reinstate this little piece of information we saved (see above).
398          */
399         SMTP->is_lmtp = is_lmtp;
400
401         if (do_response) {
402                 cprintf("250 2.0.0 Zap!\r\n");
403         }
404 }
405
406 /*
407  * Clear out the portions of the state buffer that need to be cleared out
408  * after the DATA command finishes.
409  */
410 void smtp_data_clear(void) {
411         strcpy(SMTP->from, "");
412         strcpy(SMTP->recipients, "");
413         SMTP->number_of_recipients = 0;
414         SMTP->delivery_mode = 0;
415         SMTP->message_originated_locally = 0;
416 }
417
418
419
420 /*
421  * Implements the "MAIL From:" command
422  */
423 void smtp_mail(char *argbuf) {
424         char user[SIZ];
425         char node[SIZ];
426         char name[SIZ];
427
428         if (strlen(SMTP->from) != 0) {
429                 cprintf("503 5.1.0 Only one sender permitted\r\n");
430                 return;
431         }
432
433         if (strncasecmp(argbuf, "From:", 5)) {
434                 cprintf("501 5.1.7 Syntax error\r\n");
435                 return;
436         }
437
438         strcpy(SMTP->from, &argbuf[5]);
439         striplt(SMTP->from);
440         stripallbut(SMTP->from, '<', '>');
441
442         /* We used to reject empty sender names, until it was brought to our
443          * attention that RFC1123 5.2.9 requires that this be allowed.  So now
444          * we allow it, but replace the empty string with a fake
445          * address so we don't have to contend with the empty string causing
446          * other code to fail when it's expecting something there.
447          */
448         if (strlen(SMTP->from) == 0) {
449                 strcpy(SMTP->from, "someone@somewhere.org");
450         }
451
452         /* If this SMTP connection is from a logged-in user, force the 'from'
453          * to be the user's Internet e-mail address as Citadel knows it.
454          */
455         if (CC->logged_in) {
456                 strcpy(SMTP->from, CC->cs_inet_email);
457                 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
458                 SMTP->message_originated_locally = 1;
459                 return;
460         }
461
462         else if (SMTP->is_lmtp) {
463                 /* Bypass forgery checking for LMTP */
464         }
465
466         /* Otherwise, make sure outsiders aren't trying to forge mail from
467          * this system.
468          */
469         else {
470                 process_rfc822_addr(SMTP->from, user, node, name);
471                 if (CtdlHostAlias(node) != hostalias_nomatch) {
472                         cprintf("550 5.1.8 "
473                                 "You must log in to send mail from %s\r\n",
474                                 node);
475                         strcpy(SMTP->from, "");
476                         return;
477                 }
478         }
479
480         cprintf("250 2.0.0 Sender ok\r\n");
481 }
482
483
484
485 /*
486  * Implements the "RCPT To:" command
487  */
488 void smtp_rcpt(char *argbuf) {
489         char recp[SIZ];
490         char message_to_spammer[SIZ];
491         struct recptypes *valid = NULL;
492
493         if (strlen(SMTP->from) == 0) {
494                 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
495                 return;
496         }
497
498         if (strncasecmp(argbuf, "To:", 3)) {
499                 cprintf("501 5.1.7 Syntax error\r\n");
500                 return;
501         }
502
503         strcpy(recp, &argbuf[3]);
504         striplt(recp);
505         stripallbut(recp, '<', '>');
506
507         if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
508                 cprintf("452 4.5.3 Too many recipients\r\n");
509                 return;
510         }
511
512         /* RBL check */
513         if ( (!CC->logged_in)
514            && (!SMTP->is_lmtp) ) {
515                 if (rbl_check(message_to_spammer)) {
516                         cprintf("550 %s\r\n", message_to_spammer);
517                         /* no need to phree(valid), it's not allocated yet */
518                         return;
519                 }
520         }
521
522         valid = validate_recipients(recp);
523         if (valid->num_error > 0) {
524                 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
525                 phree(valid);
526                 return;
527         }
528
529         if (valid->num_internet > 0) {
530                 if ( (SMTP->message_originated_locally == 0)
531                    && (SMTP->is_lmtp == 0) ) {
532                         cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
533                         phree(valid);
534                         return;
535                 }
536         }
537
538         cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
539         if (strlen(SMTP->recipients) > 0) {
540                 strcat(SMTP->recipients, ",");
541         }
542         strcat(SMTP->recipients, recp);
543         SMTP->number_of_recipients += 1;
544 }
545
546
547
548
549 /*
550  * Implements the DATA command
551  */
552 void smtp_data(void) {
553         char *body;
554         struct CtdlMessage *msg;
555         long msgnum;
556         char nowstamp[SIZ];
557         struct recptypes *valid;
558         int scan_errors;
559         int i;
560         char result[SIZ];
561
562         if (strlen(SMTP->from) == 0) {
563                 cprintf("503 5.5.1 Need MAIL command first.\r\n");
564                 return;
565         }
566
567         if (SMTP->number_of_recipients < 1) {
568                 cprintf("503 5.5.1 Need RCPT command first.\r\n");
569                 return;
570         }
571
572         cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
573         
574         datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
575         body = mallok(4096);
576
577         if (body != NULL) snprintf(body, 4096,
578                 "Received: from %s (%s [%s])\n"
579                 "       by %s; %s\n",
580                         SMTP->helo_node,
581                         CC->cs_host,
582                         CC->cs_addr,
583                         config.c_fqdn,
584                         nowstamp);
585         
586         body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
587         if (body == NULL) {
588                 cprintf("550 5.6.5 "
589                         "Unable to save message: internal error.\r\n");
590                 return;
591         }
592
593         lprintf(9, "Converting message...\n");
594         msg = convert_internet_message(body);
595
596         /* If the user is locally authenticated, FORCE the From: header to
597          * show up as the real sender.  Yes, this violates the RFC standard,
598          * but IT MAKES SENSE.  If you prefer strict RFC adherence over
599          * common sense, you can disable this in the configuration.
600          *
601          * We also set the "message room name" ('O' field) to MAILROOM
602          * (which is Mail> on most systems) to prevent it from getting set
603          * to something ugly like "0000058008.Sent Items>" when the message
604          * is read with a Citadel client.
605          */
606         if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
607                 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
608                 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
609                 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
610                 if (msg->cm_fields['F'] != NULL) phree(msg->cm_fields['F']);
611                 if (msg->cm_fields['O'] != NULL) phree(msg->cm_fields['O']);
612                 msg->cm_fields['A'] = strdoop(CC->user.fullname);
613                 msg->cm_fields['N'] = strdoop(config.c_nodename);
614                 msg->cm_fields['H'] = strdoop(config.c_humannode);
615                 msg->cm_fields['F'] = strdoop(CC->cs_inet_email);
616                 msg->cm_fields['O'] = strdoop(MAILROOM);
617         }
618
619         /* Submit the message into the Citadel system. */
620         valid = validate_recipients(SMTP->recipients);
621
622         /* If there are modules that want to scan this message before final
623          * submission (such as virus checkers or spam filters), call them now
624          * and give them an opportunity to reject the message.
625          */
626         scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
627
628         if (scan_errors > 0) {  /* We don't want this message! */
629
630                 if (msg->cm_fields['0'] == NULL) {
631                         msg->cm_fields['0'] = strdoop(
632                                 "5.7.1 Message rejected by filter");
633                 }
634
635                 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
636         }
637         
638         else {                  /* Ok, we'll accept this message. */
639                 msgnum = CtdlSubmitMsg(msg, valid, "");
640                 if (msgnum > 0L) {
641                         sprintf(result, "250 2.0.0 Message accepted.\r\n");
642                 }
643                 else {
644                         sprintf(result, "550 5.5.0 Internal delivery error\r\n");
645                 }
646         }
647
648         /* For SMTP and ESTMP, just print the result message.  For LMTP, we
649          * have to print one result message for each recipient.  Since there
650          * is nothing in Citadel which would cause different recipients to
651          * have different results, we can get away with just spitting out the
652          * same message once for each recipient.
653          */
654         if (SMTP->is_lmtp) {
655                 for (i=0; i<SMTP->number_of_recipients; ++i) {
656                         cprintf("%s", result);
657                 }
658         }
659         else {
660                 cprintf("%s", result);
661         }
662
663         CtdlFreeMessage(msg);
664         phree(valid);
665         smtp_data_clear();      /* clear out the buffers now */
666 }
667
668
669 /*
670  * implements the STARTTLS command (Citadel API version)
671  */
672 #ifdef HAVE_OPENSSL
673 void smtp_starttls(void)
674 {
675         char ok_response[SIZ];
676         char nosup_response[SIZ];
677         char error_response[SIZ];
678
679         sprintf(ok_response,
680                 "200 2.0.0 Begin TLS negotiation now\r\n");
681         sprintf(nosup_response,
682                 "554 5.7.3 TLS not supported here\r\n");
683         sprintf(error_response,
684                 "554 5.7.3 Internal error\r\n");
685         CtdlStartTLS(ok_response, nosup_response, error_response);
686         smtp_rset(0);
687 }
688 #endif
689
690
691
692 /* 
693  * Main command loop for SMTP sessions.
694  */
695 void smtp_command_loop(void) {
696         char cmdbuf[SIZ];
697
698         time(&CC->lastcmd);
699         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
700         if (client_gets(cmdbuf) < 1) {
701                 lprintf(3, "SMTP socket is broken.  Ending session.\n");
702                 CC->kill_me = 1;
703                 return;
704         }
705         lprintf(5, "SMTP: %s\n", cmdbuf);
706         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
707
708         if (SMTP->command_state == smtp_user) {
709                 smtp_get_user(cmdbuf);
710         }
711
712         else if (SMTP->command_state == smtp_password) {
713                 smtp_get_pass(cmdbuf);
714         }
715
716         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
717                 smtp_auth(&cmdbuf[5]);
718         }
719
720         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
721                 smtp_data();
722         }
723
724         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
725                 smtp_expn(&cmdbuf[5]);
726         }
727
728         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
729                 smtp_hello(&cmdbuf[5], 0);
730         }
731
732         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
733                 smtp_hello(&cmdbuf[5], 1);
734         }
735
736         else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
737                 smtp_hello(&cmdbuf[5], 2);
738         }
739
740         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
741                 smtp_help();
742         }
743
744         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
745                 smtp_mail(&cmdbuf[5]);
746         }
747
748         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
749                 cprintf("250 NOOP\r\n");
750         }
751
752         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
753                 cprintf("221 Goodbye...\r\n");
754                 CC->kill_me = 1;
755                 return;
756         }
757
758         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
759                 smtp_rcpt(&cmdbuf[5]);
760         }
761
762         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
763                 smtp_rset(1);
764         }
765 #ifdef HAVE_OPENSSL
766         else if (!strcasecmp(cmdbuf, "STARTTLS")) {
767                 smtp_starttls();
768         }
769 #endif
770         else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
771                 smtp_vrfy(&cmdbuf[5]);
772         }
773
774         else {
775                 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
776         }
777
778 }
779
780
781
782
783 /*****************************************************************************/
784 /*               SMTP CLIENT (OUTBOUND PROCESSING) STUFF                     */
785 /*****************************************************************************/
786
787
788
789 /*
790  * smtp_try()
791  *
792  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
793  *
794  */
795 void smtp_try(const char *key, const char *addr, int *status,
796               char *dsn, size_t n, 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                 snprintf(dsn, n, "Error creating temporary file");
823                 return;
824         }
825         else {
826                 CtdlRedirectOutput(msg_fp, -1);
827                 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 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         /* Figure out what mail exchanger host we have to connect to */
879         num_mxhosts = getmx(mxhosts, node);
880         lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
881         if (num_mxhosts < 1) {
882                 *status = 5;
883                 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
884                 return;
885         }
886
887         sock = (-1);
888         for (mx=0; (mx<num_mxhosts && sock < 0); ++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         }
896
897         if (sock < 0) {
898                 *status = 4;    /* dsn is already filled in */
899                 return;
900         }
901
902         /* Process the SMTP greeting from the server */
903         if (ml_sock_gets(sock, buf) < 0) {
904                 *status = 4;
905                 strcpy(dsn, "Connection broken during SMTP conversation");
906                 goto bail;
907         }
908         lprintf(9, "<%s\n", buf);
909         if (buf[0] != '2') {
910                 if (buf[0] == '4') {
911                         *status = 4;
912                         safestrncpy(dsn, &buf[4], 1023);
913                         goto bail;
914                 }
915                 else {
916                         *status = 5;
917                         safestrncpy(dsn, &buf[4], 1023);
918                         goto bail;
919                 }
920         }
921
922         /* At this point we know we are talking to a real SMTP server */
923
924         /* Do a HELO command */
925         snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
926         lprintf(9, ">%s", buf);
927         sock_write(sock, buf, strlen(buf));
928         if (ml_sock_gets(sock, buf) < 0) {
929                 *status = 4;
930                 strcpy(dsn, "Connection broken during SMTP HELO");
931                 goto bail;
932         }
933         lprintf(9, "<%s\n", buf);
934         if (buf[0] != '2') {
935                 if (buf[0] == '4') {
936                         *status = 4;
937                         safestrncpy(dsn, &buf[4], 1023);
938                         goto bail;
939                 }
940                 else {
941                         *status = 5;
942                         safestrncpy(dsn, &buf[4], 1023);
943                         goto bail;
944                 }
945         }
946
947
948         /* HELO succeeded, now try the MAIL From: command */
949         snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
950         lprintf(9, ">%s", buf);
951         sock_write(sock, buf, strlen(buf));
952         if (ml_sock_gets(sock, buf) < 0) {
953                 *status = 4;
954                 strcpy(dsn, "Connection broken during SMTP MAIL");
955                 goto bail;
956         }
957         lprintf(9, "<%s\n", buf);
958         if (buf[0] != '2') {
959                 if (buf[0] == '4') {
960                         *status = 4;
961                         safestrncpy(dsn, &buf[4], 1023);
962                         goto bail;
963                 }
964                 else {
965                         *status = 5;
966                         safestrncpy(dsn, &buf[4], 1023);
967                         goto bail;
968                 }
969         }
970
971
972         /* MAIL succeeded, now try the RCPT To: command */
973         snprintf(buf, sizeof buf, "RCPT To: <%s>\r\n", addr);
974         lprintf(9, ">%s", buf);
975         sock_write(sock, buf, strlen(buf));
976         if (ml_sock_gets(sock, buf) < 0) {
977                 *status = 4;
978                 strcpy(dsn, "Connection broken during SMTP RCPT");
979                 goto bail;
980         }
981         lprintf(9, "<%s\n", buf);
982         if (buf[0] != '2') {
983                 if (buf[0] == '4') {
984                         *status = 4;
985                         safestrncpy(dsn, &buf[4], 1023);
986                         goto bail;
987                 }
988                 else {
989                         *status = 5;
990                         safestrncpy(dsn, &buf[4], 1023);
991                         goto bail;
992                 }
993         }
994
995
996         /* RCPT succeeded, now try the DATA command */
997         lprintf(9, ">DATA\n");
998         sock_write(sock, "DATA\r\n", 6);
999         if (ml_sock_gets(sock, buf) < 0) {
1000                 *status = 4;
1001                 strcpy(dsn, "Connection broken during SMTP DATA");
1002                 goto bail;
1003         }
1004         lprintf(9, "<%s\n", buf);
1005         if (buf[0] != '3') {
1006                 if (buf[0] == '4') {
1007                         *status = 3;
1008                         safestrncpy(dsn, &buf[4], 1023);
1009                         goto bail;
1010                 }
1011                 else {
1012                         *status = 5;
1013                         safestrncpy(dsn, &buf[4], 1023);
1014                         goto bail;
1015                 }
1016         }
1017
1018         /* If we reach this point, the server is expecting data */
1019         rewind(msg_fp);
1020         while (msg_size > 0) {
1021                 blocksize = sizeof(buf);
1022                 if (blocksize > msg_size) blocksize = msg_size;
1023                 fread(buf, blocksize, 1, msg_fp);
1024                 sock_write(sock, buf, blocksize);
1025                 msg_size -= blocksize;
1026         }
1027         if (buf[blocksize-1] != 10) {
1028                 lprintf(5, "Possible problem: message did not correctly "
1029                         "terminate. (expecting 0x10, got 0x%02x)\n",
1030                                 buf[blocksize-1]);
1031         }
1032
1033         sock_write(sock, ".\r\n", 3);
1034         if (ml_sock_gets(sock, buf) < 0) {
1035                 *status = 4;
1036                 strcpy(dsn, "Connection broken during SMTP message transmit");
1037                 goto bail;
1038         }
1039         lprintf(9, "%s\n", buf);
1040         if (buf[0] != '2') {
1041                 if (buf[0] == '4') {
1042                         *status = 4;
1043                         safestrncpy(dsn, &buf[4], 1023);
1044                         goto bail;
1045                 }
1046                 else {
1047                         *status = 5;
1048                         safestrncpy(dsn, &buf[4], 1023);
1049                         goto bail;
1050                 }
1051         }
1052
1053         /* We did it! */
1054         safestrncpy(dsn, &buf[4], 1023);
1055         *status = 2;
1056
1057         lprintf(9, ">QUIT\n");
1058         sock_write(sock, "QUIT\r\n", 6);
1059         ml_sock_gets(sock, buf);
1060         lprintf(9, "<%s\n", buf);
1061
1062 bail:   if (msg_fp != NULL) fclose(msg_fp);
1063         sock_close(sock);
1064         return;
1065 }
1066
1067
1068
1069 /*
1070  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1071  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1072  * a "bounce" message (delivery status notification).
1073  */
1074 void smtp_do_bounce(char *instr) {
1075         int i;
1076         int lines;
1077         int status;
1078         char buf[1024];
1079         char key[1024];
1080         char addr[1024];
1081         char dsn[1024];
1082         char bounceto[1024];
1083         int num_bounces = 0;
1084         int bounce_this = 0;
1085         long bounce_msgid = (-1);
1086         time_t submitted = 0L;
1087         struct CtdlMessage *bmsg = NULL;
1088         int give_up = 0;
1089         struct recptypes *valid;
1090         int successful_bounce = 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         for (i=0; i<lines; ++i) {
1140                 extract_token(buf, instr, i, '\n');
1141                 extract(key, buf, 0);
1142                 extract(addr, buf, 1);
1143                 status = extract_int(buf, 2);
1144                 extract(dsn, buf, 3);
1145                 bounce_this = 0;
1146
1147                 lprintf(9, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1148                         key, addr, status, dsn);
1149
1150                 if (!strcasecmp(key, "bounceto")) {
1151                         strcpy(bounceto, addr);
1152                 }
1153
1154                 if (
1155                    (!strcasecmp(key, "local"))
1156                    || (!strcasecmp(key, "remote"))
1157                    || (!strcasecmp(key, "ignet"))
1158                    || (!strcasecmp(key, "room"))
1159                 ) {
1160                         if (status == 5) bounce_this = 1;
1161                         if (give_up) bounce_this = 1;
1162                 }
1163
1164                 if (bounce_this) {
1165                         ++num_bounces;
1166
1167                         if (bmsg->cm_fields['M'] == NULL) {
1168                                 lprintf(2, "ERROR ... M field is null "
1169                                         "(%s:%d)\n", __FILE__, __LINE__);
1170                         }
1171
1172                         bmsg->cm_fields['M'] = reallok(bmsg->cm_fields['M'],
1173                                 strlen(bmsg->cm_fields['M']) + 1024 );
1174                         strcat(bmsg->cm_fields['M'], addr);
1175                         strcat(bmsg->cm_fields['M'], ": ");
1176                         strcat(bmsg->cm_fields['M'], dsn);
1177                         strcat(bmsg->cm_fields['M'], "\n");
1178
1179                         remove_token(instr, i, '\n');
1180                         --i;
1181                         --lines;
1182                 }
1183         }
1184
1185         /* Deliver the bounce if there's anything worth mentioning */
1186         lprintf(9, "num_bounces = %d\n", num_bounces);
1187         if (num_bounces > 0) {
1188
1189                 /* First try the user who sent the message */
1190                 lprintf(9, "bounce to user? <%s>\n", bounceto);
1191                 if (strlen(bounceto) == 0) {
1192                         lprintf(7, "No bounce address specified\n");
1193                         bounce_msgid = (-1L);
1194                 }
1195
1196                 /* Can we deliver the bounce to the original sender? */
1197                 valid = validate_recipients(bounceto);
1198                 if (valid != NULL) {
1199                         if (valid->num_error == 0) {
1200                                 CtdlSubmitMsg(bmsg, valid, "");
1201                                 successful_bounce = 1;
1202                         }
1203                 }
1204
1205                 /* If not, post it in the Aide> room */
1206                 if (successful_bounce == 0) {
1207                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1208                 }
1209
1210                 /* Free up the memory we used */
1211                 if (valid != NULL) {
1212                         phree(valid);
1213                 }
1214         }
1215
1216         CtdlFreeMessage(bmsg);
1217         lprintf(9, "Done processing bounces\n");
1218 }
1219
1220
1221 /*
1222  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1223  * set of delivery instructions for completed deliveries and remove them.
1224  *
1225  * It returns the number of incomplete deliveries remaining.
1226  */
1227 int smtp_purge_completed_deliveries(char *instr) {
1228         int i;
1229         int lines;
1230         int status;
1231         char buf[1024];
1232         char key[1024];
1233         char addr[1024];
1234         char dsn[1024];
1235         int completed;
1236         int incomplete = 0;
1237
1238         lines = num_tokens(instr, '\n');
1239         for (i=0; i<lines; ++i) {
1240                 extract_token(buf, instr, i, '\n');
1241                 extract(key, buf, 0);
1242                 extract(addr, buf, 1);
1243                 status = extract_int(buf, 2);
1244                 extract(dsn, buf, 3);
1245
1246                 completed = 0;
1247
1248                 if (
1249                    (!strcasecmp(key, "local"))
1250                    || (!strcasecmp(key, "remote"))
1251                    || (!strcasecmp(key, "ignet"))
1252                    || (!strcasecmp(key, "room"))
1253                 ) {
1254                         if (status == 2) completed = 1;
1255                         else ++incomplete;
1256                 }
1257
1258                 if (completed) {
1259                         remove_token(instr, i, '\n');
1260                         --i;
1261                         --lines;
1262                 }
1263         }
1264
1265         return(incomplete);
1266 }
1267
1268
1269 /*
1270  * smtp_do_procmsg()
1271  *
1272  * Called by smtp_do_queue() to handle an individual message.
1273  */
1274 void smtp_do_procmsg(long msgnum, void *userdata) {
1275         struct CtdlMessage *msg;
1276         char *instr = NULL;
1277         char *results = NULL;
1278         int i;
1279         int lines;
1280         int status;
1281         char buf[1024];
1282         char key[1024];
1283         char addr[1024];
1284         char dsn[1024];
1285         long text_msgid = (-1);
1286         int incomplete_deliveries_remaining;
1287         time_t attempted = 0L;
1288         time_t last_attempted = 0L;
1289         time_t retry = SMTP_RETRY_INTERVAL;
1290
1291         lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1292
1293         msg = CtdlFetchMessage(msgnum);
1294         if (msg == NULL) {
1295                 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1296                 return;
1297         }
1298
1299         instr = strdoop(msg->cm_fields['M']);
1300         CtdlFreeMessage(msg);
1301
1302         /* Strip out the headers amd any other non-instruction line */
1303         lines = num_tokens(instr, '\n');
1304         for (i=0; i<lines; ++i) {
1305                 extract_token(buf, instr, i, '\n');
1306                 if (num_tokens(buf, '|') < 2) {
1307                         remove_token(instr, i, '\n');
1308                         --lines;
1309                         --i;
1310                 }
1311         }
1312
1313         /* Learn the message ID and find out about recent delivery attempts */
1314         lines = num_tokens(instr, '\n');
1315         for (i=0; i<lines; ++i) {
1316                 extract_token(buf, instr, i, '\n');
1317                 extract(key, buf, 0);
1318                 if (!strcasecmp(key, "msgid")) {
1319                         text_msgid = extract_long(buf, 1);
1320                 }
1321                 if (!strcasecmp(key, "retry")) {
1322                         /* double the retry interval after each attempt */
1323                         retry = extract_long(buf, 1) * 2L;
1324                         if (retry > SMTP_RETRY_MAX) {
1325                                 retry = SMTP_RETRY_MAX;
1326                         }
1327                         remove_token(instr, i, '\n');
1328                 }
1329                 if (!strcasecmp(key, "attempted")) {
1330                         attempted = extract_long(buf, 1);
1331                         if (attempted > last_attempted)
1332                                 last_attempted = attempted;
1333                 }
1334         }
1335
1336         /*
1337          * Postpone delivery if we've already tried recently.
1338          */
1339         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1340                 lprintf(7, "Retry time not yet reached.\n");
1341                 phree(instr);
1342                 return;
1343         }
1344
1345
1346         /*
1347          * Bail out if there's no actual message associated with this
1348          */
1349         if (text_msgid < 0L) {
1350                 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1351                 phree(instr);
1352                 return;
1353         }
1354
1355         /* Plow through the instructions looking for 'remote' directives and
1356          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1357          * were experienced and it's time to try again)
1358          */
1359         lines = num_tokens(instr, '\n');
1360         for (i=0; i<lines; ++i) {
1361                 extract_token(buf, instr, i, '\n');
1362                 extract(key, buf, 0);
1363                 extract(addr, buf, 1);
1364                 status = extract_int(buf, 2);
1365                 extract(dsn, buf, 3);
1366                 if ( (!strcasecmp(key, "remote"))
1367                    && ((status==0)||(status==3)||(status==4)) ) {
1368
1369                         /* Remove this "remote" instruction from the set,
1370                          * but replace the set's final newline if
1371                          * remove_token() stripped it.  It has to be there.
1372                          */
1373                         remove_token(instr, i, '\n');
1374                         if (instr[strlen(instr)-1] != '\n') {
1375                                 strcat(instr, "\n");
1376                         }
1377
1378                         --i;
1379                         --lines;
1380                         lprintf(9, "SMTP: Trying <%s>\n", addr);
1381                         smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1382                         if (status != 2) {
1383                                 if (results == NULL) {
1384                                         results = mallok(1024);
1385                                         memset(results, 0, 1024);
1386                                 }
1387                                 else {
1388                                         results = reallok(results,
1389                                                 strlen(results) + 1024);
1390                                 }
1391                                 snprintf(&results[strlen(results)], 1024,
1392                                         "%s|%s|%d|%s\n",
1393                                         key, addr, status, dsn);
1394                         }
1395                 }
1396         }
1397
1398         if (results != NULL) {
1399                 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1400                 strcat(instr, results);
1401                 phree(results);
1402         }
1403
1404
1405         /* Generate 'bounce' messages */
1406         smtp_do_bounce(instr);
1407
1408         /* Go through the delivery list, deleting completed deliveries */
1409         incomplete_deliveries_remaining = 
1410                 smtp_purge_completed_deliveries(instr);
1411
1412
1413         /*
1414          * No delivery instructions remain, so delete both the instructions
1415          * message and the message message.
1416          */
1417         if (incomplete_deliveries_remaining <= 0) {
1418                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1419                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1420         }
1421
1422
1423         /*
1424          * Uncompleted delivery instructions remain, so delete the old
1425          * instructions and replace with the updated ones.
1426          */
1427         if (incomplete_deliveries_remaining > 0) {
1428                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1429                 msg = mallok(sizeof(struct CtdlMessage));
1430                 memset(msg, 0, sizeof(struct CtdlMessage));
1431                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1432                 msg->cm_anon_type = MES_NORMAL;
1433                 msg->cm_format_type = FMT_RFC822;
1434                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1435                 snprintf(msg->cm_fields['M'],
1436                         strlen(instr)+SIZ,
1437                         "Content-type: %s\n\n%s\n"
1438                         "attempted|%ld\n"
1439                         "retry|%ld\n",
1440                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1441                 phree(instr);
1442                 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1443                 CtdlFreeMessage(msg);
1444         }
1445
1446 }
1447
1448
1449
1450 /*
1451  * smtp_do_queue()
1452  * 
1453  * Run through the queue sending out messages.
1454  */
1455 void smtp_do_queue(void) {
1456         static int doing_queue = 0;
1457
1458         /*
1459          * This is a simple concurrency check to make sure only one queue run
1460          * is done at a time.  We could do this with a mutex, but since we
1461          * don't really require extremely fine granularity here, we'll do it
1462          * with a static variable instead.
1463          */
1464         if (doing_queue) return;
1465         doing_queue = 1;
1466
1467         /* 
1468          * Go ahead and run the queue
1469          */
1470         lprintf(7, "SMTP: processing outbound queue\n");
1471
1472         if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1473                 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1474                 return;
1475         }
1476         CtdlForEachMessage(MSGS_ALL, 0L,
1477                 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1478
1479         lprintf(7, "SMTP: queue run completed\n");
1480         run_queue_now = 0;
1481         doing_queue = 0;
1482 }
1483
1484
1485
1486 /*****************************************************************************/
1487 /*                          SMTP UTILITY COMMANDS                            */
1488 /*****************************************************************************/
1489
1490 void cmd_smtp(char *argbuf) {
1491         char cmd[SIZ];
1492         char node[SIZ];
1493         char buf[SIZ];
1494         int i;
1495         int num_mxhosts;
1496
1497         if (CtdlAccessCheck(ac_aide)) return;
1498
1499         extract(cmd, argbuf, 0);
1500
1501         if (!strcasecmp(cmd, "mx")) {
1502                 extract(node, argbuf, 1);
1503                 num_mxhosts = getmx(buf, node);
1504                 cprintf("%d %d MX hosts listed for %s\n",
1505                         LISTING_FOLLOWS, num_mxhosts, node);
1506                 for (i=0; i<num_mxhosts; ++i) {
1507                         extract(node, buf, i);
1508                         cprintf("%s\n", node);
1509                 }
1510                 cprintf("000\n");
1511                 return;
1512         }
1513
1514         else if (!strcasecmp(cmd, "runqueue")) {
1515                 run_queue_now = 1;
1516                 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1517                 return;
1518         }
1519
1520         else {
1521                 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1522         }
1523
1524 }
1525
1526
1527 /*
1528  * Initialize the SMTP outbound queue
1529  */
1530 void smtp_init_spoolout(void) {
1531         struct ctdlroom qrbuf;
1532
1533         /*
1534          * Create the room.  This will silently fail if the room already
1535          * exists, and that's perfectly ok, because we want it to exist.
1536          */
1537         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0);
1538
1539         /*
1540          * Make sure it's set to be a "system room" so it doesn't show up
1541          * in the <K>nown rooms list for Aides.
1542          */
1543         if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1544                 qrbuf.QRflags2 |= QR2_SYSTEM;
1545                 lputroom(&qrbuf);
1546         }
1547 }
1548
1549
1550
1551
1552 /*****************************************************************************/
1553 /*                      MODULE INITIALIZATION STUFF                          */
1554 /*****************************************************************************/
1555
1556
1557 char *serv_smtp_init(void)
1558 {
1559         CtdlRegisterServiceHook(config.c_smtp_port,     /* On the net... */
1560                                 NULL,
1561                                 smtp_greeting,
1562                                 smtp_command_loop);
1563
1564         CtdlRegisterServiceHook(0,                      /* ...and locally */
1565                                 "lmtp.socket",
1566                                 lmtp_greeting,
1567                                 smtp_command_loop);
1568
1569         smtp_init_spoolout();
1570         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1571         CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1572         return "$Id$";
1573 }