16 #include <zypp-core/fs/PathInfo.h> 17 #include <zypp-core/Pathname.h> 18 #include <zypp-core/base/LogTools.h> 19 #include <zypp-core/base/String.h> 20 #include <zypp-core/base/StringV.h> 21 #include <zypp-curl/ProxyInfo> 22 #include <zypp-curl/auth/CurlAuthData> 23 #include <zypp-media/MediaException> 27 #define TRANSFER_TIMEOUT_MAX 60 * 60 38 static const long ret = [](){
39 const char * env = getenv(
"ZYPP_MEDIA_CURL_DEBUG");
40 return env && *env ? str::strtonum<ulong>( env ) : 0;
49 if (
const char * envp = getenv(
"ZYPP_MEDIA_CURL_IPRESOLVE" ) ) {
50 WAR <<
"env set: $ZYPP_MEDIA_CURL_IPRESOLVE='" << envp <<
"'" << std::endl;
51 if ( strcmp( envp,
"4" ) == 0 ) ret = 4;
52 else if ( strcmp( envp,
"6" ) == 0 ) ret = 6;
68 MIL <<
"global_init libcurl: build version: (" << LIBCURL_VERSION <<
"), runtime version: (" << curl_version_info(CURLVERSION_NOW)->version <<
") " << endl;
69 if ( curl_global_init( CURL_GLOBAL_ALL ) != 0 )
70 WAR <<
"curl global init failed" << std::endl;
76 auto curlV = curl_version_info ( CURLVERSION_NOW );
77 return curlV->version_num;
80 int log_curl( CURL * curl, curl_infotype info,
char * ptr,
size_t len,
void * max_lvl )
82 if ( max_lvl ==
nullptr )
85 long maxlvl = *((
long *)max_lvl);
86 const char * pfx =
"";
87 bool isContent =
true;
90 case CURLINFO_TEXT:
if ( maxlvl < 1 )
return 0; pfx =
"*";
break;
91 case CURLINFO_HEADER_IN:
if ( maxlvl < 2 )
return 0; pfx =
"<";
break;
92 case CURLINFO_HEADER_OUT:
if ( maxlvl < 2 )
return 0; pfx =
">";
break;
93 case CURLINFO_SSL_DATA_IN:
if ( maxlvl < 3 )
return 0; isContent =
false; pfx =
"<[SSL]";
break;
94 case CURLINFO_SSL_DATA_OUT:
if ( maxlvl < 3 )
return 0; isContent =
false; pfx =
">[SSL]";
break;
95 case CURLINFO_DATA_IN:
if ( maxlvl < 3 )
return 0; isContent =
false; pfx =
"<[DTA]";
break;
96 case CURLINFO_DATA_OUT:
if ( maxlvl < 3 )
return 0; isContent =
false; pfx =
">[DTA]";
break;
105 std::vector<std::string_view> lines;
106 strv::split( std::string_view( ptr, len ),
"\n", [&lines]( std::string_view line,
unsigned,
bool last ) {
109 lines.push_back( line );
111 for (
const auto & line : lines ) {
114 if ( pos == std::string::npos )
116 DBG << curl <<
" " << pfx <<
" " << line.substr( 0, pos ) <<
" <credentials removed>" << endl;
119 DBG << curl <<
" " << pfx <<
" " << line << endl;
123 DBG << curl <<
" " << pfx <<
" " << len <<
" byte" << endl;
125 hexdumpOn(
DBG << curl <<
" " << pfx <<
" ", ptr, len );
133 INT <<
"Got a NULL curl handle" << endl;
137 curl_easy_setopt( curl, CURLOPT_VERBOSE, 1L );
138 curl_easy_setopt( curl, CURLOPT_DEBUGFUNCTION,
log_curl );
147 char * lstart = ptr, * lend = ptr;
149 size_t max = size * nmemb;
150 while (pos + 1 < max)
153 for (lstart = lend; *lend !=
'\n' && pos < max; ++lend, ++pos);
156 if ( strncasecmp( lstart,
"Location:", 9 ) == 0 )
158 std::string line { lstart, *(lend-1)==
'\r' ? lend-1 : lend };
159 DBG <<
"redirecting to " << line << std::endl;
161 *
reinterpret_cast<std::string *
>( userdata ) = line;
187 if( ! param.empty() )
189 long num = str::strtonum<long>(param);
196 if ( ! param.empty() )
200 if ( ! param.empty() )
215 const std::string & verify { url.
getQueryParam(
"ssl_verify") };
216 if( verify.empty() || verify ==
"yes" )
221 else if ( verify ==
"no" )
228 std::vector<std::string> flags;
229 str::split( verify, std::back_inserter(flags),
"," );
230 for (
const auto & flag : flags )
232 if ( flag ==
"host" )
234 else if ( flag ==
"peer" )
243 if( ! ca_path.empty() )
253 if( ! client_cert.empty() )
255 if( !
PathInfo(client_cert).
isFile() || ! client_cert.absolute() )
263 if( ! client_key.empty() )
273 if ( ! param.empty() )
284 const std::string & proxyport { url.
getQueryParam(
"proxyport" ) };
285 if ( ! proxyport.empty() ) {
296 if ( ! param.empty() )
313 DBG <<
"Rethrowing as MediaUnauthorizedException.";
321 const std::string & param { url.
getQueryParam(
"head_requests") };
322 if( ! param.empty() && param ==
"no" )
354 const char char_r,
const std::string & escaped_r ) {
356 pos != std::string::npos; pos = str_r.find( char_r, pos ) ) {
357 str_r.replace( pos, 1, escaped_r );
367 char * tmp = curl_unescape( text_r.c_str(), 0 );
368 std::string ret( tmp );
402 using namespace std::literals::string_literals;
403 for (
const std::string ¶m : {
"proxy"s,
"proxyport"s,
"proxyuser"s,
"proxypass"s,
"ssl_capath"s,
"ssl_verify"s } )
405 const std::string & value( template_r.
getQueryParam( param ) );
406 if ( ! value.empty() )
412 CurlPollHelper::CurlPollHelper(
CurlPoll &p) : _parent(p) {
414 curl_multi_setopt(
_parent.
_multi, CURLMOPT_SOCKETDATA,
this );
416 curl_multi_setopt(
_parent.
_multi, CURLMOPT_TIMERDATA,
this );
420 curl_multi_setopt(
_parent.
_multi, CURLMOPT_SOCKETFUNCTION,
nullptr );
421 curl_multi_setopt(
_parent.
_multi, CURLMOPT_SOCKETDATA,
nullptr );
422 curl_multi_setopt(
_parent.
_multi, CURLMOPT_TIMERFUNCTION,
nullptr );
423 curl_multi_setopt(
_parent.
_multi, CURLMOPT_TIMERDATA,
nullptr );
427 auto it = std::find_if( userp->
socks.begin(), userp->
socks.end(), [&](
const GPollFD &fd){
return fd.fd == s; });
429 if ( what == CURL_POLL_REMOVE ) {
430 if ( it == userp->
socks.end() ) {
431 WAR <<
"Ignoring unknown socket in static_socketcb" << std::endl;
434 userp->
socks.erase(it);
436 }
else if ( what == CURL_POLL_IN ) {
437 events = G_IO_IN | G_IO_HUP | G_IO_ERR;
438 }
else if ( what == CURL_POLL_OUT ) {
439 events = G_IO_OUT | G_IO_ERR;
440 }
else if ( what == CURL_POLL_INOUT ) {
441 events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
444 if ( it != userp->
socks.end() ) {
448 userp->
socks.push_back(
471 for (
int sock = first; sock < actionsFds.size(); sock++ ) {
472 const auto &waitFd = actionsFds[sock];
473 if ( waitFd.revents == 0 )
477 if ( (waitFd.revents & G_IO_HUP) == G_IO_HUP
478 || (waitFd.revents & G_IO_IN) == G_IO_IN ) {
479 ev = CURL_CSELECT_IN;
481 if ( (waitFd.revents & G_IO_OUT) == G_IO_OUT ) {
482 ev |= CURL_CSELECT_OUT;
484 if ( (waitFd.revents & G_IO_ERR) == G_IO_ERR ) {
485 ev |= CURL_CSELECT_ERR;
489 CURLMcode mcode = curl_multi_socket_action( _parent._multi, waitFd.fd, ev, &runn );
490 if (mcode != CURLM_OK)
499 return curl_multi_socket_action( _parent._multi, CURL_SOCKET_TIMEOUT, 0, &handles );
514 #if CURLVERSION_AT_LEAST(7,19,4) 515 #if CURLVERSION_AT_LEAST(7,85,0) 518 return curl_easy_setopt ( curl, CURLOPT_REDIR_PROTOCOLS_STR,
"https" );
520 return curl_easy_setopt ( curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS );
523 return curl_easy_setopt ( curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS );
525 #endif // #if CURLVERSION_AT_LEAST(7,19,4) std::string getScheme() const
Returns the scheme name of the URL.
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
void globalInitCurlOnce()
size_t log_redirects_curl(char *ptr, size_t size, size_t nmemb, void *userdata)
void setQueryParam(const std::string ¶m, const std::string &value)
Set or add value for the specified query parameter.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
zypp::Url propagateQueryParams(zypp::Url url_r, const zypp::Url &template_r)
Flag to request encoded string(s).
static int socketcb(CURL *easy, curl_socket_t s, int what, CurlPollHelper *userp, void *sockp)
Url clearQueryString(const Url &url)
static const ViewOption WITH_SCHEME
Option to include scheme name in the URL string.
static const ViewOption WITH_HOST
Option to include hostname in the URL string.
void setPathParams(const std::string ¶ms)
Set the path parameters.
std::string curlEscapedPath(std::string path_r)
void setUsername(const std::string &user, EEncoding eflag=zypp::url::E_DECODED)
Set the username in the URL authority.
void setFragment(const std::string &fragment, EEncoding eflag=zypp::url::E_DECODED)
Set the fragment string in the URL.
CURLMcode handleSocketActions(const std::vector< GPollFD > &actionsFds, int first=0)
int ZYPP_MEDIA_CURL_IPRESOLVE()
4/6 to force IPv4/v6
void curlEscape(std::string &str_r, const char char_r, const std::string &escaped_r)
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \, const Trim trim_r=NO_TRIM)
Split line_r into words.
std::string getQueryParam(const std::string ¶m, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
std::vector< GPollFD > socks
Provides API related macros.
const long & ZYPP_MEDIA_CURL_DEBUG()
const long& for setting CURLOPT_DEBUGDATA Returns a reference to a static variable, so it's safe to pass ...
void fillSettingsFromUrl(const Url &url, media::TransferSettings &s)
Fills the settings structure using options passed on the url for example ?timeout=x&proxy=foo.
std::ostream & hexdumpOn(std::ostream &outs, const unsigned char *ptr, size_t size)
hexdump data on stream
std::string rtrim(const std::string &s)
static int timercb(CURLM *, long timeout_ms, CurlPollHelper *thatPtr)
std::string curlUnEscape(std::string text_r)
void setupZYPP_MEDIA_CURL_DEBUG(CURL *curl)
Setup CURLOPT_VERBOSE and CURLOPT_DEBUGFUNCTION according to env::ZYPP_MEDIA_CURL_DEBUG.
Wrapper class for ::stat/::lstat.
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
std::optional< long > timeout_ms
#define TRANSFER_TIMEOUT_MAX
#define EXPLICITLY_NO_PROXY
static const ViewOption WITH_PORT
Option to include port number in the URL string.
Easy-to use interface to the ZYPP dependency resolver.
int log_curl(CURL *curl, curl_infotype info, char *ptr, size_t len, void *max_lvl)
CURLcode setCurlRedirProtocols(CURL *curl)
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
void delQueryParam(const std::string ¶m)
remove the specified query parameter.
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
const std::string & msg() const
Return the message string provided to the ctor.