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