Mercurial > hg > mlmmj
comparison contrib/receivestrip/mlmmj-receive-strip.c @ 748:dfc9ab125fd4
Fix spelling of 'receive' and 'voodoo'; make mlmmj-recieve a symlink
author | Chris Webb |
---|---|
date | Sun, 03 Oct 2010 21:40:42 +1100 |
parents | contrib/recievestrip/mlmmj-recieve-strip.c@c334ffdf8807 |
children |
comparison
equal
deleted
inserted
replaced
747:fd77dd58bde1 | 748:dfc9ab125fd4 |
---|---|
1 /* Copyright (C) 2007 Sascha Sommer <ssommer at suse.de> | |
2 * | |
3 * Permission is hereby granted, free of charge, to any person obtaining a copy | |
4 * of this software and associated documentation files (the "Software"), to | |
5 * deal in the Software without restriction, including without limitation the | |
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
7 * sell copies of the Software, and to permit persons to whom the Software is | |
8 * furnished to do so, subject to the following conditions: | |
9 * | |
10 * The above copyright notice and this permission notice shall be included in | |
11 * all copies or substantial portions of the Software. | |
12 * | |
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
19 * IN THE SOFTWARE. | |
20 */ | |
21 | |
22 /* a version of mlmmj-receive that parses the mail on the fly and strips unwanted | |
23 mime parts | |
24 opens the files control/mimedeny and control/mimestrip for a list of mime | |
25 types for body parts that should be denied or stripped. | |
26 It adds an extra header X-ThisMailContainsUnwantedMimeParts: Y for mails that | |
27 contain disallowed mimeparts and X-ThisMailContainsUnwantedMimeParts: N otherwise | |
28 */ | |
29 | |
30 #include <stdlib.h> | |
31 #include <string.h> | |
32 #include <unistd.h> | |
33 #include <sys/stat.h> | |
34 #include <fcntl.h> | |
35 #include <stdio.h> | |
36 #include <errno.h> | |
37 #include <string.h> | |
38 #include <sys/types.h> | |
39 | |
40 | |
41 | |
42 #include "mlmmj.h" | |
43 #include "mygetline.h" | |
44 #include "gethdrline.h" | |
45 #include "strgen.h" | |
46 #include "chomp.h" | |
47 #include "ctrlvalue.h" | |
48 #include "ctrlvalues.h" | |
49 | |
50 #include "log_error.h" | |
51 #include "wrappers.h" | |
52 #include "memory.h" | |
53 | |
54 #define UNWANTED_MIME_HDR "X-ThisMailContainsUnwantedMimeParts: N\n" | |
55 | |
56 /* append a copy of a string to a string list */ | |
57 static void append_strtolist(struct strlist *list, char* str) { | |
58 list->count++; | |
59 list->strs = myrealloc(list->strs, | |
60 sizeof(char *) * (list->count + 1)); | |
61 list->strs[list->count-1] = mystrdup(str); | |
62 list->strs[list->count] = NULL; | |
63 } | |
64 | |
65 /* free all strings in a strlist */ | |
66 static void free_strlist(struct strlist *list) { | |
67 if(!list) | |
68 return; | |
69 if(list->strs) { | |
70 int i; | |
71 for(i=0;i < list->count;i++) | |
72 myfree(list->strs[i]); | |
73 myfree(list->strs); | |
74 } | |
75 list->strs = NULL; | |
76 list->count = 0; | |
77 } | |
78 | |
79 static int findit(char *line, char **headers) | |
80 { | |
81 int i = 0; | |
82 size_t len; | |
83 | |
84 if (!headers) | |
85 return 0; | |
86 | |
87 while(headers[i]) { | |
88 len = strlen(headers[i]); | |
89 if(strncasecmp(line, headers[i], len) == 0) | |
90 return 1; | |
91 i++; | |
92 } | |
93 | |
94 return 0; | |
95 } | |
96 | |
97 /* extract mime_type and boundary from the Content-Type header | |
98 * allocates a string for the mime_type if one is found | |
99 * always allocates a boundarie (using "--" when none is found) | |
100 * the caller needs to free the allocated strings | |
101 */ | |
102 static void extract_boundary(struct strlist *allhdrs, char** mime_type, char** boundary) | |
103 { | |
104 int x; | |
105 *boundary = NULL; | |
106 *mime_type = NULL; | |
107 for( x = 0 ; x < allhdrs->count ; x++ ){ | |
108 char* hdr = allhdrs->strs[x]; | |
109 if(hdr && !strncasecmp(hdr,"Content-Type:",13)){ | |
110 char* pos = hdr + 13; | |
111 size_t len = 0; | |
112 | |
113 /* find the start of the mimetype */ | |
114 while(*pos && (*pos == ' ' || *pos == '\t')) | |
115 ++pos; | |
116 | |
117 if(*pos == '"'){ /* handle quoted mime types */ | |
118 ++pos; | |
119 while(pos[len] && pos[len] != '"') | |
120 ++len; | |
121 }else{ | |
122 while(pos[len] && pos[len] != ' ' && pos[len] != '\t' && pos[len] != ';') | |
123 ++len; | |
124 } | |
125 | |
126 /* extract mime type if any */ | |
127 if(len){ | |
128 *mime_type = mymalloc(len+1); | |
129 strncpy(*mime_type,pos,len); | |
130 (*mime_type)[len] = '\0'; | |
131 } | |
132 | |
133 pos += len; | |
134 len = 0; | |
135 /* find start of the boundary info */ | |
136 while(*pos && strncasecmp(pos,"boundary=",9)) | |
137 ++pos; | |
138 if(*pos == '\0') /* no boundary */ | |
139 break; | |
140 | |
141 pos += 9; | |
142 if(*pos == '"'){ /* quoted boundary */ | |
143 ++pos; | |
144 while(pos[len] && pos[len] != '"') | |
145 ++len; | |
146 }else{ /* unquoted boundary */ | |
147 while(pos[len] && pos[len] != ' ' && pos[len] != '\t' && pos[len] != ';') | |
148 ++len; | |
149 } | |
150 | |
151 /* extract boundary */ | |
152 *boundary = mymalloc(len + 3); | |
153 strcpy(*boundary,"--"); | |
154 strncat(*boundary,pos,len); | |
155 break; | |
156 } | |
157 } | |
158 } | |
159 | |
160 /* read all mail headers and save them in a strlist | |
161 * check what to do with parts that contain the given mime_type | |
162 *return values | |
163 * 0: ok | |
164 * 1: strip | |
165 * sets deny to 1 if the entire mail should be denied | |
166 */ | |
167 #define MIME_OK 0 | |
168 #define MIME_STRIP 1 | |
169 static int read_hdrs(int fd, struct strlist *allhdrs,struct strlist* delmime,struct strlist* denymime,int* deny,char** boundary) { | |
170 int result = MIME_OK; | |
171 char* mime_type = NULL; | |
172 allhdrs->strs = NULL; | |
173 allhdrs->count = 0; | |
174 /* read headers */ | |
175 while(1) { | |
176 char* line = mygetline(fd); | |
177 if(!line) /* end of file and also end of headers */ | |
178 break; | |
179 | |
180 /* end of headers */ | |
181 if(line[0] == '\n'){ | |
182 myfree(line); | |
183 break; | |
184 } | |
185 if(!allhdrs->count || ((line[0] != '\t') && (line[0] != ' '))) /* first header line or no more unfolding */ | |
186 append_strtolist(allhdrs,line); | |
187 else{ | |
188 char* tmp = concatstr(2, allhdrs->strs[allhdrs->count-1], line); | |
189 myfree(allhdrs->strs[allhdrs->count-1]); | |
190 allhdrs->strs[allhdrs->count-1] = tmp; | |
191 } | |
192 myfree(line); | |
193 } | |
194 extract_boundary(allhdrs,&mime_type,boundary); | |
195 if(mime_type) { | |
196 /* check if this part should be stripped */ | |
197 if(delmime && findit(mime_type, delmime->strs)) | |
198 result = MIME_STRIP; | |
199 /* check if the mail should be denied */ | |
200 if(denymime && findit(mime_type, denymime->strs)) | |
201 *deny = 1; | |
202 myfree(mime_type); | |
203 } | |
204 return result; | |
205 } | |
206 | |
207 /* writes the mail headers if unwantedmime_hdrpos is not NULL an UNWANTED_MIME_HDR | |
208 * is inserted and its position saved in unwantedmime_hdrpos | |
209 * returns 0 on success | |
210 */ | |
211 static int write_hdrs(int outfd,struct strlist* hdrs,off_t* unwantedmime_hdrpos) { | |
212 int i; | |
213 for(i = 0; i < hdrs->count ; i++) { | |
214 if(writen(outfd, hdrs->strs[i], strlen(hdrs->strs[i])) < 0){ | |
215 log_error(LOG_ARGS, "Error when dumping headers"); | |
216 return -1; | |
217 } | |
218 } | |
219 | |
220 /* if this is not the header of an embedded part add the header that will | |
221 indicate if the mail contains unwanted mime parts */ | |
222 if(unwantedmime_hdrpos) { | |
223 if(writen(outfd, UNWANTED_MIME_HDR,strlen(UNWANTED_MIME_HDR)) < 0){ | |
224 log_error(LOG_ARGS, "Error writting unwanted mime header"); | |
225 return -1; | |
226 } | |
227 /* get the current position so that we can update the header later */ | |
228 *unwantedmime_hdrpos = lseek(outfd,0,SEEK_CUR); | |
229 if(*unwantedmime_hdrpos < 2){ | |
230 log_error(LOG_ARGS, "Error getting file position"); | |
231 return -1; | |
232 } | |
233 *unwantedmime_hdrpos -= 2; | |
234 } | |
235 | |
236 /* write a single line feed to terminate the header part */ | |
237 if(writen(outfd, "\n", 1) < 0) { | |
238 log_error(LOG_ARGS,"Error writting end of hdrs."); | |
239 return -1; | |
240 } | |
241 return 0; | |
242 } | |
243 | |
244 /* set the unwanted mime_hdr to Y */ | |
245 static int update_unwantedmime_hdr(int outfd,off_t unwantedmime_hdrpos) { | |
246 /* seek to the header position */ | |
247 if(lseek(outfd,unwantedmime_hdrpos,SEEK_SET) < 0) { | |
248 log_error(LOG_ARGS,"Error seeking to the unwantedmime_hdr"); | |
249 return -1; | |
250 } | |
251 | |
252 /* update the header */ | |
253 if(writen(outfd, "Y\n",2) < 0){ | |
254 log_error(LOG_ARGS, "Error writting extra header"); | |
255 return -1; | |
256 } | |
257 | |
258 /* seek back to the end of the mail */ | |
259 if(lseek(outfd,0,SEEK_END) < 0) { | |
260 log_error(LOG_ARGS,"Error seeking to the mail end"); | |
261 return -1; | |
262 } | |
263 return 0; | |
264 } | |
265 | |
266 static int parse_body(int infd,int outfd, struct strlist* delmime, struct strlist* denymime, | |
267 int* deny,char* boundary){ | |
268 int strip = 0; | |
269 char* line; | |
270 while((line = mygetline(infd))) { | |
271 if(boundary && !strncmp(line,boundary,strlen(boundary))){ | |
272 strip = 0; | |
273 /* check if the boundary is the beginning of a new part */ | |
274 if(strncmp(line + strlen(boundary),"--",2)){ | |
275 struct strlist hdrs; | |
276 char* new_boundary = NULL; | |
277 /* check if this part should be stripped */ | |
278 if(read_hdrs(infd, &hdrs,delmime,denymime,deny,&new_boundary) == MIME_STRIP) | |
279 strip = 1; | |
280 else { | |
281 /* write boundary */ | |
282 if(writen(outfd, line, strlen(line)) < 0){ | |
283 log_error(LOG_ARGS, "Error writting boundary"); | |
284 return -1; | |
285 } | |
286 /* write hdr */ | |
287 if(write_hdrs(outfd, &hdrs, NULL) < 0){ | |
288 log_error(LOG_ARGS, "Error writting hdrs"); | |
289 return -1; | |
290 } | |
291 /* parse embedded part if a new boundary was found */ | |
292 if(new_boundary && parse_body(infd,outfd,delmime,denymime,deny,new_boundary) != 0) { | |
293 log_error(LOG_ARGS, "Could not parse embedded part"); | |
294 return -1; | |
295 } | |
296 } | |
297 free_strlist(&hdrs); | |
298 if(new_boundary) | |
299 myfree(new_boundary); | |
300 }else{ | |
301 /* write end of part */ | |
302 if(writen(outfd, line, strlen(line)) < 0){ | |
303 log_error(LOG_ARGS, "Error writting hdrs"); | |
304 return -1; | |
305 } | |
306 /* and leave */ | |
307 myfree(line); | |
308 break; | |
309 } | |
310 }else { | |
311 if(!strip) { /* write the current line */ | |
312 if(writen(outfd, line, strlen(line)) < 0){ | |
313 log_error(LOG_ARGS, "Error when dumping line"); | |
314 return -1; | |
315 } | |
316 } | |
317 } | |
318 myfree(line); | |
319 } | |
320 return 0; | |
321 } | |
322 | |
323 | |
324 | |
325 | |
326 | |
327 /* read a mail stripping unwanted parts */ | |
328 static int dump_mail(int infd, int outfd,char* listdir) { | |
329 struct strlist hdrs; | |
330 struct strlist* delmime; | |
331 struct strlist* denymime; | |
332 char* boundary=NULL; | |
333 int deny = 0; | |
334 int result; | |
335 off_t unwantedmime_hdr_pos = 0; | |
336 | |
337 /* get list control values */ | |
338 delmime = ctrlvalues(listdir, "mimestrip"); | |
339 denymime = ctrlvalues(listdir, "mimedeny"); | |
340 | |
341 /* read mail header */ | |
342 result = read_hdrs(infd, &hdrs,delmime,denymime,&deny,&boundary); | |
343 /* write mail header */ | |
344 if(write_hdrs(outfd,&hdrs,&unwantedmime_hdr_pos) < 0) { | |
345 log_error(LOG_ARGS, "Could not write mail headers"); | |
346 return -1; | |
347 } | |
348 | |
349 /* free mail header */ | |
350 free_strlist(&hdrs); | |
351 | |
352 if(result == MIME_OK && !deny) { | |
353 /* try to parse the mail */ | |
354 if(parse_body(infd,outfd,delmime,denymime,&deny,boundary) != 0) { | |
355 log_error(LOG_ARGS, "Could not parse mail"); | |
356 return -1; | |
357 } | |
358 myfree(boundary); | |
359 }else | |
360 deny = 1; | |
361 | |
362 /* dump rest of mail */ | |
363 if(dumpfd2fd(infd, outfd) != 0) { | |
364 log_error(LOG_ARGS, "Could not receive mail"); | |
365 return -1; | |
366 } | |
367 | |
368 /* update header */ | |
369 if(deny) { | |
370 if(update_unwantedmime_hdr(outfd,unwantedmime_hdr_pos) != 0) { | |
371 log_error(LOG_ARGS, "Could not update header"); | |
372 return -1; | |
373 } | |
374 } | |
375 | |
376 /* free mime types */ | |
377 if(delmime) { | |
378 free_strlist(delmime); | |
379 myfree(delmime); | |
380 } | |
381 if(denymime) { | |
382 free_strlist(denymime); | |
383 myfree(denymime); | |
384 } | |
385 return 0; | |
386 } | |
387 | |
388 static void print_help(const char *prg) | |
389 { | |
390 printf("Usage: %s -L /path/to/listdir [-h] [-V] [-P] [-F]\n" | |
391 " -h: This help\n" | |
392 " -F: Don't fork in the background\n" | |
393 " -L: Full path to list directory\n" | |
394 " -P: Don't execute mlmmj-process\n" | |
395 " -V: Print version\n", prg); | |
396 exit(EXIT_SUCCESS); | |
397 } | |
398 | |
399 int main(int argc, char **argv) | |
400 { | |
401 char *infilename = NULL, *listdir = NULL; | |
402 char *randomstr = random_str(); | |
403 char *mlmmjprocess, *bindir; | |
404 int fd, opt, noprocess = 0, nofork = 0; | |
405 struct stat st; | |
406 uid_t uid; | |
407 pid_t childpid; | |
408 | |
409 CHECKFULLPATH(argv[0]); | |
410 | |
411 log_set_name(argv[0]); | |
412 | |
413 bindir = mydirname(argv[0]); | |
414 mlmmjprocess = concatstr(2, bindir, "/mlmmj-process"); | |
415 myfree(bindir); | |
416 | |
417 while ((opt = getopt(argc, argv, "hPVL:F")) != -1) { | |
418 switch(opt) { | |
419 case 'h': | |
420 print_help(argv[0]); | |
421 break; | |
422 case 'L': | |
423 listdir = optarg; | |
424 break; | |
425 case 'P': | |
426 noprocess = 1; | |
427 break; | |
428 case 'F': | |
429 nofork = 1; | |
430 break; | |
431 case 'V': | |
432 print_version(argv[0]); | |
433 exit(0); | |
434 } | |
435 } | |
436 | |
437 if(listdir == NULL) { | |
438 fprintf(stderr, "You have to specify -L\n"); | |
439 fprintf(stderr, "%s -h for help\n", argv[0]); | |
440 exit(EXIT_FAILURE); | |
441 } | |
442 | |
443 /* Lets make sure no random user tries to send mail to the list */ | |
444 if(listdir) { | |
445 if(stat(listdir, &st) == 0) { | |
446 uid = getuid(); | |
447 if(uid && uid != st.st_uid) { | |
448 log_error(LOG_ARGS, | |
449 "Have to invoke either as root " | |
450 "or as the user owning listdir " | |
451 "Invoked with uid = [%d]", (int)uid); | |
452 writen(STDERR_FILENO, | |
453 "Have to invoke either as root " | |
454 "or as the user owning listdir\n", 60); | |
455 exit(EXIT_FAILURE); | |
456 } | |
457 } else { | |
458 log_error(LOG_ARGS, "Could not stat %s", listdir); | |
459 exit(EXIT_FAILURE); | |
460 } | |
461 } | |
462 | |
463 infilename = concatstr(3, listdir, "/incoming/", randomstr); | |
464 myfree(randomstr); | |
465 fd = open(infilename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); | |
466 while(fd < 0 && errno == EEXIST) { | |
467 myfree(infilename); | |
468 randomstr = random_str(); | |
469 infilename = concatstr(3, listdir, "/incoming/", randomstr); | |
470 myfree(randomstr); | |
471 fd = open(infilename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); | |
472 } | |
473 | |
474 if(fd < 0) { | |
475 log_error(LOG_ARGS, "could not create mail file in " | |
476 "%s/incoming directory", listdir); | |
477 myfree(infilename); | |
478 exit(EXIT_FAILURE); | |
479 } | |
480 | |
481 if(dump_mail(fileno(stdin), fd, listdir) != 0) { | |
482 log_error(LOG_ARGS, "Could not receive mail"); | |
483 exit(EXIT_FAILURE); | |
484 } | |
485 | |
486 #if 0 | |
487 log_oper(listdir, OPLOGFNAME, "mlmmj-receive got %s", infilename); | |
488 #endif | |
489 fsync(fd); | |
490 close(fd); | |
491 | |
492 if(noprocess) { | |
493 myfree(infilename); | |
494 exit(EXIT_SUCCESS); | |
495 } | |
496 | |
497 /* | |
498 * Now we fork so we can exit with success since it could potentially | |
499 * take a long time for mlmmj-send to finish delivering the mails and | |
500 * returning, making it susceptible to getting a SIGKILL from the | |
501 * mailserver invoking mlmmj-receive. | |
502 */ | |
503 if (!nofork) { | |
504 childpid = fork(); | |
505 if(childpid < 0) | |
506 log_error(LOG_ARGS, "fork() failed! Proceeding anyway"); | |
507 | |
508 if(childpid) | |
509 exit(EXIT_SUCCESS); /* Parent says: "bye bye kids!"*/ | |
510 | |
511 close(0); | |
512 close(1); | |
513 close(2); | |
514 } | |
515 | |
516 execlp(mlmmjprocess, mlmmjprocess, | |
517 "-L", listdir, | |
518 "-m", infilename, NULL); | |
519 log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjprocess); | |
520 | |
521 exit(EXIT_FAILURE); | |
522 } | |
523 |