Diffstat (limited to 'libetpan/src/driver/interface/maildriver_tools.c') (more/less context) (ignore whitespace changes)
-rw-r--r-- | libetpan/src/driver/interface/maildriver_tools.c | 841 |
1 files changed, 841 insertions, 0 deletions
diff --git a/libetpan/src/driver/interface/maildriver_tools.c b/libetpan/src/driver/interface/maildriver_tools.c new file mode 100644 index 0000000..4501b23 --- a/dev/null +++ b/libetpan/src/driver/interface/maildriver_tools.c @@ -0,0 +1,841 @@ +/* + * libEtPan! -- a mail stuff library + * + * Copyright (C) 2001, 2005 - DINH Viet Hoa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the libEtPan! project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "maildriver_tools.h" + +#include "libetpan-config.h" + +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <dirent.h> +#include <unistd.h> + +#include "maildriver.h" +#include "mailmessage.h" +#include "mailstream.h" +#include "mailmime.h" +#include "mail_cache_db.h" + +/* ********************************************************************* */ +/* tools */ + + +int +maildriver_generic_get_envelopes_list(mailsession * session, + struct mailmessage_list * env_list) +{ + int r; + unsigned i; +#if 0 + uint32_t j; + uint32_t last_msg; + + last_msg = 0; +#endif + +#if 0 + j = 0; + i = 0; + while (i < env_list->tab->len) { + mailmessage * msg; + uint32_t index; + + msg = carray_get(env_list->tab, i); + + index = msg->index; + + if (msg->fields == NULL) { + struct mailimf_fields * fields; + + r = mailmessage_fetch_envelope(msg, &fields); + if (r != MAIL_NO_ERROR) { + /* do nothing */ + } + else { + msg->fields = fields; + + carray_set(env_list->tab, j, msg); + + j ++; + last_msg = i + 1; + } + mailmessage_flush(msg); + } + else { + j ++; + last_msg = i + 1; + } + + i ++; + } + + for(i = last_msg ; i < env_list->tab->len ; i ++) { + mailmessage_free(carray_get(env_list->tab, i)); + carray_set(env_list->tab, i, NULL); + } + + r = carray_set_size(env_list->tab, j); + if (r < 0) + return MAIL_ERROR_MEMORY; +#endif + + for(i = 0 ; i < carray_count(env_list->msg_tab) ; i ++) { + mailmessage * msg; + + msg = carray_get(env_list->msg_tab, i); + + if (msg->msg_fields == NULL) { + struct mailimf_fields * fields; + + r = mailmessage_fetch_envelope(msg, &fields); + if (r != MAIL_NO_ERROR) { + /* do nothing */ + } + else { + msg->msg_fields = fields; + } + mailmessage_flush(msg); + } + } + + return MAIL_NO_ERROR; +} + + +#if 0 +static int is_search_header_only(struct mail_search_key * key) +{ + clistiter * cur; + int result; + + switch (key->type) { + case MAIL_SEARCH_KEY_ANSWERED: + case MAIL_SEARCH_KEY_BCC: + case MAIL_SEARCH_KEY_BEFORE: + case MAIL_SEARCH_KEY_CC: + case MAIL_SEARCH_KEY_DELETED: + case MAIL_SEARCH_KEY_FLAGGED: + case MAIL_SEARCH_KEY_FROM: + case MAIL_SEARCH_KEY_NEW: + case MAIL_SEARCH_KEY_OLD: + case MAIL_SEARCH_KEY_ON: + case MAIL_SEARCH_KEY_RECENT: + case MAIL_SEARCH_KEY_SEEN: + case MAIL_SEARCH_KEY_SINCE: + case MAIL_SEARCH_KEY_SUBJECT: + case MAIL_SEARCH_KEY_TO: + case MAIL_SEARCH_KEY_UNANSWERED: + case MAIL_SEARCH_KEY_UNDELETED: + case MAIL_SEARCH_KEY_UNFLAGGED: + case MAIL_SEARCH_KEY_UNSEEN: + case MAIL_SEARCH_KEY_HEADER: + case MAIL_SEARCH_KEY_LARGER: + case MAIL_SEARCH_KEY_NOT: + case MAIL_SEARCH_KEY_SMALLER: + case MAIL_SEARCH_KEY_ALL: + return TRUE; + + case MAIL_SEARCH_KEY_BODY: + case MAIL_SEARCH_KEY_TEXT: + return FALSE; + + case MAIL_SEARCH_KEY_OR: + return (is_search_header_only(key->or1) && + is_search_header_only(key->or2)); + + case MAIL_SEARCH_KEY_MULTIPLE: + result = TRUE; + for (cur = clist_begin(key->multiple) ; cur != NULL ; + cur = clist_next(cur)) + result = result && is_search_header_only(clist_content(cur)); + return result; + + default: + return TRUE; + } +} + +static int match_header(struct mailimf_fields * fields, + char * name, char * value) +{ + clistiter * cur; + + for(cur = clist_begin(fields->list) ; cur != NULL ; + cur = clist_content(cur)) { + struct mailimf_field * field; + struct mailimf_optional_field * opt_field; + + field = clist_content(cur); + opt_field = field->optional_field; + if ((char) toupper((unsigned char) opt_field->name[0]) == + (char) toupper((unsigned char) name[0])) { + if (strcasecmp(opt_field->name, name) == 0) + if (strstr(opt_field->value, value) != NULL) + return TRUE; + } + } + return FALSE; +} + +static int comp_date(struct mailimf_fields * fields, + struct mailimf_date_time * ref_date) +{ + clistiter * cur; + struct mailimf_date_time * date; + int r; + + date = NULL; + for(cur = clist_begin(fields->list) ; cur != NULL ; + cur = clist_content(cur)) { + struct mailimf_field * field; + struct mailimf_optional_field * opt_field; + + field = clist_content(cur); + opt_field = field->optional_field; + if ((char) toupper((unsigned char) opt_field->name[0]) == 'D') { + if (strcasecmp(opt_field->name, "Date") == 0) { + size_t cur_token; + + cur_token = 0; + r = mailimf_date_time_parse(opt_field->value, strlen(opt_field->value), + &cur_token, &date); + if (r == MAILIMF_NO_ERROR) + break; + else if (r == MAILIMF_ERROR_PARSE) { + /* do nothing */ + } + else + break; + } + } + } + + if (date == NULL) + return 0; + + return mailimf_date_time_comp(date, ref_date); +} + +static int match_messages(char * message, + size_t size, + struct mailimf_fields * fields, + int32_t flags, + char * charset, + struct mail_search_key * key) +{ + clistiter * cur; + size_t length; + size_t cur_token; + int r; + + switch (key->type) { + + /* flags */ + case MAIL_SEARCH_KEY_ANSWERED: + return ((flags & MAIL_FLAG_ANSWERED) != 0); + + case MAIL_SEARCH_KEY_FLAGGED: + return ((flags & MAIL_FLAG_FLAGGED) != 0); + + case MAIL_SEARCH_KEY_DELETED: + return ((flags & MAIL_FLAG_DELETED) != 0); + + case MAIL_SEARCH_KEY_RECENT: + return ((flags & MAIL_FLAG_NEW) != 0) && + ((flags & MAIL_FLAG_SEEN) == 0); + + case MAIL_SEARCH_KEY_SEEN: + return ((flags & MAIL_FLAG_SEEN) != 0); + + case MAIL_SEARCH_KEY_NEW: + return ((flags & MAIL_FLAG_NEW) != 0); + + case MAIL_SEARCH_KEY_OLD: + return ((flags & MAIL_FLAG_NEW) == 0); + + case MAIL_SEARCH_KEY_UNANSWERED: + return ((flags & MAIL_FLAG_ANSWERED) == 0); + + case MAIL_SEARCH_KEY_UNDELETED: + return ((flags & MAIL_FLAG_DELETED) == 0); + + case MAIL_SEARCH_KEY_UNFLAGGED: + return ((flags & MAIL_FLAG_FLAGGED) == 0); + + case MAIL_SEARCH_KEY_UNSEEN: + return ((flags & MAIL_FLAG_SEEN) == 0); + + /* headers */ + case MAIL_SEARCH_KEY_BCC: + return match_header(fields, "Bcc", key->bcc); + + case MAIL_SEARCH_KEY_CC: + return match_header(fields, "Cc", key->cc); + + case MAIL_SEARCH_KEY_FROM: + return match_header(fields, "From", key->from); + + case MAIL_SEARCH_KEY_SUBJECT: + return match_header(fields, "Subject", key->subject); + + case MAIL_SEARCH_KEY_TO: + return match_header(fields, "To", key->to); + + case MAIL_SEARCH_KEY_HEADER: + return match_header(fields, key->header_name, key->header_value); + + /* date */ + case MAIL_SEARCH_KEY_BEFORE: + return (comp_date(fields, key->before) <= 0); + + case MAIL_SEARCH_KEY_ON: + return (comp_date(fields, key->before) == 0); + + case MAIL_SEARCH_KEY_SINCE: + return (comp_date(fields, key->before) >= 0); + + /* boolean */ + case MAIL_SEARCH_KEY_NOT: + return (!match_messages(message, size, fields, flags, charset, key->not)); + case MAIL_SEARCH_KEY_OR: + return (match_messages(message, size, fields, flags, charset, key->or1) || + match_messages(message, size, fields, flags, charset, key->or2)); + + case MAIL_SEARCH_KEY_MULTIPLE: + for(cur = clist_begin(key->multiple) ; cur != NULL ; + cur = clist_next(cur)) { + if (!match_messages(message, size, fields, flags, charset, + clist_content(cur))) + return FALSE; + } + + return TRUE; + + /* size */ + case MAIL_SEARCH_KEY_SMALLER: + return (size <= key->smaller); + + case MAIL_SEARCH_KEY_LARGER: + return (size >= key->larger); + + case MAIL_SEARCH_KEY_BODY: + length = strlen(message); + + cur_token = 0; + while (1) { + r = mailimf_ignore_field_parse(message, length, &cur_token); + if (r == MAILIMF_NO_ERROR) { + /* do nothing */ + } + else + break; + } + + return (strstr(message + cur_token, key->body) != NULL); + + case MAIL_SEARCH_KEY_TEXT: + return (strstr(message, key->body) != NULL); + + case MAIL_SEARCH_KEY_ALL: + default: + return TRUE; + } +} + +int maildriver_generic_search_messages(mailsession * session, char * charset, + struct mail_search_key * key, + struct mail_search_result ** result) +{ + int header; + clist * list; + struct mail_search_result * search_result; + int r; + struct mailmessage_list * env_list; + int res; + unsigned int i; + + header = is_search_header_only(key); + + r = mailsession_get_messages_list(session, &env_list); + if (r != MAIL_NO_ERROR) + return r; + + list = NULL; + for(i = 0 ; i < carray_count(env_list->tab) ; i ++) { + char * message; + size_t length; + struct mail_info * info; + uint32_t flags; + struct mailimf_fields * fields; + size_t cur_token; + + info = carray_get(env_list->tab, i); + + if (!header) { + r = mailsession_fetch_message(session, info->index, &message, &length); + if (r != MAIL_NO_ERROR) { + res = r; + goto free_list; + } + + cur_token = 0; + r = mailimf_optional_fields_parse(message, length, + &cur_token, &fields); + if (r != MAILIMF_NO_ERROR) { + res = MAIL_ERROR_PARSE; + goto free_list; + } + } + else { + char * msg_header; + int r; + size_t cur_token; + size_t header_len; + + r = mailsession_fetch_message_header(session, info->index, &msg_header, + &header_len); + if (r != MAIL_NO_ERROR) { + res = r; + goto free_list; + } + + message = NULL; + cur_token = 0; + r = mailimf_optional_fields_parse(msg_header, header_len, + &cur_token, &fields); + if (r != MAILIMF_NO_ERROR) { + res = MAIL_ERROR_PARSE; + goto free_list; + } + + mailsession_fetch_result_free(session, msg_header); + } + + r = mailsession_get_message_flags(session, info->index, &flags); + if (r != MAIL_NO_ERROR) { + res = r; + goto free_list; + } + + if (match_messages(message, info->size, fields, flags, + charset, key)) { + uint32_t * pnum; + + pnum = malloc(sizeof(* pnum)); + if (pnum == NULL) { + if (message != NULL) + mailsession_fetch_result_free(session, message); + res = MAIL_ERROR_MEMORY; + goto free_list; + } + + * pnum = info->index; + + r = clist_append(list, pnum); + if (r < 0) { + free(pnum); + if (message != NULL) + mailsession_fetch_result_free(session, message); + res = MAIL_ERROR_MEMORY; + goto free_list; + } + } + + if (message != NULL) + mailsession_fetch_result_free(session, message); + } + + search_result = mail_search_result_new(list); + if (search_result == NULL) { + res = MAIL_ERROR_MEMORY; + goto free_list; + } + + * result = search_result; + + return MAIL_NO_ERROR; + + free_list: + clist_foreach(list, (clist_func) free, NULL); + clist_free(list); + mailmessage_list_free(env_list); + return res; +} +#endif + +#if 0 +int maildriver_generic_search_messages(mailsession * session, char * charset, + struct mail_search_key * key, + struct mail_search_result ** result) +{ + return MAIL_ERROR_NOT_IMPLEMENTED; +} +#endif + +int +maildriver_env_list_to_msg_list(struct mailmessage_list * env_list, + clist ** result) +{ + clist * msg_list; + int r; + int res; + unsigned int i; + + msg_list = clist_new(); + if (msg_list == NULL) { + res = MAIL_ERROR_MEMORY; + goto err; + } + + for(i = 0 ; i < carray_count(env_list->msg_tab) ; i ++) { + mailmessage * msg; + + msg = carray_get(env_list->msg_tab, i); + + if (msg->msg_fields == NULL) { + uint32_t * pindex; + + pindex = malloc(sizeof(* pindex)); + if (pindex == NULL) { + res = MAIL_ERROR_MEMORY; + goto free_msg_list; + } + + * pindex = msg->msg_index; + + r = clist_append(msg_list, pindex); + if (r < 0) { + free(pindex); + res = MAIL_ERROR_MEMORY; + goto free_msg_list; + } + + } + } + + * result = msg_list; + + return MAIL_NO_ERROR; + + free_msg_list: + clist_foreach(msg_list, (clist_func) free, NULL); + clist_free(msg_list); + err: + return res; +} + + +int +maildriver_env_list_to_msg_list_no_flags(struct mailmessage_list * env_list, + clist ** result) +{ + clist * msg_list; + int r; + int res; + unsigned int i; + + msg_list = clist_new(); + if (msg_list == NULL) { + res = MAIL_ERROR_MEMORY; + goto err; + } + + for(i = 0 ; i < carray_count(env_list->msg_tab) ; i ++) { + mailmessage * msg; + + msg = carray_get(env_list->msg_tab, i); + + if (msg->msg_flags == NULL) { + uint32_t * pindex; + + pindex = malloc(sizeof(* pindex)); + if (pindex == NULL) { + res = MAIL_ERROR_MEMORY; + goto free_msg_list; + } + + * pindex = msg->msg_index; + + r = clist_append(msg_list, pindex); + if (r < 0) { + free(pindex); + res = MAIL_ERROR_MEMORY; + goto free_msg_list; + } + + } + } + + * result = msg_list; + + return MAIL_NO_ERROR; + + free_msg_list: + clist_foreach(msg_list, (clist_func) free, NULL); + clist_free(msg_list); + err: + return res; +} + + + +int maildriver_imf_error_to_mail_error(int error) +{ + switch (error) { + case MAILIMF_NO_ERROR: + return MAIL_NO_ERROR; + + case MAILIMF_ERROR_PARSE: + return MAIL_ERROR_PARSE; + + case MAILIMF_ERROR_MEMORY: + return MAIL_ERROR_MEMORY; + + case MAILIMF_ERROR_INVAL: + return MAIL_ERROR_INVAL; + + case MAILIMF_ERROR_FILE: + return MAIL_ERROR_FILE; + + default: + return MAIL_ERROR_INVAL; + } +} + +char * maildriver_quote_mailbox(char * mb) +{ + MMAPString * gstr; + char * str; + + gstr = mmap_string_new(""); + if (gstr == NULL) + return NULL; + + while (* mb != 0) { + char hex[3]; + + if (((* mb >= 'a') && (* mb <= 'z')) || + ((* mb >= 'A') && (* mb <= 'Z')) || + ((* mb >= '0') && (* mb <= '9'))) + mmap_string_append_c(gstr, * mb); + else { + if (mmap_string_append_c(gstr, '%') == NULL) + goto free; + snprintf(hex, 3, "%02x", (unsigned char) (* mb)); + if (mmap_string_append(gstr, hex) == NULL) + goto free; + } + mb ++; + } + + str = strdup(gstr->str); + if (str == NULL) + goto free; + + mmap_string_free(gstr); + + return str; + + free: + mmap_string_free(gstr); + return NULL; +} + + + +int maildriver_cache_clean_up(struct mail_cache_db * cache_db_env, + struct mail_cache_db * cache_db_flags, + struct mailmessage_list * env_list) +{ + chash * hash_exist; + int res; + int r; + char keyname[PATH_MAX]; + unsigned int i; + + /* flush cache */ + + hash_exist = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL); + if (hash_exist == NULL) { + res = MAIL_ERROR_MEMORY; + goto err; + } + + for(i = 0 ; i < carray_count(env_list->msg_tab) ; i ++) { + mailmessage * msg; + chashdatum key; + chashdatum value; + + msg = carray_get(env_list->msg_tab, i); + + value.data = NULL; + value.len = 0; + + if (cache_db_env != NULL) { + snprintf(keyname, PATH_MAX, "%s-envelope", msg->msg_uid); + + key.data = keyname; + key.len = strlen(keyname); + r = chash_set(hash_exist, &key, &value, NULL); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto free; + } + } + + if (cache_db_flags != NULL) { + snprintf(keyname, PATH_MAX, "%s-flags", msg->msg_uid); + + key.data = keyname; + key.len = strlen(keyname); + r = chash_set(hash_exist, &key, &value, NULL); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto free; + } + } + } + + /* clean up */ + if (cache_db_env != NULL) + mail_cache_db_clean_up(cache_db_env, hash_exist); + if (cache_db_flags != NULL) + mail_cache_db_clean_up(cache_db_flags, hash_exist); + + chash_free(hash_exist); + + return MAIL_NO_ERROR; + + free: + chash_free(hash_exist); + err: + return res; +} + + +/* + maildriver_message_cache_clean_up() + + remove files in cache_dir that does not correspond to a message. + + get_uid_from_filename() modifies the given filename so that it + is a uid when returning from the function. If get_uid_from_filename() + clears the content of file (set to empty string), this means that + this file should not be deleted. +*/ + +int maildriver_message_cache_clean_up(char * cache_dir, + struct mailmessage_list * env_list, + void (* get_uid_from_filename)(char *)) +{ + chash * hash_exist; + DIR * d; + char cached_filename[PATH_MAX]; + struct dirent * ent; + char keyname[PATH_MAX]; + unsigned int i; + int res; + int r; + + /* remove files */ + + hash_exist = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL); + if (hash_exist == NULL) { + res = MAIL_ERROR_MEMORY; + goto err; + } + + for(i = 0 ; i < carray_count(env_list->msg_tab) ; i ++) { + mailmessage * msg; + chashdatum key; + chashdatum value; + + msg = carray_get(env_list->msg_tab, i); + + key.data = msg->msg_uid; + key.len = strlen(msg->msg_uid); + value.data = NULL; + value.len = 0; + r = chash_set(hash_exist, &key, &value, NULL); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto free; + } + } + + d = opendir(cache_dir); + while ((ent = readdir(d)) != NULL) { + chashdatum key; + chashdatum value; + + if (strcmp(ent->d_name, ".") == 0) + continue; + + if (strcmp(ent->d_name, "..") == 0) + continue; + + if (strstr(ent->d_name, ".db") != NULL) + continue; + + strncpy(keyname, ent->d_name, sizeof(keyname)); + keyname[sizeof(keyname) - 1] = '\0'; + + get_uid_from_filename(keyname); + + if (* keyname == '\0') + continue; + + key.data = keyname; + key.len = strlen(keyname); + + r = chash_get(hash_exist, &key, &value); + if (r < 0) { + snprintf(cached_filename, sizeof(cached_filename), + "%s/%s", cache_dir, ent->d_name); + unlink(cached_filename); + } + } + closedir(d); + + chash_free(hash_exist); + + return MAIL_NO_ERROR; + + free: + chash_free(hash_exist); + err: + return res; +} |