#include #include #include #include #include #include struct mail_t { long start; long finish; char *msgid; struct mail_t *next; }; struct mail_t *mailbase = NULL; int get_line(FILE *file, char *buffer, size_t size) { size_t i = 0; int ret; while(i <= size) { if((ret = fgetc(file)) != EOF) { if(ret == '\n') { buffer[i] = '\0'; return i; } else { buffer[i] = ret; i++; } } else { buffer[i] = '\0'; return EOF; } } return -1; } long find_string_line(FILE *file, const char *string, int casesense) { char buffer[4096]; int len = strlen(string); int ret; int match = 0; while(!match) { ret = get_line(file,buffer,4096); if(ret == EOF) return EOF; if(ret != -1 && ret >= len) { if(casesense) { if(strncmp(buffer,string,len) == 0) match = 1; } else { if(strncasecmp(buffer,string,len) == 0) match = 1; } } //fprintf(stderr,"No Match \"%s\"\n",buffer); } return ftell(file) - (ret + 1); } char *get_msgid(const char *line) { char *msgid; char *start; int i = 0; //fprintf(stderr,"get_msgid: line:\"%s\"\n",line); while(line[i] != '\0' && line[i] != ' ') i++; if(line[i] == '\0') return NULL; start = line + i + 1; msgid = malloc(strlen(line) - i + 1); strcpy(msgid,start); //fprintf(stderr,"get_msgid: msgid:\"%s\"\n",msgid); return msgid; } long find_mail_start(FILE *spool) { char buffer[4096]; int begin; int found = 0; long off; if(ftell(spool) < 2) begin = 1; else begin = 0; while(!found) { off = find_string_line(spool,"From ",1); if(off == EOF) { return EOF; } fprintf(stderr,"Found \"%s\" line at %d\n","From ",off); if(!begin) { fseek(spool,off - 2,SEEK_SET); fread(buffer,2,1,spool); buffer[2] = '\0'; if(strncmp(buffer,"\n\n",4096) == 0) found = 1; else fseek(spool,off + 1, SEEK_SET); } else { found = 1; } } return off; } struct mail_t *get_mail(FILE *spool) { char buffer[4096]; struct mail_t *mail = malloc(sizeof(struct mail_t)); long off; long next; int begin; int ret; int found = 0; mail->msgid = NULL; mail->next = NULL; mail->finish = 0; mail->start = find_mail_start(spool); if(mail->start == EOF) { free(mail); return NULL; } fseek(spool,mail->start + 1, SEEK_SET); next = find_mail_start(spool); fseek(spool,mail->start,SEEK_SET); off = find_string_line(spool,"Message-ID:",0); if(next != EOF && off > next) { sleep(5); mail->msgid = strdup("Unknown"); fseek(spool,next,SEEK_SET); } else { fseek(spool,off,SEEK_SET); ret = get_line(spool,buffer,4096); if(ret == EOF || ret == -1) { free(mail); return NULL; } fprintf(stderr,"Found \"%s\" line at %d\n","Message-ID:",off); mail->msgid = get_msgid(buffer); } return mail; } char *filter_name(const char *name) { static char *clean = NULL; int i,j,banned; if(clean != NULL) free(clean); banned = 0; for(i = 0; name[i] != '\0' && i < 248; ++i) { if(name[i] == '/') banned++; } clean = malloc(i + banned*4 + 1); for(i = 0, j = 0; name[i] != '\0' && i < (248 - banned*3); ++i) { if(name[i] != '/') { clean[j] = name[i]; j += 1; } else { j += sprintf(clean + j,"%%%03o",name[i]); } } clean[j] = '\0'; return clean; } void split_mail(FILE *spool, char *dirname, struct mail_t *mail) { struct stat tmp; FILE *out; char path[4096]; char *path2 = NULL; char *buffer; int i = 0; long len = mail->finish - mail->start; if(mail->msgid == NULL) return; snprintf(path,4096,"%s/%s",dirname,filter_name(mail->msgid)); while(stat(path,&tmp) == 0) { if(path2 == NULL) path2 = strdup(path); snprintf(path,4096,"%s.%05d",path2,i); i++; } if(path2 != NULL) free(path2); fprintf(stderr,"Splitting %s to %s\n",mail->msgid,path); buffer = malloc(len); if(buffer == NULL) { fprintf(stderr,"Fail to get %d bytes of memory\n",len); exit(1); } fseek(spool,mail->start,SEEK_SET); fread(buffer,len,1,spool); out = fopen(path,"w"); if(out == NULL) { fprintf(stderr,"failed to open %s for write\n",path); exit(1); } fwrite(buffer,len,1,out); fclose(out); free(buffer); } int main(int argc, char *argv[]) { struct mail_t *ptr; struct mail_t *lptr; FILE *spool; int mails = 0; if(argc < 3) exit(1); spool = fopen(argv[1],"r"); if(spool == NULL) exit(1); mailbase = get_mail(spool); lptr = mailbase; while((ptr = get_mail(spool)) != NULL && !feof(spool)) { lptr->next = ptr; lptr->finish = ptr->start; fprintf(stderr,"Found %s, at %d->%d\n",lptr->msgid,lptr->start,lptr->finish); lptr = ptr; } lptr->finish = ftell(spool); for(ptr = mailbase; ptr != NULL; ptr = ptr->next) { split_mail(spool,argv[2],ptr); mails++; } fclose(spool); fprintf(stderr,"mails:%d\n",mails); return 0; }