2 * This module is an SMTP and ESMTP implementation for the Citadel system.
3 * It is compliant with all of the following:
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
20 * The VRFY and EXPN commands have been removed from this implementation
21 * because nobody uses these commands anymore, except for spammers.
23 * Copyright (c) 1998-2017 by the citadel.org team
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.
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.
43 #include <sys/types.h>
46 #if TIME_WITH_SYS_TIME
47 # include <sys/time.h>
51 # include <sys/time.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include <libcitadel.h>
67 #include "citserver.h"
74 #include "internet_addressing.h"
77 #include "clientsocket.h"
78 #include "locate_host.h"
79 #include "citadel_dirs.h"
81 #include "ctdl_module.h"
83 #include "smtp_util.h"
84 #include "smtpqueue.h"
85 #include "smtp_clienthandlers.h"
87 const char *smtp_get_Recipients(void)
89 citsmtp *sSMTP = SMTP;
93 else return ChrPtr(sSMTP->from);
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).
102 void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
115 time_t submitted = 0L;
116 struct CtdlMessage *bmsg = NULL;
119 int successful_bounce = 0;
124 syslog(LOG_DEBUG, "smtp_do_bounce() called");
125 strcpy(bounceto, "");
126 boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
128 StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq);
130 lines = num_tokens(instr, '\n');
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);
142 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
146 /* Start building our bounce message */
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);
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);
166 StrBufAppendBufPlain(
168 HKEY("\r\nThis is a multipart message in MIME format."
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);
179 StrBufAppendBufPlain(
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);
188 StrBufAppendBufPlain(
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"
197 * Now go through the instructions checking for stuff.
199 for (i=0; i<lines; ++i) {
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);
209 syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>", key, addr, status, dsn);
211 if (!strcasecmp(key, "bounceto")) {
212 strcpy(bounceto, addr);
215 if (!strcasecmp(key, "msgid")) {
219 if (!strcasecmp(key, "remote")) {
220 if (status == 5) bounce_this = 1;
221 if (give_up) bounce_this = 1;
227 StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
228 StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
229 StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
230 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
232 remove_token(instr, i, '\n');
238 /* Attach the original message */
240 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
241 StrBufAppendBuf(BounceMB, boundary, 0);
242 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
244 StrBufAppendBufPlain(
246 HKEY("Content-type: message/rfc822\r\n"), 0);
248 StrBufAppendBufPlain(
250 HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
252 StrBufAppendBufPlain(
254 HKEY("Content-Disposition: inline\r\n"), 0);
256 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
258 if (OMsgTxt == NULL) {
259 CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
260 CtdlOutputMsg(omsgid,
266 StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
267 FreeStrBuf(&CC->redirect_buffer);
270 StrBufAppendBuf(BounceMB, OMsgTxt, 0);
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);
280 /* Deliver the bounce if there's anything worth mentioning */
281 syslog(LOG_DEBUG, "num_bounces = %d", num_bounces);
282 if (num_bounces > 0) {
284 /* First try the user who sent the message */
285 if (IsEmptyStr(bounceto)) {
286 syslog(LOG_ERR, "No bounce address specified");
289 syslog(LOG_DEBUG, "bounce to user <%s>", bounceto);
291 /* Can we deliver the bounce to the original sender? */
292 valid = validate_recipients(bounceto,
293 smtp_get_Recipients (),
296 if (valid->num_error == 0) {
297 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
298 successful_bounce = 1;
302 /* If not, post it in the Aide> room */
303 if (successful_bounce == 0) {
304 CtdlSubmitMsg(bmsg, NULL, CtdlGetConfigStr("c_aideroom"), QP_EADDR);
307 /* Free up the memory we used */
309 free_recipients(valid);
312 FreeStrBuf(&boundary);
314 syslog(LOG_DEBUG, "Done processing bounces");