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) {
118 long bounce_msgid = (-1);
119 time_t submitted = 0L;
120 struct CtdlMessage *bmsg = NULL;
122 struct recptypes *valid;
123 int successful_bounce = 0;
128 CtdlLogPrintf(CTDL_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');
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);
144 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
148 /* Start building our bounce message */
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);
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);
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"
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"
185 * Now go through the instructions checking for stuff.
187 for (i=0; i<lines; ++i) {
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);
197 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
198 key, addr, status, dsn);
200 if (!strcasecmp(key, "bounceto")) {
201 strcpy(bounceto, addr);
204 if (!strcasecmp(key, "msgid")) {
208 if (!strcasecmp(key, "remote")) {
209 if (status == 5) bounce_this = 1;
210 if (give_up) bounce_this = 1;
216 StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
217 StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
218 StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
219 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
221 remove_token(instr, i, '\n');
227 /* Attach the original message */
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);
237 CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
238 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
239 StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
240 FreeStrBuf(&CC->redirect_buffer);
243 /* Close the multipart MIME scope */
244 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
245 StrBufAppendBuf(BounceMB, boundary, 0);
246 StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
247 if (bmsg->cm_fields['A'] != NULL)
248 free(bmsg->cm_fields['A']);
249 bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
250 /* Deliver the bounce if there's anything worth mentioning */
251 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
252 if (num_bounces > 0) {
254 /* First try the user who sent the message */
255 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
256 if (IsEmptyStr(bounceto)) {
257 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
258 bounce_msgid = (-1L);
261 /* Can we deliver the bounce to the original sender? */
262 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
264 if (valid->num_error == 0) {
265 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
266 successful_bounce = 1;
270 /* If not, post it in the Aide> room */
271 if (successful_bounce == 0) {
272 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
275 /* Free up the memory we used */
277 free_recipients(valid);
280 FreeStrBuf(&boundary);
281 CtdlFreeMessage(bmsg);
282 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");