Merge branch 'master' of git+ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / fs / gnunet-download.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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
56 static void
57 cleanup_task (void *cls)
58 {
59   GNUNET_FS_stop (ctx);
60   ctx = NULL;
61 }
62
63
64 static void
65 shutdown_task (void *cls)
66 {
67   if (NULL != dc)
68   {
69     GNUNET_FS_download_stop (dc, delete_incomplete);
70     dc = NULL;
71   }
72 }
73
74
75 /**
76  * Display progress bar (if tty).
77  *
78  * @param x current position in the download
79  * @param n total size of the download
80  * @param w desired number of steps in the progress bar
81  */
82 static void
83 display_bar (unsigned long long x,
84              unsigned long long n,
85              unsigned int w)
86 {
87   char buf[w + 20];
88   unsigned int p;
89   unsigned int endeq;
90   float ratio_complete;
91
92 #if !WINDOWS
93   if (0 == isatty (1))
94     return;
95 #else
96   if (FILE_TYPE_CHAR != GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)))
97     return;
98 #endif
99   ratio_complete = x/(float)n;
100   endeq = ratio_complete * w;
101   GNUNET_snprintf (buf, sizeof (buf),
102                    "%3d%% [", (int)(ratio_complete*100) );
103   for (p=0; p<endeq; p++)
104     strcat (buf, "=");
105   for (p=endeq; p<w; p++)
106     strcat (buf, " ");
107   strcat (buf, "]\r");
108   printf ("%s", buf);
109   fflush(stdout);
110 }
111
112
113 /**
114  * Called by FS client to give information about the progress of an
115  * operation.
116  *
117  * @param cls closure
118  * @param info details about the event, specifying the event type
119  *        and various bits about the event
120  * @return client-context (for the next progress call
121  *         for this operation; should be set to NULL for
122  *         SUSPEND and STOPPED events).  The value returned
123  *         will be passed to future callbacks in the respective
124  *         field in the `struct GNUNET_FS_ProgressInfo`
125  */
126 static void *
127 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
128 {
129   char *s;
130   const char *s2;
131   char *t;
132
133   switch (info->status)
134   {
135   case GNUNET_FS_STATUS_DOWNLOAD_START:
136     if (verbose > 1)
137       FPRINTF (stderr, _("Starting download `%s'.\n"),
138                info->value.download.filename);
139     break;
140   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
141     if (verbose)
142     {
143       s = GNUNET_strdup (GNUNET_STRINGS_relative_time_to_string (info->value.download.eta,
144                                                                  GNUNET_YES));
145       if (info->value.download.specifics.progress.block_download_duration.rel_value_us
146           == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
147         s2 = _("<unknown time>");
148       else
149         s2 = GNUNET_STRINGS_relative_time_to_string (
150                                                      info->value.download.specifics.progress.block_download_duration,
151                                                      GNUNET_YES);
152       t = GNUNET_STRINGS_byte_size_fancy (info->value.download.completed *
153                                           1000LL /
154                                           (info->value.download.
155                                            duration.rel_value_us + 1));
156       FPRINTF (stdout,
157                _("Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"),
158                info->value.download.filename,
159                (unsigned long long) info->value.download.completed,
160                (unsigned long long) info->value.download.size, s, t, s2);
161       GNUNET_free (s);
162       GNUNET_free (t);
163     }
164     else
165     {
166       display_bar (info->value.download.completed,
167                    info->value.download.size,
168                    60);
169     }
170     break;
171   case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
172 #if !WINDOWS
173     if (0 != isatty (1))
174       fprintf (stdout, "\n");
175 #else
176     if (FILE_TYPE_CHAR == GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)))
177       fprintf (stdout, "\n");
178 #endif
179     FPRINTF (stderr, _("Error downloading: %s.\n"),
180              info->value.download.specifics.error.message);
181     GNUNET_SCHEDULER_shutdown ();
182     break;
183   case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
184     s = GNUNET_STRINGS_byte_size_fancy (info->value.download.completed * 1000 /
185                                         (info->value.download.
186                                          duration.rel_value_us + 1));
187 #if !WINDOWS
188     if (0 != isatty (1))
189       fprintf (stdout, "\n");
190 #else
191     if (FILE_TYPE_CHAR == GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)))
192       fprintf (stdout, "\n");
193 #endif
194     FPRINTF (stdout, _("Downloading `%s' done (%s/s).\n"),
195              info->value.download.filename, s);
196     GNUNET_free (s);
197     if (info->value.download.dc == dc)
198       GNUNET_SCHEDULER_shutdown ();
199     break;
200   case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
201     if (info->value.download.dc == dc)
202       GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
203     break;
204   case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
205   case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
206     break;
207   default:
208     FPRINTF (stderr, _("Unexpected status: %d\n"), info->status);
209     break;
210   }
211   return NULL;
212 }
213
214
215 /**
216  * Main function that will be run by the scheduler.
217  *
218  * @param cls closure
219  * @param args remaining command-line arguments
220  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
221  * @param c configuration
222  */
223 static void
224 run (void *cls, char *const *args, const char *cfgfile,
225      const struct GNUNET_CONFIGURATION_Handle *c)
226 {
227   struct GNUNET_FS_Uri *uri;
228   char *emsg;
229   enum GNUNET_FS_DownloadOptions options;
230
231   if (NULL == args[0])
232   {
233     FPRINTF (stderr, "%s",  _("You need to specify a URI argument.\n"));
234     return;
235   }
236   uri = GNUNET_FS_uri_parse (args[0], &emsg);
237   if (NULL == uri)
238   {
239     FPRINTF (stderr, _("Failed to parse URI: %s\n"), emsg);
240     GNUNET_free (emsg);
241     ret = 1;
242     return;
243   }
244   if ((!GNUNET_FS_uri_test_chk (uri)) && (!GNUNET_FS_uri_test_loc (uri)))
245   {
246     FPRINTF (stderr, "%s",  _("Only CHK or LOC URIs supported.\n"));
247     ret = 1;
248     GNUNET_FS_uri_destroy (uri);
249     return;
250   }
251   if (NULL == filename)
252   {
253     FPRINTF (stderr, "%s",  _("Target filename must be specified.\n"));
254     ret = 1;
255     GNUNET_FS_uri_destroy (uri);
256     return;
257   }
258   cfg = c;
259   ctx =
260       GNUNET_FS_start (cfg, "gnunet-download", &progress_cb, NULL,
261                        GNUNET_FS_FLAGS_NONE,
262                        GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, parallelism,
263                        GNUNET_FS_OPTIONS_REQUEST_PARALLELISM,
264                        request_parallelism, GNUNET_FS_OPTIONS_END);
265   if (NULL == ctx)
266   {
267     FPRINTF (stderr, _("Could not initialize `%s' subsystem.\n"), "FS");
268     GNUNET_FS_uri_destroy (uri);
269     ret = 1;
270     return;
271   }
272   options = GNUNET_FS_DOWNLOAD_OPTION_NONE;
273   if (do_recursive)
274     options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
275   if (local_only)
276     options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY;
277   dc = GNUNET_FS_download_start (ctx, uri, NULL, filename, NULL, 0,
278                                  GNUNET_FS_uri_chk_get_file_size (uri),
279                                  anonymity, options, NULL, NULL);
280   GNUNET_FS_uri_destroy (uri);
281   if (dc == NULL)
282   {
283     GNUNET_FS_stop (ctx);
284     ctx = NULL;
285     return;
286   }
287   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
288                                  NULL);
289 }
290
291
292 /**
293  * The main function to download GNUnet.
294  *
295  * @param argc number of arguments from the command line
296  * @param argv command line arguments
297  * @return 0 ok, 1 on error
298  */
299 int
300 main (int argc, char *const *argv)
301 {
302   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
303     {'a', "anonymity", "LEVEL",
304      gettext_noop ("set the desired LEVEL of receiver-anonymity"),
305      1, &GNUNET_GETOPT_set_uint, &anonymity},
306     {'D', "delete-incomplete", NULL,
307      gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"),
308      0, &GNUNET_GETOPT_set_one, &delete_incomplete},
309     {'n', "no-network", NULL,
310      gettext_noop ("only search the local peer (no P2P network search)"),
311      0, &GNUNET_GETOPT_set_one, &local_only},
312     {'o', "output", "FILENAME",
313      gettext_noop ("write the file to FILENAME"),
314      1, &GNUNET_GETOPT_set_string, &filename},
315     {'p', "parallelism", "DOWNLOADS",
316      gettext_noop
317      ("set the maximum number of parallel downloads that is allowed"),
318      1, &GNUNET_GETOPT_set_uint, &parallelism},
319     {'r', "request-parallelism", "REQUESTS",
320      gettext_noop
321      ("set the maximum number of parallel requests for blocks that is allowed"),
322      1, &GNUNET_GETOPT_set_uint, &request_parallelism},
323     {'R', "recursive", NULL,
324      gettext_noop ("download a GNUnet directory recursively"),
325      0, &GNUNET_GETOPT_set_one, &do_recursive},
326     {'V', "verbose", NULL,
327      gettext_noop ("be verbose (print progress information)"),
328      0, &GNUNET_GETOPT_increment_value, &verbose},
329     GNUNET_GETOPT_OPTION_END
330   };
331
332   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
333     return 2;
334
335   ret = (GNUNET_OK ==
336          GNUNET_PROGRAM_run (argc, argv, "gnunet-download [OPTIONS] URI",
337                              gettext_noop
338                              ("Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"),
339                              options, &run, NULL)) ? ret : 1;
340   GNUNET_free ((void*) argv);
341   return ret;
342 }
343
344 /* end of gnunet-download.c */