aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/fs/download.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/fs/download.c')
-rw-r--r--src/plugins/fs/download.c705
1 files changed, 705 insertions, 0 deletions
diff --git a/src/plugins/fs/download.c b/src/plugins/fs/download.c
new file mode 100644
index 00000000..f425a3f7
--- /dev/null
+++ b/src/plugins/fs/download.c
@@ -0,0 +1,705 @@
1/*
2 This file is part of GNUnet.
3 (C) 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet 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 2, or (at your
8 option) any later version.
9
10 GNUnet 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 GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21
22/**
23 * @file src/plugins/fs/download.c
24 * @brief code for downloading with gnunet-gtk
25 * @author Christian Grothoff
26 */
27
28#include "fs.h"
29#include "search.h"
30#include "meta.h"
31#include "platform.h"
32
33/* ****************** FSUI download events ****************** */
34
35/**
36 * We are iterating over the contents of a
37 * directory. Add the list of entries to
38 * the search page at the position indicated
39 * by the download list.
40 */
41static int
42addFilesToDirectory(const ECRS_FileInfo * fi,
43 const HashCode512 * key,
44 int isRoot,
45 void * closure) {
46 DownloadList * list = closure;
47 GtkTreeIter iter;
48 GtkTreeIter child;
49 int i;
50 GtkTreePath * path;
51 GtkTreeModel * model;
52
53 if (isRoot == YES)
54 return OK;
55 if (! gtk_tree_row_reference_valid(list->searchViewRowReference))
56 return SYSERR;
57 model = GTK_TREE_MODEL(list->searchList->tree);
58 path = gtk_tree_row_reference_get_path(list->searchViewRowReference);
59 gtk_tree_model_get_iter(model,
60 &iter,
61 path);
62 gtk_tree_path_free(path);
63 /* check for existing entry -- this function
64 maybe called multiple times for the same
65 directory entry */
66 for (i=gtk_tree_model_iter_n_children(model,
67 &iter)-1;i>=0;i--) {
68 if (TRUE == gtk_tree_model_iter_nth_child(model,
69 &child,
70 &iter,
71 i)) {
72 struct ECRS_URI * uri;
73 uri = NULL;
74 gtk_tree_model_get(model,
75 &child,
76 SEARCH_URI, &uri,
77 -1);
78 if ( (uri != NULL) &&
79 (ECRS_equalsUri(uri,
80 fi->uri)) )
81 return OK;
82 }
83 }
84 gtk_tree_store_append(GTK_TREE_STORE(model),
85 &child,
86 &iter);
87 addEntryToSearchTree(list->searchList,
88 list,
89 fi,
90 &child);
91 return OK;
92}
93
94static void
95refreshDirectoryViewFromDisk(DownloadList * list) {
96 unsigned long long size;
97 char * data;
98 int fd;
99 struct ECRS_MetaData * meta;
100
101 if ( (list->is_directory != YES) ||
102 (list->searchList == NULL) ||
103 (list->searchViewRowReference == NULL) ||
104 (! gtk_tree_row_reference_valid(list->searchViewRowReference)) )
105 return;
106
107 if (OK != disk_file_size(ectx,
108 list->filename,
109 &size,
110 YES))
111 return;
112 fd = disk_file_open(ectx,
113 list->filename,
114 O_RDONLY);
115 if (fd == -1)
116 return;
117 data = MMAP(NULL,
118 size,
119 PROT_READ,
120 MAP_SHARED,
121 fd,
122 0);
123 if ( (data == MAP_FAILED) ||
124 (data == NULL) ) {
125 GE_LOG_STRERROR_FILE(ectx,
126 GE_ERROR | GE_ADMIN | GE_BULK,
127 "mmap",
128 list->filename);
129 CLOSE(fd);
130 return;
131 }
132 meta = NULL;
133 ECRS_listDirectory(ectx,
134 data,
135 size,
136 &meta,
137 &addFilesToDirectory,
138 list);
139 MUNMAP(data, size);
140 CLOSE(fd);
141 if (meta != NULL)
142 ECRS_freeMetaData(meta);
143}
144
145/**
146 * A download has been started. Add an entry
147 * to the search tree view (if applicable) and
148 * the download summary.
149 */
150DownloadList *
151fs_download_started(struct FSUI_DownloadList * fsui_dl,
152 DownloadList * dl_parent,
153 SearchList * sl_parent,
154 unsigned long long total,
155 unsigned int anonymityLevel,
156 const ECRS_FileInfo * fi,
157 const char * filename,
158 unsigned long long completed,
159 cron_t eta) {
160 DownloadList * list;
161 GtkTreeIter iter;
162 GtkTreePath * path;
163 unsigned long long size;
164 char * size_h;
165 const char * sname;
166 int progress;
167 char * uri_name;
168 gboolean valid;
169 struct ECRS_URI * u;
170
171 /* setup visualization */
172 list = MALLOC(sizeof(DownloadList));
173 memset(list,
174 0,
175 sizeof(DownloadList));
176 list->uri = ECRS_dupUri(fi->uri);
177 list->filename = STRDUP(filename);
178 /* FIXME: if we have dl_parent,
179 we may not want to just append! */
180 gtk_tree_store_append(download_summary,
181 &iter,
182 NULL);
183 size = ECRS_fileSize(fi->uri);
184 size_h = string_get_fancy_byte_size(size);
185 sname = &filename[strlen(filename)-1];
186 while ( (sname > filename) &&
187 (sname[-1] != '/') &&
188 (sname[-1] != '\\') )
189 sname--;
190 if (size != 0)
191 progress = completed * 100 / size;
192 else
193 progress = 100;
194 uri_name = ECRS_uriToString(fi->uri);
195 gtk_tree_store_set(download_summary,
196 &iter,
197 DOWNLOAD_FILENAME, filename,
198 DOWNLOAD_SHORTNAME, sname,
199 DOWNLOAD_SIZE, size,
200 DOWNLOAD_HSIZE, size_h,
201 DOWNLOAD_PROGRESS, progress,
202 DOWNLOAD_URISTRING, uri_name,
203 DOWNLOAD_INTERNAL, list,
204 -1);
205 FREE(uri_name);
206 FREE(size_h);
207 path = gtk_tree_model_get_path(GTK_TREE_MODEL(download_summary),
208 &iter);
209 list->summaryViewRowReference
210 = gtk_tree_row_reference_new(GTK_TREE_MODEL(download_summary),
211 path);
212 gtk_tree_path_free(path);
213 list->searchList = sl_parent;
214 if (sl_parent != NULL) {
215 if (dl_parent != NULL) {
216 /* have parent, must be download from
217 directory inside of search */
218 path = gtk_tree_row_reference_get_path(dl_parent->searchViewRowReference);
219 valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(sl_parent->tree),
220 &iter,
221 path);
222 } else {
223 /* must be top-level entry in search */
224 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(sl_parent->tree),
225 &iter);
226 }
227 if (valid == TRUE) {
228 valid = FALSE;
229 /* find matching entry */
230 do {
231 gtk_tree_model_get(GTK_TREE_MODEL(sl_parent->tree),
232 &iter,
233 SEARCH_URI, &u,
234 -1);
235 if (ECRS_equalsUri(u,
236 fi->uri)) {
237 valid = TRUE;
238 path = gtk_tree_model_get_path(GTK_TREE_MODEL(sl_parent->tree),
239 &iter);
240 list->searchViewRowReference
241 = gtk_tree_row_reference_new(GTK_TREE_MODEL(sl_parent->tree),
242 path);
243 gtk_tree_path_free(path);
244 /* TODO: extend search model with status;
245 start to indicate active download! */
246 break;
247 }
248 } while (TRUE == gtk_tree_model_iter_next(GTK_TREE_MODEL(sl_parent->tree),
249 &iter));
250 }
251 if (valid == FALSE) {
252 /* did not find matching entry in search list
253 -- bug! Continue without adding to to
254 search list! */
255 GE_BREAK(ectx, 0);
256 list->searchList = NULL;
257 }
258 }
259 list->fsui_list = fsui_dl;
260 list->total = total;
261 list->is_directory = ECRS_isDirectory(fi->meta);
262 list->next = download_head;
263 download_head = list;
264 if ( (list->is_directory == YES) &&
265 (completed != 0) )
266 refreshDirectoryViewFromDisk(list);
267 return list;
268}
269
270/**
271 * The download has progressed. Update the
272 * summary and the preview of the directory
273 * contents in the search page (if applicable).
274 */
275void fs_download_update(DownloadList * list,
276 unsigned long long completed,
277 const char * data,
278 unsigned int size) {
279 GtkTreeIter iter;
280 GtkTreePath * path;
281 unsigned int val;
282
283 path = gtk_tree_row_reference_get_path(list->summaryViewRowReference);
284 gtk_tree_model_get_iter(GTK_TREE_MODEL(download_summary),
285 &iter,
286 path);
287 gtk_tree_path_free(path);
288 if (list->total != 0)
289 val = completed * 100 / list->total;
290 else
291 val = 100;
292 gtk_tree_store_set(download_summary,
293 &iter,
294 DOWNLOAD_PROGRESS, val,
295 -1);
296 if ( (list->is_directory == YES) &&
297 (list->searchList != NULL) &&
298 (list->searchViewRowReference != NULL) ) {
299 struct ECRS_MetaData * meta;
300
301 meta = NULL;
302 ECRS_listDirectory(ectx,
303 data,
304 size,
305 &meta,
306 &addFilesToDirectory,
307 list);
308 if (meta != NULL)
309 ECRS_freeMetaData(meta);
310 }
311}
312
313/**
314 * A download has terminated successfully. Update summary and
315 * possibly refresh directory listing.
316 */
317void fs_download_completed(DownloadList * downloadContext) {
318 /* FIXME: update summary? / search list status entry (once added) */
319 downloadContext->has_terminated = YES;
320 refreshDirectoryViewFromDisk(downloadContext);
321}
322
323/**
324 * A download has been aborted. Update summary and
325 * possibly refresh directory listing.
326 */
327void fs_download_aborted(DownloadList * downloadContext) {
328 /* FIXME: update summary? / search list status entry (once added) */
329 downloadContext->has_terminated = YES;
330 refreshDirectoryViewFromDisk(downloadContext);
331}
332
333/**
334 * A download has been stopped. Remove from summary
335 * and free associated resources.
336 */
337void fs_download_stopped(DownloadList * list) {
338 GtkTreeIter iter;
339 GtkTreePath * path;
340 DownloadList * prev;
341
342 path = gtk_tree_row_reference_get_path(list->summaryViewRowReference);
343 gtk_tree_model_get_iter(GTK_TREE_MODEL(download_summary),
344 &iter,
345 path);
346 gtk_tree_path_free(path);
347 gtk_tree_row_reference_free(list->summaryViewRowReference);
348 list->summaryViewRowReference = NULL;
349 gtk_tree_store_remove(download_summary,
350 &iter);
351 if (list->searchViewRowReference != NULL) {
352 gtk_tree_row_reference_free(list->searchViewRowReference);
353 list->searchViewRowReference = NULL;
354 }
355 FREE(list->filename);
356 ECRS_freeUri(list->uri);
357
358 if (download_head == list)
359 download_head = list->next;
360 else {
361 prev = download_head;
362 while ( (prev != NULL) &&
363 (prev->next != list) )
364 prev = prev->next;
365 if (prev != NULL)
366 prev->next = list->next;
367 else
368 GE_BREAK(ectx, 0);
369 }
370 FREE(list);
371}
372
373
374/* **************** user download events ******************** */
375
376/**
377 * The user clicked the download button.
378 * Start the download of the selected entry.
379 */
380static void
381initiateDownload(GtkTreeModel * model,
382 GtkTreePath * path,
383 GtkTreeIter * iter,
384 gpointer unused) {
385 char * uri_name;
386 char * final_download_dir;
387 GtkTreeIter iiter;
388 const char * oname;
389 const char * cname;
390 char * dname;
391 GtkTreePath *dirTreePath;
392 char *dirPath;
393 unsigned int dirPathLen;
394 struct ECRS_URI * idc_uri;
395 struct ECRS_MetaData * idc_meta;
396 const char * idc_name;
397 const char * idc_mime;
398 char * idc_final_download_destination;
399 SearchList * searchContext;
400 DownloadList * parentContext;
401
402#ifdef WINDOWS
403 char *filehash = NULL;
404#endif
405
406 DEBUG_BEGIN();
407 idc_uri = NULL;
408 idc_meta = NULL;
409 idc_name = NULL;
410 idc_mime = NULL;
411 searchContext = NULL;
412 parentContext = NULL;
413 gtk_tree_model_get(model,
414 iter,
415 SEARCH_NAME, &idc_name,
416 SEARCH_URI, &idc_uri,
417 SEARCH_META, &idc_meta,
418 SEARCH_MIME, &idc_mime,
419 SEARCH_INTERNAL, &searchContext,
420 SEARCH_INTERNAL_PARENT, &parentContext,
421 -1);
422 if ( (idc_uri == NULL) ||
423 (! ECRS_isFileUri(idc_uri)) ) {
424 GE_BREAK(ectx, 0);
425 return;
426 }
427 uri_name = ECRS_uriToString(idc_uri);
428 if ( (uri_name == NULL) ||
429 (strlen(uri_name) <
430 strlen(ECRS_URI_PREFIX) +
431 strlen(ECRS_FILE_INFIX)) ) {
432 GE_BREAK(ectx, 0);
433 FREENONNULL(uri_name);
434 return;
435 }
436 if (idc_name == NULL) {
437#ifdef WINDOWS
438 filehash = STRDUP(uri_name);
439 filehash[16] = 0;
440 idc_name = filehash;
441#else
442 idc_name = uri_name;
443#endif
444 }
445 cname = idc_name;
446 oname = idc_name;
447 dname = MALLOC(strlen(idc_name)+1);
448 dname[0] = '\0';
449 while (*idc_name != '\0') {
450 if ( (*idc_name == DIR_SEPARATOR) &&
451 (idc_name[1] != '\0') ) {
452 memcpy(dname, oname, idc_name - oname);
453 dname[idc_name - oname] = '\0';
454 cname = &idc_name[1];
455 }
456 idc_name++;
457 }
458 if (*cname == '\0') /* name ended in '/' - likely directory */
459 cname = oname;
460 idc_name = cname;
461 GC_get_configuration_value_filename(cfg,
462 "FS",
463 "INCOMINGDIR",
464 "$HOME/gnunet-downloads/",
465 &final_download_dir);
466 if (strlen(dname) > 0) {
467 char * tmp;
468 tmp = MALLOC(strlen(final_download_dir) + strlen(dname) + 2);
469 strcpy(tmp, final_download_dir);
470 if (tmp[strlen(tmp)] != DIR_SEPARATOR)
471 strcat(tmp, DIR_SEPARATOR_STR);
472 if (dname[0] == DIR_SEPARATOR)
473 strcat(tmp, &dname[1]);
474 else
475 strcat(tmp, dname);
476 FREE(final_download_dir);
477 final_download_dir = tmp;
478 }
479 FREE(dname);
480 disk_directory_create(ectx, final_download_dir);
481
482
483 /* If file is inside a directory, get the full path */
484 dirTreePath = gtk_tree_path_copy(path);
485 dirPath = MALLOC(1);
486 dirPath[0] = '\0';
487 dirPathLen = 0;
488 while (gtk_tree_path_get_depth(dirTreePath) > 1) {
489 const char * dirname;
490 char * new;
491
492 if (! gtk_tree_path_up(dirTreePath))
493 break;
494
495 if (!gtk_tree_model_get_iter(model,
496 &iiter,
497 dirTreePath))
498 break;
499 gtk_tree_model_get(model,
500 &iiter,
501 SEARCH_NAME, &dirname,
502 -1);
503 dirPathLen = strlen(dirPath) + strlen(dirname) + strlen(DIR_SEPARATOR_STR) + 1;
504 new = MALLOC(dirPathLen + 1);
505 strcpy(new, dirname);
506 if (new[strlen(new)-1] != DIR_SEPARATOR)
507 strcat(new, DIR_SEPARATOR_STR);
508 strcat(new, dirPath);
509 FREE(dirPath);
510 dirPath = new;
511 }
512 gtk_tree_path_free(dirTreePath);
513
514
515 /* construct completed/directory/real-filename */
516 idc_final_download_destination = MALLOC(strlen(final_download_dir) + 2 +
517 strlen(idc_name) + strlen(GNUNET_DIRECTORY_EXT) +
518 strlen(dirPath));
519 strcpy(idc_final_download_destination, final_download_dir);
520 if (idc_final_download_destination[strlen(idc_final_download_destination)-1] != DIR_SEPARATOR)
521 strcat(idc_final_download_destination,
522 DIR_SEPARATOR_STR);
523 strcat(idc_final_download_destination, dirPath);
524 disk_directory_create(ectx,
525 idc_final_download_destination);
526 strcat(idc_final_download_destination, idc_name);
527 if ( (idc_final_download_destination[strlen(idc_final_download_destination) - 1] == '/') ||
528 (idc_final_download_destination[strlen(idc_final_download_destination) - 1] == '\\') )
529 idc_final_download_destination[strlen(idc_final_download_destination) - 1] = '\0';
530 /* append ".gnd" if needed (== directory and .gnd not present) */
531 if ( (idc_mime != NULL) &&
532 (0 == strcmp(idc_mime, GNUNET_DIRECTORY_MIME)) &&
533 ( (strlen(idc_final_download_destination) < strlen(GNUNET_DIRECTORY_EXT)) ||
534 (0 != strcmp(&idc_final_download_destination[strlen(idc_final_download_destination)
535 - strlen(GNUNET_DIRECTORY_EXT)],
536 GNUNET_DIRECTORY_EXT)) ) )
537 strcat(idc_final_download_destination, GNUNET_DIRECTORY_EXT);
538
539 addLogEntry(_("Downloading `%s'"), idc_name);
540 FSUI_startDownload(ctx,
541 0, /* FIXME: anonymity level */
542 NO, /* FIXME: isRecursive */
543 idc_uri,
544 idc_meta,
545 idc_final_download_destination,
546 searchContext->fsui_list,
547 (parentContext != NULL) ? parentContext->fsui_list : NULL);
548 FREE(uri_name);
549 FREE(dirPath);
550 FREENONNULL(final_download_dir);
551#ifdef WINDOWS
552 FREENONNULL(filehash);
553#endif
554}
555
556/**
557 * The download button in the search dialog was
558 * clicked. Download all selected entries.
559 */
560void on_downloadButton_clicked_fs(GtkWidget * treeview,
561 GtkWidget * downloadButton) {
562 GtkTreeSelection * selection;
563
564 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
565 gtk_tree_selection_selected_foreach
566 (selection,
567 &initiateDownload,
568 NULL);
569}
570
571
572/**
573 * User used the URI download entry. Start download
574 * that is NOT rooted within a search or directory.
575 *
576 * TODO:
577 * - support for recursive downloads
578 * - support for showing directories (if downloaded like this)
579 * - support for user-specified filename
580 */
581void on_statusDownloadURIEntry_editing_done_fs(GtkWidget * entry,
582 GtkWidget * downloadButton) {
583 struct ECRS_URI * idc_uri;
584 struct ECRS_MetaData * idc_meta;
585 char * idc_final_download_destination;
586 const char * uris;
587 char * urid;
588 char * final_download_dir;
589 const char * dname;
590
591 uris = gtk_entry_get_text(GTK_ENTRY(entry));
592 urid = STRDUP(uris);
593 gtk_entry_set_text(GTK_ENTRY(entry),
594 ECRS_URI_PREFIX);
595 idc_uri = ECRS_stringToUri(ectx, urid);
596 if (idc_uri == NULL) {
597 addLogEntry(_("Invalid URI `%s'"), urid);
598 FREE(urid);
599 return;
600 }
601 if (ECRS_isKeywordUri(idc_uri)) {
602 addLogEntry(_("Please use the search function for keyword (KSK) URIs!"));
603 FREE(urid);
604 ECRS_freeUri(idc_uri);
605 return;
606 } else if (ECRS_isLocationUri(idc_uri)) {
607 addLogEntry(_("Location URIs are not yet supported"));
608 FREE(urid);
609 ECRS_freeUri(idc_uri);
610 return;
611 }
612 GC_get_configuration_value_filename(cfg,
613 "FS",
614 "INCOMINGDIR",
615 "$HOME/gnunet-downloads/",
616 &final_download_dir);
617 disk_directory_create(ectx, final_download_dir);
618 dname = &uris[strlen(ECRS_URI_PREFIX) + strlen(ECRS_FILE_INFIX)];
619 idc_final_download_destination = MALLOC(strlen(final_download_dir) + strlen(dname) + 2);
620 strcpy(idc_final_download_destination, final_download_dir);
621 FREE(final_download_dir);
622 if (idc_final_download_destination[strlen(idc_final_download_destination)] != DIR_SEPARATOR)
623 strcat(idc_final_download_destination, DIR_SEPARATOR_STR);
624 strcat(idc_final_download_destination, dname);
625
626 addLogEntry(_("Downloading `%s'"), uris);
627 idc_meta = ECRS_createMetaData();
628 FSUI_startDownload(ctx,
629 getSpinButtonValue(getMainXML(),
630 "fsstatusAnonymitySpin"),
631 NO, /* FIXME: isRecursive */
632 idc_uri,
633 idc_meta,
634 idc_final_download_destination,
635 NULL,
636 NULL);
637 ECRS_freeMetaData(idc_meta);
638 FREE(urid);
639}
640
641
642static void
643clearCompletedDownloadCallback(GtkTreeModel * model,
644 GtkTreePath * path,
645 GtkTreeIter * iter,
646 gpointer unused) {
647 DownloadList * dl;
648
649 GE_ASSERT(ectx,
650 model == GTK_TREE_MODEL(download_summary));
651 gtk_tree_model_get(model,
652 iter,
653 DOWNLOAD_INTERNAL, &dl,
654 -1);
655 if (dl->has_terminated)
656 FSUI_stopDownload(ctx,
657 dl->fsui_list);
658}
659
660void on_clearCompletedDownloadsButton_clicked_fs(void * unused,
661 GtkWidget * clearButton) {
662 GtkTreeSelection * selection;
663 GtkWidget * downloadList;
664
665 downloadList = glade_xml_get_widget(getMainXML(),
666 "activeDownloadsList");
667 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(downloadList));
668 gtk_tree_selection_selected_foreach
669 (selection,
670 &clearCompletedDownloadCallback,
671 NULL);
672}
673
674static void
675abortDownloadCallback(GtkTreeModel * model,
676 GtkTreePath * path,
677 GtkTreeIter * iter,
678 gpointer unused) {
679 DownloadList * dl;
680
681 GE_ASSERT(ectx,
682 model == GTK_TREE_MODEL(download_summary));
683 gtk_tree_model_get(model,
684 iter,
685 DOWNLOAD_INTERNAL, &dl,
686 -1);
687 FSUI_abortDownload(ctx,
688 dl->fsui_list);
689}
690
691void on_abortDownloadButton_clicked_fs(void * unused,
692 GtkWidget * clearButton) {
693 GtkTreeSelection * selection;
694 GtkWidget * downloadList;
695
696 downloadList = glade_xml_get_widget(getMainXML(),
697 "activeDownloadsList");
698 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(downloadList));
699 gtk_tree_selection_selected_foreach
700 (selection,
701 &abortDownloadCallback,
702 NULL);
703}
704
705/* end of download.c */