|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
gboolean armored;
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
armored_toggled(GtkWidget *widget, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
((yki_encrypt_data_t *)data)->armored = 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 gboolean
|
|
|
|
|
public_key_search(GtkTreeModel *model,
|
|
|
|
|
gint column,
|
|
|
|
|
const gchar *pattern,
|
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
|
gpointer data)
|
|
|
|
|
{
|
|
|
|
|
gchar *uid;
|
|
|
|
|
|
|
|
|
|
gtk_tree_model_get(model, iter, 1, &uid, -1);
|
|
|
|
|
return g_strstr_len(uid, -1, pattern) ? FALSE : TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
g_signal_connect(gtk_builder_get_object(builder, "chkArmor"),
|
|
|
|
|
"clicked", G_CALLBACK(armored_toggled), yki);
|
|
|
|
|
|
|
|
|
|
widget = gtk_builder_get_object(builder, "trvPublicKeys");
|
|
|
|
|
yki->public_keys = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(widget)));
|
|
|
|
|
gtk_tree_view_set_search_column(GTK_TREE_VIEW(widget), 1);
|
|
|
|
|
gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(widget), public_key_search, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
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 = { 0 };
|
|
|
|
|
gpgutil_file_t in = { 0 }, out = { 0 };
|
|
|
|
|
GtkDialog *dlg;
|
|
|
|
|
gchar *outname;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
g_assert(gpgme && file);
|
|
|
|
|
|
|
|
|
|
yki.gpgme = gpgme;
|
|
|
|
|
|
|
|
|
|
dlg = create_encrypt_dialog(&yki);
|
|
|
|
|
ret = populate_public_key_list(gpgme, yki.public_keys, error);
|
|
|
|
|
set_default_output_filename(&yki, file);
|
|
|
|
|
|
|
|
|
|
if ( ret && gtk_dialog_run(dlg) == GTK_RESPONSE_OK && yki_can_encrypt(yki) ) {
|
|
|
|
|
gpgme_set_armor(gpgme, yki.armored);
|
|
|
|
|
|
|
|
|
|
ret = gpgutil_open_file(file, "r", &in, error);
|
|
|
|
|
|
|
|
|
|
if ( ret ) {
|
|
|
|
|
outname = g_strdup_printf("%s.%s", file, yki.armored ? "asc" : "gpg");
|
|
|
|
|
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;
|
|
|
|
|
}
|