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-2012 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.
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.
49 #include <sys/types.h>
52 #if TIME_WITH_SYS_TIME
53 # include <sys/time.h>
57 # include <sys/time.h>
67 #include <sys/socket.h>
68 #include <netinet/in.h>
69 #include <arpa/inet.h>
70 #include <libcitadel.h>
73 #include "citserver.h"
80 #include "internet_addressing.h"
83 #include "clientsocket.h"
84 #include "locate_host.h"
85 #include "citadel_dirs.h"
87 #include "ctdl_module.h"
89 #include "smtp_util.h"
91 const char *smtp_get_Recipients(void)
93 citsmtp *sSMTP = SMTP;
97 else return ChrPtr(sSMTP->from);
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).
106 void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
119 time_t submitted = 0L;
120 struct CtdlMessage *bmsg = NULL;
122 struct recptypes *valid;
123 int successful_bounce = 0;
128 syslog(LOG_DEBUG, "smtp_do_bounce() called\n");
129 strcpy(bounceto, "");
130 boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
132 StrBufAppendPrintf(boundary,
138 lines = num_tokens(instr, '\n');
140 /* See if it's time to give up on delivery of this message */
141 for (i=0; i<lines; ++i) {
142 extract_token(buf, instr, i, '\n', sizeof buf);
143 extract_token(key, buf, 0, '|', sizeof key);
144 extract_token(addr, buf, 1, '|', sizeof addr);
145 if (!strcasecmp(key, "submitted")) {
146 submitted = atol(addr);
150 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
154 /* Start building our bounce message */
156 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
157 if (bmsg == NULL) return;
158 memset(bmsg, 0, sizeof(struct CtdlMessage));
159 BounceMB = NewStrBufPlain(NULL, 1024);
161 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
162 bmsg->cm_anon_type = MES_NORMAL;
163 bmsg->cm_format_type = FMT_RFC822;
164 bmsg->cm_fields['A'] = strdup("Citadel");
165 bmsg->cm_fields['O'] = strdup(MAILROOM);
166 bmsg->cm_fields['N'] = strdup(config.c_nodename);
167 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
168 StrBufAppendBufPlain(
170 HKEY("Content-type: multipart/mixed; boundary=\""), 0);
171 StrBufAppendBuf(BounceMB, boundary, 0);
172 StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
173 StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
174 StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
176 StrBufAppendBufPlain(
178 HKEY("\r\nThis is a multipart message in MIME format."
181 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
182 StrBufAppendBuf(BounceMB, boundary, 0);
183 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
184 StrBufAppendBufPlain(BounceMB,
185 HKEY("Content-type: text/plain\r\n\r\n"), 0);
189 StrBufAppendBufPlain(
191 HKEY("A message you sent could not be delivered "
192 "to some or all of its recipients\ndue to "
193 "prolonged unavailability of its destination(s).\n"
194 "Giving up on the following addresses:\n\n"), 0);
198 StrBufAppendBufPlain(
200 HKEY("A message you sent could not be delivered "
201 "to some or all of its recipients.\n"
202 "The following addresses were undeliverable:\n\n"
207 * Now go through the instructions checking for stuff.
209 for (i=0; i<lines; ++i) {
212 extract_token(buf, instr, i, '\n', sizeof buf);
213 extract_token(key, buf, 0, '|', sizeof key);
214 addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
215 status = extract_int(buf, 2);
216 dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
219 syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
220 key, addr, status, dsn);
222 if (!strcasecmp(key, "bounceto")) {
223 strcpy(bounceto, addr);
226 if (!strcasecmp(key, "msgid")) {
230 if (!strcasecmp(key, "remote")) {
231 if (status == 5) bounce_this = 1;
232 if (give_up) bounce_this = 1;
238 StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
239 StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
240 StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
241 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
243 remove_token(instr, i, '\n');
249 /* Attach the original message */
251 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
252 StrBufAppendBuf(BounceMB, boundary, 0);
253 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
255 StrBufAppendBufPlain(
257 HKEY("Content-type: message/rfc822\r\n"), 0);
259 StrBufAppendBufPlain(
261 HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
263 StrBufAppendBufPlain(
265 HKEY("Content-Disposition: inline\r\n"), 0);
267 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
269 if (OMsgTxt == NULL) {
270 CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
271 CtdlOutputMsg(omsgid,
277 StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
278 FreeStrBuf(&CC->redirect_buffer);
281 StrBufAppendBuf(BounceMB, OMsgTxt, 0);
285 /* Close the multipart MIME scope */
286 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
287 StrBufAppendBuf(BounceMB, boundary, 0);
288 StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
289 if (bmsg->cm_fields['A'] != NULL)
290 free(bmsg->cm_fields['A']);
291 bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
292 /* Deliver the bounce if there's anything worth mentioning */
293 syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
294 if (num_bounces > 0) {
296 /* First try the user who sent the message */
297 if (IsEmptyStr(bounceto))
298 syslog(LOG_ERR, "No bounce address specified\n");
300 syslog(LOG_DEBUG, "bounce to user <%s>\n", bounceto);
301 /* Can we deliver the bounce to the original sender? */
302 valid = validate_recipients(bounceto,
303 smtp_get_Recipients (),
306 if (valid->num_error == 0) {
307 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
308 successful_bounce = 1;
312 /* If not, post it in the Aide> room */
313 if (successful_bounce == 0) {
314 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
317 /* Free up the memory we used */
319 free_recipients(valid);
322 FreeStrBuf(&boundary);
323 CtdlFreeMessage(bmsg);
324 syslog(LOG_DEBUG, "Done processing bounces\n");