1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
22 #include "tool_setup.h"
33 # include <openssl/md5.h>
34 # include <openssl/sha.h>
35 #elif defined(USE_GNUTLS_NETTLE)
36 # include <nettle/md5.h>
37 # include <nettle/sha.h>
38 # define MD5_CTX struct md5_ctx
39 # define SHA_CTX struct sha1_ctx
40 # define SHA256_CTX struct sha256_ctx
41 #elif defined(USE_GNUTLS)
43 # define MD5_CTX gcry_md_hd_t
44 # define SHA_CTX gcry_md_hd_t
45 # define SHA256_CTX gcry_md_hd_t
46 #elif defined(USE_NSS)
49 # define MD5_CTX void *
50 # define SHA_CTX void *
51 # define SHA256_CTX void *
52 static NSSInitContext *nss_context;
53 #elif defined(USE_POLARSSL)
54 # include <polarssl/md5.h>
55 # include <polarssl/sha1.h>
56 # include <polarssl/sha256.h>
57 # define MD5_CTX md5_context
58 # define SHA_CTX sha1_context
59 # define SHA256_CTX sha256_context
60 #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
61 (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \
62 (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
63 (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000))
64 /* For Apple operating systems: CommonCrypto has the functions we need.
65 The library's headers are even backward-compatible with OpenSSL's
66 headers as long as we define COMMON_DIGEST_FOR_OPENSSL first.
68 These functions are available on Tiger and later, as well as iOS 2.0
69 and later. If you're building for an older cat, well, sorry. */
70 # define COMMON_DIGEST_FOR_OPENSSL
71 # include <CommonCrypto/CommonDigest.h>
73 /* For Windows: If no other crypto library is provided, we fallback
74 to the hash functions provided within the Microsoft Windows CryptoAPI */
75 # include <wincrypt.h>
76 /* Custom structure in order to store the required provider and hash handle */
77 struct win32_crypto_hash {
78 HCRYPTPROV hCryptProv;
81 /* Custom Microsoft AES Cryptographic Provider defines required for MinGW */
82 # ifndef ALG_SID_SHA_256
83 # define ALG_SID_SHA_256 12
86 # define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256)
88 # define MD5_CTX struct win32_crypto_hash
89 # define SHA_CTX struct win32_crypto_hash
90 # define SHA256_CTX struct win32_crypto_hash
92 # error "Can't compile METALINK support without a crypto library."
97 #define ENABLE_CURLX_PRINTF
98 /* use our own printf() functions */
101 #include "tool_getparam.h"
102 #include "tool_paramhlp.h"
103 #include "tool_cfgable.h"
104 #include "tool_metalink.h"
105 #include "tool_msgs.h"
107 #include "memdebug.h" /* keep this as LAST include */
109 /* Copied from tool_getparam.c */
110 #define GetStr(str,val) do { \
116 *(str) = strdup((val)); \
118 return PARAM_NO_MEM; \
121 #ifdef USE_GNUTLS_NETTLE
123 static int MD5_Init(MD5_CTX *ctx)
129 static void MD5_Update(MD5_CTX *ctx,
130 const unsigned char *input,
131 unsigned int inputLen)
133 md5_update(ctx, inputLen, input);
136 static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
138 md5_digest(ctx, 16, digest);
141 static int SHA1_Init(SHA_CTX *ctx)
147 static void SHA1_Update(SHA_CTX *ctx,
148 const unsigned char *input,
149 unsigned int inputLen)
151 sha1_update(ctx, inputLen, input);
154 static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
156 sha1_digest(ctx, 20, digest);
159 static int SHA256_Init(SHA256_CTX *ctx)
165 static void SHA256_Update(SHA256_CTX *ctx,
166 const unsigned char *input,
167 unsigned int inputLen)
169 sha256_update(ctx, inputLen, input);
172 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
174 sha256_digest(ctx, 32, digest);
177 #elif defined(USE_GNUTLS)
179 static int MD5_Init(MD5_CTX *ctx)
181 gcry_md_open(ctx, GCRY_MD_MD5, 0);
185 static void MD5_Update(MD5_CTX *ctx,
186 const unsigned char *input,
187 unsigned int inputLen)
189 gcry_md_write(*ctx, input, inputLen);
192 static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
194 memcpy(digest, gcry_md_read(*ctx, 0), 16);
198 static int SHA1_Init(SHA_CTX *ctx)
200 gcry_md_open(ctx, GCRY_MD_SHA1, 0);
204 static void SHA1_Update(SHA_CTX *ctx,
205 const unsigned char *input,
206 unsigned int inputLen)
208 gcry_md_write(*ctx, input, inputLen);
211 static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
213 memcpy(digest, gcry_md_read(*ctx, 0), 20);
217 static int SHA256_Init(SHA256_CTX *ctx)
219 gcry_md_open(ctx, GCRY_MD_SHA256, 0);
223 static void SHA256_Update(SHA256_CTX *ctx,
224 const unsigned char *input,
225 unsigned int inputLen)
227 gcry_md_write(*ctx, input, inputLen);
230 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
232 memcpy(digest, gcry_md_read(*ctx, 0), 32);
236 #elif defined(USE_NSS)
238 static int nss_hash_init(void **pctx, SECOidTag hash_alg)
242 /* we have to initialize NSS if not initialized alraedy */
243 if(!NSS_IsInitialized() && !nss_context) {
244 static NSSInitParameters params;
245 params.length = sizeof params;
246 nss_context = NSS_InitContext("", "", "", "", ¶ms, NSS_INIT_READONLY
247 | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN
248 | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
251 ctx = PK11_CreateDigestContext(hash_alg);
253 return /* failure */ 0;
255 if(PK11_DigestBegin(ctx) != SECSuccess) {
256 PK11_DestroyContext(ctx, PR_TRUE);
257 return /* failure */ 0;
261 return /* success */ 1;
264 static void nss_hash_final(void **pctx, unsigned char *out, unsigned int len)
266 PK11Context *ctx = *pctx;
268 PK11_DigestFinal(ctx, out, &outlen, len);
269 PK11_DestroyContext(ctx, PR_TRUE);
272 static int MD5_Init(MD5_CTX *pctx)
274 return nss_hash_init(pctx, SEC_OID_MD5);
277 static void MD5_Update(MD5_CTX *pctx,
278 const unsigned char *input,
279 unsigned int input_len)
281 PK11_DigestOp(*pctx, input, input_len);
284 static void MD5_Final(unsigned char digest[16], MD5_CTX *pctx)
286 nss_hash_final(pctx, digest, 16);
289 static int SHA1_Init(SHA_CTX *pctx)
291 return nss_hash_init(pctx, SEC_OID_SHA1);
294 static void SHA1_Update(SHA_CTX *pctx,
295 const unsigned char *input,
296 unsigned int input_len)
298 PK11_DigestOp(*pctx, input, input_len);
301 static void SHA1_Final(unsigned char digest[20], SHA_CTX *pctx)
303 nss_hash_final(pctx, digest, 20);
306 static int SHA256_Init(SHA256_CTX *pctx)
308 return nss_hash_init(pctx, SEC_OID_SHA256);
311 static void SHA256_Update(SHA256_CTX *pctx,
312 const unsigned char *input,
313 unsigned int input_len)
315 PK11_DigestOp(*pctx, input, input_len);
318 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *pctx)
320 nss_hash_final(pctx, digest, 32);
323 #elif defined(USE_POLARSSL)
325 static int MD5_Init(MD5_CTX *ctx)
331 static void MD5_Update(MD5_CTX *ctx,
332 const unsigned char *input,
333 unsigned int inputLen)
335 md5_update(ctx, input, inputLen);
338 static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
340 md5_finish(ctx, digest);
343 static int SHA1_Init(SHA_CTX *ctx)
349 static void SHA1_Update(SHA_CTX *ctx,
350 const unsigned char *input,
351 unsigned int inputLen)
353 sha1_update(ctx, input, inputLen);
356 static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
358 sha1_finish(ctx, digest);
361 static int SHA256_Init(SHA256_CTX *ctx)
363 sha256_starts(ctx, 0); /* 0 = sha256 */
367 static void SHA256_Update(SHA256_CTX *ctx,
368 const unsigned char *input,
369 unsigned int inputLen)
371 sha256_update(ctx, input, inputLen);
374 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
376 sha256_finish(ctx, digest);
379 #elif defined(_WIN32) && !defined(USE_OPENSSL)
381 static void win32_crypto_final(struct win32_crypto_hash *ctx,
382 unsigned char *digest,
383 unsigned int digestLen)
385 unsigned long length;
386 CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0);
387 if(length == digestLen)
388 CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0);
390 CryptDestroyHash(ctx->hHash);
392 CryptReleaseContext(ctx->hCryptProv, 0);
395 static int MD5_Init(MD5_CTX *ctx)
397 if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
398 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
399 CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash);
404 static void MD5_Update(MD5_CTX *ctx,
405 const unsigned char *input,
406 unsigned int inputLen)
408 CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
411 static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
413 win32_crypto_final(ctx, digest, 16);
416 static int SHA1_Init(SHA_CTX *ctx)
418 if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
419 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
420 CryptCreateHash(ctx->hCryptProv, CALG_SHA1, 0, 0, &ctx->hHash);
425 static void SHA1_Update(SHA_CTX *ctx,
426 const unsigned char *input,
427 unsigned int inputLen)
429 CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
432 static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
434 win32_crypto_final(ctx, digest, 20);
437 static int SHA256_Init(SHA256_CTX *ctx)
439 if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
440 PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
441 CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash);
446 static void SHA256_Update(SHA256_CTX *ctx,
447 const unsigned char *input,
448 unsigned int inputLen)
450 CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
453 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
455 win32_crypto_final(ctx, digest, 32);
458 #endif /* CRYPTO LIBS */
460 const digest_params MD5_DIGEST_PARAMS[] = {
462 (Curl_digest_init_func) MD5_Init,
463 (Curl_digest_update_func) MD5_Update,
464 (Curl_digest_final_func) MD5_Final,
470 const digest_params SHA1_DIGEST_PARAMS[] = {
472 (Curl_digest_init_func) SHA1_Init,
473 (Curl_digest_update_func) SHA1_Update,
474 (Curl_digest_final_func) SHA1_Final,
480 const digest_params SHA256_DIGEST_PARAMS[] = {
482 (Curl_digest_init_func) SHA256_Init,
483 (Curl_digest_update_func) SHA256_Update,
484 (Curl_digest_final_func) SHA256_Final,
490 static const metalink_digest_def SHA256_DIGEST_DEF[] = {
491 {"sha-256", SHA256_DIGEST_PARAMS}
494 static const metalink_digest_def SHA1_DIGEST_DEF[] = {
495 {"sha-1", SHA1_DIGEST_PARAMS}
498 static const metalink_digest_def MD5_DIGEST_DEF[] = {
499 {"md5", MD5_DIGEST_PARAMS}
503 * The alias of supported hash functions in the order by preference
504 * (basically stronger hash comes first). We included "sha-256" and
505 * "sha256". The former is the name defined in the IANA registry named
506 * "Hash Function Textual Names". The latter is widely (and
507 * historically) used in Metalink version 3.
509 static const metalink_digest_alias digest_aliases[] = {
510 {"sha-256", SHA256_DIGEST_DEF},
511 {"sha256", SHA256_DIGEST_DEF},
512 {"sha-1", SHA1_DIGEST_DEF},
513 {"sha1", SHA1_DIGEST_DEF},
514 {"md5", MD5_DIGEST_DEF},
518 digest_context *Curl_digest_init(const digest_params *dparams)
520 digest_context *ctxt;
522 /* Create digest context */
523 ctxt = malloc(sizeof *ctxt);
528 ctxt->digest_hashctx = malloc(dparams->digest_ctxtsize);
530 if(!ctxt->digest_hashctx) {
535 ctxt->digest_hash = dparams;
537 if(dparams->digest_init(ctxt->digest_hashctx) != 1) {
545 int Curl_digest_update(digest_context *context,
546 const unsigned char *data,
549 (*context->digest_hash->digest_update)(context->digest_hashctx, data, len);
554 int Curl_digest_final(digest_context *context, unsigned char *result)
556 (*context->digest_hash->digest_final)(result, context->digest_hashctx);
558 free(context->digest_hashctx);
564 static unsigned char hex_to_uint(const char *s)
568 for(i = 0; i < 2; ++i) {
569 v[i] = Curl_raw_toupper(s[i]);
570 if('0' <= v[i] && v[i] <= '9') {
573 else if('A' <= v[i] && v[i] <= 'Z') {
577 return (unsigned char)((v[0] << 4) | v[1]);
581 * Check checksum of file denoted by filename. The expected hash value
582 * is given in hex_hash which is hex-encoded string.
584 * This function returns 1 if it succeeds or one of the following
588 * Checksum didn't match.
590 * Could not open file; or could not read data from file.
592 * Hash algorithm not available.
594 static int check_hash(const char *filename,
595 const metalink_digest_def *digest_def,
596 const unsigned char *digest, FILE *error)
598 unsigned char *result;
599 digest_context *dctx;
600 int check_ok, flags, fd;
604 /* O_BINARY is required in order to avoid binary EOF in text mode */
608 fd = open(filename, flags);
610 fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
611 digest_def->hash_name, strerror(errno));
615 dctx = Curl_digest_init(digest_def->dparams);
617 fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
618 digest_def->hash_name, "failed to initialize hash algorithm");
623 result = malloc(digest_def->dparams->digest_resultlen);
629 unsigned char buf[4096];
630 ssize_t len = read(fd, buf, sizeof(buf));
635 fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
636 digest_def->hash_name, strerror(errno));
637 Curl_digest_final(dctx, result);
641 Curl_digest_update(dctx, buf, (unsigned int)len);
643 Curl_digest_final(dctx, result);
644 check_ok = memcmp(result, digest,
645 digest_def->dparams->digest_resultlen) == 0;
646 /* sha*sum style verdict output */
648 fprintf(error, "Metalink: validating (%s) [%s] OK\n", filename,
649 digest_def->hash_name);
651 fprintf(error, "Metalink: validating (%s) [%s] FAILED (digest mismatch)\n",
652 filename, digest_def->hash_name);
659 int metalink_check_hash(struct GlobalConfig *config,
660 metalinkfile *mlfile,
661 const char *filename)
664 fprintf(config->errors, "Metalink: validating (%s)...\n", filename);
665 if(mlfile->checksum == NULL) {
666 fprintf(config->errors,
667 "Metalink: validating (%s) FAILED (digest missing)\n", filename);
670 rv = check_hash(filename, mlfile->checksum->digest_def,
671 mlfile->checksum->digest, config->errors);
675 static metalink_checksum *new_metalink_checksum_from_hex_digest
676 (const metalink_digest_def *digest_def, const char *hex_digest)
678 metalink_checksum *chksum;
679 unsigned char *digest;
681 size_t len = strlen(hex_digest);
682 digest = malloc(len/2);
686 for(i = 0; i < len; i += 2) {
687 digest[i/2] = hex_to_uint(hex_digest+i);
689 chksum = malloc(sizeof(metalink_checksum));
691 chksum->digest_def = digest_def;
692 chksum->digest = digest;
697 static metalink_resource *new_metalink_resource(const char *url)
699 metalink_resource *res;
700 res = malloc(sizeof(metalink_resource));
703 res->url = strdup(url);
712 /* Returns nonzero if hex_digest is properly formatted; that is each
713 letter is in [0-9A-Za-z] and the length of the string equals to the
714 result length of digest * 2. */
715 static int check_hex_digest(const char *hex_digest,
716 const metalink_digest_def *digest_def)
719 for(i = 0; hex_digest[i]; ++i) {
720 char c = hex_digest[i];
721 if(!(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
722 ('A' <= c && c <= 'Z'))) {
726 return digest_def->dparams->digest_resultlen * 2 == i;
729 static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo)
732 f = (metalinkfile*)malloc(sizeof(metalinkfile));
737 f->filename = strdup(fileinfo->name);
744 if(fileinfo->checksums) {
745 const metalink_digest_alias *digest_alias;
746 for(digest_alias = digest_aliases; digest_alias->alias_name;
748 metalink_checksum_t **p;
749 for(p = fileinfo->checksums; *p; ++p) {
750 if(Curl_raw_equal(digest_alias->alias_name, (*p)->type) &&
751 check_hex_digest((*p)->hash, digest_alias->digest_def)) {
753 new_metalink_checksum_from_hex_digest(digest_alias->digest_def,
763 if(fileinfo->resources) {
764 metalink_resource_t **p;
765 metalink_resource root, *tail;
768 for(p = fileinfo->resources; *p; ++p) {
769 metalink_resource *res;
770 /* Filter by type if it is non-NULL. In Metalink v3, type
771 includes the type of the resource. In curl, we are only
772 interested in HTTP, HTTPS and FTP. In addition to them,
773 Metalink v3 file may contain bittorrent type URL, which
774 points to the BitTorrent metainfo file. We ignore it here.
775 In Metalink v4, type was deprecated and all
776 fileinfo->resources point to the target file. BitTorrent
777 metainfo file URL may be appeared in fileinfo->metaurls.
779 if((*p)->type == NULL ||
780 Curl_raw_equal((*p)->type, "http") ||
781 Curl_raw_equal((*p)->type, "https") ||
782 Curl_raw_equal((*p)->type, "ftp") ||
783 Curl_raw_equal((*p)->type, "ftps")) {
784 res = new_metalink_resource((*p)->url);
789 f->resource = root.next;
794 int parse_metalink(struct OperationConfig *config, struct OutStruct *outs,
795 const char *metalink_url)
798 metalink_t* metalink;
799 metalink_file_t **files;
800 bool warnings = FALSE;
802 /* metlaink_parse_final deletes outs->metalink_parser */
803 r = metalink_parse_final(outs->metalink_parser, NULL, 0, &metalink);
804 outs->metalink_parser = NULL;
808 if(metalink->files == NULL) {
809 fprintf(config->global->errors, "Metalink: parsing (%s) WARNING "
810 "(missing or invalid file name)\n",
812 metalink_delete(metalink);
815 for(files = metalink->files; *files; ++files) {
817 /* Skip an entry which has no resource. */
818 if(!(*files)->resources) {
819 fprintf(config->global->errors, "Metalink: parsing (%s) WARNING "
820 "(missing or invalid resource)\n",
821 metalink_url, (*files)->name);
824 if(config->url_get ||
825 ((config->url_get = config->url_list) != NULL)) {
826 /* there's a node here, if it already is filled-in continue to
827 find an "empty" node */
828 while(config->url_get && (config->url_get->flags & GETOUT_URL))
829 config->url_get = config->url_get->next;
832 /* now there might or might not be an available node to fill in! */
836 url = config->url_get;
838 /* there was no free node, create one! */
839 url = new_getout(config);
842 metalinkfile *mlfile = new_metalinkfile(*files);
846 if(!mlfile->checksum) {
848 fprintf(config->global->errors,
849 "Metalink: parsing (%s) WARNING (digest missing)\n",
852 /* Set name as url */
853 GetStr(&url->url, mlfile->filename);
855 /* set flag metalink here */
856 url->flags |= GETOUT_URL | GETOUT_METALINK;
858 if(config->metalinkfile_list) {
859 config->metalinkfile_last->next = mlfile;
860 config->metalinkfile_last = mlfile;
863 config->metalinkfile_list = config->metalinkfile_last = mlfile;
867 metalink_delete(metalink);
868 return (warnings) ? -2 : 0;
871 size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb,
874 struct OutStruct *outs = userdata;
875 struct OperationConfig *config = outs->config;
879 * Once that libcurl has called back tool_write_cb() the returned value
880 * is checked against the amount that was intended to be written, if
881 * it does not match then it fails with CURLE_WRITE_ERROR. So at this
882 * point returning a value different from sz*nmemb indicates failure.
884 const size_t failure = (sz * nmemb) ? 0 : 1;
889 rv = metalink_parse_update(outs->metalink_parser, buffer, sz *nmemb);
893 fprintf(config->global->errors, "Metalink: parsing FAILED\n");
899 * Returns nonzero if content_type includes mediatype.
901 static int check_content_type(const char *content_type, const char *media_type)
903 const char *ptr = content_type;
904 size_t media_type_len = strlen(media_type);
905 for(; *ptr && (*ptr == ' ' || *ptr == '\t'); ++ptr);
909 return Curl_raw_nequal(ptr, media_type, media_type_len) &&
910 (*(ptr+media_type_len) == '\0' || *(ptr+media_type_len) == ' ' ||
911 *(ptr+media_type_len) == '\t' || *(ptr+media_type_len) == ';');
914 int check_metalink_content_type(const char *content_type)
916 return check_content_type(content_type, "application/metalink+xml");
919 int count_next_metalink_resource(metalinkfile *mlfile)
922 metalink_resource *res;
923 for(res = mlfile->resource; res; res = res->next, ++count);
927 static void delete_metalink_checksum(metalink_checksum *chksum)
932 Curl_safefree(chksum->digest);
933 Curl_safefree(chksum);
936 static void delete_metalink_resource(metalink_resource *res)
941 Curl_safefree(res->url);
945 static void delete_metalinkfile(metalinkfile *mlfile)
947 metalink_resource *res;
951 Curl_safefree(mlfile->filename);
952 delete_metalink_checksum(mlfile->checksum);
953 for(res = mlfile->resource; res;) {
954 metalink_resource *next;
956 delete_metalink_resource(res);
959 Curl_safefree(mlfile);
962 void clean_metalink(struct OperationConfig *config)
964 while(config->metalinkfile_list) {
965 metalinkfile *mlfile = config->metalinkfile_list;
966 config->metalinkfile_list = config->metalinkfile_list->next;
967 delete_metalinkfile(mlfile);
969 config->metalinkfile_last = 0;
972 void metalink_cleanup(void)
976 NSS_ShutdownContext(nss_context);
982 #endif /* USE_METALINK */