1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, 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 https://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 #undef HAVE_NSS_CONTEXT
36 # include <openssl/md5.h>
37 # include <openssl/sha.h>
38 #elif defined(USE_GNUTLS_NETTLE)
39 # include <nettle/md5.h>
40 # include <nettle/sha.h>
41 # define MD5_CTX struct md5_ctx
42 # define SHA_CTX struct sha1_ctx
43 # define SHA256_CTX struct sha256_ctx
44 #elif defined(USE_GNUTLS)
46 # define MD5_CTX gcry_md_hd_t
47 # define SHA_CTX gcry_md_hd_t
48 # define SHA256_CTX gcry_md_hd_t
49 #elif defined(USE_NSS)
52 # define MD5_CTX void *
53 # define SHA_CTX void *
54 # define SHA256_CTX void *
55 # define HAVE_NSS_CONTEXT
56 static NSSInitContext *nss_context;
57 #elif defined(USE_POLARSSL)
58 # include <polarssl/md5.h>
59 # include <polarssl/sha1.h>
60 # include <polarssl/sha256.h>
61 # define MD5_CTX md5_context
62 # define SHA_CTX sha1_context
63 # define SHA256_CTX sha256_context
64 #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
65 (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \
66 (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
67 (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000))
68 /* For Apple operating systems: CommonCrypto has the functions we need.
69 The library's headers are even backward-compatible with OpenSSL's
70 headers as long as we define COMMON_DIGEST_FOR_OPENSSL first.
72 These functions are available on Tiger and later, as well as iOS 2.0
73 and later. If you're building for an older cat, well, sorry. */
74 # define COMMON_DIGEST_FOR_OPENSSL
75 # include <CommonCrypto/CommonDigest.h>
77 /* For Windows: If no other crypto library is provided, we fallback
78 to the hash functions provided within the Microsoft Windows CryptoAPI */
79 # include <wincrypt.h>
80 /* Custom structure in order to store the required provider and hash handle */
81 struct win32_crypto_hash {
82 HCRYPTPROV hCryptProv;
85 /* Custom Microsoft AES Cryptographic Provider defines required for MinGW */
86 # ifndef ALG_SID_SHA_256
87 # define ALG_SID_SHA_256 12
90 # define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256)
92 # define MD5_CTX struct win32_crypto_hash
93 # define SHA_CTX struct win32_crypto_hash
94 # define SHA256_CTX struct win32_crypto_hash
96 # error "Can't compile METALINK support without a crypto library."
99 #define ENABLE_CURLX_PRINTF
100 /* use our own printf() functions */
103 #include "tool_getparam.h"
104 #include "tool_paramhlp.h"
105 #include "tool_cfgable.h"
106 #include "tool_metalink.h"
107 #include "tool_msgs.h"
109 #include "memdebug.h" /* keep this as LAST include */
111 /* Copied from tool_getparam.c */
112 #define GetStr(str,val) do { \
118 *(str) = strdup((val)); \
120 return PARAM_NO_MEM; \
123 #if defined(USE_OPENSSL)
124 /* Functions are already defined */
125 #elif defined(USE_GNUTLS_NETTLE)
127 static int MD5_Init(MD5_CTX *ctx)
133 static void MD5_Update(MD5_CTX *ctx,
134 const unsigned char *input,
135 unsigned int inputLen)
137 md5_update(ctx, inputLen, input);
140 static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
142 md5_digest(ctx, 16, digest);
145 static int SHA1_Init(SHA_CTX *ctx)
151 static void SHA1_Update(SHA_CTX *ctx,
152 const unsigned char *input,
153 unsigned int inputLen)
155 sha1_update(ctx, inputLen, input);
158 static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
160 sha1_digest(ctx, 20, digest);
163 static int SHA256_Init(SHA256_CTX *ctx)
169 static void SHA256_Update(SHA256_CTX *ctx,
170 const unsigned char *input,
171 unsigned int inputLen)
173 sha256_update(ctx, inputLen, input);
176 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
178 sha256_digest(ctx, 32, digest);
181 #elif defined(USE_GNUTLS)
183 static int MD5_Init(MD5_CTX *ctx)
185 gcry_md_open(ctx, GCRY_MD_MD5, 0);
189 static void MD5_Update(MD5_CTX *ctx,
190 const unsigned char *input,
191 unsigned int inputLen)
193 gcry_md_write(*ctx, input, inputLen);
196 static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
198 memcpy(digest, gcry_md_read(*ctx, 0), 16);
202 static int SHA1_Init(SHA_CTX *ctx)
204 gcry_md_open(ctx, GCRY_MD_SHA1, 0);
208 static void SHA1_Update(SHA_CTX *ctx,
209 const unsigned char *input,
210 unsigned int inputLen)
212 gcry_md_write(*ctx, input, inputLen);
215 static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
217 memcpy(digest, gcry_md_read(*ctx, 0), 20);
221 static int SHA256_Init(SHA256_CTX *ctx)
223 gcry_md_open(ctx, GCRY_MD_SHA256, 0);
227 static void SHA256_Update(SHA256_CTX *ctx,
228 const unsigned char *input,
229 unsigned int inputLen)
231 gcry_md_write(*ctx, input, inputLen);
234 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
236 memcpy(digest, gcry_md_read(*ctx, 0), 32);
240 #elif defined(USE_NSS)
242 static int nss_hash_init(void **pctx, SECOidTag hash_alg)
246 /* we have to initialize NSS if not initialized alraedy */
247 if(!NSS_IsInitialized() && !nss_context) {
248 static NSSInitParameters params;
249 params.length = sizeof params;
250 nss_context = NSS_InitContext("", "", "", "", ¶ms, NSS_INIT_READONLY
251 | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN
252 | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
255 ctx = PK11_CreateDigestContext(hash_alg);
257 return /* failure */ 0;
259 if(PK11_DigestBegin(ctx) != SECSuccess) {
260 PK11_DestroyContext(ctx, PR_TRUE);
261 return /* failure */ 0;
265 return /* success */ 1;
268 static void nss_hash_final(void **pctx, unsigned char *out, unsigned int len)
270 PK11Context *ctx = *pctx;
272 PK11_DigestFinal(ctx, out, &outlen, len);
273 PK11_DestroyContext(ctx, PR_TRUE);
276 static int MD5_Init(MD5_CTX *pctx)
278 return nss_hash_init(pctx, SEC_OID_MD5);
281 static void MD5_Update(MD5_CTX *pctx,
282 const unsigned char *input,
283 unsigned int input_len)
285 PK11_DigestOp(*pctx, input, input_len);
288 static void MD5_Final(unsigned char digest[16], MD5_CTX *pctx)
290 nss_hash_final(pctx, digest, 16);
293 static int SHA1_Init(SHA_CTX *pctx)
295 return nss_hash_init(pctx, SEC_OID_SHA1);
298 static void SHA1_Update(SHA_CTX *pctx,
299 const unsigned char *input,
300 unsigned int input_len)
302 PK11_DigestOp(*pctx, input, input_len);
305 static void SHA1_Final(unsigned char digest[20], SHA_CTX *pctx)
307 nss_hash_final(pctx, digest, 20);
310 static int SHA256_Init(SHA256_CTX *pctx)
312 return nss_hash_init(pctx, SEC_OID_SHA256);
315 static void SHA256_Update(SHA256_CTX *pctx,
316 const unsigned char *input,
317 unsigned int input_len)
319 PK11_DigestOp(*pctx, input, input_len);
322 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *pctx)
324 nss_hash_final(pctx, digest, 32);
327 #elif defined(USE_POLARSSL)
329 static int MD5_Init(MD5_CTX *ctx)
335 static void MD5_Update(MD5_CTX *ctx,
336 const unsigned char *input,
337 unsigned int inputLen)
339 md5_update(ctx, input, inputLen);
342 static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
344 md5_finish(ctx, digest);
347 static int SHA1_Init(SHA_CTX *ctx)
353 static void SHA1_Update(SHA_CTX *ctx,
354 const unsigned char *input,
355 unsigned int inputLen)
357 sha1_update(ctx, input, inputLen);
360 static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
362 sha1_finish(ctx, digest);
365 static int SHA256_Init(SHA256_CTX *ctx)
367 sha256_starts(ctx, 0); /* 0 = sha256 */
371 static void SHA256_Update(SHA256_CTX *ctx,
372 const unsigned char *input,
373 unsigned int inputLen)
375 sha256_update(ctx, input, inputLen);
378 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
380 sha256_finish(ctx, digest);
383 #elif defined(_WIN32)
385 static void win32_crypto_final(struct win32_crypto_hash *ctx,
386 unsigned char *digest,
387 unsigned int digestLen)
389 unsigned long length;
390 CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0);
391 if(length == digestLen)
392 CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0);
394 CryptDestroyHash(ctx->hHash);
396 CryptReleaseContext(ctx->hCryptProv, 0);
399 static int MD5_Init(MD5_CTX *ctx)
401 if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
402 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
403 CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash);
408 static void MD5_Update(MD5_CTX *ctx,
409 const unsigned char *input,
410 unsigned int inputLen)
412 CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
415 static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
417 win32_crypto_final(ctx, digest, 16);
420 static int SHA1_Init(SHA_CTX *ctx)
422 if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
423 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
424 CryptCreateHash(ctx->hCryptProv, CALG_SHA1, 0, 0, &ctx->hHash);
429 static void SHA1_Update(SHA_CTX *ctx,
430 const unsigned char *input,
431 unsigned int inputLen)
433 CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
436 static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
438 win32_crypto_final(ctx, digest, 20);
441 static int SHA256_Init(SHA256_CTX *ctx)
443 if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
444 PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
445 CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash);
450 static void SHA256_Update(SHA256_CTX *ctx,
451 const unsigned char *input,
452 unsigned int inputLen)
454 CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
457 static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
459 win32_crypto_final(ctx, digest, 32);
462 #endif /* CRYPTO LIBS */
464 const digest_params MD5_DIGEST_PARAMS[] = {
466 (Curl_digest_init_func) MD5_Init,
467 (Curl_digest_update_func) MD5_Update,
468 (Curl_digest_final_func) MD5_Final,
474 const digest_params SHA1_DIGEST_PARAMS[] = {
476 (Curl_digest_init_func) SHA1_Init,
477 (Curl_digest_update_func) SHA1_Update,
478 (Curl_digest_final_func) SHA1_Final,
484 const digest_params SHA256_DIGEST_PARAMS[] = {
486 (Curl_digest_init_func) SHA256_Init,
487 (Curl_digest_update_func) SHA256_Update,
488 (Curl_digest_final_func) SHA256_Final,
494 static const metalink_digest_def SHA256_DIGEST_DEF[] = {
495 {"sha-256", SHA256_DIGEST_PARAMS}
498 static const metalink_digest_def SHA1_DIGEST_DEF[] = {
499 {"sha-1", SHA1_DIGEST_PARAMS}
502 static const metalink_digest_def MD5_DIGEST_DEF[] = {
503 {"md5", MD5_DIGEST_PARAMS}
507 * The alias of supported hash functions in the order by preference
508 * (basically stronger hash comes first). We included "sha-256" and
509 * "sha256". The former is the name defined in the IANA registry named
510 * "Hash Function Textual Names". The latter is widely (and
511 * historically) used in Metalink version 3.
513 static const metalink_digest_alias digest_aliases[] = {
514 {"sha-256", SHA256_DIGEST_DEF},
515 {"sha256", SHA256_DIGEST_DEF},
516 {"sha-1", SHA1_DIGEST_DEF},
517 {"sha1", SHA1_DIGEST_DEF},
518 {"md5", MD5_DIGEST_DEF},
522 digest_context *Curl_digest_init(const digest_params *dparams)
524 digest_context *ctxt;
526 /* Create digest context */
527 ctxt = malloc(sizeof *ctxt);
532 ctxt->digest_hashctx = malloc(dparams->digest_ctxtsize);
534 if(!ctxt->digest_hashctx) {
539 ctxt->digest_hash = dparams;
541 if(dparams->digest_init(ctxt->digest_hashctx) != 1) {
549 int Curl_digest_update(digest_context *context,
550 const unsigned char *data,
553 (*context->digest_hash->digest_update)(context->digest_hashctx, data, len);
558 int Curl_digest_final(digest_context *context, unsigned char *result)
560 (*context->digest_hash->digest_final)(result, context->digest_hashctx);
562 free(context->digest_hashctx);
568 static unsigned char hex_to_uint(const char *s)
575 val = strtoul(buf, NULL, 16);
576 return (unsigned char)(val&0xff);
580 * Check checksum of file denoted by filename. The expected hash value
581 * is given in hex_hash which is hex-encoded string.
583 * This function returns 1 if it succeeds or one of the following
587 * Checksum didn't match.
589 * Could not open file; or could not read data from file.
591 * Hash algorithm not available.
593 static int check_hash(const char *filename,
594 const metalink_digest_def *digest_def,
595 const unsigned char *digest, FILE *error)
597 unsigned char *result;
598 digest_context *dctx;
599 int check_ok, flags, fd;
603 /* O_BINARY is required in order to avoid binary EOF in text mode */
607 fd = open(filename, flags);
609 fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
610 digest_def->hash_name, strerror(errno));
614 dctx = Curl_digest_init(digest_def->dparams);
616 fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
617 digest_def->hash_name, "failed to initialize hash algorithm");
622 result = malloc(digest_def->dparams->digest_resultlen);
628 unsigned char buf[4096];
629 ssize_t len = read(fd, buf, sizeof(buf));
634 fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
635 digest_def->hash_name, strerror(errno));
636 Curl_digest_final(dctx, result);
640 Curl_digest_update(dctx, buf, (unsigned int)len);
642 Curl_digest_final(dctx, result);
643 check_ok = memcmp(result, digest,
644 digest_def->dparams->digest_resultlen) == 0;
645 /* sha*sum style verdict output */
647 fprintf(error, "Metalink: validating (%s) [%s] OK\n", filename,
648 digest_def->hash_name);
650 fprintf(error, "Metalink: validating (%s) [%s] FAILED (digest mismatch)\n",
651 filename, digest_def->hash_name);
658 int metalink_check_hash(struct GlobalConfig *config,
659 metalinkfile *mlfile,
660 const char *filename)
663 fprintf(config->errors, "Metalink: validating (%s)...\n", filename);
664 if(mlfile->checksum == NULL) {
665 fprintf(config->errors,
666 "Metalink: validating (%s) FAILED (digest missing)\n", filename);
669 rv = check_hash(filename, mlfile->checksum->digest_def,
670 mlfile->checksum->digest, config->errors);
674 static metalink_checksum *new_metalink_checksum_from_hex_digest
675 (const metalink_digest_def *digest_def, const char *hex_digest)
677 metalink_checksum *chksum;
678 unsigned char *digest;
680 size_t len = strlen(hex_digest);
681 digest = malloc(len/2);
685 for(i = 0; i < len; i += 2) {
686 digest[i/2] = hex_to_uint(hex_digest + i);
688 chksum = malloc(sizeof(metalink_checksum));
690 chksum->digest_def = digest_def;
691 chksum->digest = digest;
696 static metalink_resource *new_metalink_resource(const char *url)
698 metalink_resource *res;
699 res = malloc(sizeof(metalink_resource));
702 res->url = strdup(url);
711 /* Returns nonzero if hex_digest is properly formatted; that is each
712 letter is in [0-9A-Za-z] and the length of the string equals to the
713 result length of digest * 2. */
714 static int check_hex_digest(const char *hex_digest,
715 const metalink_digest_def *digest_def)
718 for(i = 0; hex_digest[i]; ++i) {
719 char c = hex_digest[i];
720 if(!(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
721 ('A' <= c && c <= 'Z'))) {
725 return digest_def->dparams->digest_resultlen * 2 == i;
728 static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo)
731 f = (metalinkfile*)malloc(sizeof(metalinkfile));
736 f->filename = strdup(fileinfo->name);
743 if(fileinfo->checksums) {
744 const metalink_digest_alias *digest_alias;
745 for(digest_alias = digest_aliases; digest_alias->alias_name;
747 metalink_checksum_t **p;
748 for(p = fileinfo->checksums; *p; ++p) {
749 if(curl_strequal(digest_alias->alias_name, (*p)->type) &&
750 check_hex_digest((*p)->hash, digest_alias->digest_def)) {
752 new_metalink_checksum_from_hex_digest(digest_alias->digest_def,
762 if(fileinfo->resources) {
763 metalink_resource_t **p;
764 metalink_resource root, *tail;
767 for(p = fileinfo->resources; *p; ++p) {
768 metalink_resource *res;
769 /* Filter by type if it is non-NULL. In Metalink v3, type
770 includes the type of the resource. In curl, we are only
771 interested in HTTP, HTTPS and FTP. In addition to them,
772 Metalink v3 file may contain bittorrent type URL, which
773 points to the BitTorrent metainfo file. We ignore it here.
774 In Metalink v4, type was deprecated and all
775 fileinfo->resources point to the target file. BitTorrent
776 metainfo file URL may be appeared in fileinfo->metaurls.
778 if((*p)->type == NULL ||
779 curl_strequal((*p)->type, "http") ||
780 curl_strequal((*p)->type, "https") ||
781 curl_strequal((*p)->type, "ftp") ||
782 curl_strequal((*p)->type, "ftps")) {
783 res = new_metalink_resource((*p)->url);
788 f->resource = root.next;
793 int parse_metalink(struct OperationConfig *config, struct OutStruct *outs,
794 const char *metalink_url)
797 metalink_t* metalink;
798 metalink_file_t **files;
799 bool warnings = FALSE;
801 /* metlaink_parse_final deletes outs->metalink_parser */
802 r = metalink_parse_final(outs->metalink_parser, NULL, 0, &metalink);
803 outs->metalink_parser = NULL;
807 if(metalink->files == NULL) {
808 fprintf(config->global->errors, "Metalink: parsing (%s) WARNING "
809 "(missing or invalid file name)\n",
811 metalink_delete(metalink);
814 for(files = metalink->files; *files; ++files) {
816 /* Skip an entry which has no resource. */
817 if(!(*files)->resources) {
818 fprintf(config->global->errors, "Metalink: parsing (%s) WARNING "
819 "(missing or invalid resource)\n",
823 if(config->url_get ||
824 ((config->url_get = config->url_list) != NULL)) {
825 /* there's a node here, if it already is filled-in continue to
826 find an "empty" node */
827 while(config->url_get && (config->url_get->flags & GETOUT_URL))
828 config->url_get = config->url_get->next;
831 /* now there might or might not be an available node to fill in! */
835 url = config->url_get;
837 /* there was no free node, create one! */
838 url = new_getout(config);
841 metalinkfile *mlfile = new_metalinkfile(*files);
845 if(!mlfile->checksum) {
847 fprintf(config->global->errors,
848 "Metalink: parsing (%s) WARNING (digest missing)\n",
851 /* Set name as url */
852 GetStr(&url->url, mlfile->filename);
854 /* set flag metalink here */
855 url->flags |= GETOUT_URL | GETOUT_METALINK;
857 if(config->metalinkfile_list) {
858 config->metalinkfile_last->next = mlfile;
859 config->metalinkfile_last = mlfile;
862 config->metalinkfile_list = config->metalinkfile_last = mlfile;
866 metalink_delete(metalink);
867 return (warnings) ? -2 : 0;
870 size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb,
873 struct OutStruct *outs = userdata;
874 struct OperationConfig *config = outs->config;
878 * Once that libcurl has called back tool_write_cb() the returned value
879 * is checked against the amount that was intended to be written, if
880 * it does not match then it fails with CURLE_WRITE_ERROR. So at this
881 * point returning a value different from sz*nmemb indicates failure.
883 const size_t failure = (sz && nmemb) ? 0 : 1;
888 rv = metalink_parse_update(outs->metalink_parser, buffer, sz * nmemb);
892 fprintf(config->global->errors, "Metalink: parsing FAILED\n");
898 * Returns nonzero if content_type includes mediatype.
900 static int check_content_type(const char *content_type, const char *media_type)
902 const char *ptr = content_type;
903 size_t media_type_len = strlen(media_type);
904 for(; *ptr && (*ptr == ' ' || *ptr == '\t'); ++ptr);
908 return curl_strnequal(ptr, media_type, media_type_len) &&
909 (*(ptr + media_type_len) == '\0' || *(ptr + media_type_len) == ' ' ||
910 *(ptr + media_type_len) == '\t' || *(ptr + media_type_len) == ';');
913 int check_metalink_content_type(const char *content_type)
915 return check_content_type(content_type, "application/metalink+xml");
918 int count_next_metalink_resource(metalinkfile *mlfile)
921 metalink_resource *res;
922 for(res = mlfile->resource; res; res = res->next, ++count);
926 static void delete_metalink_checksum(metalink_checksum *chksum)
931 Curl_safefree(chksum->digest);
932 Curl_safefree(chksum);
935 static void delete_metalink_resource(metalink_resource *res)
940 Curl_safefree(res->url);
944 static void delete_metalinkfile(metalinkfile *mlfile)
946 metalink_resource *res;
950 Curl_safefree(mlfile->filename);
951 delete_metalink_checksum(mlfile->checksum);
952 for(res = mlfile->resource; res;) {
953 metalink_resource *next;
955 delete_metalink_resource(res);
958 Curl_safefree(mlfile);
961 void clean_metalink(struct OperationConfig *config)
963 while(config->metalinkfile_list) {
964 metalinkfile *mlfile = config->metalinkfile_list;
965 config->metalinkfile_list = config->metalinkfile_list->next;
966 delete_metalinkfile(mlfile);
968 config->metalinkfile_last = 0;
971 void metalink_cleanup(void)
973 #ifdef HAVE_NSS_CONTEXT
975 NSS_ShutdownContext(nss_context);
981 #endif /* USE_METALINK */