centralized filename calculation
[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 "config.h"
30 #include "citadel_dirs.h"
31
32 /* #define DEBUG  */    /* uncomment to get protocol traces */
33
34 int serv_sock;
35
36
37 void strip_trailing_nonprint(char *buf)
38 {
39         while ( (strlen(buf)>0) && (!isprint(buf[strlen(buf) - 1])) )
40                 buf[strlen(buf) - 1] = 0;
41 }
42
43
44 void timeout(int signum)
45 {
46         exit(signum);
47 }
48
49
50 int uds_connectsock(char *sockpath)
51 {
52         int s;
53         struct sockaddr_un addr;
54
55         memset(&addr, 0, sizeof(addr));
56         addr.sun_family = AF_UNIX;
57         strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
58
59         s = socket(AF_UNIX, SOCK_STREAM, 0);
60         if (s < 0) {
61                 fprintf(stderr, "Can't create socket: %s\n",
62                         strerror(errno));
63                 exit(3);
64         }
65
66         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
67                 fprintf(stderr, "can't connect: %s\n",
68                         strerror(errno));
69                 close(s);
70                 exit(3);
71         }
72
73         return s;
74 }
75
76
77 /*
78  * input binary data from socket
79  */
80 void serv_read(char *buf, int bytes)
81 {
82         int len, rlen;
83
84         len = 0;
85         while (len < bytes) {
86                 rlen = read(serv_sock, &buf[len], bytes - len);
87                 if (rlen < 1) {
88                         return;
89                 }
90                 len = len + rlen;
91         }
92 }
93
94
95 /*
96  * send binary to server
97  */
98 void serv_write(char *buf, int nbytes)
99 {
100         int bytes_written = 0;
101         int retval;
102         while (bytes_written < nbytes) {
103                 retval = write(serv_sock, &buf[bytes_written],
104                                nbytes - bytes_written);
105                 if (retval < 1) {
106                         return;
107                 }
108                 bytes_written = bytes_written + retval;
109         }
110 }
111
112
113
114 /*
115  * input string from socket - implemented in terms of serv_read()
116  */
117 void serv_gets(char *buf)
118 {
119         int i;
120
121         /* Read one character at a time.
122          */
123         for (i = 0;; i++) {
124                 serv_read(&buf[i], 1);
125                 if (buf[i] == '\n' || i == (SIZ-1))
126                         break;
127         }
128
129         /* If we got a long line, discard characters until the newline.
130          */
131         if (i == (SIZ-1))
132                 while (buf[i] != '\n')
133                         serv_read(&buf[i], 1);
134
135         /* Strip all trailing nonprintables (crlf)
136          */
137         buf[i] = 0;
138         strip_trailing_nonprint(buf);
139 #ifdef DEBUG
140         printf("> %s\n", buf);
141 #endif
142 }
143
144
145 /*
146  * send line to server - implemented in terms of serv_write()
147  */
148 void serv_puts(char *buf)
149 {
150 #ifdef DEBUG
151         printf("< %s\n", buf);
152 #endif
153         serv_write(buf, strlen(buf));
154         serv_write("\n", 1);
155 }
156
157
158
159
160
161 void cleanup(int exitcode) {
162         char buf[1024];
163
164         serv_puts("QUIT");
165         serv_gets(buf);
166         exit(exitcode);
167 }
168
169
170
171 int main(int argc, char **argv) {
172         char buf[1024];
173         char fromline[1024];
174         FILE *fp;
175         int i;
176         struct passwd *pw;
177         int from_header = 0;
178         int in_body = 0;
179         int relh=0;
180         int home=0;
181         char relhome[PATH_MAX]="";
182         char ctdldir[PATH_MAX]=CTDLDIR;
183
184         /* TODO: should we be able to calculate relative dirs? */
185         calc_dirs_n_files(relh, home, relhome, ctdldir);
186
187         get_config();
188
189         pw = getpwuid(getuid());
190
191         fp = tmpfile();
192         if (fp == NULL) return(errno);
193         snprintf(fromline, sizeof fromline, "From: %s@%s",
194                 pw->pw_name,
195                 config.c_fqdn
196         );
197         while (fgets(buf, 1024, stdin) != NULL) {
198                 if ( ( (buf[0] == 13) || (buf[0] == 10)) && (in_body == 0) ) {
199                         in_body = 1;
200                         if (from_header == 0) {
201                                 fprintf(fp, "%s%s", fromline, buf);
202                         }
203                 }
204                 if (!strncasecmp(buf, "From:", 5)) {
205                         strcpy(fromline, buf);
206                         if (in_body == 0) {
207                                 from_header = 1;
208                         }
209                 }
210                 fprintf(fp, "%s", buf);
211         }
212         strip_trailing_nonprint(fromline);
213
214         serv_sock = uds_connectsock(file_lmtp_socket);
215         serv_gets(buf);
216         if (buf[0]!='2') cleanup(1);
217
218         serv_puts("LHLO x");
219         do {
220                 serv_gets(buf);
221                 strcat(buf, "    ");
222         } while (buf[3] == '-');
223         if (buf[0] != '2') cleanup(1);
224
225         snprintf(buf, sizeof buf, "MAIL %s", fromline);
226         serv_puts(buf);
227         serv_gets(buf);
228         if (buf[0]!='2') cleanup(1);
229
230         for (i=1; i<argc; ++i) {
231                 if (argv[i][0] != '-') {
232                         snprintf(buf, sizeof buf, "RCPT To: %s", argv[i]);
233                         serv_puts(buf);
234                         serv_gets(buf);
235                         /* if (buf[0]!='2') cleanup(1); */
236                 }
237         }
238
239         serv_puts("DATA");
240         serv_gets(buf);
241         if (buf[0]!='3') cleanup(1);
242
243         rewind(fp);
244         while (fgets(buf, sizeof buf, fp) != NULL) {
245                 strip_trailing_nonprint(buf);
246                 serv_puts(buf);
247         }
248         serv_puts(".");
249         serv_gets(buf);
250         if (buf[0]!='2') cleanup(1);
251         else cleanup(0);
252         return(0);
253 }