libzypp 17.25.7
CurlHelper.cc
Go to the documentation of this file.
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>
11#include <list>
12
13using std::endl;
14using namespace zypp;
15
16namespace internal
17{
18
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
28int 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
62size_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
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() )
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.
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.";
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
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 {
273 }
274 s.setProxyEnabled( true );
275 }
276 catch (...) {} // no proxy if URL is malformed
277 }
278}
279
280
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
294const 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(
302 "X-ZYpp-AnonymousId: %s",
303 Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str() ) )
304 );
305 return _value.c_str();
306}
307
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(
316 "X-ZYpp-DistributionFlavor: %s",
317 Target::distributionFlavor( Pathname()/*guess root*/ ).c_str() ) )
318 );
319 return _value.c_str();
320}
321
322const 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
338void 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
346std::string curlEscapedPath( std::string path_r ) {
347 curlEscape( path_r, ' ', "%20" );
348 return path_r;
349}
350
351std::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
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
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}
#define TRANSFER_TIMEOUT_MAX
Definition: CurlHelper.h:22
#define EXPLICITLY_NO_PROXY
Definition: CurlHelper.h:25
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:392
#define DBG
Definition: Logger.h:78
#define WAR
Definition: Logger.h:80
CURL * curl
Definition: MediaCurl.cc:65
Url url
Definition: MediaCurl.cc:66
Convenience interface for handling authentication data of media user.
Edition * _value
Definition: SysContent.cc:311
const std::string & msg() const
Return the message string provided to the ctor.
Definition: Exception.h:195
const char * c_str() const
Definition: IdStringType.h:105
Url manipulation class.
Definition: Url.h:92
std::string getScheme() const
Returns the scheme name of the URL.
Definition: Url.cc:528
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:492
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:567
void setFragment(const std::string &fragment, EEncoding eflag=zypp::url::E_DECODED)
Set the fragment string in the URL.
Definition: Url.cc:717
std::string getQueryParam(const std::string &param, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
Definition: Url.cc:655
void delQueryParam(const std::string &param)
remove the specified query parameter.
Definition: Url.cc:840
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
Definition: Url.cc:734
void setPathParams(const std::string &params)
Set the path parameters.
Definition: Url.cc:786
void setQueryParam(const std::string &param, const std::string &value)
Set or add value for the specified query parameter.
Definition: Url.cc:833
void setUsername(const std::string &user, EEncoding eflag=zypp::url::E_DECODED)
Set the username in the URL authority.
Definition: Url.cc:725
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
Definition: Url.cc:575
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:221
Just inherits Exception to separate media exceptions.
bool useProxyFor(const Url &url_r) const
Return true if enabled and url_r does not match noProxy.
Definition: ProxyInfo.cc:55
std::string proxy(const Url &url) const
Definition: ProxyInfo.cc:43
Holds transfer setting.
void setProxyEnabled(bool enabled)
whether the proxy is used or not
void setProxyUsername(std::string &&val_r)
sets the proxy user
void setClientCertificatePath(Pathname &&val_r)
Sets the SSL client certificate file.
void setProxy(std::string &&val_r)
proxy to use if it is enabled
void setHeadRequestsAllowed(bool allowed)
set whether HEAD requests are allowed
void setAuthType(std::string &&val_r)
set the allowed authentication types
void setUsername(std::string &&val_r)
sets the auth username
void setVerifyHostEnabled(bool enabled)
Sets whether to verify host for ssl.
void setPassword(std::string &&val_r)
sets the auth password
void setCertificateAuthoritiesPath(Pathname &&val_r)
Sets the SSL certificate authorities path.
void setClientKeyPath(Pathname &&val_r)
Sets the SSL client key file.
void setProxyPassword(std::string &&val_r)
sets the proxy password
std::string proxyUsername() const
proxy auth username
void setVerifyPeerEnabled(bool enabled)
Sets whether to verify host for ssl.
std::string username() const
auth username
void setAnonymousAuth()
sets anonymous authentication (ie: for ftp)
void setTimeout(long t)
set the transfer timeout
int getZYPP_MEDIA_CURL_IPRESOLVE()
Definition: CurlHelper.cc:281
void fillSettingsFromUrl(const Url &url, media::TransferSettings &s)
Fills the settings structure using options passed on the url for example ?timeout=x&proxy=foo.
Definition: CurlHelper.cc:110
size_t log_redirects_curl(char *ptr, size_t size, size_t nmemb, void *userdata)
Definition: CurlHelper.cc:62
const char * anonymousIdHeader()
initialized only once, this gets the anonymous id from the target, which we pass in the http header
Definition: CurlHelper.cc:294
void globalInitCurlOnce()
Definition: CurlHelper.cc:19
const char * distributionFlavorHeader()
initialized only once, this gets the distribution flavor from the target, which we pass in the http h...
Definition: CurlHelper.cc:308
zypp::Url propagateQueryParams(zypp::Url url_r, const zypp::Url &template_r)
Definition: CurlHelper.cc:384
std::string curlUnEscape(std::string text_r)
Definition: CurlHelper.cc:351
std::string curlEscapedPath(std::string path_r)
Definition: CurlHelper.cc:346
const char * agentString()
initialized only once, this gets the agent string which also includes the curl version
Definition: CurlHelper.cc:322
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
Definition: CurlHelper.cc:258
Url clearQueryString(const Url &url)
Definition: CurlHelper.cc:358
void curlEscape(std::string &str_r, const char char_r, const std::string &escaped_r)
Definition: CurlHelper.cc:338
int log_curl(CURL *curl, curl_infotype info, char *ptr, size_t len, void *max_lvl)
Definition: CurlHelper.cc:28
SolvableIdType size_type
Definition: PoolMember.h:126
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1081
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", const Trim trim_r=NO_TRIM)
Split line_r into words.
Definition: String.h:527
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:223
@ E_ENCODED
Flag to request encoded string(s).
Definition: UrlUtils.h:53
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2