[balsa] Implement automatic sending in background



commit 727722a5d54d93db8a5a74cf4f1c62eb7bc4f6c4
Author: Albrecht Dreß <albrecht dress arcor de>
Date:   Sun Jun 18 19:24:08 2017 -0400

    Implement automatic sending in background
    
        * libbalsa/libbalsa-progress.[ch]: new files, implementing the progress dialogue.
            Its update operation shall be called using idle callbacks.  For the time being,
            it is used for SMTP only, but it might also be useful for receiving messages.
        * libbalsa/Makefile.am: add the aforementioned new files
        * libbalsa/send.c: implement the core of the patch, and do some re-factoring
            to make the source better readable.  Add functions for the auto-send timer stuff.
            Remove the old send message dialogue implementation.
        * libbalsa/send.h: export functions for controlling the auto-send feature,
            and remove the old progress dialogue stuff
        * libbalsa/smtp-server.[ch]: extend the smtp server class by a lock flag,
            implemented as atomic variable
        * src/balsa-app.c: implement auto-send callback, and initialise the feature
        * src/balsa-app.h: add auto-send application variables
        * src/main-window.c: remove old progress dialogue callback
        * src/main.c, src/threads.h: remove old progress dialogue globals and initialisation
        * src/pref-manager.c: configuration of the auto-send feature
        * src/save-restore.c: load and save the configuration of the auto-send feature
    
    Signed-off-by: Peter Bloomfield <PeterBloomfield bellsouth net>

 ChangeLog                    |   22 ++
 libbalsa/Makefile.am         |    2 +
 libbalsa/libbalsa-progress.c |  265 +++++++++++++++
 libbalsa/libbalsa-progress.h |   77 +++++
 libbalsa/send.c              |  752 ++++++++++++++++++++++++------------------
 libbalsa/send.h              |   32 +--
 libbalsa/smtp-server.c       |   26 ++
 libbalsa/smtp-server.h       |    3 +
 src/balsa-app.c              |   18 +-
 src/balsa-app.h              |    4 +
 src/main-window.c            |   86 -----
 src/main.c                   |   17 -
 src/pref-manager.c           |   44 +++-
 src/save-restore.c           |   10 +
 src/threads.h                |    6 -
 15 files changed, 910 insertions(+), 454 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 65ba99c..610c3f7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2017-06-18  Albrecht Dreß
+
+       Implement automatic sending in background
+
+       * libbalsa/libbalsa-progress.[ch]: new files, implementing the progress dialogue.
+           Its update operation shall be called using idle callbacks.  For the time being,
+           it is used for SMTP only, but it might also be useful for receiving messages.
+       * libbalsa/Makefile.am: add the aforementioned new files
+       * libbalsa/send.c: implement the core of the patch, and do some re-factoring
+           to make the source better readable.  Add functions for the auto-send timer stuff.
+           Remove the old send message dialogue implementation.
+       * libbalsa/send.h: export functions for controlling the auto-send feature,
+           and remove the old progress dialogue stuff
+       * libbalsa/smtp-server.[ch]: extend the smtp server class by a lock flag,
+           implemented as atomic variable
+       * src/balsa-app.c: implement auto-send callback, and initialise the feature
+       * src/balsa-app.h: add auto-send application variables
+       * src/main-window.c: remove old progress dialogue callback
+       * src/main.c, src/threads.h: remove old progress dialogue globals and initialisation
+       * src/pref-manager.c: configuration of the auto-send feature
+       * src/save-restore.c: load and save the configuration of the auto-send feature
+
 2017-06-16  Albrecht Dreß
 
        Build with Gtk version < 3.22
diff --git a/libbalsa/Makefile.am b/libbalsa/Makefile.am
index 351a48c..f8e5780 100644
--- a/libbalsa/Makefile.am
+++ b/libbalsa/Makefile.am
@@ -102,6 +102,8 @@ libbalsa_a_SOURCES =                \
        libbalsa-conf.h         \
        libbalsa-marshal.c      \
        libbalsa-marshal.h      \
+       libbalsa-progress.c     \
+       libbalsa-progress.h     \
        macosx-helpers.c        \
        macosx-helpers.h        \
        missing.h               \
diff --git a/libbalsa/libbalsa-progress.c b/libbalsa/libbalsa-progress.c
new file mode 100644
index 0000000..d1e2cf4
--- /dev/null
+++ b/libbalsa/libbalsa-progress.c
@@ -0,0 +1,265 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/*
+ * Flexible progress dialogue for Balsa
+ * Copyright (C) 2017 Albrecht Dreß <albrecht dress arcor de>
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "libbalsa-progress.h"
+#include <math.h>
+#include <glib/gi18n.h>
+#include "libbalsa.h"
+
+
+/* note: this value is forced as minimum and maximum width of the dialogue, which may be wrong for /very/ 
high-resolution screens */
+#define PROGRESS_DIALOG_WIDTH          320
+
+
+static void progress_dialog_response_cb(GtkWidget *dialog,
+                                           gint       response);
+static void progress_dialog_destroy_cb(GtkWidget G_GNUC_UNUSED *widget,
+                                                                          gpointer                 
user_data);
+static GtkWidget *find_widget_by_name(GtkContainer *container,
+                                                                         const gchar  *id);
+static GtkWidget *create_progress_widget(const gchar *progress_id,
+                                                                            const gchar *title);
+static gboolean remove_progress_widget(gpointer user_data);
+static void send_progress_data_free(LibbalsaProgressData *progress_data);
+
+
+/* While libbalsa_progress_dialog_update() will always be called from the main context (typically from an 
idle callback),
+ * libbalsa_progress_dialog_ensure /may/ also be called from a thread, so we must ensure the integrity of 
the progress dialogue
+ * widget.  As both functions should be really fast, one big common progress dialogue mutex is sufficient. */
+static GMutex progress_mutex;
+
+
+void
+libbalsa_progress_dialog_ensure(GtkWidget   **progress_dialog,
+                                                               const gchar  *dialog_title,
+                                                               GtkWindow    *parent,
+                                                               const gchar  *progress_id)
+{
+       GtkWidget *progress_widget;
+       GtkWidget *content_box;
+
+       g_return_if_fail((progress_dialog != NULL) && (dialog_title != NULL) && (progress_id != NULL));
+
+       g_mutex_lock(&progress_mutex);
+
+    if (*progress_dialog == NULL) {
+       GdkGeometry hints;
+
+       *progress_dialog = gtk_dialog_new_with_buttons(dialog_title, parent,
+               GTK_DIALOG_DESTROY_WITH_PARENT | libbalsa_dialog_flags(), _("_Hide"), GTK_RESPONSE_CLOSE, 
NULL);
+       gtk_window_set_role(GTK_WINDOW(*progress_dialog), "progress_dialog");
+        hints.min_width = PROGRESS_DIALOG_WIDTH;
+        hints.min_height = 1;
+        hints.max_width = PROGRESS_DIALOG_WIDTH;
+        hints.max_height = -1;
+        gtk_window_set_geometry_hints(GTK_WINDOW(*progress_dialog), NULL, &hints, GDK_HINT_MIN_SIZE + 
GDK_HINT_MAX_SIZE);
+        gtk_window_set_resizable(GTK_WINDOW(*progress_dialog), FALSE);
+        g_signal_connect(G_OBJECT(*progress_dialog), "response", G_CALLBACK(progress_dialog_response_cb), 
NULL);
+        g_signal_connect(G_OBJECT(*progress_dialog), "destroy", G_CALLBACK(progress_dialog_destroy_cb), 
progress_dialog);
+
+       content_box = gtk_dialog_get_content_area(GTK_DIALOG(*progress_dialog));
+       gtk_box_set_spacing(GTK_BOX(content_box), 6);
+
+        gtk_widget_show_all(*progress_dialog);
+    } else {
+       content_box = gtk_dialog_get_content_area(GTK_DIALOG(*progress_dialog));
+    }
+
+    progress_widget = find_widget_by_name(GTK_CONTAINER(content_box), progress_id);
+    if (progress_widget != NULL) {
+       if (!gtk_revealer_get_child_revealed(GTK_REVEALER(progress_widget))) {
+               gtk_revealer_set_reveal_child(GTK_REVEALER(progress_widget), TRUE);
+       }
+    } else {
+       progress_widget = create_progress_widget(progress_id, progress_id);
+       gtk_revealer_set_reveal_child(GTK_REVEALER(progress_widget), TRUE);
+       gtk_box_pack_start(GTK_BOX(content_box), progress_widget, FALSE, FALSE, 0);
+       gtk_widget_show_all(progress_widget);
+    }
+
+       g_mutex_unlock(&progress_mutex);
+}
+
+
+gboolean
+libbalsa_progress_dialog_update(gpointer user_data)
+{
+       LibbalsaProgressData *ctrl_data = (LibbalsaProgressData *) user_data;
+
+       g_return_val_if_fail(ctrl_data != NULL, FALSE);
+
+       g_mutex_lock(&progress_mutex);
+
+       if (ctrl_data->progress_dialog == NULL) {
+               if (ctrl_data->finished) {
+                       libbalsa_information(LIBBALSA_INFORMATION_MESSAGE, "%s:\n%s", ctrl_data->progress_id, 
ctrl_data->message);
+               }
+       } else {
+               GtkWidget *progress_widget;
+               GtkWidget *content_box;
+
+               content_box = gtk_dialog_get_content_area(GTK_DIALOG(ctrl_data->progress_dialog));
+               progress_widget = find_widget_by_name(GTK_CONTAINER(content_box), ctrl_data->progress_id);
+               if (progress_widget != NULL) {
+                       if (ctrl_data->message != NULL) {
+                               GtkLabel *label;
+
+                               label = GTK_LABEL(g_object_get_data(G_OBJECT(progress_widget), "label"));
+                               gtk_label_set_text(label, ctrl_data->message);
+                       }
+                       if (isnan(ctrl_data->fraction) != 1) {
+                               GtkProgressBar *progress;
+
+                               progress = GTK_PROGRESS_BAR(g_object_get_data(G_OBJECT(progress_widget), 
"progress"));
+                               gtk_progress_bar_set_fraction(progress, ctrl_data->fraction);
+                       }
+                       if (ctrl_data->finished) {
+                               gtk_revealer_set_reveal_child(GTK_REVEALER(progress_widget), FALSE);
+                               g_timeout_add(500, remove_progress_widget, progress_widget);
+                       }
+               }
+       }
+
+       g_mutex_unlock(&progress_mutex);
+       send_progress_data_free(ctrl_data);
+
+       return FALSE;
+}
+
+
+static void
+progress_dialog_response_cb(GtkWidget *dialog,
+                            gint       response)
+{
+    if (response == GTK_RESPONSE_CLOSE) {
+        gtk_widget_destroy(dialog);
+    }
+}
+
+
+static void
+progress_dialog_destroy_cb(GtkWidget G_GNUC_UNUSED *widget,
+                                                  gpointer                 user_data)
+{
+       g_mutex_lock(&progress_mutex);
+       *((gpointer *) user_data) = NULL;
+       g_mutex_unlock(&progress_mutex);
+}
+
+
+static GtkWidget *
+find_widget_by_name(GtkContainer *container,
+                                       const gchar  *id)
+{
+       GList *children;
+       GList *this_child;
+       GtkWidget *widget;
+
+       children = gtk_container_get_children(container);
+       widget = NULL;
+       this_child = children;
+       while ((widget == NULL) && (this_child != NULL)) {
+               if (strcmp(gtk_widget_get_name(GTK_WIDGET(this_child->data)), id) == 0) {
+                       widget = GTK_WIDGET(this_child->data);
+               } else {
+                       this_child = this_child->next;
+               }
+       }
+       g_list_free(children);
+
+       return widget;
+}
+
+
+static GtkWidget *
+create_progress_widget(const gchar *progress_id,
+                                          const gchar *title)
+{
+       GtkWidget *result;
+       GtkWidget *box;
+       GtkWidget *label;
+       GtkWidget *progress;
+
+       result = gtk_revealer_new();
+       gtk_revealer_set_transition_type(GTK_REVEALER(result), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE);
+       gtk_revealer_set_transition_duration(GTK_REVEALER(result), 500);
+       gtk_revealer_set_reveal_child(GTK_REVEALER(result), FALSE);
+       gtk_widget_set_name(result, progress_id);
+
+       box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
+       gtk_container_add(GTK_CONTAINER(result), box);
+
+       label = gtk_label_new(title);
+       gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
+
+       label = gtk_label_new(" ");
+       gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+       g_object_set_data(G_OBJECT(result), "label", label);
+       gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
+
+       progress = gtk_progress_bar_new();
+       g_object_set_data(G_OBJECT(result), "progress", progress);
+       gtk_box_pack_start(GTK_BOX(box), progress, FALSE, FALSE, 0);
+
+       return result;
+}
+
+
+static void
+count_revealers(GtkWidget *widget,
+                gpointer   data)
+{
+       guint *count = (guint *) data;
+
+       if (GTK_IS_REVEALER(widget)) {
+               *count += 1U;
+       }
+}
+
+
+static gboolean
+remove_progress_widget(gpointer user_data)
+{
+       GtkWidget *progress = GTK_WIDGET(user_data);
+       GtkWidget *parent_dialog;
+       GtkWidget *content_box;
+       guint rev_children = 0U;
+
+       parent_dialog = gtk_widget_get_toplevel(progress);
+       gtk_widget_destroy(progress);
+
+       /* count the GtkRevealer children left, so we can just destroy the dialogue if there is none */
+       content_box = gtk_dialog_get_content_area(GTK_DIALOG(parent_dialog));
+       gtk_container_foreach(GTK_CONTAINER(content_box), count_revealers, &rev_children);
+       if (rev_children == 0U) {
+               gtk_widget_destroy(parent_dialog);
+       } else {
+               gtk_window_resize(GTK_WINDOW(parent_dialog), PROGRESS_DIALOG_WIDTH, 1);
+       }
+       return FALSE;
+}
+
+
+static void
+send_progress_data_free(LibbalsaProgressData *progress_data)
+{
+       g_free(progress_data->progress_id);
+       g_free(progress_data->message);
+       g_free(progress_data);
+}
diff --git a/libbalsa/libbalsa-progress.h b/libbalsa/libbalsa-progress.h
new file mode 100644
index 0000000..5790512
--- /dev/null
+++ b/libbalsa/libbalsa-progress.h
@@ -0,0 +1,77 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/*
+ * Flexible progress dialogue for Balsa
+ * Copyright (C) 2017 Albrecht Dreß <albrecht dress arcor de>
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBBALSA_PROGRESS_H_
+#define LIBBALSA_PROGRESS_H_
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+
+typedef struct _LibbalsaProgressData LibbalsaProgressData;
+
+/** \brief Progress update data
+ *
+ * The data which shall be passed to libbalsa_progress_dialog_update() in order to update a progress 
dialogue.
+ */
+struct _LibbalsaProgressData {
+       GtkWidget *progress_dialog;             /**< Progress dialogue as created by 
libbalsa_progress_dialog_ensure(). */
+       gchar     *progress_id;                 /**< Progress identifier. */
+       gboolean   finished;                    /**< Indicates whether the progress element shall be removed 
from the dialogue.  When no
+                                                                        * progress elements are left, the 
dialogue is destroyed. */
+       gchar     *message;                             /**< Message which shall be printed above the 
progress bar, or NULL to keep the current
+                                                                        * message. */
+       gdouble    fraction;                    /**< Progress bar value, between 0.0 and 1.0, or NAN to keep 
the current value. */
+};
+
+
+/** \brief Ensure that a progress dialogue and progress section exists
+ *
+ * \param progress_dialog address of an existing progress dialogue, shall be filled with NULL to create a 
new one
+ * \param dialog_title dialogue title, used only if a new dialogue is created
+ * \param parent parent window
+ * \param progress_id progress identifier, also used as section title, \em must be unique
+ *
+ * If the passed progress dialogue address is NULL, a new dialogue is created.
+ *
+ * If no progress section with the passed id exists, it is appended.  An already existing section is 
revealed if necessary.
+ */
+void libbalsa_progress_dialog_ensure(GtkWidget   **progress_dialog,
+                                                                        const gchar  *dialog_title,
+                                                                        GtkWindow    *parent,
+                                                                        const gchar  *progress_id);
+
+
+/** \brief Progress dialogue update callback
+ *
+ * \param user_data progress update information, cast'ed to LibbalsaProgressData *
+ * \return always FALSE
+ *
+ * This function shall be called with a pointer to an update information structure, typically from an idle 
callback.
+ *
+ * \note The function will free the passed data.
+ */
+gboolean libbalsa_progress_dialog_update(gpointer user_data);
+
+
+#endif /* LIBBALSA_PROGRESS_H_ */
diff --git a/libbalsa/send.c b/libbalsa/send.c
index 79fb77d..fc447d3 100644
--- a/libbalsa/send.c
+++ b/libbalsa/send.c
@@ -26,10 +26,7 @@
 #define _POSIX_C_SOURCE 199309L
 #include "send.h"
 
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <time.h>
+#include <math.h>
 
 #include <string.h>
 
@@ -45,29 +42,33 @@
 #include "gmime-filter-header.h"
 #include "smtp-server.h"
 
+#include "libbalsa-progress.h"
+
 #include <glib/gi18n.h>
 
 typedef struct _MessageQueueItem MessageQueueItem;
+typedef struct _SendMessageInfo SendMessageInfo;
 
 struct _MessageQueueItem {
+       SendMessageInfo *smsg_info;
     LibBalsaMessage *orig;
     GMimeStream *stream;
-    LibBalsaFccboxFinder finder;
     NetClientSmtpMessage *smtp_msg;
-    gint64 message_size;
-    gint64 sent;
-    gint64 acc;
-    gint64 update;
 };
 
-typedef struct _SendMessageInfo SendMessageInfo;
-
 struct _SendMessageInfo {
        LibBalsaSmtpServer *smtp_server;
     LibBalsaMailbox *outbox;
     NetClientSmtp *session;
+    LibBalsaFccboxFinder finder;
     GList *items;               /* of MessageQueueItem */
-    gboolean debug;
+    gboolean no_dialog;
+    gchar *progress_id;
+    gint64 total_size;
+    gint64 total_sent;
+    gint last_report;
+    guint msg_count;
+    guint curr_msg;
 };
 
 
@@ -80,9 +81,53 @@ struct _SendQueueInfo {
 };
 
 
+static GMutex send_messages_lock;
 static gint sending_threads = 0; /* how many sending threads are active, access via g_atomic_* */
+static GSourceFunc auto_send_cb = NULL;
+static gboolean send_mail_auto = FALSE;
+static guint send_mail_time = 0U;
+static guint send_mail_timer_id = 0U;
+static gint retrigger_send = 0;                /* # of messages added to outbox while the smtp server was 
locked, access via g_atomic_* */
+
+static GtkWidget *send_progress_dialog;
+
+
 /* end of state variables section */
 
+/* Stop the the auto-send timer, and start it if start and send_mail_auto are TRUE, and a callback is 
defined.  In order to catch
+ * weird cases (e.g. user unflags or undeletes a message in the outbox) we ignore if the outbox is actually 
empty or does not
+ * contain any ready-to-send messages, and start the timer anyway.  The overhead of the callback just doing 
nothing should be
+ * insignificant. */
+static void
+update_send_timer(gboolean start)
+{
+    if (send_mail_timer_id != 0U) {
+        g_source_remove(send_mail_timer_id);
+        send_mail_timer_id = 0U;
+    }
+
+    if (start && send_mail_auto && (auto_send_cb != NULL)) {
+       send_mail_timer_id = g_timeout_add_seconds(send_mail_time, auto_send_cb, NULL);
+    }
+}
+
+void
+libbalsa_auto_send_init(GSourceFunc auto_send_handler)
+{
+       auto_send_cb = auto_send_handler;
+       update_send_timer(FALSE);
+}
+
+void
+libbalsa_auto_send_config(gboolean enable,
+                                                 guint    timeout_minutes)
+{
+       send_mail_auto = enable;
+       send_mail_time = timeout_minutes * 60U;
+       update_send_timer(enable);
+}
+
+
 gboolean
 libbalsa_is_sending_mail(void)
 {
@@ -119,12 +164,12 @@ libbalsa_wait_for_sending_thread(gint max_time)
 
 
 static MessageQueueItem *
-msg_queue_item_new(LibBalsaFccboxFinder finder)
+msg_queue_item_new(SendMessageInfo *smi)
 {
     MessageQueueItem *mqi;
 
     mqi = g_new0(MessageQueueItem, 1U);
-    mqi->finder = finder;
+    mqi->smsg_info = smi;
     return mqi;
 }
 
@@ -146,16 +191,19 @@ msg_queue_item_destroy(MessageQueueItem *mqi)
 
 
 static SendMessageInfo *
-send_message_info_new(LibBalsaSmtpServer *smtp_server,
-                                         LibBalsaMailbox    *outbox,
-                      NetClientSmtp      *session)
+send_message_info_new(LibBalsaSmtpServer   *smtp_server,
+                                         LibBalsaMailbox      *outbox,
+                                         LibBalsaFccboxFinder  finder,
+                      NetClientSmtp        *session)
 {
     SendMessageInfo *smi;
 
     smi = g_new0(SendMessageInfo, 1);
     smi->session = session;
-    smi->outbox = outbox;
-    smi->smtp_server = g_object_ref(smtp_server);
+    smi->outbox = g_object_ref(G_OBJECT(outbox));
+    smi->finder = finder;
+    smi->smtp_server = g_object_ref(G_OBJECT(smtp_server));
+    smi->progress_id = g_strdup_printf(_("SMTP server %s"), libbalsa_smtp_server_get_name(smtp_server));
     return smi;
 }
 
@@ -163,11 +211,17 @@ send_message_info_new(LibBalsaSmtpServer *smtp_server,
 static void
 send_message_info_destroy(SendMessageInfo *smi)
 {
+       if (smi->outbox != NULL) {
+               g_object_unref(G_OBJECT(smi->outbox));
+       }
     if (smi->session != NULL) {
         g_object_unref(G_OBJECT(smi->session));
     }
     if (smi->items != NULL) {
-        g_list_free(smi->items);
+        g_list_free_full(smi->items, (GDestroyNotify) msg_queue_item_destroy);
+    }
+    if (smi->progress_id != NULL) {
+       g_free(smi->progress_id);
     }
     g_object_unref(smi->smtp_server);
     g_free(smi);
@@ -186,7 +240,7 @@ static LibBalsaMsgCreateResult do_multipart_crypto(LibBalsaMessage *message,
 
 #endif
 
-static gboolean balsa_send_message_real(SendMessageInfo *info);
+static gpointer balsa_send_message_real(SendMessageInfo *info);
 static LibBalsaMsgCreateResult libbalsa_message_create_mime_message(LibBalsaMessage *message,
                                                                     gboolean         flow,
                                                                     gboolean         postponing,
@@ -197,69 +251,6 @@ static LibBalsaMsgCreateResult libbalsa_create_msg(LibBalsaMessage *message,
 static LibBalsaMsgCreateResult libbalsa_fill_msg_queue_item_from_queu(LibBalsaMessage  *message,
                                                                       MessageQueueItem *mqi);
 
-GtkWidget *send_progress_message = NULL;
-GtkWidget *send_dialog = NULL;
-GtkWidget *send_dialog_bar = NULL;
-
-static void
-send_dialog_response_cb(GtkWidget *w,
-                        gint       response)
-{
-    if (response == GTK_RESPONSE_CLOSE) {
-        gtk_widget_destroy(w);
-    }
-}
-
-
-static void
-send_dialog_destroy_cb(GtkWidget *w)
-{
-    send_dialog = NULL;
-    send_progress_message = NULL;
-    send_dialog_bar = NULL;
-}
-
-
-/* ensure_send_progress_dialog:
-   ensures that there is send_dialog available.
- */
-static void
-ensure_send_progress_dialog(GtkWindow *parent)
-{
-    GtkWidget *label;
-    GtkBox *content_box;
-
-    if (send_dialog != NULL) {
-        return;
-    }
-
-    send_dialog = gtk_dialog_new_with_buttons(_("Sending Mail…"),
-                                              parent,
-                                              GTK_DIALOG_DESTROY_WITH_PARENT |
-                                              libbalsa_dialog_flags(),
-                                              _("_Hide"), GTK_RESPONSE_CLOSE,
-                                              NULL);
-    gtk_window_set_role(GTK_WINDOW(send_dialog), "send_dialog");
-    label = gtk_label_new(_("Sending Mail…"));
-    content_box =
-        GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(send_dialog)));
-    gtk_box_pack_start(content_box, label, FALSE, FALSE, 0);
-
-    send_progress_message = gtk_label_new("");
-    gtk_box_pack_start(content_box, send_progress_message, FALSE, FALSE, 0);
-
-    send_dialog_bar = gtk_progress_bar_new();
-    gtk_box_pack_start(content_box, send_dialog_bar, FALSE, FALSE, 0);
-    gtk_window_set_default_size(GTK_WINDOW(send_dialog), 250, 100);
-    gtk_widget_show_all(send_dialog);
-    g_signal_connect(G_OBJECT(send_dialog), "response",
-                     G_CALLBACK(send_dialog_response_cb), NULL);
-    g_signal_connect(G_OBJECT(send_dialog), "destroy",
-                     G_CALLBACK(send_dialog_destroy_cb), NULL);
-    /* Progress bar done */
-}
-
-
 static void
 lbs_set_content(GMimePart *mime_part,
                 gchar     *content)
@@ -304,7 +295,7 @@ add_mime_body_plain(LibBalsaMessageBody * body, gboolean flow, gboolean postpone
     charset = body->charset;
 
     if (body->content_type != NULL) {
-        /* Use the suplied mime type */
+        /* Use the supplied mime type */
         gchar *type, *subtype;
 
         /* FIXME: test sending with different mime types */
@@ -432,8 +423,8 @@ dump_queue(const char *msg)
  */
 static void libbalsa_set_message_id(GMimeMessage *mime_message);
 
-LibBalsaMsgCreateResult
-libbalsa_message_queue(LibBalsaMessage    *message,
+static LibBalsaMsgCreateResult
+lbs_message_queue_real(LibBalsaMessage    *message,
                        LibBalsaMailbox    *outbox,
                        LibBalsaMailbox    *fccbox,
                        LibBalsaSmtpServer *smtp_server,
@@ -499,6 +490,28 @@ libbalsa_message_queue(LibBalsaMessage    *message,
 }
 
 
+LibBalsaMsgCreateResult
+libbalsa_message_queue(LibBalsaMessage    *message,
+                       LibBalsaMailbox    *outbox,
+                       LibBalsaMailbox    *fccbox,
+                       LibBalsaSmtpServer *smtp_server,
+                       gboolean            flow,
+                       GError            **error)
+{
+       LibBalsaMsgCreateResult result;
+
+    update_send_timer(FALSE);
+
+       result = lbs_message_queue_real(message, outbox, fccbox, smtp_server, flow, error);
+
+    if (g_atomic_int_get(&sending_threads) == 0) {
+       update_send_timer(TRUE);
+    }
+
+       return result;
+}
+
+
 /* libbalsa_message_send:
    send the given messsage (if any, it can be NULL) and all the messages
    in given outbox.
@@ -507,8 +520,8 @@ static void lbs_process_queue(LibBalsaMailbox     *outbox,
                                                          LibBalsaFccboxFinder finder,
                                                          LibBalsaSmtpServer  *smtp_server,
                                                          GtkWindow           *parent);
-static void lbs_process_queue_real(LibBalsaSmtpServer *smtp_server,
-                                                                  SendQueueInfo      *send_info);
+static gboolean lbs_process_queue_real(LibBalsaSmtpServer *smtp_server,
+                                                                          SendQueueInfo      *send_info);
 
 
 LibBalsaMsgCreateResult
@@ -527,12 +540,22 @@ libbalsa_message_send(LibBalsaMessage     *message,
                          LIBBALSA_MESSAGE_SERVER_ERROR);
 
     if (message != NULL) {
-        result = libbalsa_message_queue(message, outbox, fccbox,
+        update_send_timer(FALSE);
+
+        result = lbs_message_queue_real(message, outbox, fccbox,
                                         smtp_server, flow, error);
-    }
 
-    if (result == LIBBALSA_MESSAGE_CREATE_OK) {
-       lbs_process_queue(outbox, finder, smtp_server, parent);
+        if (result == LIBBALSA_MESSAGE_CREATE_OK) {
+               if (libbalsa_smtp_server_trylock(smtp_server)) {
+                       lbs_process_queue(outbox, finder, smtp_server, parent);
+               } else {
+                       g_atomic_int_inc(&retrigger_send);
+               }
+        } else if (g_atomic_int_get(&sending_threads) == 0) {
+               update_send_timer(TRUE);
+        } else {
+               /* nothing to do */
+        }
     }
 
     return result;
@@ -581,21 +604,30 @@ send_message_data_cb(gchar   *buffer,
 {
     ssize_t read_res;
     MessageQueueItem *mqi = (MessageQueueItem *) user_data;
+       SendMessageInfo *smi = mqi->smsg_info;
 
     read_res = g_mime_stream_read(mqi->stream, buffer, count);
-    if ((mqi->message_size > 0) && (read_res > 0)) {
-        mqi->acc += read_res;
-        if (mqi->acc >= mqi->update) {
-            float percent;
-            SendThreadMessage *threadmsg;
-
-            mqi->sent += mqi->acc;
-            mqi->acc = 0;
-            percent = (float) mqi->sent / (float) mqi->message_size;
-            if (percent > 1.0F) {
-                percent = 1.0F;
-            }
-            MSGSENDTHREAD(threadmsg, MSGSENDTHREADPROGRESS, "", NULL, NULL, percent);
+    if (!smi->no_dialog && (smi->total_size > 0) && (read_res > 0)) {
+        gdouble fraction;
+        gint ipercent;
+
+       smi->total_sent += read_res;
+       fraction = (gfloat) smi->total_sent / (gfloat) smi->total_size;
+       g_debug("%s: s=%lu t=%lu %g", __func__, (unsigned long) smi->total_sent, (unsigned long) 
smi->total_size, fraction);
+       if (fraction > 1.0) {
+            fraction = 1.0;
+        }
+       ipercent = (gint) (100.0 * (fraction + 0.5));
+       if (ipercent > smi->last_report) {
+               LibbalsaProgressData *progress;
+
+               progress = g_new0(LibbalsaProgressData, 1U);
+               progress->progress_id = g_strdup(smi->progress_id);
+               progress->progress_dialog = send_progress_dialog;
+               progress->message = g_strdup_printf(_("Message %u of %u"), smi->curr_msg, smi->msg_count);
+               progress->fraction = fraction;
+               g_idle_add(libbalsa_progress_dialog_update, progress);
+               smi->last_report = ipercent;
         }
     }
     return read_res;
@@ -609,9 +641,10 @@ lbs_check_reachable_cb(GObject  *object,
 {
     LibBalsaSmtpServer *smtp_server = LIBBALSA_SMTP_SERVER(object);
        SendQueueInfo *send_info = (SendQueueInfo *) cb_data;
+       gboolean thread_started = FALSE;
 
        if (can_reach) {
-               lbs_process_queue_real(smtp_server, send_info);
+               thread_started = lbs_process_queue_real(smtp_server, send_info);
        } else {
         libbalsa_information(LIBBALSA_INFORMATION_WARNING,
                              _("Cannot reach SMTP server %s (%s), any queued message will remain in %s."),
@@ -620,12 +653,21 @@ lbs_check_reachable_cb(GObject  *object,
                                                         send_info->outbox->name);
        }
 
+       if (!thread_started) {
+               /* if the thread has been started, it will take care of unlocking the server and re-starting 
the timer */
+               libbalsa_smtp_server_unlock(smtp_server);
+               update_send_timer(TRUE);
+       }
+
        g_object_unref(send_info->outbox);
-       g_object_unref(send_info->parent);
+       if (send_info->parent != NULL) {
+               g_object_unref(send_info->parent);
+       }
        g_free(send_info);
 }
 
 
+/* note: the following function is called with the passed smtp server being locked */
 static void
 lbs_process_queue(LibBalsaMailbox      *outbox,
                          LibBalsaFccboxFinder  finder,
@@ -637,166 +679,186 @@ lbs_process_queue(LibBalsaMailbox      *outbox,
        send_info = g_new(SendQueueInfo, 1U);
        send_info->outbox = g_object_ref(outbox);
        send_info->finder = finder;
-       send_info->parent = g_object_ref(parent);
+       if (parent != NULL) {
+               send_info->parent = g_object_ref(parent);
+       } else {
+               send_info->parent = NULL;
+       }
        libbalsa_server_test_can_reach(LIBBALSA_SERVER(smtp_server), lbs_check_reachable_cb, send_info);
 }
 
 
-/* libbalsa_process_queue:
-   treats given mailbox as a set of messages to send. Loads them up and
-   launches sending thread/routine.
-   NOTE that we do not close outbox after reading. send_real/thread message
-   handler does that.
- */
 static void
-lbs_process_queue_real(LibBalsaSmtpServer *smtp_server, SendQueueInfo *send_info)
+lbs_process_queue_msg(guint               msgno,
+                                         SendMessageInfo *send_message_info)
 {
-    LibBalsaServer *server = LIBBALSA_SERVER(smtp_server);
-    SendMessageInfo *send_message_info;
-    NetClientSmtp *session;
-    guint msgno;
-
-    g_mutex_lock(&send_messages_lock);
+       MessageQueueItem* new_message;
+       LibBalsaMessage* msg;
+       const gchar* smtp_server_name;
+       LibBalsaMsgCreateResult created;
+
+       /* Skip this message if it either FLAGGED or DELETED: */
+       if (!libbalsa_mailbox_msgno_has_flags(send_message_info->outbox, msgno, 0,
+               (LIBBALSA_MESSAGE_FLAG_FLAGGED | LIBBALSA_MESSAGE_FLAG_DELETED))) {
+               return;
+       }
 
-    if (!libbalsa_mailbox_open(send_info->outbox, NULL)) {
-        g_mutex_unlock(&send_messages_lock);
-        return;
-    }
+       msg = libbalsa_mailbox_get_message(send_message_info->outbox, msgno);
+       if (!msg) {
+               /* error? */
+               return;
+       }
 
-    /* create the SMTP session */
-    if (server->security == NET_CLIENT_CRYPT_ENCRYPTED) {
-        session = net_client_smtp_new(server->host, 465U, server->security);
-    } else {
-        // FIXME - submission (587) is the standard, but most isp's use 25...
-        session = net_client_smtp_new(server->host, 587U, server->security);
-    }
+       /* check the smtp server */
+       libbalsa_message_body_ref(msg, TRUE, TRUE);
+       smtp_server_name = libbalsa_message_get_user_header(msg, "X-Balsa-SmtpServer");
+       if (!smtp_server_name) {
+               smtp_server_name = libbalsa_smtp_server_get_name(NULL);
+       }
+       if (strcmp(smtp_server_name, libbalsa_smtp_server_get_name(send_message_info->smtp_server)) != 0) {
+               libbalsa_message_body_unref(msg);
+               g_object_unref(msg);
+               return;
+       }
 
-    /* load client certificate if configured */
-    if (server->client_cert) {
-       GError *error = NULL;
+       msg->request_dsn = (atoi(libbalsa_message_get_user_header(msg, "X-Balsa-DSN")) != 0);
+       new_message = msg_queue_item_new(send_message_info);
+       created = libbalsa_fill_msg_queue_item_from_queu(msg, new_message);
+       libbalsa_message_body_unref(msg);
 
-       g_signal_connect(G_OBJECT(session), "cert-pass", G_CALLBACK(libbalsa_server_get_cert_pass), server);
-       if (!net_client_set_cert_from_file(NET_CLIENT(session), server->cert_file, &error)) {
-            libbalsa_information(LIBBALSA_INFORMATION_ERROR,
-                                 _("Cannot load certificate file %s: %s"),
-                                                                server->cert_file, error->message);
-            g_error_free(error);
-            g_mutex_unlock(&send_messages_lock);
-               return;
-       }
-    }
+       if (created != LIBBALSA_MESSAGE_CREATE_OK) {
+               msg_queue_item_destroy(new_message);
+       } else {
+               const InternetAddress* ia;
+               const gchar* mailbox;
+
+               libbalsa_message_change_flags(msg, LIBBALSA_MESSAGE_FLAG_FLAGGED, 0);
+               send_message_info->items = g_list_prepend(send_message_info->items, new_message);
+               new_message->smtp_msg = net_client_smtp_msg_new(send_message_data_cb, new_message);
+               if (msg->request_dsn) {
+                       net_client_smtp_msg_set_dsn_opts(new_message->smtp_msg, msg->message_id, FALSE);
+               }
+
+               /* Add the sender info */
+               if (msg->headers->from && (ia = internet_address_list_get_address(msg->headers->from, 0))) {
+                       while (ia != NULL && INTERNET_ADDRESS_IS_GROUP(ia)) {
+                               ia = internet_address_list_get_address(INTERNET_ADDRESS_GROUP(
+                                       ia)->members, 0);
+                       }
+                       mailbox = ia ? INTERNET_ADDRESS_MAILBOX(ia)->addr : "";
+               } else {
+                       mailbox = "";
+               }
+
+               net_client_smtp_msg_set_sender(new_message->smtp_msg, mailbox);
+
+               /* Now need to add the recipients to the message. */
+               add_recipients(new_message->smtp_msg, msg->headers->to_list, msg->request_dsn);
+               add_recipients(new_message->smtp_msg, msg->headers->cc_list, msg->request_dsn);
+               add_recipients(new_message->smtp_msg, msg->headers->bcc_list, msg->request_dsn);
+
+               /* Estimate the size of the message.  This need not be exact but it's better to err
+                * on the large side since some message headers may be altered during the transfer. */
+               send_message_info->total_size += g_mime_stream_length(new_message->stream);
+               send_message_info->msg_count++;
+       }
+       g_object_unref(msg);
+}
 
-    /* connect signals */
-    g_signal_connect(G_OBJECT(session), "cert-check", G_CALLBACK(libbalsa_server_check_cert), session);
-    g_signal_connect(G_OBJECT(session), "auth", G_CALLBACK(libbalsa_server_get_auth), smtp_server);
 
-    send_message_info =
-        send_message_info_new(smtp_server, send_info->outbox, session);
+static NetClientSmtp *
+lbs_process_queue_init_session(LibBalsaServer* server)
+{
+       NetClientSmtp* session;
 
-    for (msgno = libbalsa_mailbox_total_messages(send_info->outbox); msgno > 0U; msgno--) {
-        MessageQueueItem *new_message;
-        LibBalsaMessage *msg;
-        const gchar *smtp_server_name;
-        LibBalsaMsgCreateResult created;
+       if (server->security == NET_CLIENT_CRYPT_ENCRYPTED) {
+               session = net_client_smtp_new(server->host, 465U, server->security);
+       } else {
+               // FIXME - submission (587) is the standard, but most isp's use 25...
+               session = net_client_smtp_new(server->host, 587U, server->security);
+       }
 
-        /* Skip this message if it either FLAGGED or DELETED: */
-        if (!libbalsa_mailbox_msgno_has_flags(send_info->outbox, msgno, 0,
-                                              (LIBBALSA_MESSAGE_FLAG_FLAGGED |
-                                               LIBBALSA_MESSAGE_FLAG_DELETED))) {
-            continue;
-        }
+       /* load client certificate if configured */
+       if (server->client_cert) {
+               GError* error = NULL;
+
+               g_signal_connect(G_OBJECT(session), "cert-pass", G_CALLBACK(libbalsa_server_get_cert_pass), 
server);
+               if (!net_client_set_cert_from_file(NET_CLIENT(session), server->cert_file, &error)) {
+                       libbalsa_information(LIBBALSA_INFORMATION_ERROR, _("Cannot load certificate file %s: 
%s"), server->cert_file,
+                               error->message);
+                       g_error_free(error);
+                       g_object_unref(session);
+                       session = NULL;
+               }
+       } else {
+               /* connect signals */
+               g_signal_connect(G_OBJECT(session), "cert-check", G_CALLBACK(libbalsa_server_check_cert), 
session);
+               g_signal_connect(G_OBJECT(session), "auth", G_CALLBACK(libbalsa_server_get_auth), server);
+       }
 
-        msg = libbalsa_mailbox_get_message(send_info->outbox, msgno);
-        if (!msg) {       /* error? */
-            continue;
-        }
-        libbalsa_message_body_ref(msg, TRUE, TRUE);
-        smtp_server_name = libbalsa_message_get_user_header(msg, "X-Balsa-SmtpServer");
-        if (!smtp_server_name) {
-            smtp_server_name = libbalsa_smtp_server_get_name(NULL);
-        }
-        if (strcmp(smtp_server_name, libbalsa_smtp_server_get_name(smtp_server)) != 0) {
-            libbalsa_message_body_unref(msg);
-            g_object_unref(msg);
-            continue;
-        }
-        msg->request_dsn = (atoi(libbalsa_message_get_user_header(msg, "X-Balsa-DSN")) != 0);
+       return session;
+}
 
-        new_message = msg_queue_item_new(send_info->finder);
-        created = libbalsa_fill_msg_queue_item_from_queu(msg, new_message);
-        libbalsa_message_body_unref(msg);
 
-        if (created != LIBBALSA_MESSAGE_CREATE_OK) {
-            msg_queue_item_destroy(new_message);
-        } else {
-            const InternetAddress *ia;
-            const gchar *mailbox;
+/* libbalsa_process_queue:
+   treats given mailbox as a set of messages to send. Loads them up and
+   launches sending thread/routine.
+   NOTE that we do not close outbox after reading. send_real/thread message
+   handler does that.
+   Returns if the thread has been launched
 
-            libbalsa_message_change_flags(msg, LIBBALSA_MESSAGE_FLAG_FLAGGED, 0);
+   Note: the function is always called with the respective SMTP server being locked, so unlock if we don't 
start the sending
+         thread
+ */
+static gboolean
+lbs_process_queue_real(LibBalsaSmtpServer *smtp_server, SendQueueInfo *send_info)
+{
+       gboolean thread_started = FALSE;
 
-            send_message_info->items = g_list_prepend(send_message_info->items, new_message);
-            new_message->smtp_msg = net_client_smtp_msg_new(send_message_data_cb, new_message);
+    g_mutex_lock(&send_messages_lock);
 
-            if (msg->request_dsn) {
-                net_client_smtp_msg_set_dsn_opts(new_message->smtp_msg, msg->message_id, FALSE);
-            }
+    if (libbalsa_mailbox_open(send_info->outbox, NULL)) {
+       NetClientSmtp *session;
+
+       /* create the SMTP session */
+       session = lbs_process_queue_init_session(LIBBALSA_SERVER(smtp_server));
+       if (session != NULL) {
+               SendMessageInfo *send_message_info;
+               guint msgno;
+
+               send_message_info = send_message_info_new(smtp_server, send_info->outbox, send_info->finder, 
session);
+
+               for (msgno = libbalsa_mailbox_total_messages(send_info->outbox); msgno > 0U; msgno--) {
+                       lbs_process_queue_msg(msgno, send_message_info);
+               }
+
+               /* launch the thread for sending the messages only if we collected any */
+               if (send_message_info->items != NULL) {
+                       GThread *send_mail;
+
+                       if (send_info->parent != NULL) {
+                               libbalsa_progress_dialog_ensure(&send_progress_dialog, _("Sending Mail"), 
send_info->parent,
+                                       send_message_info->progress_id);
+                       } else {
+                               send_message_info->no_dialog = TRUE;
+                       }
+                       g_atomic_int_inc(&sending_threads);
+                       send_mail = g_thread_new("balsa_send_message_real", (GThreadFunc) 
balsa_send_message_real, send_message_info);
+                       g_thread_unref(send_mail);
+                       thread_started = TRUE;
+               } else {
+                       send_message_info_destroy(send_message_info);
+               }
+       }
 
-            /* Add the sender info */
-            if (msg->headers->from &&
-                (ia = internet_address_list_get_address(msg->headers->from, 0))) {
-                while (ia != NULL && INTERNET_ADDRESS_IS_GROUP(ia)) {
-                    ia = internet_address_list_get_address (INTERNET_ADDRESS_GROUP(
-                                                                ia)->members, 0);
-                }
-                mailbox = ia ? INTERNET_ADDRESS_MAILBOX(ia)->addr : "";
-            } else {
-                mailbox = "";
-            }
-            net_client_smtp_msg_set_sender(new_message->smtp_msg, mailbox);
-
-            /* Now need to add the recipients to the message. */
-            add_recipients(new_message->smtp_msg, msg->headers->to_list, msg->request_dsn);
-            add_recipients(new_message->smtp_msg, msg->headers->cc_list, msg->request_dsn);
-            add_recipients(new_message->smtp_msg, msg->headers->bcc_list, msg->request_dsn);
-
-            /* Estimate the size of the message.  This need not be exact but it's better to err
-               on the large side since some
-             * message headers may be altered during the transfer. */
-            new_message->message_size = g_mime_stream_length(new_message->stream);
-
-            /* Set up counters for the progress bar.  Update is the byte count when the progress
-               bar should be updated.  This is
-             * capped around 5k so that the progress bar moves about once per second on a slow
-             * line.  On small messages it is
-             * smaller to allow smooth progress of the bar. */
-            new_message->update = new_message->message_size / 20;
-            if (new_message->update < 100) {
-                new_message->update = 100;
-            } else if (new_message->update > 5 * 1024) {
-                new_message->update = 5 * 1024;
-            }
-            new_message->sent = 0;
-            new_message->acc = 0;
+        if (!thread_started) {
+                       libbalsa_mailbox_close(send_info->outbox, TRUE);
         }
-        g_object_unref(msg);
-    }
-
-    /* launch the thread for sending the messages only if we collected any */
-    if (send_message_info->items != NULL) {
-        GThread *send_mail;
-
-        ensure_send_progress_dialog(send_info->parent);
-        g_atomic_int_inc(&sending_threads);
-        send_mail = g_thread_new("balsa_send_message_real",
-                                 (GThreadFunc) balsa_send_message_real,
-                                 send_message_info);
-        g_thread_unref(send_mail);
-    } else {
-        send_message_info_destroy(send_message_info);
     }
 
     g_mutex_unlock(&send_messages_lock);
-    return;
+
+    return thread_started;
 }
 
 
@@ -806,11 +868,34 @@ libbalsa_process_queue(LibBalsaMailbox     *outbox,
                        GSList              *smtp_servers,
                        GtkWindow           *parent)
 {
-    for (; smtp_servers; smtp_servers = smtp_servers->next) {
-        LibBalsaSmtpServer *smtp_server =
-            LIBBALSA_SMTP_SERVER(smtp_servers->data);
-        lbs_process_queue(outbox, finder, smtp_server, parent);
-    }
+       if (libbalsa_mailbox_open(outbox, NULL)) {
+               guint msgno;
+               guint pending = 0U;
+
+               update_send_timer(FALSE);
+
+               for (msgno = libbalsa_mailbox_total_messages(outbox); msgno > 0U; msgno--) {
+                       if (libbalsa_mailbox_msgno_has_flags(outbox, msgno, 0,
+                               (LIBBALSA_MESSAGE_FLAG_FLAGGED | LIBBALSA_MESSAGE_FLAG_DELETED))) {
+                               pending++;
+                       }
+               }
+
+               if (pending > 0U) {
+                       for (; smtp_servers; smtp_servers = smtp_servers->next) {
+                               LibBalsaSmtpServer *smtp_server = LIBBALSA_SMTP_SERVER(smtp_servers->data);
+
+                               if (libbalsa_smtp_server_trylock(smtp_server)) {
+                                       lbs_process_queue(outbox, finder, smtp_server, parent);
+                               }
+                       }
+               } else {
+                       update_send_timer(TRUE);
+                       g_message("%s: no messages pending", __func__);
+               }
+
+               libbalsa_mailbox_close(outbox, FALSE);
+       }
 }
 
 
@@ -824,6 +909,12 @@ libbalsa_process_queue(LibBalsaMailbox     *outbox,
 static gboolean
 balsa_send_message_real_idle_cb(LibBalsaMailbox *outbox)
 {
+       if (g_atomic_int_and(&retrigger_send, 0U) != 0U) {
+               g_idle_add(auto_send_cb, GINT_TO_POINTER(1));
+       } else {
+           update_send_timer(TRUE);
+       }
+
     libbalsa_mailbox_close(outbox, TRUE);
     g_object_unref(outbox);
 
@@ -834,12 +925,72 @@ balsa_send_message_real_idle_cb(LibBalsaMailbox *outbox)
     (g_error_matches((error), NET_CLIENT_ERROR_QUARK, NET_CLIENT_ERROR_CONNECTION_LOST) || \
      g_error_matches((error), NET_CLIENT_SMTP_ERROR_QUARK, NET_CLIENT_ERROR_SMTP_TRANSIENT))
 
-static gboolean
+
+static inline void
+balsa_send_message_success(MessageQueueItem *mqi,
+                                                  SendMessageInfo  *info)
+{
+       /* sending message successful */
+       if ((mqi->orig != NULL) && (mqi->orig->mailbox != NULL)) {
+               gboolean remove = TRUE;
+               const gchar *fccurl = libbalsa_message_get_user_header(mqi->orig, "X-Balsa-Fcc");
+
+               if (fccurl != NULL) {
+                       LibBalsaMailbox *fccbox = info->finder(fccurl);
+                       GError *err = NULL;
+                       LibbalsaProgressData *progress;
+
+                       progress = g_new0(LibbalsaProgressData, 1U);
+                       progress->progress_id = g_strdup(info->progress_id);
+                       progress->progress_dialog = send_progress_dialog;
+                       progress->message = g_strdup_printf(_("Save message in %s…"), fccbox->name);
+                       progress->fraction = NAN;
+                       g_idle_add(libbalsa_progress_dialog_update, progress);
+
+                       libbalsa_message_change_flags(mqi->orig, 0, LIBBALSA_MESSAGE_FLAG_NEW | 
LIBBALSA_MESSAGE_FLAG_FLAGGED);
+                       libbalsa_mailbox_sync_storage(mqi->orig->mailbox, FALSE);
+                       remove = libbalsa_message_copy(mqi->orig, fccbox, &err);
+                       if (!remove) {
+                               libbalsa_information(LIBBALSA_INFORMATION_ERROR, _("Saving sent message to %s 
failed: %s"), fccbox->name,
+                                       err ? err->message : "?");
+                               g_clear_error(&err);
+                       }
+               }
+
+               /* If copy failed, mark the message again as flagged - otherwise it will get
+                * resent again. And again, and again... */
+               libbalsa_message_change_flags(mqi->orig, remove ? LIBBALSA_MESSAGE_FLAG_DELETED : 
LIBBALSA_MESSAGE_FLAG_FLAGGED, 0);
+       }
+}
+
+static inline void
+balsa_send_message_error(MessageQueueItem *mqi,
+                                                GError           *error)
+{
+       /* sending message failed */
+       if ((mqi->orig != NULL) && (mqi->orig->mailbox != NULL)) {
+               if (ERROR_IS_TRANSIENT(error)) {
+                       /* Mark it as:
+                        * - neither flagged nor deleted, so it can be resent later
+                        *   without changing flags. */
+                       libbalsa_message_change_flags(mqi->orig, 0, LIBBALSA_MESSAGE_FLAG_FLAGGED | 
LIBBALSA_MESSAGE_FLAG_DELETED);
+               } else {
+                       /* Mark it as:
+                        * - flagged, so it will not be sent again until the error is fixed
+                        *   and the user manually clears the flag;
+                        * - undeleted, in case it was already deleted. */
+                       libbalsa_message_change_flags(mqi->orig, LIBBALSA_MESSAGE_FLAG_FLAGGED, 
LIBBALSA_MESSAGE_FLAG_DELETED);
+               }
+       }
+       libbalsa_information(LIBBALSA_INFORMATION_ERROR, _("Sending message failed: %s\nMessage left in your 
outbox."),
+               error->message);
+}
+
+static gpointer
 balsa_send_message_real(SendMessageInfo *info)
 {
     gboolean result;
     GError *error = NULL;
-    SendThreadMessage *threadmsg;
     gchar *greeting = NULL;
 
     g_debug("%s: starting", __func__);
@@ -847,18 +998,29 @@ balsa_send_message_real(SendMessageInfo *info)
     /* connect the SMTP server */
     result = net_client_smtp_connect(info->session, &greeting, &error);
     g_debug("%s: connect = %d [%p]: '%s'", __func__, result, info->items, greeting);
+    g_free(greeting);
     if (result) {
         GList *this_msg;
-        gchar *msg;
 
-        msg = g_strdup_printf(_("Connected to SMTP server %s: %s"), 
libbalsa_smtp_server_get_name(info->smtp_server), greeting);
-        MSGSENDTHREAD(threadmsg, MSGSENDTHREADPROGRESS, msg, NULL, NULL, 0);
-        g_free(msg);
+        if (info->no_dialog) {
+               libbalsa_information(LIBBALSA_INFORMATION_MESSAGE, _("Sending queued messages to %s"),
+                       libbalsa_smtp_server_get_name(info->smtp_server));
+        } else {
+               LibbalsaProgressData *progress;
+
+               progress = g_new0(LibbalsaProgressData, 1U);
+               progress->progress_id = g_strdup(info->progress_id);
+               progress->progress_dialog = send_progress_dialog;
+               progress->message = g_strdup_printf(_("Connected to %s"), 
net_client_get_host(NET_CLIENT(info->session)));
+               g_idle_add(libbalsa_progress_dialog_update, progress);
+        }
+
         for (this_msg = info->items; this_msg != NULL; this_msg = this_msg->next) {
             MessageQueueItem *mqi = (MessageQueueItem *) this_msg->data;
             gboolean send_res;
 
-            g_debug("%s: mqi = %p", __func__, mqi);
+            info->curr_msg++;
+            g_debug("%s: %u/%u mqi = %p", __func__, info->msg_count, info->curr_msg, mqi);
             /* send the message */
             send_res = net_client_smtp_send_msg(info->session, mqi->smtp_msg, &error);
 
@@ -874,67 +1036,15 @@ balsa_send_message_real(SendMessageInfo *info)
 
             if (send_res) {
                 /* sending message successful */
-                if ((mqi->orig != NULL) && (mqi->orig->mailbox != NULL)) {
-                    gboolean remove = TRUE;
-                    const gchar *fccurl = libbalsa_message_get_user_header(mqi->orig,
-                                                                           "X-Balsa-Fcc");
-
-                    if (fccurl != NULL) {
-                        LibBalsaMailbox *fccbox = mqi->finder(fccurl);
-                        GError *err = NULL;
-
-                        libbalsa_message_change_flags(mqi->orig,
-                                                      0,
-                                                      LIBBALSA_MESSAGE_FLAG_NEW |
-                                                      LIBBALSA_MESSAGE_FLAG_FLAGGED);
-                        libbalsa_mailbox_sync_storage(mqi->orig->mailbox, FALSE);
-                        remove = libbalsa_message_copy(mqi->orig, fccbox, &err);
-                        if (!remove) {
-                            libbalsa_information(LIBBALSA_INFORMATION_ERROR,
-                                                 _("Saving sent message to %s failed: %s"),
-                                                 fccbox->url, err ? err->message : "?");
-                            g_clear_error(&err);
-                        }
-                    }
-                    /* If copy failed, mark the message again as flagged - otherwise it will get
-                       resent again. And again, and
-                     * again... */
-                    libbalsa_message_change_flags(mqi->orig,
-                                                  remove
-                                                  ? LIBBALSA_MESSAGE_FLAG_DELETED
-                                                  : LIBBALSA_MESSAGE_FLAG_FLAGGED,
-                                                  0);
-                }
+                               balsa_send_message_success(mqi, info);
             } else {
                 /* sending message failed */
-                if ((mqi->orig != NULL) && (mqi->orig->mailbox != NULL)) {
-                    if (ERROR_IS_TRANSIENT(error)) {
-                        /* Mark it as:
-                         * - neither flagged nor deleted, so it can be resent later
-                         *   without changing flags. */
-                        libbalsa_message_change_flags(mqi->orig,
-                                                      0,
-                                                      LIBBALSA_MESSAGE_FLAG_FLAGGED |
-                                                      LIBBALSA_MESSAGE_FLAG_DELETED);
-                    } else {
-                        /* Mark it as:
-                         * - flagged, so it will not be sent again until the error is fixed
-                         *   and the user manually clears the flag;
-                         * - undeleted, in case it was already deleted. */
-                        libbalsa_message_change_flags(mqi->orig,
-                                                      LIBBALSA_MESSAGE_FLAG_FLAGGED,
-                                                      LIBBALSA_MESSAGE_FLAG_DELETED);
-                    }
-                }
-                libbalsa_information(LIBBALSA_INFORMATION_ERROR,
-                                     _(
-                                         "Sending message failed: %s\nMessage left in your outbox."),
-                                     error->message);
+                               balsa_send_message_error(mqi, error);
                 g_clear_error(&error);
+                result = FALSE;
             }
 
             /* free data */
-            msg_queue_item_destroy(mqi);
             g_mutex_unlock(&send_messages_lock);
         }
     } else {
@@ -961,18 +1071,34 @@ balsa_send_message_real(SendMessageInfo *info)
                              error->message);
         g_error_free(error);
     }
-    g_free(greeting);
 
     /* close outbox in an idle callback, as it might affect the display */
     g_idle_add((GSourceFunc) balsa_send_message_real_idle_cb, g_object_ref(info->outbox));
 
     /* clean up */
+    if (!info->no_dialog) {
+       LibbalsaProgressData *progress;
+
+       progress = g_new0(LibbalsaProgressData, 1U);
+       progress->progress_id = g_strdup(info->progress_id);
+       progress->progress_dialog = send_progress_dialog;
+       progress->message = g_strdup_printf(_("Finished"));
+       progress->fraction = 1.0;
+       progress->finished = TRUE;
+       g_idle_add(libbalsa_progress_dialog_update, progress);
+    } else if (result) {
+       libbalsa_information(LIBBALSA_INFORMATION_MESSAGE,
+               ngettext("Transmitted %u message to %s", "Transmitted %u messages to %s", info->msg_count),
+                       info->msg_count, libbalsa_smtp_server_get_name(info->smtp_server));
+    } else {
+       /* no dialogue and error: information already displayed, nothing to do */
+    }
+       libbalsa_smtp_server_unlock(info->smtp_server);
     send_message_info_destroy(info);
 
-    MSGSENDTHREAD(threadmsg, MSGSENDTHREADFINISHED, "", NULL, NULL, 0);
     (void) g_atomic_int_dec_and_test(&sending_threads);
 
-    return result;
+    return NULL;
 }
 
 
diff --git a/libbalsa/send.h b/libbalsa/send.h
index 0248e29..48a38b8 100644
--- a/libbalsa/send.h
+++ b/libbalsa/send.h
@@ -71,34 +71,8 @@ void libbalsa_process_queue(LibBalsaMailbox * outbox,
                                                        GSList * smtp_servers,
                                                        GtkWindow * parent);
 
-extern GMutex send_messages_lock;
-extern int send_thread_pipes[2];
-
-typedef struct {
-    int message_type;
-    char message_string[256];
-    LibBalsaMessage *msg;
-    LibBalsaMailbox *mbox;
-    float of_total;
-} SendThreadMessage;
-
-#define  MSGSENDTHREAD(t_message, type, string, s_msg, s_mbox, messof) \
-  t_message = g_new(SendThreadMessage, 1); \
-  t_message->message_type = type; \
-  strncpy(t_message->message_string, string, sizeof(t_message->message_string)); \
-  t_message->msg = s_msg; \
-  t_message->mbox = s_mbox; \
-  t_message->of_total = messof; \
-  if (write( send_thread_pipes[1], (void *) &t_message, sizeof(void *) ) \
-      < (ssize_t) sizeof(void *)) \
-    g_warning("pipe error");
-
-enum {
-    MSGSENDTHREADERROR,
-    MSGSENDTHREADPROGRESS,
-    MSGSENDTHREADPOSTPONE,
-    MSGSENDTHREADDELETE,
-    MSGSENDTHREADFINISHED
-};
+void libbalsa_auto_send_init(GSourceFunc auto_send_cb);
+void libbalsa_auto_send_config(gboolean enable,
+                                                  guint    timeout_minutes);
 
 #endif /* __SEND_H__ */
diff --git a/libbalsa/smtp-server.c b/libbalsa/smtp-server.c
index ba5ac89..d1a24e2 100644
--- a/libbalsa/smtp-server.c
+++ b/libbalsa/smtp-server.c
@@ -46,6 +46,8 @@ struct _LibBalsaSmtpServer {
 
     gchar *name;
     guint big_message; /* size of partial messages; in kB; 0 disables splitting */
+    gint lock_state;   /* 0 means unlocked; access via atomic operations */
+    // FIXME - add an atomic flag if an operation is running on this server
 };
 
 typedef struct _LibBalsaSmtpServerClass {
@@ -212,6 +214,30 @@ libbalsa_smtp_server_add_to_list(LibBalsaSmtpServer * smtp_server,
     *server_list = g_slist_prepend(*server_list, smtp_server);
 }
 
+gboolean
+libbalsa_smtp_server_trylock(LibBalsaSmtpServer *smtp_server)
+{
+       gint prev_state;
+       gboolean result;
+
+       prev_state = g_atomic_int_add(&smtp_server->lock_state, 1);
+       if (prev_state == 0) {
+               result = TRUE;
+       } else {
+               result = FALSE;
+               (void) g_atomic_int_dec_and_test(&smtp_server->lock_state);
+       }
+       g_debug("%s: lock %s: %d", __func__, libbalsa_smtp_server_get_name(smtp_server), result);
+       return result;
+}
+
+void
+libbalsa_smtp_server_unlock(LibBalsaSmtpServer *smtp_server)
+{
+       (void) g_atomic_int_dec_and_test(&smtp_server->lock_state);
+       g_debug("%s: unlock %s", __func__, libbalsa_smtp_server_get_name(smtp_server));
+}
+
 /* SMTP server dialog */
 
 #define LIBBALSA_SMTP_SERVER_DIALOG_KEY "libbalsa-smtp-server-dialog"
diff --git a/libbalsa/smtp-server.h b/libbalsa/smtp-server.h
index 59157f9..f037d11 100644
--- a/libbalsa/smtp-server.h
+++ b/libbalsa/smtp-server.h
@@ -58,4 +58,7 @@ void libbalsa_smtp_server_dialog(LibBalsaSmtpServer * smtp_server,
                                  GtkWindow * parent,
                                  LibBalsaSmtpServerUpdate update);
 
+gboolean libbalsa_smtp_server_trylock(LibBalsaSmtpServer *smtp_server);
+void libbalsa_smtp_server_unlock(LibBalsaSmtpServer *smtp_server);
+
 #endif                          /* __SMTP_SERVER_H__ */
diff --git a/src/balsa-app.c b/src/balsa-app.c
index 2c9f3ea..fb7e23f 100644
--- a/src/balsa-app.c
+++ b/src/balsa-app.c
@@ -34,6 +34,7 @@
 #include "filter-funcs.h"
 #include "libbalsa-conf.h"
 #include "misc.h"
+#include "send.h"
 #include "server.h"
 #include "smtp-server.h"
 #include "save-restore.h"
@@ -251,6 +252,17 @@ ask_password(LibBalsaServer *server, LibBalsaMailbox *mbox)
        return password;
 }
 
+
+/* Note: data indicates if the function shall be re-scheduled (NULL) or not (!= NULL) */
+static gboolean
+send_queued_messages_auto_cb(gpointer data)
+{
+       g_debug("%s: %p", __func__, data);
+       libbalsa_process_queue(balsa_app.outbox, balsa_find_sentbox_by_url, balsa_app.smtp_servers, NULL);
+    return (data == NULL);
+}
+
+
 void
 balsa_app_init(void)
 {
@@ -395,6 +407,8 @@ balsa_app_init(void)
     /* Message filing */
     balsa_app.folder_mru=NULL;
     balsa_app.fcc_mru=NULL;
+
+    libbalsa_auto_send_init(send_queued_messages_auto_cb);
 }
 
 void
@@ -429,7 +443,7 @@ balsa_app_destroy(void)
     if(balsa_app.debug) g_print("balsa_app: Finished cleaning up.\n");
 }
 
-static gint
+static gboolean
 check_new_messages_auto_cb(gpointer data)
 {
     check_new_messages_real(balsa_app.main_window, TYPE_BACKGROUND);
@@ -450,7 +464,7 @@ update_timer(gboolean update, guint minutes)
 
     balsa_app.check_mail_timer_id = update ?
         g_timeout_add_seconds(minutes * 60,
-                              (GSourceFunc) check_new_messages_auto_cb,
+                              check_new_messages_auto_cb,
                               NULL) : 0;
 }
 
diff --git a/src/balsa-app.h b/src/balsa-app.h
index 6afe16a..d1b09a2 100644
--- a/src/balsa-app.h
+++ b/src/balsa-app.h
@@ -328,6 +328,10 @@ extern struct BalsaApplication {
     gboolean always_queue_sent_mail;
     gboolean copy_to_sentbox;
 
+    /* timer for sending mail every xx minutes */
+    gboolean send_mail_auto;
+    guint send_mail_timer;
+
     /* mailbox indices */
     GtkWidget *notebook;
 
diff --git a/src/main-window.c b/src/main-window.c
index 5460537..b31c68a 100644
--- a/src/main-window.c
+++ b/src/main-window.c
@@ -3622,92 +3622,6 @@ mail_progress_notify_cb(GIOChannel * source, GIOCondition condition,
     return TRUE;
 }
 
-gboolean
-send_progress_notify_cb(GIOChannel * source, GIOCondition condition,
-                        BalsaWindow ** window)
-{
-    SendThreadMessage *threadmessage;
-    SendThreadMessage **currentpos;
-    void *msgbuffer;
-    ssize_t count;
-    float fraction;
-
-    msgbuffer = g_malloc(2049);
-    count = read(send_thread_pipes[0], msgbuffer, 2048);
-
-    if (count < (ssize_t) sizeof(void *)) {
-        g_free(msgbuffer);
-        return TRUE;
-    }
-
-    currentpos = (SendThreadMessage **) msgbuffer;
-
-    gdk_threads_enter();
-
-    while (count) {
-        threadmessage = *currentpos;
-
-        if (balsa_app.debug)
-            fprintf(stderr, "Send_Message: %lu, %d, %s\n",
-                    (unsigned long) threadmessage,
-                    threadmessage->message_type,
-                    threadmessage->message_string);
-
-        switch (threadmessage->message_type) {
-        case MSGSENDTHREADERROR:
-            balsa_information(LIBBALSA_INFORMATION_WARNING,
-                              _("Sending error: %s"),
-                              threadmessage->message_string);
-            break;
-
-        case MSGSENDTHREADPOSTPONE:
-            fprintf(stderr, "Send Postpone %s\n",
-                    threadmessage->message_string);
-            break;
-
-        case MSGSENDTHREADPROGRESS:
-            fraction = threadmessage->of_total;
-
-            if (fraction == 0 && send_dialog) {
-                gtk_label_set_text(GTK_LABEL(send_progress_message),
-                                   threadmessage->message_string);
-                gtk_widget_show_all(send_dialog);
-            }
-
-            if (send_dialog)
-                gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR
-                                              (send_dialog_bar),
-                                              fraction);
-            else
-                gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR
-                                              ((*window)->progress_bar),
-                                              fraction);
-
-            /* display progress x of y, y = of_total */
-            break;
-
-        case MSGSENDTHREADFINISHED:
-            /* closes progress dialog */
-            if (send_dialog)
-                gtk_widget_destroy(send_dialog);
-            break;
-
-        default:
-            fprintf(stderr, " Unknown: %s \n",
-                    threadmessage->message_string);
-        }
-        g_free(threadmessage);
-        currentpos++;
-        count -= sizeof(void *);
-    }
-
-    gdk_threads_leave();
-
-    g_free(msgbuffer);
-
-    return TRUE;
-}
-
 
 /** Returns properly formatted string informing the user about the
     amount of the new mail.
diff --git a/src/main.c b/src/main.c
index 727a09d..3bd5a14 100644
--- a/src/main.c
+++ b/src/main.c
@@ -57,14 +57,10 @@
 #endif
 
 /* Globals for Thread creation, messaging, pipe I/O */
-GMutex send_messages_lock;
 gboolean checking_mail;
 int mail_thread_pipes[2];
-int send_thread_pipes[2];
 GIOChannel *mail_thread_msg_send;
 GIOChannel *mail_thread_msg_receive;
-GIOChannel *send_thread_msg_send;
-GIOChannel *send_thread_msg_receive;
 
 static void threads_init(void);
 static void threads_destroy(void);
@@ -177,7 +173,6 @@ GMutex checking_mail_lock;
 static void
 threads_init(void)
 {
-    g_mutex_init(&send_messages_lock);
     if (pipe(mail_thread_pipes) < 0) {
        g_log("BALSA Init", G_LOG_LEVEL_DEBUG,
              "Error opening pipes.\n");
@@ -188,24 +183,12 @@ threads_init(void)
     g_io_add_watch(mail_thread_msg_receive, G_IO_IN,
                   (GIOFunc) mail_progress_notify_cb,
                    &balsa_app.main_window);
-
-    if (pipe(send_thread_pipes) < 0) {
-       g_log("BALSA Init", G_LOG_LEVEL_DEBUG,
-             "Error opening pipes.\n");
-    }
-    send_thread_msg_send = g_io_channel_unix_new(send_thread_pipes[1]);
-    send_thread_msg_receive =
-       g_io_channel_unix_new(send_thread_pipes[0]);
-    g_io_add_watch(send_thread_msg_receive, G_IO_IN,
-                  (GIOFunc) send_progress_notify_cb,
-                   &balsa_app.main_window);
 }
 
 static void
 threads_destroy(void)
 {
     g_mutex_clear(&checking_mail_lock);
-    g_mutex_clear(&send_messages_lock);
 }
 
 /* initial_open_mailboxes:
diff --git a/src/pref-manager.c b/src/pref-manager.c
index 3915def..21eee1c 100644
--- a/src/pref-manager.c
+++ b/src/pref-manager.c
@@ -33,6 +33,7 @@
 #include "address-book-config.h"
 #include "quote-color.h"
 #include "misc.h"
+#include "send.h"
 #include "imap-server.h"
 
 #if HAVE_MACOSX_DESKTOP
@@ -115,6 +116,8 @@ typedef struct _PropertyUI {
     GtkWidget *remember_open_mboxes;
     GtkWidget *mblist_show_mb_content_info;
     GtkWidget *always_queue_sent_mail;
+    GtkWidget *send_mail_auto;
+    GtkWidget *send_mail_minutes;
     GtkWidget *copy_to_sentbox;
     GtkWidget *autoquote;
     GtkWidget *reply_include_html_parts;
@@ -490,8 +493,15 @@ apply_prefs(GtkDialog * pbox)
     balsa_app.always_queue_sent_mail =
         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
                                      (pui->always_queue_sent_mail));
-    if (balsa_app.always_queue_sent_mail != save_setting)
+    if (balsa_app.always_queue_sent_mail != save_setting) {
         balsa_toolbar_model_changed(balsa_window_get_toolbar_model());
+    }
+
+       balsa_app.send_mail_auto =
+               gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pui->send_mail_auto));
+       balsa_app.send_mail_timer =
+               gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pui->send_mail_minutes));
+       libbalsa_auto_send_config(balsa_app.send_mail_auto, balsa_app.send_mail_timer);
 
     balsa_app.copy_to_sentbox =
         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
@@ -748,6 +758,12 @@ set_prefs(void)
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
                                  (pui->always_queue_sent_mail),
                                  balsa_app.always_queue_sent_mail);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pui->send_mail_auto),
+                                                                balsa_app.send_mail_auto);
+       gtk_spin_button_set_value(GTK_SPIN_BUTTON(pui->send_mail_minutes),
+                                                         (float) balsa_app.send_mail_timer);
+       gtk_widget_set_sensitive(pui->send_mail_minutes,
+                                                        
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pui->send_mail_auto)));
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pui->copy_to_sentbox),
                                  balsa_app.copy_to_sentbox);
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pui->autoquote),
@@ -1730,6 +1746,15 @@ timer_modified_cb(GtkWidget * widget, GtkWidget * pbox)
 }
 
 static void
+send_timer_modified_cb(GtkWidget * widget, GtkWidget * pbox)
+{
+       gboolean newstate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pui->send_mail_auto));
+
+       gtk_widget_set_sensitive(GTK_WIDGET(pui->send_mail_minutes), newstate);
+       properties_modified_cb(widget, pbox);
+}
+
+static void
 browse_modified_cb(GtkWidget * widget, GtkWidget * pbox)
 {
     gboolean newstate =
@@ -2258,6 +2283,8 @@ pm_grid_add_other_options_group(GtkWidget * grid_widget)
 {
     GtkGrid *grid = (GtkGrid *) grid_widget;
     gint row = pm_grid_get_next_row(grid);
+       GtkWidget *label;
+       GtkAdjustment *spinbutton_adj;
 
     pm_grid_attach(grid, pm_group_label(_("Other options")), 0, row, 3, 1);
 
@@ -2274,6 +2301,17 @@ pm_grid_add_other_options_group(GtkWidget * grid_widget)
     pui->always_queue_sent_mail =
         pm_grid_attach_check(grid, 1, ++row, 2, 1, _("Send button always queues "
                                                      "outgoing mail in outbox"));
+
+    pui->send_mail_auto =
+       pm_grid_attach_check(grid, 1, ++row, 1, 1, _("_Send queued mail automatically every"));
+       spinbutton_adj = gtk_adjustment_new(10, 1, 120, 1, 10, 0);
+       pui->send_mail_minutes = gtk_spin_button_new(spinbutton_adj, 1, 0);
+       gtk_widget_set_hexpand(pui->send_mail_minutes, TRUE);
+       pm_grid_attach(grid, pui->send_mail_minutes, 2, row, 1, 1);
+       label = gtk_label_new(_("minutes"));
+       gtk_widget_set_halign(label, GTK_ALIGN_START);
+       pm_grid_attach(grid, label, 3, row, 1, 1);
+
     pui->edit_headers =
         pm_grid_attach_check(grid, 1, ++row, 2, 1, _("Edit headers in external editor"));
     pui->reply_include_html_parts =
@@ -3421,6 +3459,10 @@ open_preferences_manager(GtkWidget * widget, gpointer data)
                      G_CALLBACK(properties_modified_cb), property_box);
     g_signal_connect(G_OBJECT(pui->always_queue_sent_mail), "toggled",
                      G_CALLBACK(properties_modified_cb), property_box);
+    g_signal_connect(G_OBJECT(pui->send_mail_auto), "toggled",
+                     G_CALLBACK(send_timer_modified_cb), property_box);
+    g_signal_connect(G_OBJECT(pui->send_mail_minutes), "changed",
+                     G_CALLBACK(send_timer_modified_cb), property_box);
     g_signal_connect(G_OBJECT(pui->copy_to_sentbox), "toggled",
                      G_CALLBACK(properties_modified_cb), property_box);
     g_signal_connect(G_OBJECT(pui->autoquote), "toggled",
diff --git a/src/save-restore.c b/src/save-restore.c
index 5799fd8..7c7c418 100644
--- a/src/save-restore.c
+++ b/src/save-restore.c
@@ -40,6 +40,7 @@
 #include "threads.h"
 
 #include "smtp-server.h"
+#include "send.h"
 
 #define FOLDER_SECTION_PREFIX "folder-"
 #define MAILBOX_SECTION_PREFIX "mailbox-"
@@ -989,6 +990,13 @@ config_global_load(void)
 
     balsa_app.autoquote = 
        libbalsa_conf_get_bool("AutoQuote=true");
+    balsa_app.send_mail_auto = libbalsa_conf_get_bool("AutoSend=true");
+    balsa_app.send_mail_timer = libbalsa_conf_get_int("AutoSendDelay=15");
+    if ((balsa_app.send_mail_timer < 1U) || (balsa_app.send_mail_timer > 120U)) {
+       balsa_app.send_mail_timer = 15U;
+    }
+    libbalsa_auto_send_config(balsa_app.send_mail_auto, balsa_app.send_mail_timer);
+
     balsa_app.reply_strip_html = 
        libbalsa_conf_get_bool("StripHtmlInReply=true");
     balsa_app.forward_attached = 
@@ -1418,6 +1426,8 @@ config_save(void)
     libbalsa_conf_set_bool("ForwardAttached", balsa_app.forward_attached);
 
        libbalsa_conf_set_int("AlwaysQueueSentMail", balsa_app.always_queue_sent_mail);
+    libbalsa_conf_set_bool("AutoSend", balsa_app.send_mail_auto);
+    libbalsa_conf_set_int("AutoSendDelay", balsa_app.send_mail_timer);
        libbalsa_conf_set_int("CopyToSentbox", balsa_app.copy_to_sentbox);
     libbalsa_conf_pop_group();
 
diff --git a/src/threads.h b/src/threads.h
index a2eb539..3414f6b 100644
--- a/src/threads.h
+++ b/src/threads.h
@@ -30,12 +30,6 @@ extern gboolean checking_mail;
 extern int mail_thread_pipes[2];
 extern GIOChannel *mail_thread_msg_send;
 extern GIOChannel *mail_thread_msg_receive;
-extern GIOChannel *send_thread_msg_send;
-extern GIOChannel *send_thread_msg_receive;
-
-extern GtkWidget *send_progress_message;
-extern GtkWidget *send_dialog;
-extern GtkWidget *send_dialog_bar;
 
 typedef struct {
     LibBalsaMailboxNotify message_type;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]