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