moved whitespace around
[citadel.git] / citadel / utils / citmail.c
1 // This program attempts to act like a local MDA if you're using postfix 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-2022 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
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 <libcitadel.h>
26 #include "../server/sysdep.h"
27 #include "../server/citadel_defs.h"
28 #include "../server/server.h"
29 #include "../server/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 // input binary data from socket
73 void serv_read(char *buf, int bytes) {
74         int len, rlen;
75
76         len = 0;
77         while (len < bytes) {
78                 rlen = read(serv_sock, &buf[len], bytes - len);
79                 if (rlen < 1) {
80                         return;
81                 }
82                 len = len + rlen;
83         }
84 }
85
86
87 // send binary to server
88 void serv_write(char *buf, int nbytes) {
89         int bytes_written = 0;
90         int retval;
91         while (bytes_written < nbytes) {
92                 retval = write(serv_sock, &buf[bytes_written],
93                                nbytes - bytes_written);
94                 if (retval < 1) {
95                         return;
96                 }
97                 bytes_written = bytes_written + retval;
98         }
99 }
100
101
102 // input string from socket - implemented in terms of serv_read()
103 void serv_gets(char *buf) {
104         int i;
105
106         // Read one character at a time.
107         for (i = 0;; i++) {
108                 serv_read(&buf[i], 1);
109                 if (buf[i] == '\n' || i == (SIZ-1))
110                         break;
111         }
112
113         // If we got a long line, discard characters until the newline.
114         if (i == (SIZ-1))
115                 while (buf[i] != '\n')
116                         serv_read(&buf[i], 1);
117
118         // Strip all trailing nonprintables (crlf)
119         buf[i] = 0;
120         strip_trailing_nonprint(buf);
121         if (debug) fprintf(stderr, "> %s\n", buf);
122 }
123
124
125 // send line to server - implemented in terms of serv_write()
126 void serv_puts(char *buf) {
127         if (debug) fprintf(stderr, "< %s\n", buf);
128         serv_write(buf, strlen(buf));
129         serv_write("\n", 1);
130 }
131
132
133 void cleanup(int exitcode) {
134         char buf[1024];
135
136         if (exitcode != 0) {
137                 fprintf(stderr, "citmail: error #%d occurred while sending mail.\n", exitcode);
138                 fprintf(stderr, "Please check your Citadel configuration.\n");
139         }
140         serv_puts("QUIT");
141         serv_gets(buf);
142         exit(exitcode);
143 }
144
145
146 int main(int argc, char **argv) {
147         char buf[1024];
148         char fromline[1024];
149         FILE *fp;
150         int i;
151         struct passwd *pw;
152         int from_header = 0;
153         int in_body = 0;
154         char ctdldir[PATH_MAX]=CTDLDIR;
155         char *sp, *ep;
156         char hostname[256];
157         char **recipients = NULL;
158         int num_recipients = 0;
159         int to_or_cc = 0;
160         int read_recipients_from_headers = 0;
161         char *add_these_recipients = NULL;
162
163         for (i=1; i<argc; ++i) {
164                 if (!strcmp(argv[i], "-d")) {
165                         debug = 1;
166                 }
167                 else if (!strcmp(argv[i], "-t")) {
168                         read_recipients_from_headers = 1;
169                 }
170                 else if (!strncmp(argv[i], "-h", 2)) {
171                         safestrncpy(ctdldir, &argv[i][2], sizeof ctdldir);
172                 }
173                 else if (argv[i][0] != '-') {
174                         ++num_recipients;
175                         recipients = realloc(recipients, (num_recipients * sizeof (char *)));
176                         recipients[num_recipients - 1] = strdup(argv[i]);
177                 }
178         }
179
180         if (chdir(ctdldir) != 0) {
181                 fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno));
182                 exit(errno);
183         }
184                
185         pw = getpwuid(getuid());
186
187         fp = tmpfile();
188         if (fp == NULL) return(errno);
189         serv_sock = uds_connectsock(file_lmtp_socket);
190         serv_gets(buf);
191         if (buf[0] != '2') {
192                 fprintf(stderr, "%s\n", &buf[4]);
193                 if (debug) fprintf(stderr, "citmail: could not connect to LMTP socket.\n");
194                 cleanup(1);
195         }
196
197         sp = strchr (buf, ' ');
198         if (sp == NULL) {
199                 if (debug) fprintf(stderr, "citmail: could not calculate hostname.\n");
200                 cleanup(2);
201         }
202         sp ++;
203         ep = strchr (sp, ' ');
204         if (ep == NULL) {
205                 if (debug) fprintf(stderr, "citmail: error parsing hostname\n");
206                 cleanup(3);
207         }
208         else
209                 *ep = '\0';
210
211         strncpy(hostname, sp, sizeof hostname);
212
213         snprintf(fromline, sizeof fromline, "From: %s@%s", pw->pw_name, hostname);
214         while (fgets(buf, 1024, stdin) != NULL) {
215                 if ( ( (buf[0] == 13) || (buf[0] == 10)) && (in_body == 0) ) {
216                         in_body = 1;
217                         if (from_header == 0) {
218                                 fprintf(fp, "%s%s", fromline, buf);
219                         }
220                 }
221                 if (in_body == 0 && !strncasecmp(buf, "From:", 5)) {
222                         strcpy(fromline, buf);
223                         from_header = 1;
224                 }
225
226                 if (read_recipients_from_headers) {
227                         add_these_recipients = NULL;
228                         if ((isspace(buf[0])) && (to_or_cc)) {
229                                 add_these_recipients = buf;
230                         }
231                         else {
232                                 if ((!strncasecmp(buf, "To:", 3)) || (!strncasecmp(buf, "Cc:", 3))) {
233                                         to_or_cc = 1;
234                                 }
235                                 else {
236                                         to_or_cc = 0;
237                                 }
238                                 if (to_or_cc) {
239                                         add_these_recipients = &buf[3];
240                                 }
241                         }
242
243                         if (add_these_recipients) {
244                                 int num_recp_on_this_line;
245                                 char this_recp[256];
246
247                                 num_recp_on_this_line = num_tokens(add_these_recipients, ',');
248                                 for (i=0; i<num_recp_on_this_line; ++i) {
249                                         extract_token(this_recp, add_these_recipients,
250                                                 i, ',', sizeof this_recp);
251                                         string_trim(this_recp);
252                                         if (!IsEmptyStr(this_recp)) {
253                                                 ++num_recipients;
254                                                 recipients = realloc(recipients,
255                                                         (num_recipients * sizeof (char *)));
256                                                 recipients[num_recipients - 1] = strdup(this_recp);
257                                         }
258                                 }
259                         }
260                 }
261
262                 fprintf(fp, "%s", buf);
263         }
264         strip_trailing_nonprint(fromline);
265
266         sprintf(buf, "LHLO %s", hostname);
267         serv_puts(buf);
268         do {
269                 serv_gets(buf);
270                 strcat(buf, "    ");
271         } while (buf[3] == '-');
272         if (buf[0] != '2') {
273                 if (debug) fprintf(stderr, "citmail: LHLO command failed\n");
274                 cleanup(4);
275         }
276
277         snprintf(buf, sizeof buf, "MAIL %s", fromline);
278         serv_puts(buf);
279         serv_gets(buf);
280         if (buf[0] != '2') {
281                 if (debug) fprintf(stderr, "citmail: MAIL command failed\n");
282                 cleanup(5);
283         }
284
285         for (i=0; i<num_recipients; ++i) {
286                 snprintf(buf, sizeof buf, "RCPT To: %s", recipients[i]);
287                 serv_puts(buf);
288                 serv_gets(buf);
289                 free(recipients[i]);
290         }
291         free(recipients);
292
293         serv_puts("DATA");
294         serv_gets(buf);
295         if (buf[0]!='3') {
296                 if (debug) fprintf(stderr, "citmail: DATA command failed\n");
297                 cleanup(6);
298         }
299
300         rewind(fp);
301         while (fgets(buf, sizeof buf, fp) != NULL) {
302                 strip_trailing_nonprint(buf);
303                 serv_puts(buf);
304         }
305         serv_puts(".");
306         serv_gets(buf);
307         if (buf[0] != '2') {
308                 fprintf(stderr, "%s\n", &buf[4]);
309                 cleanup(7);
310         }
311         else {
312                 cleanup(0);
313         }
314
315         // We won't actually reach this statement but the compiler will
316         // display a spurious warning about an invalid return type if
317         // we don't return an int.
318         return(0);
319 }