XRootD
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"
20 #include "XrdCl/XrdClConstants.hh"
21 #include "XrdCl/XrdClDefaultEnv.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 
31 namespace 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  //------------------------------------------------------------------------
307  std::string URL::GetPathWithFilteredParams() const
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  //----------------------------------------------------------------------------
412  void URL::Clear()
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