first batch of license fixes (boring)
[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 it
6      under the terms of the GNU General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15 /**
16  * @file fs/gnunet-download.c
17  * @brief downloading for files on GNUnet
18  * @author Christian Grothoff
19  * @author Krista Bennett
20  * @author James Blackwell
21  * @author Igor Wronsky
22  */
23 #include "platform.h"
24 #include "gnunet_fs_service.h"
25
26 static int ret;
27
28 static unsigned int verbose;
29
30 static int delete_incomplete;
31
32 static const struct GNUNET_CONFIGURATION_Handle *cfg;
33
34 static struct GNUNET_FS_Handle *ctx;
35
36 static struct GNUNET_FS_DownloadContext *dc;
37
38 static unsigned int anonymity = 1;
39
40 static unsigned int parallelism = 16;
41
42 static unsigned int request_parallelism = 4092;
43
44 static int do_recursive;
45
46 static char *filename;
47
48 static int local_only;
49
50
51 static void
52 cleanup_task (void *cls)
53 {
54   GNUNET_FS_stop (ctx);
55   ctx = NULL;
56 }
57
58
59 static void
60 shutdown_task (void *cls)
61 {
62   if (NULL != dc)
63   {
64     GNUNET_FS_download_stop (dc, delete_incomplete);
65     dc = NULL;
66   }
67 }
68
69
70 /**
71  * Display progress bar (if tty).
72  *
73  * @param x current position in the download
74  * @param n total size of the download
75  * @param w desired number of steps in the progress bar
76  */
77 static void
78 display_bar (unsigned long long x,
79              unsigned long long n,
80              unsigned int w)
81 {
82   char buf[w + 20];
83   unsigned int p;
84   unsigned int endeq;
85   float ratio_complete;
86
87 #if !WINDOWS
88   if (0 == isatty (1))
89     return;
90 #else
91   if (FILE_TYPE_CHAR != GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)))
92     return;
93 #endif
94   ratio_complete = x/(float)n;
95   endeq = ratio_complete * w;
96   GNUNET_snprintf (buf, sizeof (buf),
97                    "%3d%% [", (int)(ratio_complete*100) );
98   for (p=0; p<endeq; p++)
99     strcat (buf, "=");
100   for (p=endeq; p<w; p++)
101     strcat (buf, " ");
102   strcat (buf, "]\r");
103   printf ("%s", buf);
104   fflush(stdout);
105 }
106
107
108 /**
109  * Called by FS client to give information about the progress of an
110  * operation.
111  *
112  * @param cls closure
113  * @param info details about the event, specifying the event type
114  *        and various bits about the event
115  * @return client-context (for the next progress call
116  *         for this operation; should be set to NULL for
117  *         SUSPEND and STOPPED events).  The value returned
118  *         will be passed to future callbacks in the respective
119  *         field in the `struct GNUNET_FS_ProgressInfo`
120  */
121 static void *
122 progress_cb (void *cls,
123              const struct GNUNET_FS_ProgressInfo *info)
124 {
125   char *s;
126   const char *s2;
127   char *t;
128
129   switch (info->status)
130   {
131   case GNUNET_FS_STATUS_DOWNLOAD_START:
132     if (verbose > 1)
133       FPRINTF (stderr,
134                _("Starting download `%s'.\n"),
135                info->value.download.filename);
136     break;
137   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
138     if (verbose)
139     {
140       s = GNUNET_strdup (GNUNET_STRINGS_relative_time_to_string (info->value.download.eta,
141                                                                  GNUNET_YES));
142       if (info->value.download.specifics.progress.block_download_duration.rel_value_us
143           == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
144         s2 = _("<unknown time>");
145       else
146         s2 = GNUNET_STRINGS_relative_time_to_string (info->value.download.specifics.progress.block_download_duration,
147                                                      GNUNET_YES);
148       t = GNUNET_STRINGS_byte_size_fancy (info->value.download.completed *
149                                           1000LL /
150                                           (info->value.download.
151                                            duration.rel_value_us + 1));
152       FPRINTF (stdout,
153                _("Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"),
154                info->value.download.filename,
155                (unsigned long long) info->value.download.completed,
156                (unsigned long long) info->value.download.size,
157                s,
158                t,
159                s2);
160       GNUNET_free (s);
161       GNUNET_free (t);
162     }
163     else
164     {
165       display_bar (info->value.download.completed,
166                    info->value.download.size,
167                    60);
168     }
169     break;
170   case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
171 #if !WINDOWS
172     if (0 != isatty (1))
173       fprintf (stdout, "\n");
174 #else
175     if (FILE_TYPE_CHAR ==
176         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 ==
192         GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)))
193       fprintf (stdout, "\n");
194 #endif
195     FPRINTF (stdout,
196              _("Downloading `%s' done (%s/s).\n"),
197              info->value.download.filename, s);
198     GNUNET_free (s);
199     if (info->value.download.dc == dc)
200       GNUNET_SCHEDULER_shutdown ();
201     break;
202   case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
203     if (info->value.download.dc == dc)
204       GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
205     break;
206   case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
207   case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
208     break;
209   default:
210     FPRINTF (stderr,
211              _("Unexpected status: %d\n"),
212              info->status);
213     break;
214   }
215   return NULL;
216 }
217
218
219 /**
220  * Main function that will be run by the scheduler.
221  *
222  * @param cls closure
223  * @param args remaining command-line arguments
224  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
225  * @param c configuration
226  */
227 static void
228 run (void *cls,
229      char *const *args,
230      const char *cfgfile,
231      const struct GNUNET_CONFIGURATION_Handle *c)
232 {
233   struct GNUNET_FS_Uri *uri;
234   char *emsg;
235   enum GNUNET_FS_DownloadOptions options;
236
237   if (NULL == args[0])
238   {
239     FPRINTF (stderr,
240              "%s",
241              _("You need to specify a URI argument.\n"));
242     return;
243   }
244   uri = GNUNET_FS_uri_parse (args[0], &emsg);
245   if (NULL == uri)
246   {
247     FPRINTF (stderr,
248              _("Failed to parse URI: %s\n"),
249              emsg);
250     GNUNET_free (emsg);
251     ret = 1;
252     return;
253   }
254   if ( (! GNUNET_FS_uri_test_chk (uri)) &&
255        (! GNUNET_FS_uri_test_loc (uri)))
256   {
257     FPRINTF (stderr,
258              "%s",
259              _("Only CHK or LOC URIs supported.\n"));
260     ret = 1;
261     GNUNET_FS_uri_destroy (uri);
262     return;
263   }
264   if (NULL == filename)
265   {
266     FPRINTF (stderr,
267              "%s",
268              _("Target filename must be specified.\n"));
269     ret = 1;
270     GNUNET_FS_uri_destroy (uri);
271     return;
272   }
273   cfg = c;
274   ctx = GNUNET_FS_start (cfg,
275                          "gnunet-download",
276                          &progress_cb, NULL,
277                          GNUNET_FS_FLAGS_NONE,
278                          GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM,
279                          parallelism,
280                          GNUNET_FS_OPTIONS_REQUEST_PARALLELISM,
281                          request_parallelism,
282                          GNUNET_FS_OPTIONS_END);
283   if (NULL == ctx)
284   {
285     FPRINTF (stderr,
286              _("Could not initialize `%s' subsystem.\n"),
287              "FS");
288     GNUNET_FS_uri_destroy (uri);
289     ret = 1;
290     return;
291   }
292   options = GNUNET_FS_DOWNLOAD_OPTION_NONE;
293   if (do_recursive)
294     options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
295   if (local_only)
296     options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY;
297   dc = GNUNET_FS_download_start (ctx,
298                                  uri,
299                                  NULL,
300                                  filename,
301                                  NULL,
302                                  0,
303                                  GNUNET_FS_uri_chk_get_file_size (uri),
304                                  anonymity,
305                                  options,
306                                  NULL,
307                                  NULL);
308   GNUNET_FS_uri_destroy (uri);
309   if (dc == NULL)
310   {
311     GNUNET_FS_stop (ctx);
312     ctx = NULL;
313     return;
314   }
315   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
316                                  NULL);
317 }
318
319
320 /**
321  * The main function to download GNUnet.
322  *
323  * @param argc number of arguments from the command line
324  * @param argv command line arguments
325  * @return 0 ok, 1 on error
326  */
327 int
328 main (int argc, char *const *argv)
329 {
330   struct GNUNET_GETOPT_CommandLineOption options[] = {
331     GNUNET_GETOPT_option_uint ('a',
332                                "anonymity",
333                                "LEVEL",
334                                gettext_noop ("set the desired LEVEL of receiver-anonymity"),
335                                &anonymity),
336
337     GNUNET_GETOPT_option_flag ('D',
338                                "delete-incomplete",
339                                gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"),
340                                &delete_incomplete),
341
342     GNUNET_GETOPT_option_flag ('n',
343                                "no-network",
344                                gettext_noop ("only search the local peer (no P2P network search)"),
345                                &local_only), 
346     GNUNET_GETOPT_option_string ('o',
347                                  "output",
348                                  "FILENAME",
349                                  gettext_noop ("write the file to FILENAME"),
350                                  &filename),
351     GNUNET_GETOPT_option_uint ('p',
352                                "parallelism",
353                                "DOWNLOADS",
354                                gettext_noop ("set the maximum number of parallel downloads that is allowed"),
355                                &parallelism),
356     GNUNET_GETOPT_option_uint ('r',
357                                "request-parallelism",
358                                "REQUESTS",
359                                gettext_noop ("set the maximum number of parallel requests for blocks that is allowed"),
360                                &request_parallelism), 
361     GNUNET_GETOPT_option_flag ('R',
362                                "recursive",
363                                gettext_noop ("download a GNUnet directory recursively"),
364                                &do_recursive),
365     GNUNET_GETOPT_option_increment_uint ('V',
366                                          "verbose",
367                                          gettext_noop ("be verbose (print progress information)"),
368                                          &verbose), 
369     GNUNET_GETOPT_OPTION_END
370   };
371
372   if (GNUNET_OK !=
373       GNUNET_STRINGS_get_utf8_args (argc, argv,
374                                     &argc, &argv))
375     return 2;
376
377   ret = (GNUNET_OK ==
378          GNUNET_PROGRAM_run (argc, argv,
379                              "gnunet-download [OPTIONS] URI",
380                              gettext_noop
381                              ("Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"),
382                              options,
383                              &run, NULL)) ? ret : 1;
384   GNUNET_free ((void*) argv);
385   return ret;
386 }
387
388 /* end of gnunet-download.c */