No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Michael Zucchi <NotZed@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include <glib/gi18n.h>
31
32 #include <shell/e-shell.h>
33 #include <e-util/e-util.h>
34
35 #include <libemail-utils/mail-mt.h>
36
37 /* This is our hack, not part of libcamel. */
38 #include <libemail-engine/camel-null-store.h>
39
40 #include <libemail-engine/e-mail-folder-utils.h>
41 #include <libemail-engine/e-mail-session.h>
42 #include <libemail-engine/mail-folder-cache.h>
43 #include <libemail-engine/mail-ops.h>
44 #include <libemail-engine/mail-tools.h>
45
46 #include "e-mail-account-store.h"
47 #include "e-mail-ui-session.h"
48 #include "em-event.h"
49 #include "em-filter-rule.h"
50 #include "em-utils.h"
51 #include "mail-send-recv.h"
52
53 #define d(x)
54
55 /* ms between status updates to the gui */
56 #define STATUS_TIMEOUT (250)
57
58 /* pseudo-uri to key the send task on */
59 #define SEND_URI_KEY "send-task:"
60
61 #define SEND_RECV_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR
62
63 /* send/receive email */
64
65 /* ********************************************************************** */
66 /* This stuff below is independent of the stuff above */
67
68 /* this stuff is used to keep track of which folders filters have accessed, and
69 * what not. the thaw/refreeze thing doesn't really seem to work though */
70 struct _folder_info {
71 gchar *uri;
72 CamelFolder *folder;
73 time_t update;
74
75 /* How many times updated, to slow it
76 * down as we go, if we have lots. */
77 gint count;
78 };
79
80 struct _send_data {
81 GList *infos;
82
83 GtkDialog *gd;
84 gint cancelled;
85
86 /* Since we're never asked to update
87 * this one, do it ourselves. */
88 CamelFolder *inbox;
89 time_t inbox_update;
90
91 GMutex *lock;
92 GHashTable *folders;
93
94 GHashTable *active; /* send_info's by uri */
95 };
96
97 typedef enum {
98 SEND_RECEIVE, /* receiver */
99 SEND_SEND, /* sender */
100 SEND_UPDATE, /* imap-like 'just update folder info' */
101 SEND_INVALID
102 } send_info_t;
103
104 typedef enum {
105 SEND_ACTIVE,
106 SEND_CANCELLED,
107 SEND_COMPLETE
108 } send_state_t;
109
110 struct _send_info {
111 send_info_t type; /* 0 = fetch, 1 = send */
112 GCancellable *cancellable;
113 CamelSession *session;
114 CamelService *service;
115 gboolean keep_on_server;
116 send_state_t state;
117 GtkWidget *progress_bar;
118 GtkWidget *cancel_button;
119
120 gint again; /* need to run send again */
121
122 gint timeout_id;
123 gchar *what;
124 gint pc;
125
126 GtkWidget *send_account_label;
127 gchar *send_url;
128
129 /*time_t update;*/
130 struct _send_data *data;
131 };
132
133 static CamelFolder *
134 receive_get_folder (CamelFilterDriver *d,
135 const gchar *uri,
136 gpointer data,
137 GError **error);
138 static void send_done (gpointer data);
139
140 static struct _send_data *send_data = NULL;
141 static GtkWidget *send_recv_dialog = NULL;
142
143 static void
144 free_folder_info (struct _folder_info *info)
145 {
146 /*camel_folder_thaw (info->folder); */
147 mail_sync_folder (info->folder, NULL, NULL);
148 g_object_unref (info->folder);
149 g_free (info->uri);
150 g_free (info);
151 }
152
153 static void
154 free_send_info (struct _send_info *info)
155 {
156 if (info->cancellable != NULL)
157 g_object_unref (info->cancellable);
158 if (info->session != NULL)
159 g_object_unref (info->session);
160 if (info->service != NULL)
161 g_object_unref (info->service);
162 if (info->timeout_id != 0)
163 g_source_remove (info->timeout_id);
164 g_free (info->what);
165 g_free (info->send_url);
166 g_free (info);
167 }
168
169 static struct _send_data *
170 setup_send_data (EMailSession *session)
171 {
172 struct _send_data *data;
173
174 if (send_data == NULL) {
175 send_data = data = g_malloc0 (sizeof (*data));
176 data->lock = g_mutex_new ();
177 data->folders = g_hash_table_new_full (
178 g_str_hash, g_str_equal,
179 (GDestroyNotify) NULL,
180 (GDestroyNotify) free_folder_info);
181 data->inbox =
182 e_mail_session_get_local_folder (
183 session, E_MAIL_LOCAL_FOLDER_LOCAL_INBOX);
184 g_object_ref (data->inbox);
185 data->active = g_hash_table_new_full (
186 g_str_hash, g_str_equal,
187 (GDestroyNotify) g_free,
188 (GDestroyNotify) free_send_info);
189 }
190
191 return send_data;
192 }
193
194 static void
195 receive_cancel (GtkButton *button,
196 struct _send_info *info)
197 {
198 if (info->state == SEND_ACTIVE) {
199 g_cancellable_cancel (info->cancellable);
200 if (info->progress_bar != NULL)
201 gtk_progress_bar_set_text (
202 GTK_PROGRESS_BAR (info->progress_bar),
203 _("Canceling..."));
204 info->state = SEND_CANCELLED;
205 }
206 if (info->cancel_button)
207 gtk_widget_set_sensitive (info->cancel_button, FALSE);
208 }
209
210 static void
211 free_send_data (void)
212 {
213 struct _send_data *data = send_data;
214
215 g_return_if_fail (g_hash_table_size (data->active) == 0);
216
217 if (data->inbox) {
218 mail_sync_folder (data->inbox, NULL, NULL);
219 /*camel_folder_thaw (data->inbox); */
220 g_object_unref (data->inbox);
221 }
222
223 g_list_free (data->infos);
224 g_hash_table_destroy (data->active);
225 g_hash_table_destroy (data->folders);
226 g_mutex_free (data->lock);
227 g_free (data);
228 send_data = NULL;
229 }
230
231 static void
232 cancel_send_info (gpointer key,
233 struct _send_info *info,
234 gpointer data)
235 {
236 receive_cancel (GTK_BUTTON (info->cancel_button), info);
237 }
238
239 static void
240 hide_send_info (gpointer key,
241 struct _send_info *info,
242 gpointer data)
243 {
244 info->cancel_button = NULL;
245 info->progress_bar = NULL;
246
247 if (info->timeout_id != 0) {
248 g_source_remove (info->timeout_id);
249 info->timeout_id = 0;
250 }
251 }
252
253 static void
254 dialog_destroy_cb (struct _send_data *data,
255 GObject *deadbeef)
256 {
257 g_hash_table_foreach (data->active, (GHFunc) hide_send_info, NULL);
258 data->gd = NULL;
259 send_recv_dialog = NULL;
260 }
261
262 static void
263 dialog_response (GtkDialog *gd,
264 gint button,
265 struct _send_data *data)
266 {
267 switch (button) {
268 case GTK_RESPONSE_CANCEL:
269 d (printf ("cancelled whole thing\n"));
270 if (!data->cancelled) {
271 data->cancelled = TRUE;
272 g_hash_table_foreach (data->active, (GHFunc) cancel_send_info, NULL);
273 }
274 gtk_dialog_set_response_sensitive (gd, GTK_RESPONSE_CANCEL, FALSE);
275 break;
276 default:
277 d (printf ("hiding dialog\n"));
278 g_hash_table_foreach (data->active, (GHFunc) hide_send_info, NULL);
279 data->gd = NULL;
280 /*gtk_widget_destroy((GtkWidget *)gd);*/
281 break;
282 }
283 }
284
285 static GStaticMutex status_lock = G_STATIC_MUTEX_INIT;
286 static gchar *format_service_name (CamelService *service);
287
288 static gint
289 operation_status_timeout (gpointer data)
290 {
291 struct _send_info *info = data;
292
293 if (info->progress_bar) {
294 GtkProgressBar *progress_bar;
295
296 g_static_mutex_lock (&status_lock);
297
298 progress_bar = GTK_PROGRESS_BAR (info->progress_bar);
299
300 gtk_progress_bar_set_fraction (progress_bar, info->pc / 100.0);
301 if (info->what != NULL)
302 gtk_progress_bar_set_text (progress_bar, info->what);
303 if (info->service != NULL && info->send_account_label) {
304 gchar *tmp = format_service_name (info->service);
305
306 gtk_label_set_markup (
307 GTK_LABEL (info->send_account_label), tmp);
308
309 g_free (tmp);
310 }
311
312 g_static_mutex_unlock (&status_lock);
313
314 return TRUE;
315 }
316
317 return FALSE;
318 }
319
320 static void
321 set_send_status (struct _send_info *info,
322 const gchar *desc,
323 gint pc)
324 {
325 g_static_mutex_lock (&status_lock);
326
327 g_free (info->what);
328 info->what = g_strdup (desc);
329 info->pc = pc;
330
331 g_static_mutex_unlock (&status_lock);
332 }
333
334 static void
335 set_transport_service (struct _send_info *info,
336 const gchar *transport_uid)
337 {
338 CamelService *service;
339
340 g_static_mutex_lock (&status_lock);
341
342 service = camel_session_ref_service (info->session, transport_uid);
343
344 if (CAMEL_IS_TRANSPORT (service)) {
345 if (info->service != NULL)
346 g_object_unref (info->service);
347 info->service = g_object_ref (service);
348 }
349
350 if (service != NULL)
351 g_object_unref (service);
352
353 g_static_mutex_unlock (&status_lock);
354 }
355
356 /* for camel operation status */
357 static void
358 operation_status (CamelOperation *op,
359 const gchar *what,
360 gint pc,
361 struct _send_info *info)
362 {
363 set_send_status (info, what, pc);
364 }
365
366 static gchar *
367 format_service_name (CamelService *service)
368 {
369 CamelProvider *provider;
370 CamelSettings *settings;
371 gchar *service_name = NULL;
372 const gchar *display_name;
373 gchar *pretty_url = NULL;
374 gchar *host = NULL;
375 gchar *path = NULL;
376 gchar *user = NULL;
377 gchar *cp;
378 gboolean have_host = FALSE;
379 gboolean have_path = FALSE;
380 gboolean have_user = FALSE;
381
382 provider = camel_service_get_provider (service);
383 display_name = camel_service_get_display_name (service);
384
385 settings = camel_service_ref_settings (service);
386
387 if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
388 host = camel_network_settings_dup_host (
389 CAMEL_NETWORK_SETTINGS (settings));
390 have_host = (host != NULL) && (*host != '\0');
391
392 user = camel_network_settings_dup_user (
393 CAMEL_NETWORK_SETTINGS (settings));
394 have_user = (user != NULL) && (*user != '\0');
395 }
396
397 if (CAMEL_IS_LOCAL_SETTINGS (settings)) {
398 path = camel_local_settings_dup_path (
399 CAMEL_LOCAL_SETTINGS (settings));
400 have_path = (path != NULL) && (*path != '\0');
401 }
402
403 g_object_unref (settings);
404
405 /* Shorten user names with '@', since multiple '@' in a
406 * 'user@host' label look weird. This is just supposed
407 * to be a hint anyway so it doesn't matter if it's not
408 * strictly correct. */
409 if (have_user && (cp = strchr (user, '@')) != NULL)
410 *cp = '\0';
411
412 g_return_val_if_fail (provider != NULL, NULL);
413
414 /* This should never happen, but if the service has no
415 * display name, fall back to the generic service name. */
416 if (display_name == NULL || *display_name == '\0') {
417 service_name = camel_service_get_name (service, TRUE);
418 display_name = service_name;
419 }
420
421 if (have_host && have_user) {
422 pretty_url = g_markup_printf_escaped (
423 "<b>%s</b> <small>(%s@%s)</small>",
424 display_name, user, host);
425 } else if (have_host) {
426 pretty_url = g_markup_printf_escaped (
427 "<b>%s</b> <small>(%s)</small>",
428 display_name, host);
429 } else if (have_path) {
430 pretty_url = g_markup_printf_escaped (
431 "<b>%s</b> <small>(%s)</small>",
432 display_name, path);
433 } else {
434 pretty_url = g_markup_printf_escaped (
435 "<b>%s</b>", display_name);
436 }
437
438 g_free (service_name);
439 g_free (host);
440 g_free (path);
441 g_free (user);
442
443 return pretty_url;
444 }
445
446 static send_info_t
447 get_receive_type (CamelService *service)
448 {
449 CamelURL *url;
450 CamelProvider *provider;
451 const gchar *uid;
452 gboolean is_local_delivery;
453
454 /* Disregard CamelNullStores. */
455 if (CAMEL_IS_NULL_STORE (service))
456 return SEND_INVALID;
457
458 url = camel_service_new_camel_url (service);
459 is_local_delivery = em_utils_is_local_delivery_mbox_file (url);
460 camel_url_free (url);
461
462 /* mbox pointing to a file is a 'Local delivery'
463 * source which requires special processing. */
464 if (is_local_delivery)
465 return SEND_RECEIVE;
466
467 provider = camel_service_get_provider (service);
468
469 if (provider == NULL)
470 return SEND_INVALID;
471
472 /* skip some well-known services */
473 uid = camel_service_get_uid (service);
474 if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0)
475 return SEND_INVALID;
476 if (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0)
477 return SEND_INVALID;
478
479 if (provider->object_types[CAMEL_PROVIDER_STORE]) {
480 if (provider->flags & CAMEL_PROVIDER_IS_STORAGE)
481 return SEND_UPDATE;
482 else
483 return SEND_RECEIVE;
484 }
485
486 if (provider->object_types[CAMEL_PROVIDER_TRANSPORT])
487 return SEND_SEND;
488
489 return SEND_INVALID;
490 }
491
492 static gboolean
493 get_keep_on_server (CamelService *service)
494 {
495 GObjectClass *class;
496 CamelSettings *settings;
497 gboolean keep_on_server = FALSE;
498
499 settings = camel_service_ref_settings (service);
500 class = G_OBJECT_GET_CLASS (settings);
501
502 /* XXX This is a POP3-specific setting. */
503 if (g_object_class_find_property (class, "keep-on-server") != NULL)
504 g_object_get (
505 settings, "keep-on-server",
506 &keep_on_server, NULL);
507
508 g_object_unref (settings);
509
510 return keep_on_server;
511 }
512
513 static struct _send_data *
514 build_dialog (GtkWindow *parent,
515 EMailSession *session,
516 CamelFolder *outbox,
517 CamelService *transport,
518 gboolean allow_send)
519 {
520 GtkDialog *gd;
521 GtkWidget *wgrid;
522 GtkGrid *grid;
523 gint row;
524 GList *list = NULL;
525 struct _send_data *data;
526 GtkWidget *container;
527 GtkWidget *send_icon;
528 GtkWidget *recv_icon;
529 GtkWidget *scrolled_window;
530 GtkWidget *label;
531 GtkWidget *progress_bar;
532 GtkWidget *cancel_button;
533 EMailAccountStore *account_store;
534 struct _send_info *info;
535 gchar *pretty_url;
536 EMEventTargetSendReceive *target;
537 GQueue queue = G_QUEUE_INIT;
538
539 account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session));
540
541 send_recv_dialog = gtk_dialog_new ();
542
543 gd = GTK_DIALOG (send_recv_dialog);
544 gtk_window_set_modal (GTK_WINDOW (send_recv_dialog), FALSE);
545 gtk_window_set_icon_name (GTK_WINDOW (gd), "mail-send-receive");
546 gtk_window_set_default_size (GTK_WINDOW (gd), 600, 200);
547 gtk_window_set_title (GTK_WINDOW (gd), _("Send & Receive Mail"));
548 gtk_window_set_transient_for (GTK_WINDOW (gd), parent);
549
550 e_restore_window (
551 GTK_WINDOW (gd),
552 "/org/gnome/evolution/mail/send-recv-window/",
553 E_RESTORE_WINDOW_SIZE);
554
555 gtk_widget_ensure_style ((GtkWidget *) gd);
556
557 container = gtk_dialog_get_action_area (gd);
558 gtk_container_set_border_width (GTK_CONTAINER (container), 6);
559
560 container = gtk_dialog_get_content_area (gd);
561 gtk_container_set_border_width (GTK_CONTAINER (container), 0);
562
563 cancel_button = gtk_button_new_with_mnemonic (_("Cancel _All"));
564 gtk_button_set_image (
565 GTK_BUTTON (cancel_button),
566 gtk_image_new_from_stock (
567 GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON));
568 gtk_widget_show (cancel_button);
569 gtk_dialog_add_action_widget (gd, cancel_button, GTK_RESPONSE_CANCEL);
570
571 wgrid = gtk_grid_new ();
572 grid = GTK_GRID (wgrid);
573 gtk_container_set_border_width (GTK_CONTAINER (grid), 6);
574 gtk_grid_set_column_spacing (grid, 6);
575
576 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
577 gtk_container_set_border_width (
578 GTK_CONTAINER (scrolled_window), 6);
579 gtk_scrolled_window_set_policy (
580 GTK_SCROLLED_WINDOW (scrolled_window),
581 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
582 gtk_widget_set_size_request (scrolled_window, 50, 50);
583
584 container = gtk_dialog_get_content_area (gd);
585 gtk_scrolled_window_add_with_viewport (
586 GTK_SCROLLED_WINDOW (scrolled_window), wgrid);
587 gtk_box_pack_start (
588 GTK_BOX (container), scrolled_window, TRUE, TRUE, 0);
589 gtk_widget_show (scrolled_window);
590
591 /* must bet setup after send_recv_dialog as it may re-trigger send-recv button */
592 data = setup_send_data (session);
593
594 row = 0;
595 e_mail_account_store_queue_enabled_services (account_store, &queue);
596 while (!g_queue_is_empty (&queue)) {
597 CamelService *service;
598 const gchar *uid;
599
600 service = g_queue_pop_head (&queue);
601 uid = camel_service_get_uid (service);
602
603 /* see if we have an outstanding download active */
604 info = g_hash_table_lookup (data->active, uid);
605 if (info == NULL) {
606 send_info_t type = SEND_INVALID;
607
608 type = get_receive_type (service);
609
610 if (type == SEND_INVALID || type == SEND_SEND)
611 continue;
612
613 info = g_malloc0 (sizeof (*info));
614 info->type = type;
615 info->session = g_object_ref (session);
616 info->service = g_object_ref (service);
617 info->keep_on_server = get_keep_on_server (service);
618 info->cancellable = camel_operation_new ();
619 info->state = allow_send ? SEND_ACTIVE : SEND_COMPLETE;
620 info->timeout_id = g_timeout_add (
621 STATUS_TIMEOUT, operation_status_timeout, info);
622
623 g_signal_connect (
624 info->cancellable, "status",
625 G_CALLBACK (operation_status), info);
626
627 g_hash_table_insert (
628 data->active, g_strdup (uid), info);
629 list = g_list_prepend (list, info);
630
631 } else if (info->progress_bar != NULL) {
632 /* incase we get the same source pop up again */
633 continue;
634
635 } else if (info->timeout_id == 0)
636 info->timeout_id = g_timeout_add (
637 STATUS_TIMEOUT, operation_status_timeout, info);
638
639 recv_icon = gtk_image_new_from_icon_name (
640 "mail-inbox", SEND_RECV_ICON_SIZE);
641 gtk_widget_set_valign (recv_icon, GTK_ALIGN_START);
642
643 pretty_url = format_service_name (service);
644 label = gtk_label_new (NULL);
645 gtk_label_set_ellipsize (
646 GTK_LABEL (label), PANGO_ELLIPSIZE_END);
647 gtk_label_set_markup (GTK_LABEL (label), pretty_url);
648 g_free (pretty_url);
649
650 progress_bar = gtk_progress_bar_new ();
651 gtk_progress_bar_set_show_text (
652 GTK_PROGRESS_BAR (progress_bar), TRUE);
653 gtk_progress_bar_set_text (
654 GTK_PROGRESS_BAR (progress_bar),
655 (info->type == SEND_UPDATE) ?
656 _("Updating...") : _("Waiting..."));
657 gtk_widget_set_margin_bottom (progress_bar, 12);
658
659 cancel_button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
660 gtk_widget_set_valign (cancel_button, GTK_ALIGN_END);
661 gtk_widget_set_margin_bottom (cancel_button, 12);
662
663 /* g_object_set(data->label, "bold", TRUE, NULL); */
664 gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
665
666 gtk_widget_set_hexpand (label, TRUE);
667 gtk_widget_set_halign (label, GTK_ALIGN_FILL);
668
669 gtk_grid_attach (grid, recv_icon, 0, row, 1, 2);
670 gtk_grid_attach (grid, label, 1, row, 1, 1);
671 gtk_grid_attach (grid, progress_bar, 1, row + 1, 1, 1);
672 gtk_grid_attach (grid, cancel_button, 2, row, 1, 2);
673
674 info->progress_bar = progress_bar;
675 info->cancel_button = cancel_button;
676 info->data = data;
677
678 g_signal_connect (
679 cancel_button, "clicked",
680 G_CALLBACK (receive_cancel), info);
681
682 row = row + 2;
683 }
684
685 /* we also need gd during emition to be able to catch Cancel All */
686 data->gd = gd;
687 target = em_event_target_new_send_receive (
688 em_event_peek (), wgrid, data, row, EM_EVENT_SEND_RECEIVE);
689 e_event_emit (
690 (EEvent *) em_event_peek (), "mail.sendreceive",
691 (EEventTarget *) target);
692
693 /* Skip displaying the SMTP row if we've got no outbox,
694 * outgoing account or unsent mails. */
695 if (allow_send && outbox && CAMEL_IS_TRANSPORT (transport)
696 && (camel_folder_get_message_count (outbox) -
697 camel_folder_get_deleted_message_count (outbox)) != 0) {
698
699 info = g_hash_table_lookup (data->active, SEND_URI_KEY);
700 if (info == NULL) {
701 info = g_malloc0 (sizeof (*info));
702 info->type = SEND_SEND;
703 info->session = g_object_ref (session);
704 info->service = g_object_ref (transport);
705 info->keep_on_server = FALSE;
706 info->cancellable = camel_operation_new ();
707 info->state = SEND_ACTIVE;
708 info->timeout_id = g_timeout_add (
709 STATUS_TIMEOUT, operation_status_timeout, info);
710
711 g_signal_connect (
712 info->cancellable, "status",
713 G_CALLBACK (operation_status), info);
714
715 g_hash_table_insert (
716 data->active, g_strdup (SEND_URI_KEY), info);
717 list = g_list_prepend (list, info);
718 } else if (info->timeout_id == 0)
719 info->timeout_id = g_timeout_add (
720 STATUS_TIMEOUT, operation_status_timeout, info);
721
722 send_icon = gtk_image_new_from_icon_name (
723 "mail-outbox", SEND_RECV_ICON_SIZE);
724 gtk_widget_set_valign (send_icon, GTK_ALIGN_START);
725
726 pretty_url = format_service_name (transport);
727 label = gtk_label_new (NULL);
728 gtk_label_set_ellipsize (
729 GTK_LABEL (label), PANGO_ELLIPSIZE_END);
730 gtk_label_set_markup (GTK_LABEL (label), pretty_url);
731 g_free (pretty_url);
732
733 progress_bar = gtk_progress_bar_new ();
734 gtk_progress_bar_set_show_text (
735 GTK_PROGRESS_BAR (progress_bar), TRUE);
736 gtk_progress_bar_set_text (
737 GTK_PROGRESS_BAR (progress_bar), _("Waiting..."));
738 gtk_widget_set_margin_bottom (progress_bar, 12);
739
740 cancel_button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
741 gtk_widget_set_valign (cancel_button, GTK_ALIGN_END);
742
743 gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
744
745 gtk_widget_set_hexpand (label, TRUE);
746 gtk_widget_set_halign (label, GTK_ALIGN_FILL);
747
748 gtk_grid_attach (grid, send_icon, 0, row, 1, 2);
749 gtk_grid_attach (grid, label, 1, row, 1, 1);
750 gtk_grid_attach (grid, progress_bar, 1, row + 1, 1, 1);
751 gtk_grid_attach (grid, cancel_button, 2, row, 1, 2);
752
753 info->progress_bar = progress_bar;
754 info->cancel_button = cancel_button;
755 info->data = data;
756 info->send_account_label = label;
757
758 g_signal_connect (
759 cancel_button, "clicked",
760 G_CALLBACK (receive_cancel), info);
761 }
762
763 gtk_widget_show_all (wgrid);
764
765 if (parent != NULL)
766 gtk_widget_show (GTK_WIDGET (gd));
767
768 g_signal_connect (
769 gd, "response",
770 G_CALLBACK (dialog_response), data);
771
772 g_object_weak_ref ((GObject *) gd, (GWeakNotify) dialog_destroy_cb, data);
773
774 data->infos = list;
775
776 return data;
777 }
778
779 static void
780 update_folders (gchar *uri,
781 struct _folder_info *info,
782 gpointer data)
783 {
784 time_t now = *((time_t *) data);
785
786 d (printf ("checking update for folder: %s\n", info->uri));
787
788 /* let it flow through to the folders every 10 seconds */
789 /* we back off slowly as we progress */
790 if (now > info->update + 10 + info->count *5) {
791 d (printf ("upating a folder: %s\n", info->uri));
792 /*camel_folder_thaw(info->folder);
793 camel_folder_freeze (info->folder);*/
794 info->update = now;
795 info->count++;
796 }
797 }
798
799 static void
800 receive_status (CamelFilterDriver *driver,
801 enum camel_filter_status_t status,
802 gint pc,
803 const gchar *desc,
804 gpointer data)
805 {
806 struct _send_info *info = data;
807 time_t now = time (NULL);
808
809 /* let it flow through to the folder, every now and then too? */
810 g_hash_table_foreach (info->data->folders, (GHFunc) update_folders, &now);
811
812 if (info->data->inbox && now > info->data->inbox_update + 20) {
813 d (printf ("updating inbox too\n"));
814 /* this doesn't seem to work right :( */
815 /*camel_folder_thaw(info->data->inbox);
816 camel_folder_freeze (info->data->inbox);*/
817 info->data->inbox_update = now;
818 }
819
820 /* we just pile them onto the port, assuming it can handle it.
821 * We could also have a receiver port and see if they've been processed
822 * yet, so if this is necessary its not too hard to add */
823 /* the mail_gui_port receiver will free everything for us */
824 switch (status) {
825 case CAMEL_FILTER_STATUS_START:
826 case CAMEL_FILTER_STATUS_END:
827 set_send_status (info, desc, pc);
828 break;
829 case CAMEL_FILTER_STATUS_ACTION:
830 set_transport_service (info, desc);
831 break;
832 default:
833 break;
834 }
835 }
836
837 /* when receive/send is complete */
838 static void
839 receive_done (gint still_more,
840 gpointer data)
841 {
842 struct _send_info *info = data;
843 const gchar *uid;
844
845 uid = camel_service_get_uid (info->service);
846 g_return_if_fail (uid != NULL);
847
848 /* if we've been called to run again - run again */
849 if (info->type == SEND_SEND && info->state == SEND_ACTIVE && info->again) {
850 CamelFolder *local_outbox;
851
852 local_outbox =
853 e_mail_session_get_local_folder (
854 E_MAIL_SESSION (info->session),
855 E_MAIL_LOCAL_FOLDER_OUTBOX);
856
857 g_return_if_fail (CAMEL_IS_TRANSPORT (info->service));
858
859 info->again = 0;
860 mail_send_queue (
861 E_MAIL_SESSION (info->session),
862 local_outbox,
863 CAMEL_TRANSPORT (info->service),
864 E_FILTER_SOURCE_OUTGOING,
865 info->cancellable,
866 receive_get_folder, info,
867 receive_status, info,
868 send_done, info);
869 return;
870 }
871
872 if (info->progress_bar) {
873 const gchar *text;
874
875 gtk_progress_bar_set_fraction (
876 GTK_PROGRESS_BAR (info->progress_bar), 1.0);
877
878 if (info->state == SEND_CANCELLED)
879 text = _("Canceled");
880 else {
881 text = _("Complete");
882 info->state = SEND_COMPLETE;
883 }
884
885 gtk_progress_bar_set_text (
886 GTK_PROGRESS_BAR (info->progress_bar), text);
887 }
888
889 if (info->cancel_button)
890 gtk_widget_set_sensitive (info->cancel_button, FALSE);
891
892 /* remove/free this active download */
893 d (printf ("%s: freeing info %p\n", G_STRFUNC, info));
894 if (info->type == SEND_SEND) {
895 gpointer key = NULL, value = NULL;
896 if (!g_hash_table_lookup_extended (info->data->active, SEND_URI_KEY, &key, &value))
897 key = NULL;
898
899 g_hash_table_steal (info->data->active, SEND_URI_KEY);
900 g_free (key);
901 } else {
902 gpointer key = NULL, value = NULL;
903 if (!g_hash_table_lookup_extended (info->data->active, uid, &key, &value))
904 key = NULL;
905
906 g_hash_table_steal (info->data->active, uid);
907 g_free (key);
908 }
909 info->data->infos = g_list_remove (info->data->infos, info);
910
911 if (g_hash_table_size (info->data->active) == 0) {
912 if (info->data->gd)
913 gtk_widget_destroy ((GtkWidget *) info->data->gd);
914 free_send_data ();
915 }
916
917 free_send_info (info);
918 }
919
920 static void
921 send_done (gpointer data)
922 {
923 receive_done (-1, data);
924 }
925 /* although we dont do anythign smart here yet, there is no need for this interface to
926 * be available to anyone else.
927 * This can also be used to hook into which folders are being updated, and occasionally
928 * let them refresh */
929 static CamelFolder *
930 receive_get_folder (CamelFilterDriver *d,
931 const gchar *uri,
932 gpointer data,
933 GError **error)
934 {
935 struct _send_info *info = data;
936 CamelFolder *folder;
937 struct _folder_info *oldinfo;
938 gpointer oldkey, oldinfoptr;
939
940 g_mutex_lock (info->data->lock);
941 oldinfo = g_hash_table_lookup (info->data->folders, uri);
942 g_mutex_unlock (info->data->lock);
943
944 if (oldinfo) {
945 g_object_ref (oldinfo->folder);
946 return oldinfo->folder;
947 }
948
949 /* FIXME Not passing a GCancellable here. */
950 folder = e_mail_session_uri_to_folder_sync (
951 E_MAIL_SESSION (info->session), uri, 0, NULL, error);
952 if (!folder)
953 return NULL;
954
955 /* we recheck that the folder hasn't snuck in while we were loading it... */
956 /* and we assume the newer one is the same, but unref the old one anyway */
957 g_mutex_lock (info->data->lock);
958
959 if (g_hash_table_lookup_extended (
960 info->data->folders, uri, &oldkey, &oldinfoptr)) {
961 oldinfo = (struct _folder_info *) oldinfoptr;
962 g_object_unref (oldinfo->folder);
963 oldinfo->folder = folder;
964 } else {
965 oldinfo = g_malloc0 (sizeof (*oldinfo));
966 oldinfo->folder = folder;
967 oldinfo->uri = g_strdup (uri);
968 g_hash_table_insert (info->data->folders, oldinfo->uri, oldinfo);
969 }
970
971 g_object_ref (folder);
972
973 g_mutex_unlock (info->data->lock);
974
975 return folder;
976 }
977
978 /* ********************************************************************** */
979
980 static void
981 get_folders (CamelStore *store,
982 GPtrArray *folders,
983 CamelFolderInfo *info)
984 {
985 while (info) {
986 if (camel_store_can_refresh_folder (store, info, NULL)) {
987 if ((info->flags & CAMEL_FOLDER_NOSELECT) == 0) {
988 gchar *folder_uri;
989
990 folder_uri = e_mail_folder_uri_build (
991 store, info->full_name);
992 g_ptr_array_add (folders, folder_uri);
993 }
994 }
995
996 get_folders (store, folders, info->child);
997 info = info->next;
998 }
999 }
1000
1001 static void
1002 main_op_cancelled_cb (GCancellable *main_op,
1003 GCancellable *refresh_op)
1004 {
1005 g_cancellable_cancel (refresh_op);
1006 }
1007
1008 struct _refresh_folders_msg {
1009 MailMsg base;
1010
1011 struct _send_info *info;
1012 GPtrArray *folders;
1013 CamelStore *store;
1014 CamelFolderInfo *finfo;
1015 };
1016
1017 static gchar *
1018 refresh_folders_desc (struct _refresh_folders_msg *m)
1019 {
1020 return g_strdup_printf (_("Checking for new mail"));
1021 }
1022
1023 static void
1024 refresh_folders_exec (struct _refresh_folders_msg *m,
1025 GCancellable *cancellable,
1026 GError **error)
1027 {
1028 CamelFolder *folder;
1029 gint i;
1030 GError *local_error = NULL;
1031 gulong handler_id = 0;
1032
1033 if (cancellable)
1034 handler_id = g_signal_connect (
1035 m->info->cancellable, "cancelled",
1036 G_CALLBACK (main_op_cancelled_cb), cancellable);
1037
1038 get_folders (m->store, m->folders, m->finfo);
1039
1040 camel_operation_push_message (m->info->cancellable, _("Updating..."));
1041
1042 for (i = 0; i < m->folders->len; i++) {
1043 folder = e_mail_session_uri_to_folder_sync (
1044 E_MAIL_SESSION (m->info->session),
1045 m->folders->pdata[i], 0,
1046 cancellable, &local_error);
1047 if (folder) {
1048 if (camel_folder_synchronize_sync (folder, FALSE, cancellable, &local_error))
1049 camel_folder_refresh_info_sync (folder, cancellable, &local_error);
1050 g_object_unref (folder);
1051 }
1052
1053 if (local_error != NULL) {
1054 if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1055 g_warning ("Failed to refresh folder: %s: %s", (const gchar *) m->folders->pdata[i], local_error->message);
1056 g_clear_error (&local_error);
1057 }
1058
1059 if (g_cancellable_is_cancelled (m->info->cancellable) ||
1060 g_cancellable_is_cancelled (cancellable))
1061 break;
1062
1063 if (m->info->state != SEND_CANCELLED)
1064 camel_operation_progress (
1065 m->info->cancellable, 100 * i / m->folders->len);
1066 }
1067
1068 camel_operation_pop_message (m->info->cancellable);
1069
1070 if (cancellable)
1071 g_signal_handler_disconnect (m->info->cancellable, handler_id);
1072 }
1073
1074 static void
1075 refresh_folders_done (struct _refresh_folders_msg *m)
1076 {
1077 receive_done (-1, m->info);
1078 }
1079
1080 static void
1081 refresh_folders_free (struct _refresh_folders_msg *m)
1082 {
1083 gint i;
1084
1085 for (i = 0; i < m->folders->len; i++)
1086 g_free (m->folders->pdata[i]);
1087 g_ptr_array_free (m->folders, TRUE);
1088
1089 camel_store_free_folder_info (m->store, m->finfo);
1090 g_object_unref (m->store);
1091 }
1092
1093 static MailMsgInfo refresh_folders_info = {
1094 sizeof (struct _refresh_folders_msg),
1095 (MailMsgDescFunc) refresh_folders_desc,
1096 (MailMsgExecFunc) refresh_folders_exec,
1097 (MailMsgDoneFunc) refresh_folders_done,
1098 (MailMsgFreeFunc) refresh_folders_free
1099 };
1100
1101 static gboolean
1102 receive_update_got_folderinfo (MailFolderCache *folder_cache,
1103 CamelStore *store,
1104 CamelFolderInfo *info,
1105 gpointer data)
1106 {
1107 if (info) {
1108 GPtrArray *folders = g_ptr_array_new ();
1109 struct _refresh_folders_msg *m;
1110 struct _send_info *sinfo = data;
1111
1112 m = mail_msg_new (&refresh_folders_info);
1113 m->store = store;
1114 g_object_ref (store);
1115 m->folders = folders;
1116 m->info = sinfo;
1117 m->finfo = info;
1118
1119 mail_msg_unordered_push (m);
1120
1121 /* do not free folder info, we will free it later */
1122 return FALSE;
1123 } else {
1124 receive_done (-1, data);
1125 }
1126
1127 return TRUE;
1128 }
1129
1130 static void
1131 receive_update_got_store (CamelStore *store,
1132 struct _send_info *info)
1133 {
1134 MailFolderCache *folder_cache;
1135
1136 folder_cache = e_mail_session_get_folder_cache (
1137 E_MAIL_SESSION (info->session));
1138
1139 if (store != NULL) {
1140 mail_folder_cache_note_store (
1141 folder_cache, store, info->cancellable,
1142 receive_update_got_folderinfo, info);
1143 } else {
1144 receive_done (-1, info);
1145 }
1146 }
1147
1148 static CamelService *
1149 ref_default_transport (EMailSession *session)
1150 {
1151 ESource *source;
1152 ESourceRegistry *registry;
1153 CamelService *service;
1154 const gchar *extension_name;
1155 const gchar *uid;
1156
1157 registry = e_mail_session_get_registry (session);
1158 source = e_source_registry_ref_default_mail_identity (registry);
1159
1160 if (source == NULL)
1161 return NULL;
1162
1163 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
1164 if (e_source_has_extension (source, extension_name)) {
1165 ESourceMailSubmission *extension;
1166 gchar *uid;
1167
1168 extension = e_source_get_extension (source, extension_name);
1169 uid = e_source_mail_submission_dup_transport_uid (extension);
1170
1171 g_object_unref (source);
1172 source = e_source_registry_ref_source (registry, uid);
1173
1174 g_free (uid);
1175 } else {
1176 g_object_unref (source);
1177 source = NULL;
1178 }
1179
1180 if (source == NULL)
1181 return NULL;
1182
1183 uid = e_source_get_uid (source);
1184 service = camel_session_ref_service (CAMEL_SESSION (session), uid);
1185
1186 g_object_unref (source);
1187
1188 return service;
1189 }
1190
1191 static GtkWidget *
1192 send_receive (GtkWindow *parent,
1193 EMailSession *session,
1194 gboolean allow_send)
1195 {
1196 CamelFolder *local_outbox;
1197 CamelService *transport;
1198 struct _send_data *data;
1199 GList *scan;
1200
1201 if (send_recv_dialog != NULL) {
1202 if (parent != NULL && gtk_widget_get_realized (send_recv_dialog)) {
1203 gtk_window_present (GTK_WINDOW (send_recv_dialog));
1204 }
1205 return send_recv_dialog;
1206 }
1207
1208 if (!camel_session_get_online (CAMEL_SESSION (session)))
1209 return send_recv_dialog;
1210
1211 transport = ref_default_transport (session);
1212
1213 local_outbox =
1214 e_mail_session_get_local_folder (
1215 session, E_MAIL_LOCAL_FOLDER_OUTBOX);
1216
1217 data = build_dialog (
1218 parent, session, local_outbox, transport, allow_send);
1219
1220 if (transport != NULL)
1221 g_object_unref (transport);
1222
1223 for (scan = data->infos; scan != NULL; scan = scan->next) {
1224 struct _send_info *info = scan->data;
1225
1226 if (!CAMEL_IS_SERVICE (info->service))
1227 continue;
1228
1229 switch (info->type) {
1230 case SEND_RECEIVE:
1231 mail_fetch_mail (
1232 CAMEL_STORE (info->service),
1233 CAMEL_FETCH_OLD_MESSAGES, -1,
1234 E_FILTER_SOURCE_INCOMING,
1235 NULL, NULL, NULL,
1236 info->cancellable,
1237 receive_get_folder, info,
1238 receive_status, info,
1239 receive_done, info);
1240 break;
1241 case SEND_SEND:
1242 /* todo, store the folder in info? */
1243 mail_send_queue (
1244 session, local_outbox,
1245 CAMEL_TRANSPORT (info->service),
1246 E_FILTER_SOURCE_OUTGOING,
1247 info->cancellable,
1248 receive_get_folder, info,
1249 receive_status, info,
1250 send_done, info);
1251 break;
1252 case SEND_UPDATE:
1253 receive_update_got_store (
1254 CAMEL_STORE (info->service), info);
1255 break;
1256 default:
1257 break;
1258 }
1259 }
1260
1261 return send_recv_dialog;
1262 }
1263
1264 GtkWidget *
1265 mail_send_receive (GtkWindow *parent,
1266 EMailSession *session)
1267 {
1268 return send_receive (parent, session, TRUE);
1269 }
1270
1271 GtkWidget *
1272 mail_receive (GtkWindow *parent,
1273 EMailSession *session)
1274 {
1275 return send_receive (parent, session, FALSE);
1276 }
1277
1278 /* We setup the download info's in a hashtable, if we later
1279 * need to build the gui, we insert them in to add them. */
1280 void
1281 mail_receive_service (CamelService *service)
1282 {
1283 struct _send_info *info;
1284 struct _send_data *data;
1285 CamelSession *session;
1286 CamelFolder *local_outbox;
1287 const gchar *uid;
1288 send_info_t type = SEND_INVALID;
1289
1290 g_return_if_fail (CAMEL_IS_SERVICE (service));
1291
1292 uid = camel_service_get_uid (service);
1293 session = camel_service_get_session (service);
1294
1295 data = setup_send_data (E_MAIL_SESSION (session));
1296 info = g_hash_table_lookup (data->active, uid);
1297
1298 if (info != NULL)
1299 return;
1300
1301 type = get_receive_type (service);
1302
1303 if (type == SEND_INVALID || type == SEND_SEND)
1304 return;
1305
1306 info = g_malloc0 (sizeof (*info));
1307 info->type = type;
1308 info->progress_bar = NULL;
1309 info->session = g_object_ref (session);
1310 info->service = g_object_ref (service);
1311 info->keep_on_server = get_keep_on_server (service);
1312 info->cancellable = camel_operation_new ();
1313 info->cancel_button = NULL;
1314 info->data = data;
1315 info->state = SEND_ACTIVE;
1316 info->timeout_id = 0;
1317
1318 g_signal_connect (
1319 info->cancellable, "status",
1320 G_CALLBACK (operation_status), info);
1321
1322 d (printf ("Adding new info %p\n", info));
1323
1324 g_hash_table_insert (data->active, g_strdup (uid), info);
1325
1326 switch (info->type) {
1327 case SEND_RECEIVE:
1328 mail_fetch_mail (
1329 CAMEL_STORE (service),
1330 CAMEL_FETCH_OLD_MESSAGES, -1,
1331 E_FILTER_SOURCE_INCOMING,
1332 NULL, NULL, NULL,
1333 info->cancellable,
1334 receive_get_folder, info,
1335 receive_status, info,
1336 receive_done, info);
1337 break;
1338 case SEND_SEND:
1339 /* todo, store the folder in info? */
1340 local_outbox =
1341 e_mail_session_get_local_folder (
1342 E_MAIL_SESSION (session),
1343 E_MAIL_LOCAL_FOLDER_OUTBOX);
1344 mail_send_queue (
1345 E_MAIL_SESSION (session),
1346 local_outbox,
1347 CAMEL_TRANSPORT (service),
1348 E_FILTER_SOURCE_OUTGOING,
1349 info->cancellable,
1350 receive_get_folder, info,
1351 receive_status, info,
1352 send_done, info);
1353 break;
1354 case SEND_UPDATE:
1355 receive_update_got_store (CAMEL_STORE (service), info);
1356 break;
1357 default:
1358 g_return_if_reached ();
1359 }
1360 }
1361
1362 void
1363 mail_send (EMailSession *session)
1364 {
1365 CamelFolder *local_outbox;
1366 CamelService *service;
1367 struct _send_info *info;
1368 struct _send_data *data;
1369 send_info_t type = SEND_INVALID;
1370
1371 g_return_if_fail (E_IS_MAIL_SESSION (session));
1372
1373 service = ref_default_transport (session);
1374 if (service == NULL)
1375 return;
1376
1377 data = setup_send_data (session);
1378 info = g_hash_table_lookup (data->active, SEND_URI_KEY);
1379 if (info != NULL) {
1380 info->again++;
1381 d (printf ("send of %s still in progress\n", transport->url));
1382 g_object_unref (service);
1383 return;
1384 }
1385
1386 d (printf ("starting non-interactive send of '%s'\n", transport->url));
1387
1388 type = get_receive_type (service);
1389 if (type == SEND_INVALID) {
1390 g_object_unref (service);
1391 return;
1392 }
1393
1394 info = g_malloc0 (sizeof (*info));
1395 info->type = SEND_SEND;
1396 info->progress_bar = NULL;
1397 info->session = g_object_ref (session);
1398 info->service = g_object_ref (service);
1399 info->keep_on_server = FALSE;
1400 info->cancellable = NULL;
1401 info->cancel_button = NULL;
1402 info->data = data;
1403 info->state = SEND_ACTIVE;
1404 info->timeout_id = 0;
1405
1406 d (printf ("Adding new info %p\n", info));
1407
1408 g_hash_table_insert (data->active, g_strdup (SEND_URI_KEY), info);
1409
1410 /* todo, store the folder in info? */
1411 local_outbox =
1412 e_mail_session_get_local_folder (
1413 session, E_MAIL_LOCAL_FOLDER_OUTBOX);
1414
1415 mail_send_queue (
1416 session, local_outbox,
1417 CAMEL_TRANSPORT (service),
1418 E_FILTER_SOURCE_OUTGOING,
1419 info->cancellable,
1420 receive_get_folder, info,
1421 receive_status, info,
1422 send_done, info);
1423
1424 g_object_unref (service);
1425 }