From b5fdbe848bc3d088445817aa890d3f2f74ac5b02 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 26 Apr 2012 22:59:52 +0900 Subject: Support Metalink. This change adds experimental Metalink support to curl. To enable Metalink support, run configure with --with-libmetalink. To feed Metalink file to curl, use --metalink option like this: $ curl -O --metalink foo.metalink We use libmetalink to parse Metalink files. --- src/tool_metalink.c | 990 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 990 insertions(+) create mode 100644 src/tool_metalink.c (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c new file mode 100644 index 000000000..558fa8fa0 --- /dev/null +++ b/src/tool_metalink.c @@ -0,0 +1,990 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_UTIME_H +# include +#elif defined(HAVE_SYS_UTIME_H) +# include +#endif + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_metalink.h" +#include "tool_binmode.h" +#include "tool_cb_prg.h" +#include "tool_cb_wrt.h" +#include "tool_cb_rea.h" +#include "tool_cb_see.h" +#include "tool_cb_dbg.h" +#include "tool_dirhie.h" +#include "tool_util.h" +#include "tool_main.h" +#include "tool_msgs.h" +#include "tool_setopt.h" +#include "tool_sleep.h" +#include "tool_writeout.h" +#include "tool_libinfo.h" +#include "tool_homedir.h" +#include "tool_cb_hdr.h" + +#include "memdebug.h" /* keep this as LAST include */ + +static int curl_truncate_file(struct Configurable *config, + struct OutStruct outs) +{ + int res = 0; + if(outs.bytes && outs.filename) { + /* We have written data to a output file, we truncate file + */ + if(!config->mute) + fprintf(config->errors, "Throwing away %" + CURL_FORMAT_CURL_OFF_T " bytes\n", + outs.bytes); + fflush(outs.stream); + /* truncate file at the position where we started appending */ +#ifdef HAVE_FTRUNCATE + if(ftruncate( fileno(outs.stream), outs.init)) { + /* when truncate fails, we can't just append as then we'll + create something strange, bail out */ + if(!config->mute) + fprintf(config->errors, + "failed to truncate, exiting\n"); + res = CURLE_WRITE_ERROR; + assert(0); + /* goto quit_urls; */ + } + /* now seek to the end of the file, the position where we + just truncated the file in a large file-safe way */ + fseek(outs.stream, 0, SEEK_END); +#else + /* ftruncate is not available, so just reposition the file + to the location we would have truncated it. This won't + work properly with large files on 32-bit systems, but + most of those will have ftruncate. */ + fseek(outs.stream, (long)outs.init, SEEK_SET); +#endif + outs.bytes = 0; /* clear for next round */ + } + return res; +} + +struct metalinkfile *new_metalinkfile(metalink_file_t *metalinkfile) { + struct metalinkfile *f; + f = (struct metalinkfile*)malloc(sizeof(struct metalinkfile)); + f->file = metalinkfile; + f->next = NULL; + return f; +} + +struct metalink *new_metalink(metalink_t *metalink) { + struct metalink *ml; + ml = (struct metalink*)malloc(sizeof(struct metalink)); + ml->metalink = metalink; + ml->next = NULL; + return ml; +} + +int operatemetalink(CURL *curl, + struct getout *urlnode, + long retry_sleep_default, + struct OutStruct outs, + struct OutStruct heads, + char *outfiles, + struct Configurable *config) +{ + long retry_numretries; + int infd = STDIN_FILENO; + bool infdopen; + char *outfile; + struct timeval retrystart; + struct metalinkfile *mlfile; + int res = 0; + char *filename; + metalink_resource_t **mlres; + char errorbuffer[CURL_ERROR_SIZE]; + struct ProgressData progressbar; + long retry_sleep; + struct InStruct input; + curl_off_t uploadfilesize; /* -1 means unknown */ + char *uploadfile=NULL; /* a single file, never a glob */ + + uploadfilesize=-1; + + mlfile = config->metalinkfile_last; + config->metalinkfile_last = config->metalinkfile_last->next; + + filename = strdup(mlfile->file->name); + + outfile = outfiles?strdup(outfiles):NULL; + + if((urlnode->flags & GETOUT_USEREMOTE) || + (outfile && !curlx_strequal("-", outfile)) ) { + + /* + * We have specified a file name to store the result in, or we have + * decided we want to use the name attribute of file element in Metalink + * XML. + */ + + if(!outfile) { + /* Find and get file name */ + char * pc; + pc = strrchr(filename, '/'); + + if(pc) { + /* duplicate the string beyond the slash */ + pc++; + outfile = *pc ? strdup(pc): NULL; + } + else { + outfile = strdup(filename); + } + + if(!outfile || !*outfile) { + helpf(config->errors, "Metalink file[@name] has no length!\n"); + res = CURLE_WRITE_ERROR; + free(filename); + /* break; */ + return 1; + } +#if defined(MSDOS) + { + /* This is for DOS, and then we do some major replacing of + bad characters in the file name before using it */ + char file1[PATH_MAX]; + if(strlen(outfile) >= PATH_MAX) + outfile[PATH_MAX-1]=0; /* cut it */ + strcpy(file1, msdosify(outfile)); + free(outfile); + + outfile = strdup(rename_if_dos_device_name(file1)); + if(!outfile) { + res = CURLE_OUT_OF_MEMORY; + break; + } + } +#endif /* MSDOS */ + } + /* Create the directory hierarchy, if not pre-existant to a multiple + file output call */ + + if(config->create_dirs && + (CURLE_WRITE_ERROR == create_dir_hierarchy(outfile, config->errors))) + return CURLE_WRITE_ERROR; + + if(config->resume_from_current) { + /* We're told to continue from where we are now. Get the + size of the file as it is now and open it for append instead */ + + struct_stat fileinfo; + + /* VMS -- Danger, the filesize is only valid for stream files */ + if(0 == stat(outfile, &fileinfo)) + /* set offset to current file size: */ + config->resume_from = fileinfo.st_size; + else + /* let offset be 0 */ + config->resume_from = 0; + } + + outs.filename = outfile; + outs.s_isreg = TRUE; + + if(config->resume_from) { + outs.init = config->resume_from; + /* open file for output: */ + outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb"); + if(!outs.stream) { + helpf(config->errors, "Can't open '%s'!\n", outfile); + return CURLE_WRITE_ERROR; + } + } + else { + outs.stream = NULL; /* open when needed */ + } + } + infdopen=FALSE; + + if(!config->errors) + config->errors = stderr; + + if(!outfile && !config->use_ascii) { + /* We get the output to stdout and we have not got the ASCII/text + flag, then set stdout to be binary */ + set_binmode(stdout); + } + + /* Loop though all resources in Metalink */ + for(mlres = mlfile->file->resources; *mlres; ++mlres) { + int try_next_res = 0; + if(config->tcp_nodelay) + my_setopt(curl, CURLOPT_TCP_NODELAY, 1); + + /* where to store */ + my_setopt(curl, CURLOPT_WRITEDATA, &outs); + /* what call to write */ + my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb); + + + /* for uploads */ + input.fd = infd; + input.config = config; + /* Note that if CURLOPT_READFUNCTION is fread (the default), then + * lib/telnet.c will Curl_poll() on the input file descriptor + * rather then calling the READFUNCTION at regular intervals. + * The circumstances in which it is preferable to enable this + * behaviour, by omitting to set the READFUNCTION & READDATA options, + * have not been determined. + */ + my_setopt(curl, CURLOPT_READDATA, &input); + /* what call to read */ + my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb); + + /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what + CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */ + my_setopt(curl, CURLOPT_SEEKDATA, &input); + my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb); + + if(config->recvpersecond) + /* tell libcurl to use a smaller sized buffer as it allows us to + make better sleeps! 7.9.9 stuff! */ + my_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond); + + /* size of uploaded file: */ + if(uploadfilesize != -1) + my_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize); + my_setopt(curl, CURLOPT_URL, (*mlres)->url); /* what to fetch */ + my_setopt(curl, CURLOPT_NOPROGRESS, config->noprogress); + if(config->no_body) { + my_setopt(curl, CURLOPT_NOBODY, 1); + my_setopt(curl, CURLOPT_HEADER, 1); + } + else + my_setopt(curl, CURLOPT_HEADER, config->include_headers); + +#if !defined(CURL_DISABLE_PROXY) + { + /* TODO: Make this a run-time check instead of compile-time one. */ + + my_setopt_str(curl, CURLOPT_PROXY, config->proxy); + my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); + + /* new in libcurl 7.3 */ + my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel); + + /* new in libcurl 7.5 */ + if(config->proxy) + my_setopt_enum(curl, CURLOPT_PROXYTYPE, config->proxyver); + + /* new in libcurl 7.10 */ + if(config->socksproxy) { + my_setopt_str(curl, CURLOPT_PROXY, config->socksproxy); + my_setopt_enum(curl, CURLOPT_PROXYTYPE, config->socksver); + } + + /* new in libcurl 7.10.6 */ + if(config->proxyanyauth) + my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, + (long) CURLAUTH_ANY); + else if(config->proxynegotiate) + my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, + (long) CURLAUTH_GSSNEGOTIATE); + else if(config->proxyntlm) + my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, + (long) CURLAUTH_NTLM); + else if(config->proxydigest) + my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, + (long) CURLAUTH_DIGEST); + else if(config->proxybasic) + my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, + (long) CURLAUTH_BASIC); + + /* new in libcurl 7.19.4 */ + my_setopt(curl, CURLOPT_NOPROXY, config->noproxy); + } +#endif + + my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror); + my_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE); + my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly); + my_setopt(curl, CURLOPT_APPEND, config->ftp_append); + + if(config->netrc_opt) + my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); + else if(config->netrc || config->netrc_file) + my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED); + else + my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED); + + if(config->netrc_file) + my_setopt(curl, CURLOPT_NETRC_FILE, config->netrc_file); + + my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); + my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd); + my_setopt_str(curl, CURLOPT_RANGE, config->range); + my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer); + my_setopt(curl, CURLOPT_TIMEOUT, config->timeout); + + if(built_in_protos & CURLPROTO_HTTP) { + + long postRedir = 0; + + my_setopt(curl, CURLOPT_FOLLOWLOCATION, + config->followlocation); + my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, + config->unrestricted_auth); + + switch(config->httpreq) { + case HTTPREQ_SIMPLEPOST: + my_setopt_str(curl, CURLOPT_POSTFIELDS, + config->postfields); + my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, + config->postfieldsize); + break; + case HTTPREQ_POST: + my_setopt_httppost(curl, CURLOPT_HTTPPOST, config->httppost); + break; + default: + break; + } + + my_setopt_str(curl, CURLOPT_REFERER, config->referer); + my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer); + my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent); + my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers); + + /* new in libcurl 7.5 */ + my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs); + + /* new in libcurl 7.9.1 */ + if(config->httpversion) + my_setopt_enum(curl, CURLOPT_HTTP_VERSION, config->httpversion); + + /* new in libcurl 7.10.6 (default is Basic) */ + if(config->authtype) + my_setopt_bitmask(curl, CURLOPT_HTTPAUTH, (long) config->authtype); + + /* curl 7.19.1 (the 301 version existed in 7.18.2), + 303 was added in 7.26.0 */ + if(config->post301) + postRedir |= CURL_REDIR_POST_301; + if(config->post302) + postRedir |= CURL_REDIR_POST_302; + if(config->post303) + postRedir |= CURL_REDIR_POST_303; + my_setopt(curl, CURLOPT_POSTREDIR, postRedir); + + /* new in libcurl 7.21.6 */ + if(config->encoding) + my_setopt_str(curl, CURLOPT_ACCEPT_ENCODING, ""); + + /* new in libcurl 7.21.6 */ + if(config->tr_encoding) + my_setopt(curl, CURLOPT_TRANSFER_ENCODING, 1); + + } /* (built_in_protos & CURLPROTO_HTTP) */ + + my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport); + my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, + config->low_speed_limit); + my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time); + my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, + config->sendpersecond); + my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, + config->recvpersecond); + my_setopt(curl, CURLOPT_RESUME_FROM_LARGE, + config->use_resume?config->resume_from:0); + + my_setopt(curl, CURLOPT_SSLCERT, config->cert); + my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type); + my_setopt(curl, CURLOPT_SSLKEY, config->key); + my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type); + my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd); + + if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) { + + /* SSH and SSL private key uses same command-line option */ + /* new in libcurl 7.16.1 */ + my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key); + /* new in libcurl 7.16.1 */ + my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey); + + /* new in libcurl 7.17.1: SSH host key md5 checking allows us + to fail if we are not talking to who we think we should */ + my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, + config->hostpubmd5); + } + + if(config->cacert) + my_setopt_str(curl, CURLOPT_CAINFO, config->cacert); + if(config->capath) + my_setopt_str(curl, CURLOPT_CAPATH, config->capath); + if(config->crlfile) + my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile); + + if(curlinfo->features & CURL_VERSION_SSL) { + if(config->insecure_ok) { + my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); + } + else { + my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + /* libcurl default is strict verifyhost -> 2L */ + /* my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); */ + } + } + + if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) { + if(!config->insecure_ok) { + char *home; + char *file; + res = CURLE_OUT_OF_MEMORY; + home = homedir(); + if(home) { + file = aprintf("%s/%sssh/known_hosts", home, DOT_CHAR); + if(file) { + /* new in curl 7.19.6 */ + res = res_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, file); + curl_free(file); + if(res == CURLE_UNKNOWN_OPTION) + /* libssh2 version older than 1.1.1 */ + res = CURLE_OK; + } + Curl_safefree(home); + } + if(res) + goto show_error; + } + } + + if(config->no_body || config->remote_time) { + /* no body or use remote time */ + my_setopt(curl, CURLOPT_FILETIME, TRUE); + } + + my_setopt(curl, CURLOPT_CRLF, config->crlf); + my_setopt_slist(curl, CURLOPT_QUOTE, config->quote); + my_setopt_slist(curl, CURLOPT_POSTQUOTE, config->postquote); + my_setopt_slist(curl, CURLOPT_PREQUOTE, config->prequote); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + { + /* TODO: Make this a run-time check instead of compile-time one. */ + + if(config->cookie) + my_setopt_str(curl, CURLOPT_COOKIE, config->cookie); + + if(config->cookiefile) + my_setopt_str(curl, CURLOPT_COOKIEFILE, config->cookiefile); + + /* new in libcurl 7.9 */ + if(config->cookiejar) + my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar); + + /* new in libcurl 7.9.7 */ + my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession); + } +#endif + + my_setopt_enum(curl, CURLOPT_SSLVERSION, config->ssl_version); + my_setopt_enum(curl, CURLOPT_TIMECONDITION, config->timecond); + my_setopt(curl, CURLOPT_TIMEVALUE, config->condtime); + my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); + my_setopt(curl, CURLOPT_STDERR, config->errors); + + /* three new ones in libcurl 7.3: */ + my_setopt_str(curl, CURLOPT_INTERFACE, config->iface); + my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel); + + progressbarinit(&progressbar, config); + if((config->progressmode == CURL_PROGRESS_BAR) && + !config->noprogress && !config->mute) { + /* we want the alternative style, then we have to implement it + ourselves! */ + my_setopt(curl, CURLOPT_PROGRESSFUNCTION, tool_progress_cb); + my_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar); + } + + /* new in libcurl 7.6.2: */ + my_setopt_slist(curl, CURLOPT_TELNETOPTIONS, config->telnet_options); + + /* new in libcurl 7.7: */ + my_setopt_str(curl, CURLOPT_RANDOM_FILE, config->random_file); + my_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file); + my_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout); + + if(config->cipher_list) + my_setopt_str(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list); + + /* new in libcurl 7.9.2: */ + if(config->disable_epsv) + /* disable it */ + my_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE); + + /* new in libcurl 7.10.5 */ + if(config->disable_eprt) + /* disable it */ + my_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE); + + if(config->tracetype != TRACE_NONE) { + my_setopt(curl, CURLOPT_DEBUGFUNCTION, tool_debug_cb); + my_setopt(curl, CURLOPT_DEBUGDATA, config); + my_setopt(curl, CURLOPT_VERBOSE, TRUE); + } + + /* new in curl 7.9.3 */ + if(config->engine) { + res = res_setopt_str(curl, CURLOPT_SSLENGINE, config->engine); + if(res) + goto show_error; + my_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1); + } + + /* new in curl 7.10.7, extended in 7.19.4 but this only sets 0 or 1 */ + my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, + config->ftp_create_dirs); + + /* new in curl 7.10.8 */ + if(config->max_filesize) + my_setopt(curl, CURLOPT_MAXFILESIZE_LARGE, + config->max_filesize); + + if(4 == config->ip_version) + my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + else if(6 == config->ip_version) + my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + else + my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER); + + /* new in curl 7.15.5 */ + if(config->ftp_ssl_reqd) + my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); + + /* new in curl 7.11.0 */ + else if(config->ftp_ssl) + my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); + + /* new in curl 7.16.0 */ + else if(config->ftp_ssl_control) + my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL); + + /* new in curl 7.16.1 */ + if(config->ftp_ssl_ccc) + my_setopt_enum(curl, CURLOPT_FTP_SSL_CCC, config->ftp_ssl_ccc_mode); + +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + { + /* TODO: Make this a run-time check instead of compile-time one. */ + + /* new in curl 7.19.4 */ + if(config->socks5_gssapi_service) + my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_SERVICE, + config->socks5_gssapi_service); + + /* new in curl 7.19.4 */ + if(config->socks5_gssapi_nec) + my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC, + config->socks5_gssapi_nec); + } +#endif + /* curl 7.13.0 */ + my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account); + + my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl); + + /* curl 7.14.2 */ + my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip); + + /* curl 7.15.1 */ + my_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod); + + /* curl 7.15.2 */ + if(config->localport) { + my_setopt(curl, CURLOPT_LOCALPORT, config->localport); + my_setopt_str(curl, CURLOPT_LOCALPORTRANGE, + config->localportrange); + } + + /* curl 7.15.5 */ + my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, + config->ftp_alternative_to_user); + + /* curl 7.16.0 */ + if(config->disable_sessionid) + my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, + !config->disable_sessionid); + + /* curl 7.16.2 */ + if(config->raw) { + my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, FALSE); + my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, FALSE); + } + + /* curl 7.17.1 */ + if(!config->nokeepalive) { + my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); + if(config->alivetime != 0) { +#if !defined(TCP_KEEPIDLE) || !defined(TCP_KEEPINTVL) + warnf(config, "Keep-alive functionality somewhat crippled due to " + "missing support in your operating system!\n"); +#endif + my_setopt(curl, CURLOPT_TCP_KEEPIDLE, config->alivetime); + my_setopt(curl, CURLOPT_TCP_KEEPINTVL, config->alivetime); + } + } + else + my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L); + + /* curl 7.20.0 */ + if(config->tftp_blksize) + my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize); + + if(config->mail_from) + my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from); + + if(config->mail_rcpt) + my_setopt_slist(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt); + + /* curl 7.20.x */ + if(config->ftp_pret) + my_setopt(curl, CURLOPT_FTP_USE_PRET, TRUE); + + if(config->proto_present) + my_setopt_flags(curl, CURLOPT_PROTOCOLS, config->proto); + if(config->proto_redir_present) + my_setopt_flags(curl, CURLOPT_REDIR_PROTOCOLS, config->proto_redir); + + if((urlnode->flags & GETOUT_USEREMOTE) + && config->content_disposition) { + my_setopt(curl, CURLOPT_HEADERFUNCTION, tool_header_cb); + my_setopt(curl, CURLOPT_HEADERDATA, &outs); + } + else { + /* if HEADERFUNCTION was set to something in the previous loop, it + is important that we set it (back) to NULL now */ + my_setopt(curl, CURLOPT_HEADERFUNCTION, NULL); + my_setopt(curl, CURLOPT_HEADERDATA, config->headerfile?&heads:NULL); + } + + if(config->resolve) + /* new in 7.21.3 */ + my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve); + + /* new in 7.21.4 */ + if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { + if(config->tls_username) + my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME, + config->tls_username); + if(config->tls_password) + my_setopt_str(curl, CURLOPT_TLSAUTH_PASSWORD, + config->tls_password); + if(config->tls_authtype) + my_setopt_str(curl, CURLOPT_TLSAUTH_TYPE, + config->tls_authtype); + } + + /* new in 7.22.0 */ + if(config->gssapi_delegation) + my_setopt_str(curl, CURLOPT_GSSAPI_DELEGATION, + config->gssapi_delegation); + + /* new in 7.25.0 */ + if(config->ssl_allow_beast) + my_setopt(curl, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_ALLOW_BEAST); + + if(config->mail_auth) + my_setopt_str(curl, CURLOPT_MAIL_AUTH, config->mail_auth); + + /* initialize retry vars for loop below */ + retry_sleep_default = (config->retry_delay) ? + config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ + + retry_numretries = config->req_retry; + retry_sleep = retry_sleep_default; /* ms */ + retrystart = tvnow(); + +#ifndef CURL_DISABLE_LIBCURL_OPTION + res = easysrc_perform(); + if(res) { + goto show_error; + } +#endif + + for(;;) { + res = curl_easy_perform(curl); + /* if retry-max-time is non-zero, make sure we haven't exceeded the + time */ + if(retry_numretries && + (!config->retry_maxtime || + (tvdiff(tvnow(), retrystart) < config->retry_maxtime*1000)) ) { + enum { + RETRY_NO, + RETRY_TIMEOUT, + RETRY_HTTP, + RETRY_FTP, + RETRY_LAST /* not used */ + } retry = RETRY_NO; + long response; + if(CURLE_OPERATION_TIMEDOUT == res) + /* retry timeout always */ + retry = RETRY_TIMEOUT; + else if(CURLE_OK == res) { + /* Check for HTTP transient errors */ + char *this_url=NULL; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &this_url); + if(this_url && + curlx_strnequal(this_url, "http", 4)) { + /* This was HTTP(S) */ + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); + + switch(response) { + case 500: /* Internal Server Error */ + case 502: /* Bad Gateway */ + case 503: /* Service Unavailable */ + case 504: /* Gateway Timeout */ + retry = RETRY_HTTP; + /* + * At this point, we have already written data to the output + * file (or terminal). If we write to a file, we must rewind + * or close/re-open the file so that the next attempt starts + * over from the beginning. + * + * TODO: similar action for the upload case. We might need + * to start over reading from a previous point if we have + * uploaded something when this was returned. + */ + break; + } + } + } /* if CURLE_OK */ + else if(CURLE_LOGIN_DENIED == res) { + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); + + if(response/100 == 4) + /* + * This is typically when the FTP server only allows a certain + * amount of users and we are not one of them. All 4xx codes + * are transient. + */ + retry = RETRY_FTP; + } + + if(retry) { + static const char * const m[]={NULL, + "timeout", + "HTTP error", + "FTP error" + }; + warnf(config, "Transient problem: %s " + "Will retry in %ld seconds. " + "%ld retries left.\n", + m[retry], + retry_sleep/1000, + retry_numretries); + + tool_go_sleep(retry_sleep); + retry_numretries--; + if(!config->retry_delay) { + retry_sleep *= 2; + if(retry_sleep > RETRY_SLEEP_MAX) + retry_sleep = RETRY_SLEEP_MAX; + } + curl_truncate_file(config, outs); + continue; + } + } /* if retry_numretries */ + else { + /* Metalink: Decide to try the next resource or + not. Basically, we want to try the next resource if + download was not successful. */ + long response; + if(CURLE_OPERATION_TIMEDOUT == res) { + try_next_res = 1; + } + else if(CURLE_OK == res) { + char *this_url=NULL; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &this_url); + if(this_url && + curlx_strnequal(this_url, "http", 4)) { + /* This was HTTP(S) */ + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); + + switch(response) { + case 400: + case 401: + case 402: + case 403: + case 404: + case 405: + case 406: + case 407: + case 408: + case 409: + case 410: + case 411: + case 412: + case 413: + case 414: + case 415: + case 416: + case 417: + try_next_res = 1; + break; + } + } + } + else if(CURLE_LOGIN_DENIED == res) { + if(response/100 == 5) + /* + * For permanent negative return code, try the next resource. + */ + try_next_res = 1; + } + } + + /* In all ordinary cases, just break out of loop here */ + retry_sleep = retry_sleep_default; + break; + + } + if(try_next_res == 1) { + if(mlres+1) { + warnf(config, "The remote server returned negative response. " + "Will try to the next resource."); + } + curl_truncate_file(config, outs); + } + else + break; + } + if((config->progressmode == CURL_PROGRESS_BAR) && + progressbar.calls) { + /* if the custom progress bar has been displayed, we output a + newline here */ + fputs("\n", progressbar.out); + } + + if(config->writeout) { + ourWriteOut(curl, &outs, config->writeout); + } +#ifdef USE_ENVIRONMENT + if(config->writeenv) + ourWriteEnv(curl); +#endif + +show_error: + +#ifdef VMS + if(!config->showerror) { + vms_show = VMSSTS_HIDE; + } +#else + if((res!=CURLE_OK) && config->showerror) { + fprintf(config->errors, "curl: (%d) %s\n", res, + errorbuffer[0]? errorbuffer: + curl_easy_strerror((CURLcode)res)); + if(CURLE_SSL_CACERT == res) { +#define CURL_CA_CERT_ERRORMSG1 \ +"More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \ +"curl performs SSL certificate verification by default, using a \"bundle\"\n" \ +" of Certificate Authority (CA) public keys (CA certs). If the default\n" \ +" bundle file isn't adequate, you can specify an alternate file\n" \ +" using the --cacert option.\n" + +#define CURL_CA_CERT_ERRORMSG2 \ +"If this HTTPS server uses a certificate signed by a CA represented in\n" \ +" the bundle, the certificate verification probably failed due to a\n" \ +" problem with the certificate (it might be expired, or the name might\n" \ +" not match the domain name in the URL).\n" \ +"If you'd like to turn off curl's verification of the certificate, use\n" \ +" the -k (or --insecure) option.\n" + + fprintf(config->errors, "%s%s", + CURL_CA_CERT_ERRORMSG1, + CURL_CA_CERT_ERRORMSG2 ); + } + } +#endif + + if(outfile && !curlx_strequal(outfile, "-") && outs.stream) + fclose(outs.stream); + +#ifdef HAVE_UTIME + /* Important that we set the time _after_ the file has been + closed, as is done above here */ + if(config->remote_time && outs.filename) { + /* ask libcurl if we got a time. Pretty please */ + long filetime; + curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); + if(filetime >= 0) { + struct utimbuf times; + times.actime = (time_t)filetime; + times.modtime = (time_t)filetime; + utime(outs.filename, ×); /* set the time we got */ + } + } +#endif +#ifdef __AMIGA__ + /* Set the url as comment for the file. (up to 80 chars are allowed) + */ + if(strlen(url) > 78) + url[79] = '\0'; + + SetComment( outs.filename, url); +#endif + + if(filename) + free(filename); + if(outfile) + free(outfile); + + if(infdopen) + close(infd); + if(outfiles) + free(outfiles); + + return 0; +} + +void clean_metalink(struct Configurable *config) +{ + while(config->metalinkfile_list) { + struct metalinkfile *mlfile = config->metalinkfile_list; + config->metalinkfile_list = config->metalinkfile_list->next; + free(mlfile); + } + config->metalinkfile_last = 0; + while(config->metalink_list) { + struct metalink *ml = config->metalink_list; + config->metalink_list = config->metalink_list->next; + metalink_delete(ml->metalink); + free(ml); + } + config->metalink_last = 0; +} -- cgit v1.2.1 From a0d7a26e32532ced9be033439be815bbabaaba00 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 28 Apr 2012 21:48:56 +0900 Subject: metalink: show help message even if disabled Print message if --metalink is used while metalink support is not enabled. Migrated Metalink support in tool_operate.c and removed operatemetalink(). --- src/tool_metalink.c | 936 +--------------------------------------------------- 1 file changed, 6 insertions(+), 930 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 558fa8fa0..a57297b78 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -20,80 +20,10 @@ * ***************************************************************************/ #include "tool_setup.h" - -#ifdef HAVE_UNISTD_H -# include -#endif - -#ifdef HAVE_UTIME_H -# include -#elif defined(HAVE_SYS_UTIME_H) -# include -#endif - -#define ENABLE_CURLX_PRINTF -/* use our own printf() functions */ -#include "curlx.h" - #include "tool_metalink.h" -#include "tool_binmode.h" -#include "tool_cb_prg.h" -#include "tool_cb_wrt.h" -#include "tool_cb_rea.h" -#include "tool_cb_see.h" -#include "tool_cb_dbg.h" -#include "tool_dirhie.h" -#include "tool_util.h" -#include "tool_main.h" -#include "tool_msgs.h" -#include "tool_setopt.h" -#include "tool_sleep.h" -#include "tool_writeout.h" -#include "tool_libinfo.h" -#include "tool_homedir.h" -#include "tool_cb_hdr.h" #include "memdebug.h" /* keep this as LAST include */ -static int curl_truncate_file(struct Configurable *config, - struct OutStruct outs) -{ - int res = 0; - if(outs.bytes && outs.filename) { - /* We have written data to a output file, we truncate file - */ - if(!config->mute) - fprintf(config->errors, "Throwing away %" - CURL_FORMAT_CURL_OFF_T " bytes\n", - outs.bytes); - fflush(outs.stream); - /* truncate file at the position where we started appending */ -#ifdef HAVE_FTRUNCATE - if(ftruncate( fileno(outs.stream), outs.init)) { - /* when truncate fails, we can't just append as then we'll - create something strange, bail out */ - if(!config->mute) - fprintf(config->errors, - "failed to truncate, exiting\n"); - res = CURLE_WRITE_ERROR; - assert(0); - /* goto quit_urls; */ - } - /* now seek to the end of the file, the position where we - just truncated the file in a large file-safe way */ - fseek(outs.stream, 0, SEEK_END); -#else - /* ftruncate is not available, so just reposition the file - to the location we would have truncated it. This won't - work properly with large files on 32-bit systems, but - most of those will have ftruncate. */ - fseek(outs.stream, (long)outs.init, SEEK_SET); -#endif - outs.bytes = 0; /* clear for next round */ - } - return res; -} - struct metalinkfile *new_metalinkfile(metalink_file_t *metalinkfile) { struct metalinkfile *f; f = (struct metalinkfile*)malloc(sizeof(struct metalinkfile)); @@ -110,866 +40,12 @@ struct metalink *new_metalink(metalink_t *metalink) { return ml; } -int operatemetalink(CURL *curl, - struct getout *urlnode, - long retry_sleep_default, - struct OutStruct outs, - struct OutStruct heads, - char *outfiles, - struct Configurable *config) +int count_next_metalink_resource(struct metalinkfile *mlfile) { - long retry_numretries; - int infd = STDIN_FILENO; - bool infdopen; - char *outfile; - struct timeval retrystart; - struct metalinkfile *mlfile; - int res = 0; - char *filename; + int count = 0; metalink_resource_t **mlres; - char errorbuffer[CURL_ERROR_SIZE]; - struct ProgressData progressbar; - long retry_sleep; - struct InStruct input; - curl_off_t uploadfilesize; /* -1 means unknown */ - char *uploadfile=NULL; /* a single file, never a glob */ - - uploadfilesize=-1; - - mlfile = config->metalinkfile_last; - config->metalinkfile_last = config->metalinkfile_last->next; - - filename = strdup(mlfile->file->name); - - outfile = outfiles?strdup(outfiles):NULL; - - if((urlnode->flags & GETOUT_USEREMOTE) || - (outfile && !curlx_strequal("-", outfile)) ) { - - /* - * We have specified a file name to store the result in, or we have - * decided we want to use the name attribute of file element in Metalink - * XML. - */ - - if(!outfile) { - /* Find and get file name */ - char * pc; - pc = strrchr(filename, '/'); - - if(pc) { - /* duplicate the string beyond the slash */ - pc++; - outfile = *pc ? strdup(pc): NULL; - } - else { - outfile = strdup(filename); - } - - if(!outfile || !*outfile) { - helpf(config->errors, "Metalink file[@name] has no length!\n"); - res = CURLE_WRITE_ERROR; - free(filename); - /* break; */ - return 1; - } -#if defined(MSDOS) - { - /* This is for DOS, and then we do some major replacing of - bad characters in the file name before using it */ - char file1[PATH_MAX]; - if(strlen(outfile) >= PATH_MAX) - outfile[PATH_MAX-1]=0; /* cut it */ - strcpy(file1, msdosify(outfile)); - free(outfile); - - outfile = strdup(rename_if_dos_device_name(file1)); - if(!outfile) { - res = CURLE_OUT_OF_MEMORY; - break; - } - } -#endif /* MSDOS */ - } - /* Create the directory hierarchy, if not pre-existant to a multiple - file output call */ - - if(config->create_dirs && - (CURLE_WRITE_ERROR == create_dir_hierarchy(outfile, config->errors))) - return CURLE_WRITE_ERROR; - - if(config->resume_from_current) { - /* We're told to continue from where we are now. Get the - size of the file as it is now and open it for append instead */ - - struct_stat fileinfo; - - /* VMS -- Danger, the filesize is only valid for stream files */ - if(0 == stat(outfile, &fileinfo)) - /* set offset to current file size: */ - config->resume_from = fileinfo.st_size; - else - /* let offset be 0 */ - config->resume_from = 0; - } - - outs.filename = outfile; - outs.s_isreg = TRUE; - - if(config->resume_from) { - outs.init = config->resume_from; - /* open file for output: */ - outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb"); - if(!outs.stream) { - helpf(config->errors, "Can't open '%s'!\n", outfile); - return CURLE_WRITE_ERROR; - } - } - else { - outs.stream = NULL; /* open when needed */ - } - } - infdopen=FALSE; - - if(!config->errors) - config->errors = stderr; - - if(!outfile && !config->use_ascii) { - /* We get the output to stdout and we have not got the ASCII/text - flag, then set stdout to be binary */ - set_binmode(stdout); - } - - /* Loop though all resources in Metalink */ - for(mlres = mlfile->file->resources; *mlres; ++mlres) { - int try_next_res = 0; - if(config->tcp_nodelay) - my_setopt(curl, CURLOPT_TCP_NODELAY, 1); - - /* where to store */ - my_setopt(curl, CURLOPT_WRITEDATA, &outs); - /* what call to write */ - my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb); - - - /* for uploads */ - input.fd = infd; - input.config = config; - /* Note that if CURLOPT_READFUNCTION is fread (the default), then - * lib/telnet.c will Curl_poll() on the input file descriptor - * rather then calling the READFUNCTION at regular intervals. - * The circumstances in which it is preferable to enable this - * behaviour, by omitting to set the READFUNCTION & READDATA options, - * have not been determined. - */ - my_setopt(curl, CURLOPT_READDATA, &input); - /* what call to read */ - my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb); - - /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what - CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */ - my_setopt(curl, CURLOPT_SEEKDATA, &input); - my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb); - - if(config->recvpersecond) - /* tell libcurl to use a smaller sized buffer as it allows us to - make better sleeps! 7.9.9 stuff! */ - my_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond); - - /* size of uploaded file: */ - if(uploadfilesize != -1) - my_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize); - my_setopt(curl, CURLOPT_URL, (*mlres)->url); /* what to fetch */ - my_setopt(curl, CURLOPT_NOPROGRESS, config->noprogress); - if(config->no_body) { - my_setopt(curl, CURLOPT_NOBODY, 1); - my_setopt(curl, CURLOPT_HEADER, 1); - } - else - my_setopt(curl, CURLOPT_HEADER, config->include_headers); - -#if !defined(CURL_DISABLE_PROXY) - { - /* TODO: Make this a run-time check instead of compile-time one. */ - - my_setopt_str(curl, CURLOPT_PROXY, config->proxy); - my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); - - /* new in libcurl 7.3 */ - my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel); - - /* new in libcurl 7.5 */ - if(config->proxy) - my_setopt_enum(curl, CURLOPT_PROXYTYPE, config->proxyver); - - /* new in libcurl 7.10 */ - if(config->socksproxy) { - my_setopt_str(curl, CURLOPT_PROXY, config->socksproxy); - my_setopt_enum(curl, CURLOPT_PROXYTYPE, config->socksver); - } - - /* new in libcurl 7.10.6 */ - if(config->proxyanyauth) - my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, - (long) CURLAUTH_ANY); - else if(config->proxynegotiate) - my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, - (long) CURLAUTH_GSSNEGOTIATE); - else if(config->proxyntlm) - my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, - (long) CURLAUTH_NTLM); - else if(config->proxydigest) - my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, - (long) CURLAUTH_DIGEST); - else if(config->proxybasic) - my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, - (long) CURLAUTH_BASIC); - - /* new in libcurl 7.19.4 */ - my_setopt(curl, CURLOPT_NOPROXY, config->noproxy); - } -#endif - - my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror); - my_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE); - my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly); - my_setopt(curl, CURLOPT_APPEND, config->ftp_append); - - if(config->netrc_opt) - my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); - else if(config->netrc || config->netrc_file) - my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED); - else - my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED); - - if(config->netrc_file) - my_setopt(curl, CURLOPT_NETRC_FILE, config->netrc_file); - - my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); - my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd); - my_setopt_str(curl, CURLOPT_RANGE, config->range); - my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer); - my_setopt(curl, CURLOPT_TIMEOUT, config->timeout); - - if(built_in_protos & CURLPROTO_HTTP) { - - long postRedir = 0; - - my_setopt(curl, CURLOPT_FOLLOWLOCATION, - config->followlocation); - my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, - config->unrestricted_auth); - - switch(config->httpreq) { - case HTTPREQ_SIMPLEPOST: - my_setopt_str(curl, CURLOPT_POSTFIELDS, - config->postfields); - my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, - config->postfieldsize); - break; - case HTTPREQ_POST: - my_setopt_httppost(curl, CURLOPT_HTTPPOST, config->httppost); - break; - default: - break; - } - - my_setopt_str(curl, CURLOPT_REFERER, config->referer); - my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer); - my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent); - my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers); - - /* new in libcurl 7.5 */ - my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs); - - /* new in libcurl 7.9.1 */ - if(config->httpversion) - my_setopt_enum(curl, CURLOPT_HTTP_VERSION, config->httpversion); - - /* new in libcurl 7.10.6 (default is Basic) */ - if(config->authtype) - my_setopt_bitmask(curl, CURLOPT_HTTPAUTH, (long) config->authtype); - - /* curl 7.19.1 (the 301 version existed in 7.18.2), - 303 was added in 7.26.0 */ - if(config->post301) - postRedir |= CURL_REDIR_POST_301; - if(config->post302) - postRedir |= CURL_REDIR_POST_302; - if(config->post303) - postRedir |= CURL_REDIR_POST_303; - my_setopt(curl, CURLOPT_POSTREDIR, postRedir); - - /* new in libcurl 7.21.6 */ - if(config->encoding) - my_setopt_str(curl, CURLOPT_ACCEPT_ENCODING, ""); - - /* new in libcurl 7.21.6 */ - if(config->tr_encoding) - my_setopt(curl, CURLOPT_TRANSFER_ENCODING, 1); - - } /* (built_in_protos & CURLPROTO_HTTP) */ - - my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport); - my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, - config->low_speed_limit); - my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time); - my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, - config->sendpersecond); - my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, - config->recvpersecond); - my_setopt(curl, CURLOPT_RESUME_FROM_LARGE, - config->use_resume?config->resume_from:0); - - my_setopt(curl, CURLOPT_SSLCERT, config->cert); - my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type); - my_setopt(curl, CURLOPT_SSLKEY, config->key); - my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type); - my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd); - - if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) { - - /* SSH and SSL private key uses same command-line option */ - /* new in libcurl 7.16.1 */ - my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key); - /* new in libcurl 7.16.1 */ - my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey); - - /* new in libcurl 7.17.1: SSH host key md5 checking allows us - to fail if we are not talking to who we think we should */ - my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, - config->hostpubmd5); - } - - if(config->cacert) - my_setopt_str(curl, CURLOPT_CAINFO, config->cacert); - if(config->capath) - my_setopt_str(curl, CURLOPT_CAPATH, config->capath); - if(config->crlfile) - my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile); - - if(curlinfo->features & CURL_VERSION_SSL) { - if(config->insecure_ok) { - my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); - } - else { - my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - /* libcurl default is strict verifyhost -> 2L */ - /* my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); */ - } - } - - if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) { - if(!config->insecure_ok) { - char *home; - char *file; - res = CURLE_OUT_OF_MEMORY; - home = homedir(); - if(home) { - file = aprintf("%s/%sssh/known_hosts", home, DOT_CHAR); - if(file) { - /* new in curl 7.19.6 */ - res = res_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, file); - curl_free(file); - if(res == CURLE_UNKNOWN_OPTION) - /* libssh2 version older than 1.1.1 */ - res = CURLE_OK; - } - Curl_safefree(home); - } - if(res) - goto show_error; - } - } - - if(config->no_body || config->remote_time) { - /* no body or use remote time */ - my_setopt(curl, CURLOPT_FILETIME, TRUE); - } - - my_setopt(curl, CURLOPT_CRLF, config->crlf); - my_setopt_slist(curl, CURLOPT_QUOTE, config->quote); - my_setopt_slist(curl, CURLOPT_POSTQUOTE, config->postquote); - my_setopt_slist(curl, CURLOPT_PREQUOTE, config->prequote); - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - { - /* TODO: Make this a run-time check instead of compile-time one. */ - - if(config->cookie) - my_setopt_str(curl, CURLOPT_COOKIE, config->cookie); - - if(config->cookiefile) - my_setopt_str(curl, CURLOPT_COOKIEFILE, config->cookiefile); - - /* new in libcurl 7.9 */ - if(config->cookiejar) - my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar); - - /* new in libcurl 7.9.7 */ - my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession); - } -#endif - - my_setopt_enum(curl, CURLOPT_SSLVERSION, config->ssl_version); - my_setopt_enum(curl, CURLOPT_TIMECONDITION, config->timecond); - my_setopt(curl, CURLOPT_TIMEVALUE, config->condtime); - my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); - my_setopt(curl, CURLOPT_STDERR, config->errors); - - /* three new ones in libcurl 7.3: */ - my_setopt_str(curl, CURLOPT_INTERFACE, config->iface); - my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel); - - progressbarinit(&progressbar, config); - if((config->progressmode == CURL_PROGRESS_BAR) && - !config->noprogress && !config->mute) { - /* we want the alternative style, then we have to implement it - ourselves! */ - my_setopt(curl, CURLOPT_PROGRESSFUNCTION, tool_progress_cb); - my_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar); - } - - /* new in libcurl 7.6.2: */ - my_setopt_slist(curl, CURLOPT_TELNETOPTIONS, config->telnet_options); - - /* new in libcurl 7.7: */ - my_setopt_str(curl, CURLOPT_RANDOM_FILE, config->random_file); - my_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file); - my_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout); - - if(config->cipher_list) - my_setopt_str(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list); - - /* new in libcurl 7.9.2: */ - if(config->disable_epsv) - /* disable it */ - my_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE); - - /* new in libcurl 7.10.5 */ - if(config->disable_eprt) - /* disable it */ - my_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE); - - if(config->tracetype != TRACE_NONE) { - my_setopt(curl, CURLOPT_DEBUGFUNCTION, tool_debug_cb); - my_setopt(curl, CURLOPT_DEBUGDATA, config); - my_setopt(curl, CURLOPT_VERBOSE, TRUE); - } - - /* new in curl 7.9.3 */ - if(config->engine) { - res = res_setopt_str(curl, CURLOPT_SSLENGINE, config->engine); - if(res) - goto show_error; - my_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1); - } - - /* new in curl 7.10.7, extended in 7.19.4 but this only sets 0 or 1 */ - my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, - config->ftp_create_dirs); - - /* new in curl 7.10.8 */ - if(config->max_filesize) - my_setopt(curl, CURLOPT_MAXFILESIZE_LARGE, - config->max_filesize); - - if(4 == config->ip_version) - my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - else if(6 == config->ip_version) - my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); - else - my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER); - - /* new in curl 7.15.5 */ - if(config->ftp_ssl_reqd) - my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); - - /* new in curl 7.11.0 */ - else if(config->ftp_ssl) - my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); - - /* new in curl 7.16.0 */ - else if(config->ftp_ssl_control) - my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL); - - /* new in curl 7.16.1 */ - if(config->ftp_ssl_ccc) - my_setopt_enum(curl, CURLOPT_FTP_SSL_CCC, config->ftp_ssl_ccc_mode); - -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - { - /* TODO: Make this a run-time check instead of compile-time one. */ - - /* new in curl 7.19.4 */ - if(config->socks5_gssapi_service) - my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_SERVICE, - config->socks5_gssapi_service); - - /* new in curl 7.19.4 */ - if(config->socks5_gssapi_nec) - my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC, - config->socks5_gssapi_nec); - } -#endif - /* curl 7.13.0 */ - my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account); - - my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl); - - /* curl 7.14.2 */ - my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip); - - /* curl 7.15.1 */ - my_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod); - - /* curl 7.15.2 */ - if(config->localport) { - my_setopt(curl, CURLOPT_LOCALPORT, config->localport); - my_setopt_str(curl, CURLOPT_LOCALPORTRANGE, - config->localportrange); - } - - /* curl 7.15.5 */ - my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, - config->ftp_alternative_to_user); - - /* curl 7.16.0 */ - if(config->disable_sessionid) - my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, - !config->disable_sessionid); - - /* curl 7.16.2 */ - if(config->raw) { - my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, FALSE); - my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, FALSE); - } - - /* curl 7.17.1 */ - if(!config->nokeepalive) { - my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); - if(config->alivetime != 0) { -#if !defined(TCP_KEEPIDLE) || !defined(TCP_KEEPINTVL) - warnf(config, "Keep-alive functionality somewhat crippled due to " - "missing support in your operating system!\n"); -#endif - my_setopt(curl, CURLOPT_TCP_KEEPIDLE, config->alivetime); - my_setopt(curl, CURLOPT_TCP_KEEPINTVL, config->alivetime); - } - } - else - my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L); - - /* curl 7.20.0 */ - if(config->tftp_blksize) - my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize); - - if(config->mail_from) - my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from); - - if(config->mail_rcpt) - my_setopt_slist(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt); - - /* curl 7.20.x */ - if(config->ftp_pret) - my_setopt(curl, CURLOPT_FTP_USE_PRET, TRUE); - - if(config->proto_present) - my_setopt_flags(curl, CURLOPT_PROTOCOLS, config->proto); - if(config->proto_redir_present) - my_setopt_flags(curl, CURLOPT_REDIR_PROTOCOLS, config->proto_redir); - - if((urlnode->flags & GETOUT_USEREMOTE) - && config->content_disposition) { - my_setopt(curl, CURLOPT_HEADERFUNCTION, tool_header_cb); - my_setopt(curl, CURLOPT_HEADERDATA, &outs); - } - else { - /* if HEADERFUNCTION was set to something in the previous loop, it - is important that we set it (back) to NULL now */ - my_setopt(curl, CURLOPT_HEADERFUNCTION, NULL); - my_setopt(curl, CURLOPT_HEADERDATA, config->headerfile?&heads:NULL); - } - - if(config->resolve) - /* new in 7.21.3 */ - my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve); - - /* new in 7.21.4 */ - if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { - if(config->tls_username) - my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME, - config->tls_username); - if(config->tls_password) - my_setopt_str(curl, CURLOPT_TLSAUTH_PASSWORD, - config->tls_password); - if(config->tls_authtype) - my_setopt_str(curl, CURLOPT_TLSAUTH_TYPE, - config->tls_authtype); - } - - /* new in 7.22.0 */ - if(config->gssapi_delegation) - my_setopt_str(curl, CURLOPT_GSSAPI_DELEGATION, - config->gssapi_delegation); - - /* new in 7.25.0 */ - if(config->ssl_allow_beast) - my_setopt(curl, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_ALLOW_BEAST); - - if(config->mail_auth) - my_setopt_str(curl, CURLOPT_MAIL_AUTH, config->mail_auth); - - /* initialize retry vars for loop below */ - retry_sleep_default = (config->retry_delay) ? - config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ - - retry_numretries = config->req_retry; - retry_sleep = retry_sleep_default; /* ms */ - retrystart = tvnow(); - -#ifndef CURL_DISABLE_LIBCURL_OPTION - res = easysrc_perform(); - if(res) { - goto show_error; - } -#endif - - for(;;) { - res = curl_easy_perform(curl); - /* if retry-max-time is non-zero, make sure we haven't exceeded the - time */ - if(retry_numretries && - (!config->retry_maxtime || - (tvdiff(tvnow(), retrystart) < config->retry_maxtime*1000)) ) { - enum { - RETRY_NO, - RETRY_TIMEOUT, - RETRY_HTTP, - RETRY_FTP, - RETRY_LAST /* not used */ - } retry = RETRY_NO; - long response; - if(CURLE_OPERATION_TIMEDOUT == res) - /* retry timeout always */ - retry = RETRY_TIMEOUT; - else if(CURLE_OK == res) { - /* Check for HTTP transient errors */ - char *this_url=NULL; - curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &this_url); - if(this_url && - curlx_strnequal(this_url, "http", 4)) { - /* This was HTTP(S) */ - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - - switch(response) { - case 500: /* Internal Server Error */ - case 502: /* Bad Gateway */ - case 503: /* Service Unavailable */ - case 504: /* Gateway Timeout */ - retry = RETRY_HTTP; - /* - * At this point, we have already written data to the output - * file (or terminal). If we write to a file, we must rewind - * or close/re-open the file so that the next attempt starts - * over from the beginning. - * - * TODO: similar action for the upload case. We might need - * to start over reading from a previous point if we have - * uploaded something when this was returned. - */ - break; - } - } - } /* if CURLE_OK */ - else if(CURLE_LOGIN_DENIED == res) { - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - - if(response/100 == 4) - /* - * This is typically when the FTP server only allows a certain - * amount of users and we are not one of them. All 4xx codes - * are transient. - */ - retry = RETRY_FTP; - } - - if(retry) { - static const char * const m[]={NULL, - "timeout", - "HTTP error", - "FTP error" - }; - warnf(config, "Transient problem: %s " - "Will retry in %ld seconds. " - "%ld retries left.\n", - m[retry], - retry_sleep/1000, - retry_numretries); - - tool_go_sleep(retry_sleep); - retry_numretries--; - if(!config->retry_delay) { - retry_sleep *= 2; - if(retry_sleep > RETRY_SLEEP_MAX) - retry_sleep = RETRY_SLEEP_MAX; - } - curl_truncate_file(config, outs); - continue; - } - } /* if retry_numretries */ - else { - /* Metalink: Decide to try the next resource or - not. Basically, we want to try the next resource if - download was not successful. */ - long response; - if(CURLE_OPERATION_TIMEDOUT == res) { - try_next_res = 1; - } - else if(CURLE_OK == res) { - char *this_url=NULL; - curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &this_url); - if(this_url && - curlx_strnequal(this_url, "http", 4)) { - /* This was HTTP(S) */ - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - - switch(response) { - case 400: - case 401: - case 402: - case 403: - case 404: - case 405: - case 406: - case 407: - case 408: - case 409: - case 410: - case 411: - case 412: - case 413: - case 414: - case 415: - case 416: - case 417: - try_next_res = 1; - break; - } - } - } - else if(CURLE_LOGIN_DENIED == res) { - if(response/100 == 5) - /* - * For permanent negative return code, try the next resource. - */ - try_next_res = 1; - } - } - - /* In all ordinary cases, just break out of loop here */ - retry_sleep = retry_sleep_default; - break; - - } - if(try_next_res == 1) { - if(mlres+1) { - warnf(config, "The remote server returned negative response. " - "Will try to the next resource."); - } - curl_truncate_file(config, outs); - } - else - break; - } - if((config->progressmode == CURL_PROGRESS_BAR) && - progressbar.calls) { - /* if the custom progress bar has been displayed, we output a - newline here */ - fputs("\n", progressbar.out); - } - - if(config->writeout) { - ourWriteOut(curl, &outs, config->writeout); - } -#ifdef USE_ENVIRONMENT - if(config->writeenv) - ourWriteEnv(curl); -#endif - -show_error: - -#ifdef VMS - if(!config->showerror) { - vms_show = VMSSTS_HIDE; - } -#else - if((res!=CURLE_OK) && config->showerror) { - fprintf(config->errors, "curl: (%d) %s\n", res, - errorbuffer[0]? errorbuffer: - curl_easy_strerror((CURLcode)res)); - if(CURLE_SSL_CACERT == res) { -#define CURL_CA_CERT_ERRORMSG1 \ -"More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \ -"curl performs SSL certificate verification by default, using a \"bundle\"\n" \ -" of Certificate Authority (CA) public keys (CA certs). If the default\n" \ -" bundle file isn't adequate, you can specify an alternate file\n" \ -" using the --cacert option.\n" - -#define CURL_CA_CERT_ERRORMSG2 \ -"If this HTTPS server uses a certificate signed by a CA represented in\n" \ -" the bundle, the certificate verification probably failed due to a\n" \ -" problem with the certificate (it might be expired, or the name might\n" \ -" not match the domain name in the URL).\n" \ -"If you'd like to turn off curl's verification of the certificate, use\n" \ -" the -k (or --insecure) option.\n" - - fprintf(config->errors, "%s%s", - CURL_CA_CERT_ERRORMSG1, - CURL_CA_CERT_ERRORMSG2 ); - } - } -#endif - - if(outfile && !curlx_strequal(outfile, "-") && outs.stream) - fclose(outs.stream); - -#ifdef HAVE_UTIME - /* Important that we set the time _after_ the file has been - closed, as is done above here */ - if(config->remote_time && outs.filename) { - /* ask libcurl if we got a time. Pretty please */ - long filetime; - curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); - if(filetime >= 0) { - struct utimbuf times; - times.actime = (time_t)filetime; - times.modtime = (time_t)filetime; - utime(outs.filename, ×); /* set the time we got */ - } - } -#endif -#ifdef __AMIGA__ - /* Set the url as comment for the file. (up to 80 chars are allowed) - */ - if(strlen(url) > 78) - url[79] = '\0'; - - SetComment( outs.filename, url); -#endif - - if(filename) - free(filename); - if(outfile) - free(outfile); - - if(infdopen) - close(infd); - if(outfiles) - free(outfiles); - - return 0; + for(mlres = mlfile->file->resources; *mlres; ++mlres, ++count); + return count; } void clean_metalink(struct Configurable *config) @@ -977,14 +53,14 @@ void clean_metalink(struct Configurable *config) while(config->metalinkfile_list) { struct metalinkfile *mlfile = config->metalinkfile_list; config->metalinkfile_list = config->metalinkfile_list->next; - free(mlfile); + Curl_safefree(mlfile); } config->metalinkfile_last = 0; while(config->metalink_list) { struct metalink *ml = config->metalink_list; config->metalink_list = config->metalink_list->next; metalink_delete(ml->metalink); - free(ml); + Curl_safefree(ml); } config->metalink_last = 0; } -- cgit v1.2.1 From 53f2c02ac75360b53a8eba8d575e5fa55f8b3372 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 28 Apr 2012 23:46:32 +0900 Subject: metalink: parse downloaded Metalink file Parse downloaded Metalink file and add downloads described there. Fixed compile error without metalink support. --- src/tool_metalink.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index a57297b78..4b4c6bcd3 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -21,9 +21,23 @@ ***************************************************************************/ #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; +} -- cgit v1.2.1 From 383641d70a01da00f2a2484e8e8c3f6f20d24adb Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 9 May 2012 00:20:17 +0900 Subject: Support media-type parameter in Content-Type --- src/tool_metalink.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 4b4c6bcd3..b442fc0d7 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -20,6 +20,9 @@ * ***************************************************************************/ #include "tool_setup.h" + +#include "rawstr.h" + #include "tool_metalink.h" #include "tool_getparam.h" #include "tool_paramhlp.h" @@ -142,3 +145,24 @@ int parse_metalink(struct Configurable *config, const char *infile) } return 0; } + +/* + * Returns nonzero if content_type includes mediatype. + */ +static int check_content_type(const char *content_type, const char *media_type) +{ + const char *ptr = content_type; + size_t media_type_len = strlen(media_type); + for(; *ptr && (*ptr == ' ' || *ptr == '\t'); ++ptr); + if(!*ptr) { + return 0; + } + return Curl_raw_nequal(ptr, media_type, media_type_len) && + (*(ptr+media_type_len) == '\0' || *(ptr+media_type_len) == ' ' || + *(ptr+media_type_len) == '\t' || *(ptr+media_type_len) == ';'); +} + +int check_metalink_content_type(const char *content_type) +{ + return check_content_type(content_type, "application/metalink+xml"); +} -- cgit v1.2.1 From 94eb1328969b796e4ecce3a113fc6ba24c167254 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 14 May 2012 21:55:48 +0900 Subject: Fixed segmentation fault when Metalink has no valid file or no resource. --- src/tool_metalink.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index b442fc0d7..d31502ab2 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -94,6 +94,10 @@ int parse_metalink(struct Configurable *config, const char *infile) if(r != 0) { return -1; } + if(metalink->files == NULL) { + fprintf(config->errors, "\nMetalink does not contain any file.\n"); + return 0; + } ml = new_metalink(metalink); if(config->metalink_list) { @@ -107,7 +111,11 @@ int parse_metalink(struct Configurable *config, const char *infile) for(files = metalink->files; *files; ++files) { struct getout *url; /* Skip an entry which has no resource. */ - if(!(*files)->resources[0]) continue; + if(!(*files)->resources) { + fprintf(config->errors, "\nFile %s does not have any resource.\n", + (*files)->name); + 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 -- cgit v1.2.1 From 9f7f7925da648b3b39714e6c9fd65500a869bdfe Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 22 May 2012 01:40:11 +0900 Subject: Check checksum of downloaded file if checksum is available Metalink file contains several hash types of checksums, such as md5, sha-1, sha-256, etc. To deal with these checksums, I created abstraction layer based on lib/curl_md5.h and lib/md5.c. Basically, they are almost the same but I changed the code so that it is not hash type dependent. Currently, GNUTLS(nettle or gcrypt) and OpenSSL functions are supported. Checksum checking is done by reopening download file. If there is an I/O error, the current implementation just prints error message and does not try next resource. In this patch, the supported hash types are: md5, sha-1 and sha-256. --- src/tool_metalink.c | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index d31502ab2..4fb0015c7 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -21,6 +21,16 @@ ***************************************************************************/ #include "tool_setup.h" +#ifdef HAVE_UNISTD_H +# include +#endif + +#include + +#ifdef HAVE_FCNTL_H +# include +#endif + #include "rawstr.h" #include "tool_metalink.h" @@ -41,6 +51,228 @@ return PARAM_NO_MEM; \ } WHILE_FALSE +#ifdef USE_GNUTLS_NETTLE + +#include +#include + +typedef struct md5_ctx MD5_CTX; + +static void MD5_Init(MD5_CTX * ctx) +{ + md5_init(ctx); +} + +static void MD5_Update(MD5_CTX * ctx, + const unsigned char * input, + unsigned int inputLen) +{ + md5_update(ctx, inputLen, input); +} + +static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx) +{ + md5_digest(ctx, 16, digest); +} + +typedef struct sha1_ctx SHA_CTX; + +static void SHA1_Init(SHA_CTX *ctx) +{ + sha1_init(ctx); +} + +static void SHA1_Update(SHA_CTX *ctx, + const unsigned char * input, + unsigned int inputLen) +{ + sha1_update(ctx, inputLen, input); +} + +static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx) +{ + sha1_digest(ctx, 20, digest); +} + +typedef struct sha256_ctx SHA256_CTX; + +static void SHA256_Init(SHA256_CTX *ctx) +{ + sha256_init(ctx); +} + +static void SHA256_Update(SHA256_CTX *ctx, + const unsigned char * input, + unsigned int inputLen) +{ + sha256_update(ctx, inputLen, input); +} + +static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + sha256_digest(ctx, 32, digest); +} + +#else + +#ifdef USE_GNUTLS + +#include + +typedef gcry_md_hd_t MD5_CTX; + +static void MD5_Init(MD5_CTX * ctx) +{ + gcry_md_open(ctx, GCRY_MD_MD5, 0); +} + +static void MD5_Update(MD5_CTX * ctx, + const unsigned char * input, + unsigned int inputLen) +{ + gcry_md_write(*ctx, input, inputLen); +} + +static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx) +{ + memcpy(digest, gcry_md_read(*ctx, 0), 16); + gcry_md_close(*ctx); +} + +typedef gcry_md_hd_t SHA_CTX; + +static void SHA1_Init(SHA_CTX * ctx) +{ + gcry_md_open(ctx, GCRY_MD_SHA1, 0); +} + +static void SHA1_Update(SHA_CTX * ctx, + const unsigned char * input, + unsigned int inputLen) +{ + gcry_md_write(*ctx, input, inputLen); +} + +static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx) +{ + memcpy(digest, gcry_md_read(*ctx, 0), 20); + gcry_md_close(*ctx); +} + +typedef gcry_md_hd_t SHA256_CTX; + +static void SHA256_Init(SHA256_CTX * ctx) +{ + gcry_md_open(ctx, GCRY_MD_SHA256, 0); +} + +static void SHA256_Update(SHA256_CTX * ctx, + const unsigned char * input, + unsigned int inputLen) +{ + gcry_md_write(*ctx, input, inputLen); +} + +static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + memcpy(digest, gcry_md_read(*ctx, 0), 32); + gcry_md_close(*ctx); +} + +#else + +#ifdef USE_SSLEAY + +# ifdef USE_OPENSSL +# include +# include +# else +/* TODO What to do if USE_OPENSSL is undefined? */ +# endif + +#else /* USE_SSLEAY */ + +/* TODO hash functions for other libraries here */ + +#endif /* USE_SSLEAY */ + +#endif /* USE_GNUTLS */ + +#endif /* USE_GNUTLS_NETTLE */ + +const digest_params MD5_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) MD5_Init, + (Curl_digest_update_func) MD5_Update, + (Curl_digest_final_func) MD5_Final, + sizeof(MD5_CTX), + 16 + } +}; + +const digest_params SHA1_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) SHA1_Init, + (Curl_digest_update_func) SHA1_Update, + (Curl_digest_final_func) SHA1_Final, + sizeof(SHA_CTX), + 20 + } +}; + +const digest_params SHA256_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) SHA256_Init, + (Curl_digest_update_func) SHA256_Update, + (Curl_digest_final_func) SHA256_Final, + sizeof(SHA256_CTX), + 32 + } +}; + +digest_context *Curl_digest_init(const digest_params *dparams) +{ + digest_context *ctxt; + + /* Create digest context */ + ctxt = malloc(sizeof *ctxt); + + if(!ctxt) + return ctxt; + + ctxt->digest_hashctx = malloc(dparams->digest_ctxtsize); + + if(!ctxt->digest_hashctx) { + free(ctxt); + return NULL; + } + + ctxt->digest_hash = dparams; + + dparams->digest_init(ctxt->digest_hashctx); + + return ctxt; +} + +int Curl_digest_update(digest_context *context, + const unsigned char *data, + unsigned int len) +{ + (*context->digest_hash->digest_update)(context->digest_hashctx, data, len); + + return 0; +} + +int Curl_digest_final(digest_context *context, unsigned char *result) +{ + (*context->digest_hash->digest_final)(result, context->digest_hashctx); + + free(context->digest_hashctx); + free(context); + + return 0; +} + struct metalinkfile *new_metalinkfile(metalink_file_t *metalinkfile) { struct metalinkfile *f; f = (struct metalinkfile*)malloc(sizeof(struct metalinkfile)); @@ -174,3 +406,143 @@ int check_metalink_content_type(const char *content_type) { return check_content_type(content_type, "application/metalink+xml"); } + +static const metalink_digest_def SHA256_DIGEST_DEF[] = { + {"sha-256", SHA256_DIGEST_PARAMS} +}; + +static const metalink_digest_def SHA1_DIGEST_DEF[] = { + {"sha-1", SHA1_DIGEST_PARAMS} +}; + +static const metalink_digest_def MD5_DIGEST_DEF[] = { + {"md5", MD5_DIGEST_PARAMS} +}; + +/* + * The alias of supported hash functions in the order by preference + * (basically stronger hash comes first). We included "sha-256" and + * "sha256". The former is the name defined in the IANA registry named + * "Hash Function Textual Names". The latter is widely (and + * historically) used in Metalink version 3. + */ +static const metalink_digest_alias digest_aliases[] = { + {"sha-256", SHA256_DIGEST_DEF}, + {"sha256", SHA256_DIGEST_DEF}, + {"sha-1", SHA1_DIGEST_DEF}, + {"sha1", SHA1_DIGEST_DEF}, + {"md5", MD5_DIGEST_DEF}, + {NULL, NULL} +}; + +static unsigned char hex_to_uint(const char *s) +{ + int v[2]; + int i; + for(i = 0; i < 2; ++i) { + v[i] = Curl_raw_toupper(s[i]); + if('0' <= v[i] && v[i] <= '9') { + v[i] -= '0'; + } + else if('A' <= v[i] && v[i] <= 'Z') { + v[i] -= 'A'-10; + } + } + return (unsigned char)((v[0] << 4) | v[1]); +} + +/* + * Check checksum of file denoted by filename. The expected hash value + * is given in hex_hash which is hex-encoded string. + * + * This function returns 1 if it succeeds or one of the following + * integers: + * + * 0: + * Checksum didn't match. + * -1: + * Could not open file; or could not read data from file. + */ +static int check_hash(const char *filename, + const metalink_digest_def *digest_def, + const char *hex_hash, FILE *error) +{ + unsigned char *result; + digest_context *dctx; + int check_ok; + int fd; + size_t i; + fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name, + filename); + fd = open(filename, O_RDONLY); + if(fd == -1) { + fprintf(error, "Could not open file %s: %s\n", filename, strerror(errno)); + return -1; + } + dctx = Curl_digest_init(digest_def->dparams); + result = malloc(digest_def->dparams->digest_resultlen); + while(1) { + unsigned char buf[4096]; + ssize_t len = read(fd, buf, sizeof(buf)); + if(len == 0) { + break; + } + else if(len == -1) { + fprintf(error, "Could not read file %s: %s\n", filename, + strerror(errno)); + Curl_digest_final(dctx, result); + close(fd); + return -1; + } + Curl_digest_update(dctx, buf, (unsigned int)len); + } + Curl_digest_final(dctx, result); + check_ok = 1; + for(i = 0; i < digest_def->dparams->digest_resultlen; ++i) { + if(hex_to_uint(&hex_hash[i*2]) != result[i]) { + check_ok = 0; + break; + } + } + free(result); + close(fd); + return check_ok; +} + +int metalink_check_hash(struct Configurable *config, + struct metalinkfile *mlfile, + const char *filename) +{ + metalink_checksum_t **checksum; + const metalink_digest_alias *digest_alias; + int rv; + if(!mlfile->file->checksums) { + return -2; + } + for(digest_alias = digest_aliases; digest_alias->alias_name; + ++digest_alias) { + for(checksum = mlfile->file->checksums; *checksum; ++checksum) { + if(Curl_raw_equal(digest_alias->alias_name, (*checksum)->type) && + strlen((*checksum)->hash) == + digest_alias->digest_def->dparams->digest_resultlen*2) { + break; + } + } + if(*checksum) { + break; + } + } + if(!digest_alias->alias_name) { + fprintf(config->errors, "No supported checksum in Metalink file\n"); + return -2; + } + rv = check_hash(filename, digest_alias->digest_def, + (*checksum)->hash, 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; +} -- cgit v1.2.1 From 6a655ca192b65702f6c28b33e606fd4d6aec723f Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 25 May 2012 19:24:32 +0900 Subject: Minimize usage of structs from libmetalink --- src/tool_metalink.c | 160 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 114 insertions(+), 46 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 4fb0015c7..9a5f9325e 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -36,6 +36,7 @@ #include "tool_metalink.h" #include "tool_getparam.h" #include "tool_paramhlp.h" +#include "tool_cfgable.h" #include "memdebug.h" /* keep this as LAST include */ @@ -273,45 +274,121 @@ int Curl_digest_final(digest_context *context, unsigned char *result) return 0; } -struct metalinkfile *new_metalinkfile(metalink_file_t *metalinkfile) { - struct metalinkfile *f; - f = (struct metalinkfile*)malloc(sizeof(struct metalinkfile)); - f->file = metalinkfile; +static metalink_checksum *new_metalink_checksum(const char *hash_name, + const char *hash_value) +{ + metalink_checksum *chksum; + chksum = malloc(sizeof(metalink_checksum)); + chksum->next = NULL; + chksum->hash_name = strdup(hash_name); + chksum->hash_value = strdup(hash_value); + return chksum; +} + +static void delete_metalink_checksum(metalink_checksum *chksum) +{ + if(chksum == NULL) { + return; + } + Curl_safefree(chksum->hash_value); + Curl_safefree(chksum->hash_name); + Curl_safefree(chksum); +} + +static metalink_resource *new_metalink_resource(const char *url) +{ + metalink_resource *res; + res = malloc(sizeof(metalink_resource)); + res->next = NULL; + res->url = strdup(url); + return res; +} + +static void delete_metalink_resource(metalink_resource *res) +{ + if(res == NULL) { + return; + } + Curl_safefree(res->url); + Curl_safefree(res); +} + +static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo) +{ + metalinkfile *f; + f = (metalinkfile*)malloc(sizeof(metalinkfile)); f->next = NULL; + f->filename = strdup(fileinfo->name); + f->checksum = NULL; + f->resource = NULL; + if(fileinfo->checksums) { + metalink_checksum_t **p; + metalink_checksum root, *tail; + root.next = NULL; + tail = &root; + for(p = fileinfo->checksums; *p; ++p) { + metalink_checksum *chksum; + chksum = new_metalink_checksum((*p)->type, (*p)->hash); + tail->next = chksum; + tail = chksum; + } + f->checksum = root.next; + } + if(fileinfo->resources) { + metalink_resource_t **p; + metalink_resource root, *tail; + root.next = NULL; + tail = &root; + for(p = fileinfo->resources; *p; ++p) { + metalink_resource *res; + res = new_metalink_resource((*p)->url); + tail->next = res; + tail = res; + } + f->resource = root.next; + } return f; } -struct metalink *new_metalink(metalink_t *metalink) { - struct metalink *ml; - ml = (struct metalink*)malloc(sizeof(struct metalink)); - ml->metalink = metalink; - ml->next = NULL; - return ml; +static void delete_metalinkfile(metalinkfile *mlfile) +{ + metalink_checksum *mc; + metalink_resource *res; + if(mlfile == NULL) { + return; + } + Curl_safefree(mlfile->filename); + for(mc = mlfile->checksum; mc;) { + metalink_checksum *next; + next = mc->next; + delete_metalink_checksum(mc); + mc = next; + } + for(res = mlfile->resource; res;) { + metalink_resource *next; + next = res->next; + delete_metalink_resource(res); + res = next; + } + Curl_safefree(mlfile); } -int count_next_metalink_resource(struct metalinkfile *mlfile) +int count_next_metalink_resource(metalinkfile *mlfile) { int count = 0; - metalink_resource_t **mlres; - for(mlres = mlfile->file->resources; *mlres; ++mlres, ++count); + metalink_resource *res; + for(res = mlfile->resource; res; res = res->next, ++count); return count; } void clean_metalink(struct Configurable *config) { while(config->metalinkfile_list) { - struct metalinkfile *mlfile = config->metalinkfile_list; + metalinkfile *mlfile = config->metalinkfile_list; config->metalinkfile_list = config->metalinkfile_list->next; - Curl_safefree(mlfile); + delete_metalinkfile(mlfile); } config->metalinkfile_last = 0; - while(config->metalink_list) { - struct metalink *ml = config->metalink_list; - config->metalink_list = config->metalink_list->next; - metalink_delete(ml->metalink); - Curl_safefree(ml); - } - config->metalink_last = 0; } int parse_metalink(struct Configurable *config, const char *infile) @@ -319,27 +396,16 @@ 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; } if(metalink->files == NULL) { fprintf(config->errors, "\nMetalink does not contain any file.\n"); + metalink_delete(metalink); return 0; } - 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. */ @@ -363,16 +429,17 @@ int parse_metalink(struct Configurable *config, const char *infile) url = config->url_get; else /* there was no free node, create one! */ - url=new_getout(config); + url = new_getout(config); if(url) { - struct metalinkfile *mlfile; + metalinkfile *mlfile; + mlfile = new_metalinkfile(*files); + /* Set name as url */ - GetStr(&url->url, (*files)->name); + GetStr(&url->url, mlfile->filename); /* set flag metalink here */ url->flags |= GETOUT_URL | GETOUT_METALINK; - mlfile = new_metalinkfile(*files); if(config->metalinkfile_list) { config->metalinkfile_last->next = mlfile; @@ -383,6 +450,7 @@ int parse_metalink(struct Configurable *config, const char *infile) } } } + metalink_delete(metalink); return 0; } @@ -510,25 +578,25 @@ static int check_hash(const char *filename, } int metalink_check_hash(struct Configurable *config, - struct metalinkfile *mlfile, + metalinkfile *mlfile, const char *filename) { - metalink_checksum_t **checksum; + metalink_checksum *chksum; const metalink_digest_alias *digest_alias; int rv; - if(!mlfile->file->checksums) { + if(mlfile->checksum == NULL) { return -2; } for(digest_alias = digest_aliases; digest_alias->alias_name; ++digest_alias) { - for(checksum = mlfile->file->checksums; *checksum; ++checksum) { - if(Curl_raw_equal(digest_alias->alias_name, (*checksum)->type) && - strlen((*checksum)->hash) == + for(chksum = mlfile->checksum; chksum; chksum = chksum->next) { + if(Curl_raw_equal(digest_alias->alias_name, chksum->hash_name) && + strlen(chksum->hash_value) == digest_alias->digest_def->dparams->digest_resultlen*2) { break; } } - if(*checksum) { + if(chksum) { break; } } @@ -537,7 +605,7 @@ int metalink_check_hash(struct Configurable *config, return -2; } rv = check_hash(filename, digest_alias->digest_def, - (*checksum)->hash, config->errors); + chksum->hash_value, config->errors); if(rv == 1) { fprintf(config->errors, "Checksum matched\n"); } -- cgit v1.2.1 From 7bdb9fba952ea24390b903478aedb5e1615ae2d4 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 26 May 2012 21:21:53 +0900 Subject: Disable hash check if neither OpenSSL nor GNUTLS is installed. --- src/tool_metalink.c | 300 ++++++++++++++++++++++++++-------------------------- 1 file changed, 149 insertions(+), 151 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 9a5f9325e..461dbf148 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -182,25 +182,21 @@ static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) #else -#ifdef USE_SSLEAY - -# ifdef USE_OPENSSL -# include -# include -# else -/* TODO What to do if USE_OPENSSL is undefined? */ -# endif - -#else /* USE_SSLEAY */ +#ifdef USE_OPENSSL +# include +# include +#else /* USE_OPENSSL */ /* TODO hash functions for other libraries here */ -#endif /* USE_SSLEAY */ +#endif /* USE_OPENSSL */ #endif /* USE_GNUTLS */ #endif /* USE_GNUTLS_NETTLE */ +#ifdef METALINK_HASH_CHECK + const digest_params MD5_DIGEST_PARAMS[] = { { (Curl_digest_init_func) MD5_Init, @@ -274,6 +270,148 @@ int Curl_digest_final(digest_context *context, unsigned char *result) return 0; } +static const metalink_digest_def SHA256_DIGEST_DEF[] = { + {"sha-256", SHA256_DIGEST_PARAMS} +}; + +static const metalink_digest_def SHA1_DIGEST_DEF[] = { + {"sha-1", SHA1_DIGEST_PARAMS} +}; + +static const metalink_digest_def MD5_DIGEST_DEF[] = { + {"md5", MD5_DIGEST_PARAMS} +}; + +/* + * The alias of supported hash functions in the order by preference + * (basically stronger hash comes first). We included "sha-256" and + * "sha256". The former is the name defined in the IANA registry named + * "Hash Function Textual Names". The latter is widely (and + * historically) used in Metalink version 3. + */ +static const metalink_digest_alias digest_aliases[] = { + {"sha-256", SHA256_DIGEST_DEF}, + {"sha256", SHA256_DIGEST_DEF}, + {"sha-1", SHA1_DIGEST_DEF}, + {"sha1", SHA1_DIGEST_DEF}, + {"md5", MD5_DIGEST_DEF}, + {NULL, NULL} +}; + +static unsigned char hex_to_uint(const char *s) +{ + int v[2]; + int i; + for(i = 0; i < 2; ++i) { + v[i] = Curl_raw_toupper(s[i]); + if('0' <= v[i] && v[i] <= '9') { + v[i] -= '0'; + } + else if('A' <= v[i] && v[i] <= 'Z') { + v[i] -= 'A'-10; + } + } + return (unsigned char)((v[0] << 4) | v[1]); +} + +/* + * Check checksum of file denoted by filename. The expected hash value + * is given in hex_hash which is hex-encoded string. + * + * This function returns 1 if it succeeds or one of the following + * integers: + * + * 0: + * Checksum didn't match. + * -1: + * Could not open file; or could not read data from file. + */ +static int check_hash(const char *filename, + const metalink_digest_def *digest_def, + const char *hex_hash, FILE *error) +{ + unsigned char *result; + digest_context *dctx; + int check_ok; + int fd; + size_t i; + fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name, + filename); + fd = open(filename, O_RDONLY); + if(fd == -1) { + fprintf(error, "Could not open file %s: %s\n", filename, strerror(errno)); + return -1; + } + dctx = Curl_digest_init(digest_def->dparams); + result = malloc(digest_def->dparams->digest_resultlen); + while(1) { + unsigned char buf[4096]; + ssize_t len = read(fd, buf, sizeof(buf)); + if(len == 0) { + break; + } + else if(len == -1) { + fprintf(error, "Could not read file %s: %s\n", filename, + strerror(errno)); + Curl_digest_final(dctx, result); + close(fd); + return -1; + } + Curl_digest_update(dctx, buf, (unsigned int)len); + } + Curl_digest_final(dctx, result); + check_ok = 1; + for(i = 0; i < digest_def->dparams->digest_resultlen; ++i) { + if(hex_to_uint(&hex_hash[i*2]) != result[i]) { + check_ok = 0; + break; + } + } + free(result); + close(fd); + return check_ok; +} + +int metalink_check_hash(struct Configurable *config, + metalinkfile *mlfile, + const char *filename) +{ + metalink_checksum *chksum; + const metalink_digest_alias *digest_alias; + int rv; + if(mlfile->checksum == NULL) { + return -2; + } + for(digest_alias = digest_aliases; digest_alias->alias_name; + ++digest_alias) { + for(chksum = mlfile->checksum; chksum; chksum = chksum->next) { + if(Curl_raw_equal(digest_alias->alias_name, chksum->hash_name) && + strlen(chksum->hash_value) == + digest_alias->digest_def->dparams->digest_resultlen*2) { + break; + } + } + if(chksum) { + break; + } + } + if(!digest_alias->alias_name) { + fprintf(config->errors, "No supported checksum in Metalink file\n"); + return -2; + } + rv = check_hash(filename, digest_alias->digest_def, + chksum->hash_value, 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; +} + +#endif /* METALINK_HASH_CHECK */ + static metalink_checksum *new_metalink_checksum(const char *hash_name, const char *hash_value) { @@ -474,143 +612,3 @@ int check_metalink_content_type(const char *content_type) { return check_content_type(content_type, "application/metalink+xml"); } - -static const metalink_digest_def SHA256_DIGEST_DEF[] = { - {"sha-256", SHA256_DIGEST_PARAMS} -}; - -static const metalink_digest_def SHA1_DIGEST_DEF[] = { - {"sha-1", SHA1_DIGEST_PARAMS} -}; - -static const metalink_digest_def MD5_DIGEST_DEF[] = { - {"md5", MD5_DIGEST_PARAMS} -}; - -/* - * The alias of supported hash functions in the order by preference - * (basically stronger hash comes first). We included "sha-256" and - * "sha256". The former is the name defined in the IANA registry named - * "Hash Function Textual Names". The latter is widely (and - * historically) used in Metalink version 3. - */ -static const metalink_digest_alias digest_aliases[] = { - {"sha-256", SHA256_DIGEST_DEF}, - {"sha256", SHA256_DIGEST_DEF}, - {"sha-1", SHA1_DIGEST_DEF}, - {"sha1", SHA1_DIGEST_DEF}, - {"md5", MD5_DIGEST_DEF}, - {NULL, NULL} -}; - -static unsigned char hex_to_uint(const char *s) -{ - int v[2]; - int i; - for(i = 0; i < 2; ++i) { - v[i] = Curl_raw_toupper(s[i]); - if('0' <= v[i] && v[i] <= '9') { - v[i] -= '0'; - } - else if('A' <= v[i] && v[i] <= 'Z') { - v[i] -= 'A'-10; - } - } - return (unsigned char)((v[0] << 4) | v[1]); -} - -/* - * Check checksum of file denoted by filename. The expected hash value - * is given in hex_hash which is hex-encoded string. - * - * This function returns 1 if it succeeds or one of the following - * integers: - * - * 0: - * Checksum didn't match. - * -1: - * Could not open file; or could not read data from file. - */ -static int check_hash(const char *filename, - const metalink_digest_def *digest_def, - const char *hex_hash, FILE *error) -{ - unsigned char *result; - digest_context *dctx; - int check_ok; - int fd; - size_t i; - fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name, - filename); - fd = open(filename, O_RDONLY); - if(fd == -1) { - fprintf(error, "Could not open file %s: %s\n", filename, strerror(errno)); - return -1; - } - dctx = Curl_digest_init(digest_def->dparams); - result = malloc(digest_def->dparams->digest_resultlen); - while(1) { - unsigned char buf[4096]; - ssize_t len = read(fd, buf, sizeof(buf)); - if(len == 0) { - break; - } - else if(len == -1) { - fprintf(error, "Could not read file %s: %s\n", filename, - strerror(errno)); - Curl_digest_final(dctx, result); - close(fd); - return -1; - } - Curl_digest_update(dctx, buf, (unsigned int)len); - } - Curl_digest_final(dctx, result); - check_ok = 1; - for(i = 0; i < digest_def->dparams->digest_resultlen; ++i) { - if(hex_to_uint(&hex_hash[i*2]) != result[i]) { - check_ok = 0; - break; - } - } - free(result); - close(fd); - return check_ok; -} - -int metalink_check_hash(struct Configurable *config, - metalinkfile *mlfile, - const char *filename) -{ - metalink_checksum *chksum; - const metalink_digest_alias *digest_alias; - int rv; - if(mlfile->checksum == NULL) { - return -2; - } - for(digest_alias = digest_aliases; digest_alias->alias_name; - ++digest_alias) { - for(chksum = mlfile->checksum; chksum; chksum = chksum->next) { - if(Curl_raw_equal(digest_alias->alias_name, chksum->hash_name) && - strlen(chksum->hash_value) == - digest_alias->digest_def->dparams->digest_resultlen*2) { - break; - } - } - if(chksum) { - break; - } - } - if(!digest_alias->alias_name) { - fprintf(config->errors, "No supported checksum in Metalink file\n"); - return -2; - } - rv = check_hash(filename, digest_alias->digest_def, - chksum->hash_value, 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; -} -- cgit v1.2.1 From 1bfd750f3f9e173ce5a6735756106cf6a1804960 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 26 May 2012 22:51:03 +0900 Subject: Reduced #ifdef HAVE_METALINK --- src/tool_metalink.c | 126 +++++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 60 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 461dbf148..6e18b4247 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -31,6 +31,8 @@ # include #endif +#include + #include "rawstr.h" #include "tool_metalink.h" @@ -412,6 +414,8 @@ int metalink_check_hash(struct Configurable *config, #endif /* METALINK_HASH_CHECK */ +#ifdef HAVE_LIBMETALINK + static metalink_checksum *new_metalink_checksum(const char *hash_name, const char *hash_value) { @@ -423,16 +427,6 @@ static metalink_checksum *new_metalink_checksum(const char *hash_name, return chksum; } -static void delete_metalink_checksum(metalink_checksum *chksum) -{ - if(chksum == NULL) { - return; - } - Curl_safefree(chksum->hash_value); - Curl_safefree(chksum->hash_name); - Curl_safefree(chksum); -} - static metalink_resource *new_metalink_resource(const char *url) { metalink_resource *res; @@ -442,15 +436,6 @@ static metalink_resource *new_metalink_resource(const char *url) return res; } -static void delete_metalink_resource(metalink_resource *res) -{ - if(res == NULL) { - return; - } - Curl_safefree(res->url); - Curl_safefree(res); -} - static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo) { metalinkfile *f; @@ -488,47 +473,6 @@ static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo) return f; } -static void delete_metalinkfile(metalinkfile *mlfile) -{ - metalink_checksum *mc; - metalink_resource *res; - if(mlfile == NULL) { - return; - } - Curl_safefree(mlfile->filename); - for(mc = mlfile->checksum; mc;) { - metalink_checksum *next; - next = mc->next; - delete_metalink_checksum(mc); - mc = next; - } - for(res = mlfile->resource; res;) { - metalink_resource *next; - next = res->next; - delete_metalink_resource(res); - res = next; - } - Curl_safefree(mlfile); -} - -int count_next_metalink_resource(metalinkfile *mlfile) -{ - int count = 0; - metalink_resource *res; - for(res = mlfile->resource; res; res = res->next, ++count); - return count; -} - -void clean_metalink(struct Configurable *config) -{ - while(config->metalinkfile_list) { - metalinkfile *mlfile = config->metalinkfile_list; - config->metalinkfile_list = config->metalinkfile_list->next; - delete_metalinkfile(mlfile); - } - config->metalinkfile_last = 0; -} - int parse_metalink(struct Configurable *config, const char *infile) { metalink_error_t r; @@ -592,6 +536,8 @@ int parse_metalink(struct Configurable *config, const char *infile) return 0; } +#endif /* HAVE_LIBMETALINK */ + /* * Returns nonzero if content_type includes mediatype. */ @@ -612,3 +558,63 @@ int check_metalink_content_type(const char *content_type) { return check_content_type(content_type, "application/metalink+xml"); } + +int count_next_metalink_resource(metalinkfile *mlfile) +{ + int count = 0; + metalink_resource *res; + for(res = mlfile->resource; res; res = res->next, ++count); + return count; +} + +static void delete_metalink_checksum(metalink_checksum *chksum) +{ + if(chksum == NULL) { + return; + } + Curl_safefree(chksum->hash_value); + Curl_safefree(chksum->hash_name); + Curl_safefree(chksum); +} + +static void delete_metalink_resource(metalink_resource *res) +{ + if(res == NULL) { + return; + } + Curl_safefree(res->url); + Curl_safefree(res); +} + +static void delete_metalinkfile(metalinkfile *mlfile) +{ + metalink_checksum *mc; + metalink_resource *res; + if(mlfile == NULL) { + return; + } + Curl_safefree(mlfile->filename); + for(mc = mlfile->checksum; mc;) { + metalink_checksum *next; + next = mc->next; + delete_metalink_checksum(mc); + mc = next; + } + for(res = mlfile->resource; res;) { + metalink_resource *next; + next = res->next; + delete_metalink_resource(res); + res = next; + } + Curl_safefree(mlfile); +} + +void clean_metalink(struct Configurable *config) +{ + while(config->metalinkfile_list) { + metalinkfile *mlfile = config->metalinkfile_list; + config->metalinkfile_list = config->metalinkfile_list->next; + delete_metalinkfile(mlfile); + } + config->metalinkfile_last = 0; +} -- cgit v1.2.1 From 6015b71c4e3ef4dbb79839939490215b7e8b8ead Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 26 May 2012 23:20:23 +0200 Subject: metalink: fix build errors when disabled --- src/tool_metalink.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 6e18b4247..58aae831d 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -31,6 +31,8 @@ # include #endif +#ifdef HAVE_LIBMETALINK + #include #include "rawstr.h" @@ -618,3 +620,5 @@ void clean_metalink(struct Configurable *config) } config->metalinkfile_last = 0; } + +#endif /* HAVE_LIBMETALINK */ -- cgit v1.2.1 From 04ca9aecd1a6b32c5e81885f7f1e99d30d6d8ec3 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 7 Jun 2012 23:49:27 +0200 Subject: metalink: build fixes and adjustments I --- src/tool_metalink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 58aae831d..375cdd3b2 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -31,7 +31,7 @@ # include #endif -#ifdef HAVE_LIBMETALINK +#ifdef USE_METALINK #include @@ -416,7 +416,7 @@ int metalink_check_hash(struct Configurable *config, #endif /* METALINK_HASH_CHECK */ -#ifdef HAVE_LIBMETALINK +#ifdef USE_METALINK static metalink_checksum *new_metalink_checksum(const char *hash_name, const char *hash_value) @@ -538,7 +538,7 @@ int parse_metalink(struct Configurable *config, const char *infile) return 0; } -#endif /* HAVE_LIBMETALINK */ +#endif /* USE_METALINK */ /* * Returns nonzero if content_type includes mediatype. @@ -621,4 +621,4 @@ void clean_metalink(struct Configurable *config) config->metalinkfile_last = 0; } -#endif /* HAVE_LIBMETALINK */ +#endif /* USE_METALINK */ -- cgit v1.2.1 From eeeba1496cbcac7aaffdd9355270d61582e98d3a Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 8 Jun 2012 14:21:29 +0200 Subject: metalink: build fixes and adjustments II Additionally, make hash checking ability mandatory in order to allow metalink support in curl. A command line option could be introduced to skip hash checking at runtime, but the ability to check hashes should always be built-in when providing metalink support. --- src/tool_metalink.c | 241 ++++++++++++++++++++++++---------------------------- 1 file changed, 109 insertions(+), 132 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 375cdd3b2..1dfe32783 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -21,26 +21,43 @@ ***************************************************************************/ #include "tool_setup.h" -#ifdef HAVE_UNISTD_H -# include -#endif - -#include - -#ifdef HAVE_FCNTL_H -# include -#endif - #ifdef USE_METALINK #include +#ifdef USE_SSLEAY +# ifdef USE_OPENSSL +# include +# include +# else +# include +# include +# endif +#elif defined(USE_GNUTLS_NETTLE) +# include +# include +# define MD5_CTX struct md5_ctx +# define SHA_CTX struct sha1_ctx +# define SHA256_CTX struct sha256_ctx +#elif defined(USE_GNUTLS) +# include +# define MD5_CTX gcry_md_hd_t +# define SHA_CTX gcry_md_hd_t +# define SHA256_CTX gcry_md_hd_t +#else +# error "Can't compile METALINK support without a crypto library." +#endif + #include "rawstr.h" -#include "tool_metalink.h" +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + #include "tool_getparam.h" #include "tool_paramhlp.h" #include "tool_cfgable.h" +#include "tool_metalink.h" #include "memdebug.h" /* keep this as LAST include */ @@ -56,180 +73,174 @@ return PARAM_NO_MEM; \ } WHILE_FALSE -#ifdef USE_GNUTLS_NETTLE +const digest_params MD5_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) MD5_Init, + (Curl_digest_update_func) MD5_Update, + (Curl_digest_final_func) MD5_Final, + sizeof(MD5_CTX), + 16 + } +}; + +const digest_params SHA1_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) SHA1_Init, + (Curl_digest_update_func) SHA1_Update, + (Curl_digest_final_func) SHA1_Final, + sizeof(SHA_CTX), + 20 + } +}; + +const digest_params SHA256_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) SHA256_Init, + (Curl_digest_update_func) SHA256_Update, + (Curl_digest_final_func) SHA256_Final, + sizeof(SHA256_CTX), + 32 + } +}; + +static const metalink_digest_def SHA256_DIGEST_DEF[] = { + {"sha-256", SHA256_DIGEST_PARAMS} +}; -#include -#include +static const metalink_digest_def SHA1_DIGEST_DEF[] = { + {"sha-1", SHA1_DIGEST_PARAMS} +}; -typedef struct md5_ctx MD5_CTX; +static const metalink_digest_def MD5_DIGEST_DEF[] = { + {"md5", MD5_DIGEST_PARAMS} +}; -static void MD5_Init(MD5_CTX * ctx) +/* + * The alias of supported hash functions in the order by preference + * (basically stronger hash comes first). We included "sha-256" and + * "sha256". The former is the name defined in the IANA registry named + * "Hash Function Textual Names". The latter is widely (and + * historically) used in Metalink version 3. + */ +static const metalink_digest_alias digest_aliases[] = { + {"sha-256", SHA256_DIGEST_DEF}, + {"sha256", SHA256_DIGEST_DEF}, + {"sha-1", SHA1_DIGEST_DEF}, + {"sha1", SHA1_DIGEST_DEF}, + {"md5", MD5_DIGEST_DEF}, + {NULL, NULL} +}; + +#ifdef USE_GNUTLS_NETTLE + +static void MD5_Init(MD5_CTX *ctx) { md5_init(ctx); } -static void MD5_Update(MD5_CTX * ctx, - const unsigned char * input, +static void MD5_Update(MD5_CTX *ctx, + const unsigned char *input, unsigned int inputLen) { md5_update(ctx, inputLen, input); } -static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx) +static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) { md5_digest(ctx, 16, digest); } -typedef struct sha1_ctx SHA_CTX; - static void SHA1_Init(SHA_CTX *ctx) { sha1_init(ctx); } static void SHA1_Update(SHA_CTX *ctx, - const unsigned char * input, + const unsigned char *input, unsigned int inputLen) { sha1_update(ctx, inputLen, input); } -static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx) +static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx) { sha1_digest(ctx, 20, digest); } -typedef struct sha256_ctx SHA256_CTX; - static void SHA256_Init(SHA256_CTX *ctx) { sha256_init(ctx); } static void SHA256_Update(SHA256_CTX *ctx, - const unsigned char * input, + const unsigned char *input, unsigned int inputLen) { sha256_update(ctx, inputLen, input); } -static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) { sha256_digest(ctx, 32, digest); } -#else - -#ifdef USE_GNUTLS - -#include +#elif defined(USE_GNUTLS) -typedef gcry_md_hd_t MD5_CTX; - -static void MD5_Init(MD5_CTX * ctx) +static void MD5_Init(MD5_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_MD5, 0); } -static void MD5_Update(MD5_CTX * ctx, - const unsigned char * input, +static void MD5_Update(MD5_CTX *ctx, + const unsigned char *input, unsigned int inputLen) { gcry_md_write(*ctx, input, inputLen); } -static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx) +static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) { memcpy(digest, gcry_md_read(*ctx, 0), 16); gcry_md_close(*ctx); } -typedef gcry_md_hd_t SHA_CTX; - -static void SHA1_Init(SHA_CTX * ctx) +static void SHA1_Init(SHA_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_SHA1, 0); } -static void SHA1_Update(SHA_CTX * ctx, - const unsigned char * input, - unsigned int inputLen) +static void SHA1_Update(SHA_CTX *ctx, + const unsigned char *input, + unsigned int inputLen) { gcry_md_write(*ctx, input, inputLen); } -static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx) +static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx) { memcpy(digest, gcry_md_read(*ctx, 0), 20); gcry_md_close(*ctx); } -typedef gcry_md_hd_t SHA256_CTX; - -static void SHA256_Init(SHA256_CTX * ctx) +static void SHA256_Init(SHA256_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_SHA256, 0); } -static void SHA256_Update(SHA256_CTX * ctx, - const unsigned char * input, - unsigned int inputLen) +static void SHA256_Update(SHA256_CTX *ctx, + const unsigned char *input, + unsigned int inputLen) { gcry_md_write(*ctx, input, inputLen); } -static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) { memcpy(digest, gcry_md_read(*ctx, 0), 32); gcry_md_close(*ctx); } -#else - -#ifdef USE_OPENSSL -# include -# include -#else /* USE_OPENSSL */ - -/* TODO hash functions for other libraries here */ - -#endif /* USE_OPENSSL */ - -#endif /* USE_GNUTLS */ - -#endif /* USE_GNUTLS_NETTLE */ - -#ifdef METALINK_HASH_CHECK - -const digest_params MD5_DIGEST_PARAMS[] = { - { - (Curl_digest_init_func) MD5_Init, - (Curl_digest_update_func) MD5_Update, - (Curl_digest_final_func) MD5_Final, - sizeof(MD5_CTX), - 16 - } -}; - -const digest_params SHA1_DIGEST_PARAMS[] = { - { - (Curl_digest_init_func) SHA1_Init, - (Curl_digest_update_func) SHA1_Update, - (Curl_digest_final_func) SHA1_Final, - sizeof(SHA_CTX), - 20 - } -}; - -const digest_params SHA256_DIGEST_PARAMS[] = { - { - (Curl_digest_init_func) SHA256_Init, - (Curl_digest_update_func) SHA256_Update, - (Curl_digest_final_func) SHA256_Final, - sizeof(SHA256_CTX), - 32 - } -}; +#endif /* CRYPTO LIBS */ digest_context *Curl_digest_init(const digest_params *dparams) { @@ -274,34 +285,6 @@ int Curl_digest_final(digest_context *context, unsigned char *result) return 0; } -static const metalink_digest_def SHA256_DIGEST_DEF[] = { - {"sha-256", SHA256_DIGEST_PARAMS} -}; - -static const metalink_digest_def SHA1_DIGEST_DEF[] = { - {"sha-1", SHA1_DIGEST_PARAMS} -}; - -static const metalink_digest_def MD5_DIGEST_DEF[] = { - {"md5", MD5_DIGEST_PARAMS} -}; - -/* - * The alias of supported hash functions in the order by preference - * (basically stronger hash comes first). We included "sha-256" and - * "sha256". The former is the name defined in the IANA registry named - * "Hash Function Textual Names". The latter is widely (and - * historically) used in Metalink version 3. - */ -static const metalink_digest_alias digest_aliases[] = { - {"sha-256", SHA256_DIGEST_DEF}, - {"sha256", SHA256_DIGEST_DEF}, - {"sha-1", SHA1_DIGEST_DEF}, - {"sha1", SHA1_DIGEST_DEF}, - {"md5", MD5_DIGEST_DEF}, - {NULL, NULL} -}; - static unsigned char hex_to_uint(const char *s) { int v[2]; @@ -414,10 +397,6 @@ int metalink_check_hash(struct Configurable *config, return rv; } -#endif /* METALINK_HASH_CHECK */ - -#ifdef USE_METALINK - static metalink_checksum *new_metalink_checksum(const char *hash_name, const char *hash_value) { @@ -538,8 +517,6 @@ int parse_metalink(struct Configurable *config, const char *infile) return 0; } -#endif /* USE_METALINK */ - /* * Returns nonzero if content_type includes mediatype. */ -- cgit v1.2.1 From 870c99b7e76623fc2200dcdef6667759c9578a2c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 8 Jun 2012 23:13:32 +0200 Subject: metalink: restore some includes Commit eeeba1496cbca removed them and thus broke my Linux build --- src/tool_metalink.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 1dfe32783..8909397b7 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -23,6 +23,16 @@ #ifdef USE_METALINK +#ifdef HAVE_UNISTD_H +# include +#endif + +#include + +#ifdef HAVE_FCNTL_H +# include +#endif + #include #ifdef USE_SSLEAY -- cgit v1.2.1 From 424bb3587748eb59c0d56613e88ef4511ad4dcbf Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 16 Jun 2012 22:58:06 +0900 Subject: curl: Refactored metalink_checksum When creating metalink_checksum from metalink_checksum_t, first check hex digest is valid for the given hash function. We do this check in the order of digest_aliases so that first good match will be chosen (strongest hash function available). As a result, the metalinkfile now only contains at most one metalink_checksum because other entries are just redundant. --- src/tool_metalink.c | 105 +++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 54 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 8909397b7..8f6a1f870 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -325,13 +325,12 @@ static unsigned char hex_to_uint(const char *s) */ static int check_hash(const char *filename, const metalink_digest_def *digest_def, - const char *hex_hash, FILE *error) + const unsigned char *digest, FILE *error) { unsigned char *result; digest_context *dctx; int check_ok; int fd; - size_t i; fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name, filename); fd = open(filename, O_RDONLY); @@ -357,13 +356,8 @@ static int check_hash(const char *filename, Curl_digest_update(dctx, buf, (unsigned int)len); } Curl_digest_final(dctx, result); - check_ok = 1; - for(i = 0; i < digest_def->dparams->digest_resultlen; ++i) { - if(hex_to_uint(&hex_hash[i*2]) != result[i]) { - check_ok = 0; - break; - } - } + check_ok = memcmp(result, digest, + digest_def->dparams->digest_resultlen) == 0; free(result); close(fd); return check_ok; @@ -373,31 +367,12 @@ int metalink_check_hash(struct Configurable *config, metalinkfile *mlfile, const char *filename) { - metalink_checksum *chksum; - const metalink_digest_alias *digest_alias; int rv; if(mlfile->checksum == NULL) { return -2; } - for(digest_alias = digest_aliases; digest_alias->alias_name; - ++digest_alias) { - for(chksum = mlfile->checksum; chksum; chksum = chksum->next) { - if(Curl_raw_equal(digest_alias->alias_name, chksum->hash_name) && - strlen(chksum->hash_value) == - digest_alias->digest_def->dparams->digest_resultlen*2) { - break; - } - } - if(chksum) { - break; - } - } - if(!digest_alias->alias_name) { - fprintf(config->errors, "No supported checksum in Metalink file\n"); - return -2; - } - rv = check_hash(filename, digest_alias->digest_def, - chksum->hash_value, config->errors); + rv = check_hash(filename, mlfile->checksum->digest_def, + mlfile->checksum->digest, config->errors); if(rv == 1) { fprintf(config->errors, "Checksum matched\n"); } @@ -407,14 +382,20 @@ int metalink_check_hash(struct Configurable *config, return rv; } -static metalink_checksum *new_metalink_checksum(const char *hash_name, - const char *hash_value) +static metalink_checksum *new_metalink_checksum_from_hex_digest +(const metalink_digest_def *digest_def, const char *hex_digest) { metalink_checksum *chksum; + unsigned char *digest; + size_t i; + size_t len = strlen(hex_digest); + digest = malloc(len/2); + for(i = 0; i < len; i += 2) { + digest[i/2] = hex_to_uint(hex_digest+i); + } chksum = malloc(sizeof(metalink_checksum)); - chksum->next = NULL; - chksum->hash_name = strdup(hash_name); - chksum->hash_value = strdup(hash_value); + chksum->digest_def = digest_def; + chksum->digest = digest; return chksum; } @@ -427,6 +408,23 @@ static metalink_resource *new_metalink_resource(const char *url) return res; } +/* Returns nonzero if hex_digest is properly formatted; that is each + letter is in [0-9A-Za-z] and the length of the string equals to the + result length of digest * 2. */ +static int check_hex_digest(const char *hex_digest, + const metalink_digest_def *digest_def) +{ + size_t i; + for(i = 0; hex_digest[i]; ++i) { + char c = hex_digest[i]; + if(!(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z'))) { + return 0; + } + } + return digest_def->dparams->digest_resultlen * 2 == i; +} + static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo) { metalinkfile *f; @@ -436,17 +434,23 @@ static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo) f->checksum = NULL; f->resource = NULL; if(fileinfo->checksums) { - metalink_checksum_t **p; - metalink_checksum root, *tail; - root.next = NULL; - tail = &root; - for(p = fileinfo->checksums; *p; ++p) { - metalink_checksum *chksum; - chksum = new_metalink_checksum((*p)->type, (*p)->hash); - tail->next = chksum; - tail = chksum; + const metalink_digest_alias *digest_alias; + for(digest_alias = digest_aliases; digest_alias->alias_name; + ++digest_alias) { + metalink_checksum_t **p; + for(p = fileinfo->checksums; *p; ++p) { + if(Curl_raw_equal(digest_alias->alias_name, (*p)->type) && + check_hex_digest((*p)->hash, digest_alias->digest_def)) { + f->checksum = + new_metalink_checksum_from_hex_digest(digest_alias->digest_def, + (*p)->hash); + break; + } + } + if(f->checksum) { + break; + } } - f->checksum = root.next; } if(fileinfo->resources) { metalink_resource_t **p; @@ -561,8 +565,7 @@ static void delete_metalink_checksum(metalink_checksum *chksum) if(chksum == NULL) { return; } - Curl_safefree(chksum->hash_value); - Curl_safefree(chksum->hash_name); + Curl_safefree(chksum->digest); Curl_safefree(chksum); } @@ -577,18 +580,12 @@ static void delete_metalink_resource(metalink_resource *res) static void delete_metalinkfile(metalinkfile *mlfile) { - metalink_checksum *mc; metalink_resource *res; if(mlfile == NULL) { return; } Curl_safefree(mlfile->filename); - for(mc = mlfile->checksum; mc;) { - metalink_checksum *next; - next = mc->next; - delete_metalink_checksum(mc); - mc = next; - } + delete_metalink_checksum(mlfile->checksum); for(res = mlfile->resource; res;) { metalink_resource *next; next = res->next; -- cgit v1.2.1 From 196c8242caa30472564290f1c89e7e19d2f04453 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 21 Jun 2012 00:51:06 +0900 Subject: curl: Made --metalink option toggle Metalink functionality 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_metalink.c | 52 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 12 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 8f6a1f870..74a0012ac 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -33,8 +33,6 @@ # include #endif -#include - #ifdef USE_SSLEAY # ifdef USE_OPENSSL # include @@ -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. */ -- cgit v1.2.1 From aefb9196cfb60c534ce1ce31db38940f3bf4df83 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 21 Jun 2012 23:32:12 +0900 Subject: curl: Prefixed all Metalink related messages with "Metalink: " --- src/tool_metalink.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 74a0012ac..89a99d3ec 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -330,11 +330,13 @@ static int check_hash(const char *filename, digest_context *dctx; int check_ok; int fd; - fprintf(error, "Validating %s checksum (This may take some time)...\n", + fprintf(error, + "Metalink: 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)); + fprintf(error, "Metalink: Could not open file %s: %s\n", filename, + strerror(errno)); return -1; } dctx = Curl_digest_init(digest_def->dparams); @@ -346,7 +348,7 @@ static int check_hash(const char *filename, break; } else if(len == -1) { - fprintf(error, "Could not read file %s: %s\n", filename, + fprintf(error, "Metalink: Could not read file %s: %s\n", filename, strerror(errno)); Curl_digest_final(dctx, result); close(fd); @@ -359,9 +361,9 @@ static int check_hash(const char *filename, digest_def->dparams->digest_resultlen) == 0; /* sha*sum style verdict output */ if(check_ok) - fprintf(error, "%s: OK\n", filename); + fprintf(error, "Metalink: %s: OK\n", filename); else - fprintf(error, "%s: FAILED\n", filename); + fprintf(error, "Metalink: %s: FAILED\n", filename); free(result); close(fd); @@ -480,7 +482,8 @@ int parse_metalink(struct Configurable *config, struct OutStruct *outs) return -1; } if(metalink->files == NULL) { - fprintf(config->errors, "\nMetalink does not contain any file.\n"); + fprintf(config->errors, + "\nMetalink: Metalink XML file does not contain any file.\n"); metalink_delete(metalink); return 0; } @@ -488,7 +491,8 @@ int parse_metalink(struct Configurable *config, struct OutStruct *outs) struct getout *url; /* Skip an entry which has no resource. */ if(!(*files)->resources) { - fprintf(config->errors, "\nFile %s does not have any resource.\n", + fprintf(config->errors, + "\nMetalink: File %s does not have any resource.\n", (*files)->name); continue; } @@ -554,7 +558,7 @@ size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb, if(rv == 0) return sz * nmemb; else { - warnf(config, "Failed to parse Metalink XML\n"); + warnf(config, "Metalink: Failed to parse Metalink XML file\n"); return failure; } } -- cgit v1.2.1 From 3e6dfe138a959059ed3a7c69e27d99d192325fd2 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 26 Jun 2012 22:55:16 +0900 Subject: Metalink: updated message format --- src/tool_metalink.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 89a99d3ec..806574133 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -330,12 +330,10 @@ static int check_hash(const char *filename, digest_context *dctx; int check_ok; int fd; - fprintf(error, - "Metalink: Validating %s checksum (This may take some time)...\n", - digest_def->hash_name); + fprintf(error, "Metalink: validating (%s)...\n", filename); fd = open(filename, O_RDONLY); if(fd == -1) { - fprintf(error, "Metalink: Could not open file %s: %s\n", filename, + fprintf(error, "Metalink: validating (%s) FAILED (%s)\n", filename, strerror(errno)); return -1; } @@ -348,7 +346,7 @@ static int check_hash(const char *filename, break; } else if(len == -1) { - fprintf(error, "Metalink: Could not read file %s: %s\n", filename, + fprintf(error, "Metalink: validating (%s) FAILED (%s)\n", filename, strerror(errno)); Curl_digest_final(dctx, result); close(fd); @@ -361,9 +359,10 @@ static int check_hash(const char *filename, digest_def->dparams->digest_resultlen) == 0; /* sha*sum style verdict output */ if(check_ok) - fprintf(error, "Metalink: %s: OK\n", filename); + fprintf(error, "Metalink: validating (%s) OK\n", filename); else - fprintf(error, "Metalink: %s: FAILED\n", filename); + fprintf(error, "Metalink: validating (%s) FAILED (digest mismatch)\n", + filename); free(result); close(fd); @@ -469,7 +468,8 @@ static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo) return f; } -int parse_metalink(struct Configurable *config, struct OutStruct *outs) +int parse_metalink(struct Configurable *config, struct OutStruct *outs, + const char *metalink_url) { metalink_error_t r; metalink_t* metalink; @@ -482,8 +482,9 @@ int parse_metalink(struct Configurable *config, struct OutStruct *outs) return -1; } if(metalink->files == NULL) { - fprintf(config->errors, - "\nMetalink: Metalink XML file does not contain any file.\n"); + fprintf(config->errors, "\nMetalink: parsing (%s) WARNING " + "(missing or invalid file name)\n", + metalink_url); metalink_delete(metalink); return 0; } @@ -491,9 +492,9 @@ int parse_metalink(struct Configurable *config, struct OutStruct *outs) struct getout *url; /* Skip an entry which has no resource. */ if(!(*files)->resources) { - fprintf(config->errors, - "\nMetalink: File %s does not have any resource.\n", - (*files)->name); + fprintf(config->errors, "\nMetalink: parsing (%s) WARNING " + "(missing or invalid resource)\n", + metalink_url, (*files)->name); continue; } if(config->url_get || @@ -558,7 +559,7 @@ size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb, if(rv == 0) return sz * nmemb; else { - warnf(config, "Metalink: Failed to parse Metalink XML file\n"); + fprintf(config->errors, "Metalink: parsing FAILED\n"); return failure; } } -- cgit v1.2.1 From bf4580d5fdd1ae5b76acd0957afddbf62918dfee Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 28 Jun 2012 00:20:20 +0900 Subject: Metalink: message updates Print "parsing (...) OK" only when no warnings are generated. If no file is found in Metalink, treat it FAILED. If no digest is provided, print WARNING in parse_metalink(). Also print validating FAILED after download. These changes make tests 2012 to 2016 pass. --- src/tool_metalink.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 806574133..5491815a3 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -330,7 +330,6 @@ static int check_hash(const char *filename, digest_context *dctx; int check_ok; int fd; - fprintf(error, "Metalink: validating (%s)...\n", filename); fd = open(filename, O_RDONLY); if(fd == -1) { fprintf(error, "Metalink: validating (%s) FAILED (%s)\n", filename, @@ -374,7 +373,11 @@ int metalink_check_hash(struct Configurable *config, const char *filename) { int rv; + fprintf(config->errors, "Metalink: validating (%s)...\n", filename); if(mlfile->checksum == NULL) { + fprintf(config->errors, + "Metalink: validating (%s) FAILED (digest missing)\n", + filename); return -2; } rv = check_hash(filename, mlfile->checksum->digest_def, @@ -474,6 +477,7 @@ int parse_metalink(struct Configurable *config, struct OutStruct *outs, metalink_error_t r; metalink_t* metalink; metalink_file_t **files; + bool warnings = FALSE; /* metlaink_parse_final deletes outs->metalink_parser */ r = metalink_parse_final(outs->metalink_parser, NULL, 0, &metalink); @@ -482,17 +486,17 @@ int parse_metalink(struct Configurable *config, struct OutStruct *outs, return -1; } if(metalink->files == NULL) { - fprintf(config->errors, "\nMetalink: parsing (%s) WARNING " + fprintf(config->errors, "Metalink: parsing (%s) WARNING " "(missing or invalid file name)\n", metalink_url); metalink_delete(metalink); - return 0; + return -1; } for(files = metalink->files; *files; ++files) { struct getout *url; /* Skip an entry which has no resource. */ if(!(*files)->resources) { - fprintf(config->errors, "\nMetalink: parsing (%s) WARNING " + fprintf(config->errors, "Metalink: parsing (%s) WARNING " "(missing or invalid resource)\n", metalink_url, (*files)->name); continue; @@ -517,7 +521,12 @@ int parse_metalink(struct Configurable *config, struct OutStruct *outs, if(url) { metalinkfile *mlfile; mlfile = new_metalinkfile(*files); - + if(!mlfile->checksum) { + warnings = TRUE; + fprintf(config->errors, "Metalink: parsing (%s) WARNING " + "(digest missing)\n", + metalink_url); + } /* Set name as url */ GetStr(&url->url, mlfile->filename); @@ -534,7 +543,7 @@ int parse_metalink(struct Configurable *config, struct OutStruct *outs, } } metalink_delete(metalink); - return 0; + return (warnings) ? -2 : 0; } size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb, -- cgit v1.2.1 From 9f304291bd72aae66944cd7523707e1b88c6ad7b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 6 Aug 2012 19:20:35 +0200 Subject: metalink: change code order to build with gnutls-nettle Bug: http://curl.haxx.se/bug/view.cgi?id=3554668 Reported by: Anthony G. Basile --- src/tool_metalink.c | 116 ++++++++++++++++++++++++++-------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 5491815a3..3a24a1556 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -82,64 +82,6 @@ return PARAM_NO_MEM; \ } WHILE_FALSE -const digest_params MD5_DIGEST_PARAMS[] = { - { - (Curl_digest_init_func) MD5_Init, - (Curl_digest_update_func) MD5_Update, - (Curl_digest_final_func) MD5_Final, - sizeof(MD5_CTX), - 16 - } -}; - -const digest_params SHA1_DIGEST_PARAMS[] = { - { - (Curl_digest_init_func) SHA1_Init, - (Curl_digest_update_func) SHA1_Update, - (Curl_digest_final_func) SHA1_Final, - sizeof(SHA_CTX), - 20 - } -}; - -const digest_params SHA256_DIGEST_PARAMS[] = { - { - (Curl_digest_init_func) SHA256_Init, - (Curl_digest_update_func) SHA256_Update, - (Curl_digest_final_func) SHA256_Final, - sizeof(SHA256_CTX), - 32 - } -}; - -static const metalink_digest_def SHA256_DIGEST_DEF[] = { - {"sha-256", SHA256_DIGEST_PARAMS} -}; - -static const metalink_digest_def SHA1_DIGEST_DEF[] = { - {"sha-1", SHA1_DIGEST_PARAMS} -}; - -static const metalink_digest_def MD5_DIGEST_DEF[] = { - {"md5", MD5_DIGEST_PARAMS} -}; - -/* - * The alias of supported hash functions in the order by preference - * (basically stronger hash comes first). We included "sha-256" and - * "sha256". The former is the name defined in the IANA registry named - * "Hash Function Textual Names". The latter is widely (and - * historically) used in Metalink version 3. - */ -static const metalink_digest_alias digest_aliases[] = { - {"sha-256", SHA256_DIGEST_DEF}, - {"sha256", SHA256_DIGEST_DEF}, - {"sha-1", SHA1_DIGEST_DEF}, - {"sha1", SHA1_DIGEST_DEF}, - {"md5", MD5_DIGEST_DEF}, - {NULL, NULL} -}; - #ifdef USE_GNUTLS_NETTLE static void MD5_Init(MD5_CTX *ctx) @@ -251,6 +193,64 @@ static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) #endif /* CRYPTO LIBS */ +const digest_params MD5_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) MD5_Init, + (Curl_digest_update_func) MD5_Update, + (Curl_digest_final_func) MD5_Final, + sizeof(MD5_CTX), + 16 + } +}; + +const digest_params SHA1_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) SHA1_Init, + (Curl_digest_update_func) SHA1_Update, + (Curl_digest_final_func) SHA1_Final, + sizeof(SHA_CTX), + 20 + } +}; + +const digest_params SHA256_DIGEST_PARAMS[] = { + { + (Curl_digest_init_func) SHA256_Init, + (Curl_digest_update_func) SHA256_Update, + (Curl_digest_final_func) SHA256_Final, + sizeof(SHA256_CTX), + 32 + } +}; + +static const metalink_digest_def SHA256_DIGEST_DEF[] = { + {"sha-256", SHA256_DIGEST_PARAMS} +}; + +static const metalink_digest_def SHA1_DIGEST_DEF[] = { + {"sha-1", SHA1_DIGEST_PARAMS} +}; + +static const metalink_digest_def MD5_DIGEST_DEF[] = { + {"md5", MD5_DIGEST_PARAMS} +}; + +/* + * The alias of supported hash functions in the order by preference + * (basically stronger hash comes first). We included "sha-256" and + * "sha256". The former is the name defined in the IANA registry named + * "Hash Function Textual Names". The latter is widely (and + * historically) used in Metalink version 3. + */ +static const metalink_digest_alias digest_aliases[] = { + {"sha-256", SHA256_DIGEST_DEF}, + {"sha256", SHA256_DIGEST_DEF}, + {"sha-1", SHA1_DIGEST_DEF}, + {"sha1", SHA1_DIGEST_DEF}, + {"md5", MD5_DIGEST_DEF}, + {NULL, NULL} +}; + digest_context *Curl_digest_init(const digest_params *dparams) { digest_context *ctxt; -- cgit v1.2.1 From 1de496cf0f279ae27a46597408b5cbb360942eda Mon Sep 17 00:00:00 2001 From: Nick Zitzmann Date: Tue, 7 Aug 2012 17:08:54 -0600 Subject: metalink: Un-broke the build when building --with-darwinssl --- src/tool_metalink.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 3a24a1556..6e4a31c43 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -52,6 +52,15 @@ # define MD5_CTX gcry_md_hd_t # define SHA_CTX gcry_md_hd_t # define SHA256_CTX gcry_md_hd_t +#elif defined(USE_DARWINSSL) +/* For darwinssl: CommonCrypto has the functions we need. The library's + headers are even backward-compatible with OpenSSL's headers as long as + we define COMMON_DIGEST_FOR_OPENSSL first. + + These functions are available on Tiger and later, as well as iOS 5.0 + and later. If you're building for an older cat, well, sorry. */ +# define COMMON_DIGEST_FOR_OPENSSL +# include #else # error "Can't compile METALINK support without a crypto library." #endif -- cgit v1.2.1 From a6df3550cf5e28eba08a449fb820469ed316389e Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Tue, 11 Sep 2012 01:42:58 +0200 Subject: tool_metalink.c: Fixed validation of binary files containing EOF Since Windows/MinGW threat 0x1A as the EOF character, reading binary files which contain that byte does not work using text mode. The read function will only read until the first 0x1A byte. This means that the hash is not computed from the whole file and the final validation check using hash comparision fails. --- src/tool_metalink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 6e4a31c43..773158711 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -339,7 +339,8 @@ static int check_hash(const char *filename, digest_context *dctx; int check_ok; int fd; - fd = open(filename, O_RDONLY); + /* O_BINARY is required in order to avoid binary EOF in text mode */ + fd = open(filename, O_RDONLY | O_BINARY); if(fd == -1) { fprintf(error, "Metalink: validating (%s) FAILED (%s)\n", filename, strerror(errno)); -- cgit v1.2.1 From 71813f5e46d99ec19685a7359a40bbc3076b16c2 Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Tue, 11 Sep 2012 08:20:43 +0200 Subject: tool_metalink.c: Fixed error: 'O_BINARY' undeclared Check for O_BINARY which is not available on every system. --- src/tool_metalink.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 773158711..b9c291851 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -337,10 +337,15 @@ static int check_hash(const char *filename, { unsigned char *result; digest_context *dctx; - int check_ok; - int fd; + int check_ok, flags, fd; + + flags = O_RDONLY; +#ifdef O_BINARY /* O_BINARY is required in order to avoid binary EOF in text mode */ - fd = open(filename, O_RDONLY | O_BINARY); + flags |= O_BINARY; +#endif + + fd = open(filename, flags); if(fd == -1) { fprintf(error, "Metalink: validating (%s) FAILED (%s)\n", filename, strerror(errno)); -- cgit v1.2.1 From 94c3e0f702c23c30e69c17bddd77c368d1eaac22 Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Tue, 11 Sep 2012 01:58:10 +0200 Subject: tool_metalink.c: Added support for Microsoft Windows CryptoAPI Since Metalink support requires a crypto library for hash functions and Windows comes with the builtin CryptoAPI, this patch adds that API as a fallback to the supported crypto libraries. It is automatically used on Windows if no other library is provided. --- src/tool_metalink.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index b9c291851..9199b50f7 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -61,6 +61,25 @@ and later. If you're building for an older cat, well, sorry. */ # define COMMON_DIGEST_FOR_OPENSSL # include +#elif defined(_WIN32) +/* For Windows: If no other crypto library is provided, we fallback + to the hash functions provided within the Microsoft Windows CryptoAPI */ +# include +/* Custom structure in order to store the required provider and hash handle */ +struct win32_crypto_hash { + HCRYPTPROV hCryptProv; + HCRYPTHASH hHash; +}; +/* Custom Microsoft AES Cryptographic Provider defines required for MinGW */ +# ifndef ALG_SID_SHA_256 +# define ALG_SID_SHA_256 12 +# endif +# ifndef CALG_SHA_256 +# define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) +# endif +# define MD5_CTX struct win32_crypto_hash +# define SHA_CTX struct win32_crypto_hash +# define SHA256_CTX struct win32_crypto_hash #else # error "Can't compile METALINK support without a crypto library." #endif @@ -200,6 +219,82 @@ static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) gcry_md_close(*ctx); } +#elif defined(_WIN32) + +static void win32_crypto_final(struct win32_crypto_hash *ctx, + unsigned char *digest, + unsigned int digestLen) +{ + unsigned long length; + CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); + if(length == digestLen) + CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); + if(ctx->hHash) + CryptDestroyHash(ctx->hHash); + if(ctx->hCryptProv) + CryptReleaseContext(ctx->hCryptProv, 0); +} + +static void MD5_Init(MD5_CTX *ctx) +{ + if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash); + } +} + +static void MD5_Update(MD5_CTX *ctx, + const unsigned char *input, + unsigned int inputLen) +{ + CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0); +} + +static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) +{ + win32_crypto_final(ctx, digest, 16); +} + +static void SHA1_Init(SHA_CTX *ctx) +{ + if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + CryptCreateHash(ctx->hCryptProv, CALG_SHA1, 0, 0, &ctx->hHash); + } +} + +static void SHA1_Update(SHA_CTX *ctx, + const unsigned char *input, + unsigned int inputLen) +{ + CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0); +} + +static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx) +{ + win32_crypto_final(ctx, digest, 20); +} + +static void SHA256_Init(SHA256_CTX *ctx) +{ + if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, + PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { + CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash); + } +} + +static void SHA256_Update(SHA256_CTX *ctx, + const unsigned char *input, + unsigned int inputLen) +{ + CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0); +} + +static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) +{ + win32_crypto_final(ctx, digest, 32); +} + #endif /* CRYPTO LIBS */ const digest_params MD5_DIGEST_PARAMS[] = { -- cgit v1.2.1 From 7f7e2ea72f2471a20060024d6a2d8377d21bcba9 Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Tue, 11 Sep 2012 14:35:18 +0200 Subject: wincrypt: Fixed cross-compilation issues caused by include name For some reason WinCrypt.h is named wincrypt.h under MinGW. --- src/tool_metalink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 9199b50f7..279a626cb 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -64,7 +64,7 @@ #elif defined(_WIN32) /* For Windows: If no other crypto library is provided, we fallback to the hash functions provided within the Microsoft Windows CryptoAPI */ -# include +# include /* Custom structure in order to store the required provider and hash handle */ struct win32_crypto_hash { HCRYPTPROV hCryptProv; -- cgit v1.2.1 From a34197ef77cb9fcc754a84d2f58c006766bb150c Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Wed, 12 Sep 2012 00:09:23 +0200 Subject: tool_metalink.c: Added name of validation hash to messages This makes it easier to debug broken hashes or hash functions. --- src/tool_metalink.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 279a626cb..316392781 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -442,8 +442,8 @@ static int check_hash(const char *filename, fd = open(filename, flags); if(fd == -1) { - fprintf(error, "Metalink: validating (%s) FAILED (%s)\n", filename, - strerror(errno)); + fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename, + digest_def->hash_name, strerror(errno)); return -1; } dctx = Curl_digest_init(digest_def->dparams); @@ -455,8 +455,8 @@ static int check_hash(const char *filename, break; } else if(len == -1) { - fprintf(error, "Metalink: validating (%s) FAILED (%s)\n", filename, - strerror(errno)); + fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename, + digest_def->hash_name, strerror(errno)); Curl_digest_final(dctx, result); close(fd); return -1; @@ -468,10 +468,11 @@ static int check_hash(const char *filename, digest_def->dparams->digest_resultlen) == 0; /* sha*sum style verdict output */ if(check_ok) - fprintf(error, "Metalink: validating (%s) OK\n", filename); + fprintf(error, "Metalink: validating (%s) [%s] OK\n", filename, + digest_def->hash_name); else - fprintf(error, "Metalink: validating (%s) FAILED (digest mismatch)\n", - filename); + fprintf(error, "Metalink: validating (%s) [%s] FAILED (digest mismatch)\n", + filename, digest_def->hash_name); free(result); close(fd); -- cgit v1.2.1 From 42bbc5ce108e273a9890d2007ff55dd73c8d646c Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 17 Sep 2012 23:05:39 +0900 Subject: tool_metalink.c: Filtered resource URLs by type In Metalink v3, the type attribute of url element indicates the type of the resource the URL points to. It can include URL to the meta data, such as BitTorrent metainfo file. In Curl, we are not interested in these meta data URLs. Instead, we are only interested in the HTTP and FTP URLs. This change filters out non-HTTP and FTP URLs. If we don't filter out them, it will be downloaded by curl and hash check will fail if hash is provided and next URL will be tried. This change will cut this useless network transfer. --- src/tool_metalink.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 316392781..103c88649 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -573,9 +573,24 @@ static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo) tail = &root; for(p = fileinfo->resources; *p; ++p) { metalink_resource *res; - res = new_metalink_resource((*p)->url); - tail->next = res; - tail = res; + /* Filter by type if it is non-NULL. In Metalink v3, type + includes the type of the resource. In curl, we are only + interested in HTTP, HTTPS and FTP. In addition to them, + Metalink v3 file may contain bittorrent type URL, which + points to the BitTorrent metainfo file. We ignore it here. + In Metalink v4, type was deprecated and all + fileinfo->resources point to the target file. BitTorrent + metainfo file URL may be appeared in fileinfo->metaurls. + */ + if((*p)->type == NULL || + Curl_raw_equal((*p)->type, "http") || + Curl_raw_equal((*p)->type, "https") || + Curl_raw_equal((*p)->type, "ftp") || + Curl_raw_equal((*p)->type, "ftps")) { + res = new_metalink_resource((*p)->url); + tail->next = res; + tail = res; + } } f->resource = root.next; } -- cgit v1.2.1 From c79c0909d9d2ef16e119f5517a84e8ab706bf61a Mon Sep 17 00:00:00 2001 From: Guenter Knauf Date: Sat, 13 Oct 2012 01:03:34 +0200 Subject: Fix now broken libmetalink-aware OpenSSL build. --- src/tool_metalink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 103c88649..cafa63fdf 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -219,7 +219,7 @@ static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) gcry_md_close(*ctx); } -#elif defined(_WIN32) +#elif defined(_WIN32) && !defined(USE_SSLEAY) static void win32_crypto_final(struct win32_crypto_hash *ctx, unsigned char *digest, -- cgit v1.2.1 From 94891ff296c31c02a3fe8fcdc8da3b9be493fac9 Mon Sep 17 00:00:00 2001 From: Nick Zitzmann Date: Tue, 16 Oct 2012 11:48:55 -0600 Subject: metalink/md5: Use CommonCrypto on Apple operating systems Previously the Metalink code used Apple's CommonCrypto library only if curl was built using the --with-darwinssl option. Now we use CommonCrypto on all Apple operating systems including Tiger or later, or iOS 5 or later, so you don't need to build --with-darwinssl anymore. Also rolled out this change to libcurl's md5 code. --- src/tool_metalink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index cafa63fdf..16689a3c2 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -52,10 +52,10 @@ # define MD5_CTX gcry_md_hd_t # define SHA_CTX gcry_md_hd_t # define SHA256_CTX gcry_md_hd_t -#elif defined(USE_DARWINSSL) -/* For darwinssl: CommonCrypto has the functions we need. The library's - headers are even backward-compatible with OpenSSL's headers as long as - we define COMMON_DIGEST_FOR_OPENSSL first. +#elif defined(__MAC_10_4) || defined(__IPHONE_5_0) +/* For Apple operating systems: CommonCrypto has the functions we need. + The library's headers are even backward-compatible with OpenSSL's + headers as long as we define COMMON_DIGEST_FOR_OPENSSL first. These functions are available on Tiger and later, as well as iOS 5.0 and later. If you're building for an older cat, well, sorry. */ -- cgit v1.2.1 From cf75a64651e927eb00d6cfe2434d8893985a865f Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Wed, 31 Oct 2012 10:43:36 +0100 Subject: tool_metalink: introduce metalink_cleanup() in the internal API ... to release resources allocated at global scope --- src/tool_metalink.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 16689a3c2..a3d2c5018 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -773,4 +773,8 @@ void clean_metalink(struct Configurable *config) config->metalinkfile_last = 0; } +void metalink_cleanup(void) +{ +} + #endif /* USE_METALINK */ -- cgit v1.2.1 From dca8ae5f02a849b0d64befc2023876439396adee Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 30 Oct 2012 14:21:54 +0100 Subject: tool_metalink: allow to handle failure of hash alg initialization --- src/tool_metalink.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index a3d2c5018..6cec8d543 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -112,9 +112,10 @@ struct win32_crypto_hash { #ifdef USE_GNUTLS_NETTLE -static void MD5_Init(MD5_CTX *ctx) +static int MD5_Init(MD5_CTX *ctx) { md5_init(ctx); + return 0; } static void MD5_Update(MD5_CTX *ctx, @@ -129,9 +130,10 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) md5_digest(ctx, 16, digest); } -static void SHA1_Init(SHA_CTX *ctx) +static int SHA1_Init(SHA_CTX *ctx) { sha1_init(ctx); + return 0; } static void SHA1_Update(SHA_CTX *ctx, @@ -146,9 +148,10 @@ static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx) sha1_digest(ctx, 20, digest); } -static void SHA256_Init(SHA256_CTX *ctx) +static int SHA256_Init(SHA256_CTX *ctx) { sha256_init(ctx); + return 0; } static void SHA256_Update(SHA256_CTX *ctx, @@ -165,9 +168,10 @@ static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) #elif defined(USE_GNUTLS) -static void MD5_Init(MD5_CTX *ctx) +static int MD5_Init(MD5_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_MD5, 0); + return 0; } static void MD5_Update(MD5_CTX *ctx, @@ -183,9 +187,10 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) gcry_md_close(*ctx); } -static void SHA1_Init(SHA_CTX *ctx) +static int SHA1_Init(SHA_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_SHA1, 0); + return 0; } static void SHA1_Update(SHA_CTX *ctx, @@ -201,9 +206,10 @@ static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx) gcry_md_close(*ctx); } -static void SHA256_Init(SHA256_CTX *ctx) +static int SHA256_Init(SHA256_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_SHA256, 0); + return 0; } static void SHA256_Update(SHA256_CTX *ctx, @@ -235,12 +241,13 @@ static void win32_crypto_final(struct win32_crypto_hash *ctx, CryptReleaseContext(ctx->hCryptProv, 0); } -static void MD5_Init(MD5_CTX *ctx) +static int MD5_Init(MD5_CTX *ctx) { if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash); } + return 0; } static void MD5_Update(MD5_CTX *ctx, @@ -255,12 +262,13 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) win32_crypto_final(ctx, digest, 16); } -static void SHA1_Init(SHA_CTX *ctx) +static int SHA1_Init(SHA_CTX *ctx) { if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { CryptCreateHash(ctx->hCryptProv, CALG_SHA1, 0, 0, &ctx->hHash); } + return 0; } static void SHA1_Update(SHA_CTX *ctx, @@ -275,12 +283,13 @@ static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx) win32_crypto_final(ctx, digest, 20); } -static void SHA256_Init(SHA256_CTX *ctx) +static int SHA256_Init(SHA256_CTX *ctx) { if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash); } + return 0; } static void SHA256_Update(SHA256_CTX *ctx, @@ -374,7 +383,10 @@ digest_context *Curl_digest_init(const digest_params *dparams) ctxt->digest_hash = dparams; - dparams->digest_init(ctxt->digest_hashctx); + if(dparams->digest_init(ctxt->digest_hashctx) != 0) { + free(ctxt); + return NULL; + } return ctxt; } @@ -425,6 +437,8 @@ static unsigned char hex_to_uint(const char *s) * Checksum didn't match. * -1: * Could not open file; or could not read data from file. + * -2: + * Hash algorithm not available. */ static int check_hash(const char *filename, const metalink_digest_def *digest_def, @@ -446,7 +460,15 @@ static int check_hash(const char *filename, digest_def->hash_name, strerror(errno)); return -1; } + dctx = Curl_digest_init(digest_def->dparams); + if(!dctx) { + fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename, + digest_def->hash_name, "failed to initialize hash algorithm"); + close(fd); + return -2; + } + result = malloc(digest_def->dparams->digest_resultlen); while(1) { unsigned char buf[4096]; -- cgit v1.2.1 From 49c37e6c1c11962102edb44ef36bce4b1b21af53 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 30 Oct 2012 14:59:48 +0100 Subject: tool_metalink: allow to use hash algorithms provided by NSS Fixes bug #3578163: http://sourceforge.net/tracker/?func=detail&atid=100976&aid=3578163&group_id=976 --- src/tool_metalink.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index 6cec8d543..bf4bd107f 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -52,6 +52,15 @@ # define MD5_CTX gcry_md_hd_t # define SHA_CTX gcry_md_hd_t # define SHA256_CTX gcry_md_hd_t +#elif defined(USE_NSS) +# include +# include +# define MD5_CTX void * +# define SHA_CTX void * +# define SHA256_CTX void * +# ifdef HAVE_NSS_INITCONTEXT + static NSSInitContext *nss_context; +# endif #elif defined(__MAC_10_4) || defined(__IPHONE_5_0) /* For Apple operating systems: CommonCrypto has the functions we need. The library's headers are even backward-compatible with OpenSSL's @@ -225,6 +234,95 @@ static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) gcry_md_close(*ctx); } +#elif defined(USE_NSS) + +static int nss_hash_init(void **pctx, SECOidTag hash_alg) +{ + PK11Context *ctx; + + /* we have to initialize NSS if not initialized alraedy */ +#ifdef HAVE_NSS_INITCONTEXT + if(!NSS_IsInitialized() && !nss_context) { + static NSSInitParameters params; + params.length = sizeof params; + nss_context = NSS_InitContext("", "", "", "", ¶ms, NSS_INIT_READONLY + | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN + | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); + } +#endif + + ctx = PK11_CreateDigestContext(hash_alg); + if(!ctx) + return -1; + + if(PK11_DigestBegin(ctx) != SECSuccess) { + PK11_DestroyContext(ctx, PR_TRUE); + return -1; + } + + *pctx = ctx; + return 0; +} + +static void nss_hash_final(void **pctx, unsigned char *out, unsigned int len) +{ + PK11Context *ctx = *pctx; + unsigned int outlen; + PK11_DigestFinal(ctx, out, &outlen, len); + PK11_DestroyContext(ctx, PR_TRUE); +} + +static int MD5_Init(MD5_CTX *pctx) +{ + return nss_hash_init(pctx, SEC_OID_MD5); +} + +static void MD5_Update(MD5_CTX *pctx, + const unsigned char *input, + unsigned int input_len) +{ + PK11_DigestOp(*pctx, input, input_len); +} + +static void MD5_Final(unsigned char digest[16], MD5_CTX *pctx) +{ + nss_hash_final(pctx, digest, 16); +} + +static int SHA1_Init(SHA_CTX *pctx) +{ + return nss_hash_init(pctx, SEC_OID_SHA1); +} + +static void SHA1_Update(SHA_CTX *pctx, + const unsigned char *input, + unsigned int input_len) +{ + PK11_DigestOp(*pctx, input, input_len); +} + +static void SHA1_Final(unsigned char digest[20], SHA_CTX *pctx) +{ + nss_hash_final(pctx, digest, 20); +} + +static int SHA256_Init(SHA256_CTX *pctx) +{ + return nss_hash_init(pctx, SEC_OID_SHA256); +} + +static void SHA256_Update(SHA256_CTX *pctx, + const unsigned char *input, + unsigned int input_len) +{ + PK11_DigestOp(*pctx, input, input_len); +} + +static void SHA256_Final(unsigned char digest[32], SHA256_CTX *pctx) +{ + nss_hash_final(pctx, digest, 32); +} + #elif defined(_WIN32) && !defined(USE_SSLEAY) static void win32_crypto_final(struct win32_crypto_hash *ctx, @@ -797,6 +895,12 @@ void clean_metalink(struct Configurable *config) void metalink_cleanup(void) { +#if defined(USE_NSS) && defined(HAVE_NSS_INITCONTEXT) + if(nss_context) { + NSS_ShutdownContext(nss_context); + nss_context = NULL; + } +#endif } #endif /* USE_METALINK */ -- cgit v1.2.1 From 1099f3a0715f2bad376915122dba2922c33620a3 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 13 Nov 2012 13:09:43 +0100 Subject: tool_metalink: fix error detection of hash alg initialization The {MD5,SHA1,SHA256}_Init functions from OpenSSL are called directly without any wrappers and they return 1 for success, 0 otherwise. Hence, we have to use the same approach in all the wrapper functions that are used for the other crypto libraries. This commit fixes a regression introduced in commit dca8ae5f. --- src/tool_metalink.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'src/tool_metalink.c') diff --git a/src/tool_metalink.c b/src/tool_metalink.c index bf4bd107f..42b514ca3 100644 --- a/src/tool_metalink.c +++ b/src/tool_metalink.c @@ -124,7 +124,7 @@ struct win32_crypto_hash { static int MD5_Init(MD5_CTX *ctx) { md5_init(ctx); - return 0; + return 1; } static void MD5_Update(MD5_CTX *ctx, @@ -142,7 +142,7 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) static int SHA1_Init(SHA_CTX *ctx) { sha1_init(ctx); - return 0; + return 1; } static void SHA1_Update(SHA_CTX *ctx, @@ -160,7 +160,7 @@ static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx) static int SHA256_Init(SHA256_CTX *ctx) { sha256_init(ctx); - return 0; + return 1; } static void SHA256_Update(SHA256_CTX *ctx, @@ -180,7 +180,7 @@ static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx) static int MD5_Init(MD5_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_MD5, 0); - return 0; + return 1; } static void MD5_Update(MD5_CTX *ctx, @@ -199,7 +199,7 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) static int SHA1_Init(SHA_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_SHA1, 0); - return 0; + return 1; } static void SHA1_Update(SHA_CTX *ctx, @@ -218,7 +218,7 @@ static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx) static int SHA256_Init(SHA256_CTX *ctx) { gcry_md_open(ctx, GCRY_MD_SHA256, 0); - return 0; + return 1; } static void SHA256_Update(SHA256_CTX *ctx, @@ -253,15 +253,15 @@ static int nss_hash_init(void **pctx, SECOidTag hash_alg) ctx = PK11_CreateDigestContext(hash_alg); if(!ctx) - return -1; + return /* failure */ 0; if(PK11_DigestBegin(ctx) != SECSuccess) { PK11_DestroyContext(ctx, PR_TRUE); - return -1; + return /* failure */ 0; } *pctx = ctx; - return 0; + return /* success */ 1; } static void nss_hash_final(void **pctx, unsigned char *out, unsigned int len) @@ -345,7 +345,7 @@ static int MD5_Init(MD5_CTX *ctx) PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash); } - return 0; + return 1; } static void MD5_Update(MD5_CTX *ctx, @@ -366,7 +366,7 @@ static int SHA1_Init(SHA_CTX *ctx) PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { CryptCreateHash(ctx->hCryptProv, CALG_SHA1, 0, 0, &ctx->hHash); } - return 0; + return 1; } static void SHA1_Update(SHA_CTX *ctx, @@ -387,7 +387,7 @@ static int SHA256_Init(SHA256_CTX *ctx) PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash); } - return 0; + return 1; } static void SHA256_Update(SHA256_CTX *ctx, @@ -481,7 +481,7 @@ digest_context *Curl_digest_init(const digest_params *dparams) ctxt->digest_hash = dparams; - if(dparams->digest_init(ctxt->digest_hashctx) != 0) { + if(dparams->digest_init(ctxt->digest_hashctx) != 1) { free(ctxt); return NULL; } -- cgit v1.2.1