/* gpafileverifyop.c - The GpaOperation object. * Copyright (C) 2003, Miguel Coca. * * This file is part of GPA * * GPA is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GPA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #ifdef G_OS_UNIX #include #include #include #else #include #endif #include "gpa.h" #include "gtktools.h" #include "gpgmetools.h" #include "gpafileverifyop.h" #include "verifydlg.h" /* Internal functions */ static gboolean gpa_file_verify_operation_idle_cb (gpointer data); static void gpa_file_verify_operation_done_error_cb (GpaContext *context, gpg_error_t err, GpaFileVerifyOperation *op); static void gpa_file_verify_operation_done_cb (GpaContext *context, gpg_error_t err, GpaFileVerifyOperation *op); static void gpa_file_verify_operation_response_cb (GtkDialog *dialog, gint response, gpointer user_data); /* GObject */ static GObjectClass *parent_class = NULL; static void gpa_file_verify_operation_finalize (GObject *object) { GpaFileVerifyOperation *op = GPA_FILE_VERIFY_OPERATION (object); gtk_widget_destroy (op->dialog); G_OBJECT_CLASS (parent_class)->finalize (object); } static void gpa_file_verify_operation_init (GpaFileVerifyOperation *op) { op->sig_fd = -1; op->signed_text_fd = -1; op->sig = NULL; op->signed_text = NULL; op->signed_file = NULL; op->signature_file = NULL; } static GObject* gpa_file_verify_operation_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object; GpaFileVerifyOperation *op; /* Invoke parent's constructor */ object = parent_class->constructor (type, n_construct_properties, construct_properties); op = GPA_FILE_VERIFY_OPERATION (object); /* Initialize */ /* Start with the first file after going back into the main loop */ g_idle_add (gpa_file_verify_operation_idle_cb, op); /* Connect to the "done" signal */ g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done", G_CALLBACK (gpa_file_verify_operation_done_error_cb), op); g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done", G_CALLBACK (gpa_file_verify_operation_done_cb), op); /* Give a title to the progress dialog */ gtk_window_set_title (GTK_WINDOW (GPA_FILE_OPERATION (op)->progress_dialog), _("Verifying...")); /* Create the verification dialog */ op->dialog = gpa_file_verify_dialog_new (GPA_OPERATION (op)->window); g_signal_connect (G_OBJECT (op->dialog), "response", G_CALLBACK (gpa_file_verify_operation_response_cb), op); return object; } static void gpa_file_verify_operation_class_init (GpaFileVerifyOperationClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->constructor = gpa_file_verify_operation_constructor; object_class->finalize = gpa_file_verify_operation_finalize; } GType gpa_file_verify_operation_get_type (void) { static GType file_verify_operation_type = 0; if (!file_verify_operation_type) { static const GTypeInfo file_verify_operation_info = { sizeof (GpaFileVerifyOperationClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gpa_file_verify_operation_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GpaFileVerifyOperation), 0, /* n_preallocs */ (GInstanceInitFunc) gpa_file_verify_operation_init, }; file_verify_operation_type = g_type_register_static (GPA_FILE_OPERATION_TYPE, "GpaFileVerifyOperation", &file_verify_operation_info, 0); } return file_verify_operation_type; } /* API */ GpaFileVerifyOperation* gpa_file_verify_operation_new (GtkWidget *window, GList *files) { GpaFileVerifyOperation *op; op = g_object_new (GPA_FILE_VERIFY_OPERATION_TYPE, "window", window, "input_files", files, NULL); return op; } /* Internal */ static gboolean ask_use_detached_sig (const gchar *file, const gchar *sig, GtkWidget *parent) { GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(parent), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("GPA found a file that could be a signature of %s. " "Would you like to verify it instead?\n\n" "The file found is: %s"), file, sig); gboolean result; gtk_dialog_add_buttons (GTK_DIALOG (dialog), _("_Yes"), GTK_RESPONSE_YES, _("_No"), GTK_RESPONSE_NO, NULL); result = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES); gtk_widget_destroy (dialog); return result; } /* Check whether the file is a detached signature and deduce the name of the * original file. Since we only have access to the filename, this is not * very solid. */ static gboolean is_detached_sig (const gchar *filename, gchar **signature_file, gchar **signed_file, GtkWidget *window) { const gchar *sig_extension[] = {".sig", ".sign"}; int i; gchar *extension; /* First, check whether this file is a dettached signature */ *signed_file = g_strdup (filename); extension = g_strrstr (*signed_file, "."); if (extension && (g_str_equal (extension, ".sig") || g_str_equal (extension, ".sign"))) { *extension++ = '\0'; *signature_file = g_strdup (filename); return TRUE; } /* Now, check whether a dettached signature exists for this file */ else { g_free (*signed_file); *signed_file = NULL; for (i = 0; i < sizeof(sig_extension)/sizeof(sig_extension[0]); i++) { gchar *sig = g_strconcat (filename, sig_extension[i], NULL); if (g_file_test (sig, G_FILE_TEST_EXISTS) && ask_use_detached_sig (filename, sig, window)) { *signed_file = g_strdup (filename); *signature_file = sig; return TRUE; } else { g_free (sig); } } return FALSE; } } static gboolean gpa_file_verify_operation_start (GpaFileVerifyOperation *op, const gchar *sig_filename) { gpg_error_t err; if (is_detached_sig (sig_filename, &op->signature_file, &op->signed_file, GPA_OPERATION (op)->window)) { /* Allocate data objects for a detached signature */ op->sig_fd = gpa_open_input (op->signature_file, &op->sig, GPA_OPERATION (op)->window); if (op->sig_fd == -1) { return FALSE; } op->signed_text_fd = gpa_open_input (op->signed_file, &op->signed_text, GPA_OPERATION (op)->window); if (op->signed_text_fd == -1) { gpgme_data_release (op->sig); close (op->sig_fd); return FALSE; } op->plain = NULL; } else { /* Allocate data object for non-detached signatures */ op->sig_fd = gpa_open_input (sig_filename, &op->sig, GPA_OPERATION (op)->window); if (op->sig_fd == -1) { return FALSE; } if (gpg_err_code (gpgme_data_new (&op->plain)) != GPG_ERR_NO_ERROR) { gpgme_data_release (op->sig); close (op->sig_fd); return FALSE; } op->signed_text_fd = -1; op->signed_text = NULL; } /* Start the operation */ err = gpgme_op_verify_start (GPA_OPERATION (op)->context->ctx, op->sig, op->signed_text, op->plain); if (gpg_err_code (err) != GPG_ERR_NO_ERROR) { gpa_gpgme_warning (err); return FALSE; } /* Show and update the progress dialog */ gtk_widget_show_all (GPA_FILE_OPERATION (op)->progress_dialog); gpa_progress_dialog_set_label (GPA_PROGRESS_DIALOG (GPA_FILE_OPERATION (op)->progress_dialog), sig_filename); return TRUE; } static void gpa_file_verify_operation_next (GpaFileVerifyOperation *op) { if (!GPA_FILE_OPERATION (op)->current || !gpa_file_verify_operation_start (op, GPA_FILE_OPERATION (op) ->current->data)) { /* All files have been verified: show the results dialog */ gtk_widget_show_all (op->dialog); } } static void gpa_file_verify_operation_done_cb (GpaContext *context, gpg_error_t err, GpaFileVerifyOperation *op) { /* Do clean up on the operation */ if (op->plain) { gpgme_data_release (op->plain); } if (op->signed_text) { gpgme_data_release (op->signed_text); close (op->signed_text_fd); } gpgme_data_release (op->sig); close (op->sig_fd); gtk_widget_hide (GPA_FILE_OPERATION (op)->progress_dialog); /* Check for error */ if (gpg_err_code (err) != GPG_ERR_NO_ERROR) { /* Abort further verifications */ } else { gpgme_verify_result_t result; result = gpgme_op_verify_result (GPA_OPERATION (op)->context->ctx); /* Add the file to the result dialog */ gpa_file_verify_dialog_add_file (GPA_FILE_VERIFY_DIALOG (op->dialog), GPA_FILE_OPERATION (op)->current->data, op->signed_file, op->signature_file, result->signatures); /* If this was a dettached sig, reset the signed file */ if (op->signed_file) { g_free (op->signed_file); op->signed_file = NULL; g_free (op->signature_file); op->signature_file = NULL; } /* Go to the next file in the list and verify it */ GPA_FILE_OPERATION (op)->current = g_list_next (GPA_FILE_OPERATION (op)->current); gpa_file_verify_operation_next (op); } } static gboolean gpa_file_verify_operation_idle_cb (gpointer data) { GpaFileVerifyOperation *op = data; gpa_file_verify_operation_next (op); return FALSE; } static void gpa_file_verify_operation_response_cb (GtkDialog *dialog, gint response, gpointer user_data) { GpaFileVerifyOperation *op = GPA_FILE_VERIFY_OPERATION (user_data); g_signal_emit_by_name (GPA_OPERATION (op), "completed"); } static void gpa_file_verify_operation_done_error_cb (GpaContext *context, gpg_error_t err, GpaFileVerifyOperation *op) { gchar *message; switch (gpg_err_code (err)) { case GPG_ERR_NO_ERROR: case GPG_ERR_CANCELED: /* Ignore these */ break; case GPG_ERR_NO_DATA: message = g_strdup_printf (_("The file \"%s\" contained no OpenPGP " "data."), gpa_file_operation_current_file (GPA_FILE_OPERATION(op))); gpa_window_error (message, GPA_OPERATION (op)->window); g_free (message); break; case GPG_ERR_BAD_PASSPHRASE: gpa_window_error (_("Wrong passphrase!"), GPA_OPERATION (op)->window); break; default: gpa_gpgme_warning (err); break; } }