]> mj.ucw.cz Git - umpf.git/blob - ham.c
fix many little bugs, release 0.1
[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 temporary 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, to_write;
330
331         open_email();
332         for (;;) {
333                 buf = read_email(email);
334                 if (!chars_written)
335                         break;
336
337                 to_write = chars_written;
338                 while (to_write) {
339                         wr = write(fd, buf, to_write);
340                         to_write -= wr;
341                 }
342         };
343         
344         return 0;
345 }
346
347 /* try to copy e-mail to mailbox, if it fails,
348  truncate mailbox to the original size */
349 static int
350 copy_email(int fd, struct email* email)
351 {
352         off_t mb_end;
353
354         mb_end = lseek(fd, 0, SEEK_END);
355         if (mb_end < 0)
356                 return -1;
357
358         /* init globals */
359         mbox_write_err = 0;
360         mbox_write_pos = 0;
361
362         /* From line */
363         struct hlist* ph;
364         int i, len;
365         time_t t;
366         time(&t);
367         char* date = ctime(&t);
368         int datelen = strlen(date);
369
370         if (! fromline) {       
371                 len = 5 + datelen + 1; 
372                 fromline = xmalloc(len);
373                 sprintf(fromline, "From %s", date);
374                 len = strlen(fromline);
375         } else 
376                 len = strlen(fromline);
377         for (i = 0; i < len; i++)
378                 write_char_to_mailbox(fromline[i], fd);
379         
380         /* headers */
381         char* pc;
382         LIST_FOREACH(ph, email->headers){
383                 for (pc = ph->name; *pc; pc++)
384                         write_char_to_mailbox(*pc, fd);
385                 write_char_to_mailbox(':', fd); 
386                 write_char_to_mailbox(' ', fd); 
387                 for (pc = ph->value; *pc; pc++)
388                         write_char_to_mailbox(*pc, fd); 
389                 write_char_to_mailbox('\n', fd);        
390         }
391
392         write_char_to_mailbox('\n', fd);
393         if (email->body) {
394                 for (pc = email->body; pc < email->body + email->body_len; pc++){
395                                 write_char_to_mailbox(*pc, fd);
396                                 if (*pc == '\n'){
397                                         if ((pc + 5 < email->body + email->body_len)
398                                                 && pc[1] == 'F' && pc[2] == 'r'
399                                                 && pc[3] == 'o' && pc[4] == 'm'
400                                                 && pc[5] == ' ')
401                                         write_char_to_mailbox('>', fd); 
402                                 }
403                 }
404         } else {
405                 int i;
406                 char buf[MAIL_LEN];
407                 for(i = 0; i < email->body_len; i+=MAIL_LEN) {
408                         int len = MAIL_LEN;
409                         if (i >= email->body_len)
410                                 len = len - (i - email->body_len);
411                         read(email->fd, buf, len); //FIXME: check it?
412                         for (pc = buf; pc < buf + len; pc++) {
413                                 write_char_to_mailbox(*pc, fd);
414                                 if (*pc == '\n'){
415                                         if ((pc + 5 < email->body + email->body_len)
416                                         && pc[1] == 'F' && pc[2] == 'r'
417                                         && pc[3] == 'o' && pc[4] == 'm'
418                                         && pc[5] == ' ')
419                                         write_char_to_mailbox('>', fd); 
420                                 }
421                         }
422                 }       
423         }
424
425         flush_mbox_buffer(fd);
426
427         /* errors? */
428         if (mbox_write_err){
429                 /* try to truncate to the original length */
430                 ftruncate(fd, mb_end);  
431                 return -1;
432         }       
433
434         return 0; 
435 }
436
437 int
438 deliver_local_email(char* folder, struct email* email)
439 {
440         int res = -1;
441         int is_default_mailbox = 0;
442         int fd;
443
444         if (!strcmp(default_mailbox, folder))   
445                 is_default_mailbox = 1;
446
447         fd = open_mailbox(folder, is_default_mailbox);
448         if (fd < 0){
449                 if (is_default_mailbox)
450                         return res;
451                 else /* try to save to default mailbox instead */
452                         return deliver_local_email(default_mailbox, email);
453         }
454
455         res = copy_email(fd, email);
456         if (res < 0){
457
458                 /* try to deliver to the default mailbox */
459                 if (is_default_mailbox)
460                         return res;
461                 else
462                         return deliver_local_email(default_mailbox, email);
463         }
464
465         close_mailbox(fd, folder, is_default_mailbox);  
466
467         return res; 
468 }