Skip to content

Instantly share code, notes, and snippets.

@jtsiomb
Last active February 10, 2019 00:03
Show Gist options
  • Save jtsiomb/c0c8cb8f7c8e6dbaf97c896852a2acdb to your computer and use it in GitHub Desktop.
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
#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