From 8ca48344a830a11cda0ffa4a46c22ccd5cef384f Mon Sep 17 00:00:00 2001 From: Tyrolyean Date: Wed, 29 Apr 2020 14:47:50 +0200 Subject: [PATCH] Added recursive parsing Signed-off-by: Tyrolyean --- include/attach.h | 3 ++- include/mail.h | 1 + include/tools.h | 3 +++ src/attach.c | 60 +++++++++++++++++++++++++++++++++++++++++++++--- src/tools.c | 31 +++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/include/attach.h b/include/attach.h index 9a3e00a..7808f04 100644 --- a/include/attach.h +++ b/include/attach.h @@ -23,7 +23,8 @@ #include "mail.h" -struct email_t* mail_from_text(char* message, size_t length); +struct email_t* mail_from_text(char* message, size_t length, + struct email_t* parent_mail); void redetect_body_head(struct email_t* mail); void unravel_multipart_mail(struct email_t* mail); diff --git a/include/mail.h b/include/mail.h index bc53dc0..8464199 100644 --- a/include/mail.h +++ b/include/mail.h @@ -41,6 +41,7 @@ struct email_t{ char* boundary; size_t submes_cnt; struct email_t** submes; + struct email_t* parent; }; int append_header(struct email_t* mail, const char* key, const char* value); diff --git a/include/tools.h b/include/tools.h index 60494eb..f1309d4 100644 --- a/include/tools.h +++ b/include/tools.h @@ -31,4 +31,7 @@ char* get_value_from_key(size_t* val_len, size_t key_offset, char* get_multipart_boundary(char* content_type, size_t content_len, size_t* boundary_len); + +const char* get_next_line(const char* message, size_t len); +const char* get_prev_line(const char* message, size_t len_neg); #endif /* TOOLS_H */ diff --git a/src/attach.c b/src/attach.c index 0ff11fc..7c6cacf 100644 --- a/src/attach.c +++ b/src/attach.c @@ -28,7 +28,12 @@ #include #include -struct email_t* mail_from_text(char* message, size_t length){ +/* Generates an email struct from the given eml text. If the content is + * multipart, it will be slit up into several email_ts. This expansion is done + * recursively. For the root message set parent_mail to NULL + */ +struct email_t* mail_from_text(char* message, size_t length, + struct email_t* parent_mail){ struct email_t* mail = malloc(sizeof(struct email_t));; memset(mail, 0, sizeof(struct email_t)); @@ -39,6 +44,7 @@ struct email_t* mail_from_text(char* message, size_t length){ mail->is_multipart = false; mail->boundary = NULL; mail->boundary_len = 0; + mail->parent = parent_mail; redetect_body_head(mail); char* cont_type = search_header_key(mail, "Content-Type"); @@ -157,6 +163,28 @@ void unravel_multipart_mail(struct email_t* mail){ "submessages\n", mb_cnt, mb_cnt-1); } + for(ssize_t i = 0; i < ((ssize_t)mb_cnt)-1; i++){ + const char * begin_pointer = get_next_line(mail_boundarys[i], + mail->message_length - + (mail->message - mail_boundarys[i])); + + if(begin_pointer == NULL){ + continue; + } + + const char* end_pointer = get_prev_line(mail_boundarys[i+1], + (mail_boundarys[i+1] - begin_pointer)); + + if(end_pointer == NULL){ + continue; + } + struct email_t *submail = mail_from_text((char*)begin_pointer, + end_pointer - begin_pointer, mail); + mail->submes = realloc(mail->submes, ++mail->submes_cnt * + (sizeof(struct email_t))); + mail->submes[mail->submes_cnt - 1] = submail; + } + free(mail_boundarys); free(boundary); return; @@ -177,6 +205,27 @@ void free_submails(struct email_t* mail){ } +void print_mail_structure(struct email_t *email, unsigned int level){ + + for(unsigned int i = 0; i < level; i++){ + printf(" "); + } + + if(email->is_multipart){ + printf("Multipart Message with %lu submessages:\n", + email->submes_cnt); + for(size_t i = 0; i < email->submes_cnt; i++){ + print_mail_structure(email->submes[i], level+1); + } + }else{ + printf("Final message with length %lu\n", + email->message_length); + } + + return; + +} + /* Message is required to be a null terminated string, length is the mail body. * One may leave something behind the body. len is without the '\0' * Attempts to replace files inside the email with links to it on a webserver @@ -184,8 +233,13 @@ void free_submails(struct email_t* mail){ char* attach_files(char* message, size_t len){ char* mess; - struct email_t *email = mail_from_text(message,len); - + struct email_t *email = mail_from_text(message,len, NULL); + if(email == NULL){ + return NULL; + } + if(verbose){ + print_mail_structure(email, 0); + } /* Check if mails are signed/encrypted, and abort if nescessary */ if(abort_on_pgp && detect_pgp(email)){ printf("PGP detected, aborting..."); diff --git a/src/tools.c b/src/tools.c index f076ddf..e03e028 100644 --- a/src/tools.c +++ b/src/tools.c @@ -150,3 +150,34 @@ char* get_multipart_boundary(char* content_type, size_t content_len, *boundary_len = 0; return NULL; } + +/* Returns a character pointer to the first character of the next line. */ +const char* get_next_line(const char* message, size_t len){ + for(size_t i = 0; i < len-1; i++){ + if(message[i] == '\n'){ + return &message[i+1]; + } + } + + return NULL; +} + +/* Returns a character pointer to the last character of the last line which was + * NOT part of a cr lf. The len_neg attribute specifies how far we can go to + * the left. + */ +const char* get_prev_line(const char* message, size_t len_neg){ + for(size_t i = 0; i < len_neg-2; i++){ + if(message[-i] == '\n'){ + if(message[-(i+1)] == '\r'){ + return &message[i+2]; + + }else{ + return &message[i+2]; + + } + } + } + + return NULL; +}