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