Back to index

citadel  8.12
smtp_util.c
Go to the documentation of this file.
00001 /*
00002  * This module is an SMTP and ESMTP implementation for the Citadel system.
00003  * It is compliant with all of the following:
00004  *
00005  * RFC  821 - Simple Mail Transfer Protocol
00006  * RFC  876 - Survey of SMTP Implementations
00007  * RFC 1047 - Duplicate messages and SMTP
00008  * RFC 1652 - 8 bit MIME
00009  * RFC 1869 - Extended Simple Mail Transfer Protocol
00010  * RFC 1870 - SMTP Service Extension for Message Size Declaration
00011  * RFC 2033 - Local Mail Transfer Protocol
00012  * RFC 2197 - SMTP Service Extension for Command Pipelining
00013  * RFC 2476 - Message Submission
00014  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
00015  * RFC 2554 - SMTP Service Extension for Authentication
00016  * RFC 2821 - Simple Mail Transfer Protocol
00017  * RFC 2822 - Internet Message Format
00018  * RFC 2920 - SMTP Service Extension for Command Pipelining
00019  *  
00020  * The VRFY and EXPN commands have been removed from this implementation
00021  * because nobody uses these commands anymore, except for spammers.
00022  *
00023  * Copyright (c) 1998-2012 by the citadel.org team
00024  *
00025  *  This program is open source software; you can redistribute it and/or modify
00026  *  it under the terms of the GNU General Public License version 3.
00027  *  
00028  *  
00029  *
00030  *  This program is distributed in the hope that it will be useful,
00031  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00032  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00033  *  GNU General Public License for more details.
00034  *
00035  *  
00036  *  
00037  *  
00038  */
00039 
00040 #include "sysdep.h"
00041 #include <stdlib.h>
00042 #include <unistd.h>
00043 #include <stdio.h>
00044 #include <termios.h>
00045 #include <fcntl.h>
00046 #include <signal.h>
00047 #include <pwd.h>
00048 #include <errno.h>
00049 #include <sys/types.h>
00050 #include <syslog.h>
00051 
00052 #if TIME_WITH_SYS_TIME
00053 # include <sys/time.h>
00054 # include <time.h>
00055 #else
00056 # if HAVE_SYS_TIME_H
00057 #  include <sys/time.h>
00058 # else
00059 #  include <time.h>
00060 # endif
00061 #endif
00062 
00063 #include <sys/wait.h>
00064 #include <ctype.h>
00065 #include <string.h>
00066 #include <limits.h>
00067 #include <sys/socket.h>
00068 #include <netinet/in.h>
00069 #include <arpa/inet.h>
00070 #include <libcitadel.h>
00071 #include "citadel.h"
00072 #include "server.h"
00073 #include "citserver.h"
00074 #include "support.h"
00075 #include "config.h"
00076 #include "control.h"
00077 #include "user_ops.h"
00078 #include "database.h"
00079 #include "msgbase.h"
00080 #include "internet_addressing.h"
00081 #include "genstamp.h"
00082 #include "domain.h"
00083 #include "clientsocket.h"
00084 #include "locate_host.h"
00085 #include "citadel_dirs.h"
00086 
00087 #include "ctdl_module.h"
00088 
00089 #include "smtp_util.h"
00090 
00091 const char *smtp_get_Recipients(void)
00092 {
00093        citsmtp *sSMTP = SMTP;
00094 
00095        if (sSMTP == NULL)
00096               return NULL;
00097        else return sSMTP->from;
00098 }
00099 
00100 
00101 /*
00102  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
00103  * instructions for "5" codes (permanent fatal errors) and produce/deliver
00104  * a "bounce" message (delivery status notification).
00105  */
00106 void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
00107 {
00108        int i;
00109        int lines;
00110        int status;
00111        char buf[1024];
00112        char key[1024];
00113        char addr[1024];
00114        char dsn[1024];
00115        char bounceto[1024];
00116        StrBuf *boundary;
00117        int num_bounces = 0;
00118        int bounce_this = 0;
00119        time_t submitted = 0L;
00120        struct CtdlMessage *bmsg = NULL;
00121        int give_up = 0;
00122        struct recptypes *valid;
00123        int successful_bounce = 0;
00124        static int seq = 0;
00125        StrBuf *BounceMB;
00126        long omsgid = (-1);
00127 
00128        syslog(LOG_DEBUG, "smtp_do_bounce() called\n");
00129        strcpy(bounceto, "");
00130        boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
00131 
00132        StrBufAppendPrintf(boundary,
00133                         "%s_%04x%04x",
00134                         config.c_fqdn,
00135                         getpid(),
00136                         ++seq);
00137 
00138        lines = num_tokens(instr, '\n');
00139 
00140        /* See if it's time to give up on delivery of this message */
00141        for (i=0; i<lines; ++i) {
00142               extract_token(buf, instr, i, '\n', sizeof buf);
00143               extract_token(key, buf, 0, '|', sizeof key);
00144               extract_token(addr, buf, 1, '|', sizeof addr);
00145               if (!strcasecmp(key, "submitted")) {
00146                      submitted = atol(addr);
00147               }
00148        }
00149 
00150        if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
00151               give_up = 1;
00152        }
00153 
00154        /* Start building our bounce message */
00155 
00156        bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
00157        if (bmsg == NULL) return;
00158        memset(bmsg, 0, sizeof(struct CtdlMessage));
00159        BounceMB = NewStrBufPlain(NULL, 1024);
00160 
00161        bmsg->cm_magic = CTDLMESSAGE_MAGIC;
00162        bmsg->cm_anon_type = MES_NORMAL;
00163        bmsg->cm_format_type = FMT_RFC822;
00164        bmsg->cm_fields['A'] = strdup("Citadel");
00165        bmsg->cm_fields['O'] = strdup(MAILROOM);
00166        bmsg->cm_fields['N'] = strdup(config.c_nodename);
00167        bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
00168        StrBufAppendBufPlain(
00169               BounceMB,
00170               HKEY("Content-type: multipart/mixed; boundary=\""), 0);
00171        StrBufAppendBuf(BounceMB, boundary, 0);
00172        StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
00173        StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
00174        StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
00175 
00176        StrBufAppendBufPlain(
00177               BounceMB,
00178               HKEY("\r\nThis is a multipart message in MIME format."
00179                    "\r\n\r\n"), 0);
00180 
00181        StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
00182        StrBufAppendBuf(BounceMB, boundary, 0);
00183        StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
00184        StrBufAppendBufPlain(BounceMB,
00185                           HKEY("Content-type: text/plain\r\n\r\n"), 0);
00186 
00187        if (give_up)
00188        {
00189               StrBufAppendBufPlain(
00190                      BounceMB,
00191                      HKEY("A message you sent could not be delivered "
00192                           "to some or all of its recipients\ndue to "
00193                           "prolonged unavailability of its destination(s).\n"
00194                           "Giving up on the following addresses:\n\n"), 0);
00195        }
00196        else
00197        {
00198               StrBufAppendBufPlain(
00199                      BounceMB,
00200                      HKEY("A message you sent could not be delivered "
00201                           "to some or all of its recipients.\n"
00202                           "The following addresses were undeliverable:\n\n"
00203                             ), 0);
00204        }
00205 
00206        /*
00207         * Now go through the instructions checking for stuff.
00208         */
00209        for (i=0; i<lines; ++i) {
00210               long addrlen;
00211               long dsnlen;
00212               extract_token(buf, instr, i, '\n', sizeof buf);
00213               extract_token(key, buf, 0, '|', sizeof key);
00214               addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
00215               status = extract_int(buf, 2);
00216               dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
00217               bounce_this = 0;
00218 
00219               syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
00220                      key, addr, status, dsn);
00221 
00222               if (!strcasecmp(key, "bounceto")) {
00223                      strcpy(bounceto, addr);
00224               }
00225 
00226               if (!strcasecmp(key, "msgid")) {
00227                      omsgid = atol(addr);
00228               }
00229 
00230               if (!strcasecmp(key, "remote")) {
00231                      if (status == 5) bounce_this = 1;
00232                      if (give_up) bounce_this = 1;
00233               }
00234 
00235               if (bounce_this) {
00236                      ++num_bounces;
00237 
00238                      StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
00239                      StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
00240                      StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
00241                      StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
00242 
00243                      remove_token(instr, i, '\n');
00244                      --i;
00245                      --lines;
00246               }
00247        }
00248 
00249        /* Attach the original message */
00250        if (omsgid >= 0) {
00251               StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
00252               StrBufAppendBuf(BounceMB, boundary, 0);
00253               StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
00254 
00255               StrBufAppendBufPlain(
00256                      BounceMB,
00257                      HKEY("Content-type: message/rfc822\r\n"), 0);
00258 
00259               StrBufAppendBufPlain(
00260                      BounceMB,
00261                      HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
00262 
00263               StrBufAppendBufPlain(
00264                      BounceMB,
00265                      HKEY("Content-Disposition: inline\r\n"), 0);
00266 
00267               StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
00268 
00269               if (OMsgTxt == NULL) {
00270                      CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
00271                      CtdlOutputMsg(omsgid,
00272                                   MT_RFC822,
00273                                   HEADERS_ALL,
00274                                   0, 1, NULL, 0);
00275 
00276                      StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
00277                      FreeStrBuf(&CC->redirect_buffer);
00278               }
00279               else {
00280                      StrBufAppendBuf(BounceMB, OMsgTxt, 0);
00281               }
00282        }
00283 
00284        /* Close the multipart MIME scope */
00285        StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
00286        StrBufAppendBuf(BounceMB, boundary, 0);
00287        StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
00288        if (bmsg->cm_fields['A'] != NULL)
00289               free(bmsg->cm_fields['A']);
00290        bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
00291        /* Deliver the bounce if there's anything worth mentioning */
00292        syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
00293        if (num_bounces > 0) {
00294 
00295               /* First try the user who sent the message */
00296               if (IsEmptyStr(bounceto))
00297                      syslog(LOG_ERR, "No bounce address specified\n");
00298               else
00299                      syslog(LOG_DEBUG, "bounce to user <%s>\n", bounceto);
00300               /* Can we deliver the bounce to the original sender? */
00301               valid = validate_recipients(bounceto,
00302                                        smtp_get_Recipients (),
00303                                        0);
00304               if (valid != NULL) {
00305                      if (valid->num_error == 0) {
00306                             CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
00307                             successful_bounce = 1;
00308                      }
00309               }
00310 
00311               /* If not, post it in the Aide> room */
00312               if (successful_bounce == 0) {
00313                      CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
00314               }
00315 
00316               /* Free up the memory we used */
00317               if (valid != NULL) {
00318                      free_recipients(valid);
00319               }
00320        }
00321        FreeStrBuf(&boundary);
00322        CtdlFreeMessage(bmsg);
00323        syslog(LOG_DEBUG, "Done processing bounces\n");
00324 }