* Replaced citmail.c with a new one that simply SMTP-forwards to Citadel
[citadel.git] / citadel / citmail.c
1 /*
2  * 
3  * Completely reworked version of "citmail"
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 forwards the message to
6  * the Citadel SMTP listener on some non-standard port.
7  *
8  * $Id$
9  *
10  */
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 <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <netdb.h>
23 #include <string.h>
24 #include <pwd.h>
25 #include <errno.h>
26 #include <stdarg.h>
27 #include "citadel.h"
28 #include "citadel_decls.h"
29 #include "ipc.h"
30 #ifndef HAVE_SNPRINTF
31 #include "snprintf.h"
32 #endif
33
34 #ifndef INADDR_NONE
35 #define INADDR_NONE 0xffffffff
36 #endif
37
38 int serv_sock;
39
40
41 void strip_trailing_nonprint(char *buf)
42 {
43         while ( (strlen(buf)>0) && (!isprint(buf[strlen(buf) - 1])) )
44                 buf[strlen(buf) - 1] = 0;
45 }
46
47
48
49
50
51
52 void timeout(int signum)
53 {
54         exit(signum);
55 }
56
57
58 int connectsock(char *host, char *service, char *protocol)
59 {
60         struct hostent *phe;
61         struct servent *pse;
62         struct protoent *ppe;
63         struct sockaddr_in sin;
64         int s, type;
65
66         memset(&sin, 0, sizeof(sin));
67         sin.sin_family = AF_INET;
68
69         pse = getservbyname(service, protocol);
70         if (pse) {
71                 sin.sin_port = pse->s_port;
72         } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) {
73                 fprintf(stderr, "Can't get %s service entry: %s\n",
74                         service, strerror(errno));
75                 exit(3);
76         }
77         phe = gethostbyname(host);
78         if (phe) {
79                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
80         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
81                 fprintf(stderr, "Can't get %s host entry: %s\n",
82                         host, strerror(errno));
83                 exit(3);
84         }
85         if ((ppe = getprotobyname(protocol)) == 0) {
86                 fprintf(stderr, "Can't get %s protocol entry: %s\n",
87                         protocol, strerror(errno));
88                 exit(3);
89         }
90         if (!strcmp(protocol, "udp")) {
91                 type = SOCK_DGRAM;
92         } else {
93                 type = SOCK_STREAM;
94         }
95
96         s = socket(PF_INET, type, ppe->p_proto);
97         if (s < 0) {
98                 fprintf(stderr, "Can't create socket: %s\n", strerror(errno));
99                 exit(3);
100         }
101         signal(SIGALRM, timeout);
102         alarm(30);
103
104         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
105                 fprintf(stderr, "can't connect to %s.%s: %s\n",
106                         host, service, strerror(errno));
107                 exit(3);
108         }
109         alarm(0);
110         signal(SIGALRM, SIG_IGN);
111
112         return (s);
113 }
114
115 /*
116  * convert service and host entries into a six-byte numeric in the format
117  * expected by a SOCKS v4 server
118  */
119 void numericize(char *buf, char *host, char *service, char *protocol)
120 {
121         struct hostent *phe;
122         struct servent *pse;
123         struct sockaddr_in sin;
124
125         memset(&sin, 0, sizeof(sin));
126         sin.sin_family = AF_INET;
127
128         pse = getservbyname(service, protocol);
129         if (pse) {
130                 sin.sin_port = pse->s_port;
131         } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) {
132                 fprintf(stderr, "Can't get %s service entry: %s\n",
133                         service, strerror(errno));
134                 exit(3);
135         }
136         buf[1] = (((sin.sin_port) & 0xFF00) >> 8);
137         buf[0] = ((sin.sin_port) & 0x00FF);
138
139         phe = gethostbyname(host);
140         if (phe) {
141                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
142         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
143                 fprintf(stderr, "Can't get %s host entry: %s\n",
144                         host, strerror(errno));
145                 exit(3);
146         }
147         buf[5] = ((sin.sin_addr.s_addr) & 0xFF000000) >> 24;
148         buf[4] = ((sin.sin_addr.s_addr) & 0x00FF0000) >> 16;
149         buf[3] = ((sin.sin_addr.s_addr) & 0x0000FF00) >> 8;
150         buf[2] = ((sin.sin_addr.s_addr) & 0x000000FF);
151 }
152
153 /*
154  * input binary data from socket
155  */
156 void serv_read(char *buf, int bytes)
157 {
158         int len, rlen;
159
160         len = 0;
161         while (len < bytes) {
162                 rlen = read(serv_sock, &buf[len], bytes - len);
163                 if (rlen < 1) {
164                         return;
165                 }
166                 len = len + rlen;
167         }
168 }
169
170
171 /*
172  * send binary to server
173  */
174 void serv_write(char *buf, int nbytes)
175 {
176         int bytes_written = 0;
177         int retval;
178         while (bytes_written < nbytes) {
179                 retval = write(serv_sock, &buf[bytes_written],
180                                nbytes - bytes_written);
181                 if (retval < 1) {
182                         return;
183                 }
184                 bytes_written = bytes_written + retval;
185         }
186 }
187
188
189
190 /*
191  * input string from socket - implemented in terms of serv_read()
192  */
193 void serv_gets(char *buf)
194 {
195         int i;
196
197         /* Read one character at a time.
198          */
199         for (i = 0;; i++) {
200                 serv_read(&buf[i], 1);
201                 if (buf[i] == '\n' || i == 255)
202                         break;
203         }
204
205         /* If we got a long line, discard characters until the newline.
206          */
207         if (i == 255)
208                 while (buf[i] != '\n')
209                         serv_read(&buf[i], 1);
210
211         /* Strip all trailing nonprintables (crlf)
212          */
213         buf[i] = 0;
214         strip_trailing_nonprint(buf);
215 }
216
217
218 /*
219  * send line to server - implemented in terms of serv_write()
220  */
221 void serv_puts(char *buf)
222 {
223         /* printf("< %s\n", buf); */
224         serv_write(buf, strlen(buf));
225         serv_write("\n", 1);
226 }
227
228
229
230
231
232 void cleanup(int exitcode) {
233         char buf[1024];
234
235         serv_puts("QUIT");
236         serv_gets(buf);
237         fprintf(stderr, "%s\n", buf);
238         exit(exitcode);
239 }
240
241
242
243 int main(int argc, char **argv) {
244         char buf[1024];
245         char fromline[1024];
246         FILE *fp;
247
248         fp = tmpfile();
249         if (fp == NULL) return(errno);
250         sprintf(fromline, "From: someone@somewhere.org");
251         while (fgets(buf, 1024, stdin) != NULL) {
252                 fprintf(fp, "%s", buf);
253                 if (!strncasecmp(buf, "From:", 5)) strcpy(fromline, buf);
254         }
255         strip_trailing_nonprint(fromline);
256
257         sprintf(buf, "%d", SMTP_PORT);
258         serv_sock = connectsock("localhost", buf, "tcp");
259         serv_gets(buf);
260         fprintf(stderr, "%s\n", buf);
261         if (buf[0]!='2') cleanup(1);
262
263         serv_puts("HELO localhost");
264         serv_gets(buf);
265         fprintf(stderr, "%s\n", buf);
266         if (buf[0]!='2') cleanup(1);
267
268         sprintf(buf, "MAIL %s", fromline);
269         serv_puts(buf);
270         serv_gets(buf);
271         fprintf(stderr, "%s\n", buf);
272         if (buf[0]!='2') cleanup(1);
273
274         sprintf(buf, "RCPT To: %s", argv[1]);
275         serv_puts(buf);
276         serv_gets(buf);
277         fprintf(stderr, "%s\n", buf);
278         if (buf[0]!='2') cleanup(1);
279
280         serv_puts("DATA");
281         serv_gets(buf);
282         fprintf(stderr, "%s\n", buf);
283         if (buf[0]!='3') cleanup(1);
284
285         rewind(fp);
286         while (fgets(buf, sizeof buf, fp) != NULL) {
287                 strip_trailing_nonprint(buf);
288                 serv_puts(buf);
289         }
290         serv_puts(".");
291         serv_gets(buf);
292         fprintf(stderr, "%s\n", buf);
293         if (buf[0]!='2') cleanup(1);
294         else cleanup(0);
295         return(0);
296 }