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