365 lines
8.3 KiB
C
365 lines
8.3 KiB
C
/*
|
|
* mail.c - Mail editing toolset
|
|
* The author licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <string.h>
|
|
|
|
#include "mail.h"
|
|
#include "tools.h"
|
|
#include "attach.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
int append_header(struct email_t* mail, const char* key, const char* value){
|
|
|
|
if(mail == NULL || key == NULL || value == NULL){
|
|
return -1;
|
|
}
|
|
const char* in_between = ": ";
|
|
size_t len = strlen(key) + strlen(value) + strlen(in_between) + 1;
|
|
char* buffer = malloc(len);
|
|
|
|
if(buffer == NULL){
|
|
return -1;
|
|
}
|
|
|
|
memset(buffer, 0, len);
|
|
strcat(buffer, key);
|
|
strcat(buffer, in_between);
|
|
strcat(buffer, value);
|
|
if(append_to_header(mail, buffer) < 0){
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
free(buffer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
int append_to_header(struct email_t* mail, const char* pair){
|
|
|
|
if(pair == NULL || mail == NULL){
|
|
return -1;
|
|
}
|
|
|
|
char *buffer = malloc(strlen(pair) + 3);
|
|
if(buffer == NULL){
|
|
return -1;
|
|
}
|
|
memset(buffer, 0, strlen(pair) + 3);
|
|
buffer[0] = '\r';
|
|
buffer[1] = '\n';
|
|
strcat(buffer, pair);
|
|
struct email_t* root = get_root_mail(mail);
|
|
size_t root_offset = mail->header_len + (mail->message - root->message);
|
|
|
|
char* old_root = root->message;
|
|
char * new_root = insert_string(root->message, buffer,
|
|
root->message_length, root_offset);
|
|
if(new_root == NULL){
|
|
return -1;
|
|
}
|
|
propagate_size_change(mail, strlen(buffer));
|
|
|
|
propagate_root_pointer(root, new_root, old_root);
|
|
propagate_insert_delete(root, root->message+root_offset,
|
|
strlen(buffer));
|
|
|
|
mail->header_len += strlen(buffer);
|
|
mail->body_offset += strlen(buffer);
|
|
|
|
free(buffer);
|
|
redetect_body_head(mail);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Append the string text DIRECTLY to the end of the body of the message mail*/
|
|
int append_to_body(struct email_t* mail, const char* text){
|
|
|
|
if(text == NULL || mail == NULL){
|
|
return -1;
|
|
}
|
|
|
|
char *buffer = malloc(strlen(text) + 3);
|
|
memset(buffer, 0, strlen(text) + 3);
|
|
buffer[0] = '\r';
|
|
buffer[1] = '\n';
|
|
strcat(buffer, text);
|
|
struct email_t* root = get_root_mail(mail);
|
|
size_t root_offset = mail->message_length +
|
|
(mail->message - root->message);
|
|
|
|
char* old_root = root->message;
|
|
char * new_root = insert_string(root->message, buffer,
|
|
root->message_length, root_offset);
|
|
if(new_root == NULL){
|
|
return -1;
|
|
}
|
|
propagate_size_change(mail, strlen(buffer));
|
|
|
|
propagate_root_pointer(root, new_root, old_root);
|
|
propagate_insert_delete(root, root->message+root_offset,
|
|
strlen(buffer));
|
|
|
|
free(buffer);
|
|
redetect_body_head(mail);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Append the string text DIRECTLY before the last closing </html> tag inside
|
|
* the body.
|
|
*/
|
|
int append_to_html_body(struct email_t* mail, const char* text){
|
|
|
|
if(text == NULL || mail == NULL){
|
|
return -1;
|
|
}
|
|
|
|
char *buffer = malloc(strlen(text) + 3);
|
|
memset(buffer, 0, strlen(text) + 3);
|
|
buffer[0] = '\r';
|
|
buffer[1] = '\n';
|
|
strcat(buffer, text);
|
|
struct email_t* root = get_root_mail(mail);
|
|
|
|
char* body_end = mail->message_length +
|
|
mail->message;
|
|
char* html_end = mail->message;
|
|
for(;;){
|
|
|
|
char * html_end_int = strcasestr(html_end+1, "</html>");
|
|
if(html_end_int == NULL || html_end_int >= body_end){
|
|
break;
|
|
}else{
|
|
html_end = html_end_int;
|
|
}
|
|
}
|
|
size_t root_offset = 0;
|
|
if(html_end == mail->message){
|
|
root_offset = mail->message_length +
|
|
(mail->message - root->message);
|
|
|
|
}else{
|
|
root_offset = html_end - root->message;
|
|
|
|
}
|
|
|
|
char* old_root = root->message;
|
|
char * new_root = insert_string(root->message, buffer,
|
|
root->message_length, root_offset);
|
|
if(new_root == NULL){
|
|
return -1;
|
|
}
|
|
propagate_size_change(mail, strlen(buffer));
|
|
|
|
propagate_root_pointer(root, new_root, old_root);
|
|
propagate_insert_delete(root, root->message+root_offset,
|
|
strlen(buffer));
|
|
|
|
free(buffer);
|
|
redetect_body_head(mail);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int remove_mail(struct email_t* mail){
|
|
|
|
if(mail == NULL || mail->parent == NULL || !mail->parent->is_multipart){
|
|
return -1;
|
|
}
|
|
|
|
struct email_t* parent = mail->parent;
|
|
bool found = false;
|
|
|
|
struct email_t * followup = NULL;
|
|
for(size_t i = 0; i < parent->submes_cnt; i++){
|
|
|
|
if(parent->submes[i] == mail){
|
|
/* Remove ourselfs from the parent */
|
|
found = true;
|
|
if(i+1 < parent->submes_cnt){
|
|
followup = parent->submes[i+1];
|
|
}
|
|
}
|
|
|
|
if(found && (i+1 < parent->submes_cnt)){
|
|
parent->submes[i] = parent->submes[i+1];
|
|
}
|
|
|
|
}
|
|
if(!found){
|
|
fprintf(stderr, "Internal message not included in parent!\n");
|
|
return -1;
|
|
}
|
|
|
|
parent->submes_cnt--;
|
|
char* end = NULL;
|
|
if(followup == NULL){
|
|
end = parent->message + parent->message_length;
|
|
}else{
|
|
end = followup->message - 1;
|
|
}
|
|
|
|
struct email_t *root = get_root_mail(mail);
|
|
if(root == NULL){
|
|
return -1;
|
|
}
|
|
|
|
size_t remove_len = end - mail->message;
|
|
size_t remove_offset = mail->message - root->message;
|
|
|
|
|
|
if(!remove_string(root->message, root->message_length,
|
|
remove_offset, remove_len)){
|
|
fprintf(stderr, "Unwilling to remove string from message at "
|
|
"offset %lu with len %lu total length is %lu!\n",
|
|
remove_offset,
|
|
remove_len,
|
|
root->message_length);
|
|
return -1;
|
|
}
|
|
propagate_size_change(mail, -remove_len);
|
|
|
|
propagate_insert_delete(root, root->message+remove_offset, -remove_len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
struct email_t* get_root_mail(struct email_t* mail){
|
|
|
|
if(mail == NULL){
|
|
return NULL;
|
|
}
|
|
|
|
if(mail->parent == NULL){
|
|
return mail;
|
|
}else{
|
|
return get_root_mail(mail->parent);
|
|
}
|
|
|
|
}
|
|
|
|
/* This propagates a size change inside the root email down to all submails
|
|
* Call this function with the root, changes will propagate down via recursion!
|
|
* The change pointer should point to the last character IN FRONT of the change.
|
|
* an insert has a positive amount of chane, a delete a negative one.
|
|
*/
|
|
void propagate_insert_delete(struct email_t* mail, char* change_p,
|
|
ssize_t change){
|
|
|
|
if(mail == NULL || change_p == NULL || change == 0){
|
|
/* MISSION ABORT!! */
|
|
return;
|
|
}
|
|
|
|
if(mail->message >= change_p){
|
|
mail->message += change;
|
|
}
|
|
|
|
if(mail->boundary >= change_p){
|
|
mail->boundary += change;
|
|
}
|
|
|
|
if(mail->content_type >= change_p){
|
|
mail->content_type += change;
|
|
}
|
|
|
|
if(mail->is_multipart){
|
|
for(size_t i = 0; i < mail->submes_cnt; i++){
|
|
propagate_insert_delete(mail->submes[i], change_p,
|
|
change);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* This propagates a root pointer change down to all submessages.
|
|
* Call this function with the root, changes will propagate down via recursion!
|
|
* The change pointer should point to the new root pointer for all messages,
|
|
* the old_p pointer should be the old root pointer
|
|
*/
|
|
void propagate_root_pointer(struct email_t* mail, char* change_p, char* old_p){
|
|
|
|
if(mail == NULL || change_p == NULL ||old_p == NULL){
|
|
/* MISSION ABORT!! */
|
|
return;
|
|
}
|
|
|
|
size_t delta = mail->message - old_p;
|
|
mail->message = change_p + delta;
|
|
|
|
delta = mail->boundary - old_p;
|
|
mail->boundary = change_p + delta;
|
|
|
|
delta = mail->content_type - old_p;
|
|
mail->content_type = change_p + delta;
|
|
|
|
if(mail->is_multipart){
|
|
for(size_t i = 0; i < mail->submes_cnt; i++){
|
|
propagate_root_pointer(mail->submes[i], change_p,
|
|
old_p);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
struct type_file_info_t get_mime_file_info(struct email_t* mail){
|
|
|
|
struct type_file_info_t fileinfo;
|
|
fileinfo.name = NULL;
|
|
fileinfo.mime_type = NULL;
|
|
|
|
if(mail->content_type == NULL){
|
|
return fileinfo;
|
|
}
|
|
|
|
char* semic = strchr(mail->content_type, ';');
|
|
size_t mime_len = 0;
|
|
if(semic > (mail->content_type + mail->ct_len)){
|
|
mime_len = mail->ct_len;
|
|
}else{
|
|
mime_len = semic - mail->content_type;
|
|
}
|
|
|
|
fileinfo.mime_type = malloc(mime_len + 1);
|
|
memset(fileinfo.mime_type, 0, mime_len + 1);
|
|
memcpy(fileinfo.mime_type, mail->content_type, mime_len);
|
|
size_t filename_len = 0;
|
|
char* filename = get_value_equals(mail->content_type, mail->ct_len,
|
|
&filename_len, "name");
|
|
|
|
if(filename != NULL){
|
|
fileinfo.name = malloc(filename_len + 1);
|
|
memset(fileinfo.name, 0, filename_len + 1);
|
|
memcpy(fileinfo.name, filename, filename_len);
|
|
}
|
|
|
|
return fileinfo;
|
|
}
|