]> code.citadel.org Git - citadel.git/blob - citadel/serv_smtp.c
* serv_inetcfg: added
[citadel.git] / citadel / serv_smtp.c
1 /* $Id$ */
2
3 #include "sysdep.h"
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <stdio.h>
7 #include <fcntl.h>
8 #include <signal.h>
9 #include <pwd.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <sys/time.h>
13 #include <sys/wait.h>
14 #include <string.h>
15 #include <limits.h>
16 #include "citadel.h"
17 #include "server.h"
18 #include <time.h>
19 #include "sysdep_decls.h"
20 #include "citserver.h"
21 #include "support.h"
22 #include "config.h"
23 #include "dynloader.h"
24 #include "room_ops.h"
25 #include "user_ops.h"
26 #include "policy.h"
27 #include "database.h"
28 #include "msgbase.h"
29 #include "tools.h"
30 #include "internet_addressing.h"
31 #include "genstamp.h"
32 #include "domain.h"
33 #include "clientsocket.h"
34
35
36 struct citsmtp {                /* Information about the current session */
37         int command_state;
38         struct usersupp vrfy_buffer;
39         int vrfy_count;
40         char vrfy_match[256];
41         char from[256];
42         int number_of_recipients;
43         int delivery_mode;
44 };
45
46 enum {                          /* Command states for login authentication */
47         smtp_command,
48         smtp_user,
49         smtp_password
50 };
51
52 enum {                          /* Delivery modes */
53         smtp_deliver_local,
54         smtp_deliver_remote
55 };
56
57 #define SMTP            ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
58 #define SMTP_RECP       ((char *)CtdlGetUserData(SYM_SMTP_RECP))
59
60 long SYM_SMTP;
61 long SYM_SMTP_RECP;
62
63
64
65 /*****************************************************************************/
66 /*                      SMTP SERVER (INBOUND) STUFF                          */
67 /*****************************************************************************/
68
69
70
71
72 /*
73  * Here's where our SMTP session begins its happy day.
74  */
75 void smtp_greeting(void) {
76
77         strcpy(CC->cs_clientname, "SMTP session");
78         CC->internal_pgm = 1;
79         CC->cs_flags |= CS_STEALTH;
80         CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
81         CtdlAllocUserData(SYM_SMTP_RECP, 256);
82         sprintf(SMTP_RECP, "%s", "");
83
84         cprintf("220 Welcome to the Citadel/UX ESMTP server at %s\r\n",
85                 config.c_fqdn);
86 }
87
88
89 /*
90  * Implement HELO and EHLO commands.
91  */
92 void smtp_hello(char *argbuf, int is_esmtp) {
93
94         if (!is_esmtp) {
95                 cprintf("250 Greetings and joyous salutations.\r\n");
96         }
97         else {
98                 cprintf("250-Greetings and joyous salutations.\r\n");
99                 cprintf("250-HELP\r\n");
100                 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
101                 cprintf("250 AUTH=LOGIN\r\n");
102         }
103 }
104
105
106 /*
107  * Implement HELP command.
108  */
109 void smtp_help(void) {
110         cprintf("214-Here's the frequency, Kenneth:\r\n");
111         cprintf("214-    DATA\r\n");
112         cprintf("214-    EHLO\r\n");
113         cprintf("214-    EXPN\r\n");
114         cprintf("214-    HELO\r\n");
115         cprintf("214-    HELP\r\n");
116         cprintf("214-    MAIL\r\n");
117         cprintf("214-    NOOP\r\n");
118         cprintf("214-    QUIT\r\n");
119         cprintf("214-    RCPT\r\n");
120         cprintf("214-    RSET\r\n");
121         cprintf("214-    VRFY\r\n");
122         cprintf("214 I could tell you more, but then I'd have to kill you.\r\n");
123 }
124
125
126 /*
127  *
128  */
129 void smtp_get_user(char *argbuf) {
130         char buf[256];
131         char username[256];
132
133         decode_base64(username, argbuf);
134         lprintf(9, "Trying <%s>\n", username);
135         if (CtdlLoginExistingUser(username) == login_ok) {
136                 encode_base64(buf, "Password:");
137                 cprintf("334 %s\r\n", buf);
138                 SMTP->command_state = smtp_password;
139         }
140         else {
141                 cprintf("500 No such user.\r\n");
142                 SMTP->command_state = smtp_command;
143         }
144 }
145
146
147 /*
148  *
149  */
150 void smtp_get_pass(char *argbuf) {
151         char password[256];
152
153         decode_base64(password, argbuf);
154         lprintf(9, "Trying <%s>\n", password);
155         if (CtdlTryPassword(password) == pass_ok) {
156                 cprintf("235 Authentication successful.\r\n");
157                 lprintf(9, "SMTP authenticated login successful\n");
158                 CC->internal_pgm = 0;
159                 CC->cs_flags &= ~CS_STEALTH;
160         }
161         else {
162                 cprintf("500 Authentication failed.\r\n");
163         }
164         SMTP->command_state = smtp_command;
165 }
166
167
168 /*
169  *
170  */
171 void smtp_auth(char *argbuf) {
172         char buf[256];
173
174         if (strncasecmp(argbuf, "login", 5) ) {
175                 cprintf("550 We only support LOGIN authentication.\r\n");
176                 return;
177         }
178
179         if (strlen(argbuf) >= 7) {
180                 smtp_get_user(&argbuf[6]);
181         }
182
183         else {
184                 encode_base64(buf, "Username:");
185                 cprintf("334 %s\r\n", buf);
186                 SMTP->command_state = smtp_user;
187         }
188 }
189
190
191 /*
192  * Back end for smtp_vrfy() command
193  */
194 void smtp_vrfy_backend(struct usersupp *us, void *data) {
195
196         if (!fuzzy_match(us, SMTP->vrfy_match)) {
197                 ++SMTP->vrfy_count;
198                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
199         }
200 }
201
202
203 /* 
204  * Implements the VRFY (verify user name) command.
205  * Performs fuzzy match on full user names.
206  */
207 void smtp_vrfy(char *argbuf) {
208         SMTP->vrfy_count = 0;
209         strcpy(SMTP->vrfy_match, argbuf);
210         ForEachUser(smtp_vrfy_backend, NULL);
211
212         if (SMTP->vrfy_count < 1) {
213                 cprintf("550 String does not match anything.\r\n");
214         }
215         else if (SMTP->vrfy_count == 1) {
216                 cprintf("250 %s <cit%ld@%s>\r\n",
217                         SMTP->vrfy_buffer.fullname,
218                         SMTP->vrfy_buffer.usernum,
219                         config.c_fqdn);
220         }
221         else if (SMTP->vrfy_count > 1) {
222                 cprintf("553 Request ambiguous: %d users matched.\r\n",
223                         SMTP->vrfy_count);
224         }
225
226 }
227
228
229
230 /*
231  * Back end for smtp_expn() command
232  */
233 void smtp_expn_backend(struct usersupp *us, void *data) {
234
235         if (!fuzzy_match(us, SMTP->vrfy_match)) {
236
237                 if (SMTP->vrfy_count >= 1) {
238                         cprintf("250-%s <cit%ld@%s>\r\n",
239                                 SMTP->vrfy_buffer.fullname,
240                                 SMTP->vrfy_buffer.usernum,
241                                 config.c_fqdn);
242                 }
243
244                 ++SMTP->vrfy_count;
245                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
246         }
247 }
248
249
250 /* 
251  * Implements the EXPN (expand user name) command.
252  * Performs fuzzy match on full user names.
253  */
254 void smtp_expn(char *argbuf) {
255         SMTP->vrfy_count = 0;
256         strcpy(SMTP->vrfy_match, argbuf);
257         ForEachUser(smtp_expn_backend, NULL);
258
259         if (SMTP->vrfy_count < 1) {
260                 cprintf("550 String does not match anything.\r\n");
261         }
262         else if (SMTP->vrfy_count >= 1) {
263                 cprintf("250 %s <cit%ld@%s>\r\n",
264                         SMTP->vrfy_buffer.fullname,
265                         SMTP->vrfy_buffer.usernum,
266                         config.c_fqdn);
267         }
268 }
269
270
271 /*
272  * Implements the RSET (reset state) command.
273  * Currently this just zeroes out the state buffer.  If pointers to data
274  * allocated with mallok() are ever placed in the state buffer, we have to
275  * be sure to phree() them first!
276  */
277 void smtp_rset(void) {
278         memset(SMTP, 0, sizeof(struct citsmtp));
279         if (CC->logged_in) logout(CC);
280         cprintf("250 Zap!\r\n");
281 }
282
283
284
285 /*
286  * Implements the "MAIL From:" command
287  */
288 void smtp_mail(char *argbuf) {
289         char user[256];
290         char node[256];
291         int cvt;
292
293         if (strlen(SMTP->from) != 0) {
294                 cprintf("503 Only one sender permitted\r\n");
295                 return;
296         }
297
298         if (strncasecmp(argbuf, "From:", 5)) {
299                 cprintf("501 Syntax error\r\n");
300                 return;
301         }
302
303         strcpy(SMTP->from, &argbuf[5]);
304         striplt(SMTP->from);
305
306         if (strlen(SMTP->from) == 0) {
307                 cprintf("501 Empty sender name is not permitted\r\n");
308                 return;
309         }
310
311
312         /* If this SMTP connection is from a logged-in user, make sure that
313          * the user only sends email from his/her own address.
314          */
315         if (CC->logged_in) {
316                 cvt = convert_internet_address(user, node, SMTP->from);
317                 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
318                 if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
319                         cprintf("550 <%s> is not your address.\r\n", SMTP->from);
320                         strcpy(SMTP->from, "");
321                         return;
322                 }
323         }
324
325         /* Otherwise, make sure outsiders aren't trying to forge mail from
326          * this system.
327          */
328         else {
329                 cvt = convert_internet_address(user, node, SMTP->from);
330                 lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
331                 if (!strcasecmp(node, config.c_nodename)) { /* FIX use fcn */
332                         cprintf("550 You must log in to send mail from %s\r\n",
333                                 config.c_fqdn);
334                         strcpy(SMTP->from, "");
335                         return;
336                 }
337         }
338
339         cprintf("250 Sender ok.  Groovy.\r\n");
340 }
341
342
343
344 /*
345  * Implements the "RCPT To:" command
346  */
347 void smtp_rcpt(char *argbuf) {
348         int cvt;
349         char user[256];
350         char node[256];
351         char recp[256];
352         int is_spam = 0;        /* FIX implement anti-spamming */
353
354         if (strlen(SMTP->from) == 0) {
355                 cprintf("503 MAIL first, then RCPT.  Duh.\r\n");
356                 return;
357         }
358
359         if (strncasecmp(argbuf, "To:", 3)) {
360                 cprintf("501 Syntax error\r\n");
361                 return;
362         }
363
364         strcpy(recp, &argbuf[3]);
365         striplt(recp);
366         alias(recp);
367
368         cvt = convert_internet_address(user, node, recp);
369         sprintf(recp, "%s@%s", user, node);
370
371
372         switch(cvt) {
373                 case rfc822_address_locally_validated:
374                         cprintf("250 %s is a valid recipient.\r\n", user);
375                         ++SMTP->number_of_recipients;
376                         CtdlReallocUserData(SYM_SMTP_RECP,
377                                 strlen(SMTP_RECP) + 1024 );
378                         strcat(SMTP_RECP, "local|");
379                         strcat(SMTP_RECP, user);
380                         strcat(SMTP_RECP, "|0\n");
381                         return;
382
383                 case rfc822_room_delivery:
384                         cprintf("250 Delivering to room '%s'\r\n", user);
385                         ++SMTP->number_of_recipients;
386                         CtdlReallocUserData(SYM_SMTP_RECP,
387                                 strlen(SMTP_RECP) + 1024 );
388                         strcat(SMTP_RECP, "room|");
389                         strcat(SMTP_RECP, user);
390                         strcat(SMTP_RECP, "|0|\n");
391                         return;
392
393                 case rfc822_no_such_user:
394                         cprintf("550 %s: no such user\r\n", recp);
395                         return;
396
397                 case rfc822_address_invalid:
398                         if (is_spam) {
399                                 cprintf("551 Away with thee, spammer!\r\n");
400                         }
401                         else {
402                                 cprintf("250 Remote recipient %s ok\r\n", recp);
403                                 ++SMTP->number_of_recipients;
404                                 CtdlReallocUserData(SYM_SMTP_RECP,
405                                         strlen(SMTP_RECP) + 1024 );
406                                 strcat(SMTP_RECP, "remote|");
407                                 strcat(SMTP_RECP, recp);
408                                 strcat(SMTP_RECP, "|0|\n");
409                                 return;
410                         }
411                         return;
412         }
413
414         cprintf("599 Unknown error\r\n");
415 }
416
417
418
419
420
421 /*
422  * Back end for smtp_data()  ... this does the actual delivery of the message
423  * Returns 0 on success, nonzero on failure
424  */
425 int smtp_message_delivery(struct CtdlMessage *msg) {
426         char user[1024];
427         char node[1024];
428         char name[1024];
429         char buf[1024];
430         char dtype[1024];
431         char room[1024];
432         int successful_saves = 0;       /* number of successful local saves */
433         int failed_saves = 0;           /* number of failed deliveries */
434         int remote_spools = 0;          /* number of copies to send out */
435         long msgid = (-1L);
436         int i;
437         struct usersupp userbuf;
438         char *instr;                    /* Remote delivery instructions */
439         struct CtdlMessage *imsg;
440
441         lprintf(9, "smtp_message_delivery() called\n");
442
443         /* Fill in 'from' fields with envelope information if missing */
444         process_rfc822_addr(SMTP->from, user, node, name);
445         if (msg->cm_fields['A']==NULL) msg->cm_fields['A'] = strdoop(user);
446         if (msg->cm_fields['N']==NULL) msg->cm_fields['N'] = strdoop(node);
447         if (msg->cm_fields['H']==NULL) msg->cm_fields['H'] = strdoop(name);
448
449         /* Save the message in the queue */
450         msgid = CtdlSaveMsg(msg,
451                 "",
452                 SMTP_SPOOLOUT_ROOM,
453                 MES_LOCAL,
454                 1);
455         ++successful_saves;
456
457         instr = mallok(1024);
458         sprintf(instr, "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n",
459                 SPOOLMIME, msgid, time(NULL) );
460
461         for (i=0; i<SMTP->number_of_recipients; ++i) {
462                 extract_token(buf, SMTP_RECP, i, '\n');
463                 extract(dtype, buf, 0);
464
465                 /* Stuff local mailboxes */
466                 if (!strcasecmp(dtype, "local")) {
467                         extract(user, buf, 1);
468                         if (getuser(&userbuf, user) == 0) {
469                                 MailboxName(room, &userbuf, MAILROOM);
470                                 CtdlSaveMsgPointerInRoom(room, msgid, 0);
471                                 ++successful_saves;
472                         }
473                         else {
474                                 ++failed_saves;
475                         }
476                 }
477
478                 /* Delivery to local non-mailbox rooms */
479                 if (!strcasecmp(dtype, "room")) {
480                         extract(room, buf, 1);
481                         CtdlSaveMsgPointerInRoom(room, msgid, 0);
482                         ++successful_saves;
483                 }
484
485                 /* Remote delivery */
486                 if (!strcasecmp(dtype, "remote")) {
487                         extract(user, buf, 1);
488                         instr = reallok(instr, strlen(instr) + 1024);
489                         sprintf(&instr[strlen(instr)],
490                                 "remote|%s|0\n",
491                                 user);
492                         ++remote_spools;
493                 }
494
495         }
496
497         /* If there are remote spools to be done, save the instructions */
498         if (remote_spools > 0) {
499                 imsg = mallok(sizeof(struct CtdlMessage));
500                 memset(imsg, 0, sizeof(struct CtdlMessage));
501                 imsg->cm_magic = CTDLMESSAGE_MAGIC;
502                 imsg->cm_anon_type = MES_NORMAL;
503                 imsg->cm_format_type = FMT_RFC822;
504                 imsg->cm_fields['M'] = instr;
505                 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
506                 CtdlFreeMessage(imsg);
507         }
508
509         /* If there are no remote spools, delete the message */ 
510         else {
511                 phree(instr);   /* only needed here, because CtdlSaveMsg()
512                                  * would free this buffer otherwise */
513                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, NULL); 
514         }
515
516         return(failed_saves);
517 }
518
519
520
521 /*
522  * Implements the DATA command
523  */
524 void smtp_data(void) {
525         char *body;
526         struct CtdlMessage *msg;
527         int retval;
528         char nowstamp[256];
529
530         if (strlen(SMTP->from) == 0) {
531                 cprintf("503 Need MAIL command first.\r\n");
532                 return;
533         }
534
535         if (SMTP->number_of_recipients < 1) {
536                 cprintf("503 Need RCPT command first.\r\n");
537                 return;
538         }
539
540         cprintf("354 Transmit message now; terminate with '.' by itself\r\n");
541         
542         generate_rfc822_datestamp(nowstamp, time(NULL));
543         body = mallok(4096);
544         if (body != NULL) sprintf(body,
545                 "Received: from %s\n"
546                 "       by %s;\n"
547                 "       %s\n",
548                         "FIX.FIX.com",
549                         config.c_fqdn,
550                         nowstamp);
551         
552         body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
553         if (body == NULL) {
554                 cprintf("550 Unable to save message text: internal error.\r\n");
555                 return;
556         }
557
558         lprintf(9, "Converting message...\n");
559         msg = convert_internet_message(body);
560
561         /* If the user is locally authenticated, FORCE the From: header to
562          * show up as the real sender
563          */
564         if (CC->logged_in) {
565                 if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
566                 if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
567                 if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
568                 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
569                 msg->cm_fields['N'] = strdoop(config.c_nodename);
570                 msg->cm_fields['H'] = strdoop(config.c_humannode);
571         }
572
573         retval = smtp_message_delivery(msg);
574         CtdlFreeMessage(msg);
575
576         if (!retval) {
577                 cprintf("250 Message accepted for delivery.\r\n");
578         }
579         else {
580                 cprintf("550 Internal delivery errors: %d\r\n", retval);
581         }
582 }
583
584
585
586
587 /* 
588  * Main command loop for SMTP sessions.
589  */
590 void smtp_command_loop(void) {
591         char cmdbuf[256];
592
593         time(&CC->lastcmd);
594         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
595         if (client_gets(cmdbuf) < 1) {
596                 lprintf(3, "SMTP socket is broken.  Ending session.\n");
597                 CC->kill_me = 1;
598                 return;
599         }
600         lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
601         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
602
603         if (SMTP->command_state == smtp_user) {
604                 smtp_get_user(cmdbuf);
605         }
606
607         else if (SMTP->command_state == smtp_password) {
608                 smtp_get_pass(cmdbuf);
609         }
610
611         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
612                 smtp_auth(&cmdbuf[5]);
613         }
614
615         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
616                 smtp_data();
617         }
618
619         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
620                 smtp_hello(&cmdbuf[5], 1);
621         }
622
623         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
624                 smtp_expn(&cmdbuf[5]);
625         }
626
627         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
628                 smtp_hello(&cmdbuf[5], 0);
629         }
630
631         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
632                 smtp_help();
633         }
634
635         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
636                 smtp_mail(&cmdbuf[5]);
637         }
638
639         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
640                 cprintf("250 This command successfully did nothing.\r\n");
641         }
642
643         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
644                 cprintf("221 Goodbye...\r\n");
645                 CC->kill_me = 1;
646                 return;
647                 }
648
649         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
650                 smtp_rcpt(&cmdbuf[5]);
651         }
652
653         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
654                 smtp_rset();
655         }
656
657         else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
658                 smtp_vrfy(&cmdbuf[5]);
659         }
660
661         else {
662                 cprintf("502 I'm sorry Dave, I'm afraid I can't do that.\r\n");
663         }
664
665 }
666
667
668
669
670 /*****************************************************************************/
671 /*               SMTP CLIENT (OUTBOUND PROCESSING) STUFF                     */
672 /*****************************************************************************/
673
674
675
676 /*
677  * smtp_try()
678  *
679  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
680  *
681  */
682 void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
683 {
684         char buf[256];
685         int sock = (-1);
686         char mxhosts[1024];
687         int num_mxhosts;
688         int mx;
689         char user[256], node[256], name[256];
690
691         /* Parse out the host portion of the recipient address */
692         process_rfc822_addr(addr, user, node, name);
693         lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
694                 user, node, name);
695
696         num_mxhosts = getmx(mxhosts, node);
697         lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
698         if (num_mxhosts < 1) {
699                 *status = 5;
700                 sprintf(dsn, "No MX hosts found for <%s>", node);
701                 return;
702         }
703
704         for (mx=0; mx<num_mxhosts; ++mx) {
705                 extract(buf, mxhosts, mx);
706                 lprintf(9, "Trying <%s>\n", buf);
707                 sock = sock_connect(buf, "25", "tcp");
708                 sprintf(dsn, "Could not connect: %s", strerror(errno));
709                 if (sock >= 0) lprintf(9, "Connected!\n");
710                 if (sock < 0) sprintf(dsn, "%s", strerror(errno));
711                 if (sock >= 0) break;
712         }
713
714         if (sock < 0) {
715                 *status = 4;    /* dsn is already filled in */
716                 return;
717         }
718
719         /* Process the SMTP greeting from the server */
720         if (sock_gets(sock, buf) < 0) {
721                 *status = 4;
722                 strcpy(dsn, "Connection broken during SMTP conversation");
723                 sock_close(sock);
724                 return;
725         }
726         lprintf(9, "%s\n", buf);
727         if (buf[0] != '2') {
728                 if (buf[0] == '4') {
729                         *status = 4;
730                         strcpy(dsn, &buf[4]);
731                         sock_close(sock);
732                         return;
733                 }
734                 else {
735                         *status = 5;
736                         strcpy(dsn, &buf[4]);
737                         sock_close(sock);
738                         return;
739                 }
740         }
741
742         /* At this point we know we are talking to a real SMTP server */
743
744         /* Do a HELO command */
745         sprintf(buf, "HELO %s", config.c_fqdn);
746         sock_puts(sock, buf);
747         if (sock_gets(sock, buf) < 0) {
748                 *status = 4;
749                 strcpy(dsn, "Connection broken during SMTP conversation");
750                 sock_close(sock);
751                 return;
752         }
753         lprintf(9, "%s\n", buf);
754         if (buf[0] != '2') {
755                 if (buf[0] == '4') {
756                         *status = 4;
757                         strcpy(dsn, &buf[4]);
758                         sock_close(sock);
759                         return;
760                 }
761                 else {
762                         *status = 5;
763                         strcpy(dsn, &buf[4]);
764                         sock_close(sock);
765                         return;
766                 }
767         }
768
769
770         /* HELO succeeded, now try the MAIL From: command */
771         sprintf(buf, "MAIL From: FIX@uncnsrd.mt-kisco.ny.us"); /* FIX */
772         sock_puts(sock, buf);
773         if (sock_gets(sock, buf) < 0) {
774                 *status = 4;
775                 strcpy(dsn, "Connection broken during SMTP conversation");
776                 sock_close(sock);
777                 return;
778         }
779         lprintf(9, "%s\n", buf);
780         if (buf[0] != '2') {
781                 if (buf[0] == '4') {
782                         *status = 4;
783                         strcpy(dsn, &buf[4]);
784                         sock_close(sock);
785                         return;
786                 }
787                 else {
788                         *status = 5;
789                         strcpy(dsn, &buf[4]);
790                         sock_close(sock);
791                         return;
792                 }
793         }
794
795
796         /* MAIL succeeded, now try the RCPT To: command */
797         sprintf(buf, "RCPT To: %s", addr);
798         sock_puts(sock, buf);
799         if (sock_gets(sock, buf) < 0) {
800                 *status = 4;
801                 strcpy(dsn, "Connection broken during SMTP conversation");
802                 sock_close(sock);
803                 return;
804         }
805         lprintf(9, "%s\n", buf);
806         if (buf[0] != '2') {
807                 if (buf[0] == '4') {
808                         *status = 4;
809                         strcpy(dsn, &buf[4]);
810                         sock_close(sock);
811                         return;
812                 }
813                 else {
814                         *status = 5;
815                         strcpy(dsn, &buf[4]);
816                         sock_close(sock);
817                         return;
818                 }
819         }
820
821
822         /* RCPT succeeded, now try the DATA command */
823         sock_puts(sock, "DATA");
824         if (sock_gets(sock, buf) < 0) {
825                 *status = 4;
826                 strcpy(dsn, "Connection broken during SMTP conversation");
827                 sock_close(sock);
828                 return;
829         }
830         lprintf(9, "%s\n", buf);
831         if (buf[0] != '3') {
832                 if (buf[0] == '4') {
833                         *status = 3;
834                         strcpy(dsn, &buf[4]);
835                         sock_close(sock);
836                         return;
837                 }
838                 else {
839                         *status = 5;
840                         strcpy(dsn, &buf[4]);
841                         sock_close(sock);
842                         return;
843                 }
844         }
845
846         /* If we reach this point, the server is expecting data */
847         CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, NULL, sock, 1);
848         sock_puts(sock, ".");
849         if (sock_gets(sock, buf) < 0) {
850                 *status = 4;
851                 strcpy(dsn, "Connection broken during SMTP conversation");
852                 sock_close(sock);
853                 return;
854         }
855         lprintf(9, "%s\n", buf);
856         if (buf[0] != '2') {
857                 if (buf[0] == '4') {
858                         *status = 4;
859                         strcpy(dsn, &buf[4]);
860                         sock_close(sock);
861                         return;
862                 }
863                 else {
864                         *status = 5;
865                         strcpy(dsn, &buf[4]);
866                         sock_close(sock);
867                         return;
868                 }
869         }
870
871         /* We did it! */
872         strcpy(dsn, &buf[4]);
873         *status = 2;
874
875         sock_puts(sock, "QUIT");
876         sock_gets(sock, buf);
877         lprintf(9, "%s\n", buf);
878         sock_close(sock);
879 }
880
881
882
883
884 /*
885  * smtp_do_procmsg()
886  *
887  * Called by smtp_do_queue() to handle an individual message.
888  */
889 void smtp_do_procmsg(long msgnum) {
890         struct CtdlMessage *msg;
891         char *instr = NULL;
892         char *results = NULL;
893         int i;
894         int lines;
895         int status;
896         char buf[1024];
897         char key[1024];
898         char addr[1024];
899         char dsn[1024];
900         long text_msgid = (-1);
901
902         msg = CtdlFetchMessage(msgnum);
903         if (msg == NULL) {
904                 lprintf(3, "SMTP: tried %ld but no such message!\n", msgnum);
905                 return;
906         }
907
908         instr = strdoop(msg->cm_fields['M']);
909         CtdlFreeMessage(msg);
910
911         /* Strip out the headers amd any other non-instruction line */
912         lines = num_tokens(instr, '\n');
913         for (i=0; i<lines; ++i) {
914                 extract_token(buf, instr, i, '\n');
915                 if (num_tokens(buf, '|') < 2) {
916                         lprintf(9, "removing <%s>\n", buf);
917                         remove_token(instr, i, '\n');
918                         --lines;
919                         --i;
920                 }
921         }
922
923         /* Learn the message ID */
924         lines = num_tokens(instr, '\n');
925         for (i=0; i<lines; ++i) {
926                 extract_token(buf, instr, i, '\n');
927                 extract(key, buf, 0);
928                 if (!strcasecmp(key, "msgid")) {
929                         text_msgid = extract_long(buf, 1);
930                 }
931         }
932
933         if (text_msgid < 0L) {
934                 lprintf(3, "SMTP: no 'msgid' directive found!\n");
935                 phree(instr);
936                 return;
937         }
938
939         /* Plow through the instructions looking for 'remote' directives and
940          * a status of 0 (no delivery yet attempted) or 3 (transient errors
941          * were experienced and it's time to try again)
942          */
943         lines = num_tokens(instr, '\n');
944         for (i=0; i<lines; ++i) {
945                 extract_token(buf, instr, i, '\n');
946                 extract(key, buf, 0);
947                 extract(addr, buf, 1);
948                 status = extract_int(buf, 2);
949                 extract(dsn, buf, 3);
950                 if ( (!strcasecmp(key, "remote"))
951                    && ((status==0)||(status==3)) ) {
952                         remove_token(instr, i, '\n');
953                         --i;
954                         --lines;
955                         lprintf(9, "SMTP: Trying <%s>\n", addr);
956                         smtp_try(key, addr, &status, dsn, text_msgid);
957                         if (status != 2) {
958                                 if (results == NULL) {
959                                         results = mallok(1024);
960                                         memset(results, 0, 1024);
961                                 }
962                                 else {
963                                         results = reallok(results,
964                                                 strlen(results) + 1024);
965                                 }
966                                 sprintf(&results[strlen(results)],
967                                         "%s|%s|%d|%s\n",
968                                         key, addr, status, dsn);
969                         }
970                 }
971         }
972
973         if (results != NULL) {
974                 instr = reallok(instr, strlen(instr) + strlen(results) + 2);
975                 strcat(instr, results);
976                 phree(results);
977         }
978
979         /* Delete the instructions and replace with the updated ones */
980         CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);    
981         msg = mallok(sizeof(struct CtdlMessage));
982         memset(msg, 0, sizeof(struct CtdlMessage));
983         msg->cm_magic = CTDLMESSAGE_MAGIC;
984         msg->cm_anon_type = MES_NORMAL;
985         msg->cm_format_type = FMT_RFC822;
986         msg->cm_fields['M'] = malloc(strlen(instr)+256);
987         sprintf(msg->cm_fields['M'],
988                 "Content-type: %s\n\n%s\n", SPOOLMIME, instr);
989         phree(instr);
990         CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
991         CtdlFreeMessage(msg);
992 }
993
994
995
996 /*
997  * smtp_do_queue()
998  * 
999  * Run through the queue sending out messages.
1000  */
1001 void smtp_do_queue(void) {
1002         lprintf(5, "SMTP: processing outbound queue\n");
1003
1004         if (getroom(&CC->quickroom, SMTP_SPOOLOUT_ROOM) != 0) {
1005                 lprintf(3, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1006                 return;
1007         }
1008         CtdlForEachMessage(MSGS_ALL, 0L, SPOOLMIME, NULL, smtp_do_procmsg);
1009
1010         lprintf(5, "SMTP: queue run completed\n");
1011 }
1012
1013
1014 /**** FIX  temporary hack to run the queue *****/
1015 void cmd_qqqq(char *argbuf) {
1016         smtp_do_queue();
1017         cprintf("%d ok\n", OK);
1018 }
1019
1020
1021
1022 /*****************************************************************************/
1023 /*                      MODULE INITIALIZATION STUFF                          */
1024 /*****************************************************************************/
1025
1026
1027 char *Dynamic_Module_Init(void)
1028 {
1029         SYM_SMTP = CtdlGetDynamicSymbol();
1030         SYM_SMTP_RECP = CtdlGetDynamicSymbol();
1031         CtdlRegisterServiceHook(SMTP_PORT,
1032                                 smtp_greeting,
1033                                 smtp_command_loop);
1034
1035         /****  FIX ... temporary hack to run the queue ******/
1036         CtdlRegisterProtoHook(cmd_qqqq, "QQQQ", "run the queue");  
1037
1038         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
1039         return "$Id$";
1040 }
1041