Spinning Topp Logo BlackTopp Studios
inc
uri.cpp
1 // © Copyright 2010 - 2015 BlackTopp Studios Inc.
2 /* This file is part of The Mezzanine Engine.
3 
4  The Mezzanine Engine is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  The Mezzanine Engine is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with The Mezzanine Engine. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /* The original authors have included a copy of the license specified above in the
18  'Docs' folder. See 'gpl.txt'
19 */
20 /* We welcome the use of the Mezzanine engine to anyone, including companies who wish to
21  Build professional software and charge for their product.
22 
23  However there are some practical restrictions, so if your project involves
24  any of the following you should contact us and we will try to work something
25  out:
26  - DRM or Copy Protection of any kind(except Copyrights)
27  - Software Patents You Do Not Wish to Freely License
28  - Any Kind of Linking to Non-GPL licensed Works
29  - Are Currently In Violation of Another Copyright Holder's GPL License
30  - If You want to change our code and not add a few hundred MB of stuff to
31  your distribution
32 
33  These and other limitations could cause serious legal problems if you ignore
34  them, so it is best to simply contact us or the Free Software Foundation, if
35  you have any questions.
36 
37  Joseph Toppi - toppij@gmail.com
38  John Blackwood - makoenergy02@gmail.com
39 */
40 
41 #ifndef _networkuri_cpp
42 #define _networkuri_cpp
43 
44 #include "Network/uri.h"
45 #include "Network/networkenumerations.h"
46 
47 #include "stringtool.h"
48 #include "exception.h"
49 
50 // For std::setw
51 #include <iomanip>
52 
53 namespace Mezzanine
54 {
55  namespace Network
56  {
57  const String URI::GenDelims = ":/?#[]@";
58  const String URI::SubDelims = "!$&'()*+,;=";
59 
60  void BuildURIPath(String& ToBuild, const StringVector& Segments)
61  {
62  ToBuild.clear();
63  StringVector::const_iterator SegIt = Segments.begin();
64  const StringVector::const_iterator LastIt = --Segments.end();
65  while( SegIt != Segments.end() )
66  {
67  ToBuild.append( *SegIt );
68  if( SegIt != LastIt ) {
69  ToBuild.append(1,'/');
70  }
71  ++SegIt;
72  }
73  }
74 
75  String MergeURIPaths(const String& Base, const String& Relative)
76  {
77  String Ret;
78  StringVector PathSegments;
79  StringVector MergedSegments;
80  if( !Base.empty() ) {
81  StringVector BaseSegs = StringTools::Split(Base,"/",std::numeric_limits<Whole>::max());
82  if( *(Base.rbegin()) != '/' ) {
83  BaseSegs.pop_back();
84  }
85  PathSegments.insert(PathSegments.end(),BaseSegs.begin(),BaseSegs.end());
86  }
87  if( !Relative.empty() ) {
88  StringVector RelativeSegs = StringTools::Split(Relative,"/",std::numeric_limits<Whole>::max());
89  PathSegments.insert(PathSegments.end(),RelativeSegs.begin(),RelativeSegs.end());
90  }
91  for( StringVector::const_iterator SegIt = PathSegments.begin() ; SegIt != PathSegments.end() ; ++SegIt )
92  {
93  if( (*SegIt) == ".." ) {
94  if( !MergedSegments.empty() ) {
95  MergedSegments.pop_back();
96  }
97  }else if( (*SegIt) != "." ){
98  MergedSegments.push_back( (*SegIt) );
99  }
100  }
101  BuildURIPath(Ret,MergedSegments);
102  if( !Relative.empty() && *(Relative.rbegin()) == '/' ) {
103  Ret.append(1,'/');
104  }
105  return Ret;
106  }
107 
108  ///////////////////////////////////////////////////////////////////////////////
109  // URI Methods
110 
112  URIPort(0)
113  { }
114 
115  URI::URI(const URI& Other) :
116  URIScheme(Other.URIScheme),
117  URIUserInfo(Other.URIUserInfo),
118  URIHost(Other.URIHost),
119  URIPath(Other.URIPath),
120  URIQuery(Other.URIQuery),
121  URIFragment(Other.URIFragment),
122  URIPort(Other.URIPort)
123  { }
124 
125  URI::URI(const String& ToParse)
126  { this->ParseFromString(ToParse); }
127 
128  URI::URI(const String& Scheme, const String& UserInfo, const String& Host, const String& Port, const String& Path, const String& Query, const String& Fragment) :
129  URIScheme(Scheme),
130  URIUserInfo(UserInfo),
131  URIHost(Host),
132  URIPath(Path),
133  URIQuery(Query),
134  URIFragment(Fragment),
135  URIPort(StringTools::ConvertToUInt16(Port))
136  { }
137 
138  URI::URI(const String& Scheme, const String& UserInfo, const String& Host, const UInt16 Port, const String& Path, const String& Query, const String& Fragment) :
139  URIScheme(Scheme),
140  URIUserInfo(UserInfo),
141  URIHost(Host),
142  URIPath(Path),
143  URIQuery(Query),
144  URIFragment(Fragment),
145  URIPort(Port)
146  { }
147 
149  { }
150 
152  {
153  this->URIScheme.clear();
154  if( !isalpha( (*CurrIt) ) ) {
155  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"The first character of a URI Scheme must be a letter.");
156  }
157 
158  while( CurrIt != EndIt && (*CurrIt) != ':' )
159  {
160  if( StringTools::IsAlphanumeric( (*CurrIt) ) || (*CurrIt) == '+' || (*CurrIt) == '.' || (*CurrIt) == '-' ) {
161  this->URIScheme.append( 1, static_cast<Char8>( tolower( (*CurrIt) ) ) );
162  ++CurrIt;
163  }else{
164  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"An invalid character was detected when parsing URI Scheme.");
165  }
166  }
167  }
168 
170  {
171  this->URIUserInfo.clear();
172 
173  while( CurrIt != EndIt && (*CurrIt) != '@' )
174  {
175  if( (*CurrIt) == ':' || URI::GenDelims.find( (*CurrIt) ) == String::npos ) {
176  this->URIUserInfo.append( 1, (*CurrIt) );
177  ++CurrIt;
178  }else{
179  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"An invalid character was detected when parsing URI User Information.");
180  }
181  }
182  }
183 
184  void URI::ParseHost(StringIterator CurrIt, const StringIterator EndIt)
185  {
186  this->URIHost.clear();
187 
188  while( CurrIt != EndIt && (*CurrIt) != ':' && (*CurrIt) != '/' )
189  {
190  if( StringTools::IsAlphanumeric( (*CurrIt) ) || (*CurrIt) == '-' || (*CurrIt) == '_' || (*CurrIt) == '.' ) {
191  this->URIHost.append( 1, static_cast<Char8>( tolower( (*CurrIt) ) ) );
192  ++CurrIt;
193  }else{
194  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"An invalid character was detected when parsing URI Host.");
195  }
196  }
197  }
198 
199  void URI::ParsePort(StringIterator CurrIt, const StringIterator EndIt)
200  {
201  this->URIPort = 0;
202 
203  String PortStr;
204  while( CurrIt != EndIt && (*CurrIt) != '/' )
205  {
206  if( StringTools::IsDigit( (*CurrIt) ) ) {
207  PortStr.append( 1, (*CurrIt) );
208  ++CurrIt;
209  }else{
210  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"An invalid character was detected when parsing URI Port.");
211  }
212  }
213 
214  if( !PortStr.empty() ) {
215  this->URIPort = StringTools::ConvertToUInt16( PortStr );
216  }
217  }
218 
219  void URI::ParsePath(StringIterator CurrIt, const StringIterator EndIt)
220  {
221  this->URIPath.clear();
222 
223  while( CurrIt != EndIt && (*CurrIt) != '?' && (*CurrIt) != '#' )
224  {
225  if( (*CurrIt) != '[' && (*CurrIt) != ']' ) {
226  this->URIPath.append( 1, (*CurrIt) );
227  ++CurrIt;
228  }else{
229  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"An invalid character was detected when parsing URI Path.");
230  }
231  }
232  }
233 
235  {
236  this->URIQuery.clear();
237 
238  while( CurrIt != EndIt && (*CurrIt) != '#' )
239  {
240  if( (*CurrIt) != '[' && (*CurrIt) != ']' ) {
241  this->URIQuery.append( 1, static_cast<Char8>( (*CurrIt) ) );
242  ++CurrIt;
243  }else{
244  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"An invalid character was detected when parsing URI Query.");
245  }
246  }
247  }
248 
250  {
251  this->URIFragment.clear();
252 
253  while( CurrIt != EndIt )
254  {
255  if( (*CurrIt) != '#' && (*CurrIt) != '[' && (*CurrIt) != ']' ) {
256  this->URIFragment.append( 1, static_cast<Char8>( (*CurrIt) ) );
257  ++CurrIt;
258  }else{
259  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"An invalid character was detected when parsing URI Fragment.");
260  }
261  }
262  }
263 
265  {
266  // Authorities must start with two slashes
267  if( ( CurrIt != EndIt ) && ( (*CurrIt) == '/' && *(CurrIt + 1) == '/' ) ) {
268  // Skip over the two slashes we found
269  CurrIt += 2;
270  // Call the no slash parse
271  this->ParseAuthorityNoSlash(CurrIt,EndIt);
272  }
273  }
274 
276  {
277  // Iterators to store the position of the delimiters
278  const StringIterator BeginPos = CurrIt;
279  StringIterator AtPos = EndIt;
280  StringIterator ColonPos = EndIt;
281  // This purposefully doesn't check if the delimiter is already set. For the most part if a delimiter isn't
282  // allowed it'll get caught by the specific component parsing. If we're passed a perfectly legal URI then
283  // the only delimiter that could be duplicated is ':', one for the UserInfo component separating the user
284  // name and password, and another to denote the start of the port portion of the Authority. We lump the
285  // entire UserInfo component together so we really only care about the second ':'.
286  while( CurrIt != EndIt && (*CurrIt) != '/' && (*CurrIt) != '?' && (*CurrIt) != '#' )
287  {
288  switch( *CurrIt )
289  {
290  case '@': AtPos = CurrIt; break;
291  case ':': ColonPos = CurrIt; break;
292  default: break;
293  }
294  ++CurrIt;
295  }
296 
297  // By this point the "CurrIt" iterator will have to either be the slash or equal to the end iterator, so
298  // use it for the end of the parsed text.
299  if( AtPos != EndIt ) {
300  this->ParseUserInfo(BeginPos,AtPos);
301 
302  if( ColonPos != EndIt ) {
303  this->ParseHost(AtPos+1,ColonPos);
304  this->ParsePort(ColonPos+1,CurrIt);
305  }else{
306  this->ParseHost(AtPos+1,CurrIt);
307  }
308  }else{
309  if( ColonPos != EndIt ) {
310  this->ParseHost(BeginPos,ColonPos);
311  this->ParsePort(ColonPos+1,CurrIt);
312  }else{
313  this->ParseHost(BeginPos,CurrIt);
314  }
315  }
316  }
317 
319  {
320  if( CurrIt != EndIt ) {
321  // Iterators to store the position of the delimiters
322  const StringIterator BeginPos = CurrIt;
323  StringIterator QuestPos = EndIt;
324  StringIterator PoundPos = EndIt;
325 
326  while( CurrIt != EndIt )
327  {
328  switch( *CurrIt )
329  {
330  case '?': QuestPos = CurrIt; break;
331  case '#': PoundPos = CurrIt; break;
332  default: break;
333  }
334  ++CurrIt;
335  }
336  // Path components may or may not start with a forward slash, depending on expected parsing or URI scheme used.
337  // Some simple paths may not require any slashes at all. So just go nuts in regards to the path.
338  this->ParsePath(BeginPos,( QuestPos != EndIt ? QuestPos : PoundPos ));
339 
340  if( QuestPos != EndIt ) {
341  this->ParseQuery(QuestPos+1,( PoundPos != EndIt ? PoundPos : CurrIt ));
342  }
343 
344  if( PoundPos != EndIt ) {
345  this->ParseFragment(PoundPos+1,CurrIt);
346  }
347  }
348  }
349 
350  ///////////////////////////////////////////////////////////////////////////////
351  // Percent Encoding
352 
354  {
355  StringStream PercentStream;
356  PercentStream << '%' << std::hex << std::uppercase << std::setw(2) << int(ToEncode);
357  return PercentStream.str();
358  }
359 
361  {
362  // Hex conversion inspired by POCO.
363  DecodeResult Ret(false,0);
364  if( ToDecode.size() == 3 ) {
365  if( StringTools::IsDigit( ToDecode[1] ) ) {
366  Ret.second += ToDecode[1] - '0';
367  }else if( StringTools::IsLowerHexLetter( ToDecode[1] ) ) {
368  Ret.second += ( ToDecode[1] - 'a' ) + 10;
369  }else if( StringTools::IsUpperHexLetter( ToDecode[1] ) ) {
370  Ret.second += ( ToDecode[1] - 'A' ) + 10;
371  }else{
372  MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_RANGE_EXCEPTION,"Attempting to percent decode non-hexadecimal character.")
373  }
374  Ret.second = ( Ret.second << 4 );
375  if( StringTools::IsDigit( ToDecode[2] ) ) {
376  Ret.second += ToDecode[2] - '0';
377  }else if( StringTools::IsLowerHexLetter( ToDecode[2] ) ) {
378  Ret.second += ( ToDecode[2] - 'a' ) + 10;
379  }else if( StringTools::IsUpperHexLetter( ToDecode[2] ) ) {
380  Ret.second += ( ToDecode[2] - 'A' ) + 10;
381  }else{
382  MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_RANGE_EXCEPTION,"Attempting to percent decode non-hexadecimal character.")
383  }
384  Ret.first = true;
385  }
386  return Ret;
387  }
388 
389  void URI::PercentEncodeSingle(const Char8 ToEncode, String& Destination)
390  {
391  Destination.append( URI::PercentEncodeSingle(ToEncode) );
392  }
393 
394  void URI::PercentDecodeSingle(const String& ToDecode, String& Destination)
395  {
396  DecodeResult ToAppend = URI::PercentDecodeSingle(ToDecode);
397  if( ToAppend.first ) {
398  Destination.append( 1, ToAppend.second );
399  }
400  }
401 
402  String URI::PercentEncode(const String& ToEncode, const String& AdditionalDelims)
403  {
404  String Ret;
405  URI::PercentEncode(ToEncode,Ret,AdditionalDelims);
406  return Ret;
407  }
408 
409  String URI::PercentDecode(const String& ToDecode, const Boole PlusToSpace)
410  {
411  String Ret;
412  URI::PercentDecode(ToDecode,Ret,PlusToSpace);
413  return Ret;
414  }
415 
416  void URI::PercentEncode(const String& ToEncode, String& Destination, const String& AdditionalDelims)
417  {
418  StringIterator CurrIt = ToEncode.begin();
419  while( CurrIt != ToEncode.end() )
420  {
421  if( GenDelims.find( *CurrIt ) != String::npos || AdditionalDelims.find( *CurrIt ) != String::npos ) {
422  Destination.append( URI::PercentEncodeSingle( *CurrIt ) );
423  }else{
424  Destination.append( 1, (*CurrIt) );
425  }
426  ++CurrIt;
427  }
428  }
429 
430  void URI::PercentDecode(const String& ToDecode, String& Destination, const Boole PlusToSpace)
431  {
432  StringIterator CurrIt = ToDecode.begin();
433  const StringIterator EndIt = ToDecode.end();
434  while( CurrIt != EndIt )
435  {
436  if( (*CurrIt) == '%' ) {
437  if( ( EndIt - CurrIt >= 4 ) && String(CurrIt,CurrIt+4) == "%%25" ) {
438  Destination.append("%25");
439  CurrIt += 4;
440  }else if( EndIt - CurrIt >= 3 ) {
441  DecodeResult Result = URI::PercentDecodeSingle( String(CurrIt,CurrIt+3) );
442  Destination.append( 1, Result.second );
443  CurrIt += 3;
444  }else{
445  MEZZ_EXCEPTION(ExceptionBase::SYNTAX_ERROR_EXCEPTION,"Percent encode delimiter detected without following characters to decode.");
446  }
447  }else if( PlusToSpace && (*CurrIt) == '+' ) {
448  Destination.append( 1, ' ' );
449  ++CurrIt;
450  }else{
451  Destination.append( 1, (*CurrIt) );
452  ++CurrIt;
453  }
454  }
455  }
456 
457  ///////////////////////////////////////////////////////////////////////////////
458  // Utility
459 
461  {
462  if( Scheme == "http" ) {
463  return Network::WKP_HTTP;
464  }else if( Scheme == "https" ) {
465  return Network::WKP_HTTPS;
466  }else if( Scheme == "ftp" ) {
467  return Network::WKP_FTPControl;
468  }else if( Scheme == "ftps" ) {
469  return Network::WKP_FTPSControl;
470  }else if( Scheme == "ssh" ) {
471  return Network::WKP_SSH;
472  }else if( Scheme == "telnet" ) {
473  return Network::WKP_Telnet;
474  }else if( Scheme == "xmpp" ) {
475  return Network::WKP_XMPP;
476  }else if( Scheme == "nntp" ) {
477  return Network::WKP_NNTP;
478  }else if( Scheme == "ldap" ) {
479  return Network::WKP_LDAP;
480  }else if( Scheme == "rtsp" ) {
481  return Network::WKP_RTSP;
482  }else if( Scheme == "sip" ) {
483  return Network::WKP_SIP;
484  }else if( Scheme == "sips" ) {
485  return Network::WKP_SIPS;
486  }else{
487  return 0;
488  }
489  }
490 
491  void URI::RemoveDotSegments(String& ToRemove, const Boole Relative)
492  {
493  if( !ToRemove.empty() ) {
494  StringVector RetSegments;
495  StringVector PathSegments = StringTools::Split(ToRemove,"/",std::numeric_limits<Whole>::max());
496  StringVector::iterator SegIt = PathSegments.begin();
497 
498  // If we're operating on a Relative URI, then the content doesn't matter, be it a dot segment or not.
499  // If relative is false though, it cannot be a dot segment to be appended.
500  // This behavior is explicitly defined in RFC 3986, section 5.2.4, subsection 2A.
501  if( Relative ) {
502  RetSegments.push_back( (*SegIt) );
503  }else if( (*SegIt) != ".." && (*SegIt) != "." ) {
504  RetSegments.push_back( (*SegIt) );
505  }
506  ++SegIt;
507 
508  // Go over the rest of the path, clearing out frivolous dot segments.
509  while( SegIt != PathSegments.end() )
510  {
511  if( (*SegIt) == ".." ) {
512  if( !RetSegments.empty() ) {
513  /// @todo This permits dot segments that move up the directory hierarchy to be at the start of the path.
514  /// Research needs to be done to determine if this is explicitly a part of the specification and/or how often it is used.
515  /// Until then some of this code will be commented out to protect from directory traversal attacks.
516  //if( RetSegments.back() == ".." ) {
517  // RetSegments.push_back( (*SegIt) );
518  //}else{
519  RetSegments.pop_back();
520  //}
521  }
522  }else if( (*SegIt) != "." ) {
523  RetSegments.push_back( (*SegIt) );
524  }
525  ++SegIt;
526  }
527 
528  // Rebuild the path.
529  BuildURIPath(ToRemove,RetSegments);
530  }
531  }
532 
534  { return !this->URIScheme.empty(); }
535 
537  { return this->URIScheme.empty(); }
538 
540  { return !this->HasAuthority(); }
541 
543  { return this->HasAuthority(); }
544 
545  void URI::SetComponents(const String& Scheme, const String& UserInfo, const String& Host, const String& Port, const String& Path, const String& Query, const String& Fragment)
546  {
547  this->ParseScheme(Scheme.begin(),Scheme.end());
548  this->ParseUserInfo(UserInfo.begin(),UserInfo.end());
549  this->ParseHost(Host.begin(),Host.end());
550  this->ParsePort(Port.begin(),Port.end());
551  this->ParsePath(Path.begin(),Path.end());
552  this->ParseQuery(Query.begin(),Query.end());
553  this->ParseFragment(Fragment.begin(),Fragment.end());
554  }
555 
556  void URI::SetComponents(const String& Scheme, const String& UserInfo, const String& Host, const UInt16 Port, const String& Path, const String& Query, const String& Fragment)
557  {
558  this->ParseScheme(Scheme.begin(),Scheme.end());
559  this->ParseUserInfo(UserInfo.begin(),UserInfo.end());
560  this->ParseHost(Host.begin(),Host.end());
561  this->ParsePath(Path.begin(),Path.end());
562  this->ParseQuery(Query.begin(),Query.end());
563  this->ParseFragment(Fragment.begin(),Fragment.end());
564  this->URIPort = Port;
565  }
566 
567  void URI::SetComponentsNoParse(const String& Scheme, const String& UserInfo, const String& Host, const String& Port, const String& Path, const String& Query, const String& Fragment)
568  {
569  this->URIScheme = Scheme;
570  this->URIUserInfo = UserInfo;
571  this->URIHost = Host;
572  this->URIPath = Path;
573  this->URIQuery = Query;
574  this->URIFragment = Fragment;
576  }
577 
578  void URI::SetComponentsNoParse(const String& Scheme, const String& UserInfo, const String& Host, const UInt16 Port, const String& Path, const String& Query, const String& Fragment)
579  {
580  this->URIScheme = Scheme;
581  this->URIUserInfo = UserInfo;
582  this->URIHost = Host;
583  this->URIPort = Port;
584  this->URIPath = Path;
585  this->URIQuery = Query;
586  this->URIFragment = Fragment;
587  }
588 
590  {
591  // 1) Drop everything that is case in-sensitive to lower case.
592  // This should already be handled by the forcing all set methods to go through the parsing methods.
593  // An inappropriate upper case letter should never get assigned.
594 
595  // 2) Remove frivolous components in the path. For example a path like "./[anything]/.." means nothing at all.
596  this->RemoveDotSegments(this->IsRelative());
597 
598  // 3) Decode unreserved percent encoded characters. Only certain characters need to be percent encoded. Encoding more than those wastes space.
599  if( !this->URIPath.empty() ) {
600  String TempPath;
601  URI::PercentDecode(this->URIPath,TempPath);
602  std::swap(this->URIPath,TempPath);
603  }
604  if( !this->URIQuery.empty() ) {
605  String TempQuery;
606  URI::PercentDecode(this->URIQuery,TempQuery);
607  std::swap(this->URIQuery,TempQuery);
608  }
609  if( !this->URIFragment.empty() ) {
610  String TempFragment;
611  URI::PercentDecode(this->URIFragment,TempFragment);
612  std::swap(this->URIFragment,TempFragment);
613  }
614 
615  // 4) If the port is a default port for the Scheme, it will be removed.
616  if( this->URIPort != 0 && this->URIPort == this->GetDefaultPort(this->URIScheme) ) {
617  this->URIPort = 0;
618  }
619  return *this;
620  }
621 
623  {
624  URI::RemoveDotSegments(this->URIPath,Relative);
625  return *this;
626  }
627 
628  URI& URI::Swap(URI& Other)
629  {
630  std::swap(this->URIScheme,Other.URIScheme);
631  std::swap(this->URIUserInfo,Other.URIUserInfo);
632  std::swap(this->URIHost,Other.URIHost);
633  std::swap(this->URIPath,Other.URIPath);
634  std::swap(this->URIQuery,Other.URIQuery);
635  std::swap(this->URIFragment,Other.URIFragment);
636  std::swap(this->URIPort,Other.URIPort);
637  return *this;
638  }
639 
641  {
642  this->URIScheme.clear();
643  this->URIUserInfo.clear();
644  this->URIHost.clear();
645  this->URIPath.clear();
646  this->URIQuery.clear();
647  this->URIFragment.clear();
648  this->URIPort = 0;
649  return *this;
650  }
651 
653  {
654  return ( this->URIScheme.empty() &&
655  this->URIUserInfo.empty() &&
656  this->URIHost.empty() &&
657  this->URIPath.empty() &&
658  this->URIQuery.empty() &&
659  this->URIFragment.empty() &&
660  this->URIPort == 0 );
661  }
662 
663  URI URI::Resolve(const URI& Relative) const
664  {
665  URI Ret;
666  if( Relative.HasScheme() ) {
667  Ret.URIScheme = Relative.URIScheme;
668  Ret.URIUserInfo = Relative.URIUserInfo;
669  Ret.URIHost = Relative.URIHost;
670  Ret.URIPort = Relative.URIPort;
671  Ret.URIPath = Relative.URIPath;
672  Ret.URIQuery = Relative.URIQuery;
673  }else{
674  if( Relative.HasAuthority() ) {
675  Ret.URIUserInfo = Relative.URIUserInfo;
676  Ret.URIHost = Relative.URIHost;
677  Ret.URIPort = Relative.URIPort;
678  Ret.URIPath = Relative.URIPath;
679  Ret.URIQuery = Relative.URIQuery;
680  }else{
681  if( Relative.HasPath() ) {
682  if( Relative.URIPath.at(0) == '/' ) {
683  Ret.URIPath = Relative.URIPath;
684  }else{
685  Ret.URIPath = MergeURIPaths(this->URIPath,Relative.URIPath);
686  }
687  Ret.URIQuery = Relative.URIQuery;
688  }else{
689  Ret.URIPath = this->URIPath;
690  if( Relative.HasQuery() ) {
691  Ret.URIQuery = Relative.URIQuery;
692  }else{
693  Ret.URIQuery = this->URIQuery;
694  }
695  }
696  Ret.URIUserInfo = this->URIUserInfo;
697  Ret.URIHost = this->URIHost;
698  Ret.URIPort = this->URIPort;
699  }
700  Ret.URIScheme = this->URIScheme;
701  }
702  Ret.URIFragment = Relative.URIFragment;
703  Ret.RemoveDotSegments(Ret.IsRelative());
704  return Ret;
705  }
706 
707  URI& URI::ParseFromString(const String& ToParse)
708  {
709  this->Clear();
710  // Get our iterators
711  StringIterator CurrIt = ToParse.begin();
712  StringIterator BeginIt = CurrIt;
713  const StringIterator EndIt = ToParse.end();
714 
715  // Loop until we hit a major delimiter.
716  while( CurrIt != EndIt && URI::GenDelims.find( *CurrIt ) == Mezzanine::String::npos )
717  { ++CurrIt; }
718 
719  // If we caught something then process it, otherwise if no delimiters were found treat everything as the path.
720  if( CurrIt != EndIt ) {
721  // Hopefully this is a scheme, otherwise this could get a awkward.
722  if( (*CurrIt) == ':' ) {
723  this->ParseScheme(BeginIt,CurrIt);
724  // Increment passed the ':'
725  ++CurrIt;
726  // Checks necessary for detecting the appropriate components already exist in these methods, so just pass it in.
727  this->ParseAuthority(CurrIt,EndIt);
728  this->ParsePathQueryFrag(CurrIt,EndIt);
729  }else if( (*CurrIt) == '/' ) {
730  // Are there characters between our slash and where we started? If so we likely have a path. Otherwise it could be an authority.
731  if( BeginIt == CurrIt ) {
732  this->ParseAuthority(CurrIt,EndIt);
733  this->ParsePathQueryFrag(CurrIt,EndIt);
734  }else{
735  this->ParsePathQueryFrag(BeginIt,EndIt);
736  }
737  }else{
738  this->ParsePathQueryFrag(BeginIt,EndIt);
739  }
740  }else{
741  this->ParsePath(BeginIt,EndIt);
742  }
743 
744  return *this;
745  }
746 
748  {
749  StringStream URIStream;
750  if( this->HasScheme() ) {
751  URIStream << this->URIScheme << ":";
752  }
753  if( this->HasAuthority() ) {
754  URIStream << "//" << this->GetAuthority();
755  }
756  if( this->HasPath() ) {
757  if( this->HasAuthority() && this->URIPath.at(0) != '/' ) {
758  URIStream << "/";
759  }
760  URIStream << this->GetPath();
761  }
762  if( this->HasQuery() ) {
763  URIStream << "?" << this->GetQuery();
764  }
765  if( this->HasFragment() ) {
766  URIStream << "#" << this->GetFragment();
767  }
768  return URIStream.str();
769  }
770 
771  URI& URI::AddQueryParameter(const String& Param, const String& Value)
772  {
773  StringStream QueryStream;
774  QueryStream << "&" << Param << "=" << Value;
775  this->URIQuery.append(QueryStream.str());
776  return *this;
777  }
778 
780  {
781  size_t ParamStartPos = 0;
782  while( ParamStartPos < this->URIQuery.size() )
783  {
784  size_t ParamEndPos = this->URIQuery.find_first_of("=",ParamStartPos);
785  size_t ValueEndPos = this->URIQuery.find_first_of("&;",ParamEndPos + 1);
786  if( this->URIQuery.substr(ParamStartPos,ParamEndPos - ParamStartPos) == Param ) {
787  return this->URIQuery.substr(ParamEndPos + 1,ValueEndPos - (ParamEndPos + 1));
788  }
789  ParamStartPos = ValueEndPos + 1;
790  }
791  return "";
792  }
793 
794  Boole URI::HasQueryParameter(const String& Param) const
795  {
796  size_t ParamStartPos = 0;
797  while( ParamStartPos < this->URIQuery.size() )
798  {
799  size_t ParamEndPos = this->URIQuery.find_first_of("=",ParamStartPos);
800  if( this->URIQuery.substr(ParamStartPos,ParamEndPos - ParamStartPos) == Param ) {
801  return true;
802  }
803  ParamStartPos = this->URIQuery.find_first_of("&;",ParamEndPos) + 1;
804  }
805  return false;
806  }
807 
808  ///////////////////////////////////////////////////////////////////////////////
809  // Raw Component Management
810 
811  URI& URI::SetScheme(const String& Scheme)
812  {
813  this->ParseScheme(Scheme.begin(),Scheme.end());
814  return *this;
815  }
816 
817  const String& URI::GetScheme() const
818  { return this->URIScheme; }
819 
821  { return !( this->URIScheme.empty() ); }
822 
823  URI& URI::SetUserInfo(const String& UserInfo)
824  {
825  this->ParseUserInfo(UserInfo.begin(),UserInfo.end());
826  return *this;
827  }
828 
829  const String& URI::GetUserInfo() const
830  { return this->URIUserInfo; }
831 
833  { return !( this->URIUserInfo.empty() ); }
834 
835  URI& URI::SetHost(const String& Host)
836  {
837  this->ParseHost(Host.begin(),Host.end());
838  return *this;
839  }
840 
841  URI& URI::SetHost(const IPAddress& Host)
842  {
843  String IPStr;
844  if( Host.GetProtocol() == Network::NLP_IPv4 ) {
845  IPStr = Host.GetAsString();
846  }else if( Host.GetProtocol() == Network::NLP_IPv6 ) {
847  IPStr = ( "[" + Host.GetAsString() + "]" );
848  }else{
849  MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_EXCEPTION,"Attempting to set URI Host via IP with an invalid IPAddress");
850  }
851  this->ParseHost(IPStr.begin(),IPStr.end());
852  return *this;
853  }
854 
855  const String& URI::GetHost() const
856  { return this->URIHost; }
857 
859  { return !( this->URIHost.empty() ); }
860 
861  URI& URI::SetPort(const UInt16 Port)
862  { this->URIPort = Port; return *this; }
863 
865  { return this->URIPort; }
866 
868  { return ( this->URIPort != 0 ); }
869 
870  URI& URI::SetPath(const String& Path)
871  {
872  this->ParsePath(Path.begin(),Path.end());
873  return *this;
874  }
875 
876  const String& URI::GetPath() const
877  { return this->URIPath; }
878 
880  { return !( this->URIPath.empty() ); }
881 
882  URI& URI::SetQuery(const String& Query)
883  {
884  this->ParseQuery(Query.begin(),Query.end());
885  return *this;
886  }
887 
888  const String& URI::GetQuery() const
889  { return this->URIQuery; }
890 
892  { return !( this->URIQuery.empty() ); }
893 
894  URI& URI::SetFragment(const String& Fragment)
895  {
896  this->ParseFragment(Fragment.begin(),Fragment.end());
897  return *this;
898  }
899 
900  const String& URI::GetFragment() const
901  { return this->URIFragment; }
902 
904  { return !( this->URIFragment.empty() ); }
905 
906  URI& URI::SetAuthority(const String& Authority)
907  {
908  StringIterator TempIt = Authority.begin();
909  this->ParseAuthorityNoSlash(TempIt,Authority.end());
910  return *this;
911  }
912 
914  {
915  StringStream URIStream;
916  if( !this->URIHost.empty() ) {
917  if( !this->URIUserInfo.empty() ) {
918  URIStream << this->URIUserInfo << "@";
919  }
920  URIStream << this->URIHost;
921  if( this->URIPort != 0 ) {
922  URIStream << ":" << this->URIPort;
923  }
924  }
925  return URIStream.str();
926  }
927 
929  { return this->HasHost(); } //Other Authority items add description to the host, so without it the Authority means nothing.
930 
931  ///////////////////////////////////////////////////////////////////////////////
932  // Operators
933 
934  void URI::operator=(const URI& Other)
935  {
936  this->URIScheme = Other.URIScheme;
937  this->URIUserInfo = Other.URIUserInfo;
938  this->URIHost = Other.URIHost;
939  this->URIPath = Other.URIPath;
940  this->URIQuery = Other.URIQuery;
941  this->URIFragment = Other.URIFragment;
942  this->URIPort = Other.URIPort;
943  }
944 
945  Boole URI::operator==(const URI& Other) const
946  {
947  return ( this->URIScheme == Other.URIScheme &&
948  this->URIUserInfo == Other.URIUserInfo &&
949  this->URIHost == Other.URIHost &&
950  this->URIPath == Other.URIPath &&
951  this->URIQuery == Other.URIQuery &&
952  this->URIFragment == Other.URIFragment &&
953  this->URIPort == Other.URIPort );
954  }
955 
956  Boole URI::operator!=(const URI& Other) const
957  {
958  return ( this->URIScheme != Other.URIScheme ||
959  this->URIUserInfo != Other.URIUserInfo ||
960  this->URIHost != Other.URIHost ||
961  this->URIPath != Other.URIPath ||
962  this->URIQuery != Other.URIQuery ||
963  this->URIFragment != Other.URIFragment ||
964  this->URIPort != Other.URIPort );
965  }
966  }//Network
967 }//Mezzanine
968 
969 #endif
void ParseQuery(StringIterator CurrIt, const StringIterator EndIt)
Parses the Query of a URI String. an invalid character or an error is encountered a SYNTAX_ERROR_EXCE...
Definition: uri.cpp:234
Boole IsUpperHexLetter(const Char8 ToCheck)
Checks if a character is a upper-case hexadecimal letter.
Definition: stringtool.cpp:113
URI & SetQuery(const String &Query)
Sets the Query component of the URI.
Definition: uri.cpp:882
URI & Normalize()
Makes minor adjustments to the content of this URI to make it easier to process/compare.
Definition: uri.cpp:589
std::vector< String > StringVector
This is a simple datatype for a vector container of strings.
Definition: datatypes.h:212
void ParseAuthorityNoSlash(StringIterator &CurrIt, const StringIterator EndIt)
Parses the Authority of a URI String, without checking for the preceding slashes. an invalid characte...
Definition: uri.cpp:275
URI & SetHost(const String &Host)
Sets the Host component of the URI.
Definition: uri.cpp:835
Boole HasPort() const
Gets whether or not the Port component of the URI has been set.
Definition: uri.cpp:867
bool Boole
Generally acts a single bit, true or false.
Definition: datatypes.h:173
String ConvertToString() const
Combines the set components of this URI into a usable String for transmission.
Definition: uri.cpp:747
const String & GetHost() const
Gets the Host component of the URI.
Definition: uri.cpp:855
static DecodeResult PercentDecodeSingle(const String &ToDecode)
Converts a String that is percent encoded into it's appropriate character.
Definition: uri.cpp:360
URI & SetAuthority(const String &Authority)
Sets the Authority Fragments of the URI.
Definition: uri.cpp:906
String URIHost
The domain or address of the Host where the resource is located.
Definition: uri.h:86
Boole IsHierarchical() const
Gets whether or not this URI has hierarchical components.
Definition: uri.cpp:542
String URIQuery
The optional Query to provide additional non-hierarchical information about the resource.
Definition: uri.h:92
Boole HasScheme() const
Gets whether or not the Scheme component of the URI has been set.
Definition: uri.cpp:820
String::const_iterator StringIterator
Convenience typedef for String iterators.
Definition: uri.h:63
const String & GetUserInfo() const
Gets the User Information component of the URI.
Definition: uri.cpp:829
void ParseHost(StringIterator CurrIt, const StringIterator EndIt)
Parses the Host of a URI String. an invalid character or an error is encountered a SYNTAX_ERROR_EXCEP...
Definition: uri.cpp:184
URI & Swap(URI &Other)
Swaps the contents of this URI with another.
Definition: uri.cpp:628
#define MEZZ_EXCEPTION(num, desc)
An easy way to throw exceptions with rich information.
Definition: exception.h:3048
Boole HasFragment() const
Gets whether or not the Fragment component of the URI has been set.
Definition: uri.cpp:903
URI & SetPath(const String &Port)
Sets the Path component of the URI.
Definition: uri.cpp:870
static UInt16 GetDefaultPort(const String &Scheme)
Gets the default port for a named protocol.
Definition: uri.cpp:460
URI & SetPort(const UInt16 Port)
Sets the Port component of the URI.
Definition: uri.cpp:861
Boole HasPath() const
Gets whether or not the Path component of the URI has been set.
Definition: uri.cpp:879
static const String SubDelims
A String containing the sub-delimiters that some URI schemes may use to delimit scheme specific data...
Definition: uri.h:76
void SetComponentsNoParse(const String &Scheme, const String &UserInfo, const String &Host, const String &Port, const String &Path, const String &Query, const String &Fragment)
Sets each component of this URI explicitly without actually parsing the strings (direct copy)...
Definition: uri.cpp:567
static String PercentDecode(const String &ToDecode, const Boole PlusToSpace=false)
Creates a duplicate String with all percent encodings decoded and appends that to the destination Str...
Definition: uri.cpp:409
This implements the exception hiearchy for Mezzanine.
Boole IsRelative() const
Gets whether or not this URI points to a relative resource location.
Definition: uri.cpp:536
Boole IsLowerHexLetter(const Char8 ToCheck)
Checks if a character is a lower-case hexadecimal letter.
Definition: stringtool.cpp:110
Boole HasQueryParameter(const String &Param) const
Checks if the named parameter exists in this URI.
Definition: uri.cpp:794
std::stringstream StringStream
A Datatype used for streaming operations with strings.
Definition: datatypes.h:176
UInt16 URIPort
The port number on the host to connect to in order to access the resource.
Definition: uri.h:98
Internet Protocol version 6.
void ParsePathQueryFrag(StringIterator &CurrIt, const StringIterator EndIt)
Parses the Path, Query, and Fragment components of a URI String. an invalid character or an error is ...
Definition: uri.cpp:318
UInt16 ConvertToUInt16(const String &ToConvert)
Converts a string into a UInt16.
Definition: stringtool.cpp:440
std::pair< Boole, Char8 > DecodeResult
Convenience typedef for storing if a decode was successful and it's result.
Definition: uri.h:65
char Char8
A datatype to represent one character.
Definition: datatypes.h:169
const String & GetPath() const
Gets the Path component of the URI.
Definition: uri.cpp:876
static String PercentEncode(const String &ToEncode, const String &AdditionalDelims)
Creates a duplicate String with all the reserved characters percent encoded and appends that to the d...
Definition: uri.cpp:402
void SetComponents(const String &Scheme, const String &UserInfo, const String &Host, const String &Port, const String &Path, const String &Query, const String &Fragment)
Sets each component of this URI explicitly.
Definition: uri.cpp:545
URI & SetScheme(const String &Scheme)
Sets the Scheme component of the URI.
Definition: uri.cpp:811
uint16_t UInt16
An 16-bit unsigned integer.
Definition: datatypes.h:122
~URI()
Class destructor.
Definition: uri.cpp:148
Boole IsAbsolute() const
Gets whether or not this URI points to an absolute resource location.
Definition: uri.cpp:533
URI Resolve(const URI &Relative) const
Computes the absolute referenced URI from a relative URI and the absolute URI it is relative to...
Definition: uri.cpp:663
void ParsePath(StringIterator CurrIt, const StringIterator EndIt)
Parses the Path of a URI String. an invalid character or an error is encountered a SYNTAX_ERROR_EXCEP...
Definition: uri.cpp:219
URI & SetUserInfo(const String &UserInfo)
Sets the User Information component of the URI.
Definition: uri.cpp:823
This is a simple class for representing IP addresses used throughout the Network subsystem.
Definition: ipaddress.h:57
const String & GetScheme() const
Gets the Scheme component of the URI.
Definition: uri.cpp:817
StringVector Split(const String &Source, const String &Delims, const Whole MaxSplits)
Splits a string into multiple substrings based on the specified delimiters.
Definition: stringtool.cpp:155
void ParseAuthority(StringIterator &CurrIt, const StringIterator EndIt)
Parses the Authority of a URI String. an invalid character or an error is encountered a SYNTAX_ERROR_...
Definition: uri.cpp:264
Boole operator!=(const URI &Other) const
Inequality Comparison Operator.
Definition: uri.cpp:956
void operator=(const URI &Other)
Assignment Operator.
Definition: uri.cpp:934
URI & ParseFromString(const String &ToParse)
Parses the contents of a String into this URI.
Definition: uri.cpp:707
String URIPath
The path to the resource on the Host machine holding the resource.
Definition: uri.h:89
Thrown when a passed parameter is checked at runtime and not in the expected range.
Definition: exception.h:110
static void RemoveDotSegments(String &ToRemove, const Boole Relative)
Removes frivolous parts of the path that mean nothing.
Definition: uri.cpp:491
Boole operator==(const URI &Other) const
Equality Comparison Operator.
Definition: uri.cpp:945
static String PercentEncodeSingle(const Char8 ToEncode)
Converts a character into a percent encoded string that is safe for use in URI's. ...
Definition: uri.cpp:353
URI()
Blank constructor.
Definition: uri.cpp:111
Thrown when parameters are checked at runtime and found invalid.
Definition: exception.h:108
Boole HasUserInfo() const
Gets whether or not the User Information component of the URI has been set.
Definition: uri.cpp:832
String GetAsString() const
Gets this IPAddress in a human readable format.
Definition: ipaddress.cpp:173
void ParseUserInfo(StringIterator CurrIt, const StringIterator EndIt)
Parses the User Information of a URI String. an invalid character or an error is encountered a SYNTAX...
Definition: uri.cpp:169
Boole IsOpaque() const
Gets whether or not this URI lacks a hierarchy.
Definition: uri.cpp:539
Network::NetworkLayerProtocol GetProtocol() const
Gets the type of IP address this is.
Definition: ipaddress.cpp:162
String URIScheme
The Scheme of the URI, usually noting the type of data the resource is.
Definition: uri.h:80
URI & SetFragment(const String &Fragment)
Sets the Fragment component of the URI.
Definition: uri.cpp:894
void ParsePort(StringIterator CurrIt, const StringIterator EndIt)
Parses the Port of a URI String. an invalid character or an error is encountered a SYNTAX_ERROR_EXCEP...
Definition: uri.cpp:199
UInt16 GetPort() const
Gets the Port component of the URI.
Definition: uri.cpp:864
A helper class for the reading and using of Uniform Resource Identifiers.
Definition: uri.h:59
String GetAuthority() const
Gets the Authority Fragments of the URI.
Definition: uri.cpp:913
String URIFragment
The optional Fragment providing additional direction to a subset of the resource. ...
Definition: uri.h:95
Boole IsDigit(const Mezzanine::Char8 ToCheck)
Checks if a character is a decimal digit.
Definition: stringtool.cpp:98
The bulk of the engine components go in this namspace.
Definition: actor.cpp:56
const String & GetQuery() const
Gets the Query component of the URI.
Definition: uri.cpp:888
Boole HasAuthority() const
Gets whether or not any Authority component of the URI has been set.
Definition: uri.cpp:928
URI & AddQueryParameter(const String &Param, const String &Value)
Adds a single param/value pair to the Query of this URI.
Definition: uri.cpp:771
Boole HasHost() const
Gets whether or not the Host component of the URI has been set.
Definition: uri.cpp:858
Thrown when some kind of syntax exception.
Definition: exception.h:99
Boole IsAlphanumeric(const Char8 ToCheck)
Checks if a character is a letter or digit.
Definition: stringtool.cpp:122
void ParseScheme(StringIterator CurrIt, const StringIterator EndIt)
Parses the Scheme of a URI String. an invalid character or an error is encountered a SYNTAX_ERROR_EXC...
Definition: uri.cpp:151
void ParseFragment(StringIterator CurrIt, const StringIterator EndIt)
Parses the Fragment of a URI String. an invalid character or an error is encountered a SYNTAX_ERROR_E...
Definition: uri.cpp:249
static const String GenDelims
A String containing the generic delimiters for URIs.
Definition: uri.h:71
String URIUserInfo
The optional user authentication information to be used when accessing the resource.
Definition: uri.h:83
std::string String
A datatype used to a series of characters.
Definition: datatypes.h:159
Boole IsEmpty() const
Checks to see if the URI is currently empty.
Definition: uri.cpp:652
Internet Protocol version 4.
String GetParameterValue(const String &Param) const
Gets the set value for a specific parameter in the Query of this URI.
Definition: uri.cpp:779
const String & GetFragment() const
Gets the Fragment component of the URI.
Definition: uri.cpp:900
URI & Clear()
Reverts this URI to a blank slate.
Definition: uri.cpp:640
Boole HasQuery() const
Gets whether or not the Query component of the URI has been set.
Definition: uri.cpp:891