curl: Made --metalink option toggle Metalink functionality
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Wed, 20 Jun 2012 15:51:06 +0000 (00:51 +0900)
committerYang Tse <yangsita@gmail.com>
Thu, 21 Jun 2012 01:35:23 +0000 (03:35 +0200)
In this change, --metalink option no longer takes argument.  If
it is specified, given URIs are processed as Metalink XML file.
If given URIs are remote (e.g., http URI), curl downloads it
first. Regardless URI is local file (e.g., file URI scheme) or
remote, Metalink XML file is not written to local file system and
the received data is fed into Metalink XML parser directly.  This
means with --metalink option, filename related options like -O
and -o are ignored.

Usage examples:

$ curl --metalink http://example.org/foo.metalink

This will download foo.metalink and parse it and then download
the URI described there.

$ curl --metalink file://foo.metalink

This will parse local file foo.metalink and then download the URI
described there.

src/tool_cfgable.h
src/tool_getparam.c
src/tool_help.c
src/tool_metalink.c
src/tool_metalink.h
src/tool_operate.c
src/tool_sdecls.h

index 2183c04..1f6f948 100644 (file)
@@ -201,6 +201,7 @@ struct Configurable {
   long gssapi_delegation;
   bool ssl_allow_beast;     /* allow this SSL vulnerability */
 
+  bool use_metalink;        /* process given URLs as metalink XML file */
   metalinkfile *metalinkfile_list; /* point to the first node */
   metalinkfile *metalinkfile_last; /* point to the last/current node */
 }; /* struct Configurable */
index ab8ed1d..4bdf0a5 100644 (file)
@@ -171,7 +171,7 @@ static const struct LongShort aliases[]= {
   {"$G", "delegation",               TRUE},
   {"$H", "mail-auth",                TRUE},
   {"$I", "post303",                  FALSE},
-  {"$J", "metalink",                 TRUE},
+  {"$J", "metalink",                 FALSE},
   {"0",  "http1.0",                  FALSE},
   {"1",  "tlsv1",                    FALSE},
   {"2",  "sslv2",                    FALSE},
@@ -824,11 +824,7 @@ ParameterError getparameter(char *flag,    /* f or -long-flag */
       case 'J': /* --metalink */
         {
 #ifdef USE_METALINK
-          if(parse_metalink(config, nextarg) == -1) {
-            warnf(config, "Could not parse Metalink file: %s\n", nextarg);
-            /* TODO Is PARAM_BAD_USE appropriate here? */
-            return PARAM_BAD_USE;
-          }
+          config->use_metalink = toggle;
 #else
           warnf(config, "--metalink option is ignored because the binary is "
                 "built without the Metalink support.\n");
index ac92469..124f640 100644 (file)
@@ -125,7 +125,7 @@ static const char *const helptext[] = {
   "     --max-filesize BYTES  Maximum file size to download (H/F)",
   "     --max-redirs NUM  Maximum number of redirects allowed (H)",
   " -m, --max-time SECONDS  Maximum time allowed for the transfer",
-  "     --metalink      Process local Metalink file and use mirrors",
+  "     --metalink      Process given URLs as metalink XML file",
   "     --negotiate     Use HTTP Negotiate Authentication (H)",
   " -n, --netrc         Must read .netrc for user name and password",
   "     --netrc-optional Use either .netrc or URL; overrides -n",
index 8f6a1f8..74a0012 100644 (file)
@@ -33,8 +33,6 @@
 #  include <fcntl.h>
 #endif
 
-#include <metalink/metalink_parser.h>
-
 #ifdef USE_SSLEAY
 #  ifdef USE_OPENSSL
 #    include <openssl/md5.h>
@@ -68,6 +66,7 @@
 #include "tool_paramhlp.h"
 #include "tool_cfgable.h"
 #include "tool_metalink.h"
+#include "tool_msgs.h"
 
 #include "memdebug.h" /* keep this as LAST include */
 
@@ -331,8 +330,8 @@ static int check_hash(const char *filename,
   digest_context *dctx;
   int check_ok;
   int fd;
-  fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name,
-          filename);
+  fprintf(error, "Validating %s checksum (This may take some time)...\n",
+          digest_def->hash_name);
   fd = open(filename, O_RDONLY);
   if(fd == -1) {
     fprintf(error, "Could not open file %s: %s\n", filename, strerror(errno));
@@ -358,6 +357,12 @@ static int check_hash(const char *filename,
   Curl_digest_final(dctx, result);
   check_ok = memcmp(result, digest,
                     digest_def->dparams->digest_resultlen) == 0;
+  /* sha*sum style verdict output */
+  if(check_ok)
+    fprintf(error, "%s: OK\n", filename);
+  else
+    fprintf(error, "%s: FAILED\n", filename);
+
   free(result);
   close(fd);
   return check_ok;
@@ -373,12 +378,6 @@ int metalink_check_hash(struct Configurable *config,
   }
   rv = check_hash(filename, mlfile->checksum->digest_def,
                   mlfile->checksum->digest, config->errors);
-  if(rv == 1) {
-    fprintf(config->errors, "Checksum matched\n");
-  }
-  else if(rv == 0) {
-    fprintf(config->errors, "Checksum didn't match\n");
-  }
   return rv;
 }
 
@@ -468,13 +467,15 @@ static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo)
   return f;
 }
 
-int parse_metalink(struct Configurable *config, const char *infile)
+int parse_metalink(struct Configurable *config, struct OutStruct *outs)
 {
   metalink_error_t r;
   metalink_t* metalink;
   metalink_file_t **files;
 
-  r = metalink_parse_file(infile, &metalink);
+  /* metlaink_parse_final deletes outs->metalink_parser */
+  r = metalink_parse_final(outs->metalink_parser, NULL, 0, &metalink);
+  outs->metalink_parser = NULL;
   if(r != 0) {
     return -1;
   }
@@ -531,6 +532,33 @@ int parse_metalink(struct Configurable *config, const char *infile)
   return 0;
 }
 
+size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb,
+                         void *userdata)
+{
+  struct OutStruct *outs = userdata;
+  struct Configurable *config = outs->config;
+  int rv;
+
+  /*
+   * Once that libcurl has called back tool_write_cb() the returned value
+   * is checked against the amount that was intended to be written, if
+   * it does not match then it fails with CURLE_WRITE_ERROR. So at this
+   * point returning a value different from sz*nmemb indicates failure.
+   */
+  const size_t failure = (sz * nmemb) ? 0 : 1;
+
+  if(!config)
+    return failure;
+
+  rv = metalink_parse_update(outs->metalink_parser, buffer, sz *nmemb);
+  if(rv == 0)
+    return sz * nmemb;
+  else {
+    warnf(config, "Failed to parse Metalink XML\n");
+    return failure;
+  }
+}
+
 /*
  * Returns nonzero if content_type includes mediatype.
  */
index 36b4b02..c2568a3 100644 (file)
@@ -82,13 +82,21 @@ extern const digest_params MD5_DIGEST_PARAMS[1];
 extern const digest_params SHA1_DIGEST_PARAMS[1];
 extern const digest_params SHA256_DIGEST_PARAMS[1];
 
+#include <metalink/metalink_parser.h>
+
 /*
  * Counts the resource in the metalinkfile.
  */
 int count_next_metalink_resource(metalinkfile *mlfile);
 void clean_metalink(struct Configurable *config);
 
-int parse_metalink(struct Configurable *config, const char *infile);
+int parse_metalink(struct Configurable *config, struct OutStruct *outs);
+
+/*
+ * Callback function for CURLOPT_WRITEFUNCTION
+ */
+size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb,
+                         void *userdata);
 
 /*
  * Returns nonzero if content_type includes "application/metalink+xml"
index 8a5569b..6d02fb6 100644 (file)
@@ -586,8 +586,9 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
           }
         }
 
-        if((urlnode->flags&GETOUT_USEREMOTE) ||
-           (outfile && !curlx_strequal("-", outfile)) ) {
+        if(((urlnode->flags&GETOUT_USEREMOTE) ||
+            (outfile && !curlx_strequal("-", outfile))) &&
+           (metalink || !config->use_metalink)) {
 
           /*
            * We have specified a file name to store the result in, or we have
@@ -826,8 +827,15 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
 
         /* where to store */
         my_setopt(curl, CURLOPT_WRITEDATA, &outs);
-        /* what call to write */
-        my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb);
+        if(metalink || !config->use_metalink)
+          /* what call to write */
+          my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb);
+#ifdef USE_METALINK
+        else
+          /* Set Metalink specific write callback function to parse
+             XML data progressively. */
+          my_setopt(curl, CURLOPT_WRITEFUNCTION, metalink_write_cb);
+#endif /* USE_METALINK */
 
         /* for uploads */
         input.fd = infd;
@@ -1316,6 +1324,19 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
 #endif
 
         for(;;) {
+#ifdef USE_METALINK
+          if(!metalink && config->use_metalink) {
+            /* If outs.metalink_parser is non-NULL, delete it first. */
+            if(outs.metalink_parser)
+              metalink_parser_context_delete(outs.metalink_parser);
+            outs.metalink_parser = metalink_parser_context_new();
+            if(outs.metalink_parser == NULL) {
+              res = CURLE_OUT_OF_MEMORY;
+              goto show_error;
+            }
+          }
+#endif /* USE_METALINK */
+
           res = curl_easy_perform(curl);
 
           if(outs.is_cd_filename && outs.stream && !config->mute &&
@@ -1569,22 +1590,12 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
 #endif
 
 #ifdef USE_METALINK
-        if(!metalink && res == CURLE_OK && outs.filename) {
-          /* Check the content-type header field and if it indicates
-             Metalink file, parse it and add getout for them. */
-          char *content_type;
-          curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type);
-          if(content_type && check_metalink_content_type(content_type)) {
-            if(!(config->mute)) {
-              fprintf(config->errors, "\nParsing Metalink file: %s\n",
-                      outs.filename);
-            }
-            if(parse_metalink(config, outs.filename) == 0)
-              fprintf(config->errors,
-                      "Metalink file is parsed successfully\n");
-            else
-              fprintf(config->errors, "Could not parse Metalink file.\n");
-          }
+        if(!metalink && config->use_metalink && res == CURLE_OK) {
+          if(parse_metalink(config, &outs) == 0)
+            fprintf(config->errors,
+                    "Metalink XML is parsed successfully\n");
+          else
+            fprintf(config->errors, "Could not parse Metalink XML.\n");
         }
         else if(metalink && res == CURLE_OK && !metalink_next_res) {
           int rv = metalink_check_hash(config, mlfile, outs.filename);
@@ -1597,6 +1608,10 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
         /* No more business with this output struct */
         if(outs.alloc_filename)
           Curl_safefree(outs.filename);
+#ifdef USE_METALINK
+        if(outs.metalink_parser)
+          metalink_parser_context_delete(outs.metalink_parser);
+#endif /* USE_METALINK */
         memset(&outs, 0, sizeof(struct OutStruct));
         hdrcbdata.outs = NULL;
 
index da62536..8038ad7 100644 (file)
@@ -22,7 +22,9 @@
  *
  ***************************************************************************/
 #include "tool_setup.h"
-
+#ifdef USE_METALINK
+#  include <metalink/metalink_parser.h>
+#endif /* USE_METALINK */
 
 /*
  * OutStruct variables keep track of information relative to curl's
@@ -55,6 +57,9 @@
  *
  * 'init' member holds original file size or offset at which truncation is
  * taking place. Always zero unless appending to a non-empty regular file.
+ *
+ * 'metalink_parser' member is a pointer to Metalink XML parser
+ * context.
  */
 
 struct OutStruct {
@@ -67,6 +72,9 @@ struct OutStruct {
   struct Configurable *config;
   curl_off_t bytes;
   curl_off_t init;
+#ifdef USE_METALINK
+  metalink_parser_context_t *metalink_parser;
+#endif /* USE_METALINK */
 };