]> mj.ucw.cz Git - umpf.git/blob - ham.c
started with filtering mail
[umpf.git] / ham.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sysexits.h>
5
6 #include <unistd.h>
7
8 #include "umpf.h"
9 #define MAIL_LEN 65536
10
11 int curbufsize;
12
13 char mbox_write_buf[BUFSIZE];
14 int mbox_write_pos;
15 int mbox_write_err;
16
17 static void
18 new_header(char* buf, struct list* h)
19 {
20         char* p;
21         struct hlist* new;
22
23         new = xmalloc(sizeof(struct hlist));
24
25         p = strchr(buf, ':');
26         if (!p)
27                 new->value = xstrdup("");
28         else {
29                 *p = 0;
30                 new->value = xstrdup(p+1);
31                 p = strrchr(new->value, '\n');
32                 if (p && !*(p+1))
33                         *p = 0;
34         }
35         new->name = xstrdup(buf);
36
37         list_add_last(h, &new->car);
38 }
39
40 struct list*
41 make_hlist(void)
42 {
43         struct list* l = xmalloc(sizeof(struct list));
44         char* buf;
45         int i = 0; /* current position */
46         int c, last = 0;
47
48         list_init(l);
49         buf = xmalloc(BUFSIZE);
50         curbufsize = BUFSIZE;
51         
52         while ((c = getchar()) != EOF){
53                 if (c == '\r')
54                         continue;
55
56                 if (i >= curbufsize-2)
57                         buf = xrealloc(buf, curbufsize *= 2);
58                 
59                 buf[i++] = c; 
60                 if (c == '\n'){
61                         if (last == '\n')
62                                 break;
63                         if ((c = getchar()) != ' ' && c != '\t'){
64                                 if (c != EOF)
65                                         ungetc(c, stdin);
66                                 buf[i] = 0;
67                                 new_header(buf, l);
68                                 i = 0;
69                         } else
70                                 buf[i++] = c;
71                 }
72                         last = c;
73         }
74         free_string(buf);
75         return l;
76 }
77
78 struct email*
79 get_body(void)
80 {
81         char* buf;
82         struct email* b;
83         int c, fd;
84         int will_save = 0;
85         int i = 0;
86         int curbufsize = MAIL_LEN;
87         char* tmpfile;
88         int res;
89         
90         buf = xmalloc(MAIL_LEN);
91         b = xmalloc(sizeof(struct email));
92         b->body = buf;
93         b->tmpfile = NULL;
94         b->fd = -1;
95
96         /* read mail body, if it is too big, try to make tmp file */
97         while ((c = getchar()) != EOF){
98                 buf[i++] = c;
99                 if (i >= curbufsize - 1) {
100                         tmpfile = xstrdup("/tmp/umpf.XXXXXX");
101                         fd = mkstemp(tmpfile);
102                         /* cannot create tmpfile, try to continue reading */
103                         if (fd < 0)
104                                 bye(EX_TEMPFAIL, "%m");
105                         else {
106                                 will_save = 1;
107                                 b->body = NULL;
108                                 b->tmpfile = tmpfile;
109                                 b->fd = fd;
110                                 res = write(fd, buf, MAIL_LEN);
111                                 if (res < MAIL_LEN) {
112                                         unlink(b->tmpfile);
113                                         bye(EX_TEMPFAIL, "%m");
114                                 }
115                                 break;  
116                         }
117                 }
118         }
119         b->body_len = i;
120         /* save rest of the body to the tmpfile */
121         if (will_save) {
122                 int j = 0;
123                 while ((c = getchar()) != EOF){
124                         buf[j++] = c;
125                         b->body_len++;
126                         if (j >= MAIL_LEN) {
127                                 j = 0;
128                                 res = write(fd, buf, MAIL_LEN);
129                                 if (res < MAIL_LEN) {
130                                         unlink(b->tmpfile);
131                                         bye(EX_TEMPFAIL, "%m");
132                                 }
133                         }
134                 }
135                 res = write(fd, buf, j);
136                 if (res < j) {
137                         unlink(b->tmpfile);
138                         bye(EX_TEMPFAIL, "%m");
139                 }
140         }
141         return b; 
142 }
143
144 void
145 print_headers(struct list* l)
146 {
147         struct hlist* p;
148
149         LIST_FOREACH(p,l)
150                 printf("%s:%s\n",p->name,p->value);
151 }
152
153 static void
154 flush_mbox_buffer(int fd)
155 {
156         if (mbox_write_err || !mbox_write_pos)
157                 return;
158
159         int res;
160         res = write(fd, mbox_write_buf, mbox_write_pos);
161         if (res < 0)
162                 mbox_write_err++;
163         mbox_write_pos = 0;
164 }
165
166 static void
167 write_char_to_mailbox(char c, int fd)
168 {
169         int res;
170
171         if (mbox_write_pos >= BUFSIZE){
172                 res = write(fd, mbox_write_buf, BUFSIZE);
173                 if (res < 0)
174                         mbox_write_err++;
175                 mbox_write_pos = 0;
176                 return;
177         }
178         mbox_write_buf[mbox_write_pos++] = c;
179 }
180
181 /* try to copy e-mail to mailbox, if it fails,
182  truncate mailbox to the original size */
183 static int
184 copy_email(int fd, struct email* email)
185 {
186         off_t mb_end;
187
188         mb_end = lseek(fd, 0, SEEK_END);
189         if (mb_end < 0)
190                 return -1;
191
192         /* init globals */
193         mbox_write_err = 0;
194         mbox_write_pos = 0;
195
196         /* headers */
197         struct hlist* ph;
198         char* pc;
199         LIST_FOREACH(ph, email->headers){
200                 for (pc = ph->name; *pc; pc++)
201                         write_char_to_mailbox(*pc, fd);
202                 write_char_to_mailbox(':', fd); 
203                 for (pc = ph->value; *pc; pc++)
204                         write_char_to_mailbox(*pc, fd); 
205                         write_char_to_mailbox('\n', fd);        
206         }
207
208         write_char_to_mailbox('\n', fd);
209         /* body */
210         /* FIXME: do not forget change Content-Length */
211         if (email->body) {
212                 for (pc = email->body; pc < email->body + email->body_len; pc++){
213                                 write_char_to_mailbox(*pc, fd);
214                                 if (*pc == '\n'){
215                                         if ((pc + 5 < email->body + email->body_len)
216                                                 && pc[1] == 'F' && pc[2] == 'r'
217                                                 && pc[3] == 'o' && pc[4] == 'm'
218                                                 && pc[5] == ' ')
219                                         write_char_to_mailbox('>', fd); 
220                                 }
221                 }
222         } else {
223                 int i;
224                 char buf[MAIL_LEN];
225                 for(i = 0; i < email->body_len; i+=MAIL_LEN) {
226                         int len = MAIL_LEN;
227                         if (i >= email->body_len)
228                                 len = len - (i - email->body_len);
229                         read(email->fd, buf, len); //FIXME: check it?
230                         for (pc = buf; pc < buf + len; pc++) {
231                                 write_char_to_mailbox(*pc, fd);
232                                 if (*pc == '\n'){
233                                         if ((pc + 5 < email->body + email->body_len)
234                                         && pc[1] == 'F' && pc[2] == 'r'
235                                         && pc[3] == 'o' && pc[4] == 'm'
236                                         && pc[5] == ' ')
237                                         write_char_to_mailbox('>', fd); 
238                                 }
239                         }
240                 }       
241         }
242
243         flush_mbox_buffer(fd);
244
245         /* errors? */
246         if (mbox_write_err){
247                 /* try to truncate to the original length */
248                 ftruncate(fd, mb_end);  
249                 return -1;
250         }       
251
252         return 0; 
253 }
254
255 int
256 write_email_to_fd(int fd, struct email* email)
257 {
258         int written; 
259         
260         /* headers */
261         struct hlist* ph;
262         LIST_FOREACH(ph, email->headers){
263                 written = write(fd, ph->name, strlen(ph->name));
264                 if (written < (ssize_t) strlen(ph->name))
265                         return 1;
266                 written = write(fd, ":", 1);
267                 if (written < 1)
268                         return 1;
269                 written = write(fd, ph->value, strlen(ph->value));
270                 if (written < (ssize_t) strlen(ph->value))
271                         return 1;
272                 written = write(fd, "\n", 1);
273                 if (written < 1)
274                         return 1;
275         }
276         written = write(fd, "\n", 1);
277         if (written < 1)
278                 return 1;
279
280         /* body */
281         if (email->body) {
282                 written = write(fd, email->body, email->body_len);
283                 if (written < email->body_len)
284                         return 1;
285         } else {
286                 char buf[MAIL_LEN];
287                 int i;
288                 for(i = 0; i < email->body_len; i+=MAIL_LEN) {
289                         int len = MAIL_LEN;
290                         if (i >= email->body_len)
291                                 len = len - (i - email->body_len);
292                         read(email->fd, buf, len); //FIXME: check it?
293                         write(fd, buf, len);
294                 }       
295         }
296
297         return 0;
298 }
299
300 int email_pos;
301 int headers_sent;
302 int email_opened;
303
304 void
305 open_email(void)
306 {
307         email_pos = 0;
308         headers_sent = 0;
309         email_opened = 1;
310 }
311
312 char*
313 read_email(struct email* em)
314 {
315         char* buf;
316         int r, pos;
317
318         if (! email_opened) {
319                 chars_written = 0;      
320                 return NULL;
321         }
322
323         if (! headers_sent) {
324                 struct hlist* ph;
325                 int curbufsize = BUFSIZE;
326                 buf = xmalloc(BUFSIZE);
327                 pos = 0;
328
329                 LIST_FOREACH(ph, em->headers){
330                         int needed = strlen(ph->name) + strlen(ph->value) + 3;
331                         if (curbufsize < pos + needed)
332                                 buf = xrealloc(buf, curbufsize*=2);
333                         strcpy(buf + pos, ph->name);
334                         pos += strlen(ph->name);
335                         buf[pos++] = ':';
336                         strcpy(buf + pos, ph->value);
337                         pos += strlen(ph->value);
338                         buf[pos++] = '\n';
339         
340                 }
341                 buf[pos] = '\n';
342                 headers_sent = 1;
343                 chars_written = pos;
344                 return buf;
345         }
346
347         if (em->body) {
348                 buf = xstrdup(em->body);
349                 chars_written = em->body_len; 
350                 //printf("%d: %s\n", em->body_len, em->body); 
351                 email_opened = 0;
352                 return buf;
353         }
354
355         if (! email_pos)
356                 lseek(em->fd, 0, SEEK_SET);
357
358         buf = xmalloc(MAIL_LEN + 1);
359         r = read(em->fd, buf, MAIL_LEN);
360         if (r < MAIL_LEN)
361                 email_opened = 0;
362         chars_written = r;
363         
364         return buf;
365 }
366
367 int
368 deliver_local_email(char* folder, struct email* email)
369 {
370         int res = -1;
371         int is_default_mailbox = 0;
372         int fd;
373
374         if (!strcmp(default_mailbox, folder))   
375                 is_default_mailbox = 1;
376
377         fd = open_mailbox(folder, is_default_mailbox);
378         if (fd < 0){
379                 if (is_default_mailbox)
380                         return res;
381                 else /* try to save to default mailbox instead */
382                         return deliver_local_email(default_mailbox, email);
383         }
384
385         res = copy_email(fd, email);
386         if (res < 0){
387
388                 /* try to deliver to the default mailbox */
389                 if (is_default_mailbox)
390                         return res;
391                 else
392                         return deliver_local_email(default_mailbox, email);
393         }
394
395         close_mailbox(fd, folder, is_default_mailbox);  
396
397         return res; 
398 }