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