Imported Upstream version 17.25.4
[platform/upstream/libzypp.git] / zypp / media / CurlHelper.cc
1 #include "CurlHelper.h"
2
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>
11 #include <list>
12
13 using std::endl;
14 using namespace zypp;
15
16 namespace internal
17 {
18
19 void globalInitCurlOnce()
20 {
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;
25   } (), true );
26 }
27
28 int log_curl(CURL *curl, curl_infotype info,
29   char *ptr, size_t len, void *max_lvl)
30 {
31   if ( max_lvl == nullptr )
32     return 0;
33
34   long maxlvl = *((long *)max_lvl);
35
36   char pfx = ' ';
37   switch( info )
38   {
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;
42     default:
43       return 0;
44   }
45
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 )
49   {
50     if ( str::startsWith( line, "Authorization:" ) ) {
51       std::string::size_type pos { line.find( " ", 15 ) }; // Authorization: <type> <credentials>
52       if ( pos == std::string::npos )
53         pos = 15;
54       DBG << pfx << " " << line.substr( 0, pos ) << " <credentials removed>" << std::endl;
55     }
56     else
57       DBG << pfx << " " << line << std::endl;
58   }
59   return 0;
60 }
61
62 size_t log_redirects_curl( char *ptr, size_t size, size_t nmemb, void *userdata)
63 {
64   // INT << "got header: " << string(ptr, ptr + size*nmemb) << endl;
65
66   char * lstart = ptr, * lend = ptr;
67   size_t pos = 0;
68   size_t max = size * nmemb;
69   while (pos + 1 < max)
70   {
71     // get line
72     for (lstart = lend; *lend != '\n' && pos < max; ++lend, ++pos);
73
74     // look for "Location"
75     if ( lstart[0] == 'L'
76          && lstart[1] == 'o'
77          && lstart[2] == 'c'
78          && lstart[3] == 'a'
79          && lstart[4] == 't'
80          && lstart[5] == 'i'
81          && lstart[6] == 'o'
82          && lstart[7] == 'n'
83          && lstart[8] == ':' )
84     {
85       std::string line { lstart, *(lend-1)=='\r' ? lend-1 : lend };
86       DBG << "redirecting to " << line << std::endl;
87       if ( userdata ) {
88         *reinterpret_cast<std::string *>( userdata ) = line;
89       }
90       return max;
91     }
92
93     // continue with the next line
94     if (pos + 1 < max)
95     {
96       ++lend;
97       ++pos;
98     }
99     else
100       break;
101   }
102
103   return max;
104 }
105
106 /**
107  * Fills the settings structure using options passed on the url
108  * for example ?timeout=x&proxy=foo
109  */
110 void fillSettingsFromUrl( const Url &url, media::TransferSettings &s )
111 {
112   {
113     const std::string & param { url.getQueryParam("timeout") };
114     if( ! param.empty() )
115     {
116       long num = str::strtonum<long>(param);
117       if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX )
118         s.setTimeout( num );
119     }
120   }
121   {
122     std::string param { url.getUsername() };
123     if ( ! param.empty() )
124     {
125       s.setUsername( std::move(param) );
126       param = url.getPassword();
127       if ( ! param.empty() )
128         s.setPassword( std::move(param) );
129     }
130     else
131     {
132       // if there is no username, set anonymous auth
133       if ( ( url.getScheme() == "ftp" || url.getScheme() == "tftp" ) && s.username().empty() )
134         s.setAnonymousAuth();
135     }
136   }
137   if ( url.getScheme() == "https" )
138   {
139     s.setVerifyPeerEnabled( false );
140     s.setVerifyHostEnabled( false );
141
142     const std::string & verify { url.getQueryParam("ssl_verify") };
143     if( verify.empty() || verify == "yes" )
144     {
145       s.setVerifyPeerEnabled( true );
146       s.setVerifyHostEnabled( true );
147     }
148     else if ( verify == "no" )
149     {
150       s.setVerifyPeerEnabled( false );
151       s.setVerifyHostEnabled( false );
152     }
153     else
154     {
155       std::vector<std::string> flags;
156       str::split( verify, std::back_inserter(flags), "," );
157       for ( const auto & flag : flags )
158       {
159         if ( flag == "host" )
160           s.setVerifyHostEnabled( true );
161         else if ( flag == "peer" )
162           s.setVerifyPeerEnabled( true );
163         else
164           ZYPP_THROW( media::MediaBadUrlException(url, "Unknown ssl_verify flag "+flag) );
165       }
166     }
167   }
168   {
169     Pathname ca_path { url.getQueryParam("ssl_capath") };
170     if( ! ca_path.empty() )
171     {
172       if( ! PathInfo(ca_path).isDir() || ! ca_path.absolute() )
173         ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_capath path"));
174       else
175         s.setCertificateAuthoritiesPath( std::move(ca_path) );
176     }
177   }
178   {
179     Pathname client_cert { url.getQueryParam("ssl_clientcert") };
180     if( ! client_cert.empty() )
181     {
182       if( ! PathInfo(client_cert).isFile() || ! client_cert.absolute() )
183         ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_clientcert file"));
184       else
185         s.setClientCertificatePath( std::move(client_cert) );
186     }
187   }
188   {
189     Pathname client_key { url.getQueryParam("ssl_clientkey") };
190     if( ! client_key.empty() )
191     {
192       if( ! PathInfo(client_key).isFile() || ! client_key.absolute() )
193         ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_clientkey file"));
194       else
195         s.setClientKeyPath( std::move(client_key) );
196     }
197   }
198   {
199     std::string param { url.getQueryParam( "proxy" ) };
200     if ( ! param.empty() )
201     {
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);
209       }
210       else {
211         const std::string & proxyport { url.getQueryParam( "proxyport" ) };
212         if ( ! proxyport.empty() ) {
213           param += ":";
214           param += proxyport;
215         }
216         s.setProxy( std::move(param) );
217         s.setProxyEnabled( true );
218       }
219     }
220   }
221   {
222     std::string param { url.getQueryParam( "proxyuser" ) };
223     if ( ! param.empty() )
224     {
225       s.setProxyUsername( std::move(param) );
226       s.setProxyPassword( url.getQueryParam( "proxypass" ) );
227     }
228   }
229   {
230     // HTTP authentication type
231     std::string param { url.getQueryParam("auth") };
232     if ( ! param.empty() && (url.getScheme() == "http" || url.getScheme() == "https") )
233     {
234       try
235       {
236         media::CurlAuthData::auth_type_str2long (param );       // check if we know it
237       }
238       catch ( const media::MediaException & ex_r )
239       {
240         DBG << "Rethrowing as MediaUnauthorizedException.";
241         ZYPP_THROW(media::MediaUnauthorizedException(url, ex_r.msg(), "", ""));
242       }
243       s.setAuthType( std::move(param) );
244     }
245   }
246   {
247     // workarounds
248     const std::string & param { url.getQueryParam("head_requests") };
249     if( ! param.empty() && param == "no" )
250       s.setHeadRequestsAllowed( false );
251   }
252 }
253
254 /**
255  * Reads the system proxy configuration and fills the settings
256  * structure proxy information
257  */
258 void fillSettingsSystemProxy( const Url& url, media::TransferSettings &s )
259 {
260   media::ProxyInfo proxy_info;
261   if ( proxy_info.useProxyFor( url ) )
262   {
263     // We must extract any 'user:pass' from the proxy url
264     // otherwise they won't make it into curl (.curlrc wins).
265     try {
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() )
270       {
271         s.setProxyUsername( u.getUsername( url::E_ENCODED ) );
272         s.setProxyPassword( u.getPassword( url::E_ENCODED ) );
273       }
274       s.setProxyEnabled( true );
275     }
276     catch (...) {}      // no proxy if URL is malformed
277   }
278 }
279
280
281 int env::getZYPP_MEDIA_CURL_IPRESOLVE()
282 {
283   int ret = 0;
284   if ( const char * envp = getenv( "ZYPP_MEDIA_CURL_IPRESOLVE" ) )
285   {
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;
289   }
290   return ret;
291 }
292
293
294 const char * anonymousIdHeader()
295 {
296   // we need to add the release and identifier to the
297   // agent string.
298   // The target could be not initialized, and then this information
299   // is guessed.
300   static const std::string _value(
301     str::trim( str::form(
302       "X-ZYpp-AnonymousId: %s",
303       Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str() ) )
304     );
305   return _value.c_str();
306 }
307
308 const char * distributionFlavorHeader()
309 {
310   // we need to add the release and identifier to the
311   // agent string.
312   // The target could be not initialized, and then this information
313   // is guessed.
314   static const std::string _value(
315     str::trim( str::form(
316       "X-ZYpp-DistributionFlavor: %s",
317       Target::distributionFlavor( Pathname()/*guess root*/ ).c_str() ) )
318     );
319   return _value.c_str();
320 }
321
322 const char * agentString()
323 {
324   // we need to add the release and identifier to the
325   // agent string.
326   // The target could be not initialized, and then this information
327   // is guessed.
328   static const std::string _value(
329     str::form(
330       "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s"
331       , curl_version_info(CURLVERSION_NOW)->version
332       , Target::targetDistribution( Pathname()/*guess root*/ ).c_str()
333       )
334     );
335   return _value.c_str();
336 }
337
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 );
343   }
344 }
345
346 std::string curlEscapedPath( std::string path_r ) {
347   curlEscape( path_r, ' ', "%20" );
348   return path_r;
349 }
350
351 std::string curlUnEscape( std::string text_r ) {
352   char * tmp = curl_unescape( text_r.c_str(), 0 );
353   std::string ret( tmp );
354   curl_free( tmp );
355   return ret;
356 }
357
358 Url clearQueryString(const Url &url)
359 {
360   Url curlUrl (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");
380   return curlUrl;
381 }
382
383 // bsc#933839: propagate proxy settings passed in the repo URL
384 zypp::Url propagateQueryParams( zypp::Url url_r, const zypp::Url & template_r )
385 {
386   for ( std::string param : { "proxy", "proxyport", "proxyuser", "proxypass"} )
387   {
388     const std::string & value( template_r.getQueryParam( param ) );
389     if ( ! value.empty() )
390       url_r.setQueryParam( param, value );
391   }
392   return url_r;
393 }
394
395 }