A task-oriented GnuPG frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

295 lines
8.3 KiB

/*
* Yorkie - A task-oriented GnuPG frontend
* Copyright (C) 2020 Damien Goutte-Gattat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "encrypt.h"
#include <glib.h>
#include <gtk/gtk.h>
#include <gpgme.h>
#include "error.h"
#include "gpgutil.h"
/* GUI stuff. */
typedef struct {
gpgme_ctx_t gpgme;
GtkListStore *public_keys;
unsigned selected_keys;
gboolean symmetric;
gboolean sign;
gboolean detached;
GtkWidget *ok_button;
GtkWidget *detached_button;
} yki_encrypt_data_t;
static void
update_buttons(yki_encrypt_data_t *yki)
{
gtk_widget_set_sensitive(yki->ok_button,
yki->symmetric ||
yki->selected_keys ||
yki->sign);
gtk_widget_set_sensitive(yki->detached_button,
yki->sign &&
! ( yki->symmetric || yki->selected_keys));
}
static void
symmetric_encrypt_toggled(GtkWidget *widget, gpointer data)
{
((yki_encrypt_data_t *)data)->symmetric = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
update_buttons((yki_encrypt_data_t *)data);
}
static void
sign_toggled(GtkWidget *widget, gpointer data)
{
((yki_encrypt_data_t *)data)->sign = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
update_buttons((yki_encrypt_data_t *)data);
}
static void
detach_sign_toggled(GtkWidget *widget, gpointer data)
{
((yki_encrypt_data_t *)data)->detached = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
}
static void
public_key_toggled(GtkCellRendererToggle *renderer,
gchar *path,
gpointer data)
{
yki_encrypt_data_t *yki = (yki_encrypt_data_t *)data;
GtkTreeIter iter;
gboolean selected;
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yki->public_keys), &iter, path);
gtk_tree_model_get(GTK_TREE_MODEL(yki->public_keys), &iter, 0, &selected, -1);
selected = !selected;
yki->selected_keys += selected ? 1 : -1;
gtk_list_store_set(GTK_LIST_STORE(yki->public_keys), &iter, 0, selected, -1);
update_buttons(yki);
}
static GtkDialog *
create_encrypt_dialog(yki_encrypt_data_t *yki)
{
GtkBuilder *builder;
GObject *widget;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
builder = gtk_builder_new_from_resource("/org/incenp/Yorkie/yki-encrypt.ui");
g_signal_connect(gtk_builder_get_object(builder, "chkSymEncrypt"),
"clicked", G_CALLBACK(symmetric_encrypt_toggled), yki);
g_signal_connect(gtk_builder_get_object(builder, "chkSign"),
"clicked", G_CALLBACK(sign_toggled), yki);
g_signal_connect(gtk_builder_get_object(builder, "chkDetachSign"),
"clicked", G_CALLBACK(detach_sign_toggled), yki);
widget = gtk_builder_get_object(builder, "trvPublicKeys");
yki->public_keys = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(widget)));
renderer = gtk_cell_renderer_toggle_new();
column = gtk_tree_view_column_new_with_attributes("Selected", renderer, "active", 0, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(widget), column);
g_signal_connect(renderer, "toggled", G_CALLBACK(public_key_toggled), yki);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("User ID", renderer, "text", 1, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(widget), column);
yki->ok_button = GTK_WIDGET(gtk_builder_get_object(builder, "btnOK"));
yki->detached_button = GTK_WIDGET(gtk_builder_get_object(builder, "chkDetachSign"));
update_buttons(yki);
return GTK_DIALOG(gtk_builder_get_object(builder, "dlgEncrypt"));
}
/* Dealing with public keys. */
static int
populate_public_key_list(gpgme_ctx_t ctx, GtkListStore *key_list, GError **error)
{
GtkTreeIter iter;
gpgme_key_t key;
gpgme_error_t gerr;
gerr = gpgme_op_keylist_start(ctx, NULL, 0);
while ( ! gerr ) {
gerr = gpgme_op_keylist_next(ctx, &key);
if ( ! gerr ) {
if ( key->revoked || key->expired || key->disabled || key->invalid || ! key->can_encrypt )
continue;
gtk_list_store_append(key_list, &iter);
gtk_list_store_set(key_list, &iter,
0, FALSE, /* Selected key? */
1, key->uids[0].uid, /* User ID */
2, key, /* The key itself */
-1);
}
}
if ( gpgme_err_code(gerr) == GPG_ERR_EOF )
gerr = 0; /* Normal end of list, not an error condition */
if ( gerr )
yki_error_gpgme(error, YKI_ERROR_GPGME_KEYLIST, gerr);
return gpgme_err_code(gerr) == 0;
}
static gboolean
free_key_in_list(GtkTreeModel *store, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
gpgme_key_t key;
gtk_tree_model_get(store, iter, 2, &key, -1);
gpgme_key_release(key);
return TRUE;
}
static gpgme_key_t *
get_selected_keys(yki_encrypt_data_t *yki)
{
gpgme_key_t *keys;
GtkTreeIter iter;
gboolean loop;
unsigned i;
keys = g_malloc((yki->selected_keys + 1) * sizeof(gpgme_key_t));
loop = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(yki->public_keys), &iter);
i = 0;
while ( loop ) {
gboolean selected;
gpointer key;
gtk_tree_model_get(GTK_TREE_MODEL(yki->public_keys), &iter, 0, &selected, 2, &key, -1);
if ( selected )
keys[i++] = (gpgme_key_t)key;
loop = gtk_tree_model_iter_next(GTK_TREE_MODEL(yki->public_keys), &iter);
}
keys[i] = NULL;
return keys;
}
/* Actual encryption or sign operation. */
#define yki_can_encrypt(yki) ((yki).selected_keys || (yki).symmetric || (yki).sign)
static int
do_encrypt(yki_encrypt_data_t *yki, gpgme_data_t in, gpgme_data_t out, GError **error)
{
gpgme_error_t gerr = 0;
if ( yki->symmetric || yki->selected_keys ) {
gpgme_key_t *keys = get_selected_keys(yki);
gpgme_encrypt_flags_t flags = yki->symmetric ? GPGME_ENCRYPT_SYMMETRIC : 0;
if ( yki->sign )
gerr = gpgme_op_encrypt_sign(yki->gpgme, keys, flags, in, out);
else
gerr = gpgme_op_encrypt(yki->gpgme, keys, flags, in, out);
g_free(keys);
}
else if ( yki->sign ) {
gpgme_sig_mode_t mode = yki->detached ? GPGME_SIG_MODE_DETACH : GPGME_SIG_MODE_NORMAL;
gerr = gpgme_op_sign(yki->gpgme, in, out, mode);
}
else
g_assert_not_reached();
if ( gerr )
yki_error_gpgme(error, YKI_ERROR_GPGME_CRYPTO, gerr);
return gpgme_err_code(gerr) == 0;
}
/* Public interface. */
int
yki_encrypt(gpgme_ctx_t gpgme, const char *file, GError **error)
{
yki_encrypt_data_t yki;
GtkDialog *dlg;
gpgutil_file_t in, out;
gchar *outname;
int ret;
g_assert(gpgme && file);
yki.gpgme = gpgme;
yki.selected_keys = 0;
yki.symmetric = yki.sign = yki.detached = FALSE;
in.buffer = out.buffer = NULL;
dlg = create_encrypt_dialog(&yki);
ret = populate_public_key_list(gpgme, yki.public_keys, error);
if ( ret && gtk_dialog_run(dlg) == GTK_RESPONSE_OK && yki_can_encrypt(yki) ) {
ret = gpgutil_open_file(file, "r", &in, error);
if ( ret ) {
outname = g_strdup_printf("%s.gpg", file);
ret = gpgutil_open_file(outname, "w", &out, error);
g_free(outname);
}
if ( ret )
ret = do_encrypt(&yki, in.buffer, out.buffer, error);
}
gpgutil_close_file(&in, NULL);
gpgutil_close_file(&out, NULL);
gtk_tree_model_foreach(GTK_TREE_MODEL(yki.public_keys), free_key_in_list, NULL);
gtk_widget_destroy(GTK_WIDGET(dlg));
return ret;
}