XRootD
Loading...
Searching...
No Matches
XrdClURL.cc
Go to the documentation of this file.
1//------------------------------------------------------------------------------
2// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN)
3// Author: Lukasz Janyst <ljanyst@cern.ch>
4//------------------------------------------------------------------------------
5// XRootD is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// XRootD is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with XRootD. If not, see <http://www.gnu.org/licenses/>.
17//------------------------------------------------------------------------------
18
19#include "XrdCl/XrdClURL.hh"
22#include "XrdCl/XrdClLog.hh"
23#include "XrdCl/XrdClUtils.hh"
25#include <algorithm>
26#include <cctype>
27#include <cstdlib>
28#include <sstream>
29#include <vector>
30
31namespace XrdCl
32{
33 //----------------------------------------------------------------------------
34 // Constructor
35 //----------------------------------------------------------------------------
37 pPort( 1094 )
38 {
39 }
40
41 //----------------------------------------------------------------------------
42 // Constructor
43 //----------------------------------------------------------------------------
44 URL::URL( const std::string &url ):
45 pPort( 1094 )
46 {
47 FromString( url );
48 }
49
50 URL::URL( const char *url ) : pPort( 1094 )
51 {
52 FromString( url );
53 }
54
55 //----------------------------------------------------------------------------
56 // Parse URL - it is rather trivial and horribly slow but probably there
57 // is not need to have anything more fancy
58 //----------------------------------------------------------------------------
59 bool URL::FromString( const std::string &url )
60 {
61 Log *log = DefaultEnv::GetLog();
62
63 Clear();
64
65 if( url.length() == 0 )
66 {
67 log->Error( UtilityMsg, "The given URL is empty" );
68 return false;
69 }
70
71 //--------------------------------------------------------------------------
72 // Extract the protocol, assume file:// if none found
73 //--------------------------------------------------------------------------
74 size_t pos = url.find( "://" );
75
76 std::string current;
77 if( pos != std::string::npos )
78 {
79 pProtocol = url.substr( 0, pos );
80 current = url.substr( pos+3 );
81 }
82 else if( url[0] == '/' )
83 {
84 pProtocol = "file";
85 current = url;
86 }
87 else if( url[0] == '-' )
88 {
89 pProtocol = "stdio";
90 current = "-";
91 pPort = 0;
92 }
93 else
94 {
95 pProtocol = "root";
96 current = url;
97 }
98
99 //--------------------------------------------------------------------------
100 // If the protocol is HTTP(S) or DAV(S), change the default port number
101 //--------------------------------------------------------------------------
102 if (pProtocol == "http" || pProtocol == "dav") {
103 pPort = 80;
104 }
105 if (pProtocol == "https" || pProtocol == "davs") {
106 pPort = 443;
107 }
108
109 //--------------------------------------------------------------------------
110 // Extract host info and path
111 //--------------------------------------------------------------------------
112 std::string path;
113 std::string hostInfo;
114
115 if( pProtocol == "stdio" )
116 path = current;
117 else if( pProtocol == "file")
118 {
119 if( current[0] == '/' )
120 current = "localhost" + current;
121 pos = current.find( '/' );
122 if( pos == std::string::npos )
123 hostInfo = current;
124 else
125 {
126 hostInfo = current.substr( 0, pos );
127 path = current.substr( pos );
128 }
129 }
130 else
131 {
132 pos = current.find( '/' );
133 if( pos == std::string::npos )
134 hostInfo = current;
135 else
136 {
137 hostInfo = current.substr( 0, pos );
138 path = current.substr( pos+1 );
139 }
140 }
141
142 if( !ParseHostInfo( hostInfo ) )
143 {
144 log->Warning( UtilityMsg, "Failed to parse host info: %s",
145 hostInfo.c_str() );
146 Clear();
147 return false;
148 }
149
150 ParsePath( path );
151 ComputeURL();
152 return true;
153 }
154
155 //----------------------------------------------------------------------------
156 // Parse host info
157 //----------------------------------------------------------------------------
158 bool URL::ParseHostInfo( const std::string hostInfo )
159 {
160 if( pProtocol == "stdio" )
161 return true;
162
163 if( pProtocol.empty() || hostInfo.empty() )
164 return false;
165
166 size_t pos = hostInfo.find( "@" );
167 std::string hostPort;
168
169 //--------------------------------------------------------------------------
170 // We have found username-password
171 //--------------------------------------------------------------------------
172 if( pos != std::string::npos )
173 {
174 std::string userPass = hostInfo.substr( 0, pos );
175 hostPort = hostInfo.substr( pos+1 );
176 pos = userPass.find( ":" );
177
178 //------------------------------------------------------------------------
179 // It's both username and password
180 //------------------------------------------------------------------------
181 if( pos != std::string::npos )
182 {
183 pUserName = userPass.substr( 0, pos );
184 pPassword = userPass.substr( pos+1 );
185 }
186 //------------------------------------------------------------------------
187 // It's just the user name
188 //------------------------------------------------------------------------
189 else
190 pUserName = userPass;
191 }
192
193 //--------------------------------------------------------------------------
194 // No username-password
195 //--------------------------------------------------------------------------
196 else
197 hostPort = hostInfo;
198
199 //--------------------------------------------------------------------------
200 // Deal with hostname - IPv6 encoded address RFC 2732
201 //--------------------------------------------------------------------------
202 if( hostPort.length() >= 3 && hostPort[0] == '[' )
203 {
204 pos = hostPort.find( "]" );
205 if( pos != std::string::npos )
206 {
207 pHostName = hostPort.substr( 0, pos+1 );
208 hostPort.erase( 0, pos+2 );
209
210 //----------------------------------------------------------------------
211 // Check if we're IPv6 encoded IPv4
212 //----------------------------------------------------------------------
213 pos = pHostName.find( "." );
214 const size_t pos2 = pHostName.find( "[::" );
215 if( pos != std::string::npos && pos2 != std::string::npos )
216 {
217 std::string hl = pHostName;
218 std::transform(hl.begin(), hl.end(), hl.begin(),
219 [](unsigned char c){ return std::tolower(c); });
220 const size_t pos3 = hl.find( "[::ffff:" );
221 if ( pos3 != std::string::npos )
222 {
223 pHostName.erase( 0, 8 );
224 pHostName.erase( pHostName.length()-1, 1 );
225 }
226 else
227 {
228 pHostName.erase( 0, 3 );
229 pHostName.erase( pHostName.length()-1, 1 );
230 }
231 }
232 }
233 }
234 else
235 {
236 pos = hostPort.find( ":" );
237 if( pos != std::string::npos )
238 {
239 pHostName = hostPort.substr( 0, pos );
240 hostPort.erase( 0, pos+1 );
241 }
242 else
243 {
244 pHostName = hostPort;
245 hostPort = "";
246 }
247 if( pHostName.empty() )
248 return false;
249 }
250
251 //--------------------------------------------------------------------------
252 // Deal with port number
253 //--------------------------------------------------------------------------
254 if( !hostPort.empty() )
255 {
256 char *result;
257 pPort = ::strtol( hostPort.c_str(), &result, 10 );
258 if( *result != '\0' || pPort < 0 || pPort > 65535 )
259 return false;
260 }
261
262 ComputeHostId();
263 return true;
264 }
265
266 //----------------------------------------------------------------------------
267 // Parse path
268 //----------------------------------------------------------------------------
269 bool URL::ParsePath( const std::string &path )
270 {
271 size_t pos = path.find( "?" );
272 if( pos != std::string::npos )
273 {
274 pPath = path.substr( 0, pos );
275 SetParams( path.substr( pos+1, path.length() ) );
276 }
277 else
278 pPath = path;
279
280 if( !pPath.empty() )
281 {
282 std::string::iterator back = pPath.end() - 1;
283 if( pProtocol == "file" && *back == '/' )
284 pPath.erase( back );
285 }
286
287 ComputeURL();
288 return true;
289 }
290
291 //----------------------------------------------------------------------------
292 // Get path with params
293 //----------------------------------------------------------------------------
294 std::string URL::GetPathWithParams() const
295 {
296 std::ostringstream o;
297 if( !pPath.empty() )
298 o << pPath;
299
300 o << GetParamsAsString();
301 return o.str();
302 }
303
304 //------------------------------------------------------------------------
306 //------------------------------------------------------------------------
308 {
309 std::ostringstream o;
310 if( !pPath.empty() )
311 o << pPath;
312
313 o << GetParamsAsString( true );
314 return o.str();
315 }
316
317 //------------------------------------------------------------------------
319 //------------------------------------------------------------------------
320 std::string URL::GetLocation() const
321 {
322 std::ostringstream o;
323 o << pProtocol << "://";
324 if( pProtocol == "file" )
325 o << pHostName;
326 else
327 o << pHostName << ":" << pPort << "/";
328 o << pPath;
329 return o.str();
330 }
331
332 //------------------------------------------------------------------------
333 // Get the URL params as string
334 //------------------------------------------------------------------------
335 std::string URL::GetParamsAsString() const
336 {
337 return GetParamsAsString( false );
338 }
339
340 //------------------------------------------------------------------------
341 // Get the login token if present in the opaque info
342 //------------------------------------------------------------------------
343 std::string URL::GetLoginToken() const
344 {
345 auto itr = pParams.find( "xrd.logintoken" );
346 if( itr == pParams.end() )
347 return "";
348 return itr->second;
349 }
350
351 //------------------------------------------------------------------------
353 //------------------------------------------------------------------------
354 std::string URL::GetParamsAsString( bool filter ) const
355 {
356 if( pParams.empty() )
357 return "";
358
359 std::ostringstream o;
360 o << "?";
361 ParamsMap::const_iterator it;
362 for( it = pParams.begin(); it != pParams.end(); ++it )
363 {
364 // we filter out client specific parameters
365 if( filter && it->first.compare( 0, 6, "xrdcl." ) == 0 )
366 continue;
367 if( it != pParams.begin() ) o << "&";
368 o << it->first << "=" << it->second;
369 }
370 std::string ret = o.str();
371 if( ret == "?" ) ret.clear();
372 return ret;
373 }
374
375 //------------------------------------------------------------------------
376 // Set params
377 //------------------------------------------------------------------------
378 void URL::SetParams( const std::string &params )
379 {
380 pParams.clear();
381 std::string p = params;
382
383 if( p.empty() )
384 return;
385
386 if( p[0] == '?' )
387 p.erase( 0, 1 );
388
389 std::vector<std::string> paramsVect;
390 std::vector<std::string>::iterator it;
391 Utils::splitString( paramsVect, p, "&" );
392 for( it = paramsVect.begin(); it != paramsVect.end(); ++it )
393 {
394 if( it->empty() ) continue;
395 size_t qpos = it->find( '?' );
396 if( qpos != std::string::npos ) // we have login token
397 {
398 pParams["xrd.logintoken"] = it->substr( qpos + 1 );
399 it->erase( qpos );
400 }
401 size_t pos = it->find( "=" );
402 if( pos == std::string::npos )
403 pParams[*it] = "";
404 else
405 pParams[it->substr(0, pos)] = it->substr( pos+1, it->length() );
406 }
407 }
408
409 //----------------------------------------------------------------------------
410 // Clear the fields
411 //----------------------------------------------------------------------------
413 {
414 pHostId.clear();
415 pProtocol.clear();
416 pUserName.clear();
417 pPassword.clear();
418 pHostName.clear();
419 pPort = 1094;
420 pPath.clear();
421 pParams.clear();
422 pURL.clear();
423 }
424
425 //----------------------------------------------------------------------------
426 // Check validity
427 //----------------------------------------------------------------------------
428 bool URL::IsValid() const
429 {
430 if( pProtocol.empty() )
431 return false;
432 if( pProtocol == "file" && pPath.empty() )
433 return false;
434 if( pProtocol == "stdio" && pPath != "-" )
435 return false;
436 if( pProtocol != "file" && pProtocol != "stdio" && pHostName.empty() )
437 return false;
438 return true;
439 }
440
441 bool URL::IsMetalink() const
442 {
443 Env *env = DefaultEnv::GetEnv();
444 int mlProcessing = DefaultMetalinkProcessing;
445 env->GetInt( "MetalinkProcessing", mlProcessing );
446 if( !mlProcessing ) return false;
447 return PathEndsWith( ".meta4" ) || PathEndsWith( ".metalink" );
448 }
449
450 bool URL::IsLocalFile() const
451 {
452 return pProtocol == "file" && pHostName == "localhost";
453 }
454
455 //------------------------------------------------------------------------
456 // Does the protocol indicate encryption
457 //------------------------------------------------------------------------
458 bool URL::IsSecure() const
459 {
460 return ( pProtocol == "roots" || pProtocol == "xroots" );
461 }
462
463 //------------------------------------------------------------------------
464 // Is the URL used in TPC context
465 //------------------------------------------------------------------------
466 bool URL::IsTPC() const
467 {
468 ParamsMap::const_iterator itr = pParams.find( "xrdcl.intent" );
469 if( itr != pParams.end() )
470 return itr->second == "tpc";
471 return false;
472 }
473
474 std::string URL::GetObfuscatedURL() const {
475 return obfuscateAuth(pURL);
476 }
477
478 bool URL::PathEndsWith(const std::string & sufix) const
479 {
480 if (sufix.size() > pPath.size()) return false;
481 return std::equal(sufix.rbegin(), sufix.rend(), pPath.rbegin() );
482 }
483
484 //------------------------------------------------------------------------
485 //Get the host part of the URL (user:password\@host:port) plus channel
486 //specific CGI (xrdcl.identity & xrd.gsiusrpxy)
487 //------------------------------------------------------------------------
488 std::string URL::GetChannelId() const
489 {
490 std::string ret = pProtocol + "://" + pHostId + "/";
491 bool hascgi = false;
492
493 std::string keys[] = { "xrdcl.intent",
494 "xrd.gsiusrpxy",
495 "xrd.gsiusrcrt",
496 "xrd.gsiusrkey",
497 "xrd.sss",
498 "xrd.k5ccname" };
499 size_t size = sizeof( keys ) / sizeof( std::string );
500
501 for( size_t i = 0; i < size; ++i )
502 {
503 ParamsMap::const_iterator itr = pParams.find( keys[i] );
504 if( itr != pParams.end() )
505 {
506 ret += hascgi ? '&' : '?';
507 ret += itr->first;
508 ret += '=';
509 ret += itr->second;
510 hascgi = true;
511 }
512 }
513
514 return ret;
515 }
516
517 //----------------------------------------------------------------------------
518 // Recompute the host id
519 //----------------------------------------------------------------------------
520 void URL::ComputeHostId()
521 {
522 std::ostringstream o;
523 if( !pUserName.empty() )
524 {
525 o << pUserName;
526 if( !pPassword.empty() )
527 o << ":" << pPassword;
528 o << "@";
529 }
530 if( pProtocol == "file" )
531 o << pHostName;
532 else
533 o << pHostName << ":" << pPort;
534 pHostId = o.str();
535 }
536
537 //----------------------------------------------------------------------------
538 // Recreate the url
539 //----------------------------------------------------------------------------
540 void URL::ComputeURL()
541 {
542 if( !IsValid() ) {
543 pURL = "";
544 }
545
546 std::ostringstream o;
547 if( !pProtocol.empty() )
548 o << pProtocol << "://";
549
550 if( !pUserName.empty() )
551 {
552 o << pUserName;
553 if( !pPassword.empty() )
554 o << ":" << pPassword;
555 o << "@";
556 }
557
558 if( !pHostName.empty() )
559 {
560 if( pProtocol == "file" )
561 o << pHostName;
562 else
563 o << pHostName << ":" << pPort << "/";
564 }
565
566 o << GetPathWithParams();
567
568 pURL = o.str();
569 }
570}
std::string obfuscateAuth(const std::string &input)
static Log * GetLog()
Get default log.
static Env * GetEnv()
Get default client environment.
bool GetInt(const std::string &key, int &value)
Definition XrdClEnv.cc:115
Handle diagnostics.
Definition XrdClLog.hh:101
void Error(uint64_t topic, const char *format,...)
Report an error.
Definition XrdClLog.cc:231
void Warning(uint64_t topic, const char *format,...)
Report a warning.
Definition XrdClLog.cc:248
std::string GetChannelId() const
Definition XrdClURL.cc:488
bool IsMetalink() const
Is it a URL to a metalink.
Definition XrdClURL.cc:441
bool FromString(const std::string &url)
Parse a string and fill the URL fields.
Definition XrdClURL.cc:59
void SetParams(const std::string &params)
Set params.
Definition XrdClURL.cc:378
URL()
Default constructor.
Definition XrdClURL.cc:36
std::string GetPathWithFilteredParams() const
Get the path with params, filteres out 'xrdcl.'.
Definition XrdClURL.cc:307
std::string GetPathWithParams() const
Get the path with params.
Definition XrdClURL.cc:294
std::string GetObfuscatedURL() const
Get the URL with authz information obfuscated.
Definition XrdClURL.cc:474
std::string GetLocation() const
Get location (protocol://host:port/path).
Definition XrdClURL.cc:320
bool IsLocalFile() const
Definition XrdClURL.cc:450
std::string GetParamsAsString() const
Get the URL params as string.
Definition XrdClURL.cc:335
bool IsSecure() const
Does the protocol indicate encryption.
Definition XrdClURL.cc:458
bool IsValid() const
Is the url valid.
Definition XrdClURL.cc:428
void Clear()
Clear the url.
Definition XrdClURL.cc:412
bool IsTPC() const
Is the URL used in TPC context.
Definition XrdClURL.cc:466
std::string GetLoginToken() const
Get the login token if present in the opaque info.
Definition XrdClURL.cc:343
static void splitString(Container &result, const std::string &input, const std::string &delimiter)
Split a string.
Definition XrdClUtils.hh:56
const int DefaultMetalinkProcessing
const uint64_t UtilityMsg