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