metalink: parse downloaded Metalink file
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Sat, 28 Apr 2012 14:46:32 +0000 (23:46 +0900)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 26 May 2012 21:09:37 +0000 (23:09 +0200)
Parse downloaded Metalink file and add downloads described there. Fixed
compile error without metalink support.

src/tool_getparam.c
src/tool_metalink.c
src/tool_metalink.h
src/tool_operate.c

index bdb4b04..34f6420 100644 (file)
@@ -829,65 +829,10 @@ ParameterError getparameter(char *flag,    /* f or -long-flag */
       case 'J': /* --metalink */
         {
 #ifdef HAVE_LIBMETALINK
-          metalink_error_t r;
-          metalink_t* metalink;
-          metalink_file_t **files;
-          struct metalink *ml;
-
-          r = metalink_parse_file(nextarg, &metalink);
-
-          if(r != 0) {
-            fprintf(stderr, "ERROR: code=%d\n", r);
-            exit(EXIT_FAILURE);
-          }
-          ml = new_metalink(metalink);
-
-          if(config->metalink_list) {
-            config->metalink_last->next = ml;
-            config->metalink_last = ml;
-          }
-          else {
-            config->metalink_list = config->metalink_last = ml;
-          }
-
-          for(files = metalink->files; *files; ++files) {
-            struct getout *url;
-            /* Skip an entry which has no resource. */
-            if(!(*files)->resources[0]) continue;
-            if(config->url_get ||
-               ((config->url_get = config->url_list) != NULL)) {
-              /* there's a node here, if it already is filled-in continue to
-                 find an "empty" node */
-              while(config->url_get && (config->url_get->flags & GETOUT_URL))
-                config->url_get = config->url_get->next;
-            }
-
-            /* now there might or might not be an available node to fill in! */
-
-            if(config->url_get)
-              /* existing node */
-              url = config->url_get;
-            else
-              /* there was no free node, create one! */
-              url=new_getout(config);
-
-            if(url) {
-              struct metalinkfile *mlfile;
-              /* Set name as url */
-              GetStr(&url->url, (*files)->name);
-
-              /* set flag metalink here */
-              url->flags |= GETOUT_URL | GETOUT_METALINK;
-              mlfile = new_metalinkfile(*files);
-
-              if(config->metalinkfile_list) {
-                config->metalinkfile_last->next = mlfile;
-                config->metalinkfile_last = mlfile;
-              }
-              else {
-                config->metalinkfile_list = config->metalinkfile_last = mlfile;
-              }
-            }
+          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;
           }
 #else
           warnf(config, "--metalink option is ignored because the binary is "
index a57297b..4b4c6bc 100644 (file)
  ***************************************************************************/
 #include "tool_setup.h"
 #include "tool_metalink.h"
+#include "tool_getparam.h"
+#include "tool_paramhlp.h"
 
 #include "memdebug.h" /* keep this as LAST include */
 
+/* Copied from tool_getparam.c */
+#define GetStr(str,val) do { \
+  if(*(str)) { \
+    free(*(str)); \
+    *(str) = NULL; \
+  } \
+  if((val)) \
+    *(str) = strdup((val)); \
+  if(!(val)) \
+    return PARAM_NO_MEM; \
+} WHILE_FALSE
+
 struct metalinkfile *new_metalinkfile(metalink_file_t *metalinkfile) {
   struct metalinkfile *f;
   f = (struct metalinkfile*)malloc(sizeof(struct metalinkfile));
@@ -64,3 +78,67 @@ void clean_metalink(struct Configurable *config)
   }
   config->metalink_last = 0;
 }
+
+int parse_metalink(struct Configurable *config, const char *infile)
+{
+  metalink_error_t r;
+  metalink_t* metalink;
+  metalink_file_t **files;
+  struct metalink *ml;
+
+  r = metalink_parse_file(infile, &metalink);
+
+  if(r != 0) {
+    return -1;
+  }
+  ml = new_metalink(metalink);
+
+  if(config->metalink_list) {
+    config->metalink_last->next = ml;
+    config->metalink_last = ml;
+  }
+  else {
+    config->metalink_list = config->metalink_last = ml;
+  }
+
+  for(files = metalink->files; *files; ++files) {
+    struct getout *url;
+    /* Skip an entry which has no resource. */
+    if(!(*files)->resources[0]) continue;
+    if(config->url_get ||
+       ((config->url_get = config->url_list) != NULL)) {
+      /* there's a node here, if it already is filled-in continue to
+         find an "empty" node */
+      while(config->url_get && (config->url_get->flags & GETOUT_URL))
+        config->url_get = config->url_get->next;
+    }
+
+    /* now there might or might not be an available node to fill in! */
+
+    if(config->url_get)
+      /* existing node */
+      url = config->url_get;
+    else
+      /* there was no free node, create one! */
+      url=new_getout(config);
+
+    if(url) {
+      struct metalinkfile *mlfile;
+      /* Set name as url */
+      GetStr(&url->url, (*files)->name);
+
+      /* set flag metalink here */
+      url->flags |= GETOUT_URL | GETOUT_METALINK;
+      mlfile = new_metalinkfile(*files);
+
+      if(config->metalinkfile_list) {
+        config->metalinkfile_last->next = mlfile;
+        config->metalinkfile_last = mlfile;
+      }
+      else {
+        config->metalinkfile_list = config->metalinkfile_last = mlfile;
+      }
+    }
+  }
+  return 0;
+}
index e9f8162..39e6674 100644 (file)
@@ -48,4 +48,6 @@ int count_next_metalink_resource(struct metalinkfile *mlfile);
 
 void clean_metalink(struct Configurable *config);
 
+int parse_metalink(struct Configurable *config, const char *infile);
+
 #endif /* HEADER_CURL_TOOL_METALINK_H */
index 669d2dd..4bcc8b4 100644 (file)
@@ -130,7 +130,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
   struct OutStruct heads;
 
 #ifdef HAVE_LIBMETALINK
-  struct metalinkfile *mlfile_last;
+  struct metalinkfile *mlfile_last = NULL;
 #endif /* HAVE_LIBMETALINK */
 
   CURL *curl = NULL;
@@ -392,10 +392,6 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
     }
   }
 
-#ifdef HAVE_LIBMETALINK
-  mlfile_last = config->metalinkfile_list;
-#endif /* HAVE_LIBMETALINK */
-
   /*
   ** Nested loops start here.
   */
@@ -409,16 +405,23 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
     char *outfiles;
     int infilenum;
     URLGlob *inglob;
+
+#ifdef HAVE_LIBMETALINK
     int metalink; /* nonzero for metalink download */
     struct metalinkfile *mlfile;
     metalink_resource_t **mlres;
+#endif /* HAVE_LIBMETALINK */
 
     outfiles = NULL;
     infilenum = 1;
     inglob = NULL;
 
+#ifdef HAVE_LIBMETALINK
     if(urlnode->flags & GETOUT_METALINK) {
       metalink = 1;
+      if(mlfile_last == NULL) {
+        mlfile_last = config->metalinkfile_list;
+      }
       mlfile = mlfile_last;
       mlfile_last = mlfile_last->next;
       mlres = mlfile->file->resources;
@@ -428,6 +431,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
       mlfile = NULL;
       mlres = NULL;
     }
+#endif /* HAVE_LIBMETALINK */
 
     /* urlnode->url is the full URL (it might be NULL) */
 
@@ -497,12 +501,15 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
           break;
       }
 
+#ifdef HAVE_LIBMETALINK
       if(metalink) {
         /* For Metalink download, we don't use glob. Instead we use
            the number of resources as urlnum. */
         urlnum = count_next_metalink_resource(mlfile);
       }
-      else if(!config->globoff) {
+      else
+#endif /* HAVE_LIBMETALINK */
+      if(!config->globoff) {
         /* Unless explicitly shut off, we expand '{...}' and '[...]'
            expressions and return total number of URLs in pattern set */
         res = glob_url(&urls, urlnode->url, &urlnum,
@@ -533,20 +540,24 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
         long retry_sleep;
         char *this_url;
         HeaderData hdrdata;
-        int metalink_next_res;
+#ifdef HAVE_LIBMETALINK
+        int metalink_next_res = 0;
+#endif /* HAVE_LIBMETALINK */
 
         outfile = NULL;
         infdopen = FALSE;
         infd = STDIN_FILENO;
         uploadfilesize = -1; /* -1 means unknown */
-        metalink_next_res = 0;
 
         /* default output stream is stdout */
         memset(&outs, 0, sizeof(struct OutStruct));
         outs.stream = stdout;
         outs.config = config;
 
+#ifdef HAVE_LIBMETALINK
         if(metalink) {
+          /* For Metalink download, use name in Metalink file as
+             filename. */
           outfile = strdup(mlfile->file->name);
           if(!outfile) {
             res = CURLE_OUT_OF_MEMORY;
@@ -559,6 +570,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
           }
         }
         else {
+#endif /* HAVE_LIBMETALINK */
           if(urls) {
             res = glob_next_url(&this_url, urls);
             if(res)
@@ -583,7 +595,9 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
               goto show_error;
             }
           }
+#ifdef HAVE_LIBMETALINK
         }
+#endif /* HAVE_LIBMETALINK */
 
         if((urlnode->flags&GETOUT_USEREMOTE) ||
            (outfile && !curlx_strequal("-", outfile)) ) {
@@ -1429,6 +1443,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
               continue; /* curl_easy_perform loop */
             }
           } /* if retry_numretries */
+#ifdef HAVE_LIBMETALINK
           else if(metalink) {
             /* Metalink: Decide to try the next resource or
                not. Basically, we want to try the next resource if
@@ -1451,6 +1466,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
             else
               metalink_next_res = 1;
           }
+#endif /* HAVE_LIBMETALINK */
 
           /* In all ordinary cases, just break out of loop here */
           break; /* curl_easy_perform loop */
@@ -1562,10 +1578,21 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
 
 #ifdef HAVE_LIBMETALINK
         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 != NULL) {
-            printf("file=%s, content-type=%s\n", outs.filename, content_type);
+          if(content_type &&
+             Curl_raw_equal("application/metalink+xml", 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");
           }
         }
 #endif /* HAVE_LIBMETALINK */
@@ -1586,14 +1613,18 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
           infd = STDIN_FILENO;
         }
 
+#ifdef HAVE_LIBMETALINK
         if(metalink) {
+          /* Should exit if error is fatal. */
           if(is_fatal_error(res)) {
             break;
           }
           if(!metalink_next_res || *(++mlres) == NULL)
             break;
         }
-        else if(urlnum > 1) {
+        else
+#endif /* HAVE_LIBMETALINK */
+        if(urlnum > 1) {
           /* when url globbing, exit loop upon critical error */
           if(is_fatal_error(res))
             break;