/* This file is part of GNUnet (C) 2012, 2013 Christian Grothoff (and other contributing authors) GNUnet 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, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/setup/gnunet-setup-gns.c * @author Christian Grothoff * @brief everything releated to the main GNS zone tree view */ #include "gnunet_gtk.h" #include "gnunet-setup-gns.h" #include "gnunet-setup-gns-edit.h" #include #include #include #include /** * Text we use for the 'name' entry for the user to select * for creating a new name. */ #define NEW_NAME_STR gettext_noop ("") /** * Text we use for the 'type' selection for the user to * select when adding a new record. */ #define NEW_RECORD_STR gettext_noop ("") /** * Text we use for the expiration to mean 'never expires'. */ #define EXPIRE_NEVER_STRING gettext_noop ("never") /** * Text we use for invalid values. */ #define EXPIRE_INVALID_STRING gettext_noop ("invalid") /** * Height and width of the QR code we display */ #define QRCODE_IMAGE_SIZE 64 /** * Columns in the gns model. */ enum GNSTreestoreColumn { /** * A gchararray with the value for the 'name' column. */ GNS_TREESTORE_COL_NAME = 0, /** * A gboolean, TRUE if the record is public, FALSE if it is private. */ GNS_TREESTORE_COL_IS_PUBLIC, /** * A guint with the record type (numeric value) */ GNS_TREESTORE_COL_RECORD_TYPE, /** * A gchararray with the record type (human-readable string) */ GNS_TREESTORE_COL_RECORD_TYPE_AS_STR, /** * A guint64 with the expiration time (relative or absolute) */ GNS_TREESTORE_COL_EXP_TIME, /** * A gboolean, TRUE if the expiration time is relative. */ GNS_TREESTORE_COL_EXP_TIME_IS_REL, /** * A gchararray with the expiration time as a human-readable string. */ GNS_TREESTORE_COL_EXP_TIME_AS_STR, /** * A gchararray with the value of the record as a human-readable string. */ GNS_TREESTORE_COL_VAL_AS_STR, /** * A gchararray with the background color to use for the value. */ GNS_TREESTORE_COL_VAL_COLOR, /** * A gboolean; TRUE if the 'name' column should be shown, * FALSE for the lines with the individual records under a name. */ GNS_TREESTORE_COL_NAME_IS_VISIBLE, /** * A gboolean, TRUE if this row is for editing a record, * FALSE if the 'public', 'type', 'expiration' and 'value' * columns should be hidden. */ GNS_TREESTORE_COL_IS_RECORD_ROW, /** * A gboolean, FALSE if this is one of our 'dummy' rows that * is used to create a new name or record, TRUE if this is * a normal row with either a name or a record. */ GNS_TREESTORE_COL_NOT_DUMMY_ROW, /** * A gchararray with the name of the color to use for the * expiration column. */ GNS_TREESTORE_COL_EXP_TIME_COLOR, /** * A gchararray with the name of the color to use for the * name column. */ GNS_TREESTORE_COL_NAME_COLOR, /** * A gboolean; TRUE if the 'type' column can still be changed; * FALSE once we have edited the value. */ GNS_TREESTORE_COL_TYPE_IS_EDITABLE, /** * A gboolean; TRUE if the value is a shadow record. */ GNS_TREESTORE_COL_IS_SHADOW, /** * A gboolean; TRUE if the name is editable (dummy line). */ GNS_TREESTORE_COL_NAME_IS_EDITABLE, /** * A guint; offset of this record in the array. */ GNS_TREESTORE_COL_RECORD_OFFSET }; /** * Columns in the gns type model. */ enum LIST_COLUMNS { /** * A guint */ GNS_TYPE_TO_NAME_LISTSTORE_COLUMN_TYPE = 0, /** * A gchararray */ GNS_TYPE_TO_NAME_LISTSTORE_COLUMN_TYPENAME }; /** * Closure for 'operation_done_cont'. */ struct OperationContext { /** * Kept in a DLL. */ struct OperationContext *next; /** * Kept in a DLL. */ struct OperationContext *prev; /** * Associated namestore operation. */ struct GNUNET_NAMESTORE_QueueEntry *qe; }; /** * Closure for 'merge_with_existing_records'. */ struct MoveOperationContext { /** * Kept in a DLL. */ struct MoveOperationContext *next; /** * Kept in a DLL. */ struct MoveOperationContext *prev; /** * Associated namestore operation. */ struct GNUNET_NAMESTORE_QueueEntry *qe; /** * Info from editing dialog. */ struct EditDialogContext *edc; /** * Private key of target zone. */ struct GNUNET_CRYPTO_EccPrivateKey *pk; /** * Data to free. */ void *data; /** * Record to merge. */ struct GNUNET_NAMESTORE_RecordData rd; }; /** * Information we keep per name. */ struct RecordInfo { /** * Name for this record. */ char *name; /** * Location of this record in the tree view. */ GtkTreeRowReference *rr; /** * Serialized records under this name. */ void *data; /** * Number of bytes in 'data'. */ size_t data_size; /** * Number of records serialized in 'data'. */ unsigned int rd_count; }; /** * Hash map from the H(name) in the zone to the 'struct RecordInfo' * for the respective entry in the tree view. */ static struct GNUNET_CONTAINER_MultiHashMap *n2r; /** * Our current zone monitor. */ static struct GNUNET_NAMESTORE_ZoneMonitor *zmon; /** * Head of linked list of active operations. */ static struct OperationContext *oc_head; /** * Tail of linked list of active operations. */ static struct OperationContext *oc_tail; /** * Head of linked list of active operations. */ static struct MoveOperationContext *moc_head; /** * Tail of linked list of active operations. */ static struct MoveOperationContext *moc_tail; /** * Handle to the namestore. */ static struct GNUNET_NAMESTORE_Handle *namestore; /** * Tree store with all of the values we're modifying for GNS. */ static GtkTreeStore *ts; /** * Tree model (same object as 'ts', just different type). */ static GtkTreeModel *tm; /** * The main tree view for 'gns' that shows the records. */ static GtkTreeView *tv; /** * Private key of the zone we are currently editing. */ static struct GNUNET_CRYPTO_EccPrivateKey *pkey; /** * Public key of the zone we are currently editing. */ static struct GNUNET_CRYPTO_EccPublicKey pubkey; /** * Pseudonym of the current zone we are editing. */ static char *current_pseudonym; /** * Pointer to name of the configuration option that gives the * zone key for the zone we are editing right now. */ static const char *current_zone_option; /** * Connection to identity service. */ static struct GNUNET_IDENTITY_Handle *identity; /** * Request for our ego. */ static struct GNUNET_IDENTITY_Operation *id_op; #if HAVE_QRENCODE_H #include #include /** * Create the QR code image for our zone. * * @param scale factor for scaling up the size of the image to create * @return NULL on error */ static GdkPixbuf * create_qrcode (unsigned int scale) { QRinput * qri; QRcode *qrc; char *str; const gchar *pseu; GtkEntry *entry; GdkPixbuf *pb; unsigned int x; unsigned int y; unsigned int off; guchar *pixels; int n_channels; int c; const char *dir; char *fn; unsigned int size; qri = QRinput_new2 (0, QR_ECLEVEL_Q); if (NULL == qri) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_new2"); return NULL; } entry = GTK_ENTRY (GNUNET_SETUP_get_object ("GNUNET_setup_gns_pseu_entry")); pseu = gtk_entry_get_text (GTK_ENTRY(entry)); GNUNET_asprintf (&str, "gnunet://gns/%s/%s\n", GNUNET_NAMESTORE_z2s (&pubkey), pseu); if (0 != QRinput_append (qri, QR_MODE_8, strlen (str), (unsigned char*) str)) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_append"); GNUNET_free (str); return NULL; } GNUNET_free (str); qrc = QRcode_encodeInput (qri); if (NULL == qrc) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRcode_encodeInput"); QRinput_free (qri); return NULL; } /* We use a trick to create a pixbuf in a way that works for both Gtk2 and Gtk3 by loading a dummy file from disk; all other methods are not portable to both Gtk2 and Gtk3. */ dir = GNUNET_GTK_get_data_dir (); GNUNET_asprintf (&fn, "%s%s", dir, "qr_dummy.png"); size = qrc->width * scale; size += 8 - (size % 8); pb = gdk_pixbuf_new_from_file_at_size (fn, size, size, NULL); GNUNET_free (fn); if (NULL == pb) { QRinput_free (qri); return NULL; } pixels = gdk_pixbuf_get_pixels (pb); n_channels = gdk_pixbuf_get_n_channels (pb); for (x=0;xwidth / size) + (y * qrc->width / size) * qrc->width; for (c = 0; c < n_channels; c++) pixels[(y * size + x) * n_channels + c] = (0 == (qrc->data[off] & 1)) ? 0xFF : 0; } QRcode_free (qrc); QRinput_free (qri); return pb; } /** * Create the QR code image for our zone. */ static void setup_qrcode () { GdkPixbuf *pb; GtkImage *image; pb = create_qrcode (2); if (NULL == pb) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to initialize QR-code pixbuf")); return; } image = GTK_IMAGE (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_image")); if (NULL == image) { GNUNET_break (0); return; } gtk_image_set_from_pixbuf (image, pb); g_object_unref (pb); } #endif /** * Function called upon completion of the qr-code 'save as' dialog. * * @param dialog the dialog * @param response_id reason for the dialog closing * @param user_data the 'GtkBuilder' we used to create the dialog */ void GNUNET_setup_qr_save_as_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data) { #if HAVE_QRENCODE_H GtkBuilder *builder = user_data; GdkPixbuf *pb; char *filename; if (GTK_RESPONSE_OK != response_id) { gtk_widget_destroy (GTK_WIDGET (dialog)); g_object_unref (G_OBJECT (builder)); return; } filename = GNUNET_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog)); pb = create_qrcode (8); if (NULL == pb) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to initialize QR-code pixbuf")); return; } gdk_pixbuf_save (pb, filename, "png", NULL, NULL); g_free (filename); g_object_unref (pb); gtk_widget_destroy (GTK_WIDGET (dialog)); g_object_unref (G_OBJECT (builder)); #else GNUNET_break (0); #endif } /** * User clicked on 'save as' to extract the QR code. Open 'save as' * dialog to get the desired filename and file type. */ void GNUNET_setup_gns_qr_saveas_button_clicked_cb (GtkButton *button, gpointer user_data) { GtkBuilder *builder; GtkWindow *dialog; const gchar *pseu; GtkEntry *entry; char *suggestion; entry = GTK_ENTRY (GNUNET_SETUP_get_object ("GNUNET_setup_gns_pseu_entry")); pseu = gtk_entry_get_text (GTK_ENTRY(entry)); builder = GNUNET_GTK_get_new_builder ("gnunet_setup_qr_save_as_dialog.glade", NULL); if (NULL == builder) { GNUNET_break (0); return; } GNUNET_asprintf (&suggestion, "%s.png", pseu); dialog = GTK_WINDOW (gtk_builder_get_object (builder, "GNUNET_setup_qr_save_as_dialog")); gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggestion); GNUNET_free (suggestion); gtk_window_present (dialog); } /** * Check if adding a record of the given type is Ok given the other * records already present for the given name. * * @param rd_count size of the 'rd' array * @param rd existing records * @param n_type new record to be added * @return GNUNET_OK if adding this record is OK, GNUNET_NO if not */ static int check_record_permitted (unsigned int rd_count, const struct GNUNET_NAMESTORE_RecordData *rd, gint n_type) { unsigned int i; for (i=0;iqe = NULL; GNUNET_CONTAINER_DLL_remove (oc_head, oc_tail, oc); if (GNUNET_SYSERR == success) { main_window = GTK_WINDOW (GNUNET_SETUP_get_object ("GNUNET_setup_dialog")); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Operation failed: `%s'\n"), emsg); dialog = gtk_message_dialog_new (main_window, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Operation failed: `%s'\n"), emsg); g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); gtk_widget_show_all (dialog); } GNUNET_free (oc); } /** * Release the record info. * * @param cls NULL * @param key unused * @param value a RecordInfo to release * @return GNUNET_OK (continue to iterate) */ static int release_ri (void *cls, const struct GNUNET_HashCode *key, void *value) { struct RecordInfo *ri = value; gtk_tree_row_reference_free (ri->rr); GNUNET_free_non_null (ri->data); GNUNET_free (ri->name); GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (n2r, key, ri)); GNUNET_free (ri); return GNUNET_OK; } /** * Clear all entries in the zone view and hide the tree view. */ static void clear_zone_view () { GNUNET_CONTAINER_multihashmap_iterate (n2r, &release_ri, NULL); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_zone_selection_hbuttonbox"))); gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_status_label"))); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_main_scrolledwindow"))); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_image"))); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_saveas_button"))); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_vseparator"))); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_pseu_hbox"))); gtk_tree_store_clear (ts); } /** * Load a particular zone into the main tree view. * * @param zonename name of the option in the configuration file * with the name of the file with the private key of the * zone to load */ static void load_zone (const char *zonename); /** * Display an error message for the user. * * @param title title of the error message * @param emsg error message to show */ static void show_error_message (const char *title, const char *emsg) { GtkWindow *main_window; GtkDialog *dialog; /* TODO: consider replacing with widget in the main window */ main_window = GTK_WINDOW (GNUNET_SETUP_get_object ("GNUNET_setup_dialog")); dialog = GTK_DIALOG(gtk_message_dialog_new (main_window, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("%s\n%s\n"), title, emsg)); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); gtk_widget_show_all (GTK_WIDGET(dialog)); } /** * Release resources of an edit dialog context. * * @param edc resources to free */ static void free_edit_dialog_context (struct EditDialogContext *edc) { g_free (edc->name); g_free (edc->n_value); g_free (edc->new_zone_option); GNUNET_free (edc); } /** * Process a record that was stored in the namestore. * * @param cls closure * @param rd_count number of entries in 'rd' array * @param rd array of records with data to store */ static void merge_with_existing_records (void *cls, unsigned int rd_count, const struct GNUNET_NAMESTORE_RecordData *rd) { struct MoveOperationContext *moc = cls; struct EditDialogContext *edc = moc->edc; struct GNUNET_NAMESTORE_RecordData rd_new[rd_count + 1]; struct OperationContext *oc; GNUNET_CONTAINER_DLL_remove (moc_head, moc_tail, moc); if (GNUNET_OK != check_record_permitted (rd_count, rd, edc->record_type)) { show_error_message (_("Record combination not permitted"), _("Given the existing records, adding a new record of this type is not allowed.")); GNUNET_free (moc->pk); GNUNET_free (moc->data); GNUNET_free (moc); free_edit_dialog_context (edc); return; } memcpy (rd_new, rd, rd_count * sizeof (struct GNUNET_NAMESTORE_RecordData)); rd_new[rd_count] = moc->rd; /* FIXME: sanity-check merge... */ oc = GNUNET_new (struct OperationContext); GNUNET_CONTAINER_DLL_insert (oc_head, oc_tail, oc); oc->qe = GNUNET_NAMESTORE_records_store (namestore, moc->pk, edc->name, rd_count + 1, rd_new, &operation_done_cont, oc); GNUNET_free (moc->pk); GNUNET_free (moc->data); GNUNET_free (moc); free_edit_dialog_context (edc); } /** * Process an encrypted that was stored in the namestore for * merging. * * @param cls closure * @param block block to decrypt */ static void decrypt_block_for_merge (void *cls, const struct GNUNET_NAMESTORE_Block *block) { struct MoveOperationContext *moc = cls; struct GNUNET_CRYPTO_EccPublicKey pubkey; GNUNET_CRYPTO_ecc_key_get_public (moc->pk, &pubkey); if (GNUNET_OK != GNUNET_NAMESTORE_block_decrypt (block, &pubkey, moc->edc->name, &merge_with_existing_records, moc)) { show_error_message (_("Failed to decode existing record in target zone"), _("Failed to add new record.")); GNUNET_free (moc->pk); GNUNET_free (moc->data); free_edit_dialog_context (moc->edc); GNUNET_free (moc); return; } } /** * The edit dialog completed; update the namestore and the * view based on the new values in 'edc'. * * @param edc editing context information * @param ret return code of the dialog */ static void edit_dialog_continuation (struct EditDialogContext *edc, GtkResponseType ret) { struct RecordInfo *ri = edc->ri; unsigned int rd_count = (NULL == ri) ? 0 : ri->rd_count; struct GNUNET_NAMESTORE_RecordData rd_old[rd_count]; struct GNUNET_NAMESTORE_RecordData rd; void *data; size_t data_size; struct OperationContext *oc; if ( (NULL != ri) && (GNUNET_OK != GNUNET_NAMESTORE_records_deserialize (ri->data_size, ri->data, ri->rd_count, rd_old)) ) { GNUNET_break (0); free_edit_dialog_context (edc); return; } if ( (GTK_RESPONSE_CANCEL == ret) || (GTK_RESPONSE_DELETE_EVENT == ret) ) { free_edit_dialog_context (edc); return; } if (GNUNET_OK != GNUNET_NAMESTORE_string_to_value (edc->record_type, edc->n_value, &data, &data_size)) { GNUNET_break (0); free_edit_dialog_context (edc); return; } if (edc->n_public) rd.flags = GNUNET_NAMESTORE_RF_NONE; else rd.flags = GNUNET_NAMESTORE_RF_PRIVATE; if (edc->n_is_shadow) rd.flags |= GNUNET_NAMESTORE_RF_SHADOW_RECORD; rd.record_type = edc->record_type; rd.expiration_time = edc->n_exp_time; if (edc->n_is_relative) rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION; rd.data_size = data_size; rd.data = data; switch (ret) { case GTK_RESPONSE_REJECT: /* code for 'delete' */ if (GNUNET_YES == edc->old_record_in_namestore) { /* remove item from tree view and namestore */ struct GNUNET_NAMESTORE_RecordData rd_new[rd_count - 1]; GNUNET_assert (NULL != ri); memcpy (rd_new, rd_old, (rd_count - 1) * sizeof (struct GNUNET_NAMESTORE_RecordData)); rd_new[edc->off] = rd_old[rd_count - 1]; oc = GNUNET_new (struct OperationContext); GNUNET_CONTAINER_DLL_insert (oc_head, oc_tail, oc); oc->qe = GNUNET_NAMESTORE_records_store (namestore, pkey, edc->name, rd_count + 1, rd_new, &operation_done_cont, oc); } else { /* invalid choice, 'delete' should have been hidden... */ GNUNET_break (0); } break; case GTK_RESPONSE_OK: /* update model */ if (0 == strcmp (edc->new_zone_option, current_zone_option)) { if (GNUNET_YES == edc->old_record_in_namestore) { struct GNUNET_NAMESTORE_RecordData rd_new[rd_count]; GNUNET_assert (NULL != ri); memcpy (rd_new, rd_old, rd_count * sizeof (struct GNUNET_NAMESTORE_RecordData)); rd_new[edc->off] = rd; oc = GNUNET_new (struct OperationContext); GNUNET_CONTAINER_DLL_insert (oc_head, oc_tail, oc); oc->qe = GNUNET_NAMESTORE_records_store (namestore, pkey, edc->name, rd_count, rd_new, &operation_done_cont, oc); } else { struct GNUNET_NAMESTORE_RecordData rd_new[rd_count + 1]; memcpy (rd_new, rd_old, rd_count * sizeof (struct GNUNET_NAMESTORE_RecordData)); rd_new[rd_count] = rd; oc = GNUNET_new (struct OperationContext); GNUNET_CONTAINER_DLL_insert (oc_head, oc_tail, oc); oc->qe = GNUNET_NAMESTORE_records_store (namestore, pkey, edc->name, rd_count + 1, rd_new, &operation_done_cont, oc); } } else { char *keyfile; struct GNUNET_CRYPTO_EccPrivateKey *pk; struct MoveOperationContext *moc; struct GNUNET_HashCode query; /* determine target zone */ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns", edc->new_zone_option, &keyfile)) { char *emsg; GNUNET_asprintf (&emsg, _("Option `%s' missing in section `%s'\n"), edc->new_zone_option, "gns"); show_error_message (_("Failed to access key for target zone"), emsg); GNUNET_free (emsg); } pk = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile); GNUNET_free (keyfile); if (NULL == pk) { show_error_message (_("Failed to load private key for target zone"), NULL); break; } GNUNET_NAMESTORE_query_from_private_key (pk, edc->name, &query); moc = GNUNET_new (struct MoveOperationContext); moc->data = data; moc->rd = rd; moc->edc = edc; moc->pk = pk; GNUNET_CONTAINER_DLL_insert (moc_head, moc_tail, moc); moc->qe = GNUNET_NAMESTORE_lookup_block (namestore, &query, &decrypt_block_for_merge, moc); /* zone changed, remove record from old zone, add to new zone! */ if (GNUNET_YES == edc->old_record_in_namestore) { /* remove item from tree view and namestore */ struct GNUNET_NAMESTORE_RecordData rd_new[rd_count - 1]; GNUNET_assert (NULL != ri); memcpy (rd_new, rd_old, (rd_count - 1) * sizeof (struct GNUNET_NAMESTORE_RecordData)); rd_new[edc->off] = rd_old[rd_count - 1]; oc = GNUNET_new (struct OperationContext); GNUNET_CONTAINER_DLL_insert (oc_head, oc_tail, oc); oc->qe = GNUNET_NAMESTORE_records_store (namestore, pkey, edc->name, rd_count - 1, rd_new, &operation_done_cont, oc); } return; } break; default: GNUNET_break (0); break; } GNUNET_free (data); free_edit_dialog_context (edc); } /** * Launch a record editing dialog. * * @param n_type type of the record to edit * @param name name of the record to edit * @param ri record information for this name (can be NULL * if this is the first record for the name) * @param off offset of the record being edited in the * ri's list; UINT_MAX if this is a new record */ static void launch_edit_dialog (gint n_type, const char *name, struct RecordInfo *ri, unsigned int off) { struct EditDialogContext *edc; edc = GNUNET_new (struct EditDialogContext); if ( (NULL != ri) && (off < ri->rd_count) ) { struct GNUNET_NAMESTORE_RecordData rd_old[ri->rd_count]; GNUNET_break (GNUNET_OK == GNUNET_NAMESTORE_records_deserialize (ri->data_size, ri->data, ri->rd_count, rd_old)); edc->n_value = GNUNET_NAMESTORE_value_to_string (n_type, rd_old[off].data, rd_old[off].data_size); edc->n_public = (0 == (rd_old[off].flags & GNUNET_NAMESTORE_RF_PRIVATE)); edc->n_is_relative = (0 != (rd_old[off].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION)); edc->n_is_shadow = (0 != (rd_old[off].flags & GNUNET_NAMESTORE_RF_SHADOW_RECORD)); edc->n_exp_time = rd_old[off].expiration_time; edc->old_record_in_namestore = GNUNET_YES; } else { edc->n_exp_time = 1000LL * (time (NULL) + 365 * 24 * 60 * 60); /* + 1y */ } edc->ri = ri; edc->off = off; edc->name = GNUNET_strdup (name); edc->new_zone_option = g_strdup (current_zone_option); edc->record_type = n_type; edc->cont = &edit_dialog_continuation; switch (n_type) { case GNUNET_DNSPARSER_TYPE_A: GNS_edit_dialog_a (edc); break; case GNUNET_DNSPARSER_TYPE_AAAA: GNS_edit_dialog_aaaa (edc); break; case GNUNET_DNSPARSER_TYPE_CNAME: GNS_edit_dialog_cname (edc); break; case GNUNET_NAMESTORE_TYPE_LEHO: GNS_edit_dialog_leho (edc); break; case GNUNET_NAMESTORE_TYPE_PKEY: GNS_edit_dialog_pkey (edc); break; case GNUNET_DNSPARSER_TYPE_PTR: GNS_edit_dialog_ptr (edc); break; case GNUNET_DNSPARSER_TYPE_MX: GNS_edit_dialog_mx (edc); break; case GNUNET_DNSPARSER_TYPE_NS: GNS_edit_dialog_ns (edc); break; case GNUNET_DNSPARSER_TYPE_SOA: GNS_edit_dialog_soa (edc); break; case GNUNET_DNSPARSER_TYPE_SRV: GNS_edit_dialog_srv (edc); break; case GNUNET_DNSPARSER_TYPE_TXT: GNS_edit_dialog_txt (edc); break; case GNUNET_NAMESTORE_TYPE_VPN: GNS_edit_dialog_vpn (edc); break; case GNUNET_DNSPARSER_TYPE_TLSA: default: GNUNET_break (0); edit_dialog_continuation (edc, GTK_RESPONSE_CANCEL); break; } } /** * Check if, with the given record info, we are allowed to add * another record of the given type. * * @param ri existing records * @param n_type type of the new record * @return GNUNET_OK if this is allowed */ static int check_permissions (struct RecordInfo *ri, guint n_type) { struct GNUNET_NAMESTORE_RecordData rd[ri->rd_count]; GNUNET_break (GNUNET_OK == GNUNET_NAMESTORE_records_deserialize (ri->data_size, ri->data, ri->rd_count, rd)); if (GNUNET_OK != check_record_permitted (ri->rd_count, rd, n_type)) { show_error_message (_("Record combination not permitted"), _("Given the existing records, adding a new record of this type is not allowed.\n" "CNAME and PKEY records cannot co-exist with other records.\n" "NS records in GNS can only co-exist with A and AAAA records.\n")); return GNUNET_NO; } return GNUNET_OK; } /** * User selected 'edit' in the popup menu. Edit the * selected row. * * @param widget the GtkTreeView * @param user_data main window builder */ void GNUNET_setup_gns_popup_edit_button_activate_cb (GtkWidget *widget, gpointer user_data) { GtkTreeSelection *sel; gint n_type; gchar *n_name; guint off; struct RecordInfo *ri; GtkTreeIter iter; struct GNUNET_HashCode name_hash; sel = gtk_tree_view_get_selection (tv); if (! gtk_tree_selection_get_selected (sel, NULL, &iter)) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No row selected\n"); return; } gtk_tree_model_get (tm, &iter, GNS_TREESTORE_COL_NAME, &n_name, GNS_TREESTORE_COL_RECORD_TYPE, &n_type, GNS_TREESTORE_COL_RECORD_OFFSET, &off, -1); if (NULL == n_name) { GNUNET_break (0); return; } GNUNET_CRYPTO_hash (n_name, strlen (n_name), &name_hash); ri = GNUNET_CONTAINER_multihashmap_get (n2r, &name_hash); if ( (NULL == ri) || (off >= ri->rd_count) ) { GNUNET_break (0); g_free (n_name); return; } launch_edit_dialog (n_type, n_name, ri, off); g_free (n_name); } /** * The user has selected a new record type. Update the * model and then start the 'edit' dialog. * * @param text renderer updated renderer * @param path_string the path identifying the edited cell * @param new_text updated text * @param user_data unused */ void GNUNET_setup_gns_type_cellrenderercombo_edited_cb (GtkCellRendererText *text, gchar *path_string, gchar *new_text, gpointer user_data) { GtkTreeIter it; guint type; char *name_str; struct GNUNET_HashCode name_hash; struct RecordInfo *ri; if (0 == strcmp (new_text, _(NEW_RECORD_STR))) { GNUNET_break (0); g_free (new_text); return; /* no record type was selected */ } type = GNUNET_NAMESTORE_typename_to_number (new_text); if (UINT32_MAX == type) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Invalid or unsupported record type `%s'\n", new_text); show_error_message (_("Unsupported record type"), new_text); g_free (new_text); return; } /* check if this is a new record */ gtk_tree_model_get_iter_from_string (tm, &it, path_string); gtk_tree_model_get (tm, &it, GNS_TREESTORE_COL_NAME, &name_str, -1); if (NULL == name_str) { GNUNET_break (0); g_free (new_text); return; } GNUNET_CRYPTO_hash (name_str, strlen (name_str), &name_hash); ri = GNUNET_CONTAINER_multihashmap_get (n2r, &name_hash); if ( (NULL == ri) || (GNUNET_OK == check_permissions (ri, type)) ) launch_edit_dialog (type, name_str, ri, UINT_MAX); g_free (name_str); g_free (new_text); } /** * The user has edited a 'name' cell. Update the model (and if needed * create another fresh line for additional records). * * @param renderer updated renderer * @param path the path identifying the edited cell * @param new_text the new name (not modified) * @param user_data unused */ void GNUNET_setup_gns_name_cellrenderertext_edited_cb (GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer user_data) { GtkTreeIter it; gboolean not_dummy; char *name; struct RecordInfo *ri; struct GNUNET_HashCode name_hash; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New text for `%s' is `%s'\n", path, new_text); if ((0 == strcmp (new_text, NEW_NAME_STR)) || (0 == strcmp (new_text, ""))) return; if ( (GNUNET_OK != GNUNET_DNSPARSER_check_label (new_text)) && (0 != strcmp (new_text, GNUNET_GNS_MASTERZONE_STR)) ) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Name `%s' invalid for GNS/DNS (too long for a DNS label?)\n"), new_text); gdk_beep (); return; } gtk_tree_model_get_iter_from_string (tm, &it, path); gtk_tree_model_get (tm, &it, GNS_TREESTORE_COL_NOT_DUMMY_ROW, ¬_dummy, GNS_TREESTORE_COL_NAME, &name, -1); GNUNET_break (! not_dummy); GNUNET_break (0 == strcmp (name, _(NEW_NAME_STR))); g_free (name); GNUNET_CRYPTO_hash (new_text, strlen (new_text), &name_hash); ri = GNUNET_CONTAINER_multihashmap_get (n2r, &name_hash); if (NULL != ri) { GtkTreeSelection *sel; GtkTreePath *sel_path; sel_path = gtk_tree_row_reference_get_path (ri->rr); sel = gtk_tree_view_get_selection(tv); gtk_tree_selection_select_path (sel, sel_path); gtk_tree_path_free (sel_path); return; } /* change dummy line to new name, then add new dummy */ gtk_tree_store_set (ts, &it, GNS_TREESTORE_COL_NAME, new_text, GNS_TREESTORE_COL_NAME_IS_VISIBLE, TRUE, GNS_TREESTORE_COL_RECORD_TYPE, 0, GNS_TREESTORE_COL_RECORD_TYPE_AS_STR, _(NEW_RECORD_STR), GNS_TREESTORE_COL_NOT_DUMMY_ROW, FALSE, GNS_TREESTORE_COL_IS_RECORD_ROW, TRUE, GNS_TREESTORE_COL_TYPE_IS_EDITABLE, TRUE, GNS_TREESTORE_COL_NAME_IS_EDITABLE, FALSE, -1); /* add a new dummy line */ gtk_tree_store_insert_with_values (ts, &it,NULL, 0, GNS_TREESTORE_COL_NAME, _(NEW_NAME_STR), GNS_TREESTORE_COL_NAME_IS_VISIBLE, TRUE, GNS_TREESTORE_COL_RECORD_TYPE, GNUNET_DNSPARSER_TYPE_A, GNS_TREESTORE_COL_NOT_DUMMY_ROW, FALSE, GNS_TREESTORE_COL_IS_RECORD_ROW, FALSE, GNS_TREESTORE_COL_TYPE_IS_EDITABLE, FALSE, GNS_TREESTORE_COL_NAME_IS_EDITABLE, TRUE, GNS_TREESTORE_COL_RECORD_OFFSET, UINT_MAX, -1); } /** * Create a context (popup) menu for the zone iteration treeview * (if applicable). * * @return TRUE if a menu was activated */ static gboolean create_popup_menu () { GtkTreeIter it; GtkMenu *popup; GtkTreeSelection *sel; gboolean name_vis; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Considering creating popup menu...\n"); sel = gtk_tree_view_get_selection (tv); if (! gtk_tree_selection_get_selected (sel, NULL, &it)) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No row selected\n"); return FALSE; } gtk_tree_model_get (tm, &it, GNS_TREESTORE_COL_NAME_IS_VISIBLE, &name_vis, -1); if (name_vis) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Selected row is not a record row\n"); return FALSE; } popup = GTK_MENU (GNUNET_SETUP_get_object ("GNUNET_setup_gns_edit_popup_menu")); gtk_widget_show_all (GTK_WIDGET (popup)); gtk_menu_popup (popup, NULL, NULL, NULL, NULL, 0, 0); return TRUE; } /** * The zone treeview pop up menu is supposed to be created. * (Note: this is not the only method that might need to be * written to handle events to create pop up menus; right-clicks * need to be managed separately). * * @param widget the widget * @param user_data unused * @return TRUE if a menu was activated */ gboolean GNUNET_setup_gns_main_treeview_popup_menu_cb (GtkWidget *widget, gpointer user_data) { return create_popup_menu (); } /** * A button was pressed in the GtkTreeView, check for right button and * if applicable create the popup menu. * * @param widget the GtkTreeView * @param event the event * @param user_data unused * @return TRUE if a menu was activated (event was handled) */ gboolean GNUNET_setup_gns_main_treeview_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { /* Check for right click*/ if (NULL == widget) return FALSE; if ( (GDK_BUTTON_PRESS == event->type) && (3 == event->button) ) return create_popup_menu (); return FALSE; } /** * User pushed a key in the GtkTreeView. Check for 'del' and if so, delete * the currently selected row. * * @param widget originating widget * @param event event information * @param user_data unused * @return TRUE if the key was processed ('del') */ gboolean GNUNET_setup_gns_main_treeview_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { GtkTreeIter iter; GtkTreeSelection *sel; int not_dummy; GtkTreeIter parent; char *name; struct GNUNET_HashCode name_hash; struct RecordInfo *ri; struct OperationContext *oc; /* Check for delete key */ if ( (GDK_KEY_PRESS != event->type) || (GDK_KEY_Delete != event->keyval) ) return FALSE; sel = gtk_tree_view_get_selection(tv); if (! gtk_tree_selection_get_selected (sel, NULL, &iter)) return TRUE; /* nothing selected */ gtk_tree_model_get (tm, &iter, GNS_TREESTORE_COL_NOT_DUMMY_ROW, ¬_dummy, GNS_TREESTORE_COL_NAME, &name, -1); if (GNUNET_NO == not_dummy) { g_free (name); return TRUE; /* do not delete the dummy line */ } GNUNET_CRYPTO_hash (name, strlen (name), &name_hash); ri = GNUNET_CONTAINER_multihashmap_get (n2r, &name_hash); GNUNET_assert (NULL != ri); if ( (gtk_tree_model_iter_parent (tm, &parent, &iter)) && (ri->rd_count > 0) ) { struct GNUNET_NAMESTORE_RecordData rd_old[ri->rd_count]; struct GNUNET_NAMESTORE_RecordData rd_new[ri->rd_count - 1]; struct GNUNET_NAMESTORE_RecordData rd; unsigned int off; int n_type; gboolean n_public; guint64 n_exp_time; gboolean n_is_relative; gboolean n_is_shadow; char *n_value; /* Removing a single record */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing single record for name `%s'\n", name); GNUNET_break (GNUNET_OK == GNUNET_NAMESTORE_records_deserialize (ri->data_size, ri->data, ri->rd_count, rd_old)); gtk_tree_model_get (tm, &iter, GNS_TREESTORE_COL_RECORD_TYPE, &n_type, GNS_TREESTORE_COL_IS_PUBLIC, &n_public, GNS_TREESTORE_COL_EXP_TIME, &n_exp_time, GNS_TREESTORE_COL_EXP_TIME_IS_REL, &n_is_relative, GNS_TREESTORE_COL_IS_SHADOW, &n_is_shadow, GNS_TREESTORE_COL_VAL_AS_STR, &n_value, -1); /* valid name */ if (n_public) rd.flags = GNUNET_NAMESTORE_RF_NONE; else rd.flags = GNUNET_NAMESTORE_RF_PRIVATE; if (n_is_relative) rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION; if (n_is_shadow) rd.flags |= GNUNET_NAMESTORE_RF_SHADOW_RECORD; rd.record_type = n_type; rd.expiration_time = n_exp_time; if (GNUNET_OK != GNUNET_NAMESTORE_string_to_value (n_type, n_value, (void**)&rd.data, &rd.data_size)) { /* can't remove, value invalid */ GNUNET_assert (0); g_free (n_value); g_free (name); return TRUE; } for (off=0;offrd_count;off++) if (GNUNET_YES == GNUNET_NAMESTORE_records_cmp (&rd, &rd_old[off])) break; GNUNET_assert (off != ri->rd_count); memcpy (rd_new, rd_old, (ri->rd_count - 1) * sizeof (struct GNUNET_NAMESTORE_RecordData)); rd_new[off] = rd_old[ri->rd_count - 1]; oc = GNUNET_new (struct OperationContext); GNUNET_CONTAINER_DLL_insert (oc_head, oc_tail, oc); oc->qe = GNUNET_NAMESTORE_records_store (namestore, pkey, name, ri->rd_count - 1, rd_new, &operation_done_cont, oc); g_free (n_value); } else { oc = GNUNET_new (struct OperationContext); GNUNET_CONTAINER_DLL_insert (oc_head, oc_tail, oc); oc->qe = GNUNET_NAMESTORE_records_store (namestore, pkey, name, 0, NULL, &operation_done_cont, oc); } g_free (name); return TRUE; } /** * The user edited the preferred name (PSEU) of this namespace. * Push the update to the namestore. * * @param editable the edited widget * @param user_data unused */ void GNUNET_setup_gns_pseu_entry_changed_cb (GtkEditable *editable, gpointer user_data) { const gchar *pseu; struct RecordInfo *ri; struct GNUNET_HashCode hc; unsigned int rd_count; struct OperationContext *oc; pseu = gtk_entry_get_text (GTK_ENTRY (editable)); if ( (NULL != current_pseudonym) && (0 == strcmp (pseu, current_pseudonym)) ) return; if (GNUNET_OK != GNUNET_DNSPARSER_check_label (pseu)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Name `%s' invalid for GNS (too long for a DNS label?)\n"), pseu); gdk_beep (); gtk_entry_set_text (GTK_ENTRY (editable), current_pseudonym); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New Pseudonym is `%s' %u\n", pseu); GNUNET_free_non_null (current_pseudonym); if ( (NULL != pseu) && (0 != strlen (pseu)) ) current_pseudonym = GNUNET_strdup (pseu); else current_pseudonym = NULL; GNUNET_CRYPTO_hash ("+", 1, &hc); ri = GNUNET_CONTAINER_multihashmap_get (n2r, &hc); if (NULL == ri) rd_count = 0; else rd_count = ri->rd_count; /* get old records, append new one or update/remove existing one */ { struct GNUNET_NAMESTORE_RecordData rd_old[rd_count]; struct GNUNET_NAMESTORE_RecordData rd_new[rd_count + 1]; unsigned int off; unsigned int total; total = rd_count; if (NULL != ri) GNUNET_break (GNUNET_OK == GNUNET_NAMESTORE_records_deserialize (ri->data_size, ri->data, rd_count, rd_old)); memcpy (rd_new, rd_old, sizeof (struct GNUNET_NAMESTORE_RecordData) * rd_count); for (off=0;offqe = GNUNET_NAMESTORE_records_store (namestore, pkey, GNUNET_GNS_MASTERZONE_STR, total, rd_new, &operation_done_cont, oc); } #if HAVE_QRENCODE_H setup_qrcode (); #endif } /** * The user clicked on the 'copy' button. Copy the full string * with the hash of our public key to the clipboard. * * @param button the button that was clicked * @param user_data unused */ void GNUNET_setup_gns_public_key_copy_button_clicked_cb (GtkButton *button, gpointer user_data) { GtkClipboard *cb; if (NULL == pkey) return; cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text (cb, GNUNET_NAMESTORE_z2s (&pubkey), -1); } /** * Function called once our model should be consistent with * the current zone. Makes the view visible. * * @param cls NULL */ static void zone_sync_proc (void *cls) { if (0 == strcmp (current_zone_option, "ZONEKEY")) gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_pseu_hbox"))); #if HAVE_QRENCODE_H setup_qrcode (); gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_image"))); gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_saveas_button"))); gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_vseparator"))); #else gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_image"))); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_saveas_button"))); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_qr_vseparator"))); #endif gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_status_label"))); gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_main_scrolledwindow"))); gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_zone_selection_hbuttonbox"))); } /** * Function called for each record in the current zone. Update the * widgets accordingly. * * @param cls NULL * @param zone_key private key of the zone * @param name name that is being mapped (at most 255 characters long) * @param rd_count number of entries in @a rd array * @param rd array of records with data to store */ static void zone_iteration_proc (void *cls, const struct GNUNET_CRYPTO_EccPrivateKey *zone_key, const char *name, unsigned int rd_count, const struct GNUNET_NAMESTORE_RecordData *rd) { GtkTreeRowReference *rr; GtkTreePath *path; GtkTreeIter iter_name; GtkTreeIter iter_record; int c; const char *exp; char *val; char *type_str; gboolean time_is_relative; gboolean public; guint64 exp_t; GtkEntry *pseu_entry; struct GNUNET_HashCode name_hash; struct RecordInfo *ri; GtkTreeSelection *sel; GtkTreeIter sel_iter; GtkTreePath *sel_path; if ((NULL == zone_key) && (NULL == name)) { /* disconnect from namestore, clear view */ clear_zone_view (); return; } if ( (GNUNET_SYSERR == GNUNET_DNSPARSER_check_label (name)) && (0 != strcmp (name, GNUNET_GNS_MASTERZONE_STR)) ) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Got invalid record name `%s' from namestore\n"), name); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Zone iteration result `%s', %u records\n", name, rd_count); GNUNET_CRYPTO_hash (name, strlen (name), &name_hash); sel = gtk_tree_view_get_selection (tv); if (gtk_tree_selection_get_selected (sel, NULL, &sel_iter)) sel_path = gtk_tree_model_get_path (tm, &sel_iter); else sel_path = NULL; ri = GNUNET_CONTAINER_multihashmap_get (n2r, &name_hash); if (NULL != ri) { rr = ri->rr; path = gtk_tree_row_reference_get_path (rr); GNUNET_assert (gtk_tree_model_get_iter (tm, &iter_name, path)); gtk_tree_path_free (path); /* remove all records, we'll re-add those that are left next */ if (gtk_tree_model_iter_children (tm, &iter_record, &iter_name)) while (gtk_tree_store_remove (ts, &iter_record)) ; } else { ri = GNUNET_new (struct RecordInfo); gtk_tree_store_append (ts, &iter_name, NULL); gtk_tree_store_set (ts, &iter_name, GNS_TREESTORE_COL_NAME, name, GNS_TREESTORE_COL_NAME_IS_VISIBLE, TRUE, GNS_TREESTORE_COL_RECORD_TYPE, GNUNET_NAMESTORE_TYPE_ANY, GNS_TREESTORE_COL_RECORD_TYPE_AS_STR, _(NEW_RECORD_STR), GNS_TREESTORE_COL_IS_RECORD_ROW, TRUE, GNS_TREESTORE_COL_NOT_DUMMY_ROW, FALSE, GNS_TREESTORE_COL_TYPE_IS_EDITABLE, TRUE, GNS_TREESTORE_COL_RECORD_OFFSET, UINT_MAX, -1); path = gtk_tree_model_get_path (tm, &iter_name); rr = gtk_tree_row_reference_new (tm, path); ri->rr = rr; ri->name = GNUNET_strdup (name); gtk_tree_path_free (path); GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (n2r, &name_hash, ri, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); } if (0 == rd_count) { /* all records removed, remove name as well */ gtk_tree_store_remove (ts, &iter_name); GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (n2r, &name_hash, ri)); gtk_tree_row_reference_free (rr); GNUNET_free (ri->name); GNUNET_free_non_null (ri->data); GNUNET_free (ri); return; } /* update record info serialized version of the records */ GNUNET_free_non_null (ri->data); ri->rd_count = rd_count; ri->data_size = GNUNET_NAMESTORE_records_get_size (rd_count, rd); if (0 != ri->data_size) ri->data = GNUNET_malloc (ri->data_size); GNUNET_break (ri->data_size == GNUNET_NAMESTORE_records_serialize (rd_count, rd, ri->data_size, ri->data)); /* Append elements for records in tree view */ for (c = 0; c < rd_count; c ++) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Record %u: type %u flags %u expiration %llu data_size %u\n", c, rd[c].record_type, rd[c].flags, rd[c].expiration_time, rd[c].data_size); /* Set public toggle */ public = ((rd[c].flags & GNUNET_NAMESTORE_RF_PRIVATE) != GNUNET_NAMESTORE_RF_PRIVATE); /* Expiration time */ time_is_relative = (0 != (rd[c].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION)); if (time_is_relative) { struct GNUNET_TIME_Relative rel_time; rel_time.rel_value_us = rd[c].expiration_time; exp_t = rel_time.rel_value_us; exp = GNUNET_STRINGS_relative_time_to_string (rel_time, GNUNET_NO); } else { struct GNUNET_TIME_Absolute exp_abs; exp_abs.abs_value_us = rd[c].expiration_time; exp_t = exp_abs.abs_value_us; exp = GNUNET_STRINGS_absolute_time_to_string (exp_abs); } /* value */ val = GNUNET_NAMESTORE_value_to_string (rd[c].record_type, rd[c].data, rd[c].data_size); if (NULL == val) GNUNET_asprintf (&val, "%s", EXPIRE_INVALID_STRING); if (NULL != GNUNET_NAMESTORE_number_to_typename (rd[c].record_type)) type_str = strdup (GNUNET_NAMESTORE_number_to_typename (rd[c].record_type)); else GNUNET_asprintf (&type_str, "%s", EXPIRE_INVALID_STRING); if ( (0 == strcmp (name, GNUNET_GNS_MASTERZONE_STR)) && (GNUNET_NAMESTORE_TYPE_PSEU == rd[c].record_type) && (NULL == current_pseudonym) ) { pseu_entry = GTK_ENTRY((GNUNET_SETUP_get_object ("GNUNET_setup_gns_pseu_entry"))); gtk_entry_set_text (pseu_entry, val); #if HAVE_QRENCODE_H setup_qrcode (); #endif } gtk_tree_store_insert_with_values (ts, &iter_record , &iter_name, 0, GNS_TREESTORE_COL_NAME, name, GNS_TREESTORE_COL_NAME_IS_VISIBLE, FALSE, GNS_TREESTORE_COL_RECORD_TYPE, rd[c].record_type, GNS_TREESTORE_COL_RECORD_TYPE_AS_STR, type_str, GNS_TREESTORE_COL_IS_PUBLIC, public, GNS_TREESTORE_COL_EXP_TIME, exp_t, GNS_TREESTORE_COL_EXP_TIME_AS_STR, exp, GNS_TREESTORE_COL_EXP_TIME_IS_REL, time_is_relative, GNS_TREESTORE_COL_VAL_AS_STR, val, GNS_TREESTORE_COL_IS_RECORD_ROW, TRUE, GNS_TREESTORE_COL_NOT_DUMMY_ROW, TRUE, GNS_TREESTORE_COL_RECORD_OFFSET, (guint) c, -1); GNUNET_free (type_str); GNUNET_free (val); } if (NULL != sel_path) { gtk_tree_selection_select_path (sel, sel_path); gtk_tree_view_expand_to_path (tv, sel_path); gtk_tree_path_free (sel_path); } } /** * Method called to inform about the egos of this peer. Called * when we are doing a #load_zone operation and are getting the * private key of the new zone to edit. * * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get, * this function is only called ONCE, and 'NULL' being passed in * @a ego does indicate an error (i.e. name is taken or no default * value is known). If @a ego is non-NULL and if '*ctx' * is set in those callbacks, the value WILL be passed to a subsequent * call to the identity callback of #GNUNET_IDENTITY_connect (if * that one was not NULL). * * @param cls closure with the 'const char *' zonename * @param ego ego handle * @param ctx context for application to store data for this ego * (during the lifetime of this process, initially NULL) * @param name name assigned by the user for this ego, * NULL if the user just deleted the ego and it * must thus no longer be used */ static void identity_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego, void **ctx, const char *name) { const char *zonename = cls; char *emsg; char *label; GtkTreeIter toplevel; id_op = NULL; /* setup crypto keys */ if (NULL == ego) { GNUNET_asprintf (&emsg, _("Default zone for `%s' not set; did you run gnunet-gns-import.sh?\n"), zonename); show_error_message (_("Failed to load zone"), emsg); GNUNET_free (emsg); gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_zone_selection_hbuttonbox"))); return; } pkey = GNUNET_new (struct GNUNET_CRYPTO_EccPrivateKey); *pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); GNUNET_CRYPTO_ecc_key_get_public (pkey, &pubkey); label = g_markup_printf_escaped (_("Editing zone %s"), GNUNET_NAMESTORE_z2s (&pubkey)); gtk_label_set_markup (GTK_LABEL (GNUNET_SETUP_get_object ("GNUNET_setup_gns_zone_label")), label); g_free (label); /* Append a top level row and leave it empty */ gtk_tree_store_insert_with_values (ts, &toplevel, NULL, 0, GNS_TREESTORE_COL_NAME, _(NEW_NAME_STR), GNS_TREESTORE_COL_NAME_IS_VISIBLE, TRUE, GNS_TREESTORE_COL_RECORD_TYPE, GNUNET_DNSPARSER_TYPE_A, GNS_TREESTORE_COL_IS_RECORD_ROW, FALSE, GNS_TREESTORE_COL_NOT_DUMMY_ROW, FALSE, GNS_TREESTORE_COL_TYPE_IS_EDITABLE, FALSE, GNS_TREESTORE_COL_NAME_IS_EDITABLE, TRUE, GNS_TREESTORE_COL_RECORD_OFFSET, UINT_MAX, -1); /* Load zone from namestore! */ #if HAVE_QRENCODE_H setup_qrcode (); #endif zmon = GNUNET_NAMESTORE_zone_monitor_start (cfg, pkey, &zone_iteration_proc, &zone_sync_proc, NULL); } /** * Load a particular zone into the main tree view. * * @param zonename name of the option in the configuration file * with the name of the file with the private key of the * zone to load */ static void load_zone (const char *zonename) { /* clear previous zone */ if (NULL != zmon) { GNUNET_NAMESTORE_zone_monitor_stop (zmon); zmon = NULL; } if (NULL != id_op) { GNUNET_IDENTITY_cancel (id_op); id_op = NULL; } clear_zone_view (); current_zone_option = zonename; GNUNET_free_non_null (pkey); pkey = NULL; id_op = GNUNET_IDENTITY_get (identity, zonename, &identity_cb, (void *) zonename); } /** * A different zone was selected in the zone toggle bar. Load the * appropriate zone. * * @param togglebutton button that was toggled (could be to "on" or "off", we only react to "on") * @param user_data builder, unused */ void GNUNET_setup_gns_shorten_zone_selection_radiobutton_toggled_cb (GtkToggleButton *togglebutton, gpointer user_data) { if (gtk_toggle_button_get_active (togglebutton)) load_zone ("short-zone"); } /** * A different zone was selected in the zone toggle bar. Load the * appropriate zone. * * @param togglebutton button that was toggled (could be to "on" or "off", we only react to "on") * @param user_data builder, unused */ void GNUNET_setup_gns_private_zone_selection_radiobutton_toggled_cb (GtkToggleButton *togglebutton, gpointer user_data) { if (gtk_toggle_button_get_active (togglebutton)) load_zone ("private-zone"); } /** * A different zone was selected in the zone toggle bar. Load the * appropriate zone. * * @param togglebutton button that was toggled (could be to "on" or "off", we only react to "on") * @param user_data builder, unused */ void GNUNET_setup_gns_master_zone_selection_radiobutton_toggled_cb (GtkToggleButton *togglebutton, gpointer user_data) { if (gtk_toggle_button_get_active (togglebutton)) load_zone ("master-zone"); } /** * Connect to the namestore and initialize the main * GNS tree view. */ void GNUNET_SETUP_gns_init () { gchar *label; GtkLabel *status_label; namestore = GNUNET_NAMESTORE_connect (cfg); if (NULL == namestore) { status_label = GTK_LABEL (GNUNET_SETUP_get_object ("GNUNET_setup_gns_status_label")); label = g_markup_printf_escaped (_("Failed to connect to namestore")); gtk_label_set_markup (status_label, label); g_free (label); return; } identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL); ts = GTK_TREE_STORE (GNUNET_SETUP_get_object ("GNUNET_setup_gns_treestore")); tv = GTK_TREE_VIEW (GNUNET_SETUP_get_object ("GNUNET_setup_gns_main_treeview")); tm = GTK_TREE_MODEL (ts); n2r = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO); load_zone ("master-zone"); } /** * Disconnect from the namestore and clean up the main * GNS tree view. */ void GNUNET_SETUP_gns_done () { struct OperationContext *oc; struct MoveOperationContext *moc; gtk_widget_show (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_status_label"))); gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_main_scrolledwindow"))); if (NULL != zmon) { GNUNET_NAMESTORE_zone_monitor_stop (zmon); zmon = NULL; } while (NULL != (oc = oc_head)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("A pending namestore operation was not transmitted to the namestore.\n")); GNUNET_CONTAINER_DLL_remove (oc_head, oc_tail, oc); GNUNET_NAMESTORE_cancel (oc->qe); GNUNET_free (oc); } while (NULL != (moc = moc_head)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("A pending namestore operation was not transmitted to the namestore.\n")); GNUNET_CONTAINER_DLL_remove (moc_head, moc_tail, moc); GNUNET_NAMESTORE_cancel (moc->qe); GNUNET_free (moc->pk); free_edit_dialog_context (moc->edc); GNUNET_free (moc->data); GNUNET_free (moc); } if (NULL != n2r) { GNUNET_CONTAINER_multihashmap_iterate (n2r, &release_ri, NULL); GNUNET_CONTAINER_multihashmap_destroy (n2r); n2r = NULL; } gtk_tree_store_clear (ts); if (NULL != namestore) { GNUNET_NAMESTORE_disconnect (namestore); namestore = NULL; } if (NULL != pkey) { GNUNET_free (pkey); pkey = NULL; } if (NULL != current_pseudonym) { GNUNET_free (current_pseudonym); current_pseudonym = NULL; } if (NULL != id_op) { GNUNET_IDENTITY_cancel (id_op); id_op = NULL; } if (NULL != identity) { GNUNET_IDENTITY_disconnect (identity); identity = NULL; } } /* end of gnunet-setup-gns.c */