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