/* * libEtPan! -- a mail stuff library * * Copyright (C) 2001, 2002 - 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 REGENTS 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 REGENTS 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; }