/* elmo - ELectronic Mail Operator Copyright (C) 2002, 2003, 2004 rzyjontko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ---------------------------------------------------------------------- handle mail folder */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #include #include #include "ecurses.h" #include "folder.h" #include "xmalloc.h" #include "status.h" #include "error.h" #include "ask.h" #include "cmd.h" #include "wrapbox.h" #include "bayes.h" #include "mybox.h" #include "hook.h" #include "exec.h" #include "eprintf.h" #include "read.h" #include "str.h" #include "select.h" #include "color.h" #include "gettext.h" #include "label.h" #include "interface.h" #include "frames.h" #include "search.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #define MIN(a,b) (((a)<(b))?(a):(b)) #define PREAMBLE do { if (wrapbox_mail_count () < 0) return; } while (0) /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ /* Folder window consists of select_t object, and an optional label. */ static elabel_t *label = NULL; static select_t *folder_select = NULL; /* This is used in print_line, to avoid frequent alloc/free calls. */ static str_t *line_str = NULL; /* Colors used in folder window. */ static chtype text_color; static chtype tree_color; static chtype tree_bar_color; static chtype hilight_color; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static void set_tree_color (WINDOW *win) { if (win == folder_select->win){ wattrset (win, tree_color); } else { wattrset (win, tree_bar_color); } } static void unset_tree_color (WINDOW *win) { if (win == folder_select->win){ wattrset (win, text_color); } else { wattrset (win, select_bar_color ()); } } static void set_hilight_color (WINDOW *win) { if (win == folder_select->win) wattrset (win, hilight_color); else wattrset (win, select_hilight_color ()); } static void unset_hilight_color (WINDOW *win) { if (win == folder_select->win) wattrset (win, text_color); else wattrset (win, select_bar_color ()); } static int add_tree (WINDOW *window, mail_t *mail, int maxlen) { int y, x; int ret; if (mail == NULL) return 0; if (mail->tree == NULL || mail->tree_len == 0) return 0; if (maxlen <= 0) return 0; getyx (window, y, x); set_tree_color (window); ret = window_addchnstr (window, mail->tree, maxlen); unset_tree_color (window); return ret; } static int add_string (WINDOW *win, char *str, int maxlen, search_t *search) { int pos = -1; int len = 1; if (maxlen <= 0) return 0; if (search) pos = search_perform (search, str); if (pos != -1){ len = search->pattern->len; pos -= len - 1; if (pos < 0) pos = 0; window_addnstr (win, str, pos); set_hilight_color (win); window_addnstr (win, str + pos, len); unset_hilight_color (win); maxlen -= pos + len; } return pos + len + window_addnstr (win, str + pos + len, maxlen - 1); } static int add_subject (WINDOW *win, mail_t *mail, int maxlen, search_t *search) { if (mail == NULL || mail->subject == NULL) return 0; return add_string (win, mail->subject, maxlen, search); } static void print_line (WINDOW *win, int maxlen, int index, search_t *search) { mail_t *mail = mail_array_get (wrapbox_marray, index); if (win == NULL) return; if (line_str == NULL) line_str = str_create (); if (mail == NULL) str_clear (line_str); else eprintf_mail_str (mybox_format, mail, line_str); maxlen -= add_string (win, line_str->str, maxlen, search); if (wrapbox_marray && wrapbox_marray->order == ORDER_THREAD) maxlen -= add_tree (win, mail, maxlen); maxlen -= add_subject (win, mail, maxlen, search); while (maxlen-- > 0) window_addch (win, ' '); } static void redraw_label (void) { if (label == NULL) return; if (line_str == NULL) line_str = str_create (); eprintf_mail_desc (mybox_format, line_str); label_set_text (label, line_str->str); label_redraw (label); } static void copy_tree (chtype *to, chtype *from) { while (*from && *from != '>'){ if (*from == ' ' || *from == frames_ch (ACS_VLINE)) *to = *from; else if (*from == frames_ch (ACS_LLCORNER) || *from == frames_ch (ACS_HLINE)) *to = ' '; else if (*from == frames_ch (ACS_LTEE)) *to = frames_ch (ACS_VLINE); else *to = *from; to++; from++; } } static void make_trees (void) { int i; int mail_count; mail_t *mail; mail_t *parent; mail_count = wrapbox_mail_count (); for (i = 0; i < mail_count; i++){ mail = mail_array_get (wrapbox_marray, i); parent = mail_array_get (wrapbox_marray, mail->parent); if (mail->tree) xfree (mail->tree); if (parent){ mail->tree_len = parent->tree_len + ((parent->tree_len) ? 2 : 4); mail->tree = xmalloc (mail->tree_len * sizeof (chtype)); mail->tree[mail->tree_len - 3] = frames_ch (ACS_HLINE); mail->tree[mail->tree_len - 2] = '>'; mail->tree[mail->tree_len - 1] = 0; copy_tree (mail->tree, parent->tree); if (mail->is_last_child) mail->tree[mail->tree_len - 4] = frames_ch (ACS_LLCORNER); else mail->tree[mail->tree_len - 4] = frames_ch (ACS_LTEE); } else { mail->tree_len = 0; mail->tree = xcalloc (1, sizeof (chtype)); } } } static void free_resources (void) { if (line_str) str_destroy (line_str); line_str = NULL; if (folder_select) select_close (folder_select); folder_select = NULL; if (label) label_destroy (label); label = NULL; } static void display_status_info (void) { int count; int read; int old; int new; char *seek = NULL; if (wrapbox_marray == NULL) return; if (ask_for_default ("relative_names", NULL)) seek = strrchr (wrapbox_marray->path, '/'); if (seek == NULL) seek = wrapbox_marray->path; else seek++; count = wrapbox_marray->count; read = mail_array_read (wrapbox_marray); old = mail_array_old (wrapbox_marray); new = count - read - old; status_put_mailbox (seek, count, old, new); } static int folder_count (select_t *nothing) { return (wrapbox_marray) ? wrapbox_marray->count : 0; } /* This file is generated by interface.pl script from interface.desc, and inc.in. */ static WINDOW *interface_init (void); #include "folder.inc" /**************************************************************************** * DISPLAY FUNCTIONS ****************************************************************************/ void folder_init (void) { WINDOW *window; char *box; window = interface_init (); folder_select = select_open (window, 0, print_line, folder_count); window_set_functions (window, folder_refresh, folder_redraw, folder_set_focus, folder_unset_focus); window_show (window); box = mybox_start (); if (box){ mybox_switching_to (box); folder_read_box (box); xfree (box); } } void folder_redraw (void) { select_redraw (folder_select); redraw_label (); display_status_info (); } void folder_refresh (void) { select_show (folder_select); if (label) label_show (label); } void folder_set_focus (void) { if (label){ label_set_focus (label); } folder_redraw (); } void folder_unset_focus (void) { if (label){ label_unset_focus (label); redraw_label (); } } /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ mail_t * folder_mail_selected (void) { int mail_count = wrapbox_mail_count (); int index; mail_t *mail = NULL; index = folder_select->bar_pos; if (mail_count == -1 || index < 0 || index >= mail_count) return NULL; mail = mail_array_get (wrapbox_marray, index); return mail; } void folder_free_resources (void) { folder_at_leave (); free_resources (); } int folder_read_box (char *name) { int index; if (wrapbox_read_box (name)){ error_ (0, "%s is not a valid mailbox", name); return 1; } display_status_info (); mail_array_sort_date (wrapbox_marray); index = mail_array_first_unread_index (wrapbox_marray); if (index == -1) index = wrapbox_mail_count () - 1; select_goto (folder_select, index); folder_spam_check_all (); folder_sort_threads (); return 0; } void folder_update (void) { int index; mail_t *mail; void *ptr; void *ptr_next; void *ptr_prev; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); ptr = (mail) ? mail->place.generic : NULL; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos + 1); if (mail == NULL) mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); ptr_next = (mail) ? mail->place.generic : NULL; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos - 1); if (mail == NULL) mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); ptr_prev = (mail) ? mail->place.generic : NULL; wrapbox_refresh (); mail_array_sort_date (wrapbox_marray); index = mail_array_find_place (wrapbox_marray, ptr); if (index == -1) index = mail_array_find_place (wrapbox_marray, ptr_next); if (index == -1) index = mail_array_find_place (wrapbox_marray, ptr_prev); if (index != -1) select_goto (folder_select, index); folder_spam_check_all (); folder_sort_threads (); display_status_info (); folder_at_bar_move (); } void folder_flush (void) { PREAMBLE; folder_spam_check_all (); folder_spam_flush (); folder_update (); } /**************************************************************************** * SEARCHING PRIVATE FUNCTIONS ****************************************************************************/ static int match_subject (search_t *search, int index) { mail_t *mail = mail_array_get (wrapbox_marray, index); if (mail == NULL) return 0; if (search == NULL) return 1; if (mail->subject == NULL) return 0; if (search_perform (search, mail->subject) != -1) return 1; return 0; } static int match_from (search_t *search, int index) { mail_t *mail = mail_array_get (wrapbox_marray, index); if (mail == NULL) return 0; if (search == NULL) return 1; if (mail->from == NULL || mail->from->full == NULL) return 0; if (search_perform (search, mail->from->full) != -1) return 1; return 0; } static int match_any (search_t *search, int index) { return match_subject (search, index) || match_from (search, index); } /**************************************************************************** * SEARCHING FUNCTIONS ****************************************************************************/ void folder_search_backward (void) { select_search_setup_backward (folder_select, match_any); } void folder_search_forward (void) { select_search_setup_forward (folder_select, match_any); } /**************************************************************************** * SORTING FUNCTIONS ****************************************************************************/ void folder_sort_threads (void) { int index; mail_t *mail; void *ptr = NULL; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail){ ptr = mail->place.generic; } mail_array_sort_threads (wrapbox_marray); if (mail){ index = mail_array_find_place (wrapbox_marray, ptr); } else { index = wrapbox_marray->count - 1; } make_trees (); select_goto (folder_select, index); folder_redraw (); } void folder_sort_date (void) { int index; mail_t *mail; void *ptr = NULL; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail) { ptr = mail->place.generic; } mail_array_sort_date (wrapbox_marray); index = mail_array_find_place (wrapbox_marray, ptr); select_goto (folder_select, index); folder_redraw (); } void folder_sort_from (void) { int index; mail_t *mail; void *ptr = NULL; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail) { ptr = mail->place.generic; } mail_array_sort_from (wrapbox_marray); index = mail_array_find_place (wrapbox_marray, ptr); select_goto (folder_select, index); folder_redraw (); } void folder_sort_subject (void) { int index; mail_t *mail; void *ptr = NULL; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail) { ptr = mail->place.generic; } mail_array_sort_subject (wrapbox_marray); index = mail_array_find_place (wrapbox_marray, ptr); select_goto (folder_select, index); folder_redraw (); } /**************************************************************************** * BAR MOVEMENT FUNCTIONS ****************************************************************************/ void folder_page_next (void) { PREAMBLE; select_next_page (folder_select); folder_at_bar_move (); } void folder_page_prev (void) { PREAMBLE; select_prev_page (folder_select); folder_at_bar_move (); } void folder_bar_next (void) { PREAMBLE; select_next (folder_select); folder_at_bar_move (); } void folder_bar_prev (void) { PREAMBLE; select_prev (folder_select); folder_at_bar_move (); } void folder_scroll_down (void) { PREAMBLE; select_scroll_down (folder_select); } void folder_scroll_up (void) { PREAMBLE; select_scroll_up (folder_select); } void folder_bar_first (void) { PREAMBLE; select_first (folder_select); folder_at_bar_move (); } void folder_bar_last (void) { PREAMBLE; select_last (folder_select); folder_at_bar_move (); } void folder_recenter (void) { PREAMBLE; select_recenter (folder_select); } void folder_next_unread (void) { int index; PREAMBLE; index = mail_array_find_unread (wrapbox_marray, folder_select->bar_pos + 1); if (index != -1) select_goto (folder_select, index); folder_redraw (); } void folder_prev_unread (void) { int index; PREAMBLE; index = mail_array_find_unread_back (wrapbox_marray, folder_select->bar_pos - 1); if (index != -1) select_goto (folder_select, index); folder_redraw (); } /**************************************************************************** * ACTIONS PRIVATE FUNCTIONS ****************************************************************************/ static void action_move_mail (mail_t *mail, char *box) { if (box == NULL) return; if (wrapbox_move_mail_to (mail, box)){ error_ (0, _("couldn't move mail to %s"), box); } } static void action_remove_mail (mail_t *mail, char *nothing) { wrapbox_remove (mail); } static void action_make_read (mail_t *mail, char *nothing) { mail->flags |= FLAG_READ; } static void action_check_spam (mail_t *mail, char *nothing) { if (bayes_is_spam (mail)){ mail->flags &= ~ FLAG_NOSPAM; mail->flags &= ~ FLAG_LEGITIMATE; mail->flags |= FLAG_SPAM; } else { mail->flags &= ~ FLAG_SPAM; mail->flags |= FLAG_NOSPAM; } } static void action_delete_spam (mail_t *mail, char *trash) { if (mail->flags & FLAG_LEGITIMATE){ bayes_unscan_legitimate (mail); } bayes_scan_spam (mail); wrapbox_move_mail_to (mail, trash); } static void action_is_not_spam (mail_t *mail, char *nothing) { mail->flags |= FLAG_NOSPAM; mail->flags &= ~ FLAG_SPAM; } static void action_remove_flag (mail_t *mail, char *flag) { if (*flag == 'L') mail->flags &= ~ FLAG_LEGITIMATE; } static void take_action (void (*fun)(mail_t *, char*), char *arg) { int i; int took = 0; mail_t *mail; PREAMBLE; for (i = 0; i < wrapbox_marray->count; i++){ mail = mail_array_get (wrapbox_marray, i); if (mail == NULL) continue; if (mail->flags & FLAG_FLAGGED){ mail->flags &= ~ FLAG_FLAGGED; fun (mail, arg); took = 1; } } if (! took){ mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail) fun (mail, arg); } } /**************************************************************************** * ACTIONS INTERFACE FUNCTIONS ****************************************************************************/ void folder_after_read (void) { mail_t *mail; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail == NULL) return; mail->flags |= FLAG_OLD; mail->flags |= FLAG_READ; wrapbox_marray->old_count = -1; wrapbox_marray->read_count = -1; folder_redraw (); } void folder_after_reply (void) { mail_t *mail; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail == NULL) return; mail->flags |= FLAG_ANSWERED; folder_redraw (); } void folder_after_fwd (void) { mail_t *mail; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail == NULL) return; mail->flags |= FLAG_PASSED; folder_redraw (); } void folder_kill_mail (void) { PREAMBLE; take_action (action_remove_mail, NULL); folder_update (); } void folder_delete_mail (void) { char *box; PREAMBLE; box = mybox_trash (); if (box == NULL){ folder_kill_mail (); } else { take_action (action_move_mail, box); xfree (box); folder_update (); } } void folder_move_mail (void) { char *box; PREAMBLE; box = mybox_select_subdir (); if (box == NULL) return; take_action (action_move_mail, box); xfree (box); folder_update (); } void folder_make_read (void) { PREAMBLE; take_action (action_make_read, NULL); folder_redraw (); } void folder_at_leave (void) { exec_t *exec; exec = exec_lookup_fun (folder_at_leave); hook_execute (exec->hook); folder_spam_flush (); wrapbox_apply_flags (); wrapbox_dump_box (); } void folder_at_bar_move (void) { exec_t *exec; exec = exec_lookup_fun (folder_at_bar_move); hook_execute (exec->hook); } /**************************************************************************** * FLAGGING FUNCTIONS ****************************************************************************/ void folder_toggle_flag (void) { mail_t *mail; PREAMBLE; mail = mail_array_get (wrapbox_marray, folder_select->bar_pos); if (mail == NULL) return; if (mail->flags & FLAG_FLAGGED) mail->flags &= ~ FLAG_FLAGGED; else mail->flags |= FLAG_FLAGGED; folder_redraw (); } void folder_flag_all (void) { int i; mail_t *mail; PREAMBLE; for (i = 0; i < wrapbox_marray->count; i++){ mail = mail_array_get (wrapbox_marray, i); mail->flags |= FLAG_FLAGGED; } folder_redraw (); } void folder_unflag_all (void) { int i; mail_t *mail; PREAMBLE; for (i = 0; i < wrapbox_marray->count; i++){ mail = mail_array_get (wrapbox_marray, i); mail->flags &= ~ FLAG_FLAGGED; } folder_redraw (); } void folder_flag_invert (void) { int i; mail_t *mail; PREAMBLE; for (i = 0; i < wrapbox_marray->count; i++){ mail = mail_array_get (wrapbox_marray, i); if (mail->flags & FLAG_FLAGGED) mail->flags &= ~ FLAG_FLAGGED; else mail->flags |= FLAG_FLAGGED; } folder_redraw (); } void folder_flag_duplicates (void) { int i; mail_t *mail; PREAMBLE; for (i = 0; i < wrapbox_marray->count; i++){ mail = mail_array_get (wrapbox_marray, i); if (mail->flags & FLAG_DUPLICATE) mail->flags |= FLAG_FLAGGED; } folder_redraw (); } void folder_remove_flag (void) { char *flag; PREAMBLE; flag = read_argument ("Flag: ", NULL, COMPLETE_FILES, HIDE_NO); if (flag && *flag != '\0') take_action (action_remove_flag, flag); folder_update (); } /**************************************************************************** * SPAM FUNCTIONS ****************************************************************************/ void folder_spam_check (void) { if (! mybox_protect) return; PREAMBLE; take_action (action_check_spam, NULL); folder_redraw (); } void folder_spam_check_all (void) { int i; mail_t *mail; if (! mybox_protect) return; PREAMBLE; for (i = 0; i < wrapbox_marray->count; i++){ mail = mail_array_get (wrapbox_marray, i); if ((mail->flags & FLAG_SPAM) || (mail->flags & FLAG_NOSPAM) || (mail->flags & FLAG_LEGITIMATE)) continue; if (bayes_is_spam (mail)) mail->flags |= FLAG_SPAM; else mail->flags |= FLAG_NOSPAM; } folder_redraw (); } void folder_spam_delete (void) { char *box; if (! mybox_protect) return; PREAMBLE; box = mybox_spam (); take_action (action_delete_spam, box); xfree (box); folder_update (); } void folder_spam_is_not (void) { if (! mybox_protect) return; PREAMBLE; take_action (action_is_not_spam, NULL); folder_redraw (); } void folder_spam_flush (void) { int i; char *box; mail_t *mail; if (! mybox_protect) return; PREAMBLE; box = mybox_spam (); for (i = 0; i < wrapbox_marray->count; i++){ mail = mail_array_get (wrapbox_marray, i); if (mail->flags & FLAG_SPAM){ bayes_scan_spam (mail); wrapbox_move_mail_to (mail, box); } else if ((mail->flags & FLAG_NOSPAM) && ! (mail->flags & FLAG_LEGITIMATE)){ mail->flags |= FLAG_LEGITIMATE; bayes_scan_legitimate (mail); } } if (box) xfree (box); } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE folder.c * ****************************************************************************/