]> mj.ucw.cz Git - umpf.git/blob - ham.c
714ebc4ae51d69340cec30e0be52eebae524d83c
[umpf.git] / ham.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include <unistd.h>
6
7 #include "umpf.h"
8 #define MAIL_LEN 65536
9
10 int curbufsize;
11
12 char mbox_write_buf[BUFSIZE];
13 int mbox_write_pos;
14 int mbox_write_err;
15
16 static void
17 new_header(char* buf, struct list* h)
18 {
19         char* p;
20         struct hlist* new;
21
22         new = xmalloc(sizeof(struct hlist));
23
24         p = strchr(buf, ':');
25         if (!p)
26                 new->value = xstrdup("");
27         else {
28                 *p = 0;
29                 new->value = xstrdup(p+1);
30                 p = strrchr(new->value, '\n');
31                 if (p && !*(p+1))
32                         *p = 0;
33         }
34         new->name = xstrdup(buf);
35
36         list_add_last(h, &new->car);
37 }
38
39 struct list*
40 make_hlist(void)
41 {
42         struct list* l = xmalloc(sizeof(struct list));
43         char* buf;
44         int i = 0; /* current position */
45         int c, last = 0;
46
47         list_init(l);
48         buf = xmalloc(BUFSIZE);
49         curbufsize = BUFSIZE;
50         
51         while ((c = getchar()) != EOF){
52                 if (c == '\r')
53                         continue;
54
55                 if (i >= curbufsize-2)
56                         buf = xrealloc(buf, curbufsize *= 2);
57                 
58                 buf[i++] = c; 
59                 if (c == '\n'){
60                         if (last == '\n')
61                                 break;
62                         if ((c = getchar()) != ' ' && c != '\t'){
63                                 if (c != EOF)
64                                         ungetc(c, stdin);
65                                 buf[i] = 0;
66                                 new_header(buf, l);
67                                 i = 0;
68                         } else
69                                 buf[i++] = c;
70                 }
71                         last = c;
72         }
73         free_string(buf);
74         return l;
75 }
76
77 struct email*
78 get_body(void)
79 {
80         char* buf;
81         struct email* b;
82         int c, fd, cannot_save = 0;
83         int will_save = 0;
84         int i = 0;
85         int j = 0;
86         int curbufsize = MAIL_LEN;
87         char* tmpfile;
88         int res;
89         
90         buf = xmalloc(MAIL_LEN);
91         b = xmalloc(sizeof(struct email));
92         b->body = buf;
93         b->tmpfile = NULL;
94         b->fd = -1;
95
96         /* read mail body, if it is too big, try to make tmp file */
97         while ((c = getchar()) != EOF){
98                 buf[i++] = c;
99                 if (i >= curbufsize - 1) {
100                         if (cannot_save) {
101                                 buf = xrealloc(buf, curbufsize *= 2);
102                                 continue;
103                         }
104                         tmpfile = xstrdup("/tmp/umpf.XXXXXX");
105                         fd = mkstemp(tmpfile);
106                         /* cannot create tmpfile, try to continue reading */
107                         if (fd < 0) {
108                                 buf = xrealloc(buf, curbufsize *= 2);
109                                 cannot_save = 1;
110                                 free_string(tmpfile);
111                                 continue;
112                         } else {
113                                 will_save = 1;
114                                 b->body = NULL;
115                                 b->tmpfile = tmpfile;
116                                 b->fd = fd;
117                                 break;  
118                         }
119                 }
120         }
121         b->body_len = i;
122         /* save rest of the body to the tmpfile */
123         if (will_save) {
124                 while ((c = getchar()) != EOF){
125                         buf[j++] = c;
126                         b->body_len++;
127                         if (j >= MAIL_LEN) {
128                                 j = 0;
129                                 res = write(fd, buf, MAIL_LEN);
130                                 if (res < MAIL_LEN) {
131                                         int missing = MAIL_LEN - res;
132                                         curbufsize = 2*b->body_len;
133                                         buf = xrealloc(buf, curbufsize);
134                                         // no point to check length here
135                                         read(fd, buf, b->body_len - missing);
136                                         unlink(b->tmpfile);
137                                         b->tmpfile = NULL;
138                                         b->fd = -1;
139                                         b->body = buf;
140                                         break;
141                                 }
142                         }
143                 }
144                 /* could not write all the body to the tmpfile, try to read
145                         the rest */
146                 if (b->body) {
147                         while ((c = getchar()) != EOF){
148                                 buf[j++] = c;
149                                 b->body_len++;
150                                 if (i >= curbufsize - 1) {
151                                         buf = xrealloc(buf, curbufsize *= 2);
152                                 }
153                         }
154                 }
155         }
156
157         return b; 
158 }
159
160 void
161 print_headers(struct list* l)
162 {
163         struct hlist* p;
164
165         LIST_FOREACH(p,l)
166                 printf("%s:%s\n",p->name,p->value);
167 }
168
169 static void
170 flush_mbox_buffer(int fd)
171 {
172         if (mbox_write_err || !mbox_write_pos)
173                 return;
174
175         int res;
176         res = write(fd, mbox_write_buf, mbox_write_pos);
177         if (res < 0)
178                 mbox_write_err++;
179         mbox_write_pos = 0;
180 }
181
182 static void
183 write_char_to_mailbox(char c, int fd)
184 {
185         int res;
186
187         if (mbox_write_pos >= BUFSIZE){
188                 res = write(fd, mbox_write_buf, BUFSIZE);
189                 if (res < 0)
190                         mbox_write_err++;
191                 mbox_write_pos = 0;
192                 return;
193         }
194         mbox_write_buf[mbox_write_pos++] = c;
195 }
196
197 /* try to copy e-mail to mailbox, if it fails,
198  truncate mailbox to the original size */
199 static int
200 copy_email(int fd, struct email* email)
201 {
202         off_t mb_end;
203
204         mb_end = lseek(fd, 0, SEEK_END);
205         if (mb_end < 0)
206                 return -1;
207
208         /* init globals */
209         mbox_write_err = 0;
210         mbox_write_pos = 0;
211
212         /* headers */
213         struct hlist* ph;
214         char* pc;
215         LIST_FOREACH(ph, email->headers){
216                 for (pc = ph->name; *pc; pc++)
217                         write_char_to_mailbox(*pc, fd);
218                 write_char_to_mailbox(':', fd); 
219                 write_char_to_mailbox(' ', fd); 
220                 for (pc = ph->value; *pc; pc++)
221                         write_char_to_mailbox(*pc, fd); 
222                         write_char_to_mailbox('\n', fd);        
223         }
224
225         write_char_to_mailbox('\n', fd);
226         /* body */
227         /* FIXME: do not forget change Content-Length */
228         if (email->body) {
229                 for (pc = email->body; pc < email->body + email->body_len; pc++){
230                                 write_char_to_mailbox(*pc, fd);
231                                 if (*pc == '\n'){
232                                         if ((pc + 5 < email->body + email->body_len)
233                                                 && pc[1] == 'F' && pc[2] == 'r'
234                                                 && pc[3] == 'o' && pc[4] == 'm'
235                                                 && pc[5] == ' ')
236                                         write_char_to_mailbox('>', fd); 
237                                 }
238                 }
239         } else {
240                 int i;
241                 char buf[MAIL_LEN];
242                 for(i = 0; i < email->body_len; i+=MAIL_LEN) {
243                         int len = MAIL_LEN;
244                         if (i >= email->body_len)
245                                 len = len - (i - email->body_len);
246                         read(email->fd, buf, len); //FIXME: check it?
247                         for (pc = buf; pc < buf + len; pc++) {
248                                 write_char_to_mailbox(*pc, fd);
249                                 if (*pc == '\n'){
250                                         if ((pc + 5 < email->body + email->body_len)
251                                         && pc[1] == 'F' && pc[2] == 'r'
252                                         && pc[3] == 'o' && pc[4] == 'm'
253                                         && pc[5] == ' ')
254                                         write_char_to_mailbox('>', fd); 
255                                 }
256                         }
257                 }       
258         }
259
260         flush_mbox_buffer(fd);
261
262         /* errors? */
263         if (mbox_write_err){
264                 /* try to truncate to the original length */
265                 ftruncate(fd, mb_end);  
266                 return -1;
267         }       
268
269         return 0; 
270 }
271
272 int
273 write_email_to_fd(int fd, struct email* email)
274 {
275         int written; 
276         
277         /* headers */
278         struct hlist* ph;
279         LIST_FOREACH(ph, email->headers){
280                 written = write(fd, ph->name, strlen(ph->name));
281                 if (written < strlen(ph->name))
282                         return 1;
283                 written = write(fd, ": ", 2);
284                 if (written < 2)
285                         return 1;
286                 written = write(fd, ph->value, strlen(ph->value));
287                 if (written < strlen(ph->value))
288                         return 1;
289                 written = write(fd, "\n", 2);
290                 if (written < 1)
291                         return 1;
292         }
293         written = write(fd, "\n", 2);
294         if (written < 1)
295                 return 1;
296
297         /* body */
298
299         if (email->body) {
300                 written = write(fd, email->body, email->body_len);
301                 if (written < email->body_len)
302                         return 1;
303         } else {
304                 char buf[MAIL_LEN];
305                 int i;
306                 for(i = 0; i < email->body_len; i+=MAIL_LEN) {
307                         int len = MAIL_LEN;
308                         if (i >= email->body_len)
309                                 len = len - (i - email->body_len);
310                         read(email->fd, buf, len); //FIXME: check it?
311                         write(fd, buf, len);
312                 }       
313         }
314
315         return 0;
316 }
317
318 int
319 deliver_local_email(char* folder, struct email* email)
320 {
321         int res = -1;
322         int is_default_mailbox = 0;
323         int fd;
324
325         if (!strcmp(default_mailbox, folder))   
326                 is_default_mailbox = 1;
327
328         fd = open_mailbox(folder, is_default_mailbox);
329         if (fd < 0){
330                 if (is_default_mailbox)
331                         return res;
332                 else /* try to save to default mailbox instead */
333                         return deliver_local_email(default_mailbox, email);
334         }
335
336         res = copy_email(fd, email);
337         if (res < 0){
338
339                 /* try to deliver to the default mailbox */
340                 if (is_default_mailbox)
341                         return res;
342                 else
343                         return deliver_local_email(default_mailbox, email);
344         }
345
346         close_mailbox(fd, folder, is_default_mailbox);  
347
348         return res; 
349 }