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