More removal of $Id$ tags
[citadel.git] / citadel / utils / citmail.c
1 /*
2  * This program attempts to act like a local MDA if you're using sendmail or
3  * some other non-Citadel MTA.  It basically just contacts the Citadel LMTP
4  * listener on a unix domain socket and transmits the message.
5  *
6  * Copyright (c) 1987-2009 by the citadel.org team
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "sysdep.h"
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <signal.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <netdb.h>
33 #include <string.h>
34 #include <pwd.h>
35 #include <errno.h>
36 #include <stdarg.h>
37 #include <limits.h>
38 #include <libcitadel.h>
39 #include "citadel.h"
40 #ifndef HAVE_SNPRINTF
41 #include "snprintf.h"
42 #endif
43 #include "citadel_dirs.h"
44
45 int serv_sock;
46 int debug = 0;
47
48 void strip_trailing_nonprint(char *buf)
49 {
50         while ( (!IsEmptyStr(buf)) && (!isprint(buf[strlen(buf) - 1])) )
51                 buf[strlen(buf) - 1] = 0;
52 }
53
54
55 void timeout(int signum)
56 {
57         exit(signum);
58 }
59
60
61 int uds_connectsock(char *sockpath)
62 {
63         int s;
64         struct sockaddr_un addr;
65
66         memset(&addr, 0, sizeof(addr));
67         addr.sun_family = AF_UNIX;
68         strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
69
70         s = socket(AF_UNIX, SOCK_STREAM, 0);
71         if (s < 0) {
72                 fprintf(stderr, "citmail: Can't create socket: %s\n",
73                         strerror(errno));
74                 exit(3);
75         }
76
77         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
78                 fprintf(stderr, "citmail: can't connect: %s\n",
79                         strerror(errno));
80                 close(s);
81                 exit(3);
82         }
83
84         return s;
85 }
86
87
88 /*
89  * input binary data from socket
90  */
91 void serv_read(char *buf, int bytes)
92 {
93         int len, rlen;
94
95         len = 0;
96         while (len < bytes) {
97                 rlen = read(serv_sock, &buf[len], bytes - len);
98                 if (rlen < 1) {
99                         return;
100                 }
101                 len = len + rlen;
102         }
103 }
104
105
106 /*
107  * send binary to server
108  */
109 void serv_write(char *buf, int nbytes)
110 {
111         int bytes_written = 0;
112         int retval;
113         while (bytes_written < nbytes) {
114                 retval = write(serv_sock, &buf[bytes_written],
115                                nbytes - bytes_written);
116                 if (retval < 1) {
117                         return;
118                 }
119                 bytes_written = bytes_written + retval;
120         }
121 }
122
123
124
125 /*
126  * input string from socket - implemented in terms of serv_read()
127  */
128 void serv_gets(char *buf)
129 {
130         int i;
131
132         /* Read one character at a time.
133          */
134         for (i = 0;; i++) {
135                 serv_read(&buf[i], 1);
136                 if (buf[i] == '\n' || i == (SIZ-1))
137                         break;
138         }
139
140         /* If we got a long line, discard characters until the newline.
141          */
142         if (i == (SIZ-1))
143                 while (buf[i] != '\n')
144                         serv_read(&buf[i], 1);
145
146         /* Strip all trailing nonprintables (crlf)
147          */
148         buf[i] = 0;
149         strip_trailing_nonprint(buf);
150         if (debug) fprintf(stderr, "> %s\n", buf);
151 }
152
153
154 /*
155  * send line to server - implemented in terms of serv_write()
156  */
157 void serv_puts(char *buf)
158 {
159         if (debug) fprintf(stderr, "< %s\n", buf);
160         serv_write(buf, strlen(buf));
161         serv_write("\n", 1);
162 }
163
164
165
166 void cleanup(int exitcode) {
167         char buf[1024];
168
169         if (exitcode != 0) {
170                 fprintf(stderr, "citmail: error #%d occurred while sending mail.\n", exitcode);
171                 fprintf(stderr, "Please check your Citadel configuration.\n");
172         }
173         serv_puts("QUIT");
174         serv_gets(buf);
175         exit(exitcode);
176 }
177
178
179
180 int main(int argc, char **argv) {
181         char buf[1024];
182         char fromline[1024];
183         FILE *fp;
184         int i;
185         struct passwd *pw;
186         int from_header = 0;
187         int in_body = 0;
188         int relh=0;
189         int home=0;
190         char relhome[PATH_MAX]="";
191         char ctdldir[PATH_MAX]=CTDLDIR;
192         char *sp, *ep;
193         char hostname[256];
194         char **recipients = NULL;
195         int num_recipients = 0;
196         int to_or_cc = 0;
197         int read_recipients_from_headers = 0;
198         char *add_these_recipients = NULL;
199
200         for (i=1; i<argc; ++i) {
201                 if (!strcmp(argv[i], "-d")) {
202                         debug = 1;
203                 }
204                 else if (!strcmp(argv[i], "-t")) {
205                         read_recipients_from_headers = 1;
206                 }
207                 else if (argv[i][0] != '-') {
208                         ++num_recipients;
209                         recipients = realloc(recipients, (num_recipients * sizeof (char *)));
210                         recipients[num_recipients - 1] = strdup(argv[i]);
211                 }
212         }
213                
214         /* TODO: should we be able to calculate relative dirs? */
215         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
216
217         pw = getpwuid(getuid());
218
219         fp = tmpfile();
220         if (fp == NULL) return(errno);
221         serv_sock = uds_connectsock(file_lmtp_socket);  /* FIXME: if called as 'sendmail' connect to file_lmtp_unfiltered_socket */
222         serv_gets(buf);
223         if (buf[0] != '2') {
224                 fprintf(stderr, "%s\n", &buf[4]);
225                 if (debug) fprintf(stderr, "citmail: could not connect to LMTP socket.\n");
226                 cleanup(1);
227         }
228
229         sp = strchr (buf, ' ');
230         if (sp == NULL) {
231                 if (debug) fprintf(stderr, "citmail: ould not calculate hostname.\n");
232                 cleanup(2);
233         }
234         sp ++;
235         ep = strchr (sp, ' ');
236         if (ep == NULL) cleanup(3);
237         *ep = '\0';
238         strncpy(hostname, sp, sizeof hostname);
239
240         snprintf(fromline, sizeof fromline, "From: %s@%s", pw->pw_name, hostname);
241         while (fgets(buf, 1024, stdin) != NULL) {
242                 if ( ( (buf[0] == 13) || (buf[0] == 10)) && (in_body == 0) ) {
243                         in_body = 1;
244                         if (from_header == 0) {
245                                 fprintf(fp, "%s%s", fromline, buf);
246                         }
247                 }
248                 if (in_body == 0 && !strncasecmp(buf, "From:", 5)) {
249                         strcpy(fromline, buf);
250                         from_header = 1;
251                 }
252
253                 if (read_recipients_from_headers) {
254                         add_these_recipients = NULL;
255                         if ((isspace(buf[0])) && (to_or_cc)) {
256                                 add_these_recipients = buf;
257                         }
258                         else {
259                                 if ((!strncasecmp(buf, "To:", 3)) || (!strncasecmp(buf, "Cc:", 3))) {
260                                         to_or_cc = 1;
261                                 }
262                                 else {
263                                         to_or_cc = 0;
264                                 }
265                                 if (to_or_cc) {
266                                         add_these_recipients = &buf[3];
267                                 }
268                         }
269
270                         if (add_these_recipients) {
271                                 int num_recp_on_this_line;
272                                 char this_recp[256];
273
274                                 num_recp_on_this_line = num_tokens(add_these_recipients, ',');
275                                 for (i=0; i<num_recp_on_this_line; ++i) {
276                                         extract_token(this_recp, add_these_recipients,
277                                                 i, ',', sizeof this_recp);
278                                         striplt(this_recp);
279                                         if (!IsEmptyStr(this_recp)) {
280                                                 ++num_recipients;
281                                                 recipients = realloc(recipients,
282                                                         (num_recipients * sizeof (char *)));
283                                                 recipients[num_recipients - 1] = strdup(this_recp);
284                                         }
285                                 }
286                         }
287                 }
288
289                 fprintf(fp, "%s", buf);
290         }
291         strip_trailing_nonprint(fromline);
292
293         sprintf(buf, "LHLO %s", hostname);
294         serv_puts(buf);
295         do {
296                 serv_gets(buf);
297                 strcat(buf, "    ");
298         } while (buf[3] == '-');
299         if (buf[0] != '2') cleanup(4);
300
301         snprintf(buf, sizeof buf, "MAIL %s", fromline);
302         serv_puts(buf);
303         serv_gets(buf);
304         if (buf[0] != '2') cleanup(5);
305
306         for (i=0; i<num_recipients; ++i) {
307                 snprintf(buf, sizeof buf, "RCPT To: %s", recipients[i]);
308                 serv_puts(buf);
309                 serv_gets(buf);
310                 free(recipients[i]);
311         }
312         free(recipients);
313
314         serv_puts("DATA");
315         serv_gets(buf);
316         if (buf[0]!='3') cleanup(6);
317
318         rewind(fp);
319         while (fgets(buf, sizeof buf, fp) != NULL) {
320                 strip_trailing_nonprint(buf);
321                 serv_puts(buf);
322         }
323         serv_puts(".");
324         serv_gets(buf);
325         if (buf[0] != '2') {
326                 fprintf(stderr, "%s\n", &buf[4]);
327                 cleanup(7);
328         }
329         else {
330                 cleanup(0);
331         }
332
333         /* We won't actually reach this statement but the compiler will
334          * display a spurious warning about an invalid return type if
335          * we don't return an int.
336          */
337         return(0);
338 }