check against all pending requests with same hash, not just first; this is a true...
[oweals/gnunet.git] / src / fs / gnunet-search.c
index 0226a340e7e491f74e204c29f3fc2276964970b2..5d76335916fc700f1ce506bb212900e976a95fcd 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 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
 
      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
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
@@ -14,8 +14,8 @@
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
 
      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.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 /**
  * @file fs/gnunet-search.c
 */
 /**
  * @file fs/gnunet-search.c
@@ -32,8 +32,6 @@ static int ret;
 
 static const struct GNUNET_CONFIGURATION_Handle *cfg;
 
 
 static const struct GNUNET_CONFIGURATION_Handle *cfg;
 
-static struct GNUNET_SCHEDULER_Handle *sched;
-
 static struct GNUNET_FS_Handle *ctx;
 
 static struct GNUNET_FS_SearchContext *sc;
 static struct GNUNET_FS_Handle *ctx;
 
 static struct GNUNET_FS_SearchContext *sc;
@@ -44,6 +42,15 @@ static struct GNUNET_FS_DirectoryBuilder *db;
 
 static unsigned int anonymity = 1;
 
 
 static unsigned int anonymity = 1;
 
+/**
+ * Timeout for the search, 0 means to wait for CTRL-C.
+ */
+static struct GNUNET_TIME_Relative timeout;
+
+static unsigned int results_limit;
+
+static unsigned int results;
+
 static int verbose;
 
 static int local_only;
 static int verbose;
 
 static int local_only;
@@ -58,36 +65,38 @@ static int local_only;
  *        used in the main libextractor library and yielding
  *        meta data).
  * @param type libextractor-type describing the meta data
  *        used in the main libextractor library and yielding
  *        meta data).
  * @param type libextractor-type describing the meta data
- * @param format basic format information about data 
+ * @param format basic format information about data
  * @param data_mime_type mime-type of data (not of the original file);
  *        can be NULL (if mime-type is not known)
  * @param data actual meta-data found
  * @param data_size number of bytes in data
  * @return 0 to continue extracting, 1 to abort
  * @param data_mime_type mime-type of data (not of the original file);
  *        can be NULL (if mime-type is not known)
  * @param data actual meta-data found
  * @param data_size number of bytes in data
  * @return 0 to continue extracting, 1 to abort
- */ 
+ */
 static int
 static int
-item_printer (void *cls,
-             const char *plugin_name,
-             enum EXTRACTOR_MetaType type, 
-             enum EXTRACTOR_MetaFormat format,
-             const char *data_mime_type,
-             const char *data,
-             size_t data_size)
+item_printer (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
+              enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
+              const char *data, size_t data_size)
 {
 {
-  if ( (format != EXTRACTOR_METAFORMAT_UTF8) &&
-       (format != EXTRACTOR_METAFORMAT_C_STRING) )
+  if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
+      (format != EXTRACTOR_METAFORMAT_C_STRING))
+    return 0;
+  if (type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
     return 0;
     return 0;
+#if HAVE_LIBEXTRACTOR
   printf ("\t%20s: %s\n",
           dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN,
   printf ("\t%20s: %s\n",
           dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN,
-                    EXTRACTOR_metatype_to_string (type)),
-         data);
+                    EXTRACTOR_metatype_to_string (type)), data);
+#else
+  printf ("\t%20d: %s\n",
+          type,
+          data);
+#endif
   return 0;
 }
 
 
 static void
   return 0;
 }
 
 
 static void
-clean_task (void *cls,
-           const struct GNUNET_SCHEDULER_TaskContext *tc)
+clean_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   size_t dsize;
   void *ddata;
 {
   size_t dsize;
   void *ddata;
@@ -96,32 +105,28 @@ clean_task (void *cls,
   ctx = NULL;
   if (output_filename == NULL)
     return;
   ctx = NULL;
   if (output_filename == NULL)
     return;
-  if (GNUNET_OK !=
-      GNUNET_FS_directory_builder_finish (db,
-                                         &dsize,       
-                                         &ddata))
-    {
-      GNUNET_break (0);
-      GNUNET_free (output_filename);    
-      return;
-    }
-  if (dsize != 
-      GNUNET_DISK_fn_write (output_filename,
-                           ddata,
-                           dsize,
-                           GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE))
-    {
-      fprintf (stderr,
-              _("Failed to write directory with search results to `%s'\n"),
-              output_filename);
-    }
+  if (GNUNET_OK != GNUNET_FS_directory_builder_finish (db, &dsize, &ddata))
+  {
+    GNUNET_break (0);
+    GNUNET_free (output_filename);
+    return;
+  }
+  if (dsize !=
+      GNUNET_DISK_fn_write (output_filename, ddata, dsize,
+                            GNUNET_DISK_PERM_USER_READ |
+                            GNUNET_DISK_PERM_USER_WRITE))
+  {
+    FPRINTF (stderr,
+             _("Failed to write directory with search results to `%s'\n"),
+             output_filename);
+  }
   GNUNET_free_non_null (ddata);
   GNUNET_free (output_filename);
 }
 
 
 /**
   GNUNET_free_non_null (ddata);
   GNUNET_free (output_filename);
 }
 
 
 /**
- * Called by FS client to give information about the progress of an 
+ * Called by FS client to give information about the progress of an
  * operation.
  *
  * @param cls closure
  * operation.
  *
  * @param cls closure
@@ -134,84 +139,84 @@ clean_task (void *cls,
  *         field in the GNUNET_FS_ProgressInfo struct.
  */
 static void *
  *         field in the GNUNET_FS_ProgressInfo struct.
  */
 static void *
-progress_cb (void *cls,
-            const struct GNUNET_FS_ProgressInfo *info)
+progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
 {
   static unsigned int cnt;
 {
   static unsigned int cnt;
+  int is_directory;
   char *uri;
   char *uri;
-  char *dotdot;
   char *filename;
 
   switch (info->status)
   char *filename;
 
   switch (info->status)
+  {
+  case GNUNET_FS_STATUS_SEARCH_START:
+    break;
+  case GNUNET_FS_STATUS_SEARCH_RESULT:
+    if (db != NULL)
+      GNUNET_FS_directory_builder_add (db,
+                                       info->value.search.specifics.result.uri,
+                                       info->value.search.specifics.result.meta,
+                                       NULL);
+    uri = GNUNET_FS_uri_to_string (info->value.search.specifics.result.uri);
+    printf ("#%u:\n", cnt++);
+    filename =
+        GNUNET_CONTAINER_meta_data_get_by_type (info->value.search.
+                                                specifics.result.meta,
+                                                EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
+    is_directory =
+        GNUNET_FS_meta_data_test_for_directory (info->value.search.
+                                                specifics.result.meta);
+    if (filename != NULL)
     {
     {
-    case GNUNET_FS_STATUS_SEARCH_START:
-      break;
-    case GNUNET_FS_STATUS_SEARCH_RESULT:
-      if (db != NULL)
-       GNUNET_FS_directory_builder_add (db,
-                                        info->value.search.specifics.result.uri,
-                                        info->value.search.specifics.result.meta,
-                                        NULL);
-      uri = GNUNET_FS_uri_to_string (info->value.search.specifics.result.uri);
-      printf ("#%u:\n", cnt++);
-      filename =
-        GNUNET_CONTAINER_meta_data_get_by_type (info->value.search.specifics.result.meta,
-                                               EXTRACTOR_METATYPE_FILENAME);
-      if (filename != NULL)
-        {
-          while (NULL != (dotdot = strstr (filename, "..")))
-            dotdot[0] = dotdot[1] = '_';
-          printf ("gnunet-download -o \"%s\" %s\n", 
-                 filename, 
-                 uri);
-        }
+      GNUNET_DISK_filename_canonicalize (filename);
+      if (GNUNET_YES == is_directory)
+        printf ("gnunet-download -o \"%s%s\" -R %s\n", filename, GNUNET_FS_DIRECTORY_EXT, uri);
       else
       else
-        printf ("gnunet-download %s\n", uri);
-      if (verbose)
-       GNUNET_CONTAINER_meta_data_iterate (info->value.search.specifics.result.meta, 
-                                           &item_printer,
-                                           NULL);
-      printf ("\n");
-      fflush(stdout);
-      GNUNET_free_non_null (filename);
-      GNUNET_free (uri);
-      break;
-    case GNUNET_FS_STATUS_SEARCH_UPDATE:
-      break;
-    case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
-      /* ignore */
-      break;
-    case GNUNET_FS_STATUS_SEARCH_ERROR:
-      fprintf (stderr,
-              _("Error searching: %s.\n"),
-              info->value.search.specifics.error.message);
-      GNUNET_SCHEDULER_shutdown (sched);
-      break;
-    case GNUNET_FS_STATUS_SEARCH_STOPPED: 
-      GNUNET_SCHEDULER_add_continuation (sched,
-                                        &clean_task, 
-                                        NULL,
-                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
-      break;      
-    default:
-      fprintf (stderr,
-              _("Unexpected status: %d\n"),
-              info->status);
-      break;
+        printf ("gnunet-download -o \"%s\" %s\n", filename, uri);
     }
     }
+    else if (GNUNET_YES == is_directory)
+      printf ("gnunet-download -o \"collection%s\" -R %s\n", GNUNET_FS_DIRECTORY_EXT, uri);
+    else
+      printf ("gnunet-download %s\n", uri);
+    if (verbose)
+      GNUNET_CONTAINER_meta_data_iterate (info->value.search.specifics.
+                                          result.meta, &item_printer, NULL);
+    printf ("\n");
+    fflush (stdout);
+    GNUNET_free_non_null (filename);
+    GNUNET_free (uri);
+    results++;
+    if ((results_limit > 0) && (results >= results_limit))
+      GNUNET_SCHEDULER_shutdown ();
+    break;
+  case GNUNET_FS_STATUS_SEARCH_UPDATE:
+    break;
+  case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
+    /* ignore */
+    break;
+  case GNUNET_FS_STATUS_SEARCH_ERROR:
+    FPRINTF (stderr, _("Error searching: %s.\n"),
+             info->value.search.specifics.error.message);
+    GNUNET_SCHEDULER_shutdown ();
+    break;
+  case GNUNET_FS_STATUS_SEARCH_STOPPED:
+    GNUNET_SCHEDULER_add_now (&clean_task, NULL);
+    break;
+  default:
+    FPRINTF (stderr, _("Unexpected status: %d\n"), info->status);
+    break;
+  }
   return NULL;
 }
 
 
 static void
   return NULL;
 }
 
 
 static void
-shutdown_task (void *cls,
-              const struct GNUNET_SCHEDULER_TaskContext *tc)
+shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   if (sc != NULL)
 {
   if (sc != NULL)
-    {
-      GNUNET_FS_search_stop (sc); 
-      sc = NULL;
-    }
+  {
+    GNUNET_FS_search_stop (sc);
+    sc = NULL;
+  }
 }
 
 
 }
 
 
@@ -219,101 +224,61 @@ shutdown_task (void *cls,
  * Main function that will be run by the scheduler.
  *
  * @param cls closure
  * Main function that will be run by the scheduler.
  *
  * @param cls closure
- * @param s the scheduler to use
  * @param args remaining command-line arguments
  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
  * @param c configuration
  */
 static void
  * @param args remaining command-line arguments
  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
  * @param c configuration
  */
 static void
-run (void *cls,
-     struct GNUNET_SCHEDULER_Handle *s,
-     char *const *args,
-     const char *cfgfile,
+run (void *cls, char *const *args, const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
   struct GNUNET_FS_Uri *uri;
   unsigned int argc;
   enum GNUNET_FS_SearchOptions options;
 
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
   struct GNUNET_FS_Uri *uri;
   unsigned int argc;
   enum GNUNET_FS_SearchOptions options;
 
-  sched = s;
   argc = 0;
   while (NULL != args[argc])
     argc++;
   argc = 0;
   while (NULL != args[argc])
     argc++;
-  uri = GNUNET_FS_uri_ksk_create_from_args (argc,
-                                           (const char **) args);
+  uri = GNUNET_FS_uri_ksk_create_from_args (argc, (const char **) args);
   if (NULL == uri)
   if (NULL == uri)
-    {
-      fprintf (stderr,
-              _("Could not create keyword URI from arguments.\n"));
-      ret = 1;
-      GNUNET_FS_uri_destroy (uri);
-      return;
-    }
+  {
+    FPRINTF (stderr, "%s",  _("Could not create keyword URI from arguments.\n"));
+    ret = 1;
+    return;
+  }
   cfg = c;
   cfg = c;
-  ctx = GNUNET_FS_start (sched,
-                        cfg,
-                        "gnunet-search",
-                        &progress_cb,
-                        NULL,
-                        GNUNET_FS_FLAGS_NONE,
-                        GNUNET_FS_OPTIONS_END);
+  ctx =
+      GNUNET_FS_start (cfg, "gnunet-search", &progress_cb, NULL,
+                       GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
   if (NULL == ctx)
   if (NULL == ctx)
-    {
-      fprintf (stderr,
-              _("Could not initialize `%s' subsystem.\n"),
-              "FS");
-      GNUNET_FS_uri_destroy (uri);
-      GNUNET_FS_stop (ctx);
-      ret = 1;
-      return;
-    }
+  {
+    FPRINTF (stderr, _("Could not initialize `%s' subsystem.\n"), "FS");
+    GNUNET_FS_uri_destroy (uri);
+    ret = 1;
+    return;
+  }
   if (output_filename != NULL)
     db = GNUNET_FS_directory_builder_create (NULL);
   options = GNUNET_FS_SEARCH_OPTION_NONE;
   if (local_only)
     options |= GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY;
   if (output_filename != NULL)
     db = GNUNET_FS_directory_builder_create (NULL);
   options = GNUNET_FS_SEARCH_OPTION_NONE;
   if (local_only)
     options |= GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY;
-  sc = GNUNET_FS_search_start (ctx,
-                              uri,
-                              anonymity,
-                              options,
-                              NULL);
+  sc = GNUNET_FS_search_start (ctx, uri, anonymity, options, NULL);
   GNUNET_FS_uri_destroy (uri);
   if (NULL == sc)
   GNUNET_FS_uri_destroy (uri);
   if (NULL == sc)
-    {
-      fprintf (stderr,
-              _("Could not start searching.\n"));
-      GNUNET_FS_stop (ctx);
-      ret = 1;
-      return;
-    }
-  GNUNET_SCHEDULER_add_delayed (sched,
-                               GNUNET_TIME_UNIT_FOREVER_REL,
-                               &shutdown_task,
-                               NULL);
+  {
+    FPRINTF (stderr, "%s",  _("Could not start searching.\n"));
+    GNUNET_FS_stop (ctx);
+    ret = 1;
+    return;
+  }
+  if (0 != timeout.rel_value_us)
+    GNUNET_SCHEDULER_add_delayed (timeout, &shutdown_task, NULL);
+  else
+    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
+                                  NULL);
 }
 
 
 }
 
 
-/**
- * gnunet-search command line options
- */
-static struct GNUNET_GETOPT_CommandLineOption options[] = {
-  {'a', "anonymity", "LEVEL",
-   gettext_noop ("set the desired LEVEL of receiver-anonymity"),
-   1, &GNUNET_GETOPT_set_uint, &anonymity},
-  {'n', "no-network", NULL,
-   gettext_noop ("only search the local peer (no P2P network search)"),
-   1, &GNUNET_GETOPT_set_uint, &local_only},
-  {'o', "output", "PREFIX",
-   gettext_noop
-   ("write search results to file starting with PREFIX"),
-   1, &GNUNET_GETOPT_set_string, &output_filename}, 
-  {'V', "verbose", NULL,
-   gettext_noop ("be verbose (print progress information)"),
-   0, &GNUNET_GETOPT_set_one, &verbose},
-  GNUNET_GETOPT_OPTION_END
-};
-
-
 /**
  * The main function to search GNUnet.
  *
 /**
  * The main function to search GNUnet.
  *
@@ -324,14 +289,39 @@ static struct GNUNET_GETOPT_CommandLineOption options[] = {
 int
 main (int argc, char *const *argv)
 {
 int
 main (int argc, char *const *argv)
 {
-  return (GNUNET_OK ==
-          GNUNET_PROGRAM_run (argc,
-                              argv,
-                              "gnunet-search",
-                              gettext_noop
-                              ("Search GNUnet."),
-                              options, &run, NULL)) ? ret : 1;
+  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    {'a', "anonymity", "LEVEL",
+     gettext_noop ("set the desired LEVEL of receiver-anonymity"),
+     1, &GNUNET_GETOPT_set_uint, &anonymity},
+    {'n', "no-network", NULL,
+     gettext_noop ("only search the local peer (no P2P network search)"),
+     0, &GNUNET_GETOPT_set_one, &local_only},
+    {'o', "output", "PREFIX",
+     gettext_noop ("write search results to file starting with PREFIX"),
+     1, &GNUNET_GETOPT_set_string, &output_filename},
+    {'t', "timeout", "DELAY",
+     gettext_noop ("automatically terminate search after DELAY"),
+     1, &GNUNET_GETOPT_set_relative_time, &timeout},
+    {'V', "verbose", NULL,
+     gettext_noop ("be verbose (print progress information)"),
+     0, &GNUNET_GETOPT_set_one, &verbose},
+    {'N', "results", "VALUE",
+     gettext_noop
+     ("automatically terminate search after VALUE results are found"),
+     1, &GNUNET_GETOPT_set_uint, &results_limit},
+    GNUNET_GETOPT_OPTION_END
+  };
+
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 2;
+
+  ret = (GNUNET_OK ==
+        GNUNET_PROGRAM_run (argc, argv, "gnunet-search [OPTIONS] KEYWORD",
+                            gettext_noop
+                            ("Search GNUnet for files that were published on GNUnet"),
+                            options, &run, NULL)) ? ret : 1;
+  GNUNET_free ((void*) argv);
+  return ret;
 }
 
 /* end of gnunet-search.c */
 }
 
 /* end of gnunet-search.c */
-