2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 the Claws Mail team
4 * This file Copyright (C) 2006 Colin Leroy <colin@colino.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "claws-features.h"
28 #include <glib/gi18n.h>
29 #include <sys/types.h>
31 # include <sys/wait.h>
36 #if (defined(__DragonFly__) || defined(SOLARIS) || defined (__NetBSD__) || defined (__FreeBSD__) || defined (__OpenBSD__))
37 # include <sys/signal.h>
41 #include "common/claws.h"
42 #include "mainwindow.h"
46 #include "prefs_common.h"
47 #include "prefs_gpg.h"
48 #include "alertpanel.h"
51 typedef struct _PgpViewer PgpViewer
;
53 static MimeViewerFactory pgp_viewer_factory
;
57 MimeViewer mimeviewer
;
61 static gchar
*content_types
[] =
62 {"application/pgp-signature", NULL
};
64 static GtkWidget
*pgp_get_widget(MimeViewer
*_viewer
)
66 PgpViewer
*viewer
= (PgpViewer
*) _viewer
;
68 debug_print("pgp_get_widget\n");
70 return GTK_WIDGET(viewer
->textview
->vbox
);
80 static void *_import_threaded(void *arg
)
82 struct _ImportCtx
*ctx
= (struct _ImportCtx
*)arg
;
85 PROCESS_INFORMATION pi
= {0};
88 result
= CreateProcess(NULL
, ctx
->cmd
, NULL
, NULL
, FALSE
,
89 NORMAL_PRIORITY_CLASS
| CREATE_NO_WINDOW
,
90 NULL
, NULL
, &si
, &pi
);
93 debug_print("Couldn't execute '%s'\n", ctx
->cmd
);
95 WaitForSingleObject(pi
.hProcess
, 10000);
96 result
= GetExitCodeProcess(pi
.hProcess
, &ctx
->exitcode
);
97 if (ctx
->exitcode
== STILL_ACTIVE
) {
98 debug_print("Process still running, terminating it.\n");
99 TerminateProcess(pi
.hProcess
, 255);
102 CloseHandle(pi
.hProcess
);
103 CloseHandle(pi
.hThread
);
106 debug_print("Process executed, but we couldn't get its exit code (huh?)\n");
115 static void pgpview_show_mime_part(TextView
*textview
, MimeInfo
*partinfo
)
118 GtkTextBuffer
*buffer
;
120 gpgme_data_t sigdata
= NULL
;
121 gpgme_verify_result_t sigstatus
= NULL
;
122 gpgme_ctx_t ctx
= NULL
;
123 gpgme_key_t key
= NULL
;
124 gpgme_signature_t sig
= NULL
;
125 gpgme_error_t err
= 0;
126 gboolean imported
= FALSE
;
127 MsgInfo
*msginfo
= textview
->messageview
->msginfo
;
129 if (!partinfo
) return;
131 textview_set_font(textview
, NULL
);
132 textview_clear(textview
);
134 text
= GTK_TEXT_VIEW(textview
->text
);
135 buffer
= gtk_text_view_get_buffer(text
);
136 gtk_text_buffer_get_start_iter(buffer
, &iter
);
138 err
= gpgme_new (&ctx
);
140 debug_print("err : %s\n", gpgme_strerror(err
));
141 textview_show_mime_part(textview
, partinfo
);
145 sigdata
= sgpgme_data_from_mimeinfo(partinfo
);
147 g_warning("no sigdata");
148 textview_show_mime_part(textview
, partinfo
);
152 /* Here we do not care about what data we attempt to verify with the
153 * signature, or about result of the verification - all we care about
154 * is that we find out ID of the key used to make this signature. */
155 sigstatus
= sgpgme_verify_signature(ctx
, sigdata
, NULL
, sigdata
);
156 if (!sigstatus
|| sigstatus
== GINT_TO_POINTER(-GPG_ERR_SYSTEM_ERROR
)) {
157 g_warning("no sigstatus");
158 textview_show_mime_part(textview
, partinfo
);
161 sig
= sigstatus
->signatures
;
164 textview_show_mime_part(textview
, partinfo
);
167 gpgme_get_key(ctx
, sig
->fpr
, &key
, 0);
169 gchar
*gpgbin
= get_gpg_executable_name();
170 gchar
*from_addr
= g_strdup(msginfo
->from
);
171 extract_address(from_addr
);
172 gchar
*cmd_ks
= g_strdup_printf("\"%s\" --batch --no-tty --recv-keys %s",
173 (gpgbin
? gpgbin
: "gpg2"), sig
->fpr
);
174 gchar
*cmd_wkd
= g_strdup_printf("\"%s\" --batch --no-tty --locate-keys \"%s\"",
175 (gpgbin
? gpgbin
: "gpg2"), from_addr
);
177 AlertValue val
= G_ALERTDEFAULT
;
178 if (!prefs_common_get_prefs()->work_offline
) {
179 val
= alertpanel(_("Key import"),
180 _("This key is not in your keyring. Do you want "
181 "Claws Mail to try to import it?"),
182 NULL
, _("_No"), NULL
, _("from keyserver"),
183 NULL
, _("from Web Key Directory"), ALERTFOCUS_SECOND
);
186 if (val
== G_ALERTDEFAULT
) {
187 TEXTVIEW_INSERT(_("\n Key ID "));
188 TEXTVIEW_INSERT(sig
->fpr
);
189 TEXTVIEW_INSERT(":\n\n");
190 TEXTVIEW_INSERT(_(" This key is not in your keyring.\n"));
191 TEXTVIEW_INSERT(_(" It should be possible to import it "));
192 if (prefs_common_get_prefs()->work_offline
)
193 TEXTVIEW_INSERT(_("when working online,\n or "));
194 TEXTVIEW_INSERT(_("with either of the following commands: \n\n "));
195 TEXTVIEW_INSERT(cmd_ks
);
196 TEXTVIEW_INSERT("\n\n");
197 TEXTVIEW_INSERT(cmd_wkd
);
198 } else if (val
== G_ALERTALTERNATE
|| val
== G_ALERTOTHER
) {
199 TEXTVIEW_INSERT(_("\n Importing key ID "));
200 TEXTVIEW_INSERT(sig
->fpr
);
201 TEXTVIEW_INSERT(":\n\n");
203 main_window_cursor_wait(mainwindow_get_mainwindow());
204 textview_cursor_wait(textview
);
214 } else if (pid
== 0) {
217 if (val
== G_ALERTOTHER
)
218 argv
= strsplit_with_quote(cmd_wkd
, " ", 0);
220 argv
= strsplit_with_quote(cmd_ks
, " ", 0);
221 res
= execvp(argv
[0], argv
);
226 time_t start_wait
= time(NULL
);
229 if (waitpid(pid
, &status
, WNOHANG
) == 0 || !WIFEXITED(status
)) {
232 res
= WEXITSTATUS(status
);
235 if (time(NULL
) - start_wait
> 9) {
236 debug_print("SIGTERM'ing gpg %d\n", pid
);
239 if (time(NULL
) - start_wait
> 10) {
240 debug_print("SIGKILL'ing gpg %d\n", pid
);
246 debug_print("res %d\n", res
);
250 /* We need to call gpg in a separate thread, so that waiting for
251 * it to finish does not block the UI. */
253 struct _ImportCtx
*ctx
= malloc(sizeof(struct _ImportCtx
));
256 ctx
->exitcode
= STILL_ACTIVE
;
257 ctx
->cmd
= (val
== G_ALERTOTHER
)? cmd_wkd
: cmd_ks
;
259 if (pthread_create(&pt
, NULL
,
260 _import_threaded
, (void *)ctx
) != 0) {
261 debug_print("Couldn't create thread, continuing unthreaded.\n");
262 _import_threaded(ctx
);
264 debug_print("Thread created, waiting for it to finish...\n");
269 debug_print("Thread finished.\n");
270 pthread_join(pt
, NULL
);
272 if (ctx
->exitcode
== 0) {
277 main_window_cursor_normal(mainwindow_get_mainwindow());
278 textview_cursor_normal(textview
);
280 TEXTVIEW_INSERT(_(" This key has been imported to your keyring.\n"));
282 TEXTVIEW_INSERT(_(" This key couldn't be imported to your keyring.\n"));
283 TEXTVIEW_INSERT(_(" Key servers are sometimes slow.\n"));
284 TEXTVIEW_INSERT(_(" You can try to import it manually with the command:"));
285 TEXTVIEW_INSERT("\n\n ");
286 TEXTVIEW_INSERT(cmd_ks
);
287 TEXTVIEW_INSERT("\n\n ");
288 TEXTVIEW_INSERT(_("or"));
289 TEXTVIEW_INSERT("\n\n ");
290 TEXTVIEW_INSERT(cmd_wkd
);
297 TEXTVIEW_INSERT(_("\n Key ID "));
299 #if defined GPGME_VERSION_NUMBER && GPGME_VERSION_NUMBER >= 0x010700
300 TEXTVIEW_INSERT(key
->fpr
);
302 TEXTVIEW_INSERT(sig
->fpr
);
305 TEXTVIEW_INSERT(":\n\n");
306 TEXTVIEW_INSERT(_(" This key is in your keyring.\n"));
307 gpgme_key_unref(key
);
309 gpgme_data_release(sigdata
);
311 textview_show_icon(textview
, "dialog-password");
315 static void pgp_show_mimepart(MimeViewer
*_viewer
,
319 PgpViewer
*viewer
= (PgpViewer
*)_viewer
;
320 debug_print("pgp_show_mimepart\n");
321 viewer
->textview
->messageview
= _viewer
->mimeview
->messageview
;
322 pgpview_show_mime_part(viewer
->textview
, partinfo
);
325 static void pgp_clear_viewer(MimeViewer
*_viewer
)
327 PgpViewer
*viewer
= (PgpViewer
*)_viewer
;
328 debug_print("pgp_clear_viewer\n");
329 textview_clear(viewer
->textview
);
332 static void pgp_destroy_viewer(MimeViewer
*_viewer
)
334 PgpViewer
*viewer
= (PgpViewer
*)_viewer
;
335 debug_print("pgp_destroy_viewer\n");
336 textview_destroy(viewer
->textview
);
339 static MimeViewer
*pgp_viewer_create(void)
343 debug_print("pgp_viewer_create\n");
345 viewer
= g_new0(PgpViewer
, 1);
346 viewer
->mimeviewer
.factory
= &pgp_viewer_factory
;
347 viewer
->mimeviewer
.get_widget
= pgp_get_widget
;
348 viewer
->mimeviewer
.show_mimepart
= pgp_show_mimepart
;
349 viewer
->mimeviewer
.clear_viewer
= pgp_clear_viewer
;
350 viewer
->mimeviewer
.destroy_viewer
= pgp_destroy_viewer
;
351 viewer
->mimeviewer
.get_selection
= NULL
;
352 viewer
->textview
= textview_create();
353 textview_init(viewer
->textview
);
355 gtk_widget_show_all(viewer
->textview
->vbox
);
357 return (MimeViewer
*) viewer
;
360 static MimeViewerFactory pgp_viewer_factory
=
368 void pgp_viewer_init(void)
370 mimeview_register_viewer_factory(&pgp_viewer_factory
);
373 void pgp_viewer_done(void)
375 mimeview_unregister_viewer_factory(&pgp_viewer_factory
);