anastasis-gtk_io.c (8435B)
1 /* 2 This file is part of anastasis-gtk. 3 Copyright (C) 2020 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 Anastasis is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with Anastasis; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file src/anastasis/anastasis-gtk_io.c 22 * @brief 23 * @author Christian Grothoff 24 */ 25 #include <gnunet/gnunet_util_lib.h> 26 #include "anastasis_gtk_util.h" 27 #include "anastasis-gtk_attributes.h" 28 #include "anastasis-gtk_dispatch.h" 29 #include "anastasis-gtk_helper.h" 30 #include <jansson.h> 31 32 33 /** 34 * Return a file filter matching the files we store states in. 35 * 36 * @return the filter, ownership is passed to the caller 37 */ 38 static GListModel * 39 state_filters (void) 40 { 41 GtkFileFilter *filter; 42 GListStore *filters; 43 44 filter = gtk_file_filter_new (); 45 gtk_file_filter_set_name (filter, 46 _ ("Anastasis state files")); 47 gtk_file_filter_add_pattern (filter, 48 "*.ana"); 49 filters = g_list_store_new (GTK_TYPE_FILE_FILTER); 50 g_list_store_append (filters, 51 filter); 52 g_object_unref (filter); 53 return G_LIST_MODEL (filters); 54 } 55 56 57 /** 58 * Function called once the user picked the state file to open. 59 * 60 * @param source the file dialog 61 * @param res the result of the user's choice 62 * @param user_data unused 63 */ 64 static void 65 open_state_ready_cb (GObject *source, 66 GAsyncResult *res, 67 gpointer user_data) 68 { 69 GFile *file; 70 char *filename; 71 72 (void) user_data; 73 file = gtk_file_dialog_open_finish (GTK_FILE_DIALOG (source), 74 res, 75 NULL); 76 if (NULL == file) 77 return; /* user aborted */ 78 filename = ANASTASIS_GTK_file_get_path_utf8 (file); 79 g_object_unref (file); 80 if (NULL == filename) 81 return; 82 AG_load (filename); 83 GNUNET_free (filename); 84 } 85 86 87 /** 88 * User clicked the "open" button. 89 * 90 * @param button the button 91 * @param user_data unused 92 */ 93 void 94 anastasis_gtk_open_state_clicked_cb (GtkButton *button, 95 gpointer user_data) 96 { 97 GtkFileDialog *dialog; 98 GListModel *filters; 99 100 (void) user_data; 101 dialog = gtk_file_dialog_new (); 102 gtk_file_dialog_set_title (dialog, 103 _ ("Open saved Anastasis state")); 104 filters = state_filters (); 105 gtk_file_dialog_set_filters (dialog, 106 filters); 107 g_object_unref (filters); 108 gtk_file_dialog_open (dialog, 109 GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))), 110 NULL, 111 &open_state_ready_cb, 112 NULL); 113 g_object_unref (dialog); 114 } 115 116 117 /** 118 * Serialize state of currently shown use attribute editing frame to JSON. 119 */ 120 static void 121 save_user_attributes_collecting (void) 122 { 123 json_t *ia; 124 125 ia = AG_collect_attributes (true); 126 if (NULL == ia) 127 { 128 GNUNET_break (0); 129 return; 130 } 131 GNUNET_break (0 == 132 json_object_set (AG_redux_state, 133 "identity_attributes", 134 json_object_get (ia, 135 "identity_attributes"))); 136 json_decref (ia); 137 } 138 139 140 /** 141 * Write the current state to @a filename, appending the ".ana" suffix if the 142 * user did not provide it. 143 * 144 * @param filename where to write the state to, freed by this function 145 */ 146 static void 147 write_state (char *filename) 148 { 149 { 150 const char *ana; 151 152 ana = strstr (filename, 153 ".ana"); 154 if ( (NULL == ana) || 155 (4 != strlen (ana)) ) 156 { 157 char *tmp; 158 159 GNUNET_asprintf (&tmp, 160 "%s.ana", 161 filename); 162 GNUNET_free (filename); 163 filename = tmp; 164 } 165 } 166 if (0 != 167 json_dump_file (AG_redux_state, 168 filename, 169 JSON_COMPACT)) 170 { 171 AG_error ("Failed to write state to `%s'\n", 172 filename); 173 } 174 GNUNET_free (filename); 175 } 176 177 178 /** 179 * The user answered the question whether they really want their secret written 180 * to disk in cleartext. 181 * 182 * @param source the warning dialog 183 * @param res the result of the user's choice 184 * @param user_data the file name to write to 185 */ 186 static void 187 cleartext_warning_response_cb (GObject *source, 188 GAsyncResult *res, 189 gpointer user_data) 190 { 191 char *filename = user_data; 192 193 if (0 != 194 gtk_alert_dialog_choose_finish (GTK_ALERT_DIALOG (source), 195 res, 196 NULL)) 197 { 198 /* user aborted */ 199 GNUNET_free (filename); 200 return; 201 } 202 write_state (filename); 203 } 204 205 206 /** 207 * Function called once the user picked the file to save the state in. 208 * 209 * @param source the file dialog 210 * @param res the result of the user's choice 211 * @param user_data unused 212 */ 213 static void 214 save_state_ready_cb (GObject *source, 215 GAsyncResult *res, 216 gpointer user_data) 217 { 218 static const struct DispatchItem save_state[] = { 219 { .state = "USER_ATTRIBUTES_COLLECTING", 220 .action = &save_user_attributes_collecting }, 221 { .state = NULL, 222 .action = NULL } 223 }; 224 GFile *file; 225 char *filename; 226 227 (void) user_data; 228 file = gtk_file_dialog_save_finish (GTK_FILE_DIALOG (source), 229 res, 230 NULL); 231 if (NULL == file) 232 return; /* user aborted */ 233 filename = ANASTASIS_GTK_file_get_path_utf8 (file); 234 g_object_unref (file); 235 if (NULL == filename) 236 return; 237 (void) AG_dispatch (save_state); 238 239 /* check if we should warn the user about writing 'core_secret' to disk */ 240 { 241 json_t *cs; 242 243 cs = json_object_get (AG_redux_state, 244 "core_secret"); 245 if ( (NULL != cs) && 246 (! json_is_null (cs)) ) 247 { 248 GtkAlertDialog *diag; 249 GtkRoot *toplevel; 250 const char *buttons[] = { 251 _ ("_OK"), 252 _ ("_Cancel"), 253 NULL 254 }; 255 256 toplevel = gtk_widget_get_root ( 257 GTK_WIDGET (GCG_get_main_window_object ( 258 "anastasis_gtk_main_window"))); 259 diag = gtk_alert_dialog_new ( 260 "%s", 261 _ ("This will write your secret to disk in cleartext!")); 262 gtk_alert_dialog_set_buttons (diag, 263 buttons); 264 gtk_alert_dialog_set_cancel_button (diag, 265 1); 266 gtk_alert_dialog_set_default_button (diag, 267 0); 268 gtk_alert_dialog_choose (diag, 269 GTK_WINDOW (toplevel), 270 NULL, 271 &cleartext_warning_response_cb, 272 filename); 273 g_object_unref (diag); 274 return; 275 } 276 } 277 278 /* all good, do writing! */ 279 write_state (filename); 280 } 281 282 283 /** 284 * User clicked the "save as" button. 285 * 286 * @param button the button 287 * @param user_data unused 288 */ 289 void 290 anastasis_gtk_main_window_save_as_button_clicked_cb (GtkButton *button, 291 gpointer user_data) 292 { 293 GtkFileDialog *dialog; 294 GListModel *filters; 295 296 (void) user_data; 297 dialog = gtk_file_dialog_new (); 298 gtk_file_dialog_set_title (dialog, 299 _ ("Save Anastasis state")); 300 filters = state_filters (); 301 gtk_file_dialog_set_filters (dialog, 302 filters); 303 g_object_unref (filters); 304 gtk_file_dialog_set_initial_name (dialog, 305 "untitled.ana"); 306 gtk_file_dialog_save (dialog, 307 GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))), 308 NULL, 309 &save_state_ready_cb, 310 NULL); 311 g_object_unref (dialog); 312 }