Mercurial > hg > mlmmj
view 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 |
line wrap: on
line source
/* Copyright (C) 2007 Sascha Sommer <ssommer at suse.de> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* a version of mlmmj-receive that parses the mail on the fly and strips unwanted mime parts opens the files control/mimedeny and control/mimestrip for a list of mime types for body parts that should be denied or stripped. It adds an extra header X-ThisMailContainsUnwantedMimeParts: Y for mails that contain disallowed mimeparts and X-ThisMailContainsUnwantedMimeParts: N otherwise */ #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include "mlmmj.h" #include "mygetline.h" #include "gethdrline.h" #include "strgen.h" #include "chomp.h" #include "ctrlvalue.h" #include "ctrlvalues.h" #include "log_error.h" #include "wrappers.h" #include "memory.h" #define UNWANTED_MIME_HDR "X-ThisMailContainsUnwantedMimeParts: N\n" /* append a copy of a string to a string list */ static void append_strtolist(struct strlist *list, char* str) { list->count++; list->strs = myrealloc(list->strs, sizeof(char *) * (list->count + 1)); list->strs[list->count-1] = mystrdup(str); list->strs[list->count] = NULL; } /* free all strings in a strlist */ static void free_strlist(struct strlist *list) { if(!list) return; if(list->strs) { int i; for(i=0;i < list->count;i++) myfree(list->strs[i]); myfree(list->strs); } list->strs = NULL; list->count = 0; } static int findit(char *line, char **headers) { int i = 0; size_t len; if (!headers) return 0; while(headers[i]) { len = strlen(headers[i]); if(strncasecmp(line, headers[i], len) == 0) return 1; i++; } return 0; } /* extract mime_type and boundary from the Content-Type header * allocates a string for the mime_type if one is found * always allocates a boundarie (using "--" when none is found) * the caller needs to free the allocated strings */ static void extract_boundary(struct strlist *allhdrs, char** mime_type, char** boundary) { int x; *boundary = NULL; *mime_type = NULL; for( x = 0 ; x < allhdrs->count ; x++ ){ char* hdr = allhdrs->strs[x]; if(hdr && !strncasecmp(hdr,"Content-Type:",13)){ char* pos = hdr + 13; size_t len = 0; /* find the start of the mimetype */ while(*pos && (*pos == ' ' || *pos == '\t')) ++pos; if(*pos == '"'){ /* handle quoted mime types */ ++pos; while(pos[len] && pos[len] != '"') ++len; }else{ while(pos[len] && pos[len] != ' ' && pos[len] != '\t' && pos[len] != ';') ++len; } /* extract mime type if any */ if(len){ *mime_type = mymalloc(len+1); strncpy(*mime_type,pos,len); (*mime_type)[len] = '\0'; } pos += len; len = 0; /* find start of the boundary info */ while(*pos && strncasecmp(pos,"boundary=",9)) ++pos; if(*pos == '\0') /* no boundary */ break; pos += 9; if(*pos == '"'){ /* quoted boundary */ ++pos; while(pos[len] && pos[len] != '"') ++len; }else{ /* unquoted boundary */ while(pos[len] && pos[len] != ' ' && pos[len] != '\t' && pos[len] != ';') ++len; } /* extract boundary */ *boundary = mymalloc(len + 3); strcpy(*boundary,"--"); strncat(*boundary,pos,len); break; } } } /* read all mail headers and save them in a strlist * check what to do with parts that contain the given mime_type *return values * 0: ok * 1: strip * sets deny to 1 if the entire mail should be denied */ #define MIME_OK 0 #define MIME_STRIP 1 static int read_hdrs(int fd, struct strlist *allhdrs,struct strlist* delmime,struct strlist* denymime,int* deny,char** boundary) { int result = MIME_OK; char* mime_type = NULL; allhdrs->strs = NULL; allhdrs->count = 0; /* read headers */ while(1) { char* line = mygetline(fd); if(!line) /* end of file and also end of headers */ break; /* end of headers */ if(line[0] == '\n'){ myfree(line); break; } if(!allhdrs->count || ((line[0] != '\t') && (line[0] != ' '))) /* first header line or no more unfolding */ append_strtolist(allhdrs,line); else{ char* tmp = concatstr(2, allhdrs->strs[allhdrs->count-1], line); myfree(allhdrs->strs[allhdrs->count-1]); allhdrs->strs[allhdrs->count-1] = tmp; } myfree(line); } extract_boundary(allhdrs,&mime_type,boundary); if(mime_type) { /* check if this part should be stripped */ if(delmime && findit(mime_type, delmime->strs)) result = MIME_STRIP; /* check if the mail should be denied */ if(denymime && findit(mime_type, denymime->strs)) *deny = 1; myfree(mime_type); } return result; } /* writes the mail headers if unwantedmime_hdrpos is not NULL an UNWANTED_MIME_HDR * is inserted and its position saved in unwantedmime_hdrpos * returns 0 on success */ static int write_hdrs(int outfd,struct strlist* hdrs,off_t* unwantedmime_hdrpos) { int i; for(i = 0; i < hdrs->count ; i++) { if(writen(outfd, hdrs->strs[i], strlen(hdrs->strs[i])) < 0){ log_error(LOG_ARGS, "Error when dumping headers"); return -1; } } /* if this is not the header of an embedded part add the header that will indicate if the mail contains unwanted mime parts */ if(unwantedmime_hdrpos) { if(writen(outfd, UNWANTED_MIME_HDR,strlen(UNWANTED_MIME_HDR)) < 0){ log_error(LOG_ARGS, "Error writting unwanted mime header"); return -1; } /* get the current position so that we can update the header later */ *unwantedmime_hdrpos = lseek(outfd,0,SEEK_CUR); if(*unwantedmime_hdrpos < 2){ log_error(LOG_ARGS, "Error getting file position"); return -1; } *unwantedmime_hdrpos -= 2; } /* write a single line feed to terminate the header part */ if(writen(outfd, "\n", 1) < 0) { log_error(LOG_ARGS,"Error writting end of hdrs."); return -1; } return 0; } /* set the unwanted mime_hdr to Y */ static int update_unwantedmime_hdr(int outfd,off_t unwantedmime_hdrpos) { /* seek to the header position */ if(lseek(outfd,unwantedmime_hdrpos,SEEK_SET) < 0) { log_error(LOG_ARGS,"Error seeking to the unwantedmime_hdr"); return -1; } /* update the header */ if(writen(outfd, "Y\n",2) < 0){ log_error(LOG_ARGS, "Error writting extra header"); return -1; } /* seek back to the end of the mail */ if(lseek(outfd,0,SEEK_END) < 0) { log_error(LOG_ARGS,"Error seeking to the mail end"); return -1; } return 0; } static int parse_body(int infd,int outfd, struct strlist* delmime, struct strlist* denymime, int* deny,char* boundary){ int strip = 0; char* line; while((line = mygetline(infd))) { if(boundary && !strncmp(line,boundary,strlen(boundary))){ strip = 0; /* check if the boundary is the beginning of a new part */ if(strncmp(line + strlen(boundary),"--",2)){ struct strlist hdrs; char* new_boundary = NULL; /* check if this part should be stripped */ if(read_hdrs(infd, &hdrs,delmime,denymime,deny,&new_boundary) == MIME_STRIP) strip = 1; else { /* write boundary */ if(writen(outfd, line, strlen(line)) < 0){ log_error(LOG_ARGS, "Error writting boundary"); return -1; } /* write hdr */ if(write_hdrs(outfd, &hdrs, NULL) < 0){ log_error(LOG_ARGS, "Error writting hdrs"); return -1; } /* parse embedded part if a new boundary was found */ if(new_boundary && parse_body(infd,outfd,delmime,denymime,deny,new_boundary) != 0) { log_error(LOG_ARGS, "Could not parse embedded part"); return -1; } } free_strlist(&hdrs); if(new_boundary) myfree(new_boundary); }else{ /* write end of part */ if(writen(outfd, line, strlen(line)) < 0){ log_error(LOG_ARGS, "Error writting hdrs"); return -1; } /* and leave */ myfree(line); break; } }else { if(!strip) { /* write the current line */ if(writen(outfd, line, strlen(line)) < 0){ log_error(LOG_ARGS, "Error when dumping line"); return -1; } } } myfree(line); } return 0; } /* read a mail stripping unwanted parts */ static int dump_mail(int infd, int outfd,char* listdir) { struct strlist hdrs; struct strlist* delmime; struct strlist* denymime; char* boundary=NULL; int deny = 0; int result; off_t unwantedmime_hdr_pos = 0; /* get list control values */ delmime = ctrlvalues(listdir, "mimestrip"); denymime = ctrlvalues(listdir, "mimedeny"); /* read mail header */ result = read_hdrs(infd, &hdrs,delmime,denymime,&deny,&boundary); /* write mail header */ if(write_hdrs(outfd,&hdrs,&unwantedmime_hdr_pos) < 0) { log_error(LOG_ARGS, "Could not write mail headers"); return -1; } /* free mail header */ free_strlist(&hdrs); if(result == MIME_OK && !deny) { /* try to parse the mail */ if(parse_body(infd,outfd,delmime,denymime,&deny,boundary) != 0) { log_error(LOG_ARGS, "Could not parse mail"); return -1; } myfree(boundary); }else deny = 1; /* dump rest of mail */ if(dumpfd2fd(infd, outfd) != 0) { log_error(LOG_ARGS, "Could not receive mail"); return -1; } /* update header */ if(deny) { if(update_unwantedmime_hdr(outfd,unwantedmime_hdr_pos) != 0) { log_error(LOG_ARGS, "Could not update header"); return -1; } } /* free mime types */ if(delmime) { free_strlist(delmime); myfree(delmime); } if(denymime) { free_strlist(denymime); myfree(denymime); } return 0; } static void print_help(const char *prg) { printf("Usage: %s -L /path/to/listdir [-h] [-V] [-P] [-F]\n" " -h: This help\n" " -F: Don't fork in the background\n" " -L: Full path to list directory\n" " -P: Don't execute mlmmj-process\n" " -V: Print version\n", prg); exit(EXIT_SUCCESS); } int main(int argc, char **argv) { char *infilename = NULL, *listdir = NULL; char *randomstr = random_str(); char *mlmmjprocess, *bindir; int fd, opt, noprocess = 0, nofork = 0; struct stat st; uid_t uid; pid_t childpid; CHECKFULLPATH(argv[0]); log_set_name(argv[0]); bindir = mydirname(argv[0]); mlmmjprocess = concatstr(2, bindir, "/mlmmj-process"); myfree(bindir); while ((opt = getopt(argc, argv, "hPVL:F")) != -1) { switch(opt) { case 'h': print_help(argv[0]); break; case 'L': listdir = optarg; break; case 'P': noprocess = 1; break; case 'F': nofork = 1; break; case 'V': print_version(argv[0]); exit(0); } } if(listdir == NULL) { fprintf(stderr, "You have to specify -L\n"); fprintf(stderr, "%s -h for help\n", argv[0]); exit(EXIT_FAILURE); } /* Lets make sure no random user tries to send mail to the list */ if(listdir) { if(stat(listdir, &st) == 0) { uid = getuid(); if(uid && uid != st.st_uid) { log_error(LOG_ARGS, "Have to invoke either as root " "or as the user owning listdir " "Invoked with uid = [%d]", (int)uid); writen(STDERR_FILENO, "Have to invoke either as root " "or as the user owning listdir\n", 60); exit(EXIT_FAILURE); } } else { log_error(LOG_ARGS, "Could not stat %s", listdir); exit(EXIT_FAILURE); } } infilename = concatstr(3, listdir, "/incoming/", randomstr); myfree(randomstr); fd = open(infilename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); while(fd < 0 && errno == EEXIST) { myfree(infilename); randomstr = random_str(); infilename = concatstr(3, listdir, "/incoming/", randomstr); myfree(randomstr); fd = open(infilename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); } if(fd < 0) { log_error(LOG_ARGS, "could not create mail file in " "%s/incoming directory", listdir); myfree(infilename); exit(EXIT_FAILURE); } if(dump_mail(fileno(stdin), fd, listdir) != 0) { log_error(LOG_ARGS, "Could not receive mail"); exit(EXIT_FAILURE); } #if 0 log_oper(listdir, OPLOGFNAME, "mlmmj-receive got %s", infilename); #endif fsync(fd); close(fd); if(noprocess) { myfree(infilename); exit(EXIT_SUCCESS); } /* * Now we fork so we can exit with success since it could potentially * take a long time for mlmmj-send to finish delivering the mails and * returning, making it susceptible to getting a SIGKILL from the * mailserver invoking mlmmj-receive. */ if (!nofork) { childpid = fork(); if(childpid < 0) log_error(LOG_ARGS, "fork() failed! Proceeding anyway"); if(childpid) exit(EXIT_SUCCESS); /* Parent says: "bye bye kids!"*/ close(0); close(1); close(2); } execlp(mlmmjprocess, mlmmjprocess, "-L", listdir, "-m", infilename, NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjprocess); exit(EXIT_FAILURE); }