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 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_"));
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 syslog(LOG_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 if (OMsgTxt == NULL) {
238 CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
239 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
240 StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
241 FreeStrBuf(&CC->redirect_buffer);
244 StrBufAppendBuf(BounceMB, OMsgTxt, 0);
248 /* Close the multipart MIME scope */
249 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
250 StrBufAppendBuf(BounceMB, boundary, 0);
251 StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
252 if (bmsg->cm_fields['A'] != NULL)
253 free(bmsg->cm_fields['A']);
254 bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
255 /* Deliver the bounce if there's anything worth mentioning */
256 syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
257 if (num_bounces > 0) {
259 /* First try the user who sent the message */
260 if (IsEmptyStr(bounceto))
261 syslog(LOG_ERR, "No bounce address specified\n");
263 syslog(LOG_DEBUG, "bounce to user <%s>\n", bounceto);
264 /* Can we deliver the bounce to the original sender? */
265 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
267 if (valid->num_error == 0) {
268 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
269 successful_bounce = 1;
273 /* If not, post it in the Aide> room */
274 if (successful_bounce == 0) {
275 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
278 /* Free up the memory we used */
280 free_recipients(valid);
283 FreeStrBuf(&boundary);
284 CtdlFreeMessage(bmsg);
285 syslog(LOG_DEBUG, "Done processing bounces\n");