Last active
February 10, 2019 00:03
-
-
Save jtsiomb/c0c8cb8f7c8e6dbaf97c896852a2acdb to your computer and use it in GitHub Desktop.
Scans a mailbox (mbox format) and extracts all attachments with the help of munpack
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <ctype.h> | |
#include <alloca.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <dirent.h> | |
#include <sys/wait.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
void print_usage(const char *argv0); | |
int proc_mbox(const char *fname); | |
int proc_mail(const char *mboxname, int idx, char *mail, long size); | |
int listonly; | |
int match_msgid = -1; | |
int dumptext; | |
int main(int argc, char **argv) | |
{ | |
int i; | |
signal(SIGPIPE, SIG_IGN); | |
for(i=1; i<argc; i++) { | |
if(argv[i][0] == '-') { | |
if(argv[i][2] == 0) { | |
switch(argv[i][1]) { | |
case 'l': | |
listonly = 1; | |
break; | |
case 'a': | |
dumptext = 1; | |
break; | |
case 'm': | |
if(!isdigit(argv[++i][0])) { | |
fprintf(stderr, "-m must be followed by the message number\n"); | |
return 1; | |
} | |
match_msgid = atoi(argv[i]); | |
break; | |
case 'h': | |
print_usage(argv[0]); | |
return 0; | |
default: | |
fprintf(stderr, "invalid option: %s\n", argv[i]); | |
print_usage(argv[0]); | |
return 1; | |
} | |
} else { | |
fprintf(stderr, "invalid option: %s\n", argv[i]); | |
print_usage(argv[0]); | |
return 1; | |
} | |
} else { | |
if(proc_mbox(argv[i]) == -1) { | |
return 1; | |
} | |
} | |
} | |
return 0; | |
} | |
void print_usage(const char *argv0) | |
{ | |
printf("Usage: %s [options] <mbox> [mbox2 ...]\n", argv0); | |
printf("Options:\n"); | |
printf(" -l: list messages only, do not extract attachments\n"); | |
printf(" -m <msg number>: process a single message out of the mailbox\n"); | |
printf(" -a: dump all parts of each mail, not only the attachments\n"); | |
printf(" -h: print usage information and exit\n"); | |
} | |
int proc_mbox(const char *fname) | |
{ | |
int idx, fd; | |
struct stat st; | |
char *start, *ptr, *end, *msgend; | |
const char *match = "\nFrom "; | |
int match_len = strlen(match); | |
if((fd = open(fname, O_RDONLY)) == -1) { | |
fprintf(stderr, "failed to open mailbox: %s: %s\n", fname, strerror(errno)); | |
return -1; | |
} | |
fstat(fd, &st); | |
if((start = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == (void*)-1) { | |
fprintf(stderr, "failed to map mailbox: %s: %s\n", fname, strerror(errno)); | |
close(fd); | |
return -1; | |
} | |
ptr = start; | |
end = start + st.st_size; | |
idx = 0; | |
while(end - ptr > match_len) { | |
if(ptr == start || memcmp(ptr, match, match_len) == 0) { | |
msgend = ptr + 1; | |
while(msgend < end && memcmp(msgend, match, match_len) != 0) { | |
msgend++; | |
} | |
if(match_msgid == -1 || match_msgid == idx) { | |
if(proc_mail(fname, idx, ptr, msgend - ptr) == -1) { | |
munmap(start, st.st_size); | |
close(fd); | |
return -1; | |
} | |
} | |
idx++; | |
ptr = msgend; | |
} else { | |
ptr++; | |
} | |
} | |
munmap(start, st.st_size); | |
close(fd); | |
return 0; | |
} | |
void print_field(const char *s, const char *end) | |
{ | |
while(s < end && *s != '\n') { | |
putchar(*s++); | |
} | |
} | |
int proc_mail(const char *mboxname, int idx, char *mail, long size) | |
{ | |
int pid, pfd[2]; | |
long i; | |
char *from = "(unknown sender)"; | |
char *subj = "Subject: (no subject)"; | |
char *dirname; | |
DIR *dir; | |
struct dirent *dent; | |
for(i=0; i<size; i++) { | |
if(memcmp(mail + i, "\nFrom: ", 7) == 0) { | |
from = mail + i + 1; | |
} | |
if(memcmp(mail + i, "\nSubject: ", 10) == 0) { | |
subj = mail + i + 1; | |
} | |
} | |
printf("%d: ", idx); | |
print_field(subj + 9, mail + size); | |
printf(" - "); | |
print_field(from, mail + size); | |
printf("\n"); | |
if(listonly) return 0; | |
if(pipe(pfd) == -1) { | |
fprintf(stderr, "failed to create message pipe\n"); | |
return -1; | |
} | |
dirname = alloca(strlen(mboxname) + 16); | |
sprintf(dirname, "%s%05d", mboxname, idx); | |
if(mkdir(dirname, 0777) == -1 && errno != EEXIST) { | |
fprintf(stderr, "failed to create directory: %s: %s\n", dirname, strerror(errno)); | |
return -1; | |
} | |
if(!(pid = fork())) { | |
close(0); | |
dup(pfd[0]); | |
close(pfd[1]); | |
execlp("munpack", "munpack", "-f", "-C", dirname, dumptext ? "-t" : (void*)0, (void*)0); | |
fprintf(stderr, "failed to execute munpack: %s\n", strerror(errno)); | |
_exit(1); | |
} | |
close(pfd[0]); | |
while(size > 0) { | |
ssize_t wr = write(pfd[1], mail, size); | |
if(wr < 0) { | |
if(errno == EINTR) continue; | |
fprintf(stderr, "failed to write to message pipe: %s\n", strerror(errno)); | |
kill(pid, SIGINT); | |
break; | |
} | |
size -= wr; | |
mail += wr; | |
} | |
close(pfd[1]); | |
wait(0); | |
if((dir = opendir(dirname))) { | |
while((dent = readdir(dir))) { | |
if(strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0 && | |
strcmp(dent->d_name, "tempdesc.txt") != 0) { | |
break; | |
} | |
} | |
closedir(dir); | |
if(!dent) { | |
/* directory is empty, remove it */ | |
char *path = alloca(strlen(dirname) + 16); | |
sprintf(path, "%s/tempdesc.txt", dirname); | |
remove(path); | |
rmdir(dirname); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment