style cleanup
[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-2017 by the citadel.org team
24  *
25  * This program is open source software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License version 3.
27  *
28  * This program is distributed in the hope that it will be useful,
29  * but WITHOUT ANY WARRANTY; without even the implied warranty of
30  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31  * GNU General Public License for more details.
32  */
33
34 #include "sysdep.h"
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <termios.h>
39 #include <fcntl.h>
40 #include <signal.h>
41 #include <pwd.h>
42 #include <errno.h>
43 #include <sys/types.h>
44 #include <syslog.h>
45
46 #if TIME_WITH_SYS_TIME
47 # include <sys/time.h>
48 # include <time.h>
49 #else
50 # if HAVE_SYS_TIME_H
51 #  include <sys/time.h>
52 # else
53 #  include <time.h>
54 # endif
55 #endif
56
57 #include <sys/wait.h>
58 #include <ctype.h>
59 #include <string.h>
60 #include <limits.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include <libcitadel.h>
65 #include "citadel.h"
66 #include "server.h"
67 #include "citserver.h"
68 #include "support.h"
69 #include "config.h"
70 #include "control.h"
71 #include "user_ops.h"
72 #include "database.h"
73 #include "msgbase.h"
74 #include "internet_addressing.h"
75 #include "genstamp.h"
76 #include "domain.h"
77 #include "clientsocket.h"
78 #include "locate_host.h"
79 #include "citadel_dirs.h"
80
81 #include "ctdl_module.h"
82
83 #include "smtp_util.h"
84 #include "smtpqueue.h"
85 #include "smtp_clienthandlers.h"
86
87 const char *smtp_get_Recipients(void)
88 {
89         citsmtp *sSMTP = SMTP;
90
91         if (sSMTP == NULL)
92                 return NULL;
93         else return ChrPtr(sSMTP->from);
94 }
95
96
97 /*
98  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
99  * instructions for "5" codes (permanent fatal errors) and produce/deliver
100  * a "bounce" message (delivery status notification).
101  */
102 void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
103 {
104         int i;
105         int lines;
106         int status;
107         char buf[1024];
108         char key[1024];
109         char addr[1024];
110         char dsn[1024];
111         char bounceto[1024];
112         StrBuf *boundary;
113         int num_bounces = 0;
114         int bounce_this = 0;
115         time_t submitted = 0L;
116         struct CtdlMessage *bmsg = NULL;
117         int give_up = 0;
118         recptypes *valid;
119         int successful_bounce = 0;
120         static int seq = 0;
121         StrBuf *BounceMB;
122         long omsgid = (-1);
123
124         syslog(LOG_DEBUG, "smtp_do_bounce() called");
125         strcpy(bounceto, "");
126         boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
127
128         StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq);
129
130         lines = num_tokens(instr, '\n');
131
132         /* See if it's time to give up on delivery of this message */
133         for (i=0; i<lines; ++i) {
134                 extract_token(buf, instr, i, '\n', sizeof buf);
135                 extract_token(key, buf, 0, '|', sizeof key);
136                 extract_token(addr, buf, 1, '|', sizeof addr);
137                 if (!strcasecmp(key, "submitted")) {
138                         submitted = atol(addr);
139                 }
140         }
141
142         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
143                 give_up = 1;
144         }
145
146         /* Start building our bounce message */
147
148         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
149         if (bmsg == NULL) return;
150         memset(bmsg, 0, sizeof(struct CtdlMessage));
151         BounceMB = NewStrBufPlain(NULL, 1024);
152
153         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
154         bmsg->cm_anon_type = MES_NORMAL;
155         bmsg->cm_format_type = FMT_RFC822;
156         CM_SetField(bmsg, eAuthor, HKEY("Citadel"));
157         CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM));
158         CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename")));
159         CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)"));
160         StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
161         StrBufAppendBuf(BounceMB, boundary, 0);
162         StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
163         StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
164         StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
165
166         StrBufAppendBufPlain(
167                 BounceMB,
168                 HKEY("\r\nThis is a multipart message in MIME format."
169                      "\r\n\r\n"), 0);
170
171         StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
172         StrBufAppendBuf(BounceMB, boundary, 0);
173         StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
174         StrBufAppendBufPlain(BounceMB,
175                              HKEY("Content-type: text/plain\r\n\r\n"), 0);
176
177         if (give_up)
178         {
179                 StrBufAppendBufPlain(
180                         BounceMB,
181                         HKEY("A message you sent could not be delivered "
182                              "to some or all of its recipients\ndue to "
183                              "prolonged unavailability of its destination(s).\n"
184                              "Giving up on the following addresses:\n\n"), 0);
185         }
186         else
187         {
188                 StrBufAppendBufPlain(
189                         BounceMB,
190                         HKEY("A message you sent could not be delivered "
191                              "to some or all of its recipients.\n"
192                              "The following addresses were undeliverable:\n\n"
193                                 ), 0);
194         }
195
196         /*
197          * Now go through the instructions checking for stuff.
198          */
199         for (i=0; i<lines; ++i) {
200                 long addrlen;
201                 long dsnlen;
202                 extract_token(buf, instr, i, '\n', sizeof buf);
203                 extract_token(key, buf, 0, '|', sizeof key);
204                 addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
205                 status = extract_int(buf, 2);
206                 dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
207                 bounce_this = 0;
208
209                 syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>", key, addr, status, dsn);
210
211                 if (!strcasecmp(key, "bounceto")) {
212                         strcpy(bounceto, addr);
213                 }
214
215                 if (!strcasecmp(key, "msgid")) {
216                         omsgid = atol(addr);
217                 }
218
219                 if (!strcasecmp(key, "remote")) {
220                         if (status == 5) bounce_this = 1;
221                         if (give_up) bounce_this = 1;
222                 }
223
224                 if (bounce_this) {
225                         ++num_bounces;
226
227                         StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
228                         StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
229                         StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
230                         StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
231
232                         remove_token(instr, i, '\n');
233                         --i;
234                         --lines;
235                 }
236         }
237
238         /* Attach the original message */
239         if (omsgid >= 0) {
240                 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
241                 StrBufAppendBuf(BounceMB, boundary, 0);
242                 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
243
244                 StrBufAppendBufPlain(
245                         BounceMB,
246                         HKEY("Content-type: message/rfc822\r\n"), 0);
247
248                 StrBufAppendBufPlain(
249                         BounceMB,
250                         HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
251
252                 StrBufAppendBufPlain(
253                         BounceMB,
254                         HKEY("Content-Disposition: inline\r\n"), 0);
255
256                 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
257
258                 if (OMsgTxt == NULL) {
259                         CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
260                         CtdlOutputMsg(omsgid,
261                                       MT_RFC822,
262                                       HEADERS_ALL,
263                                       0, 1, NULL, 0,
264                                       NULL, NULL, NULL);
265
266                         StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
267                         FreeStrBuf(&CC->redirect_buffer);
268                 }
269                 else {
270                         StrBufAppendBuf(BounceMB, OMsgTxt, 0);
271                 }
272         }
273
274         /* Close the multipart MIME scope */
275         StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
276         StrBufAppendBuf(BounceMB, boundary, 0);
277         StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
278         CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB);
279
280         /* Deliver the bounce if there's anything worth mentioning */
281         syslog(LOG_DEBUG, "num_bounces = %d", num_bounces);
282         if (num_bounces > 0) {
283
284                 /* First try the user who sent the message */
285                 if (IsEmptyStr(bounceto)) {
286                         syslog(LOG_ERR, "No bounce address specified");
287                 }
288                 else {
289                         syslog(LOG_DEBUG, "bounce to user <%s>", bounceto);
290                 }
291                 /* Can we deliver the bounce to the original sender? */
292                 valid = validate_recipients(bounceto,
293                                             smtp_get_Recipients (),
294                                             0);
295                 if (valid != NULL) {
296                         if (valid->num_error == 0) {
297                                 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
298                                 successful_bounce = 1;
299                         }
300                 }
301
302                 /* If not, post it in the Aide> room */
303                 if (successful_bounce == 0) {
304                         CtdlSubmitMsg(bmsg, NULL, CtdlGetConfigStr("c_aideroom"), QP_EADDR);
305                 }
306
307                 /* Free up the memory we used */
308                 if (valid != NULL) {
309                         free_recipients(valid);
310                 }
311         }
312         FreeStrBuf(&boundary);
313         CM_Free(bmsg);
314         syslog(LOG_DEBUG, "Done processing bounces");
315 }