]> code.citadel.org Git - citadel.git/blob - citadel/serv_smtp.c
* mime_parser.c: change to memory allocation algorithm ... some badly done
[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 int run_queue_now = 0;  /* Set to 1 to ignore SMTP send retry times */
88
89
90
91 /*****************************************************************************/
92 /*                      SMTP SERVER (INBOUND) STUFF                          */
93 /*****************************************************************************/
94
95
96
97
98 /*
99  * Here's where our SMTP session begins its happy day.
100  */
101 void smtp_greeting(void) {
102
103         strcpy(CC->cs_clientname, "SMTP session");
104         CC->internal_pgm = 1;
105         CC->cs_flags |= CS_STEALTH;
106         CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
107         CtdlAllocUserData(SYM_SMTP_RECP, SIZ);
108         sprintf(SMTP_RECP, "%s", "");
109
110         cprintf("220 %s ESMTP Citadel/UX server ready.\r\n", config.c_fqdn);
111 }
112
113
114 /*
115  * Implement HELO and EHLO commands.
116  */
117 void smtp_hello(char *argbuf, int is_esmtp) {
118
119         safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
120
121         if (!is_esmtp) {
122                 cprintf("250 Greetings and joyous salutations.\r\n");
123         }
124         else {
125                 cprintf("250-Greetings and joyous salutations.\r\n");
126                 cprintf("250-HELP\r\n");
127                 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
128                 cprintf("250-PIPELINING\r\n");
129                 cprintf("250 AUTH=LOGIN\r\n");
130         }
131 }
132
133
134 /*
135  * Implement HELP command.
136  */
137 void smtp_help(void) {
138         cprintf("214-Commands accepted:\r\n");
139         cprintf("214-    DATA\r\n");
140         cprintf("214-    EHLO\r\n");
141         cprintf("214-    EXPN\r\n");
142         cprintf("214-    HELO\r\n");
143         cprintf("214-    HELP\r\n");
144         cprintf("214-    MAIL\r\n");
145         cprintf("214-    NOOP\r\n");
146         cprintf("214-    QUIT\r\n");
147         cprintf("214-    RCPT\r\n");
148         cprintf("214-    RSET\r\n");
149         cprintf("214-    VRFY\r\n");
150         cprintf("214     \r\n");
151 }
152
153
154 /*
155  *
156  */
157 void smtp_get_user(char *argbuf) {
158         char buf[SIZ];
159         char username[SIZ];
160
161         decode_base64(username, argbuf, SIZ);
162         lprintf(9, "Trying <%s>\n", username);
163         if (CtdlLoginExistingUser(username) == login_ok) {
164                 encode_base64(buf, "Password:");
165                 cprintf("334 %s\r\n", buf);
166                 SMTP->command_state = smtp_password;
167         }
168         else {
169                 cprintf("500 No such user.\r\n");
170                 SMTP->command_state = smtp_command;
171         }
172 }
173
174
175 /*
176  *
177  */
178 void smtp_get_pass(char *argbuf) {
179         char password[SIZ];
180
181         decode_base64(password, argbuf, SIZ);
182         lprintf(9, "Trying <%s>\n", password);
183         if (CtdlTryPassword(password) == pass_ok) {
184                 cprintf("235 Authentication successful.\r\n");
185                 lprintf(9, "SMTP authenticated login successful\n");
186                 CC->internal_pgm = 0;
187                 CC->cs_flags &= ~CS_STEALTH;
188         }
189         else {
190                 cprintf("500 Authentication failed.\r\n");
191         }
192         SMTP->command_state = smtp_command;
193 }
194
195
196 /*
197  *
198  */
199 void smtp_auth(char *argbuf) {
200         char buf[SIZ];
201
202         if (strncasecmp(argbuf, "login", 5) ) {
203                 cprintf("550 We only support LOGIN authentication.\r\n");
204                 return;
205         }
206
207         if (strlen(argbuf) >= 7) {
208                 smtp_get_user(&argbuf[6]);
209         }
210
211         else {
212                 encode_base64(buf, "Username:");
213                 cprintf("334 %s\r\n", buf);
214                 SMTP->command_state = smtp_user;
215         }
216 }
217
218
219 /*
220  * Back end for smtp_vrfy() command
221  */
222 void smtp_vrfy_backend(struct usersupp *us, void *data) {
223
224         if (!fuzzy_match(us, SMTP->vrfy_match)) {
225                 ++SMTP->vrfy_count;
226                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
227         }
228 }
229
230
231 /* 
232  * Implements the VRFY (verify user name) command.
233  * Performs fuzzy match on full user names.
234  */
235 void smtp_vrfy(char *argbuf) {
236         SMTP->vrfy_count = 0;
237         strcpy(SMTP->vrfy_match, argbuf);
238         ForEachUser(smtp_vrfy_backend, NULL);
239
240         if (SMTP->vrfy_count < 1) {
241                 cprintf("550 String does not match anything.\r\n");
242         }
243         else if (SMTP->vrfy_count == 1) {
244                 cprintf("250 %s <cit%ld@%s>\r\n",
245                         SMTP->vrfy_buffer.fullname,
246                         SMTP->vrfy_buffer.usernum,
247                         config.c_fqdn);
248         }
249         else if (SMTP->vrfy_count > 1) {
250                 cprintf("553 Request ambiguous: %d users matched.\r\n",
251                         SMTP->vrfy_count);
252         }
253
254 }
255
256
257
258 /*
259  * Back end for smtp_expn() command
260  */
261 void smtp_expn_backend(struct usersupp *us, void *data) {
262
263         if (!fuzzy_match(us, SMTP->vrfy_match)) {
264
265                 if (SMTP->vrfy_count >= 1) {
266                         cprintf("250-%s <cit%ld@%s>\r\n",
267                                 SMTP->vrfy_buffer.fullname,
268                                 SMTP->vrfy_buffer.usernum,
269                                 config.c_fqdn);
270                 }
271
272                 ++SMTP->vrfy_count;
273                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
274         }
275 }
276
277
278 /* 
279  * Implements the EXPN (expand user name) command.
280  * Performs fuzzy match on full user names.
281  */
282 void smtp_expn(char *argbuf) {
283         SMTP->vrfy_count = 0;
284         strcpy(SMTP->vrfy_match, argbuf);
285         ForEachUser(smtp_expn_backend, NULL);
286
287         if (SMTP->vrfy_count < 1) {
288                 cprintf("550 String does not match anything.\r\n");
289         }
290         else if (SMTP->vrfy_count >= 1) {
291                 cprintf("250 %s <cit%ld@%s>\r\n",
292                         SMTP->vrfy_buffer.fullname,
293                         SMTP->vrfy_buffer.usernum,
294                         config.c_fqdn);
295         }
296 }
297
298
299 /*
300  * Implements the RSET (reset state) command.
301  * Currently this just zeroes out the state buffer.  If pointers to data
302  * allocated with mallok() are ever placed in the state buffer, we have to
303  * be sure to phree() them first!
304  */
305 void smtp_rset(void) {
306         memset(SMTP, 0, sizeof(struct citsmtp));
307         if (SMTP_RECP != NULL) strcpy(SMTP_RECP, "");
308         if (CC->logged_in) logout(CC);
309         cprintf("250 Zap!\r\n");
310 }
311
312 /*
313  * Clear out the portions of the state buffer that need to be cleared out
314  * after the DATA command finishes.
315  */
316 void smtp_data_clear(void) {
317         strcpy(SMTP->from, "");
318         SMTP->number_of_recipients = 0;
319         SMTP->delivery_mode = 0;
320         SMTP->message_originated_locally = 0;
321         if (SMTP_RECP != NULL) strcpy(SMTP_RECP, "");
322 }
323
324
325
326 /*
327  * Implements the "MAIL From:" command
328  */
329 void smtp_mail(char *argbuf) {
330         char user[SIZ];
331         char node[SIZ];
332         int cvt;
333
334         if (strlen(SMTP->from) != 0) {
335                 cprintf("503 Only one sender permitted\r\n");
336                 return;
337         }
338
339         if (strncasecmp(argbuf, "From:", 5)) {
340                 cprintf("501 Syntax error\r\n");
341                 return;
342         }
343
344         strcpy(SMTP->from, &argbuf[5]);
345         striplt(SMTP->from);
346
347         if (strlen(SMTP->from) == 0) {
348                 cprintf("501 Empty sender name is not permitted\r\n");
349                 return;
350         }
351
352
353         /* If this SMTP connection is from a logged-in user, make sure that
354          * the user only sends email from his/her own address.
355          */
356         if (CC->logged_in) {
357                 cvt = convert_internet_address(user, node, SMTP->from);
358                 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
359                 if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
360                         cprintf("550 <%s> is not your address.\r\n", SMTP->from);
361                         strcpy(SMTP->from, "");
362                         return;
363                 }
364                 else {
365                         SMTP->message_originated_locally = 1;
366                 }
367         }
368
369         /* Otherwise, make sure outsiders aren't trying to forge mail from
370          * this system.
371          */
372         else {
373                 cvt = convert_internet_address(user, node, SMTP->from);
374                 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
375                 if (CtdlHostAlias(node) == hostalias_localhost) {
376                         cprintf("550 You must log in to send mail from %s\r\n",
377                                 node);
378                         strcpy(SMTP->from, "");
379                         return;
380                 }
381         }
382
383         cprintf("250 Sender ok\r\n");
384 }
385
386
387
388 /*
389  * Implements the "RCPT To:" command
390  */
391 void smtp_rcpt(char *argbuf) {
392         int cvt;
393         char user[SIZ];
394         char node[SIZ];
395         char recp[SIZ];
396
397         if (strlen(SMTP->from) == 0) {
398                 cprintf("503 Need MAIL before RCPT\r\n");
399                 return;
400         }
401
402         if (strncasecmp(argbuf, "To:", 3)) {
403                 cprintf("501 Syntax error\r\n");
404                 return;
405         }
406
407         strcpy(recp, &argbuf[3]);
408         striplt(recp);
409         alias(recp);
410
411         cvt = convert_internet_address(user, node, recp);
412         snprintf(recp, sizeof recp, "%s@%s", user, node);
413         lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
414
415         switch(cvt) {
416                 case rfc822_address_locally_validated:
417                         cprintf("250 %s is a valid recipient.\r\n", user);
418                         ++SMTP->number_of_recipients;
419                         CtdlReallocUserData(SYM_SMTP_RECP,
420                                 strlen(SMTP_RECP) + 1024 );
421                         strcat(SMTP_RECP, "local|");
422                         strcat(SMTP_RECP, user);
423                         strcat(SMTP_RECP, "|0\n");
424                         return;
425
426                 case rfc822_room_delivery:
427                         cprintf("250 Delivering to room '%s'\r\n", user);
428                         ++SMTP->number_of_recipients;
429                         CtdlReallocUserData(SYM_SMTP_RECP,
430                                 strlen(SMTP_RECP) + 1024 );
431                         strcat(SMTP_RECP, "room|");
432                         strcat(SMTP_RECP, user);
433                         strcat(SMTP_RECP, "|0|\n");
434                         return;
435
436                 case rfc822_no_such_user:
437                         cprintf("550 %s: no such user\r\n", recp);
438                         return;
439
440                 case rfc822_address_on_citadel_network:
441                         cprintf("250 %s is on the local network\r\n", recp);
442                         ++SMTP->number_of_recipients;
443                         CtdlReallocUserData(SYM_SMTP_RECP,
444                                 strlen(SMTP_RECP) + 1024 );
445                         strcat(SMTP_RECP, "ignet|");
446                         strcat(SMTP_RECP, user);
447                         strcat(SMTP_RECP, "|");
448                         strcat(SMTP_RECP, node);
449                         strcat(SMTP_RECP, "|0|\n");
450                         return;
451
452                 case rfc822_address_nonlocal:
453                         if (SMTP->message_originated_locally == 0) {
454                                 cprintf("551 Relaying denied.\r\n");
455                         }
456                         else {
457                                 cprintf("250 Remote recipient %s ok\r\n", recp);
458                                 ++SMTP->number_of_recipients;
459                                 CtdlReallocUserData(SYM_SMTP_RECP,
460                                         strlen(SMTP_RECP) + 1024 );
461                                 strcat(SMTP_RECP, "remote|");
462                                 strcat(SMTP_RECP, recp);
463                                 strcat(SMTP_RECP, "|0|\n");
464                                 return;
465                         }
466                         return;
467         }
468
469         cprintf("599 Unknown error\r\n");
470 }
471
472
473
474 /*
475  * Send a message out through the local network
476  * (This is kind of ugly.  IGnet should be done using clean server-to-server
477  * code instead of the old style spool.)
478  */
479 void smtp_deliver_ignet(struct CtdlMessage *msg, char *user, char *dest) {
480         struct ser_ret smr;
481         char *hold_R, *hold_D, *hold_O;
482         FILE *fp;
483         char filename[SIZ];
484         static int seq = 0;
485
486         lprintf(9, "smtp_deliver_ignet(msg, %s, %s)\n", user, dest);
487
488         hold_R = msg->cm_fields['R'];
489         hold_D = msg->cm_fields['D'];
490         hold_O = msg->cm_fields['O'];
491         msg->cm_fields['R'] = user;
492         msg->cm_fields['D'] = dest;
493         msg->cm_fields['O'] = MAILROOM;
494
495         serialize_message(&smr, msg);
496
497         msg->cm_fields['R'] = hold_R;
498         msg->cm_fields['D'] = hold_D;
499         msg->cm_fields['O'] = hold_O;
500
501         if (smr.len != 0) {
502                 snprintf(filename, sizeof filename,
503                         "./network/spoolin/%s.%04x.%04x",
504                         dest, getpid(), ++seq);
505                 lprintf(9, "spool file name is <%s>\n", filename);
506                 fp = fopen(filename, "wb");
507                 if (fp != NULL) {
508                         fwrite(smr.ser, smr.len, 1, fp);
509                         fclose(fp);
510                 }
511                 phree(smr.ser);
512         }
513
514 }
515
516
517
518 /*
519  * Back end for smtp_data()  ... this does the actual delivery of the message
520  * Returns 0 on success, nonzero on failure
521  */
522 int smtp_message_delivery(struct CtdlMessage *msg) {
523         char user[1024];
524         char node[1024];
525         char name[1024];
526         char buf[1024];
527         char dtype[1024];
528         char room[1024];
529         int successful_saves = 0;       /* number of successful local saves */
530         int failed_saves = 0;           /* number of failed deliveries */
531         int remote_spools = 0;          /* number of copies to send out */
532         long msgid = (-1L);
533         int i;
534         struct usersupp userbuf;
535         char *instr;                    /* Remote delivery instructions */
536         struct CtdlMessage *imsg;
537
538         lprintf(9, "smtp_message_delivery() called\n");
539
540         /* Fill in 'from' fields with envelope information if missing */
541         process_rfc822_addr(SMTP->from, user, node, name);
542         if (msg->cm_fields['A']==NULL) msg->cm_fields['A'] = strdoop(user);
543         if (msg->cm_fields['N']==NULL) msg->cm_fields['N'] = strdoop(node);
544         if (msg->cm_fields['H']==NULL) msg->cm_fields['H'] = strdoop(name);
545         if (msg->cm_fields['O']==NULL) msg->cm_fields['O'] = strdoop(MAILROOM);
546
547         /* Save the message in the queue */
548         msgid = CtdlSaveMsg(msg,
549                 "",
550                 SMTP_SPOOLOUT_ROOM,
551                 MES_LOCAL);
552         ++successful_saves;
553
554         instr = mallok(1024);
555         snprintf(instr, 1024,
556                         "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
557                         "bounceto|%s\n",
558                 SPOOLMIME, msgid, (long)time(NULL),
559                 SMTP->from );
560
561         for (i=0; i<SMTP->number_of_recipients; ++i) {
562                 extract_token(buf, SMTP_RECP, i, '\n');
563                 extract(dtype, buf, 0);
564
565                 /* Stuff local mailboxes */
566                 if (!strcasecmp(dtype, "local")) {
567                         extract(user, buf, 1);
568                         if (getuser(&userbuf, user) == 0) {
569                                 MailboxName(room, &userbuf, MAILROOM);
570                                 CtdlSaveMsgPointerInRoom(room, msgid, 0);
571                                 ++successful_saves;
572                         }
573                         else {
574                                 ++failed_saves;
575                         }
576                 }
577
578                 /* Delivery to local non-mailbox rooms */
579                 if (!strcasecmp(dtype, "room")) {
580                         extract(room, buf, 1);
581                         CtdlSaveMsgPointerInRoom(room, msgid, 0);
582                         ++successful_saves;
583                 }
584
585                 /* Delivery over the local Citadel network (IGnet) */
586                 if (!strcasecmp(dtype, "ignet")) {
587                         extract(user, buf, 1);
588                         extract(node, buf, 2);
589                         smtp_deliver_ignet(msg, user, node);
590                 }
591
592                 /* Remote delivery */
593                 if (!strcasecmp(dtype, "remote")) {
594                         extract(user, buf, 1);
595                         instr = reallok(instr, strlen(instr) + 1024);
596                         snprintf(&instr[strlen(instr)],
597                                 strlen(instr) + 1024,
598                                 "remote|%s|0\n",
599                                 user);
600                         ++remote_spools;
601                 }
602
603         }
604
605         /* If there are remote spools to be done, save the instructions */
606         if (remote_spools > 0) {
607                 imsg = mallok(sizeof(struct CtdlMessage));
608                 memset(imsg, 0, sizeof(struct CtdlMessage));
609                 imsg->cm_magic = CTDLMESSAGE_MAGIC;
610                 imsg->cm_anon_type = MES_NORMAL;
611                 imsg->cm_format_type = FMT_RFC822;
612                 imsg->cm_fields['M'] = instr;
613                 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
614                 CtdlFreeMessage(imsg);
615         }
616
617         /* If there are no remote spools, delete the message */ 
618         else {
619                 phree(instr);   /* only needed here, because CtdlSaveMsg()
620                                  * would free this buffer otherwise */
621                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, ""); 
622         }
623
624         return(failed_saves);
625 }
626
627
628
629 /*
630  * Implements the DATA command
631  */
632 void smtp_data(void) {
633         char *body;
634         struct CtdlMessage *msg;
635         int retval;
636         char nowstamp[SIZ];
637
638         if (strlen(SMTP->from) == 0) {
639                 cprintf("503 Need MAIL command first.\r\n");
640                 return;
641         }
642
643         if (SMTP->number_of_recipients < 1) {
644                 cprintf("503 Need RCPT command first.\r\n");
645                 return;
646         }
647
648         cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
649         
650         datestring(nowstamp, time(NULL), DATESTRING_RFC822);
651         body = mallok(4096);
652
653         if (body != NULL) snprintf(body, 4096,
654                 "Received: from %s\n"
655                 "       by %s;\n"
656                 "       %s\n",
657                         SMTP->helo_node,
658                         config.c_fqdn,
659                         nowstamp);
660         
661         body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
662         if (body == NULL) {
663                 cprintf("550 Unable to save message: internal error.\r\n");
664                 return;
665         }
666
667         lprintf(9, "Converting message...\n");
668         msg = convert_internet_message(body);
669
670         /* If the user is locally authenticated, FORCE the From: header to
671          * show up as the real sender.  Yes, this violates the RFC standard,
672          * but IT MAKES SENSE.  Comment it out if you don't like this behavior.
673          */
674         if (CC->logged_in) {
675                 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
676                 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
677                 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
678                 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
679                 msg->cm_fields['N'] = strdoop(config.c_nodename);
680                 msg->cm_fields['H'] = strdoop(config.c_humannode);
681         }
682
683         /* Submit the message into the Citadel system. */
684         retval = smtp_message_delivery(msg);
685         CtdlFreeMessage(msg);
686
687         if (!retval) {
688                 cprintf("250 Message accepted.\r\n");
689         }
690         else {
691                 cprintf("550 Internal delivery errors: %d\r\n", retval);
692         }
693
694         smtp_data_clear();      /* clear out the buffers now */
695 }
696
697
698
699
700 /* 
701  * Main command loop for SMTP sessions.
702  */
703 void smtp_command_loop(void) {
704         char cmdbuf[SIZ];
705
706         time(&CC->lastcmd);
707         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
708         if (client_gets(cmdbuf) < 1) {
709                 lprintf(3, "SMTP socket is broken.  Ending session.\n");
710                 CC->kill_me = 1;
711                 return;
712         }
713         lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
714         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
715
716         if (SMTP->command_state == smtp_user) {
717                 smtp_get_user(cmdbuf);
718         }
719
720         else if (SMTP->command_state == smtp_password) {
721                 smtp_get_pass(cmdbuf);
722         }
723
724         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
725                 smtp_auth(&cmdbuf[5]);
726         }
727
728         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
729                 smtp_data();
730         }
731
732         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
733                 smtp_hello(&cmdbuf[5], 1);
734         }
735
736         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
737                 smtp_expn(&cmdbuf[5]);
738         }
739
740         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
741                 smtp_hello(&cmdbuf[5], 0);
742         }
743
744         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
745                 smtp_help();
746         }
747
748         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
749                 smtp_mail(&cmdbuf[5]);
750         }
751
752         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
753                 cprintf("250 NOOP\r\n");
754         }
755
756         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
757                 cprintf("221 Goodbye...\r\n");
758                 CC->kill_me = 1;
759                 return;
760         }
761
762         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
763                 smtp_rcpt(&cmdbuf[5]);
764         }
765
766         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
767                 smtp_rset();
768         }
769
770         else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
771                 smtp_vrfy(&cmdbuf[5]);
772         }
773
774         else {
775                 cprintf("502 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(char *key, char *addr, int *status, char *dsn, long msgnum)
796 {
797         int sock = (-1);
798         char mxhosts[1024];
799         int num_mxhosts;
800         int mx;
801         int i;
802         char user[SIZ], node[SIZ], name[SIZ];
803         char buf[1024];
804         char mailfrom[1024];
805         int lp, rp;
806         FILE *msg_fp = NULL;
807         size_t msg_size;
808         size_t blocksize = 0;
809         int scan_done;
810
811         /* Parse out the host portion of the recipient address */
812         process_rfc822_addr(addr, user, node, name);
813
814         lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
815                 user, node, name);
816
817         /* Load the message out of the database into a temp file */
818         msg_fp = tmpfile();
819         if (msg_fp == NULL) {
820                 *status = 4;
821                 sprintf(dsn, "Error creating temporary file");
822                 return;
823         }
824         else {
825                 CtdlRedirectOutput(msg_fp, -1);
826                 CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
827                 CtdlRedirectOutput(NULL, -1);
828                 fseek(msg_fp, 0L, SEEK_END);
829                 msg_size = ftell(msg_fp);
830         }
831
832
833         /* Extract something to send later in the 'MAIL From:' command */
834         strcpy(mailfrom, "");
835         rewind(msg_fp);
836         scan_done = 0;
837         do {
838                 if (fgets(buf, sizeof buf, msg_fp)==NULL) scan_done = 1;
839                 if (!strncasecmp(buf, "From:", 5)) {
840                         safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
841                         striplt(mailfrom);
842                         for (i=0; i<strlen(mailfrom); ++i) {
843                                 if (!isprint(mailfrom[i])) {
844                                         strcpy(&mailfrom[i], &mailfrom[i+1]);
845                                         i=0;
846                                 }
847                         }
848
849                         /* Strip out parenthesized names */
850                         lp = (-1);
851                         rp = (-1);
852                         for (i=0; i<strlen(mailfrom); ++i) {
853                                 if (mailfrom[i] == '(') lp = i;
854                                 if (mailfrom[i] == ')') rp = i;
855                         }
856                         if ((lp>0)&&(rp>lp)) {
857                                 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
858                         }
859
860                         /* Prefer brokketized names */
861                         lp = (-1);
862                         rp = (-1);
863                         for (i=0; i<strlen(mailfrom); ++i) {
864                                 if (mailfrom[i] == '<') lp = i;
865                                 if (mailfrom[i] == '>') rp = i;
866                         }
867                         if ((lp>=0)&&(rp>lp)) {
868                                 mailfrom[rp] = 0;
869                                 strcpy(mailfrom, &mailfrom[lp]);
870                         }
871
872                         scan_done = 1;
873                 }
874         } while (scan_done == 0);
875         if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
876
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         for (mx=0; mx<num_mxhosts; ++mx) {
888                 extract(buf, mxhosts, mx);
889                 lprintf(9, "Trying <%s>\n", buf);
890                 sock = sock_connect(buf, "25", "tcp");
891                 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
892                 if (sock >= 0) lprintf(9, "Connected!\n");
893                 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
894                 if (sock >= 0) break;
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         int mes_type = 0;
1090
1091         lprintf(9, "smtp_do_bounce() called\n");
1092         strcpy(bounceto, "");
1093
1094         lines = num_tokens(instr, '\n');
1095
1096
1097         /* See if it's time to give up on delivery of this message */
1098         for (i=0; i<lines; ++i) {
1099                 extract_token(buf, instr, i, '\n');
1100                 extract(key, buf, 0);
1101                 extract(addr, buf, 1);
1102                 if (!strcasecmp(key, "submitted")) {
1103                         submitted = atol(addr);
1104                 }
1105         }
1106
1107         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1108                 give_up = 1;
1109         }
1110
1111
1112
1113         bmsg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
1114         if (bmsg == NULL) return;
1115         memset(bmsg, 0, sizeof(struct CtdlMessage));
1116
1117         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1118         bmsg->cm_anon_type = MES_NORMAL;
1119         bmsg->cm_format_type = 1;
1120         bmsg->cm_fields['A'] = strdoop("Citadel");
1121         bmsg->cm_fields['O'] = strdoop(MAILROOM);
1122         bmsg->cm_fields['N'] = strdoop(config.c_nodename);
1123
1124         if (give_up) bmsg->cm_fields['M'] = strdoop(
1125 "A message you sent could not be delivered to some or all of its recipients\n"
1126 "due to prolonged unavailability of its destination(s).\n"
1127 "Giving up on the following addresses:\n\n"
1128 );
1129
1130         else bmsg->cm_fields['M'] = strdoop(
1131 "A message you sent could not be delivered to some or all of its recipients.\n"
1132 "The following addresses were undeliverable:\n\n"
1133 );
1134
1135         /*
1136          * Now go through the instructions checking for stuff.
1137          */
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                 TRACE;
1192                 if (strlen(bounceto) == 0) {
1193                         lprintf(7, "No bounce address specified\n");
1194                         bounce_msgid = (-1L);
1195                 }
1196                 else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
1197                         lprintf(7, "Invalid bounce address <%s>\n", bounceto);
1198                         bounce_msgid = (-1L);
1199                 }
1200                 else {
1201                         bounce_msgid = CtdlSaveMsg(bmsg,
1202                                 bounceto,
1203                                 "", mes_type);
1204                 }
1205                 TRACE;
1206
1207                 /* Otherwise, go to the Aide> room */
1208                 lprintf(9, "bounce to room?\n");
1209                 if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
1210                         "", AIDEROOM,
1211                         MES_LOCAL);
1212         }
1213
1214         CtdlFreeMessage(bmsg);
1215         lprintf(9, "Done processing bounces\n");
1216 }
1217
1218
1219 /*
1220  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1221  * set of delivery instructions for completed deliveries and remove them.
1222  *
1223  * It returns the number of incomplete deliveries remaining.
1224  */
1225 int smtp_purge_completed_deliveries(char *instr) {
1226         int i;
1227         int lines;
1228         int status;
1229         char buf[1024];
1230         char key[1024];
1231         char addr[1024];
1232         char dsn[1024];
1233         int completed;
1234         int incomplete = 0;
1235
1236         lines = num_tokens(instr, '\n');
1237         for (i=0; i<lines; ++i) {
1238                 extract_token(buf, instr, i, '\n');
1239                 extract(key, buf, 0);
1240                 extract(addr, buf, 1);
1241                 status = extract_int(buf, 2);
1242                 extract(dsn, buf, 3);
1243
1244                 completed = 0;
1245
1246                 if (
1247                    (!strcasecmp(key, "local"))
1248                    || (!strcasecmp(key, "remote"))
1249                    || (!strcasecmp(key, "ignet"))
1250                    || (!strcasecmp(key, "room"))
1251                 ) {
1252                         if (status == 2) completed = 1;
1253                         else ++incomplete;
1254                 }
1255
1256                 if (completed) {
1257                         remove_token(instr, i, '\n');
1258                         --i;
1259                         --lines;
1260                 }
1261         }
1262
1263         return(incomplete);
1264 }
1265
1266
1267 /*
1268  * smtp_do_procmsg()
1269  *
1270  * Called by smtp_do_queue() to handle an individual message.
1271  */
1272 void smtp_do_procmsg(long msgnum, void *userdata) {
1273         struct CtdlMessage *msg;
1274         char *instr = NULL;
1275         char *results = NULL;
1276         int i;
1277         int lines;
1278         int status;
1279         char buf[1024];
1280         char key[1024];
1281         char addr[1024];
1282         char dsn[1024];
1283         long text_msgid = (-1);
1284         int incomplete_deliveries_remaining;
1285         time_t attempted = 0L;
1286         time_t last_attempted = 0L;
1287         time_t retry = SMTP_RETRY_INTERVAL;
1288
1289         lprintf(9, "smtp_do_procmsg(%ld)\n", msgnum);
1290
1291         msg = CtdlFetchMessage(msgnum);
1292         if (msg == NULL) {
1293                 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
1294                 return;
1295         }
1296
1297         instr = strdoop(msg->cm_fields['M']);
1298         CtdlFreeMessage(msg);
1299
1300         /* Strip out the headers amd any other non-instruction line */
1301         lines = num_tokens(instr, '\n');
1302         for (i=0; i<lines; ++i) {
1303                 extract_token(buf, instr, i, '\n');
1304                 if (num_tokens(buf, '|') < 2) {
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          * Postpone delivery if we've already tried recently.
1336          */
1337         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1338                 lprintf(7, "Retry time not yet reached.\n");
1339                 phree(instr);
1340                 return;
1341         }
1342
1343
1344         /*
1345          * Bail out if there's no actual message associated with this
1346          */
1347         if (text_msgid < 0L) {
1348                 lprintf(3, "SMTP: no 'msgid' directive found!\n");
1349                 phree(instr);
1350                 return;
1351         }
1352
1353         /* Plow through the instructions looking for 'remote' directives and
1354          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1355          * were experienced and it's time to try again)
1356          */
1357         lines = num_tokens(instr, '\n');
1358         for (i=0; i<lines; ++i) {
1359                 extract_token(buf, instr, i, '\n');
1360                 extract(key, buf, 0);
1361                 extract(addr, buf, 1);
1362                 status = extract_int(buf, 2);
1363                 extract(dsn, buf, 3);
1364                 if ( (!strcasecmp(key, "remote"))
1365                    && ((status==0)||(status==3)||(status==4)) ) {
1366                         remove_token(instr, i, '\n');
1367                         --i;
1368                         --lines;
1369                         lprintf(9, "SMTP: Trying <%s>\n", addr);
1370                         smtp_try(key, addr, &status, dsn, text_msgid);
1371                         if (status != 2) {
1372                                 if (results == NULL) {
1373                                         results = mallok(1024);
1374                                         memset(results, 0, 1024);
1375                                 }
1376                                 else {
1377                                         results = reallok(results,
1378                                                 strlen(results) + 1024);
1379                                 }
1380                                 sprintf(&results[strlen(results)],
1381                                         "%s|%s|%d|%s\n",
1382                                         key, addr, status, dsn);
1383                         }
1384                 }
1385         }
1386
1387         if (results != NULL) {
1388                 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
1389                 strcat(instr, results);
1390                 phree(results);
1391         }
1392
1393
1394         /* Generate 'bounce' messages */
1395         smtp_do_bounce(instr);
1396
1397         /* Go through the delivery list, deleting completed deliveries */
1398         incomplete_deliveries_remaining = 
1399                 smtp_purge_completed_deliveries(instr);
1400
1401
1402         /*
1403          * No delivery instructions remain, so delete both the instructions
1404          * message and the message message.
1405          */
1406         if (incomplete_deliveries_remaining <= 0) {
1407                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1408                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, "");
1409         }
1410
1411
1412         /*
1413          * Uncompleted delivery instructions remain, so delete the old
1414          * instructions and replace with the updated ones.
1415          */
1416         if (incomplete_deliveries_remaining > 0) {
1417                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, "");
1418                 msg = mallok(sizeof(struct CtdlMessage));
1419                 memset(msg, 0, sizeof(struct CtdlMessage));
1420                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1421                 msg->cm_anon_type = MES_NORMAL;
1422                 msg->cm_format_type = FMT_RFC822;
1423                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1424                 snprintf(msg->cm_fields['M'],
1425                         strlen(instr)+SIZ,
1426                         "Content-type: %s\n\n%s\n"
1427                         "attempted|%ld\n"
1428                         "retry|%ld\n",
1429                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1430                 phree(instr);
1431                 CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
1432                 CtdlFreeMessage(msg);
1433         }
1434
1435 }
1436
1437
1438
1439 /*
1440  * smtp_do_queue()
1441  * 
1442  * Run through the queue sending out messages.
1443  */
1444 void smtp_do_queue(void) {
1445         static int doing_queue = 0;
1446
1447         /*
1448          * This is a simple concurrency check to make sure only one queue run
1449          * is done at a time.  We could do this with a mutex, but since we
1450          * don't really require extremely fine granularity here, we'll do it
1451          * with a static variable instead.
1452          */
1453         if (doing_queue) return;
1454         doing_queue = 1;
1455
1456         /* 
1457          * Go ahead and run the queue
1458          */
1459         lprintf(7, "SMTP: processing outbound queue\n");
1460
1461         if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1462                 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1463                 return;
1464         }
1465         CtdlForEachMessage(MSGS_ALL, 0L, (-127),
1466                 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1467
1468         lprintf(7, "SMTP: queue run completed\n");
1469         run_queue_now = 0;
1470         doing_queue = 0;
1471 }
1472
1473
1474
1475 /*****************************************************************************/
1476 /*                          SMTP UTILITY COMMANDS                            */
1477 /*****************************************************************************/
1478
1479 void cmd_smtp(char *argbuf) {
1480         char cmd[SIZ];
1481         char node[SIZ];
1482         char buf[SIZ];
1483         int i;
1484         int num_mxhosts;
1485
1486         if (CtdlAccessCheck(ac_aide)) return;
1487
1488         extract(cmd, argbuf, 0);
1489
1490         if (!strcasecmp(cmd, "mx")) {
1491                 extract(node, argbuf, 1);
1492                 num_mxhosts = getmx(buf, node);
1493                 cprintf("%d %d MX hosts listed for %s\n",
1494                         LISTING_FOLLOWS, num_mxhosts, node);
1495                 for (i=0; i<num_mxhosts; ++i) {
1496                         extract(node, buf, i);
1497                         cprintf("%s\n", node);
1498                 }
1499                 cprintf("000\n");
1500                 return;
1501         }
1502
1503         else if (!strcasecmp(cmd, "runqueue")) {
1504                 run_queue_now = 1;
1505                 cprintf("%d All outbound SMTP will be retried now.\n", OK);
1506                 return;
1507         }
1508
1509         else {
1510                 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
1511         }
1512
1513 }
1514
1515
1516
1517
1518 /*****************************************************************************/
1519 /*                      MODULE INITIALIZATION STUFF                          */
1520 /*****************************************************************************/
1521
1522
1523 char *Dynamic_Module_Init(void)
1524 {
1525         SYM_SMTP = CtdlGetDynamicSymbol();
1526         SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1527
1528         CtdlRegisterServiceHook(config.c_smtp_port,     /* On the net... */
1529                                 NULL,
1530                                 smtp_greeting,
1531                                 smtp_command_loop);
1532
1533         CtdlRegisterServiceHook(0,                      /* ...and locally */
1534                                 "smtp.socket",
1535                                 smtp_greeting,
1536                                 smtp_command_loop);
1537
1538         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1);
1539         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1540         CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1541         return "$Id$";
1542 }