Reduced #ifdef HAVE_METALINK
[platform/upstream/curl.git] / src / tool_metalink.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 #include "tool_setup.h"
23
24 #ifdef HAVE_UNISTD_H
25 #  include <unistd.h>
26 #endif
27
28 #include <sys/stat.h>
29
30 #ifdef HAVE_FCNTL_H
31 #  include <fcntl.h>
32 #endif
33
34 #include <metalink/metalink_parser.h>
35
36 #include "rawstr.h"
37
38 #include "tool_metalink.h"
39 #include "tool_getparam.h"
40 #include "tool_paramhlp.h"
41 #include "tool_cfgable.h"
42
43 #include "memdebug.h" /* keep this as LAST include */
44
45 /* Copied from tool_getparam.c */
46 #define GetStr(str,val) do { \
47   if(*(str)) { \
48     free(*(str)); \
49     *(str) = NULL; \
50   } \
51   if((val)) \
52     *(str) = strdup((val)); \
53   if(!(val)) \
54     return PARAM_NO_MEM; \
55 } WHILE_FALSE
56
57 #ifdef USE_GNUTLS_NETTLE
58
59 #include <nettle/md5.h>
60 #include <nettle/sha.h>
61
62 typedef struct md5_ctx MD5_CTX;
63
64 static void MD5_Init(MD5_CTX * ctx)
65 {
66   md5_init(ctx);
67 }
68
69 static void MD5_Update(MD5_CTX * ctx,
70                        const unsigned char * input,
71                        unsigned int inputLen)
72 {
73   md5_update(ctx, inputLen, input);
74 }
75
76 static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
77 {
78   md5_digest(ctx, 16, digest);
79 }
80
81 typedef struct sha1_ctx SHA_CTX;
82
83 static void SHA1_Init(SHA_CTX *ctx)
84 {
85   sha1_init(ctx);
86 }
87
88 static void SHA1_Update(SHA_CTX *ctx,
89                         const unsigned char * input,
90                         unsigned int inputLen)
91 {
92   sha1_update(ctx, inputLen, input);
93 }
94
95 static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx)
96 {
97   sha1_digest(ctx, 20, digest);
98 }
99
100 typedef struct sha256_ctx SHA256_CTX;
101
102 static void SHA256_Init(SHA256_CTX *ctx)
103 {
104   sha256_init(ctx);
105 }
106
107 static void SHA256_Update(SHA256_CTX *ctx,
108                           const unsigned char * input,
109                           unsigned int inputLen)
110 {
111   sha256_update(ctx, inputLen, input);
112 }
113
114 static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
115 {
116   sha256_digest(ctx, 32, digest);
117 }
118
119 #else
120
121 #ifdef USE_GNUTLS
122
123 #include <gcrypt.h>
124
125 typedef gcry_md_hd_t MD5_CTX;
126
127 static void MD5_Init(MD5_CTX * ctx)
128 {
129   gcry_md_open(ctx, GCRY_MD_MD5, 0);
130 }
131
132 static void MD5_Update(MD5_CTX * ctx,
133                        const unsigned char * input,
134                        unsigned int inputLen)
135 {
136   gcry_md_write(*ctx, input, inputLen);
137 }
138
139 static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
140 {
141   memcpy(digest, gcry_md_read(*ctx, 0), 16);
142   gcry_md_close(*ctx);
143 }
144
145 typedef gcry_md_hd_t SHA_CTX;
146
147 static void SHA1_Init(SHA_CTX * ctx)
148 {
149   gcry_md_open(ctx, GCRY_MD_SHA1, 0);
150 }
151
152 static void SHA1_Update(SHA_CTX * ctx,
153                        const unsigned char * input,
154                        unsigned int inputLen)
155 {
156   gcry_md_write(*ctx, input, inputLen);
157 }
158
159 static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx)
160 {
161   memcpy(digest, gcry_md_read(*ctx, 0), 20);
162   gcry_md_close(*ctx);
163 }
164
165 typedef gcry_md_hd_t SHA256_CTX;
166
167 static void SHA256_Init(SHA256_CTX * ctx)
168 {
169   gcry_md_open(ctx, GCRY_MD_SHA256, 0);
170 }
171
172 static void SHA256_Update(SHA256_CTX * ctx,
173                        const unsigned char * input,
174                        unsigned int inputLen)
175 {
176   gcry_md_write(*ctx, input, inputLen);
177 }
178
179 static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
180 {
181   memcpy(digest, gcry_md_read(*ctx, 0), 32);
182   gcry_md_close(*ctx);
183 }
184
185 #else
186
187 #ifdef USE_OPENSSL
188 #  include <openssl/md5.h>
189 #  include <openssl/sha.h>
190 #else /* USE_OPENSSL */
191
192 /* TODO hash functions for other libraries here */
193
194 #endif /* USE_OPENSSL */
195
196 #endif /* USE_GNUTLS */
197
198 #endif /* USE_GNUTLS_NETTLE */
199
200 #ifdef METALINK_HASH_CHECK
201
202 const digest_params MD5_DIGEST_PARAMS[] = {
203   {
204     (Curl_digest_init_func) MD5_Init,
205     (Curl_digest_update_func) MD5_Update,
206     (Curl_digest_final_func) MD5_Final,
207     sizeof(MD5_CTX),
208     16
209   }
210 };
211
212 const digest_params SHA1_DIGEST_PARAMS[] = {
213   {
214     (Curl_digest_init_func) SHA1_Init,
215     (Curl_digest_update_func) SHA1_Update,
216     (Curl_digest_final_func) SHA1_Final,
217     sizeof(SHA_CTX),
218     20
219   }
220 };
221
222 const digest_params SHA256_DIGEST_PARAMS[] = {
223   {
224     (Curl_digest_init_func) SHA256_Init,
225     (Curl_digest_update_func) SHA256_Update,
226     (Curl_digest_final_func) SHA256_Final,
227     sizeof(SHA256_CTX),
228     32
229   }
230 };
231
232 digest_context *Curl_digest_init(const digest_params *dparams)
233 {
234   digest_context *ctxt;
235
236   /* Create digest context */
237   ctxt = malloc(sizeof *ctxt);
238
239   if(!ctxt)
240     return ctxt;
241
242   ctxt->digest_hashctx = malloc(dparams->digest_ctxtsize);
243
244   if(!ctxt->digest_hashctx) {
245     free(ctxt);
246     return NULL;
247   }
248
249   ctxt->digest_hash = dparams;
250
251   dparams->digest_init(ctxt->digest_hashctx);
252
253   return ctxt;
254 }
255
256 int Curl_digest_update(digest_context *context,
257                        const unsigned char *data,
258                        unsigned int len)
259 {
260   (*context->digest_hash->digest_update)(context->digest_hashctx, data, len);
261
262   return 0;
263 }
264
265 int Curl_digest_final(digest_context *context, unsigned char *result)
266 {
267   (*context->digest_hash->digest_final)(result, context->digest_hashctx);
268
269   free(context->digest_hashctx);
270   free(context);
271
272   return 0;
273 }
274
275 static const metalink_digest_def SHA256_DIGEST_DEF[] = {
276   {"sha-256", SHA256_DIGEST_PARAMS}
277 };
278
279 static const metalink_digest_def SHA1_DIGEST_DEF[] = {
280   {"sha-1", SHA1_DIGEST_PARAMS}
281 };
282
283 static const metalink_digest_def MD5_DIGEST_DEF[] = {
284   {"md5", MD5_DIGEST_PARAMS}
285 };
286
287 /*
288  * The alias of supported hash functions in the order by preference
289  * (basically stronger hash comes first). We included "sha-256" and
290  * "sha256". The former is the name defined in the IANA registry named
291  * "Hash Function Textual Names". The latter is widely (and
292  * historically) used in Metalink version 3.
293  */
294 static const metalink_digest_alias digest_aliases[] = {
295   {"sha-256", SHA256_DIGEST_DEF},
296   {"sha256", SHA256_DIGEST_DEF},
297   {"sha-1", SHA1_DIGEST_DEF},
298   {"sha1", SHA1_DIGEST_DEF},
299   {"md5", MD5_DIGEST_DEF},
300   {NULL, NULL}
301 };
302
303 static unsigned char hex_to_uint(const char *s)
304 {
305   int v[2];
306   int i;
307   for(i = 0; i < 2; ++i) {
308     v[i] = Curl_raw_toupper(s[i]);
309     if('0' <= v[i] && v[i] <= '9') {
310       v[i] -= '0';
311     }
312     else if('A' <= v[i] && v[i] <= 'Z') {
313       v[i] -= 'A'-10;
314     }
315   }
316   return (unsigned char)((v[0] << 4) | v[1]);
317 }
318
319 /*
320  * Check checksum of file denoted by filename. The expected hash value
321  * is given in hex_hash which is hex-encoded string.
322  *
323  * This function returns 1 if it succeeds or one of the following
324  * integers:
325  *
326  * 0:
327  *   Checksum didn't match.
328  * -1:
329  *   Could not open file; or could not read data from file.
330  */
331 static int check_hash(const char *filename,
332                       const metalink_digest_def *digest_def,
333                       const char *hex_hash, FILE *error)
334 {
335   unsigned char *result;
336   digest_context *dctx;
337   int check_ok;
338   int fd;
339   size_t i;
340   fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name,
341           filename);
342   fd = open(filename, O_RDONLY);
343   if(fd == -1) {
344     fprintf(error, "Could not open file %s: %s\n", filename, strerror(errno));
345     return -1;
346   }
347   dctx = Curl_digest_init(digest_def->dparams);
348   result = malloc(digest_def->dparams->digest_resultlen);
349   while(1) {
350     unsigned char buf[4096];
351     ssize_t len = read(fd, buf, sizeof(buf));
352     if(len == 0) {
353       break;
354     }
355     else if(len == -1) {
356       fprintf(error, "Could not read file %s: %s\n", filename,
357               strerror(errno));
358       Curl_digest_final(dctx, result);
359       close(fd);
360       return -1;
361     }
362     Curl_digest_update(dctx, buf, (unsigned int)len);
363   }
364   Curl_digest_final(dctx, result);
365   check_ok = 1;
366   for(i = 0; i < digest_def->dparams->digest_resultlen; ++i) {
367     if(hex_to_uint(&hex_hash[i*2]) != result[i]) {
368       check_ok = 0;
369       break;
370     }
371   }
372   free(result);
373   close(fd);
374   return check_ok;
375 }
376
377 int metalink_check_hash(struct Configurable *config,
378                         metalinkfile *mlfile,
379                         const char *filename)
380 {
381   metalink_checksum *chksum;
382   const metalink_digest_alias *digest_alias;
383   int rv;
384   if(mlfile->checksum == NULL) {
385     return -2;
386   }
387   for(digest_alias = digest_aliases; digest_alias->alias_name;
388       ++digest_alias) {
389     for(chksum = mlfile->checksum; chksum; chksum = chksum->next) {
390       if(Curl_raw_equal(digest_alias->alias_name, chksum->hash_name) &&
391          strlen(chksum->hash_value) ==
392          digest_alias->digest_def->dparams->digest_resultlen*2) {
393         break;
394       }
395     }
396     if(chksum) {
397       break;
398     }
399   }
400   if(!digest_alias->alias_name) {
401     fprintf(config->errors, "No supported checksum in Metalink file\n");
402     return -2;
403   }
404   rv = check_hash(filename, digest_alias->digest_def,
405                   chksum->hash_value, config->errors);
406   if(rv == 1) {
407     fprintf(config->errors, "Checksum matched\n");
408   }
409   else if(rv == 0) {
410     fprintf(config->errors, "Checksum didn't match\n");
411   }
412   return rv;
413 }
414
415 #endif /* METALINK_HASH_CHECK */
416
417 #ifdef HAVE_LIBMETALINK
418
419 static metalink_checksum *new_metalink_checksum(const char *hash_name,
420                                                 const char *hash_value)
421 {
422   metalink_checksum *chksum;
423   chksum = malloc(sizeof(metalink_checksum));
424   chksum->next = NULL;
425   chksum->hash_name = strdup(hash_name);
426   chksum->hash_value = strdup(hash_value);
427   return chksum;
428 }
429
430 static metalink_resource *new_metalink_resource(const char *url)
431 {
432   metalink_resource *res;
433   res = malloc(sizeof(metalink_resource));
434   res->next = NULL;
435   res->url = strdup(url);
436   return res;
437 }
438
439 static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo)
440 {
441   metalinkfile *f;
442   f = (metalinkfile*)malloc(sizeof(metalinkfile));
443   f->next = NULL;
444   f->filename = strdup(fileinfo->name);
445   f->checksum = NULL;
446   f->resource = NULL;
447   if(fileinfo->checksums) {
448     metalink_checksum_t **p;
449     metalink_checksum root, *tail;
450     root.next = NULL;
451     tail = &root;
452     for(p = fileinfo->checksums; *p; ++p) {
453       metalink_checksum *chksum;
454       chksum = new_metalink_checksum((*p)->type, (*p)->hash);
455       tail->next = chksum;
456       tail = chksum;
457     }
458     f->checksum = root.next;
459   }
460   if(fileinfo->resources) {
461     metalink_resource_t **p;
462     metalink_resource root, *tail;
463     root.next = NULL;
464     tail = &root;
465     for(p = fileinfo->resources; *p; ++p) {
466       metalink_resource *res;
467       res = new_metalink_resource((*p)->url);
468       tail->next = res;
469       tail = res;
470     }
471     f->resource = root.next;
472   }
473   return f;
474 }
475
476 int parse_metalink(struct Configurable *config, const char *infile)
477 {
478   metalink_error_t r;
479   metalink_t* metalink;
480   metalink_file_t **files;
481
482   r = metalink_parse_file(infile, &metalink);
483   if(r != 0) {
484     return -1;
485   }
486   if(metalink->files == NULL) {
487     fprintf(config->errors, "\nMetalink does not contain any file.\n");
488     metalink_delete(metalink);
489     return 0;
490   }
491   for(files = metalink->files; *files; ++files) {
492     struct getout *url;
493     /* Skip an entry which has no resource. */
494     if(!(*files)->resources) {
495       fprintf(config->errors, "\nFile %s does not have any resource.\n",
496               (*files)->name);
497       continue;
498     }
499     if(config->url_get ||
500        ((config->url_get = config->url_list) != NULL)) {
501       /* there's a node here, if it already is filled-in continue to
502          find an "empty" node */
503       while(config->url_get && (config->url_get->flags & GETOUT_URL))
504         config->url_get = config->url_get->next;
505     }
506
507     /* now there might or might not be an available node to fill in! */
508
509     if(config->url_get)
510       /* existing node */
511       url = config->url_get;
512     else
513       /* there was no free node, create one! */
514       url = new_getout(config);
515
516     if(url) {
517       metalinkfile *mlfile;
518       mlfile = new_metalinkfile(*files);
519
520       /* Set name as url */
521       GetStr(&url->url, mlfile->filename);
522
523       /* set flag metalink here */
524       url->flags |= GETOUT_URL | GETOUT_METALINK;
525
526       if(config->metalinkfile_list) {
527         config->metalinkfile_last->next = mlfile;
528         config->metalinkfile_last = mlfile;
529       }
530       else {
531         config->metalinkfile_list = config->metalinkfile_last = mlfile;
532       }
533     }
534   }
535   metalink_delete(metalink);
536   return 0;
537 }
538
539 #endif /* HAVE_LIBMETALINK */
540
541 /*
542  * Returns nonzero if content_type includes mediatype.
543  */
544 static int check_content_type(const char *content_type, const char *media_type)
545 {
546   const char *ptr = content_type;
547   size_t media_type_len = strlen(media_type);
548   for(; *ptr && (*ptr == ' ' || *ptr == '\t'); ++ptr);
549   if(!*ptr) {
550     return 0;
551   }
552   return Curl_raw_nequal(ptr, media_type, media_type_len) &&
553     (*(ptr+media_type_len) == '\0' || *(ptr+media_type_len) == ' ' ||
554      *(ptr+media_type_len) == '\t' || *(ptr+media_type_len) == ';');
555 }
556
557 int check_metalink_content_type(const char *content_type)
558 {
559   return check_content_type(content_type, "application/metalink+xml");
560 }
561
562 int count_next_metalink_resource(metalinkfile *mlfile)
563 {
564   int count = 0;
565   metalink_resource *res;
566   for(res = mlfile->resource; res; res = res->next, ++count);
567   return count;
568 }
569
570 static void delete_metalink_checksum(metalink_checksum *chksum)
571 {
572   if(chksum == NULL) {
573     return;
574   }
575   Curl_safefree(chksum->hash_value);
576   Curl_safefree(chksum->hash_name);
577   Curl_safefree(chksum);
578 }
579
580 static void delete_metalink_resource(metalink_resource *res)
581 {
582   if(res == NULL) {
583     return;
584   }
585   Curl_safefree(res->url);
586   Curl_safefree(res);
587 }
588
589 static void delete_metalinkfile(metalinkfile *mlfile)
590 {
591   metalink_checksum *mc;
592   metalink_resource *res;
593   if(mlfile == NULL) {
594     return;
595   }
596   Curl_safefree(mlfile->filename);
597   for(mc = mlfile->checksum; mc;) {
598     metalink_checksum *next;
599     next = mc->next;
600     delete_metalink_checksum(mc);
601     mc = next;
602   }
603   for(res = mlfile->resource; res;) {
604     metalink_resource *next;
605     next = res->next;
606     delete_metalink_resource(res);
607     res = next;
608   }
609   Curl_safefree(mlfile);
610 }
611
612 void clean_metalink(struct Configurable *config)
613 {
614   while(config->metalinkfile_list) {
615     metalinkfile *mlfile = config->metalinkfile_list;
616     config->metalinkfile_list = config->metalinkfile_list->next;
617     delete_metalinkfile(mlfile);
618   }
619   config->metalinkfile_last = 0;
620 }