diff options
-rw-r--r-- | RELEASE-NOTES | 8 | ||||
-rw-r--r-- | configure.ac | 14 | ||||
-rw-r--r-- | lib/vtls/curl_schannel.c | 24 | ||||
-rw-r--r-- | src/tool_cfgable.c | 11 | ||||
-rw-r--r-- | src/tool_cfgable.h | 7 | ||||
-rw-r--r-- | src/tool_getparam.c | 38 | ||||
-rw-r--r-- | src/tool_getparam.h | 10 | ||||
-rw-r--r-- | src/tool_help.c | 4 | ||||
-rw-r--r-- | src/tool_hugehelp.h | 4 | ||||
-rw-r--r-- | src/tool_main.c | 28 | ||||
-rw-r--r-- | src/tool_operate.c | 69 | ||||
-rw-r--r-- | src/tool_operate.h | 2 | ||||
-rw-r--r-- | src/tool_parsecfg.c | 11 | ||||
-rw-r--r-- | src/tool_parsecfg.h | 2 |
14 files changed, 109 insertions, 123 deletions
diff --git a/RELEASE-NOTES b/RELEASE-NOTES index e8df1488f..1222cc174 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -19,6 +19,7 @@ This release includes the following changes: o added CURLOPT_SSL_ENABLE_NPN and CURLOPT_SSL_ENABLE_ALPN o winssl: enable TLSv1.1 and TLSv1.2 by default o winssl: TLSv1.2 disables certificate signatures using MD5 hash + o winssl: enable hostname verification of IP address using SAN or CN [11] This release includes the following bugfixes: @@ -58,9 +59,9 @@ This release would not have looked like this without help, code, reports and advice from friends like these: Adam Sampson, Arvid Norberg, Colin Hogben, Dan Fandrich, Daniel Stenberg, - Fabian Frank, Gisle Vanem, Hubert Kario, Jeff King, Maks Naumov, - Kamil Dudka, Marc Hoersken, Patrick Monnerat, Prash Dush, Remi Gacogne, - Rob Davies, Romulo A. Ceccon, Shao Shuchao, Steve Holme, + David Ryskalczyk, Fabian Frank, Gisle Vanem, Hubert Kario, Jeff King, + Maks Naumov, Kamil Dudka, Marc Hoersken, Patrick Monnerat, Prash Dush, + Remi Gacogne, Rob Davies, Romulo A. Ceccon, Shao Shuchao, Steve Holme, Tatsuhiro Tsujikawa, Thomas Braun, Tiit Pikma, Yehezkel Horowitz, Thanks! (and sorry if I forgot to mention someone) @@ -77,3 +78,4 @@ References to bug reports and discussions on issues: [8] = http://curl.haxx.se/mail/lib-2014-02/0097.html [9] = http://thread.gmane.org/gmane.comp.version-control.git/242213 [10] = http://curl.haxx.se/mail/lib-2014-02/0155.html + [11] = http://curl.haxx.se/mail/lib-2014-02/0243.html diff --git a/configure.ac b/configure.ac index 9dc68664f..ee207f974 100644 --- a/configure.ac +++ b/configure.ac @@ -2469,19 +2469,19 @@ AC_HELP_STRING([--disable-versioned-symbols], [Disable versioned symbols in shar AC_MSG_RESULT(yes) if test "x$OPENSSL_ENABLED" = "x1"; then versioned_symbols_flavour="OPENSSL_" - elif test "x$GNUTLS_ENABLED" == "x1"; then + elif test "x$GNUTLS_ENABLED" = "x1"; then versioned_symbols_flavour="GNUTLS_" - elif test "x$NSS_ENABLED" == "x1"; then + elif test "x$NSS_ENABLED" = "x1"; then versioned_symbols_flavour="NSS_" - elif test "x$POLARSSL_ENABLED" == "x1"; then + elif test "x$POLARSSL_ENABLED" = "x1"; then versioned_symbols_flavour="POLARSSL_" - elif test "x$CYASSL_ENABLED" == "x1"; then + elif test "x$CYASSL_ENABLED" = "x1"; then versioned_symbols_flavour="CYASSL_" - elif test "x$AXTLS_ENABLED" == "x1"; then + elif test "x$AXTLS_ENABLED" = "x1"; then versioned_symbols_flavour="AXTLS_" - elif test "x$WINSSL_ENABLED" == "x1"; then + elif test "x$WINSSL_ENABLED" = "x1"; then versioned_symbols_flavour="WINSSL_" - elif test "x$DARWINSSL_ENABLED" == "x1"; then + elif test "x$DARWINSSL_ENABLED" = "x1"; then versioned_symbols_flavour="DARWINSSL_" else versioned_symbols_flavour="" diff --git a/lib/vtls/curl_schannel.c b/lib/vtls/curl_schannel.c index 33c9aac8e..701fa556c 100644 --- a/lib/vtls/curl_schannel.c +++ b/lib/vtls/curl_schannel.c @@ -5,9 +5,9 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2013, Marc Hoersken, <info@marc-hoersken.de> + * Copyright (C) 2012 - 2014, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -156,17 +156,6 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) infof(data, "schannel: disable server certificate revocation checks\n"); } - if(Curl_inet_pton(AF_INET, conn->host.name, &addr) -#ifdef ENABLE_IPV6 - || Curl_inet_pton(AF_INET6, conn->host.name, &addr6) -#endif - ) { - schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; - infof(data, "schannel: using IP address, SNI is being disabled by " - "disabling the servername check against the " - "subject names in server certificates.\n"); - } - if(!data->set.ssl.verifyhost) { schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; infof(data, "schannel: verifyhost setting prevents Schannel from " @@ -228,6 +217,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) } } + /* Warn if SNI is disabled due to use of an IP address */ + if(Curl_inet_pton(AF_INET, conn->host.name, &addr) +#ifdef ENABLE_IPV6 + || Curl_inet_pton(AF_INET6, conn->host.name, &addr6) +#endif + ) { + infof(data, "schannel: using IP address, SNI is not supported by OS.\n"); + } + /* setup output buffer */ InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&outbuf_desc, &outbuf, 1); diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index e0010a702..2a3c06ffa 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -32,7 +32,6 @@ void config_init(struct OperationConfig* config) config->errors = stderr; /* default errors to stderr */ config->postfieldsize = -1; - config->showerror = -1; /* will show errors */ config->use_httpget = FALSE; config->create_dirs = FALSE; config->maxredirs = DEFAULT_MAXREDIRS; @@ -154,19 +153,13 @@ void config_free(struct OperationConfig *config) { struct OperationConfig *last = config; - /* Find the last config structure */ - while(last->next) - last = last->next; - /* Free each of the structures in reverse order */ - do { + while(last) { struct OperationConfig *prev = last->prev; - if(prev) - last->easy = NULL; free_config_fields(last); free(last); last = prev; - } while(last); + } } diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 69c2c2ee9..e88b97896 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -70,9 +70,6 @@ struct OperationConfig { char *dns_interface; /* interface name */ char *dns_ipv4_addr; /* dot notation */ char *dns_ipv6_addr; /* dot notation */ - int showerror; /* -1 == unset, default => show errors - 0 => -s is used to NOT show errors - 1 => -S has been used to show errors */ char *userpwd; char *login_options; char *tls_username; @@ -222,8 +219,12 @@ struct OperationConfig { struct GlobalConfig { CURL *easy; /* Once we have one, we keep it here */ + int showerror; /* -1 == unset, default => show errors + 0 => -s is used to NOT show errors + 1 => -S has been used to show errors */ struct OperationConfig *first; + struct OperationConfig *current; struct OperationConfig *last; /* Always last in the struct */ }; diff --git a/src/tool_getparam.c b/src/tool_getparam.c index f6b889804..5e907416b 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -373,6 +373,7 @@ ParameterError getparameter(char *flag, /* f or -long-flag */ char *nextarg, /* NULL if unset */ bool *usedarg, /* set to TRUE if the arg has been used */ + struct GlobalConfig *global, struct OperationConfig *config) { char letter; @@ -1433,7 +1434,7 @@ ParameterError getparameter(char *flag, /* f or -long-flag */ config->insecure_ok = toggle; break; case 'K': /* parse config file */ - if(parseconfig(nextarg, config)) + if(parseconfig(nextarg, global)) warnf(config, "error trying read config from the '%s' file\n", nextarg); break; @@ -1618,15 +1619,15 @@ ParameterError getparameter(char *flag, /* f or -long-flag */ config->mute = config->noprogress = TRUE; else config->mute = config->noprogress = FALSE; - if(config->showerror < 0) + if(global->showerror < 0) /* if still on the default value, set showerror to the reverse of toggle. This is to allow -S and -s to be used in an independent order but still have the same effect. */ - config->showerror = (!toggle)?TRUE:FALSE; /* toggle off */ + global->showerror = (!toggle)?TRUE:FALSE; /* toggle off */ break; case 'S': /* show errors */ - config->showerror = toggle?1:0; /* toggle on if used with -s */ + global->showerror = toggle?1:0; /* toggle on if used with -s */ break; case 't': /* Telnet options */ @@ -1798,32 +1799,36 @@ ParameterError getparameter(char *flag, /* f or -long-flag */ return PARAM_OK; } -ParameterError parse_args(struct OperationConfig *config, int argc, +ParameterError parse_args(struct GlobalConfig *config, int argc, argv_item_t argv[]) { int i; bool stillflags; char *orig_opt; ParameterError result = PARAM_OK; + struct OperationConfig *operation = config->first; for(i = 1, stillflags = TRUE; i < argc && !result; i++) { orig_opt = argv[i]; if(curlx_strequal(":", argv[i]) && - (config->url_list && config->url_list->url)) { + (operation->url_list && operation->url_list->url)) { /* Allocate the next config */ - config->next = malloc(sizeof(struct OperationConfig)); - if(config->next) { + operation->next = malloc(sizeof(struct OperationConfig)); + if(operation->next) { /* Initialise the newly created config */ - config_init(config->next); + config_init(operation->next); /* Copy the easy handle */ - config->next->easy = config->easy; + operation->next->easy = config->easy; + + /* Update the last operation pointer */ + config->last = operation->next; /* Move onto the new config */ - config->next->prev = config; - config = config->next; + operation->next->prev = operation; + operation = operation->next; /* Reset the flag indicator */ stillflags = TRUE; @@ -1843,7 +1848,7 @@ ParameterError parse_args(struct OperationConfig *config, int argc, else { nextarg = (i < (argc - 1)) ? argv[i + 1] : NULL; - result = getparameter(flag, nextarg, &passarg, config); + result = getparameter(flag, nextarg, &passarg, config, operation); if(!result && passarg) i++; /* we're supposed to skip this */ } @@ -1852,7 +1857,8 @@ ParameterError parse_args(struct OperationConfig *config, int argc, bool used; /* Just add the URL please */ - result = getparameter((char *)"--url", argv[i], &used, config); + result = getparameter((char *)"--url", argv[i], &used, config, + operation); } } @@ -1863,9 +1869,9 @@ ParameterError parse_args(struct OperationConfig *config, int argc, const char *reason = param2text(result); if(!curlx_strequal(":", orig_opt)) - helpf(config->errors, "option %s: %s\n", orig_opt, reason); + helpf(operation->errors, "option %s: %s\n", orig_opt, reason); else - helpf(config->errors, "%s\n", reason); + helpf(operation->errors, "%s\n", reason); } return result; diff --git a/src/tool_getparam.h b/src/tool_getparam.h index 159319072..9ecd35401 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -41,12 +41,12 @@ typedef enum { PARAM_LAST } ParameterError; +struct GlobalConfig; struct OperationConfig; -ParameterError getparameter(char *flag, - char *nextarg, - bool *usedarg, - struct OperationConfig *config); +ParameterError getparameter(char *flag, char *nextarg, bool *usedarg, + struct GlobalConfig *global, + struct OperationConfig *operation); #ifdef UNITTESTS void parse_cert_parameter(const char *cert_parameter, @@ -54,7 +54,7 @@ void parse_cert_parameter(const char *cert_parameter, char **passphrase); #endif -ParameterError parse_args(struct OperationConfig *config, int argc, +ParameterError parse_args(struct GlobalConfig *config, int argc, argv_item_t argv[]); #endif /* HEADER_CURL_TOOL_GETPARAM_H */ diff --git a/src/tool_help.c b/src/tool_help.c index d00e6dcc2..3f93b7736 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -107,8 +107,6 @@ static const char *const helptext[] = { " -0, --http1.0 Use HTTP 1.0 (H)", " --http1.1 Use HTTP 1.1 (H)", " --http2 Use HTTP 2 (H)", - " --no-npn Disable the NPN TLS extension", - " --no-alpn Disable the ALPN TLS extension", " --ignore-content-length Ignore the HTTP Content-Length header", " -i, --include Include protocol headers in the output (H/F)", " -k, --insecure Allow connections to SSL sites without certs (H)", @@ -140,8 +138,10 @@ static const char *const helptext[] = { " -n, --netrc Must read .netrc for user name and password", " --netrc-optional Use either .netrc or URL; overrides -n", " --netrc-file FILE Set up the netrc filename to use", + " --no-alpn Disable the ALPN TLS extension (H)", " -N, --no-buffer Disable buffering of the output stream", " --no-keepalive Disable keepalive use on the connection", + " --no-npn Disable the NPN TLS extension (H)", " --no-sessionid Disable SSL session-ID reusing (SSL)", " --noproxy List of hosts which do not use proxy", " --ntlm Use HTTP NTLM authentication (H)", diff --git a/src/tool_hugehelp.h b/src/tool_hugehelp.h index a7ae784e4..442579e5d 100644 --- a/src/tool_hugehelp.h +++ b/src/tool_hugehelp.h @@ -23,10 +23,6 @@ ***************************************************************************/ #include "tool_setup.h" -#ifdef USE_MANUAL void hugehelp(void); -#else -#define hugehelp() Curl_nop_stmt -#endif #endif /* HEADER_CURL_TOOL_HUGEHELP_H */ diff --git a/src/tool_main.c b/src/tool_main.c index bceb65c7b..06c28395f 100644 --- a/src/tool_main.c +++ b/src/tool_main.c @@ -130,8 +130,11 @@ static CURLcode main_init(struct GlobalConfig *config) _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; #endif + /* Initialise the global config */ + config->showerror = -1; /* Will show errors */ + /* Allocate the initial operate config */ - config->first = malloc(sizeof(struct OperationConfig)); + config->first = config->last = malloc(sizeof(struct OperationConfig)); if(config->first) { /* Perform the libcurl initialization */ result = curl_global_init(CURL_GLOBAL_DEFAULT); @@ -140,8 +143,17 @@ static CURLcode main_init(struct GlobalConfig *config) result = get_libcurl_info(); if(!result) { - /* Initialise the config */ - config_init(config->first); + /* Get a curl handle to use for all forthcoming curl transfers */ + config->easy = curl_easy_init(); + if(config->easy) { + /* Initialise the config */ + config_init(config->first); + config->first->easy = config->easy; + } + else { + helpf(stderr, "error initializing curl easy handle\n"); + result = CURLE_FAILED_INIT; + } } else helpf(stderr, "error retrieving curl library information\n"); @@ -163,13 +175,17 @@ static CURLcode main_init(struct GlobalConfig *config) */ static void main_free(struct GlobalConfig *config) { + /* Cleanup the easy handle */ + curl_easy_cleanup(config->easy); + config->easy = NULL; + /* Main cleanup */ curl_global_cleanup(); convert_cleanup(); metalink_cleanup(); /* Free the config structures */ - config_free(config->first); + config_free(config->last); config->first = NULL; config->last = NULL; } @@ -197,10 +213,10 @@ int main(int argc, char *argv[]) result = main_init(&global); if(!result) { /* Start our curl operation */ - result = operate(global.first, argc, argv); + result = operate(&global, argc, argv); #ifdef __SYMBIAN32__ - if(global.first->showerror) + if(global.showerror) tool_pressanykey(); #endif diff --git a/src/tool_operate.c b/src/tool_operate.c index 783d86cce..faecd8f46 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -187,24 +187,8 @@ static curl_off_t VmsSpecialSize(const char * name, } #endif /* __VMS */ -static CURLcode operate_init(struct OperationConfig *config) -{ - /* Get a curl handle to use for all forthcoming curl transfers */ - config->easy = curl_easy_init(); - if(!config->easy) { - helpf(config->errors, "error initializing curl easy handle\n"); - return CURLE_FAILED_INIT; - } - - /* Setup proper locale from environment */ -#ifdef HAVE_SETLOCALE - setlocale(LC_ALL, ""); -#endif - - return CURLE_OK; -} - -static CURLcode operate_do(struct OperationConfig *config) +static CURLcode operate_do(struct GlobalConfig *global, + struct OperationConfig *config) { char errorbuffer[CURL_ERROR_SIZE]; struct ProgressData progressbar; @@ -425,7 +409,7 @@ static CURLcode operate_do(struct OperationConfig *config) if(!config->globoff && infiles) { /* Unless explicitly shut off */ res = glob_url(&inglob, infiles, &infilenum, - config->showerror?config->errors:NULL); + global->showerror?config->errors:NULL); if(res) { Curl_safefree(outfiles); break; @@ -476,7 +460,7 @@ static CURLcode operate_do(struct OperationConfig *config) /* Unless explicitly shut off, we expand '{...}' and '[...]' expressions and return total number of URLs in pattern set */ res = glob_url(&urls, urlnode->url, &urlnum, - config->showerror?config->errors:NULL); + global->showerror?config->errors:NULL); if(res) { Curl_safefree(uploadfile); break; @@ -1571,12 +1555,12 @@ static CURLcode operate_do(struct OperationConfig *config) #ifdef __VMS if(is_vms_shell()) { /* VMS DCL shell behavior */ - if(!config->showerror) + if(!global->showerror) vms_show = VMSSTS_HIDE; } else #endif - if(res && config->showerror) { + if(res && global->showerror) { fprintf(config->errors, "curl: (%d) %s\n", res, (errorbuffer[0]) ? errorbuffer : curl_easy_strerror((CURLcode)res)); if(res == CURLE_SSL_CACERT) @@ -1789,36 +1773,28 @@ static CURLcode operate_do(struct OperationConfig *config) dumpeasysrc(config); #endif - return (CURLcode)res; -} - -static void operate_free(struct OperationConfig *config) -{ - if(config->easy) { - curl_easy_cleanup(config->easy); - config->easy = NULL; - } - /* Release metalink related resources here */ clean_metalink(config); + + return (CURLcode)res; } -CURLcode operate(struct OperationConfig *config, int argc, argv_item_t argv[]) +CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[]) { CURLcode result = CURLE_OK; - /* Initialize the easy interface */ - result = operate_init(config); - if(result) - return result; + /* Setup proper locale from environment */ +#ifdef HAVE_SETLOCALE + setlocale(LC_ALL, ""); +#endif /* Parse .curlrc if necessary */ if((argc == 1) || (!curlx_strequal(argv[1], "-q"))) { parseconfig(NULL, config); /* ignore possible failure */ /* If we had no arguments then make sure a url was specified in .curlrc */ - if((argc < 2) && (!config->url_list)) { - helpf(config->errors, NULL); + if((argc < 2) && (!config->first->url_list)) { + helpf(config->first->errors, NULL); result = CURLE_FAILED_INIT; } } @@ -1847,7 +1823,7 @@ CURLcode operate(struct OperationConfig *config, int argc, argv_item_t argv[]) /* Perform the main operations */ else { size_t count = 0; - struct OperationConfig *operation = config; + struct OperationConfig *operation = config->first; /* Get the required aguments for each operation */ while(!result && operation) { @@ -1856,20 +1832,17 @@ CURLcode operate(struct OperationConfig *config, int argc, argv_item_t argv[]) operation = operation->next; } - /* Reset the operation pointer */ - operation = config; + /* Set the current operation pointer */ + config->current = config->first; /* Perform each operation */ - while(!result && operation) { - result = operate_do(operation); + while(!result && config->current) { + result = operate_do(config, config->current); - operation = operation->next; + config->current = config->current->next; } } } - /* Perform the cleanup */ - operate_free(config); - return result; } diff --git a/src/tool_operate.h b/src/tool_operate.h index 86283f7a4..1d5c1a96f 100644 --- a/src/tool_operate.h +++ b/src/tool_operate.h @@ -23,7 +23,7 @@ ***************************************************************************/ #include "tool_setup.h" -CURLcode operate(struct OperationConfig *config, int argc, argv_item_t argv[]); +CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[]); #endif /* HEADER_CURL_TOOL_OPERATE_H */ diff --git a/src/tool_parsecfg.c b/src/tool_parsecfg.c index 69bacbf18..da10c878f 100644 --- a/src/tool_parsecfg.c +++ b/src/tool_parsecfg.c @@ -44,7 +44,7 @@ static const char *unslashquote(const char *line, char *param); static char *my_get_line(FILE *fp); /* return 0 on everything-is-fine, and non-zero otherwise */ -int parseconfig(const char *filename, struct OperationConfig *config) +int parseconfig(const char *filename, struct GlobalConfig *global) { int res; FILE *file; @@ -52,6 +52,7 @@ int parseconfig(const char *filename, struct OperationConfig *config) bool usedarg; char *home; int rc = 0; + struct OperationConfig *operation = global->first; if(!filename || !*filename) { /* NULL or no file name attempts to load .curlrc from the homedir! */ @@ -201,8 +202,8 @@ int parseconfig(const char *filename, struct OperationConfig *config) case '#': /* comment */ break; default: - warnf(config, "%s:%d: warning: '%s' uses unquoted white space in the" - " line that may cause side-effects!\n", + warnf(operation, "%s:%d: warning: '%s' uses unquoted white space in" + " the line that may cause side-effects!\n", filename, lineno, option); } } @@ -218,7 +219,7 @@ int parseconfig(const char *filename, struct OperationConfig *config) #ifdef DEBUG_CONFIG fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)")); #endif - res = getparameter(option, param, &usedarg, config); + res = getparameter(option, param, &usedarg, global, operation); if(param && *param && !usedarg) /* we passed in a parameter that wasn't used! */ @@ -234,7 +235,7 @@ int parseconfig(const char *filename, struct OperationConfig *config) res != PARAM_VERSION_INFO_REQUESTED && res != PARAM_ENGINES_REQUESTED) { const char *reason = param2text(res); - warnf(config, "%s:%d: warning: '%s' %s\n", + warnf(operation, "%s:%d: warning: '%s' %s\n", filename, lineno, option, reason); } } diff --git a/src/tool_parsecfg.h b/src/tool_parsecfg.h index af9f50bf1..c3b19d544 100644 --- a/src/tool_parsecfg.h +++ b/src/tool_parsecfg.h @@ -23,7 +23,7 @@ ***************************************************************************/ #include "tool_setup.h" -int parseconfig(const char *filename, struct OperationConfig *config); +int parseconfig(const char *filename, struct GlobalConfig *config); #endif /* HEADER_CURL_TOOL_PARSECFG_H */ |