1 #include "CurlHelper.h"
3 #include <zypp/PathInfo.h>
4 #include <zypp/Pathname.h>
5 #include <zypp/Target.h>
6 #include <zypp/base/Logger.h>
7 #include <zypp/base/String.h>
8 #include <zypp/media/ProxyInfo.h>
9 #include <zypp/media/MediaUserAuth.h>
10 #include <zypp/media/MediaException.h>
19 void globalInitCurlOnce()
21 // function-level static <=> std::call_once
22 static bool once __attribute__ ((__unused__)) = ( [] {
23 if ( curl_global_init( CURL_GLOBAL_ALL ) != 0 )
24 WAR << "curl global init failed" << std::endl;
28 int log_curl(CURL *curl, curl_infotype info,
29 char *ptr, size_t len, void *max_lvl)
31 if ( max_lvl == nullptr )
34 long maxlvl = *((long *)max_lvl);
39 case CURLINFO_TEXT: if ( maxlvl < 1 ) return 0; pfx = '*'; break;
40 case CURLINFO_HEADER_IN: if ( maxlvl < 2 ) return 0; pfx = '<'; break;
41 case CURLINFO_HEADER_OUT: if ( maxlvl < 2 ) return 0; pfx = '>'; break;
46 std::vector<std::string> lines;
47 str::split( std::string(ptr,len), std::back_inserter(lines), "\r\n" );
48 for( const auto & line : lines )
50 if ( str::startsWith( line, "Authorization:" ) ) {
51 std::string::size_type pos { line.find( " ", 15 ) }; // Authorization: <type> <credentials>
52 if ( pos == std::string::npos )
54 DBG << pfx << " " << line.substr( 0, pos ) << " <credentials removed>" << std::endl;
57 DBG << pfx << " " << line << std::endl;
62 size_t log_redirects_curl( char *ptr, size_t size, size_t nmemb, void *userdata)
64 // INT << "got header: " << string(ptr, ptr + size*nmemb) << endl;
66 char * lstart = ptr, * lend = ptr;
68 size_t max = size * nmemb;
72 for (lstart = lend; *lend != '\n' && pos < max; ++lend, ++pos);
74 // look for "Location"
85 std::string line { lstart, *(lend-1)=='\r' ? lend-1 : lend };
86 DBG << "redirecting to " << line << std::endl;
88 *reinterpret_cast<std::string *>( userdata ) = line;
93 // continue with the next line
107 * Fills the settings structure using options passed on the url
108 * for example ?timeout=x&proxy=foo
110 void fillSettingsFromUrl( const Url &url, media::TransferSettings &s )
113 const std::string & param { url.getQueryParam("timeout") };
114 if( ! param.empty() )
116 long num = str::strtonum<long>(param);
117 if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX )
122 std::string param { url.getUsername() };
123 if ( ! param.empty() )
125 s.setUsername( std::move(param) );
126 param = url.getPassword();
127 if ( ! param.empty() )
128 s.setPassword( std::move(param) );
132 // if there is no username, set anonymous auth
133 if ( ( url.getScheme() == "ftp" || url.getScheme() == "tftp" ) && s.username().empty() )
134 s.setAnonymousAuth();
137 if ( url.getScheme() == "https" )
139 s.setVerifyPeerEnabled( false );
140 s.setVerifyHostEnabled( false );
142 const std::string & verify { url.getQueryParam("ssl_verify") };
143 if( verify.empty() || verify == "yes" )
145 s.setVerifyPeerEnabled( true );
146 s.setVerifyHostEnabled( true );
148 else if ( verify == "no" )
150 s.setVerifyPeerEnabled( false );
151 s.setVerifyHostEnabled( false );
155 std::vector<std::string> flags;
156 str::split( verify, std::back_inserter(flags), "," );
157 for ( const auto & flag : flags )
159 if ( flag == "host" )
160 s.setVerifyHostEnabled( true );
161 else if ( flag == "peer" )
162 s.setVerifyPeerEnabled( true );
164 ZYPP_THROW( media::MediaBadUrlException(url, "Unknown ssl_verify flag "+flag) );
169 Pathname ca_path { url.getQueryParam("ssl_capath") };
170 if( ! ca_path.empty() )
172 if( ! PathInfo(ca_path).isDir() || ! ca_path.absolute() )
173 ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_capath path"));
175 s.setCertificateAuthoritiesPath( std::move(ca_path) );
179 Pathname client_cert { url.getQueryParam("ssl_clientcert") };
180 if( ! client_cert.empty() )
182 if( ! PathInfo(client_cert).isFile() || ! client_cert.absolute() )
183 ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_clientcert file"));
185 s.setClientCertificatePath( std::move(client_cert) );
189 Pathname client_key { url.getQueryParam("ssl_clientkey") };
190 if( ! client_key.empty() )
192 if( ! PathInfo(client_key).isFile() || ! client_key.absolute() )
193 ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_clientkey file"));
195 s.setClientKeyPath( std::move(client_key) );
199 std::string param { url.getQueryParam( "proxy" ) };
200 if ( ! param.empty() )
202 if ( param == EXPLICITLY_NO_PROXY ) {
203 // Workaround TransferSettings shortcoming: With an
204 // empty proxy string, code will continue to look for
205 // valid proxy settings. So set proxy to some non-empty
206 // string, to indicate it has been explicitly disabled.
207 s.setProxy(EXPLICITLY_NO_PROXY);
208 s.setProxyEnabled(false);
211 const std::string & proxyport { url.getQueryParam( "proxyport" ) };
212 if ( ! proxyport.empty() ) {
216 s.setProxy( std::move(param) );
217 s.setProxyEnabled( true );
222 std::string param { url.getQueryParam( "proxyuser" ) };
223 if ( ! param.empty() )
225 s.setProxyUsername( std::move(param) );
226 s.setProxyPassword( url.getQueryParam( "proxypass" ) );
230 // HTTP authentication type
231 std::string param { url.getQueryParam("auth") };
232 if ( ! param.empty() && (url.getScheme() == "http" || url.getScheme() == "https") )
236 media::CurlAuthData::auth_type_str2long (param ); // check if we know it
238 catch ( const media::MediaException & ex_r )
240 DBG << "Rethrowing as MediaUnauthorizedException.";
241 ZYPP_THROW(media::MediaUnauthorizedException(url, ex_r.msg(), "", ""));
243 s.setAuthType( std::move(param) );
248 const std::string & param { url.getQueryParam("head_requests") };
249 if( ! param.empty() && param == "no" )
250 s.setHeadRequestsAllowed( false );
255 * Reads the system proxy configuration and fills the settings
256 * structure proxy information
258 void fillSettingsSystemProxy( const Url& url, media::TransferSettings &s )
260 media::ProxyInfo proxy_info;
261 if ( proxy_info.useProxyFor( url ) )
263 // We must extract any 'user:pass' from the proxy url
264 // otherwise they won't make it into curl (.curlrc wins).
266 Url u( proxy_info.proxy( url ) );
267 s.setProxy( u.asString( url::ViewOption::WITH_SCHEME + url::ViewOption::WITH_HOST + url::ViewOption::WITH_PORT ) );
268 // don't overwrite explicit auth settings
269 if ( s.proxyUsername().empty() )
271 s.setProxyUsername( u.getUsername( url::E_ENCODED ) );
272 s.setProxyPassword( u.getPassword( url::E_ENCODED ) );
274 s.setProxyEnabled( true );
276 catch (...) {} // no proxy if URL is malformed
281 int env::getZYPP_MEDIA_CURL_IPRESOLVE()
284 if ( const char * envp = getenv( "ZYPP_MEDIA_CURL_IPRESOLVE" ) )
286 WAR << "env set: $ZYPP_MEDIA_CURL_IPRESOLVE='" << envp << "'" << std::endl;
287 if ( strcmp( envp, "4" ) == 0 ) ret = 4;
288 else if ( strcmp( envp, "6" ) == 0 ) ret = 6;
294 const char * anonymousIdHeader()
296 // we need to add the release and identifier to the
298 // The target could be not initialized, and then this information
300 static const std::string _value(
301 str::trim( str::form(
302 "X-ZYpp-AnonymousId: %s",
303 Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str() ) )
305 return _value.c_str();
308 const char * distributionFlavorHeader()
310 // we need to add the release and identifier to the
312 // The target could be not initialized, and then this information
314 static const std::string _value(
315 str::trim( str::form(
316 "X-ZYpp-DistributionFlavor: %s",
317 Target::distributionFlavor( Pathname()/*guess root*/ ).c_str() ) )
319 return _value.c_str();
322 const char * agentString()
324 // we need to add the release and identifier to the
326 // The target could be not initialized, and then this information
328 static const std::string _value(
330 "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s"
331 , curl_version_info(CURLVERSION_NOW)->version
332 , Target::targetDistribution( Pathname()/*guess root*/ ).c_str()
335 return _value.c_str();
338 void curlEscape( std::string & str_r,
339 const char char_r, const std::string & escaped_r ) {
340 for ( std::string::size_type pos = str_r.find( char_r );
341 pos != std::string::npos; pos = str_r.find( char_r, pos ) ) {
342 str_r.replace( pos, 1, escaped_r );
346 std::string curlEscapedPath( std::string path_r ) {
347 curlEscape( path_r, ' ', "%20" );
351 std::string curlUnEscape( std::string text_r ) {
352 char * tmp = curl_unescape( text_r.c_str(), 0 );
353 std::string ret( tmp );
358 Url clearQueryString(const Url &url)
361 curlUrl.setUsername( "" );
362 curlUrl.setPassword( "" );
363 curlUrl.setPathParams( "" );
364 curlUrl.setFragment( "" );
365 curlUrl.delQueryParam("cookies");
366 curlUrl.delQueryParam("proxy");
367 curlUrl.delQueryParam("proxyport");
368 curlUrl.delQueryParam("proxyuser");
369 curlUrl.delQueryParam("proxypass");
370 curlUrl.delQueryParam("ssl_capath");
371 curlUrl.delQueryParam("ssl_verify");
372 curlUrl.delQueryParam("ssl_clientcert");
373 curlUrl.delQueryParam("timeout");
374 curlUrl.delQueryParam("auth");
375 curlUrl.delQueryParam("username");
376 curlUrl.delQueryParam("password");
377 curlUrl.delQueryParam("mediahandler");
378 curlUrl.delQueryParam("credentials");
379 curlUrl.delQueryParam("head_requests");
383 // bsc#933839: propagate proxy settings passed in the repo URL
384 zypp::Url propagateQueryParams( zypp::Url url_r, const zypp::Url & template_r )
386 for ( std::string param : { "proxy", "proxyport", "proxyuser", "proxypass"} )
388 const std::string & value( template_r.getQueryParam( param ) );
389 if ( ! value.empty() )
390 url_r.setQueryParam( param, value );