/* This file is part of GNUnet. (C) 2010, 2012 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/fs/gnunet-fs-gtk_common.c * @brief Common functions used in various places * @author Christian Grothoff */ #include "gnunet-fs-gtk_common.h" #include "gnunet-fs-gtk_download-save-as.h" #include "gnunet-fs-gtk.h" #include "gnunet-fs-gtk_event-handler.h" /** * Converts metadata specified by @a data of size @a data_len * and saved in format @a format to UTF-8 encoded string. * Works only for C-string and UTF8 metadata formats * (returns NULL for everything else). * Verifies UTF-8 strings. * * @param format format of the @a data * @param data data to convert * @param data_len length of the @a data buffer (in bytes) * @return NULL if can't be converted, allocated string otherwise, * freeable with #GNUNET_free. */ char * GNUNET_FS_GTK_dubious_meta_to_utf8 (enum EXTRACTOR_MetaFormat format, const char *data, size_t data_len) { switch (format) { case EXTRACTOR_METAFORMAT_UTF8: /* data must not contain NULLs (hence the -1) */ if (g_utf8_validate (data, data_len - 1, NULL)) return GNUNET_strdup (data); GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to validate supposedly utf-8 string `%s' of length %u, assuming it to be a C string\n"), data, (unsigned int) data_len); format = EXTRACTOR_METAFORMAT_C_STRING; /* fall-through */ case EXTRACTOR_METAFORMAT_C_STRING: if (data_len > 0) { /* There are no guarantees that data is NULL-terminated, AFAIU, * so let's play it safe, shall we? */ char data_copy[data_len + 1]; memcpy (data_copy, data, data_len); data_copy[data_len] = '\0'; return GNUNET_GTK_from_loc_to_utf8 (data_copy); } break; default: break; } return NULL; } /** * Add meta data to list store. * * @param cls closure (the GtkListStore) * @param plugin_name name of the plugin that produced this value; * special values can be used (i.e. '\' for zlib being * used in the main libextractor library and yielding * meta data). * @param type libextractor-type describing the meta data * @param format basic format information about data * @param data_mime_type mime-type of data (not of the original file); * can be NULL (if mime-type is not known) * @param data actual meta-data found * @param data_len number of bytes in @a data * @return 0 to continue (always) */ int GNUNET_FS_GTK_add_meta_data_to_list_store (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format, const char *data_mime_type, const char *data, size_t data_len) { GtkListStore *ls = GTK_LIST_STORE (cls); char *data_to_insert; data_to_insert = GNUNET_FS_GTK_dubious_meta_to_utf8 (format, data, data_len); if (NULL == data_to_insert) return 0; gtk_list_store_insert_with_values (ls, NULL, G_MAXINT, GNUNET_GTK_FS_MAIN_WINDOW_META_DATA_MC_META_TYPE, type, GNUNET_GTK_FS_MAIN_WINDOW_META_DATA_MC_META_FORMAT, format, GNUNET_GTK_FS_MAIN_WINDOW_META_DATA_MC_META_TYPE_STRING, EXTRACTOR_metatype_to_string (type), GNUNET_GTK_FS_MAIN_WINDOW_META_DATA_MC_META_VALUE, data_to_insert, -1); GNUNET_free (data_to_insert); return 0; } /** * Convert the year from the spin button to an expiration * time (on midnight, January 1st of that year). * * @param spin button with an expiration year * @return expiration time in the usual GNUnet format */ struct GNUNET_TIME_Absolute GNUNET_FS_GTK_get_expiration_time (GtkSpinButton * spin) { struct GNUNET_TIME_Absolute ret; int year; year = gtk_spin_button_get_value_as_int (spin); GNUNET_assert (year >= 0); ret = GNUNET_FS_year_to_time ((unsigned int) year); GNUNET_break (GNUNET_TIME_absolute_get ().abs_value_us < ret.abs_value_us); return ret; } /** * Initialize the 'expiration_year_adjustment' of the given * builder to have a lower range of current-year+1 and a * default of current-year+2. * * @param builder builder object for which we should manipulate * the adjustment */ void GNUNET_FS_GTK_setup_expiration_year_adjustment (GtkBuilder * builder) { GtkAdjustment *aj; unsigned int year; year = GNUNET_FS_get_current_year (); aj = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "expiration_year_adjustment")); gtk_adjustment_set_value (aj, year + 2); gtk_adjustment_set_lower (aj, year + 1); } /** * Obtain pixbuf from thumbnail data in meta data. * * @param meta input meta data * @return NULL on error, otherwise the embedded thumbnail */ GdkPixbuf * GNUNET_FS_GTK_get_thumbnail_from_meta_data (const struct GNUNET_CONTAINER_MetaData *meta) { GdkPixbuf *pixbuf; GdkPixbufLoader *loader; size_t ts; unsigned char *thumb; thumb = NULL; ts = GNUNET_CONTAINER_meta_data_get_thumbnail (meta, &thumb); if (0 == ts) return NULL; loader = gdk_pixbuf_loader_new (); gdk_pixbuf_loader_write (loader, (const guchar *) thumb, ts, NULL); pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); gdk_pixbuf_loader_close (loader, NULL); if (NULL != pixbuf) g_object_ref (pixbuf); g_object_unref (loader); GNUNET_free (thumb); return pixbuf; } /** * mmap the given file and run the #GNUNET_FS_directory_list_contents * function on it. * * @param filename name with the directory * @param dep function to call on each entry * @param dep_cls closure for @a dep * @return #GNUNET_OK on success */ int GNUNET_FS_GTK_mmap_and_scan (const char *filename, GNUNET_FS_DirectoryEntryProcessor dep, void *dep_cls) { struct GNUNET_DISK_FileHandle *fh; struct GNUNET_DISK_MapHandle *mh; uint64_t fsize; void *ddata; int ret; if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fsize, GNUNET_YES, GNUNET_YES)) { GNUNET_break (0); return GNUNET_SYSERR; } if (0 == fsize) { /* empty file, cannot be a directory */ return GNUNET_SYSERR; } if (NULL == (fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE))) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename); return GNUNET_SYSERR; } if (NULL == (ddata = GNUNET_DISK_file_map (fh, &mh, GNUNET_DISK_MAP_TYPE_READ, (size_t) fsize))) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mmap", filename); GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh)); return GNUNET_SYSERR; } if (GNUNET_SYSERR == GNUNET_FS_directory_list_contents ((size_t) fsize, ddata, 0, dep, dep_cls)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Selected file `%s' is not a GNUnet directory!\n"), filename); ret = GNUNET_SYSERR; } else { ret = GNUNET_OK; } GNUNET_break (GNUNET_OK == GNUNET_DISK_file_unmap (mh)); GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh)); return ret; } /** * Obtain the string we will use to describe a search result from * the respective meta data. * * @param meta meta data to inspect * @param is_a_dup is set to #GNUNET_YES if the result is a dup, and there was * no description to be found. #GNUNET_NO otherwise. * @return description of the result in utf-8, never NULL */ char * GNUNET_FS_GTK_get_description_from_metadata (const struct GNUNET_CONTAINER_MetaData *meta, int *is_a_dup) { char *desc; char *utf8_desc; desc = GNUNET_CONTAINER_meta_data_get_first_by_types (meta, EXTRACTOR_METATYPE_PACKAGE_NAME, EXTRACTOR_METATYPE_TITLE, EXTRACTOR_METATYPE_BOOK_TITLE, EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME, EXTRACTOR_METATYPE_FILENAME, EXTRACTOR_METATYPE_DESCRIPTION, EXTRACTOR_METATYPE_SUMMARY, EXTRACTOR_METATYPE_ALBUM, EXTRACTOR_METATYPE_COMMENT, EXTRACTOR_METATYPE_SUBJECT, EXTRACTOR_METATYPE_KEYWORDS, -1); if (desc == NULL) { *is_a_dup = GNUNET_YES; return GNUNET_strdup (_("no description supplied")); } utf8_desc = GNUNET_FS_GTK_dubious_meta_to_utf8 (EXTRACTOR_METAFORMAT_UTF8, desc, strlen (desc) + 1); GNUNET_free (desc); if (utf8_desc == NULL) { *is_a_dup = GNUNET_YES; return GNUNET_strdup (_("no description supplied")); } *is_a_dup = GNUNET_NO; return utf8_desc; } /** * A URI was selected (or pasted into the application). Run * the appropriate action. * * @param uri the URI * @param anonymity_level anonymity level to use */ void GNUNET_FS_GTK_handle_uri (const struct GNUNET_FS_Uri *uri, guint anonymity_level) { GtkTreeIter iter; GtkTreeModel *namespace_treestore; GtkTreeView *namespace_tree; gchar *value; GtkLabel *sel_namespace_label; GtkTreePath *treepath; GtkEntry *query_entry; struct GNUNET_CRYPTO_EccPublicKey *nsid; struct GNUNET_CRYPTO_EccPublicKey want; if (GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri)) { struct DownloadEntry *de; de = GNUNET_new (struct DownloadEntry); de->anonymity = anonymity_level; de->uri = GNUNET_FS_uri_dup (uri); GNUNET_FS_GTK_open_download_as_dialog (de); return; } query_entry = GTK_ENTRY (GNUNET_FS_GTK_get_main_window_object ("main_window_search_entry")); namespace_tree = GTK_TREE_VIEW (GNUNET_FS_GTK_get_main_window_object ("namespace_selector_treeview")); namespace_treestore = GTK_TREE_MODEL (GNUNET_FS_GTK_get_main_window_object ("main_window_search_namespace_treestore")); sel_namespace_label = GTK_LABEL (GNUNET_FS_GTK_get_main_window_object ("main_window_search_selected_namespace_label")); if (GNUNET_FS_uri_test_sks (uri)) { /* select the namespace */ if (GNUNET_OK != GNUNET_FS_uri_sks_get_namespace (uri, &want)) { GNUNET_break (0); return; } if (! gtk_tree_model_get_iter_first (namespace_treestore, &iter)) { GNUNET_break (0); return; } gtk_tree_model_get (namespace_treestore, &iter, GNUNET_GTK_FS_MAIN_WINDOW_SEARCH_NAMESPACE_MC_KEY, &nsid, -1); while ( ( (NULL == nsid) || (0 != memcmp (nsid, &want, sizeof (struct GNUNET_CRYPTO_EccPublicKey))) ) && (gtk_tree_model_iter_next (namespace_treestore, &iter)) ) gtk_tree_model_get (namespace_treestore, &iter, GNUNET_GTK_FS_MAIN_WINDOW_SEARCH_NAMESPACE_MC_KEY, &nsid, -1); if ( (NULL == nsid) || (0 != memcmp (nsid, &want, sizeof (struct GNUNET_CRYPTO_EccPublicKey))) ) { /* namespace unknown / not in list!? */ GNUNET_break (0); return; } gtk_tree_selection_select_iter (gtk_tree_view_get_selection (namespace_tree), &iter); treepath = gtk_tree_model_get_path (namespace_treestore, &iter); if (GNUNET_GTK_get_tree_string (namespace_tree, treepath, GNUNET_GTK_FS_MAIN_WINDOW_SEARCH_NAMESPACE_MC_NAME, &value)) gtk_label_set_text (sel_namespace_label, value); gtk_tree_path_free (treepath); /* set search entry to the namespace identifier */ { char *query_string; query_string = GNUNET_FS_uri_sks_get_content_id (uri); gtk_entry_set_text (query_entry, query_string); GNUNET_free (query_string); } return; } if (GNUNET_FS_uri_test_ksk (uri)) { /* select "no" namespace, which should be the first entry in the namespace */ if (gtk_tree_model_get_iter_first (namespace_treestore, &iter)) { gtk_tree_selection_select_iter (gtk_tree_view_get_selection (namespace_tree), &iter); treepath = gtk_tree_model_get_path (namespace_treestore, &iter); if (GNUNET_GTK_get_tree_string (namespace_tree, treepath, GNUNET_GTK_FS_MAIN_WINDOW_SEARCH_NAMESPACE_MC_NAME, &value)) gtk_label_set_text (sel_namespace_label, value); gtk_tree_path_free (treepath); } /* set search entry to the query string */ { char *query_string; query_string = GNUNET_FS_uri_ksk_to_string_fancy (uri); gtk_entry_set_text (query_entry, query_string); GNUNET_free (query_string); } return; } GNUNET_break (0); } /** * Converts a GtkTreeRowReference to a GtkTreeIter. * * @param rr row reference * @param iter pointer to an iter structure to fill * @return #GNUNET_OK if iter was filled, #GNUNET_SYSERR otherwise */ int GNUNET_GTK_get_iter_from_reference (GtkTreeRowReference *rr, GtkTreeIter *iter) { int result = GNUNET_SYSERR; if (rr != NULL) { if (gtk_tree_row_reference_valid (rr)) { GtkTreePath *path; GtkTreeModel *model; path = gtk_tree_row_reference_get_path (rr); model = gtk_tree_row_reference_get_model (rr); if (path != NULL && model != NULL) { if (gtk_tree_model_get_iter (model, iter, path)) result = GNUNET_OK; gtk_tree_path_free (path); } } } return result; } /** * Creates a GtkTreeRowReference from a GtkTreeIter. * * @param model a model to reference * @param iter an iter that points to a row in the model * @return newly created reference or NULL in case of error */ GtkTreeRowReference * GNUNET_GTK_get_reference_from_iter (GtkTreeModel *model, GtkTreeIter *iter) { GtkTreeRowReference *result = NULL; if (iter != NULL && model != NULL) { GtkTreePath *path = gtk_tree_model_get_path (model, iter); if (path != NULL) { result = gtk_tree_row_reference_new (model, path); gtk_tree_path_free (path); } } return result; } /** * Fills "next_iter" with iterator for an item that comes next in the tree * after "iter". * Next item is, in order of precedence: * 1) First child of "iter", if "iter" has children and "allow_children" * is enabled. * 2) Next sibling of "iter", unless "iter" is the last sibling. * If none of those are present, function recursively checks parents of * "iter" until it finds next item or runs out of parents. * * @param model a model to reference * @param iter an iter that points to current row in the model * @param allow_children whether child of "iter" is considered to be next. * @param next_iter will be filled with the next row in the model on success * @return TRUE if next_iter is set to a valid iter, * FALSE if ran out of parents */ gboolean GNUNET_GTK_tree_model_get_next_flat_iter (GtkTreeModel *model, GtkTreeIter *iter, gboolean allow_children, GtkTreeIter *next_iter) { GtkTreeIter current_iter = *iter; while (TRUE) { GtkTreeIter tmp_iter; tmp_iter = current_iter; if (gtk_tree_model_iter_next (model, &tmp_iter)) { *next_iter = tmp_iter; return TRUE; } if (allow_children) { if (gtk_tree_model_iter_children (model, &tmp_iter, ¤t_iter)) { *next_iter = tmp_iter; return TRUE; } } allow_children = FALSE; if (!gtk_tree_model_iter_parent (model, &tmp_iter, ¤t_iter)) return FALSE; current_iter = tmp_iter; } return FALSE; } /* end of gnunet-fs-gtk-common.c */