Back to index

citadel  8.12
citmail.c
Go to the documentation of this file.
00001 /*
00002  * This program attempts to act like a local MDA if you're using sendmail or
00003  * some other non-Citadel MTA.  It basically just contacts the Citadel LMTP
00004  * listener on a unix domain socket and transmits the message.
00005  *
00006  * Copyright (c) 1987-2012 by the citadel.org team
00007  *
00008  *  This program is open source software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License version 3.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  */
00016 
00017 #include "sysdep.h"
00018 #include <stdlib.h>
00019 #include <unistd.h>
00020 #include <ctype.h>
00021 #include <stdio.h>
00022 #include <signal.h>
00023 #include <sys/types.h>
00024 #include <sys/socket.h>
00025 #include <sys/un.h>
00026 #include <netdb.h>
00027 #include <string.h>
00028 #include <pwd.h>
00029 #include <errno.h>
00030 #include <stdarg.h>
00031 #include <limits.h>
00032 #include <libcitadel.h>
00033 #include "citadel.h"
00034 #ifndef HAVE_SNPRINTF
00035 #include "snprintf.h"
00036 #endif
00037 #include "citadel_dirs.h"
00038 
00039 int serv_sock;
00040 int debug = 0;
00041 
00042 void strip_trailing_nonprint(char *buf)
00043 {
00044         while ( (!IsEmptyStr(buf)) && (!isprint(buf[strlen(buf) - 1])) )
00045                 buf[strlen(buf) - 1] = 0;
00046 }
00047 
00048 
00049 void timeout(int signum)
00050 {
00051        exit(signum);
00052 }
00053 
00054 
00055 int uds_connectsock(char *sockpath)
00056 {
00057        int s;
00058        struct sockaddr_un addr;
00059 
00060        memset(&addr, 0, sizeof(addr));
00061        addr.sun_family = AF_UNIX;
00062        strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
00063 
00064        s = socket(AF_UNIX, SOCK_STREAM, 0);
00065        if (s < 0) {
00066               fprintf(stderr, "citmail: Can't create socket: %s\n",
00067                      strerror(errno));
00068               exit(3);
00069        }
00070 
00071        if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00072               fprintf(stderr, "citmail: can't connect: %s\n",
00073                      strerror(errno));
00074               close(s);
00075               exit(3);
00076        }
00077 
00078        return s;
00079 }
00080 
00081 
00082 /*
00083  * input binary data from socket
00084  */
00085 void serv_read(char *buf, int bytes)
00086 {
00087        int len, rlen;
00088 
00089        len = 0;
00090        while (len < bytes) {
00091               rlen = read(serv_sock, &buf[len], bytes - len);
00092               if (rlen < 1) {
00093                      return;
00094               }
00095               len = len + rlen;
00096        }
00097 }
00098 
00099 
00100 /*
00101  * send binary to server
00102  */
00103 void serv_write(char *buf, int nbytes)
00104 {
00105        int bytes_written = 0;
00106        int retval;
00107        while (bytes_written < nbytes) {
00108               retval = write(serv_sock, &buf[bytes_written],
00109                             nbytes - bytes_written);
00110               if (retval < 1) {
00111                      return;
00112               }
00113               bytes_written = bytes_written + retval;
00114        }
00115 }
00116 
00117 
00118 
00119 /*
00120  * input string from socket - implemented in terms of serv_read()
00121  */
00122 void serv_gets(char *buf)
00123 {
00124        int i;
00125 
00126        /* Read one character at a time.
00127         */
00128        for (i = 0;; i++) {
00129               serv_read(&buf[i], 1);
00130               if (buf[i] == '\n' || i == (SIZ-1))
00131                      break;
00132        }
00133 
00134        /* If we got a long line, discard characters until the newline.
00135         */
00136        if (i == (SIZ-1))
00137               while (buf[i] != '\n')
00138                      serv_read(&buf[i], 1);
00139 
00140        /* Strip all trailing nonprintables (crlf)
00141         */
00142        buf[i] = 0;
00143        strip_trailing_nonprint(buf);
00144        if (debug) fprintf(stderr, "> %s\n", buf);
00145 }
00146 
00147 
00148 /*
00149  * send line to server - implemented in terms of serv_write()
00150  */
00151 void serv_puts(char *buf)
00152 {
00153        if (debug) fprintf(stderr, "< %s\n", buf);
00154        serv_write(buf, strlen(buf));
00155        serv_write("\n", 1);
00156 }
00157 
00158 
00159 
00160 void cleanup(int exitcode) {
00161        char buf[1024];
00162 
00163        if (exitcode != 0) {
00164               fprintf(stderr, "citmail: error #%d occurred while sending mail.\n", exitcode);
00165               fprintf(stderr, "Please check your Citadel configuration.\n");
00166        }
00167        serv_puts("QUIT");
00168        serv_gets(buf);
00169        exit(exitcode);
00170 }
00171 
00172 
00173 
00174 int main(int argc, char **argv) {
00175        char buf[1024];
00176        char fromline[1024];
00177        FILE *fp;
00178        int i;
00179        struct passwd *pw;
00180        int from_header = 0;
00181        int in_body = 0;
00182        int relh=0;
00183        int home=0;
00184        char relhome[PATH_MAX]="";
00185        char ctdldir[PATH_MAX]=CTDLDIR;
00186        char *sp, *ep;
00187        char hostname[256];
00188        char **recipients = NULL;
00189        int num_recipients = 0;
00190        int to_or_cc = 0;
00191        int read_recipients_from_headers = 0;
00192        char *add_these_recipients = NULL;
00193 
00194        for (i=1; i<argc; ++i) {
00195               if (!strcmp(argv[i], "-d")) {
00196                      debug = 1;
00197               }
00198               else if (!strcmp(argv[i], "-t")) {
00199                      read_recipients_from_headers = 1;
00200               }
00201               else if (argv[i][0] != '-') {
00202                      ++num_recipients;
00203                      recipients = realloc(recipients, (num_recipients * sizeof (char *)));
00204                      recipients[num_recipients - 1] = strdup(argv[i]);
00205               }
00206        }
00207               
00208        /* TODO: should we be able to calculate relative dirs? */
00209        calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
00210 
00211        pw = getpwuid(getuid());
00212 
00213        fp = tmpfile();
00214        if (fp == NULL) return(errno);
00215        serv_sock = uds_connectsock(file_lmtp_socket);   /* FIXME: if called as 'sendmail' connect to file_lmtp_unfiltered_socket */
00216        serv_gets(buf);
00217        if (buf[0] != '2') {
00218               fprintf(stderr, "%s\n", &buf[4]);
00219               if (debug) fprintf(stderr, "citmail: could not connect to LMTP socket.\n");
00220               cleanup(1);
00221        }
00222 
00223        sp = strchr (buf, ' ');
00224        if (sp == NULL) {
00225               if (debug) fprintf(stderr, "citmail: could not calculate hostname.\n");
00226               cleanup(2);
00227        }
00228        sp ++;
00229        ep = strchr (sp, ' ');
00230        if (ep == NULL) {
00231               if (debug) fprintf(stderr, "citmail: error parsing hostname\n");
00232               cleanup(3);
00233        }
00234        *ep = '\0';
00235        strncpy(hostname, sp, sizeof hostname);
00236 
00237        snprintf(fromline, sizeof fromline, "From: %s@%s", pw->pw_name, hostname);
00238        while (fgets(buf, 1024, stdin) != NULL) {
00239               if ( ( (buf[0] == 13) || (buf[0] == 10)) && (in_body == 0) ) {
00240                      in_body = 1;
00241                      if (from_header == 0) {
00242                             fprintf(fp, "%s%s", fromline, buf);
00243                      }
00244               }
00245               if (in_body == 0 && !strncasecmp(buf, "From:", 5)) {
00246                      strcpy(fromline, buf);
00247                      from_header = 1;
00248               }
00249 
00250               if (read_recipients_from_headers) {
00251                      add_these_recipients = NULL;
00252                      if ((isspace(buf[0])) && (to_or_cc)) {
00253                             add_these_recipients = buf;
00254                      }
00255                      else {
00256                             if ((!strncasecmp(buf, "To:", 3)) || (!strncasecmp(buf, "Cc:", 3))) {
00257                                    to_or_cc = 1;
00258                             }
00259                             else {
00260                                    to_or_cc = 0;
00261                             }
00262                             if (to_or_cc) {
00263                                    add_these_recipients = &buf[3];
00264                             }
00265                      }
00266 
00267                      if (add_these_recipients) {
00268                             int num_recp_on_this_line;
00269                             char this_recp[256];
00270 
00271                             num_recp_on_this_line = num_tokens(add_these_recipients, ',');
00272                             for (i=0; i<num_recp_on_this_line; ++i) {
00273                                    extract_token(this_recp, add_these_recipients,
00274                                           i, ',', sizeof this_recp);
00275                                    striplt(this_recp);
00276                                    if (!IsEmptyStr(this_recp)) {
00277                                           ++num_recipients;
00278                                           recipients = realloc(recipients,
00279                                                  (num_recipients * sizeof (char *)));
00280                                           recipients[num_recipients - 1] = strdup(this_recp);
00281                                    }
00282                             }
00283                      }
00284               }
00285 
00286               fprintf(fp, "%s", buf);
00287        }
00288        strip_trailing_nonprint(fromline);
00289 
00290        sprintf(buf, "LHLO %s", hostname);
00291        serv_puts(buf);
00292        do {
00293               serv_gets(buf);
00294               strcat(buf, "    ");
00295        } while (buf[3] == '-');
00296        if (buf[0] != '2') {
00297               if (debug) fprintf(stderr, "citmail: LHLO command failed\n");
00298               cleanup(4);
00299        }
00300 
00301        snprintf(buf, sizeof buf, "MAIL %s", fromline);
00302        serv_puts(buf);
00303        serv_gets(buf);
00304        if (buf[0] != '2') {
00305               if (debug) fprintf(stderr, "citmail: MAIL command failed\n");
00306               cleanup(5);
00307        }
00308 
00309        for (i=0; i<num_recipients; ++i) {
00310               snprintf(buf, sizeof buf, "RCPT To: %s", recipients[i]);
00311               serv_puts(buf);
00312               serv_gets(buf);
00313               free(recipients[i]);
00314        }
00315        free(recipients);
00316 
00317        serv_puts("DATA");
00318        serv_gets(buf);
00319        if (buf[0]!='3') {
00320               if (debug) fprintf(stderr, "citmail: DATA command failed\n");
00321               cleanup(6);
00322        }
00323 
00324        rewind(fp);
00325        while (fgets(buf, sizeof buf, fp) != NULL) {
00326               strip_trailing_nonprint(buf);
00327               serv_puts(buf);
00328        }
00329        serv_puts(".");
00330        serv_gets(buf);
00331        if (buf[0] != '2') {
00332               fprintf(stderr, "%s\n", &buf[4]);
00333               cleanup(7);
00334        }
00335        else {
00336               cleanup(0);
00337        }
00338 
00339        /* We won't actually reach this statement but the compiler will
00340         * display a spurious warning about an invalid return type if
00341         * we don't return an int.
00342         */
00343        return(0);
00344 }