/* This file is part of GNUnet. (C) 2005, 2006 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 2, 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/plugins/fs/download.c * @brief code for downloading with gnunet-gtk * @author Christian Grothoff */ #include "platform.h" #include "fs.h" #include "search.h" #include "status.h" #include "meta.h" /* ****************** FSUI download events ****************** */ /** * We are iterating over the contents of a * directory. Add the list of entries to * the search page at the position indicated * by the download list. */ static int addFilesToDirectory (const GNUNET_ECRS_FileInfo * fi, const GNUNET_HashCode * key, int isRoot, void *closure) { DownloadList *list = closure; GtkTreeIter iter; GtkTreeIter child; int i; GtkTreePath *path; GtkTreeModel *model; if (isRoot == GNUNET_YES) return GNUNET_OK; if (!gtk_tree_row_reference_valid (list->searchViewRowReference)) return GNUNET_SYSERR; model = GTK_TREE_MODEL (list->searchList->tree); path = gtk_tree_row_reference_get_path (list->searchViewRowReference); if (path == NULL) { GNUNET_GE_BREAK (ectx, 0); return GNUNET_SYSERR; } gtk_tree_model_get_iter (model, &iter, path); gtk_tree_path_free (path); /* check for existing entry -- this function maybe called multiple times for the same directory entry */ for (i = gtk_tree_model_iter_n_children (model, &iter) - 1; i >= 0; i--) { if (TRUE == gtk_tree_model_iter_nth_child (model, &child, &iter, i)) { struct GNUNET_ECRS_URI *uri; uri = NULL; gtk_tree_model_get (model, &child, SEARCH_URI, &uri, -1); if ((uri != NULL) && (GNUNET_ECRS_uri_test_equal (uri, fi->uri))) return GNUNET_OK; } } gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); addEntryToSearchTree (list->searchList, list, fi, &child); return GNUNET_OK; } static void refreshDirectoryViewFromDisk (DownloadList * list) { unsigned long long size; const char *data; int fd; char *fn; struct GNUNET_MetaData *meta; struct stat buf; const char *f; if ((list->is_directory != GNUNET_YES) || (list->searchList == NULL) || (list->searchViewRowReference == NULL) || (!gtk_tree_row_reference_valid (list->searchViewRowReference))) return; if (0 != stat (list->filename, &buf)) return; if (S_ISDIR (buf.st_mode)) { fn = GNUNET_malloc (strlen (list->filename) + strlen (GNUNET_DIRECTORY_EXT) + 1); strcpy (fn, list->filename); if (fn[strlen (fn) - 1] == '/') fn[strlen (fn) - 1] = '\0'; strcat (fn, GNUNET_DIRECTORY_EXT); if (0 != stat (list->filename, &buf)) { GNUNET_free (fn); return; } f = fn; } else { fn = NULL; f = list->filename; } size = buf.st_size; if (size == 0) { GNUNET_free_non_null (fn); return; } fd = GNUNET_disk_file_open (ectx, f, O_RDONLY); if (fd == -1) { GNUNET_free_non_null (fn); return; } data = MMAP (NULL, size, PROT_READ, MAP_SHARED, fd, 0); if ((data == MAP_FAILED) || (data == NULL)) { GNUNET_GE_LOG_STRERROR_FILE (ectx, GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, "mmap", f); CLOSE (fd); GNUNET_free_non_null (fn); return; } GNUNET_free_non_null (fn); meta = NULL; GNUNET_ECRS_directory_list_contents (ectx, data, size, &meta, &addFilesToDirectory, list); MUNMAP ((void *) data, size); CLOSE (fd); if (meta != NULL) GNUNET_meta_data_destroy (meta); } /** * A download has been started. Add an entry * to the search tree view (if applicable) and * the download summary. */ DownloadList * fs_download_started (struct GNUNET_FSUI_DownloadList *fsui_dl, DownloadList * dl_parent, SearchList * sl_parent, unsigned long long total, unsigned int anonymityLevel, const GNUNET_ECRS_FileInfo * fi, const char *filename, unsigned long long completed, GNUNET_CronTime eta, GNUNET_FSUI_State state) { DownloadList *list; GtkTreeIter iter; GtkTreeIter piter; GtkTreePath *path; unsigned long long size; char *size_h; const char *sname; int progress; char *uri_name; gboolean valid; struct GNUNET_ECRS_URI *u; GtkTreeModel *model; /* setup visualization */ list = GNUNET_malloc (sizeof (DownloadList)); memset (list, 0, sizeof (DownloadList)); list->uri = GNUNET_ECRS_uri_duplicate (fi->uri); list->filename = GNUNET_strdup (filename); if ((dl_parent != NULL) && (NULL != (path = gtk_tree_row_reference_get_path (dl_parent->summaryViewRowReference)))) { valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (download_summary), &piter, path); if (valid) { gtk_tree_store_append (download_summary, &iter, &piter); } else { gtk_tree_store_append (download_summary, &iter, NULL); } gtk_tree_path_free (path); } else { gtk_tree_store_append (download_summary, &iter, NULL); } size = GNUNET_ECRS_uri_get_file_size (fi->uri); size_h = GNUNET_get_byte_size_as_fancy_string (size); sname = &filename[strlen (filename) - 1]; while ((sname > filename) && (sname[-1] != '/') && (sname[-1] != '\\')) sname--; if (size != 0) progress = completed * 100 / size; else progress = 100; uri_name = GNUNET_ECRS_uri_to_string (fi->uri); gtk_tree_store_set (download_summary, &iter, DOWNLOAD_FILENAME, filename, DOWNLOAD_SHORTNAME, sname, DOWNLOAD_SIZE, size, DOWNLOAD_HSIZE, size_h, DOWNLOAD_PROGRESS, progress, DOWNLOAD_URISTRING, uri_name, DOWNLOAD_INTERNAL, list, -1); GNUNET_free (uri_name); GNUNET_free (size_h); path = gtk_tree_model_get_path (GTK_TREE_MODEL (download_summary), &iter); list->summaryViewRowReference = gtk_tree_row_reference_new (GTK_TREE_MODEL (download_summary), path); gtk_tree_path_free (path); list->searchList = sl_parent; list->searchViewRowReference = NULL; if (sl_parent != NULL) { model = GTK_TREE_MODEL (sl_parent->tree); if (dl_parent != NULL) { /* have parent, must be download from directory inside of search */ /* FIXME: this requires GTK 2.8. Since it doesn't support Win9x, the quick solution is to #ifndef it */ #ifndef MINGW GNUNET_GE_BREAK (ectx, gtk_tree_row_reference_get_model (dl_parent->searchViewRowReference) == model); #endif path = gtk_tree_row_reference_get_path (dl_parent->searchViewRowReference); if (path != NULL) { valid = gtk_tree_model_get_iter (model, &piter, path); GNUNET_GE_BREAK (ectx, valid == TRUE); if (valid == TRUE) { valid = gtk_tree_model_iter_children (model, &iter, &piter); GNUNET_GE_BREAK (ectx, valid == TRUE); } } else { GNUNET_GE_BREAK (ectx, 0); valid = FALSE; } } else { /* no download-parent, must be top-level entry in search */ valid = gtk_tree_model_get_iter_first (model, &iter); GNUNET_GE_BREAK (ectx, valid == TRUE); } while (valid == TRUE) { /* find matching entry */ gtk_tree_model_get (model, &iter, SEARCH_URI, &u, -1); if (GNUNET_ECRS_uri_test_equal (u, fi->uri)) { path = gtk_tree_model_get_path (model, &iter); list->searchViewRowReference = gtk_tree_row_reference_new (model, path); gtk_tree_path_free (path); gtk_tree_store_set (sl_parent->tree, &iter, SEARCH_CELL_BG_COLOR, getColorCode (GNUNET_URITRACK_DOWNLOAD_STARTED), SEARCH_STATUS, getStatusName (GNUNET_URITRACK_DOWNLOAD_STARTED), -1); break; } valid = gtk_tree_model_iter_next (model, &iter); } if (valid == FALSE) { /* did not find matching entry in search list -- bug! Continue without adding to to search list! */ GNUNET_GE_BREAK (ectx, 0); list->searchList = NULL; } } list->fsui_list = fsui_dl; list->total = total; list->is_directory = GNUNET_meta_data_test_for_directory (fi->meta); list->has_terminated = ((state != GNUNET_FSUI_ACTIVE) && (state != GNUNET_FSUI_PENDING)); list->next = download_head; download_head = list; if ((list->is_directory == GNUNET_YES) && (completed != 0)) refreshDirectoryViewFromDisk (list); return list; } /** * The download has progressed. Update the * summary and the preview of the directory * contents in the search page (if applicable). */ void fs_download_update (DownloadList * list, unsigned long long completed, const char *data, unsigned int size) { GtkTreeIter iter; GtkTreePath *path; unsigned int val; struct GNUNET_MetaData *meta; path = gtk_tree_row_reference_get_path (list->summaryViewRowReference); if (path == NULL) { GNUNET_GE_BREAK (ectx, 0); return; } gtk_tree_model_get_iter (GTK_TREE_MODEL (download_summary), &iter, path); gtk_tree_path_free (path); if (list->total != 0) val = completed * 100 / list->total; else val = 100; gtk_tree_store_set (download_summary, &iter, DOWNLOAD_PROGRESS, val, -1); if ((list->is_directory == GNUNET_YES) && (list->searchList != NULL) && (list->searchViewRowReference != NULL)) { meta = NULL; GNUNET_ECRS_directory_list_contents (ectx, data, size, &meta, &addFilesToDirectory, list); if (meta != NULL) GNUNET_meta_data_destroy (meta); } } /** * A download has terminated successfully. Update summary and * possibly refresh directory listing. */ void fs_download_completed (DownloadList * downloadContext) { GtkTreeIter iter; GtkTreePath *path; if (downloadContext->searchViewRowReference != NULL) { path = gtk_tree_row_reference_get_path (downloadContext->searchViewRowReference); gtk_tree_model_get_iter (GTK_TREE_MODEL (downloadContext->searchList->tree), &iter, path); gtk_tree_path_free (path); gtk_tree_store_set (downloadContext->searchList->tree, &iter, SEARCH_CELL_BG_COLOR, getColorCode (GNUNET_URITRACK_DOWNLOAD_COMPLETED), SEARCH_STATUS, getStatusName (GNUNET_URITRACK_DOWNLOAD_COMPLETED), -1); } downloadContext->has_terminated = GNUNET_YES; refreshDirectoryViewFromDisk (downloadContext); } /** * A download has been aborted. Update summary and * possibly refresh directory listing. */ void fs_download_aborted (DownloadList * downloadContext) { GtkTreeIter iter; GtkTreePath *path; if (downloadContext->searchViewRowReference != NULL) { path = gtk_tree_row_reference_get_path (downloadContext->searchViewRowReference); gtk_tree_model_get_iter (GTK_TREE_MODEL (downloadContext->searchList->tree), &iter, path); gtk_tree_path_free (path); gtk_tree_store_set (downloadContext->searchList->tree, &iter, SEARCH_CELL_BG_COLOR, getColorCode (GNUNET_URITRACK_DOWNLOAD_ABORTED), SEARCH_STATUS, getStatusName (GNUNET_URITRACK_DOWNLOAD_ABORTED), -1); } downloadContext->has_terminated = GNUNET_YES; refreshDirectoryViewFromDisk (downloadContext); } /** * A download has been stopped. Remove from summary * and free associated resources. */ void fs_download_stopped (DownloadList * list) { GtkTreeIter iter; GtkTreeIter piter; GtkTreePath *path; DownloadList *prev; int valid; GtkTreeModel *model; path = gtk_tree_row_reference_get_path (list->summaryViewRowReference); if (path == NULL) { GNUNET_GE_BREAK (ectx, 0); } else { gtk_tree_model_get_iter (GTK_TREE_MODEL (download_summary), &iter, path); gtk_tree_path_free (path); gtk_tree_row_reference_free (list->summaryViewRowReference); list->summaryViewRowReference = NULL; gtk_tree_store_remove (download_summary, &iter); } GNUNET_free (list->filename); GNUNET_ECRS_uri_destroy (list->uri); /* if we have child-results in view, remove them! */ if (list->searchList != NULL) { path = gtk_tree_row_reference_get_path (list->searchViewRowReference); if (path == NULL) { GNUNET_GE_BREAK (ectx, 0); } else { model = GTK_TREE_MODEL (list->searchList->tree); gtk_tree_model_get_iter (model, &piter, path); gtk_tree_path_free (path); valid = gtk_tree_model_iter_children (model, &iter, &piter); while (TRUE == valid) valid = gtk_tree_store_remove (GTK_TREE_STORE (model), &iter); } } if (list->searchViewRowReference != NULL) { gtk_tree_row_reference_free (list->searchViewRowReference); list->searchViewRowReference = NULL; } if (download_head == list) { download_head = list->next; } else { prev = download_head; while ((prev != NULL) && (prev->next != list)) prev = prev->next; if (prev != NULL) prev->next = list->next; else GNUNET_GE_BREAK (ectx, 0); } GNUNET_free (list); } /* **************** user download events ******************** */ /** * Check if a download for the given filename is * already running. * * @return GNUNET_OK if no download is pending, GNUNET_SYSERR if * such a download is already active. */ static int check_pending (const char *filename, GtkTreeIter * parent) { GtkTreeModel *model; GtkTreeIter iter; char *name; model = GTK_TREE_MODEL (download_summary); if (gtk_tree_model_iter_children (model, &iter, parent)) { do { gtk_tree_model_get (model, &iter, DOWNLOAD_FILENAME, &name, -1); if ((name != NULL) && (0 == strcmp (name, filename))) { free (name); return GNUNET_SYSERR; } if (name != NULL) free (name); if (GNUNET_SYSERR == check_pending (filename, &iter)) return GNUNET_SYSERR; } while (gtk_tree_model_iter_next (model, &iter)); } return GNUNET_OK; } typedef struct { char *uri_name; struct GNUNET_ECRS_URI *idc_uri; struct GNUNET_MetaData *idc_meta; char *idc_final_download_destination; SearchList *searchContext; DownloadList *parentContext; unsigned int anonymity; int recursive; } SDC; static void * init_download_helper (void *cls) { SDC *sdc = cls; GNUNET_FSUI_download_start (ctx, sdc->anonymity, sdc->recursive, sdc->idc_uri, sdc->idc_meta, sdc->idc_final_download_destination, (sdc->searchContext != NULL) ? sdc->searchContext->fsui_list : NULL, (sdc->parentContext != NULL) ? sdc->parentContext->fsui_list : NULL); return NULL; } /** * The user clicked the download button. * Start the download of the selected entry. */ static void initiateDownload (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer unused) { SDC sdc; char *final_download_dir; GtkTreeIter iiter; char *tmp; char *cname; char *dname; GtkTreePath *dirTreePath; char *dirPath; unsigned int dirPathLen; char *idc_name; sdc.idc_uri = NULL; sdc.idc_meta = NULL; idc_name = NULL; sdc.searchContext = NULL; sdc.parentContext = NULL; gtk_tree_model_get (model, iter, SEARCH_NAME, &idc_name, SEARCH_URI, &sdc.idc_uri, SEARCH_META, &sdc.idc_meta, SEARCH_INTERNAL, &sdc.searchContext, SEARCH_INTERNAL_PARENT, &sdc.parentContext, -1); if ((sdc.idc_uri == NULL) || (!(GNUNET_ECRS_uri_test_chk (sdc.idc_uri) || GNUNET_ECRS_uri_test_loc (sdc.idc_uri)))) { GNUNET_GE_BREAK (ectx, 0); GNUNET_free_non_null (idc_name); return; } sdc.uri_name = GNUNET_ECRS_uri_to_string (sdc.idc_uri); if ((sdc.uri_name == NULL) || (strlen (sdc.uri_name) < strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_FILE_INFIX))) { GNUNET_GE_BREAK (ectx, 0); GNUNET_free_non_null (sdc.uri_name); GNUNET_free_non_null (idc_name); return; } /* reduce "//" to "/" */ if (idc_name != NULL) { while (strstr (idc_name, "//") != NULL) memcpy (strstr (idc_name, "//"), strstr (idc_name, "//") + 1, strlen (strstr (idc_name, "//"))); } /* if no name given or just "/", produce better name */ if ((idc_name == NULL) || (0 == strcmp ("/", idc_name))) { #ifdef WINDOWS char *filehash; GNUNET_GE_ASSERT (NULL, strlen (sdc.uri_name) > strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_FILE_INFIX)); GNUNET_free_non_null (idc_name); filehash = GNUNET_strdup (&sdc.uri_name[strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_FILE_INFIX)]); filehash[16] = 0; idc_name = GNUNET_strdup (filehash); GNUNET_free_non_null (filehash); #else GNUNET_GE_ASSERT (NULL, strlen (sdc.uri_name) > strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_FILE_INFIX)); GNUNET_free_non_null (idc_name); idc_name = GNUNET_strdup (&sdc.uri_name[strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_FILE_INFIX)]); #endif } /* dname = directory portion of idc_name */ cname = idc_name; dname = GNUNET_strdup (idc_name); cname = &dname[strlen (dname) - 1]; if (cname != dname) cname--; /* ignore tailing '/' */ while ((cname != dname) && (*cname != DIR_SEPARATOR)) cname--; if (*cname == DIR_SEPARATOR) { *cname = '\0'; GNUNET_free (idc_name); idc_name = GNUNET_strdup (cname + 1); } else { *cname = '\0'; } cname = NULL; GNUNET_GC_get_configuration_value_filename (cfg, "FS", "INCOMINGDIR", "$HOME/gnunet-downloads/", &final_download_dir); if (strlen (dname) > 0) { tmp = GNUNET_malloc (strlen (final_download_dir) + strlen (dname) + 2); strcpy (tmp, final_download_dir); if (tmp[strlen (tmp)] != DIR_SEPARATOR) strcat (tmp, DIR_SEPARATOR_STR); if (dname[0] == DIR_SEPARATOR) strcat (tmp, &dname[1]); else strcat (tmp, dname); GNUNET_free (final_download_dir); final_download_dir = tmp; } GNUNET_free (dname); dname = NULL; /* If file is inside a directory, get the full path */ dirTreePath = gtk_tree_path_copy (path); dirPath = GNUNET_malloc (1); dirPath[0] = '\0'; dirPathLen = 0; while (gtk_tree_path_get_depth (dirTreePath) > 1) { char *dirname; char *newPath; if (!gtk_tree_path_up (dirTreePath)) break; if (!gtk_tree_model_get_iter (model, &iiter, dirTreePath)) break; gtk_tree_model_get (model, &iiter, SEARCH_NAME, &dirname, -1); dirPathLen = strlen (dirPath) + strlen (dirname) + strlen (DIR_SEPARATOR_STR) + 1; newPath = GNUNET_malloc (dirPathLen + 1); strcpy (newPath, dirname); if (newPath[strlen (newPath) - 1] != DIR_SEPARATOR) strcat (newPath, DIR_SEPARATOR_STR); strcat (newPath, dirPath); GNUNET_free (dirPath); dirPath = newPath; free (dirname); } gtk_tree_path_free (dirTreePath); /* construct completed/directory/real-filename */ sdc.idc_final_download_destination = GNUNET_malloc (strlen (final_download_dir) + 2 + strlen (idc_name) + strlen (GNUNET_DIRECTORY_EXT) + strlen (dirPath)); strcpy (sdc.idc_final_download_destination, final_download_dir); if (sdc.idc_final_download_destination[strlen (sdc.idc_final_download_destination) - 1] != DIR_SEPARATOR) strcat (sdc.idc_final_download_destination, DIR_SEPARATOR_STR); strcat (sdc.idc_final_download_destination, dirPath); strcat (sdc.idc_final_download_destination, idc_name); sdc.anonymity = getSpinButtonValue (sdc.searchContext->searchXML, "downloadAnonymitySpinButton"); sdc.recursive = getToggleButtonValue (sdc.searchContext->searchXML, "downloadRecursiveCheckButton"); if (GNUNET_OK == check_pending (idc_name, NULL)) { GNUNET_GTK_add_log_entry (_("Downloading `%s'\n"), idc_name); GNUNET_GTK_run_with_save_calls (&init_download_helper, &sdc); } else { GNUNET_GTK_add_log_entry (_("ERROR: already downloading `%s'"), idc_name); } GNUNET_free (sdc.uri_name); GNUNET_free (dirPath); GNUNET_free (sdc.idc_final_download_destination); GNUNET_free_non_null (final_download_dir); GNUNET_free_non_null (idc_name); } /** * The download button in the search dialog was * clicked. GNUNET_ND_DOWNLOAD all selected entries. */ void on_downloadButton_clicked_fs (GtkWidget * treeview, GtkWidget * downloadButton) { GtkTreeSelection *selection; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); GNUNET_GTK_tree_selection_selected_foreach (selection, &initiateDownload, NULL); } /** * User used the URI download entry. Start download * that is NOT rooted within a search or directory. * * TODO: * - support for recursive downloads * - support for user-specified filename * - enable button only if valid URI is entered */ void on_statusDownloadURIEntry_editing_done_fs (GtkWidget * entry, GtkWidget * downloadButton) { const char *uris; char *urid; char *final_download_dir; const char *dname; SDC sdc; uris = gtk_entry_get_text (GTK_ENTRY (entry)); urid = GNUNET_strdup (uris); gtk_entry_set_text (GTK_ENTRY (entry), GNUNET_ECRS_URI_PREFIX); sdc.idc_uri = GNUNET_ECRS_string_to_uri (ectx, urid); if (sdc.idc_uri == NULL) { GNUNET_GTK_add_log_entry (_("Invalid URI `%s'"), urid); GNUNET_free (urid); return; } if (GNUNET_ECRS_uri_test_ksk (sdc.idc_uri)) { GNUNET_GTK_add_log_entry (_ ("Please use the search function for keyword (KSK) URIs!")); GNUNET_free (urid); GNUNET_ECRS_uri_destroy (sdc.idc_uri); return; } else if (GNUNET_ECRS_uri_test_loc (sdc.idc_uri)) { GNUNET_GTK_add_log_entry (_("Location URIs are not yet supported")); GNUNET_free (urid); GNUNET_ECRS_uri_destroy (sdc.idc_uri); return; } GNUNET_GC_get_configuration_value_filename (cfg, "FS", "INCOMINGDIR", "$HOME/gnunet-downloads/", &final_download_dir); GNUNET_disk_directory_create (ectx, final_download_dir); dname = &uris[strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_FILE_INFIX)]; sdc.idc_final_download_destination = GNUNET_malloc (strlen (final_download_dir) + strlen (dname) + 2); strcpy (sdc.idc_final_download_destination, final_download_dir); GNUNET_free (final_download_dir); if (sdc.idc_final_download_destination[strlen (sdc.idc_final_download_destination)] != DIR_SEPARATOR) strcat (sdc.idc_final_download_destination, DIR_SEPARATOR_STR); strcat (sdc.idc_final_download_destination, dname); GNUNET_GTK_add_log_entry (_("Downloading `%s'\n"), uris); sdc.idc_meta = GNUNET_meta_data_create (); sdc.anonymity = getSpinButtonValue (GNUNET_GTK_get_main_glade_XML (), "fsstatusAnonymitySpin"); sdc.recursive = GNUNET_NO; sdc.searchContext = NULL; sdc.parentContext = NULL; GNUNET_GTK_run_with_save_calls (&init_download_helper, &sdc); GNUNET_meta_data_destroy (sdc.idc_meta); GNUNET_free (sdc.idc_final_download_destination); GNUNET_free (urid); } struct FCBC { int (*method) (struct GNUNET_FSUI_DownloadList * list); struct GNUNET_FSUI_DownloadList *argument; }; static void * fsui_callback (void *cls) { struct FCBC *fcbc = cls; fcbc->method (fcbc->argument); return NULL; } static void clearCompletedDownloadCallback (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer unused) { DownloadList *dl; struct FCBC fcbc; GNUNET_GE_ASSERT (ectx, model == GTK_TREE_MODEL (download_summary)); gtk_tree_model_get (model, iter, DOWNLOAD_INTERNAL, &dl, -1); if ((FALSE == gtk_tree_model_iter_has_child (model, iter)) && (dl->has_terminated)) { fcbc.method = &GNUNET_FSUI_download_stop; fcbc.argument = dl->fsui_list; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); } } void on_clearCompletedDownloadsButton_clicked_fs (void *unused, GtkWidget * clearButton) { GNUNET_GTK_tree_model_foreach (GTK_TREE_MODEL (download_summary), &clearCompletedDownloadCallback, NULL); } static void fsuiCallDownloadCallback (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer fsui_call) { DownloadList *dl; struct FCBC fcbc; GNUNET_GE_ASSERT (ectx, model == GTK_TREE_MODEL (download_summary)); gtk_tree_model_get (model, iter, DOWNLOAD_INTERNAL, &dl, -1); fcbc.method = fsui_call; fcbc.argument = dl->fsui_list; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); } void on_abortDownloadButton_clicked_fs (void *unused, GtkWidget * abortButton) { GtkTreeSelection *selection; GtkWidget *downloadList; downloadList = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeDownloadsList"); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (downloadList)); GNUNET_GTK_tree_selection_selected_foreach (selection, &fsuiCallDownloadCallback, &GNUNET_FSUI_download_abort); GNUNET_GTK_tree_selection_selected_foreach (selection, &fsuiCallDownloadCallback, &GNUNET_FSUI_download_stop); } void on_stopDownloadButton_clicked_fs (void *unused, GtkWidget * stopButton) { GtkTreeSelection *selection; GtkWidget *downloadList; downloadList = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeDownloadsList"); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (downloadList)); GNUNET_GTK_tree_selection_selected_foreach (selection, &fsuiCallDownloadCallback, &GNUNET_FSUI_download_stop); } /* end of download.c */