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-2009 by the citadel.org team
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.
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.
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
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 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 long bounce_msgid = (-1);
120 time_t submitted = 0L;
121 struct CtdlMessage *bmsg = NULL;
123 struct recptypes *valid;
124 int successful_bounce = 0;
129 CtdlLogPrintf(CTDL_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');
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);
145 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
149 /* Start building our bounce message */
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);
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);
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"
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"
186 * Now go through the instructions checking for stuff.
188 for (i=0; i<lines; ++i) {
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);
198 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
199 key, addr, status, dsn);
201 if (!strcasecmp(key, "bounceto")) {
202 strcpy(bounceto, addr);
205 if (!strcasecmp(key, "msgid")) {
209 if (!strcasecmp(key, "remote")) {
210 if (status == 5) bounce_this = 1;
211 if (give_up) bounce_this = 1;
217 StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
218 StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
219 StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
220 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
222 remove_token(instr, i, '\n');
228 /* Attach the original message */
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);
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);
245 StrBufAppendBuf(BounceMB, OMsgTxt, 0);
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 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
258 if (num_bounces > 0) {
260 /* First try the user who sent the message */
261 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
262 if (IsEmptyStr(bounceto)) {
263 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
264 bounce_msgid = (-1L);
267 /* Can we deliver the bounce to the original sender? */
268 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
270 if (valid->num_error == 0) {
271 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
272 successful_bounce = 1;
276 /* If not, post it in the Aide> room */
277 if (successful_bounce == 0) {
278 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
281 /* Free up the memory we used */
283 free_recipients(valid);
286 FreeStrBuf(&boundary);
287 CtdlFreeMessage(bmsg);
288 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");