add filtering conditions message greater than, smaller than, and exactly matching...
[claws.git] / src / selective_download.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28 #include <gtk/gtkoptionmenu.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <regex.h>
35
36 #include "intl.h"
37 #include "main.h"
38 #include "prefs.h"
39 #include "prefs_matcher.h"
40 #include "prefs_filtering.h"
41 #include "prefs_common.h"
42 #include "prefs_account.h"
43 #include "account.h"
44 #include "mainwindow.h"
45 #include "foldersel.h"
46 #include "manage_window.h"
47 #include "stock_pixmap.h"
48 #include "inc.h"
49 #include "utils.h"
50 #include "gtkutils.h"
51 #include "alertpanel.h"
52 #include "folder.h"
53 #include "inc.h"
54 #include "filtering.h"
55 #include "matcher_parser.h"
56 #include "selective_download.h"
57 #include "procheader.h"
58
59 struct _SDView {
60         MainWindow *mainwin;
61
62         GtkWidget *window;
63         GtkWidget *btn_getmail;
64         GtkWidget *btn_remove;
65         GtkWidget *btn_done;
66         GtkWidget *clist;
67         GtkWidget *label_account_name;
68         GtkWidget *label_mails;
69 }selective;
70
71 GSList *header_item_list = NULL;
72
73 static GdkPixmap *checkboxonxpm;
74 static GdkPixmap *checkboxonxpmmask;
75 static GdkPixmap *checkboxoffxpm;
76 static GdkPixmap *checkboxoffxpmmask;
77
78 static void sd_window_create(MainWindow *mainwin);
79 static void sd_clist_set();
80 static void sd_remove_header_files();
81
82
83 void selective_download(MainWindow *mainwin)
84 {
85         PrefsAccount *account = cur_account;
86
87         summary_write_cache(mainwin->summaryview);
88         main_window_lock(mainwin);
89
90         sd_remove_header_files();
91
92         header_item_list = NULL;
93         
94         sd_window_create(mainwin);
95
96         stock_pixmap_gdk(selective.clist, STOCK_PIXMAP_CHECKBOX_OFF,
97                          &checkboxoffxpm, &checkboxoffxpmmask);
98         stock_pixmap_gdk(selective.clist, STOCK_PIXMAP_CHECKBOX_ON,
99                          &checkboxonxpm, &checkboxonxpmmask);
100         
101         gtk_label_set_text(GTK_LABEL(selective.label_account_name), account->account_name);
102 }
103
104
105 /* sd_remove_header_files()
106  * 
107  * - removes any stale header files in HEADER_CACHE_DIR 
108  *
109  */
110 static void sd_remove_header_files()
111 {
112         gchar *path = g_strconcat(get_header_cache_dir(), G_DIR_SEPARATOR_S, NULL);
113         
114         remove_all_files(path);
115         g_free(path);
116 }
117
118
119 /* sd_toggle_btn_remove()
120  *
121  * - checks whether at least on email is selected for deletion
122  *   if so, untoggle remove button
123  */
124 static gboolean sd_toggle_btn_remove()
125 {
126         GSList *cur;
127         gint num = 0;
128         gpointer row;
129
130
131         for (cur = header_item_list; cur != NULL; cur = cur->next) {
132                 HeaderItems *item = (HeaderItems*)cur->data;
133                 
134                 if (item->state == CHECKED) {
135                         gtk_widget_set_sensitive (selective.btn_remove, TRUE);
136                         return CHECKED;
137                 }
138                 num++;
139         }
140
141         gtk_widget_set_sensitive (selective.btn_remove, FALSE);
142         return UNCHECKED;
143 }
144
145
146 /* sd_header_filter(MsgInfo *msginfo)
147  *
148  * - parse header line and look for any applying filtering rules
149  * - if message matches other MATCHACTION --> return
150  *
151  */
152 gboolean sd_header_filter(MsgInfo *msginfo)
153 {
154         GSList *rules;
155
156         /* parse header line for line */
157         for (rules = global_processing; rules != NULL; rules = rules->next) { 
158
159                 FilteringProp *prop = (FilteringProp*) rules->data; 
160                 gchar line[POPBUFSIZE];
161
162                 if ( matcherlist_match(prop->matchers, msginfo) ) {
163                         if (prop->action->type == MATCHACTION_DELETE_ON_SERVER) {
164                                 return CHECKED;
165                         }
166                         else 
167                                 return UNCHECKED;
168                 }
169                 
170         }
171         return UNCHECKED;
172 }
173
174
175 /* sd_get_msginfo_from_file(const gchar *filename)
176  *
177  * - retrieve msginfo from saved header files
178  */
179 MsgInfo *sd_get_msginfo_from_file(const gchar *filename)
180 {
181         FILE *fp;
182         gchar buf[BUFFSIZE];
183         MsgInfo *msginfo  = g_new0(MsgInfo, 1);
184         MsgFlags msgflags = { 0, 0 };
185
186         msginfo  = (MsgInfo*) procheader_parse(filename, msgflags, TRUE, FALSE);
187         if ( (fp = fopen(filename, "rb")) != NULL ) {
188                 static HeaderEntry hentry[] = { { SIZE_HEADER, NULL, FALSE},
189                                                 { NULL, NULL, FALSE} };
190                 
191                 procheader_get_one_field (buf, sizeof(buf), fp, hentry);
192                 fclose(fp);
193         }
194
195         if (buf)
196                 msginfo->size = (off_t)atoi(buf + SIZE_HEADER_LEN);
197         else
198                 msginfo->size = 0;
199         
200         return msginfo;
201 }
202
203
204 /* sd_clist_set()
205  *
206  * - fill clist and set toggle button accordingly
207  */
208 void sd_clist_set()
209 {
210         gint index = 1;
211
212         gchar *path     = g_strconcat(get_header_cache_dir(), G_DIR_SEPARATOR_S, NULL);
213         gchar *filename = g_strdup_printf("%s%i", path, index);
214         
215         while ( is_file_exist(filename) ) {
216
217                 MsgInfo *msginfo  = sd_get_msginfo_from_file(filename);
218                 HeaderItems *line = g_new0 (HeaderItems, 1);
219                 gchar *row[4];
220                 gint msgid;     
221                 
222                 line->index = index;
223
224                 row[0] = "";
225                 row[1] = line->from    = msginfo->from;
226                 row[2] = line->subject = msginfo->subject;
227                 row[3] = line->size    = to_human_readable(msginfo->size);
228                 
229                 gtk_clist_append (GTK_CLIST(selective.clist), row);
230
231                 msginfo->folder = folder_get_default_processing();
232
233                 /* move msg file to drop folder */
234                 if ((msginfo->msgnum = folder_item_add_msg(msginfo->folder, 
235                                                            filename, TRUE)) < 0) {
236                         unlink(filename);
237                         return;
238                 }
239                 
240                 if (sd_header_filter(msginfo)) {
241                         line->state = CHECKED;
242                         gtk_clist_set_pixmap (GTK_CLIST (selective.clist), index - 1, 0,
243                                               checkboxonxpm, checkboxonxpmmask);
244                 }
245                 else {
246                         line->state = UNCHECKED;
247                         gtk_clist_set_pixmap (GTK_CLIST (selective.clist), index - 1, 0,
248                                               checkboxoffxpm, checkboxoffxpmmask);
249                 }
250                 
251                 folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
252
253                 header_item_list = g_slist_append(header_item_list, line);
254                 
255                 index++;
256                 filename = g_strdup_printf("%s%i", path, index);
257         }
258         
259         sd_toggle_btn_remove();
260
261         g_free(path);
262         g_free(filename);
263 }
264
265
266 /* --- Callbacks -- */
267 static void sd_btn_remove_cb()
268 {
269         GSList *cur;
270         PrefsAccount *account = cur_account;
271         gboolean enable_dele  = FALSE;
272
273         account->to_delete = NULL;
274
275         /* loop through list collecting mails marked for delete */
276         for (cur = header_item_list; cur != NULL; cur = cur->next) {
277                 
278                 HeaderItems *items = (HeaderItems*)cur->data;
279                 
280                 if (items->state == CHECKED) {
281                         gchar *row[4];
282
283                         /* replace deleted Mail */
284                         row[0] = "D";
285                         row[1] = items->from;
286                         row[2] = items->subject;
287                         row[3] = items->size;
288
289                         account->to_delete = g_slist_append(account->to_delete, 
290                                                             GINT_TO_POINTER(items->index));
291
292                         gtk_clist_remove(GTK_CLIST(selective.clist), items->index-1);
293                         gtk_clist_insert(GTK_CLIST(selective.clist), items->index-1, row);
294                         gtk_clist_set_selectable(GTK_CLIST(selective.clist), 
295                                                  items->index - 1, FALSE);
296                         enable_dele = TRUE;
297                         debug_print(_("marked to delete %i\n"), 
298                                     items->index);
299                         
300                         items->state = UNCHECKED;
301                 }
302         }
303
304
305         if (enable_dele == TRUE) {
306                 inc_selective_download(selective.mainwin, DELE_HEADER);
307         }
308
309         sd_toggle_btn_remove(); 
310
311         g_slist_free(account->to_delete);
312         account->to_delete = NULL;      
313 }
314
315 static void sd_btn_receive_cb()
316 {
317
318         manage_window_focus_in(selective.window, NULL, NULL);
319
320         inc_selective_download(selective.mainwin, RETR_HEADER);
321
322         gtk_clist_clear(GTK_CLIST(selective.clist));
323         sd_clist_set();
324 }
325
326 static void sd_btn_done_cb()
327 {
328         sd_remove_header_files();
329         
330         if (header_item_list)
331                 slist_free_strings (header_item_list);
332
333         gtk_widget_hide(selective.window);
334         main_window_unlock(selective.mainwin);
335 }
336
337 static void sd_select_row_cb(GtkCList *clist, gint row, gint column,
338                              GdkEvent *event, gpointer user_data)
339 {
340
341         if ((row >= 0) && (column >= 0)) {
342                 HeaderItems *item = (HeaderItems*) g_slist_nth_data (header_item_list, row);
343
344                 if (!item) return;
345                 if (!gtk_clist_get_selectable(GTK_CLIST(selective.clist), row)) return;
346
347                 if (item->state == UNCHECKED) {
348                         item->state = CHECKED;
349                         gtk_clist_set_pixmap (GTK_CLIST (selective.clist), row, 0,
350                                               checkboxonxpm, checkboxonxpmmask);
351                 } else {
352                         item->state = UNCHECKED;
353                         gtk_clist_set_pixmap (GTK_CLIST (selective.clist), row, 0,
354                                               checkboxoffxpm, checkboxoffxpmmask);
355                 }
356                 sd_toggle_btn_remove();
357         }
358 }
359
360 static void sd_window_create(MainWindow *mainwin)
361 {
362         GtkWidget *window;
363         GtkWidget *table;
364         GtkWidget *label_mails;
365         GtkWidget *hbox_bottom;
366         GtkWidget *label_fixed;
367         GtkWidget *label_account_name;
368         GtkWidget *scrolledwindow;
369         GtkWidget *clist;
370         GtkWidget *label_state;
371         GtkWidget *label_from;
372         GtkWidget *label_subject;
373         GtkWidget *label_size;
374         GtkWidget *hbox_toolbar;
375         GtkWidget *toolbar;
376         GtkWidget *btn_receive;
377         GtkWidget *tmp_toolbar_icon;
378         GtkWidget *btn_remove;
379         GtkWidget *btn_done;
380
381         window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
382         gtk_object_set_data (GTK_OBJECT (window), "window", window);
383         gtk_window_set_title (GTK_WINDOW (window), _("Selective Download"));
384         gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
385         gtk_window_set_default_size (GTK_WINDOW (window), 450, 250);
386         
387         table = gtk_table_new (2, 2, FALSE);
388         gtk_widget_ref (table);
389         gtk_object_set_data_full (GTK_OBJECT (window), "table", table,
390                                   (GtkDestroyNotify) gtk_widget_unref);
391         gtk_widget_show (table);
392         gtk_container_add (GTK_CONTAINER (window), table);
393         
394         label_mails = gtk_label_new (_("0 Mail(s)"));
395         gtk_widget_ref (label_mails);
396         gtk_object_set_data_full (GTK_OBJECT (window), "label_mails", label_mails,
397                                   (GtkDestroyNotify) gtk_widget_unref);
398         gtk_widget_show (label_mails);
399         gtk_table_attach (GTK_TABLE (table), label_mails, 1, 2, 1, 2,
400                           (GtkAttachOptions) (GTK_FILL),
401                           (GtkAttachOptions) (0), 0, 0);
402         gtk_misc_set_alignment (GTK_MISC (label_mails), 0, 0.5);
403         
404         hbox_bottom = gtk_hbox_new (FALSE, 0);
405         gtk_widget_ref (hbox_bottom);
406         gtk_object_set_data_full (GTK_OBJECT (window), "hbox_bottom", hbox_bottom,
407                                   (GtkDestroyNotify) gtk_widget_unref);
408         gtk_widget_show (hbox_bottom);
409         gtk_table_attach (GTK_TABLE (table), hbox_bottom, 0, 1, 1, 2,
410                           (GtkAttachOptions) (GTK_FILL),
411                           (GtkAttachOptions) (GTK_FILL), 0, 0);
412         
413         label_fixed = gtk_label_new (_("current Account:"));
414         gtk_widget_ref (label_fixed);
415         gtk_object_set_data_full (GTK_OBJECT (window), "label_fixed", label_fixed,
416                                   (GtkDestroyNotify) gtk_widget_unref);
417         gtk_widget_show (label_fixed);
418         gtk_box_pack_start (GTK_BOX (hbox_bottom), label_fixed, FALSE, FALSE, 0);
419         
420         label_account_name = gtk_label_new (_("none"));
421         gtk_widget_ref (label_account_name);
422         gtk_object_set_data_full (GTK_OBJECT (window), "label_account_name", label_account_name,
423                                   (GtkDestroyNotify) gtk_widget_unref);
424         gtk_widget_show (label_account_name);
425         gtk_box_pack_start (GTK_BOX (hbox_bottom), label_account_name, TRUE, FALSE, 0);
426         
427         scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
428         gtk_widget_ref (scrolledwindow);
429         gtk_object_set_data_full (GTK_OBJECT (window), "scrolledwindow", scrolledwindow,
430                                   (GtkDestroyNotify) gtk_widget_unref);
431         gtk_widget_show (scrolledwindow);
432         gtk_table_attach (GTK_TABLE (table), scrolledwindow, 0, 1, 0, 1,
433                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
434                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
435         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
436         
437         clist = gtk_clist_new (4);
438         gtk_widget_ref (clist);
439         gtk_object_set_data_full (GTK_OBJECT (window), "clist", clist,
440                                   (GtkDestroyNotify) gtk_widget_unref);
441         gtk_widget_show (clist);
442         gtk_container_add (GTK_CONTAINER (scrolledwindow), clist);
443         gtk_container_set_border_width (GTK_CONTAINER (clist), 5);
444         gtk_clist_set_column_width (GTK_CLIST (clist), 0, 21);
445         gtk_clist_set_column_width (GTK_CLIST (clist), 1, 143);
446         gtk_clist_set_column_width (GTK_CLIST (clist), 2, 158);
447         gtk_clist_set_column_width (GTK_CLIST (clist), 3, 15);
448         gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_BROWSE);
449         gtk_clist_column_titles_show (GTK_CLIST (clist));
450         
451         label_state = gtk_label_new (_("#"));
452         gtk_widget_ref (label_state);
453         gtk_object_set_data_full (GTK_OBJECT (window), "label_state", label_state,
454                                   (GtkDestroyNotify) gtk_widget_unref);
455         gtk_widget_show (label_state);
456         gtk_clist_set_column_widget (GTK_CLIST (clist), 0, label_state);
457         
458         label_from = gtk_label_new (_("From"));
459         gtk_widget_ref (label_from);
460         gtk_object_set_data_full (GTK_OBJECT (window), "label_from", label_from,
461                                   (GtkDestroyNotify) gtk_widget_unref);
462         gtk_widget_show (label_from);
463         gtk_clist_set_column_widget (GTK_CLIST (clist), 1, label_from);
464         
465         label_subject = gtk_label_new (_("Subject"));
466         gtk_widget_ref (label_subject);
467         gtk_object_set_data_full (GTK_OBJECT (window), "label_subject", label_subject,
468                                   (GtkDestroyNotify) gtk_widget_unref);
469         gtk_widget_show (label_subject);
470         gtk_clist_set_column_widget (GTK_CLIST (clist), 2, label_subject);
471         
472         label_size = gtk_label_new (_("Size"));
473         gtk_widget_ref (label_size);
474         gtk_object_set_data_full (GTK_OBJECT (window), "label_size", label_size,
475                                   (GtkDestroyNotify) gtk_widget_unref);
476         gtk_widget_show (label_size);
477         gtk_clist_set_column_widget (GTK_CLIST (clist), 3, label_size);
478         
479         hbox_toolbar = gtk_hbox_new (FALSE, 0);
480         gtk_widget_ref (hbox_toolbar);
481         gtk_object_set_data_full (GTK_OBJECT (window), "hbox_toolbar", hbox_toolbar,
482                                   (GtkDestroyNotify) gtk_widget_unref);
483         gtk_widget_show (hbox_toolbar);
484         gtk_table_attach (GTK_TABLE (table), hbox_toolbar, 1, 2, 0, 1,
485                           (GtkAttachOptions) (GTK_FILL),
486                           (GtkAttachOptions) (GTK_FILL), 0, 0);
487         
488         toolbar = gtk_toolbar_new (GTK_ORIENTATION_VERTICAL, GTK_TOOLBAR_BOTH);
489         gtk_widget_ref (toolbar);
490         gtk_object_set_data_full (GTK_OBJECT (window), "toolbar", toolbar,
491                                   (GtkDestroyNotify) gtk_widget_unref);
492         gtk_widget_show (toolbar);
493         gtk_box_pack_start (GTK_BOX (hbox_toolbar), toolbar, FALSE, FALSE, 0);
494         gtk_toolbar_set_space_size (GTK_TOOLBAR (toolbar), 20);
495         gtk_toolbar_set_space_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_SPACE_LINE);
496         
497         tmp_toolbar_icon = stock_pixmap_widget(hbox_toolbar, STOCK_PIXMAP_MAIL_RECEIVE);
498         btn_receive = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
499                                                   GTK_TOOLBAR_CHILD_BUTTON,
500                                                   NULL,
501                                                   _("Receive"),
502                                                   _("preview E-Mail"), NULL,
503                                                   tmp_toolbar_icon, NULL, NULL);
504         gtk_widget_ref (btn_receive);
505         gtk_object_set_data_full (GTK_OBJECT (window), "btn_receive", btn_receive,
506                                   (GtkDestroyNotify) gtk_widget_unref);
507         gtk_widget_show (btn_receive);
508         
509         gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
510         
511         tmp_toolbar_icon = stock_pixmap_widget(hbox_toolbar, STOCK_PIXMAP_CONTINUE);
512         btn_remove = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
513                                                  GTK_TOOLBAR_CHILD_BUTTON,
514                                                  NULL,
515                                                  _("Remove"),
516                                                  _("remove selected E-Mails"), NULL,
517                                                  tmp_toolbar_icon, NULL, NULL);
518         gtk_widget_ref (btn_remove);
519         gtk_object_set_data_full (GTK_OBJECT (window), "btn_remove", btn_remove,
520                                   (GtkDestroyNotify) gtk_widget_unref);
521         gtk_widget_set_sensitive (btn_remove, FALSE);
522         
523         gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
524         
525         tmp_toolbar_icon = stock_pixmap_widget (hbox_toolbar, STOCK_PIXMAP_CLOSE);
526         btn_done = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
527                                                GTK_TOOLBAR_CHILD_BUTTON,
528                                                NULL,
529                                                _("Done"),
530                                                _("Exit Dialog"), NULL,
531                                                tmp_toolbar_icon, NULL, NULL);
532         gtk_widget_ref (btn_done);
533         gtk_object_set_data_full (GTK_OBJECT (window), "btn_done", btn_done,
534                                   (GtkDestroyNotify) gtk_widget_unref);
535         gtk_widget_show (btn_done);
536         
537         gtk_signal_connect (GTK_OBJECT (window), "destroy",
538                             GTK_SIGNAL_FUNC (sd_btn_done_cb),
539                             NULL);
540         gtk_signal_connect (GTK_OBJECT (btn_receive), "clicked",
541                             GTK_SIGNAL_FUNC (sd_btn_receive_cb),
542                             NULL);
543         gtk_signal_connect (GTK_OBJECT (btn_remove), "clicked",
544                             GTK_SIGNAL_FUNC (sd_btn_remove_cb),
545                             NULL);
546         gtk_signal_connect (GTK_OBJECT (btn_done), "clicked",
547                             GTK_SIGNAL_FUNC (sd_btn_done_cb),
548                             NULL);
549         gtk_signal_connect (GTK_OBJECT (clist), "select_row",
550                             GTK_SIGNAL_FUNC (sd_select_row_cb),
551                             NULL);
552
553
554
555         selective.mainwin            = mainwin;
556         selective.window             = window;
557         selective.btn_remove         = btn_remove;
558         selective.btn_done           = btn_done;
559         selective.clist              = clist;
560         selective.label_account_name = label_account_name;
561         selective.label_mails        = label_mails;
562
563         gtk_widget_show_all(window);
564 }
565