Removed the logging facility from citserver, use syslog instead
[citadel.git] / citadel / modules / smtp / smtp_util.c
1 /*
2  * This module is an SMTP and ESMTP implementation for the Citadel system.
3  * It is compliant with all of the following:
4  *
5  * RFC  821 - Simple Mail Transfer Protocol
6  * RFC  876 - Survey of SMTP Implementations
7  * RFC 1047 - Duplicate messages and SMTP
8  * RFC 1652 - 8 bit MIME
9  * RFC 1869 - Extended Simple Mail Transfer Protocol
10  * RFC 1870 - SMTP Service Extension for Message Size Declaration
11  * RFC 2033 - Local Mail Transfer Protocol
12  * RFC 2197 - SMTP Service Extension for Command Pipelining
13  * RFC 2476 - Message Submission
14  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
15  * RFC 2554 - SMTP Service Extension for Authentication
16  * RFC 2821 - Simple Mail Transfer Protocol
17  * RFC 2822 - Internet Message Format
18  * RFC 2920 - SMTP Service Extension for Command Pipelining
19  *  
20  * The VRFY and EXPN commands have been removed from this implementation
21  * because nobody uses these commands anymore, except for spammers.
22  *
23  * Copyright (c) 1998-2009 by the citadel.org team
24  *
25  *  This program is free software; you can redistribute it and/or modify
26  *  it under the terms of the GNU General Public License as published by
27  *  the Free Software Foundation; either version 3 of the License, or
28  *  (at your option) any later version.
29  *
30  *  This program is distributed in the hope that it will be useful,
31  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
32  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  *  GNU General Public License for more details.
34  *
35  *  You should have received a copy of the GNU General Public License
36  *  along with this program; if not, write to the Free Software
37  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
38  */
39
40 #include "sysdep.h"
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <stdio.h>
44 #include <termios.h>
45 #include <fcntl.h>
46 #include <signal.h>
47 #include <pwd.h>
48 #include <errno.h>
49 #include <sys/types.h>
50 #include <syslog.h>
51
52 #if TIME_WITH_SYS_TIME
53 # include <sys/time.h>
54 # include <time.h>
55 #else
56 # if HAVE_SYS_TIME_H
57 #  include <sys/time.h>
58 # else
59 #  include <time.h>
60 # endif
61 #endif
62
63 #include <sys/wait.h>
64 #include <ctype.h>
65 #include <string.h>
66 #include <limits.h>
67 #include <sys/socket.h>
68 #include <netinet/in.h>
69 #include <arpa/inet.h>
70 #include <libcitadel.h>
71 #include "citadel.h"
72 #include "server.h"
73 #include "citserver.h"
74 #include "support.h"
75 #include "config.h"
76 #include "control.h"
77 #include "user_ops.h"
78 #include "database.h"
79 #include "msgbase.h"
80 #include "internet_addressing.h"
81 #include "genstamp.h"
82 #include "domain.h"
83 #include "clientsocket.h"
84 #include "locate_host.h"
85 #include "citadel_dirs.h"
86
87 #include "ctdl_module.h"
88
89 #include "smtp_util.h"
90
91 const char *smtp_get_Recipients(void)
92 {
93         citsmtp *sSMTP = SMTP;
94
95         if (sSMTP == NULL)
96                 return NULL;
97         else return sSMTP->from;
98 }
99
100
101 /*
102  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
103  * instructions for "5" codes (permanent fatal errors) and produce/deliver
104  * a "bounce" message (delivery status notification).
105  */
106 void smtp_do_bounce(char *instr, StrBuf *OMsgTxt) 
107 {
108         int i;
109         int lines;
110         int status;
111         char buf[1024];
112         char key[1024];
113         char addr[1024];
114         char dsn[1024];
115         char bounceto[1024];
116         StrBuf *boundary;
117         int num_bounces = 0;
118         int bounce_this = 0;
119         long bounce_msgid = (-1);
120         time_t submitted = 0L;
121         struct CtdlMessage *bmsg = NULL;
122         int give_up = 0;
123         struct recptypes *valid;
124         int successful_bounce = 0;
125         static int seq = 0;
126         StrBuf *BounceMB;
127         long omsgid = (-1);
128
129         syslog(LOG_DEBUG, "smtp_do_bounce() called\n");
130         strcpy(bounceto, "");
131         boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
132         StrBufAppendPrintf(boundary, "%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
133         lines = num_tokens(instr, '\n');
134
135         /* See if it's time to give up on delivery of this message */
136         for (i=0; i<lines; ++i) {
137                 extract_token(buf, instr, i, '\n', sizeof buf);
138                 extract_token(key, buf, 0, '|', sizeof key);
139                 extract_token(addr, buf, 1, '|', sizeof addr);
140                 if (!strcasecmp(key, "submitted")) {
141                         submitted = atol(addr);
142                 }
143         }
144
145         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
146                 give_up = 1;
147         }
148
149         /* Start building our bounce message */
150
151         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
152         if (bmsg == NULL) return;
153         memset(bmsg, 0, sizeof(struct CtdlMessage));
154         BounceMB = NewStrBufPlain(NULL, 1024);
155
156         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
157         bmsg->cm_anon_type = MES_NORMAL;
158         bmsg->cm_format_type = FMT_RFC822;
159         bmsg->cm_fields['A'] = strdup("Citadel");
160         bmsg->cm_fields['O'] = strdup(MAILROOM);
161         bmsg->cm_fields['N'] = strdup(config.c_nodename);
162         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
163         StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
164         StrBufAppendBuf(BounceMB, boundary, 0);
165         StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
166         StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
167         StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
168         StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
169         StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
170         StrBufAppendBuf(BounceMB, boundary, 0);
171         StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
172         StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);
173
174         if (give_up) StrBufAppendBufPlain(BounceMB, HKEY(
175 "A message you sent could not be delivered to some or all of its recipients\n"
176 "due to prolonged unavailability of its destination(s).\n"
177 "Giving up on the following addresses:\n\n"
178                                                   ), 0);
179
180         else StrBufAppendBufPlain(BounceMB, HKEY(
181 "A message you sent could not be delivered to some or all of its recipients.\n"
182 "The following addresses were undeliverable:\n\n"
183                                           ), 0);
184
185         /*
186          * Now go through the instructions checking for stuff.
187          */
188         for (i=0; i<lines; ++i) {
189                 long addrlen;
190                 long dsnlen;
191                 extract_token(buf, instr, i, '\n', sizeof buf);
192                 extract_token(key, buf, 0, '|', sizeof key);
193                 addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
194                 status = extract_int(buf, 2);
195                 dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
196                 bounce_this = 0;
197
198                 syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
199                        key, addr, status, dsn);
200
201                 if (!strcasecmp(key, "bounceto")) {
202                         strcpy(bounceto, addr);
203                 }
204
205                 if (!strcasecmp(key, "msgid")) {
206                         omsgid = atol(addr);
207                 }
208
209                 if (!strcasecmp(key, "remote")) {
210                         if (status == 5) bounce_this = 1;
211                         if (give_up) bounce_this = 1;
212                 }
213
214                 if (bounce_this) {
215                         ++num_bounces;
216
217                         StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
218                         StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
219                         StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
220                         StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
221
222                         remove_token(instr, i, '\n');
223                         --i;
224                         --lines;
225                 }
226         }
227
228         /* Attach the original message */
229         if (omsgid >= 0) {
230                 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
231                 StrBufAppendBuf(BounceMB, boundary, 0);
232                 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
233                 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0);
234                 StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
235                 StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0);
236                 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
237         
238                 if (OMsgTxt == NULL) {
239                         CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
240                         CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
241                         StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
242                         FreeStrBuf(&CC->redirect_buffer);
243                 }
244                 else {
245                         StrBufAppendBuf(BounceMB, OMsgTxt, 0);
246                 }
247         }
248
249         /* Close the multipart MIME scope */
250         StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
251         StrBufAppendBuf(BounceMB, boundary, 0);
252         StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
253         if (bmsg->cm_fields['A'] != NULL)
254                 free(bmsg->cm_fields['A']);
255         bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
256         /* Deliver the bounce if there's anything worth mentioning */
257         syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
258         if (num_bounces > 0) {
259
260                 /* First try the user who sent the message */
261                 syslog(LOG_DEBUG, "bounce to user? <%s>\n", bounceto);
262                 if (IsEmptyStr(bounceto)) {
263                         syslog(LOG_ERR, "No bounce address specified\n");
264                         bounce_msgid = (-1L);
265                 }
266
267                 /* Can we deliver the bounce to the original sender? */
268                 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
269                 if (valid != NULL) {
270                         if (valid->num_error == 0) {
271                                 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
272                                 successful_bounce = 1;
273                         }
274                 }
275
276                 /* If not, post it in the Aide> room */
277                 if (successful_bounce == 0) {
278                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
279                 }
280
281                 /* Free up the memory we used */
282                 if (valid != NULL) {
283                         free_recipients(valid);
284                 }
285         }
286         FreeStrBuf(&boundary);
287         CtdlFreeMessage(bmsg);
288         syslog(LOG_DEBUG, "Done processing bounces\n");
289 }