fix warnings in libev related codebase
[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         time_t submitted = 0L;
120         struct CtdlMessage *bmsg = NULL;
121         int give_up = 0;
122         struct recptypes *valid;
123         int successful_bounce = 0;
124         static int seq = 0;
125         StrBuf *BounceMB;
126         long omsgid = (-1);
127
128         syslog(LOG_DEBUG, "smtp_do_bounce() called\n");
129         strcpy(bounceto, "");
130         boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
131         StrBufAppendPrintf(boundary, "%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
132         lines = num_tokens(instr, '\n');
133
134         /* See if it's time to give up on delivery of this message */
135         for (i=0; i<lines; ++i) {
136                 extract_token(buf, instr, i, '\n', sizeof buf);
137                 extract_token(key, buf, 0, '|', sizeof key);
138                 extract_token(addr, buf, 1, '|', sizeof addr);
139                 if (!strcasecmp(key, "submitted")) {
140                         submitted = atol(addr);
141                 }
142         }
143
144         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
145                 give_up = 1;
146         }
147
148         /* Start building our bounce message */
149
150         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
151         if (bmsg == NULL) return;
152         memset(bmsg, 0, sizeof(struct CtdlMessage));
153         BounceMB = NewStrBufPlain(NULL, 1024);
154
155         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
156         bmsg->cm_anon_type = MES_NORMAL;
157         bmsg->cm_format_type = FMT_RFC822;
158         bmsg->cm_fields['A'] = strdup("Citadel");
159         bmsg->cm_fields['O'] = strdup(MAILROOM);
160         bmsg->cm_fields['N'] = strdup(config.c_nodename);
161         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
162         StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
163         StrBufAppendBuf(BounceMB, boundary, 0);
164         StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
165         StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
166         StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
167         StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
168         StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
169         StrBufAppendBuf(BounceMB, boundary, 0);
170         StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
171         StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);
172
173         if (give_up) StrBufAppendBufPlain(BounceMB, HKEY(
174 "A message you sent could not be delivered to some or all of its recipients\n"
175 "due to prolonged unavailability of its destination(s).\n"
176 "Giving up on the following addresses:\n\n"
177                                                   ), 0);
178
179         else StrBufAppendBufPlain(BounceMB, HKEY(
180 "A message you sent could not be delivered to some or all of its recipients.\n"
181 "The following addresses were undeliverable:\n\n"
182                                           ), 0);
183
184         /*
185          * Now go through the instructions checking for stuff.
186          */
187         for (i=0; i<lines; ++i) {
188                 long addrlen;
189                 long dsnlen;
190                 extract_token(buf, instr, i, '\n', sizeof buf);
191                 extract_token(key, buf, 0, '|', sizeof key);
192                 addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
193                 status = extract_int(buf, 2);
194                 dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
195                 bounce_this = 0;
196
197                 syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
198                        key, addr, status, dsn);
199
200                 if (!strcasecmp(key, "bounceto")) {
201                         strcpy(bounceto, addr);
202                 }
203
204                 if (!strcasecmp(key, "msgid")) {
205                         omsgid = atol(addr);
206                 }
207
208                 if (!strcasecmp(key, "remote")) {
209                         if (status == 5) bounce_this = 1;
210                         if (give_up) bounce_this = 1;
211                 }
212
213                 if (bounce_this) {
214                         ++num_bounces;
215
216                         StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
217                         StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
218                         StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
219                         StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
220
221                         remove_token(instr, i, '\n');
222                         --i;
223                         --lines;
224                 }
225         }
226
227         /* Attach the original message */
228         if (omsgid >= 0) {
229                 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
230                 StrBufAppendBuf(BounceMB, boundary, 0);
231                 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
232                 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0);
233                 StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
234                 StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0);
235                 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
236         
237                 if (OMsgTxt == NULL) {
238                         CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
239                         CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
240                         StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
241                         FreeStrBuf(&CC->redirect_buffer);
242                 }
243                 else {
244                         StrBufAppendBuf(BounceMB, OMsgTxt, 0);
245                 }
246         }
247
248         /* Close the multipart MIME scope */
249         StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
250         StrBufAppendBuf(BounceMB, boundary, 0);
251         StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
252         if (bmsg->cm_fields['A'] != NULL)
253                 free(bmsg->cm_fields['A']);
254         bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
255         /* Deliver the bounce if there's anything worth mentioning */
256         syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
257         if (num_bounces > 0) {
258
259                 /* First try the user who sent the message */
260                 if (IsEmptyStr(bounceto)) 
261                         syslog(LOG_ERR, "No bounce address specified\n");
262                 else
263                         syslog(LOG_DEBUG, "bounce to user <%s>\n", bounceto);
264                 /* Can we deliver the bounce to the original sender? */
265                 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
266                 if (valid != NULL) {
267                         if (valid->num_error == 0) {
268                                 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
269                                 successful_bounce = 1;
270                         }
271                 }
272
273                 /* If not, post it in the Aide> room */
274                 if (successful_bounce == 0) {
275                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
276                 }
277
278                 /* Free up the memory we used */
279                 if (valid != NULL) {
280                         free_recipients(valid);
281                 }
282         }
283         FreeStrBuf(&boundary);
284         CtdlFreeMessage(bmsg);
285         syslog(LOG_DEBUG, "Done processing bounces\n");
286 }