Minor cleanups to citmail. Added a command-line '-d' parameter
[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
180         for (i=1; i<argc; ++i) {
181                 if (!strcmp(argv[i], "-d")) {
182                         debug = 1;
183                 }
184         }
185                
186         /* TODO: should we be able to calculate relative dirs? */
187         calc_dirs_n_files(relh, home, relhome, ctdldir);
188
189         pw = getpwuid(getuid());
190
191         fp = tmpfile();
192         if (fp == NULL) return(errno);
193         serv_sock = uds_connectsock(file_lmtp_socket);
194         serv_gets(buf);
195         if (buf[0]!='2') cleanup(1);
196
197         sp = strchr (buf, ' ');
198         if (sp == NULL) cleanup(1);
199         sp ++;
200         ep = strchr (sp, ' ');
201         if (ep == NULL) cleanup(1);
202         *ep = '\0';
203         strncpy(hostname, sp, sizeof hostname);
204
205         snprintf(fromline, sizeof fromline, "From: %s@%s",
206                  pw->pw_name,
207                  hostname
208         );
209         while (fgets(buf, 1024, stdin) != NULL) {
210                 if ( ( (buf[0] == 13) || (buf[0] == 10)) && (in_body == 0) ) {
211                         in_body = 1;
212                         if (from_header == 0) {
213                                 fprintf(fp, "%s%s", fromline, buf);
214                         }
215                 }
216                 if (!strncasecmp(buf, "From:", 5)) {
217                         strcpy(fromline, buf);
218                         if (in_body == 0) {
219                                 from_header = 1;
220                         }
221                 }
222                 fprintf(fp, "%s", buf);
223         }
224         strip_trailing_nonprint(fromline);
225
226         sprintf(buf, "LHLO %s", hostname);
227         serv_puts(buf);
228         do {
229                 serv_gets(buf);
230                 strcat(buf, "    ");
231         } while (buf[3] == '-');
232         if (buf[0] != '2') cleanup(1);
233
234         snprintf(buf, sizeof buf, "MAIL %s", fromline);
235         serv_puts(buf);
236         serv_gets(buf);
237         if (buf[0] != '2') cleanup(1);
238
239         for (i=1; i<argc; ++i) {
240                 if (argv[i][0] != '-') {
241                         snprintf(buf, sizeof buf, "RCPT To: %s", argv[i]);
242                         serv_puts(buf);
243                         serv_gets(buf);
244                         /* if (buf[0] != '2') cleanup(1); */
245                 }
246         }
247
248         serv_puts("DATA");
249         serv_gets(buf);
250         if (buf[0]!='3') cleanup(1);
251
252         rewind(fp);
253         while (fgets(buf, sizeof buf, fp) != NULL) {
254                 strip_trailing_nonprint(buf);
255                 serv_puts(buf);
256         }
257         serv_puts(".");
258         serv_gets(buf);
259         if (buf[0] != '2') {
260                 cleanup(1);
261         }
262         else {
263                 cleanup(0);
264         }
265
266         /* We won't actually reach this statement but the compiler will
267          * display a spurious warning about an invalid return type if
268          * we don't return an int.
269          */
270         return(0);
271 }