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