1 /*      key_selection_dialog.c
2  *
3  *      Copyright 2011 Hans Alves <alves.h88@gmail.com>
4  *
5  *      This program is free software; you can redistribute it and/or modify
6  *      it under the terms of the GNU General Public License as published by
7  *      the Free Software Foundation; either version 2 of the License, or
8  *      (at your option) any later version.
9  *
10  *      This program is distributed in the hope that it will be useful,
11  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *      GNU General Public License for more details.
14  *
15  *      You should have received a copy of the GNU General Public License
16  *      along with this program; if not, write to the Free Software
17  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  *      MA 02110-1301, USA.
19  */
20 
21 /* something about premature optimization, it still needs to be done */
22 
23 #include "geanypg.h"
24 
25 enum
26 {
27     TOGGLE_COLUMN,
28     RECIPIENT_COLUMN,
29     KEYID_COLUMN,
30     N_COLUMNS
31 };
32 
33 typedef struct
34 {
35     GtkListStore * store;
36     gint column;
37 } listdata;
38 
geanypg_toggled_cb(GtkCellRendererToggle * cell_renderer,gchar * path,listdata * udata)39 static void geanypg_toggled_cb(GtkCellRendererToggle * cell_renderer,
40                         gchar * path,
41                         listdata * udata)
42 {
43     GtkTreeIter iter;
44     gboolean value;
45     if (!udata) return;
46     if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(udata->store), &iter, path))
47     {
48         gtk_tree_model_get(GTK_TREE_MODEL(udata->store), &iter, udata->column, &value, -1);
49         value = !value;
50         gtk_list_store_set(udata->store, &iter, udata->column, value, -1);
51     }
52 }
53 
geanypg_makelist(gpgme_key_t * key_array,unsigned long nkeys,int addnone)54 static GtkListStore * geanypg_makelist(gpgme_key_t * key_array, unsigned long nkeys, int addnone)
55 {
56     GtkTreeIter iter;
57     unsigned long idx;
58     char empty_string = '\0';
59     GtkListStore * list = gtk_list_store_new(N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING,  G_TYPE_STRING);
60     if (addnone)
61     {
62         gtk_list_store_append(list, &iter);
63         gtk_list_store_set(list, &iter,
64                            TOGGLE_COLUMN, FALSE,
65                            RECIPIENT_COLUMN, "None",
66                            KEYID_COLUMN, "",
67                            -1);
68     }
69     for (idx = 0; idx < nkeys; ++idx)
70     {
71         char * name = (key_array[idx]->uids && key_array[idx]->uids->name) ? key_array[idx]->uids->name : &empty_string;
72         char * email = (key_array[idx]->uids && key_array[idx]->uids->email) ? key_array[idx]->uids->email : &empty_string;
73         gchar * buffer = g_strdup_printf("%s    <%s>", name, email);
74         gtk_list_store_append(list, &iter);
75         gtk_list_store_set(list, &iter,
76                            TOGGLE_COLUMN, FALSE,
77                            RECIPIENT_COLUMN, buffer,
78                            KEYID_COLUMN, key_array[idx]->subkeys->keyid,
79                            -1);
80         g_free(buffer);
81     }
82     return list;
83 }
84 
geanypg_combobox(GtkListStore * list)85 static GtkWidget * geanypg_combobox(GtkListStore * list)
86 {
87     GtkWidget * combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list));
88     GtkCellRenderer * cell1 = gtk_cell_renderer_text_new();
89     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), cell1, FALSE);
90     gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combobox), cell1,
91                                   "text", RECIPIENT_COLUMN);
92     return combobox;
93 }
94 
geanypg_listview(GtkListStore * list,listdata * data)95 static GtkWidget * geanypg_listview(GtkListStore * list, listdata * data)
96 {
97     GtkTreeViewColumn * column;
98     GtkCellRenderer * togglerenderer, * textrenderer;
99     GtkWidget * listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list));
100     /* checkbox column */
101     togglerenderer = gtk_cell_renderer_toggle_new();
102     g_signal_connect(G_OBJECT(togglerenderer), "toggled", G_CALLBACK(geanypg_toggled_cb), NULL);
103     column = gtk_tree_view_column_new_with_attributes("?",
104                                                      togglerenderer,
105                                                      "active", TOGGLE_COLUMN,
106                                                      NULL);
107     gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column);
108     data->store = list;
109     data->column = TOGGLE_COLUMN;
110     g_signal_connect(G_OBJECT(togglerenderer), "toggled", G_CALLBACK(geanypg_toggled_cb), (gpointer) data);
111     /* recipient column */
112     textrenderer = gtk_cell_renderer_text_new();
113     column = gtk_tree_view_column_new_with_attributes("recipient",
114                                                       textrenderer,
115                                                       "text", RECIPIENT_COLUMN,
116                                                       NULL);
117     gtk_tree_view_column_set_resizable(column, TRUE);
118     gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column);
119     /* keyid column */
120     column = gtk_tree_view_column_new_with_attributes("keyid",
121                                                       textrenderer,
122                                                       "text", KEYID_COLUMN,
123                                                       NULL);
124     gtk_tree_view_column_set_resizable(column, TRUE);
125     gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column);
126     return listview;
127 }
128 
geanypg_encrypt_selection_dialog(encrypt_data * ed,gpgme_key_t ** selected,int * sign)129 int geanypg_encrypt_selection_dialog(encrypt_data * ed, gpgme_key_t ** selected, int * sign)
130 {
131     GtkWidget * dialog = gtk_dialog_new();
132     unsigned long idx, sidx, capacity;
133     int response;
134     GtkWidget * contentarea, * listview, * scrollwin, * combobox;
135     GtkTreeIter iter;
136     listdata data;
137     gboolean active;
138     GtkListStore * list;
139 
140     *sign = 0;
141 
142     list = geanypg_makelist(ed->key_array, ed->nkeys, 0);
143     listview = geanypg_listview(list, &data);
144     scrollwin = gtk_scrolled_window_new(NULL, NULL);
145     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollwin),
146                         listview);
147     gtk_widget_set_size_request(scrollwin, 500, 160);
148     combobox = geanypg_combobox(geanypg_makelist(ed->skey_array, ed->nskeys, 1));
149 
150 
151     contentarea = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
152     gtk_box_pack_start(GTK_BOX(contentarea), gtk_label_new(_("Please select any recipients")), FALSE, FALSE, 5);
153     gtk_box_pack_start(GTK_BOX(contentarea), scrollwin, TRUE, TRUE, 0);
154     gtk_box_pack_start(GTK_BOX(contentarea), gtk_label_new(_("Sign the message as:")), FALSE, FALSE, 5);
155     gtk_box_pack_start(GTK_BOX(contentarea), combobox, FALSE, FALSE, 0);
156 
157 
158     /* add ok and cancel buttons */
159     gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
160     gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
161 
162     gtk_window_set_title(GTK_WINDOW(dialog), _("Select recipients"));
163     gtk_widget_show_all(dialog);
164     /* make sure dialog is destroyed when user responds */
165     response = gtk_dialog_run(GTK_DIALOG(dialog));
166     if (response == GTK_RESPONSE_CANCEL)
167     {
168         gtk_widget_destroy(dialog);
169         return 0;
170     }
171     idx = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox));
172     if (idx && idx <= ed->nskeys)
173     {
174         *sign = 1;
175         gpgme_signers_add(ed->ctx, ed->skey_array[idx - 1]); /* -1 because the first option is `None' */
176     }
177     /* try to loop all the keys in the list
178      * if they are active (the user checked the checkbox in front of the key)
179      * add it to the selected array, finaly make sure that the array
180      * is NULL terminated */
181     if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list), &iter))
182     {
183         capacity = SIZE;
184         *selected = (gpgme_key_t*) malloc(SIZE * sizeof(gpgme_key_t));
185         idx = 0;
186         sidx = 0;
187         gtk_tree_model_get(GTK_TREE_MODEL(list), &iter, TOGGLE_COLUMN, &active, -1);
188         if (active)
189                 (*selected)[sidx++] = ed->key_array[idx];
190 
191         while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter))
192         {
193             ++idx;
194             gtk_tree_model_get(GTK_TREE_MODEL(list), &iter, TOGGLE_COLUMN, &active, -1);
195             if (active)
196                 (*selected)[sidx++] = ed->key_array[idx];
197             if (sidx >= capacity - 1)
198             {
199                 capacity += SIZE;
200                 *selected = (gpgme_key_t*) realloc(*selected, capacity * sizeof(gpgme_key_t));
201             }
202         }
203         (*selected)[sidx] = NULL;
204     }
205     else
206     {
207         gtk_widget_destroy(dialog);
208         return 0;
209     }
210 
211     gtk_widget_destroy(dialog);
212     return 1;
213 }
214 
geanypg_sign_selection_dialog(encrypt_data * ed)215 int geanypg_sign_selection_dialog(encrypt_data * ed)
216 {
217     GtkWidget * dialog = gtk_dialog_new();
218     unsigned long idx;
219     int response;
220     GtkWidget * contentarea = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
221     GtkWidget * combobox = geanypg_combobox(
222                                 geanypg_makelist(ed->skey_array, ed->nskeys, 0));
223 
224     gtk_box_pack_start(GTK_BOX(contentarea), gtk_label_new(_("Choose a key to sign with:")), FALSE, FALSE, 5);
225     gtk_box_pack_start(GTK_BOX(contentarea), combobox, TRUE, TRUE, 0);
226 
227     /* add ok and cancel buttons */
228     gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
229     gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
230 
231     gtk_widget_show_all(dialog);
232     gtk_window_set_title(GTK_WINDOW(dialog), _("Select signer"));
233     /* make sure dialog is destroyed when user responds */
234     response = gtk_dialog_run(GTK_DIALOG(dialog));
235     if (response == GTK_RESPONSE_CANCEL)
236     {
237         gtk_widget_destroy(dialog);
238         return 0;
239     }
240     idx = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox));
241     gpgme_signers_clear(ed->ctx);
242     if (idx < ed->nskeys)
243         gpgme_signers_add(ed->ctx, ed->skey_array[idx]);
244 
245     gtk_widget_destroy(dialog);
246     return 1;
247 }
248