[Commits] [SCM] claws branch, gtk3tree, updated. 4.0.0-396-g46bab8db2

paul at claws-mail.org paul at claws-mail.org
Wed Nov 3 11:31:23 UTC 2021


The branch, gtk3tree has been updated
       via  46bab8db2b94bd150dd7b27ea53a02ac6721c730 (commit)
      from  c134d0648710e3cb1a6d2403561590dbc2e3d652 (commit)

Summary of changes:
 src/mimeview.c                    | 393 ++++++++++------------------
 src/mimeview.h                    |  28 +-
 src/plugins/pgpcore/claws.def     |   3 +
 src/plugins/pgpcore/pgp_viewer.c  |   1 +
 src/plugins/pgpcore/plugin.def    |  42 +--
 src/plugins/pgpcore/sgpgme.c      | 200 +++++++++++++-
 src/plugins/pgpcore/sgpgme.h      |  30 ++-
 src/plugins/pgpinline/claws.def   |   2 +
 src/plugins/pgpinline/pgpinline.c | 318 ++++++++++++++---------
 src/plugins/pgpinline/plugin.c    |   2 +-
 src/plugins/pgpmime/claws.def     |   7 +-
 src/plugins/pgpmime/mypgpcore.def |   7 +-
 src/plugins/pgpmime/pgpmime.c     | 211 +++++----------
 src/plugins/pgpmime/plugin.c      |   4 +-
 src/plugins/smime/claws.def       |   9 +-
 src/plugins/smime/mypgpcore.def   |   5 +-
 src/plugins/smime/plugin.c        |  10 +-
 src/plugins/smime/smime.c         | 534 ++++++++++++++++++++++++--------------
 src/privacy.c                     | 115 ++++----
 src/privacy.h                     |  36 ++-
 src/procmime.c                    |   5 +-
 src/procmime.h                    |   7 +-
 22 files changed, 1123 insertions(+), 846 deletions(-)


- Log -----------------------------------------------------------------
commit 46bab8db2b94bd150dd7b27ea53a02ac6721c730
Author: Jonathan Boeing <jonathan at claws-mail.org>
Date:   Mon Aug 2 23:02:40 2021 -0700

    fix bug 4517, 'Thread safety issues in signature checking code'
    
    Move from using raw pthreads to GLib tasks for safety and convenience.
    Also includes some extra fixes for leaked gpgme keys and contexts.

diff --git a/src/mimeview.c b/src/mimeview.c
index eb25edb1d..c57dd1617 100644
--- a/src/mimeview.c
+++ b/src/mimeview.c
@@ -557,26 +557,11 @@ void mimeview_show_message(MimeView *mimeview, MimeInfo *mimeinfo,
 					  mimeview_selected, mimeview);
 }
 
-#ifdef USE_PTHREAD
-static void mimeview_check_sig_cancel_now(MimeView *mimeview);
-#endif
-
 static void mimeview_free_mimeinfo(MimeView *mimeview)
 {
-	gboolean defer = FALSE;
-#ifdef USE_PTHREAD
-	defer = (mimeview->check_data != NULL);
-	if (defer)
-		mimeview->check_data->free_after_use = TRUE;
-#endif
-	if (mimeview->mimeinfo != NULL && !defer) {
+	if (mimeview->mimeinfo != NULL) {
 		procmime_mimeinfo_free_all(&mimeview->mimeinfo);
 		mimeview->mimeinfo = NULL;
-	} else if (defer) {
-#ifdef USE_PTHREAD
-		debug_print("deferring free(mimeinfo) and cancelling check\n");
-		mimeview_check_sig_cancel_now(mimeview);
-#endif
 	}
 }
 
@@ -591,20 +576,19 @@ void mimeview_destroy(MimeView *mimeview)
 	g_slist_free(mimeview->viewers);
 	gtk_target_list_unref(mimeview->target_list);
 
-#ifdef USE_PTHREAD
-	if (mimeview->check_data) {
-		mimeview->check_data->destroy_mimeview = TRUE;
-		debug_print("deferring destroy\n");
-	} else 
-#endif
-	{
-		mimeview_free_mimeinfo(mimeview);
-		gtk_tree_path_free(mimeview->opened);
-		g_free(mimeview->file);
-		g_free(mimeview);
-		mimeviews = g_slist_remove(mimeviews, mimeview);
+	if (mimeview->sig_check_timeout_tag != 0)
+		g_source_remove(mimeview->sig_check_timeout_tag);
+	if (mimeview->sig_check_cancellable != NULL) {
+		/* Set last_sig_check_task to NULL to discard results in async_cb */
+		mimeview->siginfo->last_sig_check_task = NULL;
+		g_cancellable_cancel(mimeview->sig_check_cancellable);
+		g_object_unref(mimeview->sig_check_cancellable);
 	}
-	
+	mimeview_free_mimeinfo(mimeview);
+	gtk_tree_path_free(mimeview->opened);
+	g_free(mimeview->file);
+	g_free(mimeview);
+	mimeviews = g_slist_remove(mimeviews, mimeview);
 }
 
 MimeInfo *mimeview_get_selected_part(MimeView *mimeview)
@@ -998,6 +982,19 @@ void mimeview_clear(MimeView *mimeview)
 	if (g_slist_find(mimeviews, mimeview) == NULL)
 		return;
 	
+	if (mimeview->sig_check_timeout_tag != 0) {
+		g_source_remove(mimeview->sig_check_timeout_tag);
+		mimeview->sig_check_timeout_tag = 0;
+	}
+
+	if (mimeview->sig_check_cancellable != NULL) {
+		/* Set last_sig_check_task to NULL to discard results in async_cb */
+		mimeview->siginfo->last_sig_check_task = NULL;
+		g_cancellable_cancel(mimeview->sig_check_cancellable);
+		g_object_unref(mimeview->sig_check_cancellable);
+		mimeview->sig_check_cancellable = NULL;
+	}
+
 	noticeview_hide(mimeview->siginfoview);
 
 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(mimeview->ctree));
@@ -1035,21 +1032,20 @@ gchar * get_message_check_signature_shortcut(MessageView *messageview) {
 static void check_signature_cb(GtkWidget *widget, gpointer user_data);
 static void display_full_info_cb(GtkWidget *widget, gpointer user_data);
 
-static void update_signature_noticeview(MimeView *mimeview, MimeInfo *mimeinfo, 
-					gboolean special, SignatureStatus code)
+static void update_signature_noticeview(MimeView *mimeview, gboolean special, SignatureStatus code)
 {
 	gchar *text = NULL, *button_text = NULL;
 	void  *func = NULL;
 	StockPixmap icon = STOCK_PIXMAP_PRIVACY_SIGNED;
 	SignatureStatus mycode = SIGNATURE_UNCHECKED;
 	
-	cm_return_if_fail(mimeview != NULL);
-	cm_return_if_fail(mimeinfo != NULL);
-	
+	if (mimeview == NULL || mimeview->siginfo == NULL)
+		g_error("bad call to update noticeview");
+
 	if (special)
 		mycode = code;
-	else 
-		mycode = privacy_mimeinfo_get_sig_status(mimeinfo);
+	else
+		mycode = privacy_mimeinfo_get_sig_status(mimeview->siginfo);
 
 	switch (mycode) {
 	case SIGNATURE_UNCHECKED:
@@ -1077,6 +1073,7 @@ static void update_signature_noticeview(MimeView *mimeview, MimeInfo *mimeinfo,
 		func = display_full_info_cb;
 		icon = STOCK_PIXMAP_PRIVACY_FAILED;
 		break;
+	case SIGNATURE_CHECK_ERROR:
 	case SIGNATURE_CHECK_FAILED:
 	case SIGNATURE_CHECK_TIMEOUT:
 		button_text = _("Check again");
@@ -1086,19 +1083,17 @@ static void update_signature_noticeview(MimeView *mimeview, MimeInfo *mimeinfo,
 	default:
 		break;
 	}
+
 	if (mycode == SIGNATURE_UNCHECKED) {
-		gchar *tmp = privacy_mimeinfo_sig_info_short(mimeinfo);
+		gchar *tmp = privacy_mimeinfo_get_sig_info(mimeview->siginfo, FALSE);
 		gchar *shortcut = get_message_check_signature_shortcut(mimeview->messageview);
 
 		if (*shortcut == '\0')
 			text = g_strdup_printf(_("%s Click the icon to check it."), tmp);
 		else
-			text = g_strdup_printf(_("%s Click the icon or hit '%s' to check it."),
-				tmp, shortcut);
-		g_free(tmp);
+			text = g_strdup_printf(_("%s Click the icon or hit '%s' to check it."), tmp, shortcut);
+
 		g_free(shortcut);
-	} else if (mycode != SIGNATURE_CHECK_TIMEOUT) {
-		text = privacy_mimeinfo_sig_info_short(mimeinfo);
 	} else if (mycode == SIGNATURE_CHECK_TIMEOUT) {
 		gchar *shortcut = get_message_check_signature_shortcut(mimeview->messageview);
 
@@ -1106,13 +1101,25 @@ static void update_signature_noticeview(MimeView *mimeview, MimeInfo *mimeinfo,
 			text = g_strdup(_("Timeout checking the signature. Click the icon to try again."));
 		else
 			text = g_strdup_printf(_("Timeout checking the signature. Click the icon or hit '%s' to try again."), shortcut);
+
+		g_free(shortcut);
+	} else if (mycode == SIGNATURE_CHECK_ERROR) {
+		gchar *shortcut = get_message_check_signature_shortcut(mimeview->messageview);
+
+		if (*shortcut == '\0')
+			text = g_strdup(_("Error checking the signature. Click the icon to try again."));
+		else
+			text = g_strdup_printf(_("Error checking the signature. Click the icon or hit '%s' to try again."), shortcut);
+
 		g_free(shortcut);
+	} else {
+		text = g_strdup(privacy_mimeinfo_get_sig_info(mimeview->siginfo, FALSE));
 	}
 
 	noticeview_set_text(mimeview->siginfoview, text);
 	gtk_label_set_selectable(GTK_LABEL(mimeview->siginfoview->text), TRUE);
-
 	g_free(text);
+
 	noticeview_set_button_text(mimeview->siginfoview, NULL);
 	noticeview_set_button_press_callback(
 		mimeview->siginfoview,
@@ -1125,242 +1132,129 @@ static void update_signature_noticeview(MimeView *mimeview, MimeInfo *mimeinfo,
 	icon_list_create(mimeview, mimeview->mimeinfo);
 }
 
-#ifdef USE_PTHREAD
-
-/* reset all thread stuff, and do the cleanups we've been left to do */
-static void mimeview_check_data_reset(MimeView *mimeview)
+static void check_signature_async_cb(GObject *source_object,
+	GAsyncResult *async_result,
+	gpointer user_data)
 {
-	gboolean must_free;
-	gboolean must_destroy;
+	GTask *task = G_TASK(async_result);
+	GCancellable *cancellable;
+	MimeView *mimeview = (MimeView *)user_data;
+	gboolean cancelled;
+	SigCheckTaskResult *result;
+	GError *error = NULL;
 
-	if (!mimeview->check_data)
+	if (mimeview->siginfo == NULL) {
+		debug_print("discarding stale sig check task result task:%p\n", task);
 		return;
-
-	must_free = mimeview->check_data->free_after_use;
-	must_destroy = mimeview->check_data->destroy_mimeview;
-	
-	if (mimeview->check_data->cancel_th_init) {
-		debug_print("killing canceller thread\n");
-		mimeview->check_data->cancel_th_init = FALSE;
-		pthread_cancel(mimeview->check_data->cancel_th);
-	}
-
-	if (must_free) {
-		debug_print("freeing deferred mimeinfo\n");
-		procmime_mimeinfo_free_all(&mimeview->check_data->siginfo);
+	} else if (task != mimeview->siginfo->last_sig_check_task) {
+		debug_print("discarding stale sig check task result last_task:%p task:%p\n",
+			mimeview->siginfo->last_sig_check_task, task);
+		return;
+	} else {
+		debug_print("using sig check task result task:%p\n", task);
+		mimeview->siginfo->last_sig_check_task = NULL;
 	}
 
-	g_free(mimeview->check_data);
-	mimeview->check_data = NULL;
+	cancellable = g_task_get_cancellable(task);
+	cancelled = g_cancellable_set_error_if_cancelled(cancellable, &error);
+	if (cancelled) {
+		debug_print("sig check task was cancelled: task:%p GError: domain:%s code:%d message:\"%s\"\n",
+			task, g_quark_to_string(error->domain), error->code, error->message);
+		g_error_free(error);
+		update_signature_noticeview(mimeview, TRUE, SIGNATURE_CHECK_TIMEOUT);
+		return;
+	} else {
+		if (mimeview->sig_check_cancellable == NULL)
+			g_error("bad cancellable");
+		if (mimeview->sig_check_timeout_tag == 0)
+			g_error("bad cancel source tag");
 
-	if (must_destroy) {
-		debug_print("freeing deferred mimeview\n");
-		mimeview_free_mimeinfo(mimeview);
-		gtk_tree_path_free(mimeview->opened);
-		g_free(mimeview->file);
-		g_free(mimeview);
-		mimeviews = g_slist_remove(mimeviews, mimeview);
+		g_source_remove(mimeview->sig_check_timeout_tag);
+		mimeview->sig_check_timeout_tag = 0;
+		g_object_unref(mimeview->sig_check_cancellable);
+		mimeview->sig_check_cancellable = NULL;
 	}
-}
 
-/* GUI update once the checker thread is done or killed */
-static gboolean mimeview_check_sig_thread_cb(void *data)
-{
-	MimeView *mimeview = (MimeView *) data;
-	MimeInfo *mimeinfo = mimeview->siginfo;
+	result = g_task_propagate_pointer(task, &error);
 
-	debug_print("mimeview_check_sig_thread_cb\n");
-	
-	if (mimeinfo == NULL) {
-		/* message changed !? */
-		g_warning("no more siginfo!");
-		goto end;
-	}
-	
-	if (!mimeview->check_data) {
-		g_warning("nothing to check");
-		return FALSE;
+	if (mimeview->siginfo->sig_data) {
+		privacy_free_signature_data(mimeview->siginfo->sig_data);
+		mimeview->siginfo->sig_data = NULL;
 	}
 
-	if (mimeview->check_data->siginfo != mimeinfo) {
-		/* message changed !? */
-		g_warning("different siginfo!");
-		goto end;
-	}
-
-	if (mimeview->check_data->destroy_mimeview ||
-	    mimeview->check_data->free_after_use) {
-	    	debug_print("not bothering, we're changing message\n"); 
-		goto end;
+	if (result == NULL) {
+		debug_print("sig check task propagated NULL task:%p GError: domain:%s code:%d message:\"%s\"\n",
+			task, g_quark_to_string(error->domain), error->code, error->message);
+		g_error_free(error);
+		update_signature_noticeview(mimeview, TRUE, SIGNATURE_CHECK_ERROR);
+		return;
 	}
-	
-	/* update status */
-	if (mimeview->check_data->timeout) 
-		update_signature_noticeview(mimeview, mimeview->siginfo, 
-			TRUE, SIGNATURE_CHECK_TIMEOUT);
-	else
-		update_signature_noticeview(mimeview, mimeview->siginfo, 
-			FALSE, 0);
 
-end:
-	mimeview_check_data_reset(mimeview);
-	return FALSE;
-}
+	mimeview->siginfo->sig_data = result->sig_data;
+	update_signature_noticeview(mimeview, FALSE, 0);
 
-/* sig checker thread */
-static void *mimeview_check_sig_worker_thread(void *data)
-{
-	MimeView *mimeview = (MimeView *)data;
-	MimeInfo *mimeinfo = mimeview->siginfo;
-	
-	debug_print("checking...\n");
-
-	if (!mimeview->check_data)
-		return NULL;
-
-	if (mimeinfo && mimeinfo == mimeview->check_data->siginfo) {
-		privacy_mimeinfo_check_signature(mimeinfo);
-		if (mimeview->check_data && mimeview->check_data->cancel_th_init) {
-			mimeview->check_data->cancel_th_init = FALSE;
-			pthread_cancel(mimeview->check_data->cancel_th);
-		}
-	} else {
-		/* that's strange! we changed message without 
-		 * getting killed. */
-		g_warning("different siginfo!");
-		mimeview_check_data_reset(mimeview);
-		return NULL;
+	if (result->newinfo) {
+		g_warning("Check sig task returned an unexpected new MimeInfo");
+		procmime_mimeinfo_free_all(&result->newinfo);
 	}
 
-	/* use g_timeout so that GUI updates is done from the
-	 * correct thread */
-	g_timeout_add(0,mimeview_check_sig_thread_cb,mimeview);
-	
-	return NULL;
+	g_free(result);
 }
 
-/* killer thread - acts when the checker didn't work fast
- * enough. */
-static void *mimeview_check_sig_cancel_thread(void *data)
+gboolean mimeview_check_sig_timeout(gpointer user_data)
 {
-	MimeView *mimeview = (MimeView *)data;
-	
-	if (!mimeview->check_data)
-		return NULL; /* nothing to kill ! */
+	MimeView *mimeview = (MimeView *)user_data;
+	GCancellable *cancellable = mimeview->sig_check_cancellable;
 
-	/* wait for a few seconds... */
-	debug_print("waiting a while\n");
+	mimeview->sig_check_timeout_tag = 0;
 
-	g_usleep(5 * 1000 * 1000);
-	
-	if (!mimeview->check_data)
-		return NULL; /* nothing to kill, it's done in time :) */
-	
-	/* too late, go away checker thread */
-	debug_print("killing checker thread\n");
-	if (mimeview->check_data->th_init) {
-		mimeview->check_data->th_init = FALSE;
-		pthread_cancel(mimeview->check_data->th);
+	if (cancellable == NULL) {
+		return G_SOURCE_REMOVE;
 	}
 
-	/* tell upstream it was a timeout */
-	mimeview->check_data->timeout = TRUE;
-	/* use g_timeout so that GUI updates is done from the
-	 * correct thread */
-	g_timeout_add(0,mimeview_check_sig_thread_cb,mimeview);
+	mimeview->sig_check_cancellable = NULL;
+	g_cancellable_cancel(cancellable);
+	g_object_unref(cancellable);
 
-	return NULL;
+	return G_SOURCE_REMOVE;
 }
 
-/* get rid of the checker thread right now - used when changing the
- * displayed message for example. */
-static void mimeview_check_sig_cancel_now(MimeView *mimeview)
-{
-	if (!mimeview->check_data)
-		return;
-	debug_print("killing checker thread NOW\n");
-	if (mimeview->check_data->th_init) {
-		mimeview->check_data->th_init = FALSE;
-		pthread_cancel(mimeview->check_data->th);
-	}
-
-	/* tell upstream it was a timeout */
-	mimeview->check_data->timeout = TRUE;
-	mimeview_check_sig_thread_cb(mimeview);
-	return;
-}
-
-/* creates a thread to check the signature, and a second one
- * to kill the first one after a timeout */
-static void mimeview_check_sig_in_thread(MimeView *mimeview)
-{
-	pthread_t th, th2;
-	pthread_attr_t detach, detach2;
-	
-	if (mimeview->check_data) {
-		g_warning("already checking it");
-		return;
-	}
-	
-	mimeview->check_data = g_new0(SigCheckData, 1);
-	mimeview->check_data->siginfo = mimeview->siginfo;
-	debug_print("creating thread\n");
-
-	/* init thread attributes and create the checker thread */
-	if (pthread_attr_init(&detach) != 0 ||
-	    pthread_attr_setdetachstate(&detach, PTHREAD_CREATE_DETACHED) != 0 ||
-	    pthread_attr_init(&detach2) != 0 ||
-	    pthread_attr_setdetachstate(&detach2, PTHREAD_CREATE_DETACHED) != 0 ||
-	    pthread_create(&th, &detach, 
-			mimeview_check_sig_worker_thread, 
-			mimeview) != 0) {
-		/* arh. We'll do it synchronously. */
-		g_warning("can't create checked thread");
-		g_free(mimeview->check_data);
-		mimeview->check_data = NULL;
-		return;
-	} else {
-		mimeview->check_data->th = th;
-		mimeview->check_data->th_init = TRUE;
-	}
-
-	/* create the killer thread */
-	if (pthread_create(&th2, &detach2, 
-			mimeview_check_sig_cancel_thread, 
-			mimeview) != 0) {
-		g_warning("can't create killer thread");
-		g_free(mimeview->check_data);
-		mimeview->check_data = NULL;
-		return;
-	} else {
-		mimeview->check_data->cancel_th = th2;
-		mimeview->check_data->cancel_th_init = TRUE;
-	}
-}
-#endif
-
 static void check_signature_cb(GtkWidget *widget, gpointer user_data)
 {
 	MimeView *mimeview = (MimeView *) user_data;
 	MimeInfo *mimeinfo = mimeview->siginfo;
+	gint ret;
 	
 	if (mimeinfo == NULL || !noticeview_is_visible(mimeview->siginfoview))
 		return;
-#ifdef USE_PTHREAD
-	if (mimeview->check_data)
-		return;
-#endif
+
 	noticeview_set_text(mimeview->siginfoview, _("Checking signature..."));
 	GTK_EVENTS_FLUSH();
-#ifdef USE_PTHREAD
-	/* let's do it non-blocking */
-	mimeview_check_sig_in_thread(mimeview);
-	if (!mimeview->check_data) /* let's check syncronously */
-#endif
-	{
-		debug_print("checking without thread\n");
-		privacy_mimeinfo_check_signature(mimeinfo);
-		update_signature_noticeview(mimeview, mimeview->siginfo, FALSE, 0);
+
+	if (mimeview->sig_check_cancellable != NULL) {
+		if (mimeview->sig_check_timeout_tag == 0)
+			g_error("bad cancel source tag");
+		g_source_remove(mimeview->sig_check_timeout_tag);
+		g_cancellable_cancel(mimeview->sig_check_cancellable);
+		g_object_unref(mimeview->sig_check_cancellable);
+	}
+
+	mimeview->sig_check_cancellable = g_cancellable_new();
+
+	ret = privacy_mimeinfo_check_signature(mimeview->siginfo,
+		mimeview->sig_check_cancellable,
+		check_signature_async_cb,
+		mimeview);
+	if (ret == 0) {
+		mimeview->sig_check_timeout_tag = g_timeout_add_seconds(5, mimeview_check_sig_timeout, mimeview);
+	} else if (ret < 0) {
+		g_object_unref(mimeview->sig_check_cancellable);
+		mimeview->sig_check_cancellable = NULL;
+		update_signature_noticeview(mimeview, TRUE, SIGNATURE_CHECK_ERROR);
+	} else {
+		g_object_unref(mimeview->sig_check_cancellable);
+		mimeview->sig_check_cancellable = NULL;
+		update_signature_noticeview(mimeview, FALSE, 0);
 	}
 }
 
@@ -1381,11 +1275,8 @@ static void redisplay_email(GtkWidget *widget, gpointer user_data)
 static void display_full_info_cb(GtkWidget *widget, gpointer user_data)
 {
 	MimeView *mimeview = (MimeView *) user_data;
-	gchar *siginfo;
 
-	siginfo = privacy_mimeinfo_sig_info_full(mimeview->siginfo);
-	textview_set_text(mimeview->textview, siginfo);
-	g_free(siginfo);
+	textview_set_text(mimeview->textview, privacy_mimeinfo_get_sig_info(mimeview->siginfo, TRUE));
 	noticeview_set_button_text(mimeview->siginfoview, NULL);
 	noticeview_set_button_press_callback(
 		mimeview->siginfoview,
@@ -1434,7 +1325,7 @@ static void update_signature_info(MimeView *mimeview, MimeInfo *selected)
 		return;
 	}
 	
-	update_signature_noticeview(mimeview, siginfo, FALSE, 0);
+	update_signature_noticeview(mimeview, FALSE, 0);
 	noticeview_show(mimeview->siginfoview);
 }
 
@@ -2637,6 +2528,7 @@ static void icon_list_append_icon (MimeView *mimeview, MimeInfo *mimeinfo)
 	if (siginfo != NULL) {
 		switch (privacy_mimeinfo_get_sig_status(siginfo)) {
 		case SIGNATURE_UNCHECKED:
+		case SIGNATURE_CHECK_ERROR:
 		case SIGNATURE_CHECK_FAILED:
 		case SIGNATURE_CHECK_TIMEOUT:
 			pixmap = stock_pixmap_widget_with_overlay(stockp,
@@ -2656,7 +2548,7 @@ static void icon_list_append_icon (MimeView *mimeview, MimeInfo *mimeinfo)
 			    STOCK_PIXMAP_PRIVACY_EMBLEM_FAILED, OVERLAY_BOTTOM_RIGHT, 6, 3);
 			break;
 		}
-		sigshort = privacy_mimeinfo_sig_info_short(siginfo);
+		sigshort = privacy_mimeinfo_get_sig_info(siginfo, FALSE);
 	} else if (encrypted != NULL) {
 			pixmap = stock_pixmap_widget_with_overlay(stockp,
 			    STOCK_PIXMAP_PRIVACY_EMBLEM_ENCRYPTED, OVERLAY_BOTTOM_RIGHT, 6, 3);		
@@ -2706,7 +2598,6 @@ static void icon_list_append_icon (MimeView *mimeview, MimeInfo *mimeinfo)
 		g_free(sigshort_escaped);
 		tip = tiptmp;
 	}
-	g_free(sigshort);
 
 	gtk_widget_set_tooltip_markup(button, tip);
 	g_free(tip);
diff --git a/src/mimeview.h b/src/mimeview.h
index d7f3b7cd4..7d5f8ccac 100644
--- a/src/mimeview.h
+++ b/src/mimeview.h
@@ -1,6 +1,6 @@
 /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2021 the Claws Mail team and Hiroyuki Yamamoto
  *
  * 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
@@ -26,9 +26,6 @@ typedef struct _MimeViewer 		MimeViewer;
 #include <glib.h>
 #include <gdk/gdk.h>
 #include <gtk/gtk.h>
-#ifdef USE_PTHREAD
-#include <pthread.h>
-#endif
 
 #include "textview.h"
 #include "messageview.h"
@@ -41,22 +38,6 @@ typedef enum
 	MIMEVIEW_VIEWER
 } MimeViewType;
 
-#ifdef USE_PTHREAD
-typedef struct _SigCheckData SigCheckData;
-struct _SigCheckData
-{
-	pthread_t th;
-	pthread_t cancel_th;
-	gboolean th_init;
-	gboolean cancel_th_init;
-
-	MimeInfo *siginfo;
-	gboolean free_after_use;
-	gboolean destroy_mimeview;
-	gboolean timeout;
-};
-#endif
-
 struct _MimeView
 {
 	GtkWidget *hbox;
@@ -101,9 +82,8 @@ struct _MimeView
 	GtkActionGroup *action_group;
 	gboolean signed_part;
 
-#ifdef USE_PTHREAD
-	SigCheckData *check_data;
-#endif
+	GCancellable *sig_check_cancellable;
+	guint sig_check_timeout_tag;
 };
 
 struct _MimeViewerFactory
diff --git a/src/plugins/pgpcore/claws.def b/src/plugins/pgpcore/claws.def
index 87a931d4c..2ded33e41 100644
--- a/src/plugins/pgpcore/claws.def
+++ b/src/plugins/pgpcore/claws.def
@@ -60,9 +60,12 @@ prefs_set_block_label
 prefs_set_default
 prefs_write_open
 prefs_write_param
+privacy_free_sig_check_task_result
+privacy_free_signature_data
 privacy_get_error
 privacy_set_error
 procmime_mimeinfo_get_parameter
+procmime_mimeinfo_parent
 procmime_decode_content
 procmime_get_tmp_file_name
 procmime_get_part
diff --git a/src/plugins/pgpcore/pgp_viewer.c b/src/plugins/pgpcore/pgp_viewer.c
index 974531dc9..055d14242 100644
--- a/src/plugins/pgpcore/pgp_viewer.c
+++ b/src/plugins/pgpcore/pgp_viewer.c
@@ -304,6 +304,7 @@ static void pgpview_show_mime_part(TextView *textview, MimeInfo *partinfo)
 
 		TEXTVIEW_INSERT(":\n\n");
 		TEXTVIEW_INSERT(_("   This key is in your keyring.\n"));
+		gpgme_key_unref(key);
 	}
 	gpgme_data_release(sigdata);
 	gpgme_release(ctx);
diff --git a/src/plugins/pgpcore/plugin.def b/src/plugins/pgpcore/plugin.def
index 1f156d953..c9803ee18 100644
--- a/src/plugins/pgpcore/plugin.def
+++ b/src/plugins/pgpcore/plugin.def
@@ -1,29 +1,29 @@
 EXPORTS
-        plugin_desc
-        plugin_done
-        plugin_init
-        plugin_licence
-        plugin_name
+	plugin_desc
+	plugin_done
+	plugin_init
+	plugin_licence
+	plugin_name
 	plugin_provides
-        plugin_type
-        plugin_version
+	plugin_type
+	plugin_version
 
-        sgpgme_data_from_mimeinfo
+	sgpgme_data_from_mimeinfo
 	sgpgme_data_release_and_get_mem
-        sgpgme_decrypt_verify
-        sgpgme_get_encrypt_data
-        sgpgme_setup_signers
-        sgpgme_sigstat_gpgme_to_privacy
-        sgpgme_sigstat_info_full
-        sgpgme_sigstat_info_short
-        sgpgme_verify_signature
+	sgpgme_decrypt_verify
+	sgpgme_get_encrypt_data
+	sgpgme_setup_signers
+	sgpgme_sigstat_gpgme_to_privacy
+	sgpgme_sigstat_info_full
+	sgpgme_sigstat_info_short
+	sgpgme_verify_signature
 
-        gpgmegtk_passphrase_cb
-        prefs_gpg_add_skip_encryption_warning
-        prefs_gpg_enable_agent
-        prefs_gpg_remove_skip_encryption_warning
-        prefs_gpg_should_skip_encryption_warning
-        prefs_gpg_get_config
+	gpgmegtk_passphrase_cb
+	prefs_gpg_add_skip_encryption_warning
+	prefs_gpg_enable_agent
+	prefs_gpg_remove_skip_encryption_warning
+	prefs_gpg_should_skip_encryption_warning
+	prefs_gpg_get_config
 
 	cm_gpgme_data_rewind
 
diff --git a/src/plugins/pgpcore/sgpgme.c b/src/plugins/pgpcore/sgpgme.c
index 4e7312320..1d1775d35 100644
--- a/src/plugins/pgpcore/sgpgme.c
+++ b/src/plugins/pgpcore/sgpgme.c
@@ -67,6 +67,192 @@ static void sgpgme_disable_all(void)
      * gpgme messages */
 }
 
+void cm_free_detached_sig_task_data(gpointer data)
+{
+	DetachedSigTaskData *task_data = (DetachedSigTaskData *)data;
+
+	g_free(task_data->boundary);
+	g_free(task_data->text_filename);
+	g_free(task_data->sig_filename);
+	g_free(task_data);
+}
+
+void cm_check_detached_sig(GTask *task,
+	gpointer source_object,
+	gpointer _task_data,
+	GCancellable *cancellable)
+{
+	DetachedSigTaskData *task_data = (DetachedSigTaskData *)_task_data;
+	GQuark domain;
+	FILE *fp;
+	gpgme_ctx_t ctx;
+	gpgme_error_t err;
+	gpgme_data_t textdata = NULL;
+	gpgme_data_t sigdata = NULL;
+	gpgme_verify_result_t gpgme_res;
+	gchar *textstr;
+	gboolean return_err = TRUE;
+	gboolean cancelled = FALSE;
+	SigCheckTaskResult *task_result = NULL;
+	char err_str[GPGERR_BUFSIZE];
+
+	domain = g_quark_from_static_string("claws_pgpcore");
+
+	err = gpgme_new(&ctx);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("couldn't initialize GPG context: %s", err_str);
+		goto out;
+	}
+
+	err = gpgme_set_protocol(ctx, task_data->protocol);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("couldn't set GPG protocol: %s", err_str);
+		goto out_ctx;
+	}
+
+	fp = claws_fopen(task_data->text_filename, "rb");
+	if (fp == NULL) {
+		err = GPG_ERR_GENERAL;
+		g_snprintf(err_str, GPGERR_BUFSIZE, "claws_fopen failed");
+		goto out_ctx;
+	}
+
+	textstr = task_data->get_canonical_content(fp, task_data->boundary);
+	claws_fclose(fp);
+
+	err = gpgme_data_new_from_mem(&textdata, textstr, textstr?strlen(textstr):0, 0);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_data_new_from_mem failed: %s", err_str);
+		goto out_textstr;
+	}
+
+	fp = claws_fopen(task_data->sig_filename, "rb");
+	if (fp == NULL) {
+		err = GPG_ERR_GENERAL;
+		g_snprintf(err_str, GPGERR_BUFSIZE, "claws_fopen failed");
+		goto out_textdata;
+	}
+
+	err = gpgme_data_new_from_filepart(&sigdata, NULL, fp, task_data->sig_offset, task_data->sig_length);
+	claws_fclose(fp);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_data_new_from_filepart failed: %s", err_str);
+		goto out_textdata;
+	}
+
+	if (task_data->sig_encoding == ENC_BASE64) {
+		err = gpgme_data_set_encoding(sigdata, GPGME_DATA_ENCODING_BASE64);
+		if (err != GPG_ERR_NO_ERROR) {
+			gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+			g_warning("gpgme_data_set_encoding failed: %s\n", err_str);
+			goto out_sigdata;
+		}
+	}
+
+	if (g_task_return_error_if_cancelled(task)) {
+		debug_print("task was cancelled, aborting task:%p\n", task);
+		cancelled = TRUE;
+		goto out_sigdata;
+	}
+
+	err = gpgme_op_verify(ctx, sigdata, textdata, NULL);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_op_verify failed: %s\n", err_str);
+		goto out_sigdata;
+	}
+
+	if (g_task_return_error_if_cancelled(task)) {
+		debug_print("task was cancelled, aborting task:%p\n", task);
+		cancelled = TRUE;
+		goto out_sigdata;
+	}
+
+	gpgme_res = gpgme_op_verify_result(ctx);
+	if (gpgme_res && gpgme_res->signatures == NULL) {
+		err = GPG_ERR_SYSTEM_ERROR;
+		g_warning("no signature found");
+		g_snprintf(err_str, GPGERR_BUFSIZE, "No signature found");
+		goto out_sigdata;
+	}
+
+	task_result = g_new0(SigCheckTaskResult, 1);
+	task_result->sig_data = g_new0(SignatureData, 1);
+
+	task_result->sig_data->status = sgpgme_sigstat_gpgme_to_privacy(ctx, gpgme_res);
+	task_result->sig_data->info_short = sgpgme_sigstat_info_short(ctx, gpgme_res);
+	task_result->sig_data->info_full = sgpgme_sigstat_info_full(ctx, gpgme_res);
+
+	return_err = FALSE;
+
+out_sigdata:
+	gpgme_data_release(sigdata);
+out_textdata:
+	gpgme_data_release(textdata);
+out_textstr:
+	g_free(textstr);
+out_ctx:
+	gpgme_release(ctx);
+out:
+	if (cancelled)
+		return;
+
+	if (return_err)
+		g_task_return_new_error(task, domain, err, err_str);
+	else
+		g_task_return_pointer(task, task_result, privacy_free_sig_check_task_result);
+}
+
+gint cm_check_detached_sig_async(MimeInfo *mimeinfo,
+	GCancellable *cancellable,
+	GAsyncReadyCallback callback,
+	gpointer user_data,
+	gpgme_protocol_t protocol,
+	gchar *(*get_canonical_content)(FILE *, const gchar *))
+{
+	GTask *task;
+	DetachedSigTaskData *task_data;
+	MimeInfo *parent;
+	MimeInfo *signature;
+	gchar *boundary;
+
+	parent = procmime_mimeinfo_parent(mimeinfo);
+
+	boundary = g_hash_table_lookup(parent->typeparameters, "boundary");
+	if (boundary == NULL) {
+		debug_print("failed to lookup boundary string\n");
+		return -1;
+	}
+
+	signature = (MimeInfo *) mimeinfo->node->next->data;
+
+	task_data = g_new0(DetachedSigTaskData, 1);
+
+	task_data->protocol = protocol;
+	task_data->boundary = g_strdup(boundary);
+	task_data->text_filename = g_strdup(parent->data.filename);
+	task_data->sig_filename = g_strdup(signature->data.filename);
+	task_data->sig_offset = signature->offset;
+	task_data->sig_length = signature->length;
+	task_data->sig_encoding = signature->encoding_type;
+	task_data->get_canonical_content = get_canonical_content;
+
+	task = g_task_new(NULL, cancellable, callback, user_data);
+	mimeinfo->last_sig_check_task = task;
+
+	g_task_set_task_data(task, task_data, cm_free_detached_sig_task_data);
+	debug_print("creating check sig async task:%p task_data:%p\n", task, task_data);
+	g_task_set_return_on_cancel(task, TRUE);
+	g_task_run_in_thread(task, cm_check_detached_sig);
+	g_object_unref(task);
+
+	return 0;
+}
+
 gpgme_verify_result_t sgpgme_verify_signature(gpgme_ctx_t ctx, gpgme_data_t sig, 
 					gpgme_data_t plain, gpgme_data_t dummy)
 {
@@ -312,7 +498,7 @@ gchar *sgpgme_sigstat_info_short(gpgme_ctx_t ctx, gpgme_verify_result_t status)
 	g_free(uname);
 
 	if (key)
-		gpgme_key_release(key);
+		gpgme_key_unref(key);
 
 	return result;
 }
@@ -338,6 +524,7 @@ gchar *sgpgme_sigstat_info_full(gpgme_ctx_t ctx, gpgme_verify_result_t status)
 		struct tm lt;
 		gpgme_key_t key;
 		gpgme_error_t err;
+		gpgme_user_id_t tmp;
 		const gchar *keytype, *keyid, *uid;
 		
 		err = gpgme_get_key(ctx, sig->fpr, &key, 0);
@@ -398,14 +585,14 @@ gchar *sgpgme_sigstat_info_full(gpgme_ctx_t ctx, gpgme_verify_result_t status)
 		if (sig->status != GPG_ERR_BAD_SIGNATURE) {
 			gint j = 1;
 			if (key) {
-				key->uids = key->uids ? key->uids->next : NULL;
-				while (key->uids != NULL) {
+				tmp = key->uids ? key->uids->next : NULL;
+				while (tmp != NULL) {
 					g_string_append_printf(siginfo,
 						_("                    uid \"%s\" (Validity: %s)\n"),
-						key->uids->uid,
-						key->uids->revoked==TRUE?_("Revoked"):get_validity_str(key->uids->validity));
+						tmp->uid,
+						tmp->revoked==TRUE?_("Revoked"):get_validity_str(tmp->validity));
 					j++;
-					key->uids = key->uids->next;
+					tmp = tmp->next;
 				}
 			}
 			g_string_append_printf(siginfo,_("Owner Trust: %s\n"),
@@ -446,6 +633,7 @@ gchar *sgpgme_sigstat_info_full(gpgme_ctx_t ctx, gpgme_verify_result_t status)
 		g_string_append(siginfo, "\n");
 		i++;
 		sig = sig->next;
+		gpgme_key_unref(key);
 	}
 bail:
 	ret = siginfo->str;
diff --git a/src/plugins/pgpcore/sgpgme.h b/src/plugins/pgpcore/sgpgme.h
index 78127f463..8e494af60 100644
--- a/src/plugins/pgpcore/sgpgme.h
+++ b/src/plugins/pgpcore/sgpgme.h
@@ -1,6 +1,6 @@
 /*
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2012 the Claws Mail team
+ * Copyright (C) 1999-2021 the Claws Mail team
  *
  * 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
@@ -24,9 +24,37 @@
 
 #include "privacy.h"
 
+#define GPGERR_BUFSIZE 128
+
+typedef struct _DetachedSigTaskData
+{
+	gpgme_protocol_t protocol;
+	gchar *boundary;
+	gchar *text_filename;
+	gchar *sig_filename;
+	guint sig_offset;
+	guint sig_length;
+	EncodingType sig_encoding;
+	gchar *(*get_canonical_content)(FILE *, const gchar *);
+} DetachedSigTaskData;
+
 void sgpgme_init(void);
 void sgpgme_done(void);
 
+void cm_free_detached_sig_task_data(gpointer data);
+
+void cm_check_detached_sig(GTask *task,
+	gpointer source_object,
+	gpointer _task_data,
+	GCancellable *cancellable);
+
+gint cm_check_detached_sig_async(MimeInfo *mimeinfo,
+	GCancellable *cancellable,
+	GAsyncReadyCallback callback,
+	gpointer user_data,
+	gpgme_protocol_t protocol,
+	gchar *(*get_canonical_content)(FILE *, const gchar *));
+
 gpgme_verify_result_t sgpgme_verify_signature	(gpgme_ctx_t ctx,
 				    	 gpgme_data_t sig,
 				    	 gpgme_data_t plain,
diff --git a/src/plugins/pgpinline/claws.def b/src/plugins/pgpinline/claws.def
index fe11fa107..ed5ce6ee4 100644
--- a/src/plugins/pgpinline/claws.def
+++ b/src/plugins/pgpinline/claws.def
@@ -14,6 +14,8 @@ debug_srcname
 file_read_stream_to_str_no_recode
 get_mime_tmp_dir
 my_tmpfile
+privacy_free_sig_check_task_result
+privacy_free_signature_data
 privacy_register_system
 privacy_reset_error
 privacy_set_error
diff --git a/src/plugins/pgpinline/pgpinline.c b/src/plugins/pgpinline/pgpinline.c
index 9b81fdcc9..37321be9e 100644
--- a/src/plugins/pgpinline/pgpinline.c
+++ b/src/plugins/pgpinline/pgpinline.c
@@ -1,6 +1,6 @@
 /*
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2016 Colin Leroy and the Claws Mail team
+ * Copyright (C) 1999-2021 Colin Leroy and the Claws Mail team
  *
  * 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
@@ -52,10 +52,14 @@ struct _PrivacyDataPGP
 	
 	gboolean	done_sigtest;
 	gboolean	is_signed;
-	gpgme_verify_result_t	sigstatus;
-	gpgme_ctx_t 	ctx;
 };
 
+typedef struct _PGPInlineTaskData
+{
+	gchar *rawtext;
+	gchar *charset;
+} PGPInlineTaskData;
+
 static PrivacySystem pgpinline_system;
 
 static gint pgpinline_check_signature(MimeInfo *mimeinfo);
@@ -63,26 +67,15 @@ static gint pgpinline_check_signature(MimeInfo *mimeinfo);
 static PrivacyDataPGP *pgpinline_new_privacydata()
 {
 	PrivacyDataPGP *data;
-	gpgme_error_t err;
 
 	data = g_new0(PrivacyDataPGP, 1);
 	data->data.system = &pgpinline_system;
-	data->done_sigtest = FALSE;
-	data->is_signed = FALSE;
-	data->sigstatus = NULL;
-	if ((err = gpgme_new(&data->ctx)) != GPG_ERR_NO_ERROR) {
-		debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err));
-        g_free(data);
-		return NULL;
-	}
 	
 	return data;
 }
 
-static void pgpinline_free_privacydata(PrivacyData *_data)
+static void pgpinline_free_privacydata(PrivacyData *data)
 {
-	PrivacyDataPGP *data = (PrivacyDataPGP *) _data;
-	gpgme_release(data->ctx);
 	g_free(data);
 }
 
@@ -144,102 +137,178 @@ static gboolean pgpinline_is_signed(MimeInfo *mimeinfo)
 	return TRUE;
 }
 
-static gint pgpinline_check_signature(MimeInfo *mimeinfo)
+static void pgpinline_free_task_data(gpointer data)
 {
-	PrivacyDataPGP *data = NULL;
-	gchar *textdata = NULL, *tmp = NULL;
-	gpgme_data_t plain = NULL, cipher = NULL;
-	gpgme_error_t err;
+	PGPInlineTaskData *task_data = (PGPInlineTaskData *)data;
 
-	cm_return_val_if_fail(mimeinfo != NULL, 0);
+	g_free(task_data->rawtext);
+	g_free(task_data->charset);
+	g_free(task_data);
+}
 
-	if (procmime_mimeinfo_parent(mimeinfo) == NULL) {
-		privacy_set_error(_("Incorrect part"));
-		return 0; /* not parent */
+static gchar *get_sig_data(gchar *rawtext, gchar *charset)
+{
+	gchar *conv;
+
+	conv = conv_codeset_strdup(rawtext, CS_UTF_8, charset);
+	if (!conv)
+		conv = conv_codeset_strdup(rawtext, CS_UTF_8, conv_get_locale_charset_str_no_utf8());
+
+	if (!conv) {
+		g_warning("can't convert charset to anything sane");
+		conv = conv_codeset_strdup(rawtext, CS_UTF_8, CS_US_ASCII);
 	}
-	if (mimeinfo->type != MIMETYPE_TEXT) {
-		privacy_set_error(_("Not a text part"));
-		debug_print("type %d\n", mimeinfo->type);
-		return 0;
+
+	return conv;
+}
+
+static void pgpinline_check_sig_task(GTask *task,
+	gpointer source_object,
+	gpointer g_task_data,
+	GCancellable *cancellable)
+{
+	PGPInlineTaskData *task_data = (PGPInlineTaskData *)g_task_data;
+	GQuark domain;
+	gpgme_ctx_t ctx;
+	gpgme_error_t err;
+	gpgme_data_t sigdata = NULL;
+	gpgme_data_t plain = NULL;
+	gpgme_verify_result_t gpgme_res;
+	gboolean return_err = TRUE;
+	gboolean cancelled = FALSE;
+	SigCheckTaskResult *task_result = NULL;
+	gchar *textstr;
+	char err_str[GPGERR_BUFSIZE];
+
+	domain = g_quark_from_static_string("claws_pgpinline");
+
+	err = gpgme_new(&ctx);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("couldn't initialize GPG context: %s", err_str);
+		goto out;
 	}
-	cm_return_val_if_fail(mimeinfo->privacy != NULL, 0);
-	data = (PrivacyDataPGP *) mimeinfo->privacy;
 
-	textdata = procmime_get_part_as_string(mimeinfo, TRUE);
+	gpgme_set_textmode(ctx, 1);
+	gpgme_set_armor(ctx, 1);
 
-	if (!textdata) {
-		g_free(textdata);
-		privacy_set_error(_("Couldn't get text data."));
-		return 0;
+	textstr = get_sig_data(task_data->rawtext, task_data->charset);
+	if (!textstr) {
+		err = GPG_ERR_GENERAL;
+		g_snprintf(err_str, GPGERR_BUFSIZE, "Couldn't convert text data to any sane charset.");
+		goto out_ctx;
 	}
 
-	/* gtk2: convert back from utf8 */
-	tmp = conv_codeset_strdup(textdata, CS_UTF_8,
-			procmime_mimeinfo_get_parameter(mimeinfo, "charset"));
-	if (!tmp) {
-		tmp = conv_codeset_strdup(textdata, CS_UTF_8,
-			conv_get_locale_charset_str_no_utf8());
+	err = gpgme_data_new_from_mem(&sigdata, textstr, strlen(textstr), 1);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_data_new_from_mem failed: %s", err_str);
+		goto out_textstr;
 	}
-	if (!tmp) {
-		g_warning("can't convert charset to anything sane");
-		tmp = conv_codeset_strdup(textdata, CS_UTF_8, CS_US_ASCII);
+
+	err = gpgme_data_new(&plain);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_data_new failed: %s", err_str);
+		goto out_sigdata;
 	}
-	g_free(textdata);
 
-	if (!tmp) {
-		privacy_set_error(_("Couldn't convert text data to any sane charset."));
-		return 0;
+	if (g_task_return_error_if_cancelled(task)) {
+		debug_print("task was cancelled, aborting task:%p\n", task);
+		cancelled = TRUE;
+		goto out_sigdata;
 	}
-	textdata = g_strdup(tmp);
-	g_free(tmp);
-	
-	if ((err = gpgme_new(&data->ctx)) != GPG_ERR_NO_ERROR) {
-		debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err));
-		privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
-		g_free(textdata);
-		return 0;
+
+	err = gpgme_op_verify(ctx, sigdata, NULL, plain);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_op_verify failed: %s\n", err_str);
+		goto out_plain;
 	}
-	gpgme_set_textmode(data->ctx, 1);
-	gpgme_set_armor(data->ctx, 1);
-	
-	gpgme_data_new_from_mem(&plain, textdata, (size_t)strlen(textdata), 1);
-	gpgme_data_new(&cipher);
 
-	data->sigstatus = sgpgme_verify_signature(data->ctx, plain, NULL, cipher);
-	
+	if (g_task_return_error_if_cancelled(task)) {
+		debug_print("task was cancelled, aborting task:%p\n", task);
+		cancelled = TRUE;
+		goto out_sigdata;
+	}
+
+	gpgme_res = gpgme_op_verify_result(ctx);
+	if (gpgme_res && gpgme_res->signatures == NULL) {
+		err = GPG_ERR_SYSTEM_ERROR;
+		g_warning("no signature found");
+		g_snprintf(err_str, GPGERR_BUFSIZE, "No signature found");
+		goto out_plain;
+	}
+
+	task_result = g_new0(SigCheckTaskResult, 1);
+	task_result->sig_data = g_new0(SignatureData, 1);
+
+	task_result->sig_data->status = sgpgme_sigstat_gpgme_to_privacy(ctx, gpgme_res);
+	task_result->sig_data->info_short = sgpgme_sigstat_info_short(ctx, gpgme_res);
+	task_result->sig_data->info_full = sgpgme_sigstat_info_full(ctx, gpgme_res);
+
+	return_err = FALSE;
+
+out_plain:
 	gpgme_data_release(plain);
-	gpgme_data_release(cipher);
-	
-	g_free(textdata);
-	
-	return 0;
+out_sigdata:
+	gpgme_data_release(sigdata);
+out_textstr:
+	g_free(textstr);
+out_ctx:
+	gpgme_release(ctx);
+out:
+	if (cancelled)
+		return;
+
+	if (return_err)
+		g_task_return_new_error(task, domain, err, err_str);
+	else
+		g_task_return_pointer(task, task_result, privacy_free_sig_check_task_result);
 }
 
-static SignatureStatus pgpinline_get_sig_status(MimeInfo *mimeinfo)
+static gint pgpinline_check_sig_async(MimeInfo *mimeinfo,
+	GCancellable *cancellable,
+	GAsyncReadyCallback callback,
+	gpointer user_data)
 {
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
+	GTask *task;
+	PGPInlineTaskData *task_data;
+	gchar *rawtext;
+	const gchar *charset;
 
-	cm_return_val_if_fail(data != NULL, SIGNATURE_INVALID);
+	if (procmime_mimeinfo_parent(mimeinfo) == NULL) {
+		g_warning("Checking signature on incorrect part");
+		return -1;
+	}
 
-	return sgpgme_sigstat_gpgme_to_privacy(data->ctx, data->sigstatus);
-}
+	if (mimeinfo->type != MIMETYPE_TEXT) {
+		g_warning("Checking signature on a non-text part");
+		return -1;
+	}
 
-static gchar *pgpinline_get_sig_info_short(MimeInfo *mimeinfo)
-{
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
+	rawtext = procmime_get_part_as_string(mimeinfo, TRUE);
+	if (rawtext == NULL) {
+		g_warning("Failed to get part as string");
+		return -1;
+	}
 
-	cm_return_val_if_fail(data != NULL, g_strdup("Error"));
+	charset = procmime_mimeinfo_get_parameter(mimeinfo, "charset");
 
-	return sgpgme_sigstat_info_short(data->ctx, data->sigstatus);
-}
+	task_data = g_new0(PGPInlineTaskData, 1);
+	task_data->rawtext = rawtext;
+	task_data->charset = g_strdup(charset);
 
-static gchar *pgpinline_get_sig_info_full(MimeInfo *mimeinfo)
-{
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
-	
-	cm_return_val_if_fail(data != NULL, g_strdup("Error"));
+	task = g_task_new(NULL, cancellable, callback, user_data);
+	mimeinfo->last_sig_check_task = task;
 
-	return sgpgme_sigstat_info_full(data->ctx, data->sigstatus);
+	g_task_set_task_data(task, task_data, pgpinline_free_task_data);
+	debug_print("creating check sig async task:%p task_data:%p\n", task, task_data);
+	g_task_set_return_on_cancel(task, TRUE);
+	g_task_run_in_thread(task, pgpinline_check_sig_task);
+	g_object_unref(task);
+
+	return 0;
 }
 
 static gboolean pgpinline_is_encrypted(MimeInfo *mimeinfo)
@@ -301,6 +370,7 @@ static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
 	const gchar *begin_indicator = "-----BEGIN PGP MESSAGE-----";
 	const gchar *end_indicator = "-----END PGP MESSAGE-----";
 	gchar *pos;
+	SignatureData *sig_data = NULL;
 	
 	if (gpgme_new(&ctx) != GPG_ERR_NO_ERROR)
 		return NULL;
@@ -329,27 +399,35 @@ static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
 	gpgme_data_new_from_mem(&cipher, textdata, (size_t)strlen(textdata), 1);
 
 	plain = sgpgme_decrypt_verify(cipher, &sigstat, ctx);
-	if (sigstat && !sigstat->signatures)
-		sigstat = NULL;
 
+	if (sigstat != NULL && sigstat->signatures != NULL) {
+		sig_data = g_new0(SignatureData, 1);
+		sig_data->status = sgpgme_sigstat_gpgme_to_privacy(ctx, sigstat);
+		sig_data->info_short = sgpgme_sigstat_info_short(ctx, sigstat);
+		sig_data->info_full = sgpgme_sigstat_info_full(ctx, sigstat);
+	}
+
+	gpgme_release(ctx);
 	gpgme_data_release(cipher);
 	
 	if (plain == NULL) {
-		gpgme_release(ctx);
+		g_free(textdata);
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 
-    	fname = g_strdup_printf("%s%cplaintext.%08x",
+	fname = g_strdup_printf("%s%cplaintext.%08x",
 		get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
 
-    	if ((dstfp = claws_fopen(fname, "wb")) == NULL) {
-        	FILE_OP_ERROR(fname, "claws_fopen");
+	if ((dstfp = claws_fopen(fname, "wb")) == NULL) {
+		FILE_OP_ERROR(fname, "claws_fopen");
 		privacy_set_error(_("Couldn't open decrypted file %s"), fname);
-        	g_free(fname);
-        	gpgme_data_release(plain);
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
+		g_free(textdata);
+		g_free(fname);
+		gpgme_data_release(plain);
 		return NULL;
-    	}
+	}
 
 	src_codeset = procmime_mimeinfo_get_parameter(mimeinfo, "charset");
 	if (src_codeset == NULL)
@@ -360,7 +438,7 @@ static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
 			"Content-Transfer-Encoding: 8bit\r\n"
 			"\r\n",
 			src_codeset) < 0) {
-        	FILE_OP_ERROR(fname, "fprintf");
+		FILE_OP_ERROR(fname, "fprintf");
 		privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
 		goto FILE_ERROR;
 	}
@@ -368,13 +446,13 @@ static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
 	/* Store any part before encrypted text */
 	pos = pgp_locate_armor_header(textdata, begin_indicator);
 	if (pos != NULL && (pos - textdata) > 0) {
-	    if (claws_fwrite(textdata, 1, pos - textdata, dstfp) < pos - textdata) {
-        	FILE_OP_ERROR(fname, "claws_fwrite");
-		privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
-		goto FILE_ERROR;
-	    }
+		if (claws_fwrite(textdata, 1, pos - textdata, dstfp) < pos - textdata) {
+			FILE_OP_ERROR(fname, "claws_fwrite");
+			privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
+			goto FILE_ERROR;
+		}
 	}
-	
+
 	if (claws_fwrite(_("\n--- Start of PGP/Inline encrypted data ---\n"), 1,
 		strlen(_("\n--- Start of PGP/Inline encrypted data ---\n")), 
 		dstfp) < strlen(_("\n--- Start of PGP/Inline encrypted data ---\n"))) {
@@ -412,12 +490,14 @@ static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
 	    }
 	}
 
+	g_free(textdata);
+
 	if (claws_safe_fclose(dstfp) == EOF) {
         	FILE_OP_ERROR(fname, "claws_fclose");
 		privacy_set_error(_("Couldn't close decrypted file %s"), fname);
         	g_free(fname);
         	gpgme_data_release(plain);
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 	
@@ -425,16 +505,16 @@ static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
 	g_free(fname);
 	
 	if (parseinfo == NULL) {
-		gpgme_release(ctx);
 		privacy_set_error(_("Couldn't scan decrypted file."));
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 	decinfo = g_node_first_child(parseinfo->node) != NULL ?
 		g_node_first_child(parseinfo->node)->data : NULL;
 		
 	if (decinfo == NULL) {
-		gpgme_release(ctx);
 		privacy_set_error(_("Couldn't scan decrypted file parts."));
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 
@@ -443,7 +523,7 @@ static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
 
 	decinfo->tmp = TRUE;
 
-	if (sigstat != GPGME_SIG_STAT_NONE) {
+	if (sig_data != NULL) {
 		if (decinfo->privacy != NULL) {
 			data = (PrivacyDataPGP *) decinfo->privacy;
 		} else {
@@ -453,21 +533,18 @@ static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
 		if (data != NULL) {
 			data->done_sigtest = TRUE;
 			data->is_signed = TRUE;
-			data->sigstatus = sigstat;
-			if (data->ctx)
-				gpgme_release(data->ctx);
-			data->ctx = ctx;
+			decinfo->sig_data = sig_data;
 		}
-	} else
-		gpgme_release(ctx);
+	}
 
 	return decinfo;
 
 FILE_ERROR:
+	privacy_free_signature_data(sig_data);
+	g_free(textdata);
 	claws_fclose(dstfp);
 	g_free(fname);
 	gpgme_data_release(plain);
-	gpgme_release(ctx);
 	return NULL;
 }
 
@@ -668,6 +745,8 @@ static gboolean pgpinline_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 		if (err) {
 			debug_print("can't add key '%s'[%d] (%s)\n", fprs[i],i, gpgme_strerror(err));
 			privacy_set_error(_("Couldn't add GPG key %s, %s"), fprs[i], gpgme_strerror(err));
+			for (gint x = 0; x < i; x++)
+				gpgme_key_unref(kset[x]);
 			g_free(kset);
 			g_free(fprs);
 			return FALSE;
@@ -685,6 +764,8 @@ static gboolean pgpinline_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 		if (!msgcontent->node->children) {
 			debug_print("msgcontent->node->children NULL, bailing\n");
 			privacy_set_error(_("Malformed message"));
+			for (gint x = 0; x < i; x++)
+				gpgme_key_unref(kset[x]);
 			g_free(kset);
 			g_free(fprs);
 			return FALSE;
@@ -698,6 +779,8 @@ static gboolean pgpinline_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 	if (fp == NULL) {
 		privacy_set_error(_("Couldn't create temporary file, %s"), g_strerror(errno));
 		perror("my_tmpfile");
+		for (gint x = 0; x < i; x++)
+			gpgme_key_unref(kset[x]);
 		g_free(kset);
 		g_free(fprs);
 		return FALSE;
@@ -716,6 +799,8 @@ static gboolean pgpinline_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 	if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
 		debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err));
 		privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
+		for (gint x = 0; x < i; x++)
+			gpgme_key_unref(kset[x]);
 		g_free(kset);
 		g_free(fprs);
 		return FALSE;
@@ -725,6 +810,8 @@ static gboolean pgpinline_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 	err = gpgme_op_encrypt(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, gpgtext, gpgenc);
 
 	enccontent = sgpgme_data_release_and_get_mem(gpgenc, &len);
+	for (gint x = 0; x < i; x++)
+		gpgme_key_unref(kset[x]);
 	g_free(kset);
 
 	if (enccontent == NULL || len <= 0) {
@@ -769,10 +856,7 @@ static PrivacySystem pgpinline_system = {
 	pgpinline_free_privacydata,	/* free_privacydata */
 
 	pgpinline_is_signed,		/* is_signed(MimeInfo *) */
-	pgpinline_check_signature,	/* check_signature(MimeInfo *) */
-	pgpinline_get_sig_status,	/* get_sig_status(MimeInfo *) */
-	pgpinline_get_sig_info_short,	/* get_sig_info_short(MimeInfo *) */
-	pgpinline_get_sig_info_full,	/* get_sig_info_full(MimeInfo *) */
+	pgpinline_check_sig_async,
 
 	pgpinline_is_encrypted,		/* is_encrypted(MimeInfo *) */
 	pgpinline_decrypt,		/* decrypt(MimeInfo *) */
diff --git a/src/plugins/pgpinline/plugin.c b/src/plugins/pgpinline/plugin.c
index c467f86d3..bf25bf29f 100644
--- a/src/plugins/pgpinline/plugin.c
+++ b/src/plugins/pgpinline/plugin.c
@@ -35,7 +35,7 @@
 
 gint plugin_init(gchar **error)
 {
-	if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
+	if (!check_plugin_version(MAKE_NUMERIC_VERSION(4,0,0,441),
 				VERSION_NUMERIC, PLUGIN_NAME, error))
   		return -1;
 
diff --git a/src/plugins/pgpmime/claws.def b/src/plugins/pgpmime/claws.def
index 7f8f7001b..8dfafcefb 100644
--- a/src/plugins/pgpmime/claws.def
+++ b/src/plugins/pgpmime/claws.def
@@ -1,17 +1,18 @@
 LIBRARY CLAWS-MAIL.EXE
 EXPORTS
 canonicalize_str
-claws_fopen
-claws_fdopen
+check_plugin_version
 claws_fclose
+claws_fdopen
+claws_fopen
 claws_safe_fclose
-check_plugin_version
 debug_print_real
 debug_srcname
 file_read_stream_to_str
 generate_mime_boundary
 get_mime_tmp_dir
 my_tmpfile
+privacy_free_signature_data
 privacy_register_system
 privacy_reset_error
 privacy_set_error
diff --git a/src/plugins/pgpmime/mypgpcore.def b/src/plugins/pgpmime/mypgpcore.def
index ef8158072..a0e70c369 100644
--- a/src/plugins/pgpmime/mypgpcore.def
+++ b/src/plugins/pgpmime/mypgpcore.def
@@ -1,7 +1,11 @@
 LIBRARY PGPCORE.DLL
 EXPORTS
+cm_check_detached_sig_async
+cm_gpgme_data_rewind
 gpgmegtk_passphrase_cb
+pgp_locate_armor_header
 prefs_gpg_add_skip_encryption_warning
+prefs_gpg_auto_check_signatures
 prefs_gpg_enable_agent
 prefs_gpg_get_config
 prefs_gpg_remove_skip_encryption_warning
@@ -15,6 +19,3 @@ sgpgme_sigstat_gpgme_to_privacy
 sgpgme_sigstat_info_full
 sgpgme_sigstat_info_short
 sgpgme_verify_signature
-cm_gpgme_data_rewind
-pgp_locate_armor_header
-prefs_gpg_auto_check_signatures
diff --git a/src/plugins/pgpmime/pgpmime.c b/src/plugins/pgpmime/pgpmime.c
index 618bb6fa2..5f0ca0b96 100644
--- a/src/plugins/pgpmime/pgpmime.c
+++ b/src/plugins/pgpmime/pgpmime.c
@@ -1,6 +1,6 @@
 /*
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2016 the Claws Mail team
+ * Copyright (C) 1999-2021 the Claws Mail team
  *
  * 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
@@ -52,37 +52,22 @@ struct _PrivacyDataPGP
 	
 	gboolean	done_sigtest;
 	gboolean	is_signed;
-	gpgme_verify_result_t	sigstatus;
-	gpgme_ctx_t 	ctx;
 };
 
 static PrivacySystem pgpmime_system;
 
-static gint pgpmime_check_signature(MimeInfo *mimeinfo);
-
 static PrivacyDataPGP *pgpmime_new_privacydata()
 {
 	PrivacyDataPGP *data;
-	gpgme_error_t err;
 
 	data = g_new0(PrivacyDataPGP, 1);
 	data->data.system = &pgpmime_system;
-	data->done_sigtest = FALSE;
-	data->is_signed = FALSE;
-	data->sigstatus = NULL;
-	if ((err = gpgme_new(&data->ctx)) != GPG_ERR_NO_ERROR) {
-		g_warning("couldn't initialize GPG context: %s", gpgme_strerror(err));
-        g_free(data);
-		return NULL;
-	}
 	
 	return data;
 }
 
-static void pgpmime_free_privacydata(PrivacyData *_data)
+static void pgpmime_free_privacydata(PrivacyData *data)
 {
-	PrivacyDataPGP *data = (PrivacyDataPGP *) _data;
-	gpgme_release(data->ctx);
 	g_free(data);
 }
 
@@ -139,7 +124,6 @@ static gboolean pgpmime_is_signed(MimeInfo *mimeinfo)
 
 static gchar *get_canonical_content(FILE *fp, const gchar *boundary)
 {
-	gchar *ret;
 	GString *textbuffer;
 	guint boundary_len;
 	gchar buf[BUFFSIZE];
@@ -162,106 +146,20 @@ static gchar *get_canonical_content(FILE *fp, const gchar *boundary)
 	}
 	g_string_truncate(textbuffer, textbuffer->len - 2);
 		
-	ret = textbuffer->str;
-	g_string_free(textbuffer, FALSE);
-
-	return ret;
+	return g_string_free(textbuffer, FALSE);
 }
 
-static gint pgpmime_check_signature(MimeInfo *mimeinfo)
+static gint pgpmime_check_sig_async(MimeInfo *mimeinfo,
+	GCancellable *cancellable,
+	GAsyncReadyCallback callback,
+	gpointer user_data)
 {
-	PrivacyDataPGP *data;
-	MimeInfo *parent, *signature;
-	FILE *fp;
-	gchar *boundary;
-	gchar *textstr;
-	gpgme_data_t sigdata = NULL, textdata = NULL;
-	gpgme_error_t err;
-	cm_return_val_if_fail(mimeinfo != NULL, -1);
-	cm_return_val_if_fail(mimeinfo->privacy != NULL, -1);
-	data = (PrivacyDataPGP *) mimeinfo->privacy;
-	if ((err = gpgme_new(&data->ctx)) != GPG_ERR_NO_ERROR) {
-		debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err));
-		privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
-		return 0;
-	}
-
-	
-	debug_print("Checking PGP/MIME signature\n");
-
-	err = gpgme_set_protocol(data->ctx, GPGME_PROTOCOL_OpenPGP);
-
-	if (err) {
-		debug_print ("gpgme_set_protocol failed: %s\n",
-                   gpgme_strerror (err));
-	}
-	parent = procmime_mimeinfo_parent(mimeinfo);
-
-	fp = claws_fopen(parent->data.filename, "rb");
-	cm_return_val_if_fail(fp != NULL, SIGNATURE_INVALID);
-	
-	boundary = g_hash_table_lookup(parent->typeparameters, "boundary");
-	if (!boundary) {
-		privacy_set_error(_("Signature boundary not found."));
-		claws_fclose(fp);
-		return 0;
-	}
-	textstr = get_canonical_content(fp, boundary);
-
-	err = gpgme_data_new_from_mem(&textdata, textstr, (size_t)strlen(textstr), 0);
-	if (err) {
-		debug_print ("gpgme_data_new_from_mem failed: %s\n",
-                   gpgme_strerror (err));
-	}
-	signature = (MimeInfo *) mimeinfo->node->next->data;
-	sigdata = sgpgme_data_from_mimeinfo(signature);
-
-	err = 0;
-	if (signature->encoding_type == ENC_BASE64) {
-		err = gpgme_data_set_encoding (sigdata, GPGME_DATA_ENCODING_BASE64);
-	}
-	
-	if (err) {
-		debug_print ("gpgme_data_set_encoding failed: %s\n",
-			gpgme_strerror (err));
-	}
-
-	data->sigstatus =
-		sgpgme_verify_signature	(data->ctx, sigdata, textdata, NULL);
-
-	gpgme_data_release(sigdata);
-	gpgme_data_release(textdata);
-	g_free(textstr);
-	claws_fclose(fp);
-	
-	return 0;
-}
-
-static SignatureStatus pgpmime_get_sig_status(MimeInfo *mimeinfo)
-{
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
-	
-	cm_return_val_if_fail(data != NULL, SIGNATURE_INVALID);
-
-	return sgpgme_sigstat_gpgme_to_privacy(data->ctx, data->sigstatus);
-}
-
-static gchar *pgpmime_get_sig_info_short(MimeInfo *mimeinfo)
-{
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
-	
-	cm_return_val_if_fail(data != NULL, g_strdup("Error"));
-
-	return sgpgme_sigstat_info_short(data->ctx, data->sigstatus);
-}
-
-static gchar *pgpmime_get_sig_info_full(MimeInfo *mimeinfo)
-{
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
-
-	cm_return_val_if_fail(data != NULL, g_strdup("Error"));
-
-	return sgpgme_sigstat_info_full(data->ctx, data->sigstatus);
+	return cm_check_detached_sig_async(mimeinfo,
+		cancellable,
+		callback,
+		user_data,
+		GPGME_PROTOCOL_OpenPGP,
+		get_canonical_content);
 }
 
 static gboolean pgpmime_is_encrypted(MimeInfo *mimeinfo)
@@ -325,6 +223,7 @@ static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo)
 	gchar *chars;
 	size_t len;
 	gpgme_error_t err;
+	SignatureData *sig_data = NULL;
 
 	if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
 		debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err));
@@ -339,33 +238,41 @@ static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo)
 	cipher = sgpgme_data_from_mimeinfo(encinfo);
 	plain = sgpgme_decrypt_verify(cipher, &sigstat, ctx);
 
+	if (sigstat != NULL && sigstat->signatures != NULL) {
+		sig_data = g_new0(SignatureData, 1);
+		sig_data->status = sgpgme_sigstat_gpgme_to_privacy(ctx, sigstat);
+		sig_data->info_short = sgpgme_sigstat_info_short(ctx, sigstat);
+		sig_data->info_full = sgpgme_sigstat_info_full(ctx, sigstat);
+	}
+
+	gpgme_release(ctx);
 	gpgme_data_release(cipher);
 	if (plain == NULL) {
 		debug_print("plain is null!\n");
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 
-    	fname = g_strdup_printf("%s%cplaintext.%08x",
+	fname = g_strdup_printf("%s%cplaintext.%08x",
 		get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
 
-    	if ((dstfp = claws_fopen(fname, "wb")) == NULL) {
-        	FILE_OP_ERROR(fname, "claws_fopen");
+	if ((dstfp = claws_fopen(fname, "wb")) == NULL) {
+		FILE_OP_ERROR(fname, "claws_fopen");
 		privacy_set_error(_("Couldn't open decrypted file %s"), fname);
-        	g_free(fname);
-        	gpgme_data_release(plain);
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
+		g_free(fname);
+		gpgme_data_release(plain);
 		debug_print("can't open!\n");
 		return NULL;
-    	}
+	}
 
 	if (fprintf(dstfp, "MIME-Version: 1.0\n") < 0) {
-        	FILE_OP_ERROR(fname, "fprintf");
+		FILE_OP_ERROR(fname, "fprintf");
 		claws_fclose(dstfp);
 		privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
-        	g_free(fname);
-        	gpgme_data_release(plain);
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
+		g_free(fname);
+		gpgme_data_release(plain);
 		debug_print("can't open!\n");
 		return NULL;
 	}
@@ -373,13 +280,13 @@ static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo)
 	chars = sgpgme_data_release_and_get_mem(plain, &len);
 	if (len > 0) {
 		if (claws_fwrite(chars, 1, len, dstfp) < len) {
-        		FILE_OP_ERROR(fname, "claws_fwrite");
+			FILE_OP_ERROR(fname, "claws_fwrite");
 			g_free(chars);
 			claws_fclose(dstfp);
 			privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
-        		g_free(fname);
-        		gpgme_data_release(plain);
-			gpgme_release(ctx);
+			privacy_free_signature_data(sig_data);
+			g_free(fname);
+			gpgme_data_release(plain);
 			debug_print("can't open!\n");
 			return NULL;
 		}
@@ -387,11 +294,11 @@ static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo)
 	g_free(chars);
 
 	if (claws_safe_fclose(dstfp) == EOF) {
-        	FILE_OP_ERROR(fname, "claws_fclose");
+		FILE_OP_ERROR(fname, "claws_fclose");
 		privacy_set_error(_("Couldn't close decrypted file %s"), fname);
-        	g_free(fname);
-        	gpgme_data_release(plain);
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
+		g_free(fname);
+		gpgme_data_release(plain);
 		debug_print("can't open!\n");
 		return NULL;
 	}
@@ -399,15 +306,15 @@ static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo)
 	parseinfo = procmime_scan_file(fname);
 	g_free(fname);
 	if (parseinfo == NULL) {
-		gpgme_release(ctx);
 		privacy_set_error(_("Couldn't parse decrypted file."));
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 	decinfo = g_node_first_child(parseinfo->node) != NULL ?
 		g_node_first_child(parseinfo->node)->data : NULL;
 	if (decinfo == NULL) {
 		privacy_set_error(_("Couldn't parse decrypted file parts."));
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 
@@ -416,23 +323,20 @@ static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo)
 
 	decinfo->tmp = TRUE;
 
-	if (sigstat != NULL && sigstat->signatures != NULL) {
+	if (sig_data != NULL) {
 		if (decinfo->privacy != NULL) {
 			data = (PrivacyDataPGP *) decinfo->privacy;
 		} else {
 			data = pgpmime_new_privacydata();
-			decinfo->privacy = (PrivacyData *) data;	
+			decinfo->privacy = (PrivacyData *) data;
 		}
+
 		if (data != NULL) {
 			data->done_sigtest = TRUE;
 			data->is_signed = TRUE;
-			data->sigstatus = sigstat;
-			if (data->ctx)
-				gpgme_release(data->ctx);
-			data->ctx = ctx;
+			decinfo->sig_data = sig_data;
 		}
-	} else
-		gpgme_release(ctx);
+	}
 
 	return decinfo;
 }
@@ -588,6 +492,7 @@ gboolean pgpmime_sign(MimeInfo *mimeinfo, PrefsAccount *account, const gchar *fr
 		privacy_set_error(_("Data signing failed, no contents."));
 		g_free(micalg);
 		g_free(sigcontent);
+		gpgme_release(ctx);
 		return FALSE;
 	}
 
@@ -651,8 +556,7 @@ gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 		i++;
 	}
 	
-	kset = g_malloc(sizeof(gpgme_key_t)*(i+1));
-	memset(kset, 0, sizeof(gpgme_key_t)*(i+1));
+	kset = g_malloc0(sizeof(gpgme_key_t)*(i+1));
 	if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
 		debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err));
 		privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
@@ -667,8 +571,11 @@ gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 		if (err) {
 			debug_print("can't add key '%s'[%d] (%s)\n", fprs[i],i, gpgme_strerror(err));
 			privacy_set_error(_("Couldn't add GPG key %s, %s"), fprs[i], gpgme_strerror(err));
+			for (gint x = 0; x < i; x++)
+				gpgme_key_unref(kset[x]);
 			g_free(kset);
 			g_free(fprs);
+			gpgme_release(ctx);
 			return FALSE;
 		}
 		debug_print("found %s at %d\n", fprs[i], i);
@@ -698,9 +605,12 @@ gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 	if (fp == NULL) {
 		perror("my_tmpfile");
 		privacy_set_error(_("Couldn't create temporary file, %s"), g_strerror(errno));
+		for (gint x = 0; x < i; x++)
+			gpgme_key_unref(kset[x]);
 		g_free(kset);
 		g_free(boundary);
 		g_free(fprs);
+		gpgme_release(ctx);
 		return FALSE;
 	}
 	procmime_write_mimeinfo(encmultipart, fp);
@@ -723,6 +633,8 @@ gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 	enccontent = sgpgme_data_release_and_get_mem(gpgenc, &len);
 	gpgme_data_release(gpgtext);
 	g_free(textstr);
+	for (gint x = 0; x < i; x++)
+		gpgme_key_unref(kset[x]);
 	g_free(kset);
 
 	if (enccontent == NULL || len <= 0) {
@@ -772,10 +684,7 @@ static PrivacySystem pgpmime_system = {
 	pgpmime_free_privacydata,	/* free_privacydata */
 
 	pgpmime_is_signed,		/* is_signed(MimeInfo *) */
-	pgpmime_check_signature,	/* check_signature(MimeInfo *) */
-	pgpmime_get_sig_status,		/* get_sig_status(MimeInfo *) */
-	pgpmime_get_sig_info_short,	/* get_sig_info_short(MimeInfo *) */
-	pgpmime_get_sig_info_full,	/* get_sig_info_full(MimeInfo *) */
+	pgpmime_check_sig_async,
 
 	pgpmime_is_encrypted,		/* is_encrypted(MimeInfo *) */
 	pgpmime_decrypt,		/* decrypt(MimeInfo *) */
diff --git a/src/plugins/pgpmime/plugin.c b/src/plugins/pgpmime/plugin.c
index f2b6f0276..3fb902ea1 100644
--- a/src/plugins/pgpmime/plugin.c
+++ b/src/plugins/pgpmime/plugin.c
@@ -1,6 +1,6 @@
 /*
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2012 the Claws Mail team
+ * Copyright (C) 1999-2021 the Claws Mail team
  *
  * 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
@@ -35,7 +35,7 @@
 
 gint plugin_init(gchar **error)
 {
-	if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
+	if (!check_plugin_version(MAKE_NUMERIC_VERSION(4,0,0,441),
 				VERSION_NUMERIC, PLUGIN_NAME, error))
 		return -1;
 
diff --git a/src/plugins/smime/claws.def b/src/plugins/smime/claws.def
index 351805937..77e8fd836 100644
--- a/src/plugins/smime/claws.def
+++ b/src/plugins/smime/claws.def
@@ -3,11 +3,11 @@ EXPORTS
 alertpanel_error
 canonicalize_file_replace
 canonicalize_str
-claws_fopen
-claws_fdopen
+check_plugin_version
 claws_fclose
+claws_fdopen
+claws_fopen
 claws_safe_fclose
-check_plugin_version
 claws_unlink
 debug_print_real
 debug_srcname
@@ -18,12 +18,15 @@ generate_mime_boundary
 get_mime_tmp_dir
 get_tmp_file
 my_tmpfile
+privacy_free_sig_check_task_result
+privacy_free_signature_data
 privacy_register_system
 privacy_set_error
 privacy_unregister_system
 procmime_decode_content
 procmime_encode_content
 procmime_get_part
+procmime_get_part_as_string
 procmime_mimeinfo_free_all
 procmime_mimeinfo_get_parameter
 procmime_mimeinfo_new
diff --git a/src/plugins/smime/mypgpcore.def b/src/plugins/smime/mypgpcore.def
index 44dd8d22e..f2a911c61 100644
--- a/src/plugins/smime/mypgpcore.def
+++ b/src/plugins/smime/mypgpcore.def
@@ -1,7 +1,10 @@
 LIBRARY PGPCORE.DLL
 EXPORTS
+cm_check_detached_sig_async
+cm_gpgme_data_rewind
 gpgmegtk_passphrase_cb
 prefs_gpg_add_skip_encryption_warning
+prefs_gpg_auto_check_signatures
 prefs_gpg_enable_agent
 prefs_gpg_get_config
 prefs_gpg_remove_skip_encryption_warning
@@ -15,5 +18,3 @@ sgpgme_sigstat_gpgme_to_privacy
 sgpgme_sigstat_info_full
 sgpgme_sigstat_info_short
 sgpgme_verify_signature
-cm_gpgme_data_rewind
-prefs_gpg_auto_check_signatures
diff --git a/src/plugins/smime/plugin.c b/src/plugins/smime/plugin.c
index bbd25975f..d29d1b319 100644
--- a/src/plugins/smime/plugin.c
+++ b/src/plugins/smime/plugin.c
@@ -1,6 +1,6 @@
 /*
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2012 Colin Leroy <colin at colino.net> and 
+ * Copyright (C) 1999-2021 Colin Leroy <colin at colino.net> and 
  * the Claws Mail team
  *
  * This program is free software; you can redistribute it and/or modify
@@ -32,10 +32,12 @@
 #include "common/claws.h"
 #include "smime.h"
 
+#define PLUGIN_NAME (_("S/MIME"))
+
 gint plugin_init(gchar **error)
 {
-	if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
-				VERSION_NUMERIC, _("S/MIME"), error))
+	if (!check_plugin_version(MAKE_NUMERIC_VERSION(4,0,0,441),
+				VERSION_NUMERIC, PLUGIN_NAME, error))
 		return -1;
 
 	smime_init();
@@ -51,7 +53,7 @@ gboolean plugin_done(void)
 
 const gchar *plugin_name(void)
 {
-	return _("S/MIME");
+	return PLUGIN_NAME;
 }
 
 const gchar *plugin_desc(void)
diff --git a/src/plugins/smime/smime.c b/src/plugins/smime/smime.c
index 324fc67df..f135714c8 100644
--- a/src/plugins/smime/smime.c
+++ b/src/plugins/smime/smime.c
@@ -1,6 +1,6 @@
 /* 
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2016 Colin Leroy and the Claws Mail team
+ * Copyright (C) 1999-2021 Colin Leroy and the Claws Mail team
  *
  * 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
@@ -52,41 +52,34 @@ struct _PrivacyDataPGP
 	
 	gboolean	done_sigtest;
 	gboolean	is_signed;
-	gpgme_verify_result_t	sigstatus;
-	gpgme_ctx_t 	ctx;
+	gboolean	inserted_mimeinfo;
 };
 
-static PrivacySystem smime_system;
+typedef struct _PKCS7MimeTaskData {
+	gchar *textstr;
+	EncodingType encoding;
+	gboolean create_mimeinfo;
+} PKCS7MimeTaskData;
 
-static gint smime_check_signature(MimeInfo *mimeinfo);
+static PrivacySystem smime_system;
 
 static PrivacyDataPGP *smime_new_privacydata()
 {
 	PrivacyDataPGP *data;
-	gpgme_ctx_t 	ctx;
-
-	if (gpgme_new(&ctx) != GPG_ERR_NO_ERROR) {
-		debug_print("gpgme_new failed\n");
-		return NULL;
-	}
 
 	data = g_new0(PrivacyDataPGP, 1);
 	data->data.system = &smime_system;
-	data->done_sigtest = FALSE;
-	data->is_signed = FALSE;
-	data->sigstatus = NULL;
-	data->ctx = ctx;
 	
 	return data;
 }
 
-static void smime_free_privacydata(PrivacyData *_data)
+static void smime_free_privacydata(PrivacyData *data)
 {
-	PrivacyDataPGP *data = (PrivacyDataPGP *) _data;
-	gpgme_release(data->ctx);
 	g_free(data);
 }
 
+static gint check_pkcs7_mime_sig(MimeInfo *, GCancellable *, GAsyncReadyCallback, gpointer);
+
 static gboolean smime_is_signed(MimeInfo *mimeinfo)
 {
 	MimeInfo *parent;
@@ -114,7 +107,7 @@ static gboolean smime_is_signed(MimeInfo *mimeinfo)
 
 			data->done_sigtest = TRUE;
 			data->is_signed = TRUE;
-			smime_check_signature(mimeinfo);
+			check_pkcs7_mime_sig(mimeinfo, NULL, NULL, NULL);
 			return TRUE;
 		}
 	}
@@ -163,7 +156,6 @@ static gboolean smime_is_signed(MimeInfo *mimeinfo)
 
 static gchar *get_canonical_content(FILE *fp, const gchar *boundary)
 {
-	gchar *ret;
 	GString *textbuffer;
 	guint boundary_len = 0;
 	gchar buf[BUFFSIZE];
@@ -188,190 +180,338 @@ static gchar *get_canonical_content(FILE *fp, const gchar *boundary)
 	}
 	g_string_truncate(textbuffer, textbuffer->len - 2);
 		
-	ret = textbuffer->str;
-	g_string_free(textbuffer, FALSE);
+	return g_string_free(textbuffer, FALSE);
+}
 
-	return ret;
+static void free_pkcs7_mime_task_data(gpointer data)
+{
+	PKCS7MimeTaskData *task_data = (PKCS7MimeTaskData *)data;
+
+	g_free(task_data->textstr);
+	g_free(task_data);
 }
 
-static gint smime_check_signature(MimeInfo *mimeinfo)
+static gboolean create_mimeinfo_for_plaintext(const GString *verified, MimeInfo **created)
 {
-	PrivacyDataPGP *data;
-	MimeInfo *parent, *signature;
-	FILE *fp;
-	gchar *boundary;
-	gchar *textstr = NULL;
-	const gchar *tmpstr;
-	gpgme_data_t sigdata = NULL, textdata = NULL;
+	gchar *tmpfile;
+	MimeInfo *newinfo = NULL;
+	MimeInfo *decinfo = NULL;
+
+	tmpfile = get_tmp_file();
+
+	str_write_to_file(verified->str, tmpfile, TRUE);
+	newinfo = procmime_scan_file(tmpfile);
+	g_free(tmpfile);
+	decinfo = g_node_first_child(newinfo->node) != NULL ?
+		g_node_first_child(newinfo->node)->data : NULL;
+
+	if (decinfo == NULL)
+		return FALSE;
+
+	g_node_unlink(decinfo->node);
+	procmime_mimeinfo_free_all(&newinfo);
+	decinfo->tmp = TRUE;
+
+	*created = decinfo;
+	return TRUE;
+}
+
+static void check_pkcs7_mime_sig_task(GTask *task,
+	gpointer source_object,
+	gpointer _task_data,
+	GCancellable *cancellable)
+{
+	PKCS7MimeTaskData *task_data = (PKCS7MimeTaskData *)_task_data;
+	GQuark domain;
+	gpgme_ctx_t ctx;
 	gpgme_error_t err;
-	EncodingType oldenc = ENC_BINARY;
+	gpgme_data_t sigdata = NULL;
+	gpgme_data_t plain;
+	gpgme_verify_result_t gpgme_res;
+	size_t len;
+	gboolean return_err = TRUE;
+	gboolean cancelled = FALSE;
+	SigCheckTaskResult *task_result = NULL;
+	MimeInfo *created = NULL;
+	GString *verified;
+	gchar *tmp;
+	char err_str[GPGERR_BUFSIZE];
 
-	cm_return_val_if_fail(mimeinfo != NULL, -1);
-	cm_return_val_if_fail(mimeinfo->privacy != NULL, -1);
+	domain = g_quark_from_static_string("claws_smime");
 
-	data = (PrivacyDataPGP *) mimeinfo->privacy;
+	err = gpgme_new(&ctx);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("couldn't initialize GPG context: %s", err_str);
+		goto out;
+	}
 
-	if (!data->ctx) {
-		if ((err = gpgme_new(&data->ctx)) != GPG_ERR_NO_ERROR) {
-			debug_print("gpgme_new failed: %s\n",
-				gpgme_strerror(err));
-			return -1;
+	err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("couldn't set GPG protocol: %s", err_str);
+		goto out_ctx;
+	}
+
+	err = gpgme_data_new_from_mem(&sigdata,
+		task_data->textstr,
+		task_data->textstr ? strlen(task_data->textstr) : 0,
+		0);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_data_new_from_mem failed: %s", err_str);
+		goto out_ctx;
+	}
+
+	if (task_data->encoding == ENC_BASE64) {
+		err = gpgme_data_set_encoding (sigdata, GPGME_DATA_ENCODING_BASE64);
+		if (err != GPG_ERR_NO_ERROR) {
+			gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+			g_warning("gpgme_data_set_encoding failed: %s\n", err_str);
+			goto out_sigdata;
 		}
 	}
 
-	debug_print("Checking S/MIME signature\n");
+	err = gpgme_data_new(&plain);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_data_new failed: %s", err_str);
+		goto out_sigdata;
+	}
 
-	err = gpgme_set_protocol(data->ctx, GPGME_PROTOCOL_CMS);
+	if (g_task_return_error_if_cancelled(task)) {
+		debug_print("task was cancelled, aborting task:%p\n", task);
+		cancelled = TRUE;
+		goto out_sigdata;
+	}
 
-	if (err) {
-		debug_print ("gpgme_set_protocol failed: %s\n",
-                   gpgme_strerror (err));
+	err = gpgme_op_verify(ctx, sigdata, NULL, plain);
+	if (err != GPG_ERR_NO_ERROR) {
+		gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
+		g_warning("gpgme_op_verify failed: %s\n", err_str);
+		goto out_plain;
 	}
-	parent = procmime_mimeinfo_parent(mimeinfo);
 
-	fp = claws_fopen(parent->data.filename, "rb");
-	cm_return_val_if_fail(fp != NULL, SIGNATURE_INVALID);
-	
-	boundary = g_hash_table_lookup(parent->typeparameters, "boundary");
-	if (!boundary) {
-		gchar *tmpfile = get_tmp_file();
-		debug_print("no boundary\n");
-		if (tmpfile) {
-			if (mimeinfo->encoding_type != ENC_BASE64) {
-				procmime_encode_content(mimeinfo, ENC_BASE64);
-			}
-			oldenc = mimeinfo->encoding_type;
-			if (mimeinfo->encoding_type == ENC_BASE64)
-				mimeinfo->encoding_type = ENC_BINARY;
-			if (procmime_get_part(tmpfile, mimeinfo) == 0) {
-				textstr = file_read_to_str(tmpfile);
-			} else {
-				textstr = NULL;
-			}
-			if (mimeinfo->encoding_type != oldenc)
-				mimeinfo->encoding_type = oldenc;
-		}
-		g_free(tmpfile);
-	} else {
-		textstr = get_canonical_content(fp, boundary);
+	if (g_task_return_error_if_cancelled(task)) {
+		debug_print("task was cancelled, aborting task:%p\n", task);
+		cancelled = TRUE;
+		goto out_sigdata;
 	}
-	err = gpgme_data_new_from_mem(&textdata, textstr, textstr?strlen(textstr):0, 0);
-	
-	if (err) {
-		debug_print ("gpgme_data_new_from_mem failed: %s\n",
-                   gpgme_strerror (err));
+
+	gpgme_res = gpgme_op_verify_result(ctx);
+	if (gpgme_res && gpgme_res->signatures == NULL) {
+		err = GPG_ERR_SYSTEM_ERROR;
+		g_warning("no signature found");
+		g_snprintf(err_str, GPGERR_BUFSIZE, "No signature found");
+		goto out_plain;
 	}
 
-	if (!g_ascii_strcasecmp(mimeinfo->subtype, "pkcs7-mime") ||
-	    !g_ascii_strcasecmp(mimeinfo->subtype, "x-pkcs7-mime")) {
-		tmpstr = procmime_mimeinfo_get_parameter(mimeinfo, "smime-type");
-		if (tmpstr && !g_ascii_strcasecmp(tmpstr, "signed-data")) {
-			gpgme_data_t cipher;
-			size_t len;
-			if (oldenc == ENC_BASE64)
-				gpgme_data_set_encoding (textdata, GPGME_DATA_ENCODING_BASE64);
-			gpgme_data_new(&cipher);
-			data->sigstatus =
-				sgpgme_verify_signature	(data->ctx, textdata, NULL, cipher);
-			gpgme_data_release(textdata);
-			g_free(textstr);
-			cm_gpgme_data_rewind(cipher);
-			textstr = sgpgme_data_release_and_get_mem(cipher, &len);
-			claws_fclose(fp);
-			if (textstr && len > 0)
-				textstr[len-1]='\0';
-
-			if (textstr && len) {
-				gchar *tmp_file = get_tmp_file();
-				MimeInfo *newinfo = NULL, *decinfo = NULL, *parentinfo = NULL;
-
-				str_write_to_file(textstr, tmp_file, TRUE);
-				newinfo = procmime_scan_file(tmp_file);
-				decinfo = g_node_first_child(newinfo->node) != NULL ?
-					g_node_first_child(newinfo->node)->data : NULL;
-
-				if (decinfo == NULL) {
-					g_free(textstr);
-					return -1;
-				}
-
-				g_node_unlink(decinfo->node);
-				procmime_mimeinfo_free_all(&newinfo);
-				decinfo->tmp = TRUE;
-				parentinfo = procmime_mimeinfo_parent(mimeinfo);
-
-				if (parentinfo->type == MIMETYPE_MESSAGE && 
-				    !strcmp(parentinfo->subtype, "rfc822")) {
-					procmime_decode_content(parentinfo);
-					procmime_encode_content(parentinfo, ENC_BASE64);
-					procmime_encode_content(parentinfo, ENC_8BIT);
-					if (parentinfo->content == MIMECONTENT_MEM) {
-						gint newlen = 
-							(gint)(strstr(parentinfo->data.mem, "\n\n") - parentinfo->data.mem);
-						if (newlen > 0)
-							parentinfo->length = newlen;
-					}
-				}
-				g_node_prepend(parentinfo->node, decinfo->node);
-				g_free(textstr);
-				return 0;
-			} else {
-				if (textstr)
-					g_free(textstr);
-				return -1;
-			}
+	if (task_data->create_mimeinfo) {
+		tmp = gpgme_data_release_and_get_mem(plain, &len);
+		if (!tmp) {
+			debug_print("S/MIME signed message had no plaintext\n");
+			goto out_sigdata;
 		}
-	}
 
-	signature = (MimeInfo *) mimeinfo->node->next->data;
-	sigdata = sgpgme_data_from_mimeinfo(signature);
+		verified = g_string_new_len(tmp, len);
+		gpgme_free(tmp);
 
-	err = 0;
-	if (signature->encoding_type == ENC_BASE64) {
-		err = gpgme_data_set_encoding (sigdata, GPGME_DATA_ENCODING_BASE64);
-	}
-	
-	if (err) {
-		debug_print ("gpgme_data_set_encoding failed: %s\n",
-			gpgme_strerror (err));
+		if (!create_mimeinfo_for_plaintext(verified, &created)) {
+			g_warning("Failed to create new mimeinfo from plaintext");
+			g_string_free(verified, TRUE);
+			goto out_sigdata;
+		}
+
+		g_string_free(verified, TRUE);
+	} else {
+		gpgme_data_release(plain);
 	}
 
-	data->sigstatus =
-		sgpgme_verify_signature	(data->ctx, sigdata, textdata, NULL);
+	task_result = g_new0(SigCheckTaskResult, 1);
+	task_result->sig_data = g_new0(SignatureData, 1);
+
+	task_result->sig_data->status = sgpgme_sigstat_gpgme_to_privacy(ctx, gpgme_res);
+	task_result->sig_data->info_short = sgpgme_sigstat_info_short(ctx, gpgme_res);
+	task_result->sig_data->info_full = sgpgme_sigstat_info_full(ctx, gpgme_res);
+
+	task_result->newinfo = created;
+	return_err = FALSE;
+
+	goto out_sigdata;
 
+out_plain:
+	gpgme_data_release(plain);
+out_sigdata:
 	gpgme_data_release(sigdata);
-	gpgme_data_release(textdata);
-	g_free(textstr);
-	claws_fclose(fp);
-	
-	return 0;
+out_ctx:
+	gpgme_release(ctx);
+out:
+	if (cancelled)
+		return;
+
+	if (return_err)
+		g_task_return_new_error(task, domain, err, err_str);
+	else
+		g_task_return_pointer(task, task_result, privacy_free_sig_check_task_result);
 }
 
-static SignatureStatus smime_get_sig_status(MimeInfo *mimeinfo)
+/* Check PKCS7-MIME signed-data type signature either synchronously or asynchronously.
+ * Check it asynchronously if the caller provides a callback.
+ * If the caller does not provide a callback, and we have not already done so, create
+ * and insert a new MimeInfo for the plaintext data returned by the sig verification.
+ */
+static gint check_pkcs7_mime_sig(MimeInfo *mimeinfo,
+	GCancellable *_cancellable,
+	GAsyncReadyCallback callback,
+	gpointer user_data)
 {
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
-	
-	cm_return_val_if_fail(data != NULL, SIGNATURE_INVALID);
+	MimeInfo *parent;
+	const gchar *tmp;
+	EncodingType real_enc;
+	gchar *textstr = NULL;
+	PrivacyDataPGP *privacy_data;
+	GCancellable *cancellable;
+	GTask *task;
+	PKCS7MimeTaskData *task_data;
+	SigCheckTaskResult *task_result;
+	GError *error;
+	gboolean unref_cancellable = FALSE;
 
-	return sgpgme_sigstat_gpgme_to_privacy(data->ctx, data->sigstatus);
-}
+	debug_print("Checking pkcs7-mime signature\n");
 
-static gchar *smime_get_sig_info_short(MimeInfo *mimeinfo)
-{
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
-	
-	cm_return_val_if_fail(data != NULL, g_strdup("Error"));
+	parent = procmime_mimeinfo_parent(mimeinfo);
+	tmp = g_hash_table_lookup(parent->typeparameters, "boundary");
+	if (tmp) {
+		g_warning("Unexpected S/MIME message format subtype:%s boundary:%s",
+			mimeinfo->subtype, tmp);
+		return -1;
+	}
+
+	if (g_ascii_strcasecmp(mimeinfo->subtype, "pkcs7-mime") &&
+		g_ascii_strcasecmp(mimeinfo->subtype, "x-pkcs7-mime"))
+	{
+		g_warning("Unexpected S/MIME subtype:%s", mimeinfo->subtype);
+		return -1;
+	}
+
+	tmp = procmime_mimeinfo_get_parameter(mimeinfo, "smime-type");
+	if (!tmp || g_ascii_strcasecmp(tmp, "signed-data")) {
+		g_warning("Unexpected S/MIME smime-type parameter:%s", tmp);
+		return -1;
+	}
+
+	real_enc = mimeinfo->encoding_type;
+	mimeinfo->encoding_type = ENC_BINARY;
+	textstr = procmime_get_part_as_string(mimeinfo, TRUE);
+	mimeinfo->encoding_type = real_enc;
+	if (!textstr) {
+		g_warning("Failed to get PKCS7-Mime signature data");
+		return -1;
+	}
+
+	privacy_data = (PrivacyDataPGP *)mimeinfo->privacy;
+
+	task_data = g_new0(PKCS7MimeTaskData, 1);
+	task_data->textstr = textstr;
+	task_data->encoding = mimeinfo->encoding_type;
+
+	if (!callback && !privacy_data->inserted_mimeinfo)
+		task_data->create_mimeinfo = TRUE;
+
+	if (_cancellable != NULL) {
+		cancellable = _cancellable;
+	} else {
+		cancellable = g_cancellable_new();
+		unref_cancellable = TRUE;
+	}
+
+	task = g_task_new(NULL, cancellable, callback, user_data);
+	mimeinfo->last_sig_check_task = task;
+
+	g_task_set_task_data(task, task_data, free_pkcs7_mime_task_data);
+	g_task_set_return_on_cancel(task, TRUE);
+
+	if (callback) {
+		debug_print("creating check sig async task:%p task_data:%p\n", task, task_data);
+		g_task_run_in_thread(task, check_pkcs7_mime_sig_task);
+		g_object_unref(task);
+		return 0;
+	}
+
+	debug_print("creating check sig sync task:%p task_data:%p\n", task, task_data);
+	g_task_run_in_thread_sync(task, check_pkcs7_mime_sig_task);
+	mimeinfo->last_sig_check_task = NULL;
+
+	task_result = g_task_propagate_pointer(task, &error);
+	g_object_unref(task);
+	if (unref_cancellable)
+		g_object_unref(cancellable);
+
+	if (mimeinfo->sig_data) {
+		privacy_free_signature_data(mimeinfo->sig_data);
+		mimeinfo->sig_data = NULL;
+	}
+
+	if (task_result == NULL) {
+		debug_print("sig check task propagated NULL task:%p GError: domain:%s code:%d message:\"%s\"\n",
+			task, g_quark_to_string(error->domain), error->code, error->message);
+		g_error_free(error);
+		return -1;
+	}
+
+	mimeinfo->sig_data = task_result->sig_data;
 
-	return sgpgme_sigstat_info_short(data->ctx, data->sigstatus);
+	if (task_result->newinfo) {
+		if (parent->type == MIMETYPE_MESSAGE && !strcmp(parent->subtype, "rfc822")) {
+			if (parent->content == MIMECONTENT_MEM) {
+				gint newlen = (gint)(strstr(parent->data.mem, "\n\n") - parent->data.mem);
+				if (newlen > 0)
+				parent->length = newlen;
+			}
+		}
+
+		g_node_prepend(parent->node, task_result->newinfo->node);
+		privacy_data->inserted_mimeinfo = TRUE;
+	}
+
+	/* Only free the task result struct, not the SigData and MimeInfo */
+	g_free(task_result);
+
+	return 1;
 }
 
-static gchar *smime_get_sig_info_full(MimeInfo *mimeinfo)
+static gint smime_check_sig_async(MimeInfo *mimeinfo,
+	GCancellable *cancellable,
+	GAsyncReadyCallback callback,
+	gpointer user_data)
 {
-	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
-	
-	cm_return_val_if_fail(data != NULL, g_strdup("Error"));
+	MimeInfo *parent;
+	gchar *boundary;
+
+	/* Detached signature with a boundary */
+	if (g_ascii_strcasecmp(mimeinfo->subtype, "pkcs7-mime") &&
+		g_ascii_strcasecmp(mimeinfo->subtype, "x-pkcs7-mime"))
+	{
+		parent = procmime_mimeinfo_parent(mimeinfo);
+		boundary = g_hash_table_lookup(parent->typeparameters, "boundary");
 
-	return sgpgme_sigstat_info_full(data->ctx, data->sigstatus);
+		if (boundary == NULL) {
+			g_warning("Unexpected S/MIME format subtype:%s without a boundary",
+				mimeinfo->subtype);
+			return -1;
+		}
+
+		return cm_check_detached_sig_async(mimeinfo,
+			cancellable,
+			callback,
+			user_data,
+			GPGME_PROTOCOL_CMS,
+			get_canonical_content);
+
+	/* Opaque pkcs7-mime blob with smime-type=signed-data */
+	} else {
+		return check_pkcs7_mime_sig(mimeinfo, cancellable, callback, user_data);
+	}
 }
 
 static gboolean smime_is_encrypted(MimeInfo *mimeinfo)
@@ -410,6 +550,7 @@ static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
 	gpgme_error_t err;
 	gchar *chars;
 	size_t len;
+	SignatureData *sig_data = NULL;
 
 	cm_return_val_if_fail(smime_is_encrypted(mimeinfo), NULL);
 	
@@ -434,10 +575,18 @@ static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
 	
 	plain = sgpgme_decrypt_verify(cipher, &sigstat, ctx);
 
+	if (sigstat != NULL && sigstat->signatures != NULL) {
+		sig_data = g_new0(SignatureData, 1);
+		sig_data->status = sgpgme_sigstat_gpgme_to_privacy(ctx, sigstat);
+		sig_data->info_short = sgpgme_sigstat_info_short(ctx, sigstat);
+		sig_data->info_full = sgpgme_sigstat_info_full(ctx, sigstat);
+	}
+
+	gpgme_release(ctx);
 	gpgme_data_release(cipher);
 	if (plain == NULL) {
 		debug_print("plain is null!\n");
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 
@@ -448,9 +597,9 @@ static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
         	FILE_OP_ERROR(fname, "claws_fopen");
         	g_free(fname);
         	gpgme_data_release(plain);
-		gpgme_release(ctx);
 		debug_print("can't open!\n");
 		privacy_set_error(_("Couldn't open temporary file"));
+		privacy_free_signature_data(sig_data);
 		return NULL;
     	}
 
@@ -459,9 +608,9 @@ static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
         	g_free(fname);
 		claws_fclose(dstfp);
         	gpgme_data_release(plain);
-		gpgme_release(ctx);
 		debug_print("can't close!\n");
 		privacy_set_error(_("Couldn't write to temporary file"));
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 
@@ -474,9 +623,9 @@ static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
         		g_free(fname);
         		g_free(chars);
         		gpgme_data_release(plain);
-			gpgme_release(ctx);
 			debug_print("can't write!\n");
 			privacy_set_error(_("Couldn't write to temporary file"));
+			privacy_free_signature_data(sig_data);
 			return NULL;
 		}
 	}
@@ -485,9 +634,9 @@ static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
         	g_free(fname);
        		g_free(chars);
         	gpgme_data_release(plain);
-		gpgme_release(ctx);
 		debug_print("can't close!\n");
 		privacy_set_error(_("Couldn't close temporary file"));
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 	g_free(chars);
@@ -496,14 +645,14 @@ static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
 	g_free(fname);
 	if (parseinfo == NULL) {
 		privacy_set_error(_("Couldn't parse decrypted file."));
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 	decinfo = g_node_first_child(parseinfo->node) != NULL ?
 		g_node_first_child(parseinfo->node)->data : NULL;
 	if (decinfo == NULL) {
 		privacy_set_error(_("Couldn't parse decrypted file parts."));
-		gpgme_release(ctx);
+		privacy_free_signature_data(sig_data);
 		return NULL;
 	}
 
@@ -512,28 +661,24 @@ static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
 
 	decinfo->tmp = TRUE;
 
-	if (sigstat != NULL && sigstat->signatures != NULL) {
+	if (sig_data != NULL) {
 		if (decinfo->privacy != NULL) {
 			data = (PrivacyDataPGP *) decinfo->privacy;
 		} else {
 			data = smime_new_privacydata();
 			if (!data) {
-				gpgme_release(ctx);
 				return NULL;
 			}
 			decinfo->privacy = (PrivacyData *) data;	
 		}
-		data->done_sigtest = TRUE;
-		data->is_signed = TRUE;
-		data->sigstatus = sigstat;
-		if (data->ctx)
-			gpgme_release(data->ctx);
-		data->ctx = ctx;
-	} else
-		gpgme_release(ctx);
-	
-	
-	
+
+		if (data != NULL) {
+			data->done_sigtest = TRUE;
+			data->is_signed = TRUE;
+			decinfo->sig_data = sig_data;
+		}
+	}
+
 	return decinfo;
 }
 
@@ -803,6 +948,8 @@ gboolean smime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 	fp = claws_fopen(tmpfile, "wb");
 	if (fp == NULL) {
 		FILE_OP_ERROR(tmpfile, "create");
+		for (gint x = 0; x < i; x++)
+			gpgme_key_unref(kset[x]);
 		g_free(kset);
 		g_free(tmpfile);
 		return FALSE;
@@ -815,6 +962,8 @@ gboolean smime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 	fp = claws_fopen(tmpfile, "rb");
 	if (fp == NULL) {
 		FILE_OP_ERROR(tmpfile, "open");
+		for (gint x = 0; x < i; x++)
+			gpgme_key_unref(kset[x]);
 		g_free(kset);
 		g_free(tmpfile);
 		return FALSE;
@@ -834,6 +983,8 @@ gboolean smime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
 	gpgme_op_encrypt(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, gpgtext, gpgenc);
 
 	gpgme_release(ctx);
+	for (gint x = 0; x < i; x++)
+		gpgme_key_unref(kset[x]);
 	g_free(kset);
 	enccontent = sgpgme_data_release_and_get_mem(gpgenc, &len);
 
@@ -887,10 +1038,7 @@ static PrivacySystem smime_system = {
 	smime_free_privacydata,	/* free_privacydata */
 
 	smime_is_signed,		/* is_signed(MimeInfo *) */
-	smime_check_signature,	/* check_signature(MimeInfo *) */
-	smime_get_sig_status,		/* get_sig_status(MimeInfo *) */
-	smime_get_sig_info_short,	/* get_sig_info_short(MimeInfo *) */
-	smime_get_sig_info_full,	/* get_sig_info_full(MimeInfo *) */
+	smime_check_sig_async,
 
 	smime_is_encrypted,		/* is_encrypted(MimeInfo *) */
 	smime_decrypt,			/* decrypt(MimeInfo *) */
diff --git a/src/privacy.c b/src/privacy.c
index 01b282a6b..a77e9a814 100644
--- a/src/privacy.c
+++ b/src/privacy.c
@@ -1,6 +1,6 @@
 /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2012 Hiroyuki Yamamoto & the Claws Mail team
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2021 the Claws Mail team and Hiroyuki Yamamoto
  *
  * 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
@@ -109,6 +109,25 @@ void privacy_free_privacydata(PrivacyData *privacydata)
 	system->free_privacydata(privacydata);
 }
 
+void privacy_free_signature_data(gpointer data)
+{
+	SignatureData *sig_data = (SignatureData *)data;
+
+	g_free(sig_data->info_short);
+	g_free(sig_data->info_full);
+	g_free(sig_data);
+}
+
+void privacy_free_sig_check_task_result(gpointer data)
+{
+	SigCheckTaskResult *result = (SigCheckTaskResult *)data;
+
+	privacy_free_signature_data(result->sig_data);
+	if (result->newinfo)
+		procmime_mimeinfo_free_all(&result->newinfo);
+	g_free(result);
+}
+
 /**
  * Check if a MimeInfo is signed with one of the available
  * privacy system. If a privacydata is set in the MimeInfo
@@ -193,26 +212,32 @@ void privacy_msginfo_get_signed_state(MsgInfo *msginfo, gchar **system)
  * \return Error code indicating the result of the check,
  *         < 0 if an error occurred
  */
-gint privacy_mimeinfo_check_signature(MimeInfo *mimeinfo)
+gint privacy_mimeinfo_check_signature(MimeInfo *mimeinfo,
+	GCancellable *cancellable,
+	GAsyncReadyCallback callback,
+	gpointer user_data)
 {
 	PrivacySystem *system;
 
-	cm_return_val_if_fail(mimeinfo != NULL, -1);
+	if (mimeinfo == NULL)
+		g_error("siginfo was NULL");
+
+	if (mimeinfo->privacy == NULL) {
+		g_warning("mimeinfo->privacy was NULL");
 
-	if (mimeinfo->privacy == NULL)
 		privacy_mimeinfo_is_signed(mimeinfo);
-	
-	if (mimeinfo->privacy == NULL)
-		return -1;
-	
+		if (mimeinfo->privacy == NULL) {
+			g_error("failed to set up PrivacyData");
+		}
+	}
+
 	system = privacy_data_get_system(mimeinfo->privacy);
 	if (system == NULL)
-		return -1;
+		g_error("failed to get privacy system");
+	else if (system->check_signature == NULL)
+		g_error("didn't find check_signature function");
 
-	if (system->check_signature == NULL)
-		return -1;
-	
-	return system->check_signature(mimeinfo);
+	return system->check_signature(mimeinfo, cancellable, callback, user_data);
 }
 
 SignatureStatus privacy_mimeinfo_get_sig_status(MimeInfo *mimeinfo)
@@ -221,61 +246,49 @@ SignatureStatus privacy_mimeinfo_get_sig_status(MimeInfo *mimeinfo)
 
 	cm_return_val_if_fail(mimeinfo != NULL, -1);
 
-	if (mimeinfo->privacy == NULL)
+	if (mimeinfo->privacy == NULL) {
 		privacy_mimeinfo_is_signed(mimeinfo);
-	
-	if (mimeinfo->privacy == NULL)
-		return SIGNATURE_UNCHECKED;
-	
+
+		if (mimeinfo->privacy == NULL)
+			return SIGNATURE_UNCHECKED;
+	}
+
 	system = privacy_data_get_system(mimeinfo->privacy);
 	if (system == NULL)
 		return SIGNATURE_UNCHECKED;
-	if (system->get_sig_status == NULL)
+
+	if (mimeinfo->sig_data == NULL)
 		return SIGNATURE_UNCHECKED;
-	
-	return system->get_sig_status(mimeinfo);
+	else
+		return mimeinfo->sig_data->status;
 }
 
-gchar *privacy_mimeinfo_sig_info_short(MimeInfo *mimeinfo)
+gchar *privacy_mimeinfo_get_sig_info(MimeInfo *mimeinfo, gboolean full)
 {
 	PrivacySystem *system;
+	gchar *info;
 
 	cm_return_val_if_fail(mimeinfo != NULL, NULL);
 
-	if (mimeinfo->privacy == NULL)
+	if (mimeinfo->privacy == NULL) {
 		privacy_mimeinfo_is_signed(mimeinfo);
-	
-	if (mimeinfo->privacy == NULL)
-		return g_strdup(_("No signature found"));
-	
+
+		if (mimeinfo->privacy == NULL)
+			return _("No signature found");
+	}
+
 	system = privacy_data_get_system(mimeinfo->privacy);
 	if (system == NULL)
-		return g_strdup(_("No signature found"));
-	if (system->get_sig_info_short == NULL)
-		return g_strdup(_("No information available"));
-	
-	return system->get_sig_info_short(mimeinfo);
-}
+		return _("No signature found");
 
-gchar *privacy_mimeinfo_sig_info_full(MimeInfo *mimeinfo)
-{
-	PrivacySystem *system;
+	if (mimeinfo->sig_data == NULL)
+		return _("No information available");
 
-	cm_return_val_if_fail(mimeinfo != NULL, NULL);
+	info = full ? mimeinfo->sig_data->info_full : mimeinfo->sig_data->info_short;
+	if (info == NULL)
+		return _("No information available");
 
-	if (mimeinfo->privacy == NULL)
-		privacy_mimeinfo_is_signed(mimeinfo);
-	
-	if (mimeinfo->privacy == NULL)
-		return g_strdup(_("No signature found"));
-	
-	system = privacy_data_get_system(mimeinfo->privacy);
-	if (system == NULL)
-		return g_strdup(_("No signature found"));
-	if (system->get_sig_info_full == NULL)
-		return g_strdup(_("No information available"));
-	
-	return system->get_sig_info_full(mimeinfo);
+	return info;
 }
 
 gboolean privacy_mimeinfo_is_encrypted(MimeInfo *mimeinfo)
diff --git a/src/privacy.h b/src/privacy.h
index ebc191972..3aafae9ad 100644
--- a/src/privacy.h
+++ b/src/privacy.h
@@ -1,6 +1,6 @@
 /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2021 the Claws Mail team and Hiroyuki Yamamoto
  *
  * 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
@@ -30,9 +30,21 @@ typedef enum {
 	SIGNATURE_KEY_EXPIRED,
 	SIGNATURE_INVALID,
 	SIGNATURE_CHECK_FAILED,
-	SIGNATURE_CHECK_TIMEOUT
+	SIGNATURE_CHECK_TIMEOUT,
+	SIGNATURE_CHECK_ERROR
 } SignatureStatus;
 
+typedef struct _SignatureData {
+	SignatureStatus status;
+	gchar *info_short;
+	gchar *info_full;
+} SignatureData;
+
+typedef struct _SigCheckTaskResult {
+	SignatureData *sig_data;
+	struct _MimeInfo *newinfo;
+} SigCheckTaskResult;
+
 #include <glib.h>
 
 #include "procmime.h"
@@ -42,13 +54,17 @@ void privacy_register_system			(PrivacySystem *system);
 void privacy_unregister_system			(PrivacySystem *system);
 
 void privacy_free_privacydata			(PrivacyData *);
+void privacy_free_signature_data		(gpointer);
+void privacy_free_sig_check_task_result	(gpointer);
 
 void privacy_msginfo_get_signed_state		(MsgInfo *, gchar **system);
 gboolean privacy_mimeinfo_is_signed		(MimeInfo *);
-gint privacy_mimeinfo_check_signature		(MimeInfo *);
+gint privacy_mimeinfo_check_signature	(MimeInfo *mimeinfo,
+	GCancellable *cancellable,
+	GAsyncReadyCallback callback,
+	gpointer user_data);
 SignatureStatus privacy_mimeinfo_get_sig_status	(MimeInfo *);
-gchar *privacy_mimeinfo_sig_info_short		(MimeInfo *);
-gchar *privacy_mimeinfo_sig_info_full		(MimeInfo *);
+gchar *privacy_mimeinfo_get_sig_info		(MimeInfo *, gboolean);
 
 gboolean privacy_mimeinfo_is_encrypted		(MimeInfo *);
 gint privacy_mimeinfo_decrypt			(MimeInfo *);
@@ -83,10 +99,10 @@ struct _PrivacySystem {
 	void		 (*free_privacydata)	(PrivacyData *data);
 
 	gboolean	 (*is_signed)		(MimeInfo *mimeinfo);
-	gint		 (*check_signature)	(MimeInfo *mimeinfo);
-	SignatureStatus	 (*get_sig_status)	(MimeInfo *mimeinfo);
-	gchar		*(*get_sig_info_short)	(MimeInfo *mimeinfo);
-	gchar		*(*get_sig_info_full)	(MimeInfo *mimeinfo);
+	gint		 (*check_signature)	(MimeInfo *mimeinfo,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
 
 	gboolean	 (*is_encrypted)	(MimeInfo *mimeinfo);
 	MimeInfo	*(*decrypt)		(MimeInfo *mimeinfo);
diff --git a/src/procmime.c b/src/procmime.c
index 36cda8046..dfb78b59c 100644
--- a/src/procmime.c
+++ b/src/procmime.c
@@ -1,6 +1,6 @@
 /*
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2021 the Claws Mail Team and Hiroyuki Yamamoto
+ * Copyright (C) 1999-2021 the Claws Mail team and Hiroyuki Yamamoto
  *
  * 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
@@ -144,6 +144,9 @@ static gboolean free_func(GNode *node, gpointer data)
 	if (mimeinfo->privacy)
 		privacy_free_privacydata(mimeinfo->privacy);
 
+	if (mimeinfo->sig_data)
+		privacy_free_signature_data(mimeinfo->sig_data);
+
 	g_free(mimeinfo);
 
 	return FALSE;
diff --git a/src/procmime.h b/src/procmime.h
index e87a27624..8b6859f9b 100644
--- a/src/procmime.h
+++ b/src/procmime.h
@@ -1,6 +1,6 @@
 /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2021 the Claws Mail team and Hiroyuki Yamamoto
  *
  * 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
@@ -29,6 +29,7 @@
 
 #include "utils.h"
 #include "proctypes.h"
+#include "privacy.h"
 typedef enum
 {
 	ENC_7BIT,
@@ -149,6 +150,8 @@ struct _MimeInfo
 
 	/* Privacy */
 	struct _PrivacyData	*privacy;
+	GTask *last_sig_check_task;
+	SignatureData *sig_data;
 
 	gboolean	 broken;
 };

-----------------------------------------------------------------------


hooks/post-receive
-- 
Claws Mail


More information about the Commits mailing list