-fixing #2139
[oweals/gnunet.git] / src / fs / gnunet-download.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 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 3, 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  * @file fs/gnunet-download.c
22  * @brief downloading for files on GNUnet
23  * @author Christian Grothoff
24  * @author Krista Bennett
25  * @author James Blackwell
26  * @author Igor Wronsky
27  */
28 #include "platform.h"
29 #include "gnunet_fs_service.h"
30
31 static int ret;
32
33 static int verbose;
34
35 static int delete_incomplete;
36
37 static const struct GNUNET_CONFIGURATION_Handle *cfg;
38
39 static struct GNUNET_FS_Handle *ctx;
40
41 static struct GNUNET_FS_DownloadContext *dc;
42
43 static unsigned int anonymity = 1;
44
45 static unsigned int parallelism = 16;
46
47 static unsigned int request_parallelism = 4092;
48
49 static int do_recursive;
50
51 static char *filename;
52
53 static int local_only;
54
55 static void
56 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
57 {
58   GNUNET_FS_stop (ctx);
59   ctx = NULL;
60 }
61
62
63 static void
64 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
65 {
66   struct GNUNET_FS_DownloadContext *d;
67
68   if (dc != NULL)
69   {
70     d = dc;
71     dc = NULL;
72     GNUNET_FS_download_stop (d, delete_incomplete);
73   }
74 }
75
76
77 /**
78  * Called by FS client to give information about the progress of an
79  * operation.
80  *
81  * @param cls closure
82  * @param info details about the event, specifying the event type
83  *        and various bits about the event
84  * @return client-context (for the next progress call
85  *         for this operation; should be set to NULL for
86  *         SUSPEND and STOPPED events).  The value returned
87  *         will be passed to future callbacks in the respective
88  *         field in the GNUNET_FS_ProgressInfo struct.
89  */
90 static void *
91 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
92 {
93   char *s, *s2;
94   char *t;
95
96   switch (info->status)
97   {
98   case GNUNET_FS_STATUS_DOWNLOAD_START:
99     if (verbose > 1)
100       FPRINTF (stderr, _("Starting download `%s'.\n"),
101                info->value.download.filename);
102     break;
103   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
104     if (verbose)
105     {
106       s = GNUNET_STRINGS_relative_time_to_string (info->value.download.eta);
107       if (info->value.download.specifics.progress.block_download_duration.rel_value 
108           == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
109         s2 = GNUNET_strdup (_("<unknown time>"));
110       else
111         s2 = GNUNET_STRINGS_relative_time_to_string (
112               info->value.download.specifics.progress.block_download_duration);
113       t = GNUNET_STRINGS_byte_size_fancy (info->value.download.completed *
114                                           1000LL /
115                                           (info->value.download.
116                                            duration.rel_value + 1));
117       FPRINTF (stdout,
118                _("Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"),
119                info->value.download.filename,
120                (unsigned long long) info->value.download.completed,
121                (unsigned long long) info->value.download.size, s, t, s2);
122       GNUNET_free (s);
123       GNUNET_free (s2);
124       GNUNET_free (t);
125     }
126     break;
127   case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
128     FPRINTF (stderr, _("Error downloading: %s.\n"),
129              info->value.download.specifics.error.message);
130     GNUNET_SCHEDULER_shutdown ();
131     break;
132   case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
133     s = GNUNET_STRINGS_byte_size_fancy (info->value.download.completed * 1000 /
134                                         (info->value.download.
135                                          duration.rel_value + 1));
136     FPRINTF (stdout, _("Downloading `%s' done (%s/s).\n"),
137              info->value.download.filename, s);
138     GNUNET_free (s);
139     if (info->value.download.dc == dc)
140       GNUNET_SCHEDULER_shutdown ();
141     break;
142   case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
143     if (info->value.download.dc == dc)
144       GNUNET_SCHEDULER_add_continuation (&cleanup_task, NULL,
145                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
146     break;
147   case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
148   case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
149     break;
150   default:
151     FPRINTF (stderr, _("Unexpected status: %d\n"), info->status);
152     break;
153   }
154   return NULL;
155 }
156
157
158 /**
159  * Main function that will be run by the scheduler.
160  *
161  * @param cls closure
162  * @param args remaining command-line arguments
163  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
164  * @param c configuration
165  */
166 static void
167 run (void *cls, char *const *args, const char *cfgfile,
168      const struct GNUNET_CONFIGURATION_Handle *c)
169 {
170   struct GNUNET_FS_Uri *uri;
171   char *emsg;
172   enum GNUNET_FS_DownloadOptions options;
173
174   if (NULL == args[0])
175   {
176     FPRINTF (stderr, "%s",  _("You need to specify a URI argument.\n"));
177     return;
178   }
179   uri = GNUNET_FS_uri_parse (args[0], &emsg);
180   if (NULL == uri)
181   {
182     FPRINTF (stderr, _("Failed to parse URI: %s\n"), emsg);
183     GNUNET_free (emsg);
184     ret = 1;
185     return;
186   }
187   if ((!GNUNET_FS_uri_test_chk (uri)) && (!GNUNET_FS_uri_test_loc (uri)))
188   {
189     FPRINTF (stderr, "%s",  _("Only CHK or LOC URIs supported.\n"));
190     ret = 1;
191     GNUNET_FS_uri_destroy (uri);
192     return;
193   }
194   if (NULL == filename)
195   {
196     FPRINTF (stderr, "%s",  _("Target filename must be specified.\n"));
197     ret = 1;
198     GNUNET_FS_uri_destroy (uri);
199     return;
200   }
201   cfg = c;
202   ctx =
203       GNUNET_FS_start (cfg, "gnunet-download", &progress_cb, NULL,
204                        GNUNET_FS_FLAGS_NONE,
205                        GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, parallelism,
206                        GNUNET_FS_OPTIONS_REQUEST_PARALLELISM,
207                        request_parallelism, GNUNET_FS_OPTIONS_END);
208   if (NULL == ctx)
209   {
210     FPRINTF (stderr, _("Could not initialize `%s' subsystem.\n"), "FS");
211     GNUNET_FS_uri_destroy (uri);
212     ret = 1;
213     return;
214   }
215   options = GNUNET_FS_DOWNLOAD_OPTION_NONE;
216   if (do_recursive)
217     options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
218   if (local_only)
219     options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY;
220   dc = GNUNET_FS_download_start (ctx, uri, NULL, filename, NULL, 0,
221                                  GNUNET_FS_uri_chk_get_file_size (uri),
222                                  anonymity, options, NULL, NULL);
223   GNUNET_FS_uri_destroy (uri);
224   if (dc == NULL)
225   {
226     GNUNET_FS_stop (ctx);
227     ctx = NULL;
228     return;
229   }
230   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
231                                 NULL);
232 }
233
234
235 /**
236  * The main function to download GNUnet.
237  *
238  * @param argc number of arguments from the command line
239  * @param argv command line arguments
240  * @return 0 ok, 1 on error
241  */
242 int
243 main (int argc, char *const *argv)
244 {
245   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
246     {'a', "anonymity", "LEVEL",
247      gettext_noop ("set the desired LEVEL of receiver-anonymity"),
248      1, &GNUNET_GETOPT_set_uint, &anonymity},
249     {'D', "delete-incomplete", NULL,
250      gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"),
251      0, &GNUNET_GETOPT_set_one, &delete_incomplete},
252     {'n', "no-network", NULL,
253      gettext_noop ("only search the local peer (no P2P network search)"),
254      0, &GNUNET_GETOPT_set_uint, &local_only},
255     {'o', "output", "FILENAME",
256      gettext_noop ("write the file to FILENAME"),
257      1, &GNUNET_GETOPT_set_string, &filename},
258     {'p', "parallelism", "DOWNLOADS",
259      gettext_noop
260      ("set the maximum number of parallel downloads that is allowed"),
261      1, &GNUNET_GETOPT_set_uint, &parallelism},
262     {'r', "request-parallelism", "REQUESTS",
263      gettext_noop
264      ("set the maximum number of parallel requests for blocks that is allowed"),
265      1, &GNUNET_GETOPT_set_uint, &request_parallelism},
266     {'R', "recursive", NULL,
267      gettext_noop ("download a GNUnet directory recursively"),
268      0, &GNUNET_GETOPT_set_one, &do_recursive},
269     {'V', "verbose", NULL,
270      gettext_noop ("be verbose (print progress information)"),
271      0, &GNUNET_GETOPT_increment_value, &verbose},
272     GNUNET_GETOPT_OPTION_END
273   };
274   return (GNUNET_OK ==
275           GNUNET_PROGRAM_run (argc, argv, "gnunet-download [OPTIONS] URI",
276                               gettext_noop
277                               ("Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"),
278                               options, &run, NULL)) ? ret : 1;
279 }
280
281 /* end of gnunet-download.c */