Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TUri.cxx
Go to the documentation of this file.
1// @(#)root/base:$Id$
2// Author: Gerhard E. Bruckner 15/07/07
3
4/*************************************************************************
5 * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12/** \class TUri
13\ingroup Base
14
15This class represents a RFC 3986 compatible URI.
16\see https://doi.org/10.17487/RFC3986
17It provides member functions to set and return the different
18the different parts of an URI. The functionality is that of
19a validating parser.
20*/
21
22#include <cctype> // for tolower()
23#include "TUri.h"
24#include "TObjArray.h"
25#include "TObjString.h"
26#include "TPRegexp.h"
27
28//RFC3986:
29// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
30const char* const kURI_pchar = "(?:[[:alpha:][:digit:]\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f][0-9A-Fa-f])";
31
32//unreserved characters, see chapter 2.3
33const char* const kURI_unreserved = "[[:alpha:][:digit:]\\-._~]";
34
35// reserved characters, see chapter
36// reserved = gen-delims / sub-delims
37//const char* const kURI_reserved = "[:/?#[]@!$&'()*+,;=]";
38
39// gen-delims, see chapter 2.2
40// delimiters of the generic URI components
41//const char* const kURI_gendelims = "[:/?#[]@]";
42
43// sub-delims, see chapter 2.2
44//const char* const kURI_subdelims = "[!$&'()*+,;=]";
45
46
47
48////////////////////////////////////////////////////////////////////////////////
49/// Constructor that calls SetUri with a complete URI.
50
52{
53 SetUri(uri);
54}
55
56////////////////////////////////////////////////////////////////////////////////
57/// Constructor that calls SetUri with a complete URI.
58
59TUri::TUri(const char *uri)
60{
61 SetUri(uri);
62}
63
64////////////////////////////////////////////////////////////////////////////////
65/// TUri copy ctor.
66
67TUri::TUri(const TUri &uri) : TObject(uri)
68{
69 fScheme = uri.fScheme;
70 fUserinfo = uri.fUserinfo;
71 fHost = uri.fHost;
72 fPort = uri.fPort;
73 fPath = uri.fPath;
74 fQuery = uri.fQuery;
75 fFragment = uri.fFragment;
78 fHasHost = uri.fHasHost;
79 fHasPort = uri.fHasPort;
80 fHasPath = uri.fHasPath;
81 fHasQuery = uri.fHasQuery;
83}
84
85////////////////////////////////////////////////////////////////////////////////
86/// TUri assignment operator.
87
89{
90 if (this != &rhs) {
92 fScheme = rhs.fScheme;
93 fUserinfo = rhs.fUserinfo;
94 fHost = rhs.fHost;
95 fPort = rhs.fPort;
96 fPath = rhs.fPath;
97 fQuery = rhs.fQuery;
98 fFragment = rhs.fFragment;
99 fHasScheme = rhs.fHasScheme;
100 fHasUserinfo = rhs.fHasUserinfo;
101 fHasHost = rhs.fHasHost;
102 fHasPort = rhs.fHasPort;
103 fHasPath = rhs.fHasPath;
104 fHasQuery = rhs.fHasQuery;
105 fHasFragment = rhs.fHasFragment;
106 }
107 return *this;
108}
109
110////////////////////////////////////////////////////////////////////////////////
111/// Implementation of a TUri Equivalence operator
112/// that uses syntax-based normalisation
113/// see chapter 6.2.2.
114
116{
117 // make temporary copies of the operands
118 TUri u11 = u1;
119 TUri u22 = u2;
120 // normalise them
121 u11.Normalise();
122 u22.Normalise();
123 // compare them as TStrings
124 return u11.GetUri() == u22.GetUri();
125}
126
127////////////////////////////////////////////////////////////////////////////////
128/// Returns the whole URI -
129/// an implementation of chapter 5.3 component recomposition.
130/// The result URI is composed out of the five basic parts.
131/// ~~~ {.cpp}
132/// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
133/// hier-part = "//" authority path-abempty
134/// / path-absolute
135/// / path-rootless
136/// / path-empty
137/// ~~~
138
140{
141 TString result = "";
142 if (fHasScheme)
143 result = fScheme + ":";
144 result += GetHierPart();
145 if (fHasQuery)
146 result += TString("?") + fQuery;
147 if (fHasFragment)
148 result += TString("#") + fFragment;
149 return result;
150}
151
152////////////////////////////////////////////////////////////////////////////////
153/// This functions implements the "remove_dot_segments" routine
154/// of chapter 5.2.4 "for interpreting and removing the
155/// special '.' and '..' complete path segments from a
156/// referenced path".
157
159{
161 TString sink = TString(""); // sink buffer
162
163 // Step 2 "While the source buffer is not empty, loop as follows:"
164 while (source.Length() > 0) {
165 // Rule 2.A
166 if (TPRegexp("^\\.\\.?/(.*)$").Substitute(source, "/$1") > 0)
167 continue;
168
169 // Rule 2.B
170 if (TPRegexp("^/\\./(.*)$|^/\\.($)").Substitute(source, "/$1") > 0)
171 continue;
172
173 // Rule 2.C
174 if (TPRegexp("^/\\.\\./(.*)$|^/\\.\\.($)").Substitute(source, "/$1") > 0) {
175 Ssiz_t last = sink.Last('/');
176 if (last == -1)
177 last = 0;
178 sink.Remove(last, sink.Length() - last);
179 continue;
180 }
181
182 // Rule 2.D
183 if (source.CompareTo(".") == 0 || source.CompareTo("..") == 0) {
184 source.Remove(0, source.Length() - 11);
185 continue;
186 }
187
188 // Rule 2.E
189 TPRegexp regexp = TPRegexp("^(/?[^/]*)(?:/|$)");
190 TObjArray *tokens = regexp.MatchS(source);
191 TString segment = ((TObjString*) tokens->At(1))->GetString();
192 sink += segment;
193 source.Remove(0, segment.Length());
194 delete tokens;
195 }
196
197 // Step 3: return sink buffer
198 return sink;
199}
200
201////////////////////////////////////////////////////////////////////////////////
202/// Returns kTRUE if instance qualifies as absolute-URI
203/// absolute-URI = scheme ":" hier-part [ "?" query ]
204/// cf. Appendix A.
205
207{
208 return (HasScheme() && HasHierPart() && !HasFragment());
209}
210
211////////////////////////////////////////////////////////////////////////////////
212/// Returns kTRUE if instance qualifies as relative-ref
213/// relative-ref = relative-part [ "?" query ] [ "#" fragment ]
214/// cf. Appendix A.
215
217{
218 return (!HasScheme() && HasRelativePart());
219}
220
221////////////////////////////////////////////////////////////////////////////////
222/// Returns kTRUE if instance qualifies as URI
223/// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
224/// cf. Appendix A.
225
227{
228 return (HasScheme() && HasHierPart());
229}
230
231////////////////////////////////////////////////////////////////////////////////
232/// Returns kTRUE if instance qualifies as URI-reference
233/// URI-reference = URI / relative-ref
234/// cf. Appendix A.
235
237{
238 return (IsUri() || IsRelative());
239}
240
241////////////////////////////////////////////////////////////////////////////////
242/// Set scheme component of URI:
243/// ~~~ {.cpp}
244/// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
245/// ~~~
246
248{
249 if (!scheme) {
251 return kTRUE;
252 }
253 if (IsScheme(scheme)) {
254 fScheme = scheme;
256 return kTRUE;
257 } else {
258 Error("SetScheme", "<scheme> component \"%s\" of URI is not compliant with RFC 3986.", scheme.Data());
259 return kFALSE;
260 }
261}
262
263////////////////////////////////////////////////////////////////////////////////
264/// Returns kTRUE if string qualifies as URI scheme:
265/// ~~~ {.cpp}
266/// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
267/// ~~~
268
270{
271 return TPRegexp(
272 "^[[:alpha:]][[:alpha:][:digit:]+\\-.]*$"
273 ).Match(string);
274}
275
276////////////////////////////////////////////////////////////////////////////////
277/// Returns the authority part of the instance:
278/// ~~~ {.cpp}
279/// authority = [ userinfo "@" ] host [ ":" port ]
280/// ~~~
281
283{
285 if (fHasPort && !fPort.IsNull())
286 // add port only if not empty
287 authority += TString(":") + TString(fPort);
288 return (authority);
289}
290
291////////////////////////////////////////////////////////////////////////////////
292/// Set query component of URI:
293/// ~~~ {.cpp}
294/// query = *( pchar / "/" / "?" )
295/// ~~~
296
298{
299 if (!query) {
301 return kTRUE;
302 }
303 if (IsQuery(query)) {
304 fQuery = query;
306 return kTRUE;
307 } else {
308 Error("SetQuery", "<query> component \"%s\" of URI is not compliant with RFC 3986.", query.Data());
309 return kFALSE;
310 }
311}
312
313////////////////////////////////////////////////////////////////////////////////
314/// Returns kTRUE if string qualifies as URI query:
315/// ~~~ {.cpp}
316/// query = *( pchar / "/" / "?" )
317/// ~~~
318
320{
321 return TPRegexp(
322 TString("^([/?]|") + kURI_pchar + ")*$"
323 ).Match(string);
324}
325
326////////////////////////////////////////////////////////////////////////////////
327/// Set authority part of URI:
328/// ~~~ {.cpp}
329/// authority = [ userinfo "@" ] host [ ":" port ]
330/// ~~~
331///
332/// Split into components {userinfo@, host, :port},
333/// remember that according to the RFC, it is necessary to
334/// distinguish between missing component (no delimiter)
335/// and empty component (delimiter present).
336
338{
339 if (authority.IsNull()) {
343 return kTRUE;
344 }
345 TPRegexp regexp = TPRegexp("^(?:(.*@))?([^:]*)((?::.*)?)$");
346 TObjArray *tokens = regexp.MatchS(authority);
347
348 if (tokens->GetEntries() != 4) {
349 Error("SetAuthority", "<authority> component \"%s\" of URI is not compliant with RFC 3986.", authority.Data());
350 return kFALSE;
351 }
352
354
355 // handle userinfo
356 TString userinfo = ((TObjString*) tokens->At(1))->GetString();
357 if (userinfo.EndsWith("@")) {
358 userinfo.Remove(TString::kTrailing, '@');
360 }
361
362 // handle host
363 TString host = ((TObjString*) tokens->At(2))->GetString();
364 valid &= SetHost(host);
365
366 // handle port
367 TString port = ((TObjString*) tokens->At(3))->GetString();
368 if (port.BeginsWith(":")) {
369 port.Remove(TString::kLeading, ':');
370 valid &= SetPort(port);
371 }
372
373 return valid;
374}
375
376////////////////////////////////////////////////////////////////////////////////
377/// Returns kTRUE if string qualifies as valid URI authority:
378/// ~~~ {.cpp}
379/// authority = [ userinfo "@" ] host [ ":" port ]
380/// ~~~
381
383{
384 // split into parts {userinfo, host, port}
385 TPRegexp regexp = TPRegexp("^(?:(.*)@)?([^:]*)(?::(.*))?$");
386 TObjArray *tokens = regexp.MatchS(string);
387 TString userinfo = ((TObjString*) tokens->At(1))->GetString();
388 TString host = ((TObjString*) tokens->At(2))->GetString();
389 TString port;
390 // port is optional
391 if (tokens->GetEntries() == 4)
392 port = ((TObjString*) tokens->At(3))->GetString();
393 else
394 port = "";
395 return (IsHost(host) && IsUserInfo(userinfo) && IsPort(port));
396}
397
398////////////////////////////////////////////////////////////////////////////////
399/// Set userinfo component of URI:
400/// ~~~ {.cpp}
401/// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
402/// ~~~
403
405{
406 if (userinfo.IsNull()) {
408 return kTRUE;
409 }
410 if (IsUserInfo(userinfo)) {
413 return kTRUE;
414 } else {
415 Error("SetUserInfo", "<userinfo> component \"%s\" of URI is not compliant with RFC 3986.", userinfo.Data());
416 return kFALSE;
417 }
418}
419
420////////////////////////////////////////////////////////////////////////////////
421/// Return kTRUE is string qualifies as valid URI userinfo:
422/// ~~~ {.cpp}
423/// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
424/// ~~~
425/// this equals to pchar without the '@' character
426
428{
429 return (TPRegexp(
430 "^" + TString(kURI_pchar) + "*$"
431 ).Match(string) > 0 && !TString(string).Contains("@"));
432}
433
434////////////////////////////////////////////////////////////////////////////////
435/// Set host component of URI:
436/// ~~~ {.cpp}
437/// RFC 3986: host = IP-literal / IPv4address / reg-name
438/// implemented: host = IPv4address / reg-name
439/// ~~~
440
442{
443 if (IsHost(host)) {
444 fHost = host;
445 fHasHost = kTRUE;
446 return kTRUE;
447 } else {
448 Error("SetHost", "<host> component \"%s\" of URI is not compliant with RFC 3986.", host.Data());
449 return kFALSE;
450 }
451}
452
453////////////////////////////////////////////////////////////////////////////////
454/// Set port component of URI:
455/// ~~~ {.cpp}
456/// port = *DIGIT
457/// ~~~
458
460{
461 if (IsPort(port)) {
462 fPort = port;
463 fHasPort = kTRUE;
464 return kTRUE;
465 }
466 Error("SetPort", "<port> component \"%s\" of URI is not compliant with RFC 3986.", port.Data());
467 return kFALSE;
468}
469
470////////////////////////////////////////////////////////////////////////////////
471/// Set path component of URI:
472/// ~~~ {.cpp}
473/// path = path-abempty ; begins with "/" or is empty
474/// / path-absolute ; begins with "/" but not "//"
475/// / path-noscheme ; begins with a non-colon segment
476/// / path-rootless ; begins with a segment
477/// / path-empty ; zero characters
478/// ~~~
479
481{
482 if (IsPath(path)) {
483 fPath = path;
484 fHasPath = kTRUE;
485 return kTRUE;
486 }
487 Error("SetPath", "<path> component \"%s\" of URI is not compliant with RFC 3986.", path.Data());
488 return kFALSE;
489}
490
491////////////////////////////////////////////////////////////////////////////////
492/// Set fragment component of URI:
493/// ~~~ {.cpp}
494/// fragment = *( pchar / "/" / "?" )
495/// ~~~
496
498{
499 if (IsFragment(fragment)) {
502 return kTRUE;
503 } else {
504 Error("SetFragment", "<fragment> component \"%s\" of URI is not compliant with RFC 3986.", fragment.Data());
505 return kFALSE;
506 }
507}
508
509////////////////////////////////////////////////////////////////////////////////
510/// Returns kTRUE if string qualifies as valid fragment component
511/// ~~~ {.cpp}
512/// fragment = *( pchar / "/" / "?" )
513/// ~~~
514
516{
517 return (TPRegexp(
518 "^(" + TString(kURI_pchar) + "|[/?])*$"
519 ).Match(string) > 0);
520}
521
522////////////////////////////////////////////////////////////////////////////////
523/// Display function,
524/// - option "d" .. debug output
525/// - anything else .. simply print URI.
526
528{
529 if (strcmp(option, "d") != 0) {
530 Printf("%s", GetUri().Data());
531 return ;
532 }
533 // debug output
534 Printf("URI: <%s>", GetUri().Data());
535 Printf("(%c) |--scheme---------<%s>", fHasScheme ? 't' : 'f', fScheme.Data());
536 Printf(" |--hier-----------<%s>", GetHierPart().Data());
537 Printf("(%c) |--authority------<%s>", HasAuthority() ? 't' : 'f', GetAuthority().Data());
538 Printf("(%c) |--userinfo---<%s>", fHasUserinfo ? 't' : 'f', fUserinfo.Data());
539 Printf("(%c) |--host-------<%s>", fHasHost ? 't' : 'f', fHost.Data());
540 Printf("(%c) |--port-------<%s>", fHasPort ? 't' : 'f', fPort.Data());
541 Printf("(%c) |--path-------<%s>", fHasPath ? 't' : 'f', fPath.Data());
542 Printf("(%c) |--query------<%s>", fHasQuery ? 't' : 'f', fQuery.Data());
543 Printf("(%c) |--fragment---<%s>", fHasFragment ? 't' : 'f', fFragment.Data());
544 printf("path flags: ");
545 if (IsPathAbempty(fPath))
546 printf("abempty ");
548 printf("absolute ");
550 printf("rootless ");
551 if (IsPathEmpty(fPath))
552 printf("empty ");
553 printf("\nURI flags: ");
554 if (IsAbsolute())
555 printf("absolute-URI ");
556 if (IsRelative())
557 printf("relative-ref ");
558 if (IsUri())
559 printf("URI ");
560 if (IsReference())
561 printf("URI-reference ");
562 printf("\n");
563}
564
565////////////////////////////////////////////////////////////////////////////////
566/// Initialize this URI object.
567/// Set all TString members to empty string,
568/// set all Bool_t members to kFALSE.
569
571{
572 fScheme = "";
573 fUserinfo = "";
574 fHost = "";
575 fPort = "";
576 fPath = "";
577 fQuery = "";
578 fFragment = "";
579
587}
588
589////////////////////////////////////////////////////////////////////////////////
590/// Parse URI and set the member variables accordingly,
591/// returns kTRUE if URI validates, and kFALSE otherwise:
592/// ~~~ {.cpp}
593/// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
594/// hier-part = "//" authority path-abempty
595/// / path-absolute
596/// / path-rootless
597/// / path-empty
598/// ~~~
599
601{
602 // Reset member variables
603 Reset();
604
605 // regular expression taken from appendix B
606 // reference points 12 3 4 5 6 7 8 9
607 TPRegexp regexp = TPRegexp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)([?]([^#]*))?(#(.*))?");
608 TObjArray *tokens = regexp.MatchS(uri);
609
610 // collect bool values to see if all setters succeed
612 //tokens->Print();
613 switch (tokens->GetEntries()) {
614 case 10:
615 // URI contains fragment delimiter '#'
616 valid &= SetFragment(((TObjString*) tokens->At(9))->GetString());
617 // fallthrough
618
619 case 8:
620 // URI does not contain a fragment delimiter
621 // if there is a query delimiter '?', set query
622 if (!((TString)((TObjString*) tokens->At(6))->GetString()).IsNull())
623 valid &= SetQuery(((TObjString*) tokens->At(7))->GetString());
624 // fallthrough
625
626 case 6:
627 // URI does not contain fragment or query delimiters
628 valid &= SetPath(((TObjString*) tokens->At(5))->GetString());
629 // if there is an authority delimiter '//', set authority
630 if (!((TString)((TObjString*) tokens->At(3))->GetString()).IsNull())
631 valid &= SetAuthority(((TObjString*) tokens->At(4))->GetString());
632 // if there is a scheme delimiter ':', set scheme
633 if (!((TString)((TObjString*) tokens->At(1))->GetString()).IsNull())
634 valid &= SetScheme(((TObjString*) tokens->At(2))->GetString());
635 break;
636
637 default:
638 // regular expression did not match
639 Error("SetUri", "URI \"%s\" is not is not compliant with RFC 3986.", uri.Data());
640 valid = kFALSE;
641 }
642
643 // reset member variables once again, if one at least setter failed
644 if (!valid)
645 Reset();
646
647 delete tokens;
648 return valid;
649}
650
651////////////////////////////////////////////////////////////////////////////////
652/// ~~~ {.cpp}
653/// hier-part = "//" authority path-abempty
654/// / path-absolute
655/// / path-rootless
656/// / path-empty
657/// ~~~
658
660{
662 return (TString("//") + GetAuthority() + fPath);
663 else
664 return fPath;
665}
666
667////////////////////////////////////////////////////////////////////////////////
668/// relative-part = "//" authority path-abempty
669/// ~~~ {.cpp}
670/// / path-absolute
671/// / path-noscheme
672/// / path-empty
673/// ~~~
674
676{
678 return (TString("//") + GetAuthority() + fPath);
679 else
680 return fPath;
681}
682
683////////////////////////////////////////////////////////////////////////////////
684/// returns hier-part component of URI
685/// ~~~ {.cpp}
686/// hier-part = "//" authority path-abempty
687/// / path-absolute
688/// / path-rootless
689/// / path-empty
690/// ~~~
691
693{
694 /* if ( IsPathAbsolute(hier) || IsPathRootless(hier) || IsPathEmpty(hier) ) {
695 SetPath (hier);
696 return kTRUE;
697 }
698 */
699
700 // reference points: 1 2 3
701 TPRegexp regexp = TPRegexp("^(//([^/?#]*))?([^?#]*)$");
702 TObjArray *tokens = regexp.MatchS(hier);
703
704 if (tokens->GetEntries() == 0) {
705 Error("SetHierPart", "<hier-part> component \"%s\" of URI is not compliant with RFC 3986.", hier.Data());
706 delete tokens;
707 return false;
708 }
709
710 TString delm = ((TObjString*) tokens->At(1))->GetString();
711 TString auth = ((TObjString*) tokens->At(2))->GetString();
712 TString path = ((TObjString*) tokens->At(3))->GetString();
713
715
716 if (!delm.IsNull() && IsPathAbempty(path)) {
717 // URI contains an authority delimiter '//' ...
719 valid &= SetPath(path);
720 } else {
721 // URI does not contain an authority
722 if (IsPathAbsolute(path) || IsPathRootless(path) || IsPathEmpty(path))
723 valid &= SetPath(path);
724 else {
725 valid = kFALSE;
726 Error("SetHierPart", "<hier-part> component \"%s\" of URI is not compliant with RFC 3986.", hier.Data());
727 }
728 }
729 delete tokens;
730 return valid;
731}
732
733////////////////////////////////////////////////////////////////////////////////
734/// Returns kTRUE if string qualifies as hier-part:
735/// ~~~ {.cpp}
736/// hier-part = "//" authority path-abempty
737/// / path-absolute
738/// / path-rootless
739/// / path-empty
740/// ~~~
741
743{
744 // use functionality of SetHierPart
745 // in order to avoid duplicate code
746 TUri uri;
747 return (uri.SetHierPart(string));
748}
749
750////////////////////////////////////////////////////////////////////////////////
751/// Returns kTRUE is string qualifies as relative-part:
752/// ~~~ {.cpp}
753/// relative-part = "//" authority path-abempty
754/// / path-absolute
755/// / path-noscheme
756/// / path-empty
757/// ~~~
758
760{
761 // use functionality of SetRelativePart
762 // in order to avoid duplicate code
763 TUri uri;
764 return (uri.SetRelativePart(string));
765}
766
767////////////////////////////////////////////////////////////////////////////////
768/// Returns kTRUE is string qualifies as relative-part:
769/// ~~~ {.cpp}
770/// relative-part = "//" authority path-abempty
771/// / path-absolute
772/// / path-noscheme
773/// / path-empty
774/// ~~~
775
777{
778 // reference points: 1 2 3
779 TPRegexp regexp = TPRegexp("^(//([^/?#]*))?([^?#]*)$");
780 TObjArray *tokens = regexp.MatchS(relative);
781
782 if (tokens->GetEntries() == 0) {
783 Error("SetRelativePath", "<relative-part> component \"%s\" of URI is not compliant with RFC 3986.", relative.Data());
784 delete tokens;
785 return false;
786 }
787 TString delm = ((TObjString*) tokens->At(1))->GetString();
788 TString auth = ((TObjString*) tokens->At(2))->GetString();
789 TString path = ((TObjString*) tokens->At(3))->GetString();
790
792
793 if (!delm.IsNull() && IsPathAbempty(path)) {
794 // URI contains an authority delimiter '//' ...
796 valid &= SetPath(path);
797 } else {
798 // URI does not contain an authority
799 if (IsPathAbsolute(path) || IsPathNoscheme(path) || IsPathEmpty(path))
800 valid &= SetPath(path);
801 else {
802 valid = kFALSE;
803 Error("SetRelativePath", "<relative-part> component \"%s\" of URI is not compliant with RFC 3986.", relative.Data());
804 }
805 }
806 delete tokens;
807 return valid;
808}
809
810////////////////////////////////////////////////////////////////////////////////
811/// Percent-encode and return the given string according to RFC 3986
812/// in principle, this function cannot fail or produce an error.
813
815{
816 TString sink = "";
817 // iterate through source
818 for (Int_t i = 0; i < source.Length(); i++) {
819 if (IsUnreserved(TString(source(i)))) {
820 // unreserved character -> copy
821 sink = sink + source[i];
822 } else {
823 // reserved character -> encode to 2 digit hex
824 // preceded by '%'
825 char buffer[4];
826 snprintf(buffer, 4, "%%%02X", source[i]);
827 sink = sink + buffer;
828 }
829 }
830 return sink;
831}
832
833////////////////////////////////////////////////////////////////////////////////
834/// Returns kTRUE if string qualifies as valid host component:
835/// host = IP-literal / IPv4address / reg-name
836/// implemented: host = IPv4address / reg-name
837
839{
840 return (IsRegName(string) || IsIpv4(string));
841}
842
843////////////////////////////////////////////////////////////////////////////////
844/// Returns kTRUE if string qualifies as valid path component:
845/// ~~~ {.cpp}
846/// path = path-abempty ; begins with "/" or is empty
847/// / path-absolute ; begins with "/" but not "//"
848/// / path-noscheme ; begins with a non-colon segment
849/// / path-rootless ; begins with a segment
850/// / path-empty ; zero characters
851/// ~~~
852
854{
855 return (IsPathAbempty(string) ||
856 IsPathAbsolute(string) ||
857 IsPathNoscheme(string) ||
858 IsPathRootless(string) ||
859 IsPathEmpty(string));
860}
861
862////////////////////////////////////////////////////////////////////////////////
863/// Returns kTRUE if string qualifies as valid path-abempty component:
864/// ~~~ {.cpp}
865/// path-abempty = *( "/" segment )
866/// segment = *pchar
867/// ~~~
868
870{
871 return (TPRegexp(
872 TString("^(/") + TString(kURI_pchar) + "*)*$"
873 ).Match(string) > 0);
874}
875
876////////////////////////////////////////////////////////////////////////////////
877/// Returns kTRUE if string qualifies as valid path-absolute component
878/// ~~~ {.cpp}
879/// path-absolute = "/" [ segment-nz *( "/" segment ) ]
880/// segment-nz = 1*pchar
881/// segment = *pchar
882/// ~~~
883
885{
886 return (TPRegexp(
887 TString("^/(") + TString(kURI_pchar) + "+(/" + TString(kURI_pchar) + "*)*)?$"
888 ).Match(string) > 0);
889}
890
891////////////////////////////////////////////////////////////////////////////////
892/// Returns kTRUE if string qualifies as valid path-noscheme component:
893/// ~~~ {.cpp}
894/// path-noscheme = segment-nz-nc *( "/" segment )
895/// segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
896/// segment = *pchar
897/// ~~~
898
900{
901 return (TPRegexp(
902 TString("^(([[:alpha:][:digit:]\\-._~!$&'()*+,;=@]|%[0-9A-Fa-f][0-9A-Fa-f])+)(/") + TString(kURI_pchar) + "*)*$"
903 ).Match(string) > 0);
904}
905
906////////////////////////////////////////////////////////////////////////////////
907/// Returns kTRUE if string qualifies as valid path-rootless component:
908/// ~~~ {.cpp}
909/// path-rootless = segment-nz *( "/" segment )
910/// ~~~
911
913{
914 return TPRegexp(
915 TString("^") + TString(kURI_pchar) + "+(/" + TString(kURI_pchar) + "*)*$"
916 ).Match(string);
917}
918
919////////////////////////////////////////////////////////////////////////////////
920/// Returns kTRUE if string qualifies as valid path-empty component:
921/// ~~~ {.cpp}
922/// path-empty = 0<pchar>
923/// ~~~
924
926{
927 return TString(string).IsNull();
928}
929
930////////////////////////////////////////////////////////////////////////////////
931/// Returns kTRUE if string qualifies as valid port component:
932/// ~~~ {.cpp}
933/// RFC 3986: port = *DIGIT
934/// ~~~
935
937{
938 return (TPRegexp("^[[:digit:]]*$").Match(string) > 0);
939}
940
941////////////////////////////////////////////////////////////////////////////////
942/// Returns kTRUE if string qualifies as valid reg-name:
943/// ~~~ {.cpp}
944/// reg-name = *( unreserved / pct-encoded / sub-delims )
945/// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
946/// / "*" / "+" / "," / ";" / "="
947/// ~~~
948
950{
951 return (TPRegexp(
952 "^([[:alpha:][:digit:]\\-._~!$&'()*+,;=]|%[0-9A-Fa-f][0-9A-Fa-f])*$").Match(string) > 0);
953}
954
955////////////////////////////////////////////////////////////////////////////////
956/// Returns kTRUE, if string holds a valid IPv4 address
957/// currently only decimal variant supported.
958/// Existence of leading 0s or numeric range remains unchecked
959/// IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet.
960
962{
963 return (TPRegexp(
964 "^([[:digit:]]{1,3}[.]){3}[[:digit:]]{1,3}$").Match(string) > 0);
965}
966
967////////////////////////////////////////////////////////////////////////////////
968/// Returns kTRUE, if the given string does not contain
969/// RFC 3986 reserved characters
970/// ~~~ {.cpp}
971/// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
972/// ~~~
973
975{
976 return (TPRegexp(
977 "^" + TString(kURI_unreserved) + "*$").Match(string) > 0);
978}
979
980////////////////////////////////////////////////////////////////////////////////
981/// Syntax based normalisation according to
982/// RFC chapter 6.2.2.
983
985{
986 // case normalisation of host and scheme
987 // cf. chapter 6.2.2.1
989 if (fHasHost) {
990 TString host = GetHost();
991 host.ToLower();
992 SetHost(host);
993 }
994 // percent-encoding normalisation (6.2.2.2) for
995 // userinfo, host (reg-name), path, query, fragment
1001
1002 // path segment normalisation (6.2.2.3)
1003 if (fHasPath)
1005}
1006
1007////////////////////////////////////////////////////////////////////////////////
1008/// Percent-decode the given string according to chapter 2.1
1009/// we assume a valid pct-encoded string.
1010
1012{
1013 TString sink = "";
1014 Int_t i = 0;
1015 while (i < source.Length()) {
1016 if (source[i] == '%') {
1017 if (source.Length() < i+2) {
1018 // abort if out of bounds
1019 return sink;
1020 }
1021 // two hex digits follow -> decode to ASCII
1022 // upper nibble, bits 4-7
1023 char c1 = tolower(source[i + 1]) - '0';
1024 if (c1 > 9) // a-f
1025 c1 -= 39;
1026 // lower nibble, bits 0-3
1027 char c0 = tolower(source[i + 2]) - '0';
1028 if (c0 > 9) // a-f
1029 c0 -= 39;
1030 char decoded = c1 << 4 | c0;
1031 if (TPRegexp(kURI_unreserved).Match(decoded) > 0) {
1032 // we have an unreserved character -> store decoded version
1033 sink = sink + decoded;
1034 } else {
1035 // this is a reserved character
1036 TString pct = source(i,3);
1037 pct.ToUpper();
1038 sink = sink + pct;
1039 }
1040 // advance 2 characters
1041 i += 2;
1042 } else {
1043 // regular character -> copy
1044 sink = sink + source[i];
1045 }
1046 i++;
1047 }
1048 return sink;
1049}
1050
1051////////////////////////////////////////////////////////////////////////////////
1052/// Normalise the percent-encoded parts of the string
1053/// i.e. uppercase the hexadecimal digits
1054/// %[:alpha:][:alpha:] -> %[:ALPHA:][:ALPHA:]
1055
1057{
1058 TString sink = "";
1059 Int_t i = 0;
1060 while (i < source.Length()) {
1061 if (source[i] == '%') {
1062 if (source.Length() < i+2) {
1063 // abort if out of bounds
1064 return sink;
1065 }
1066 TString pct = source(i,3);
1067 // uppercase the pct part
1068 pct.ToUpper();
1069 sink = sink + pct;
1070 // advance 2 characters
1071 i += 2;
1072 } else {
1073 // regular character -> copy
1074 sink = sink + source[i];
1075 }
1076 i++;
1077 }
1078 return sink;
1079}
1080
1081////////////////////////////////////////////////////////////////////////////////
1082/// Percent-decode the given string according to chapter 2.1
1083/// we assume a valid pct-encoded string.
1084
1086{
1087 TString sink = "";
1088 Int_t i = 0;
1089 while (i < source.Length()) {
1090 if (source[i] == '%') {
1091 if (source.Length() < i+2) {
1092 // abort if out of bounds
1093 return sink;
1094 }
1095 // two hex digits follow -> decode to ASCII
1096 // upper nibble, bits 4-7
1097 char c1 = tolower(source[i + 1]) - '0';
1098 if (c1 > 9) // a-f
1099 c1 -= 39;
1100 // lower nibble, bits 0-3
1101 char c0 = tolower(source[i + 2]) - '0';
1102 if (c0 > 9) // a-f
1103 c0 -= 39;
1104 sink = sink + (char)(c1 << 4 | c0);
1105 // advance 2 characters
1106 i += 2;
1107 } else {
1108 // regular character -> copy
1109 sink = sink + source[i];
1110 }
1111 i++;
1112 }
1113 return sink;
1114}
1115
1116////////////////////////////////////////////////////////////////////////////////
1117/// Transform a URI reference into its target URI using
1118/// given a base URI.
1119/// This is an implementation of the pseudocode in chapter 5.2.2.
1120
1121TUri TUri::Transform(const TUri &reference, const TUri &base)
1122{
1123 TUri target;
1124 if (reference.HasScheme()) {
1125 target.SetScheme(reference.GetScheme());
1126 if (reference.HasAuthority())
1127 target.SetAuthority(reference.GetAuthority());
1128 if (reference.HasPath())
1129 target.SetPath(RemoveDotSegments(reference.GetPath()));
1130 if (reference.HasQuery())
1131 target.SetQuery(reference.GetQuery());
1132 } else {
1133 if (reference.HasAuthority()) {
1134 target.SetAuthority(reference.GetAuthority());
1135 if (reference.HasPath())
1136 target.SetPath(RemoveDotSegments(reference.GetPath()));
1137 if (reference.HasQuery())
1138 target.SetQuery(reference.GetQuery());
1139 } else {
1140 if (reference.GetPath().IsNull()) {
1141 target.SetPath(base.GetPath());
1142 if (reference.HasQuery()) {
1143 target.SetQuery(reference.GetQuery());
1144 } else {
1145 if (base.HasQuery())
1146 target.SetQuery(base.GetQuery());
1147 }
1148 } else {
1149 if (reference.GetPath().BeginsWith("/")) {
1150 target.SetPath(RemoveDotSegments(reference.GetPath()));
1151 } else {
1152 target.SetPath(RemoveDotSegments(MergePaths(reference, base)));
1153 }
1154 if (reference.HasQuery())
1155 target.SetQuery(reference.GetQuery());
1156 }
1157 if (base.HasAuthority())
1158 target.SetAuthority(base.GetAuthority());
1159 }
1160 if (base.HasScheme())
1161 target.SetScheme(base.GetScheme());
1162 }
1163 if (reference.HasFragment())
1164 target.SetFragment(reference.GetFragment());
1165 return target;
1166}
1167
1168////////////////////////////////////////////////////////////////////////////////
1169/// RFC 3986, 5.3.2.
1170/// If the base URI has a defined authority component and an empty
1171/// path, then return a string consisting of "/" concatenated with the
1172/// reference's path; otherwise,
1173/// return a string consisting of the reference's path component
1174/// appended to all but the last segment of the base URI's path (i.e.,
1175/// excluding any characters after the right-most "/" in the base URI
1176/// path, or excluding the entire base URI path if it does not contain
1177/// any "/" characters).
1178
1179const TString TUri::MergePaths(const TUri &reference, const TUri &base)
1180{
1181 TString result = "";
1182 if (base.HasAuthority() && base.GetPath().IsNull()) {
1183 result = TString("/") + reference.GetPath();
1184 } else {
1185 TString basepath = base.GetPath();
1186 Ssiz_t last = basepath.Last('/');
1187 if (last == -1)
1188 result = reference.GetPath();
1189 else
1190 result = basepath(0, last + 1) + reference.GetPath();
1191 }
1192 return result;
1193}
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
const char Option_t
Option string (const char)
Definition RtypesCore.h:80
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
void Printf(const char *fmt,...)
Formats a string in a circular formatting buffer and prints the string.
Definition TString.cxx:2509
Bool_t operator==(const TUri &u1, const TUri &u2)
Implementation of a TUri Equivalence operator that uses syntax-based normalisation see chapter 6....
Definition TUri.cxx:115
const char *const kURI_unreserved
Definition TUri.cxx:33
const char *const kURI_pchar
Definition TUri.cxx:30
#define snprintf
Definition civetweb.c:1579
An array of TObjects.
Definition TObjArray.h:31
Collectable string class.
Definition TObjString.h:28
Mother of all ROOT objects.
Definition TObject.h:41
TObject & operator=(const TObject &rhs) noexcept
TObject assignment operator.
Definition TObject.h:299
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1071
Int_t Match(const TString &s, const TString &mods="", Int_t start=0, Int_t nMaxMatch=10, TArrayI *pos=nullptr)
The number of matches is returned, this equals the full match + sub-pattern matches.
Definition TPRegexp.cxx:409
TObjArray * MatchS(const TString &s, const TString &mods="", Int_t start=0, Int_t nMaxMatch=10)
Returns a TObjArray of matched substrings as TObjString's.
Definition TPRegexp.cxx:440
Basic string class.
Definition TString.h:138
void ToLower()
Change string to lower-case.
Definition TString.cxx:1189
const char * Data() const
Definition TString.h:384
@ kLeading
Definition TString.h:284
@ kTrailing
Definition TString.h:284
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:631
Bool_t IsNull() const
Definition TString.h:422
TString & Remove(Ssiz_t pos)
Definition TString.h:693
This class represents a RFC 3986 compatible URI.
Definition TUri.h:35
const TString GetRelativePart() const
relative-part = "//" authority path-abempty
Definition TUri.cxx:675
static Bool_t IsPath(const TString &)
Returns kTRUE if string qualifies as valid path component:
Definition TUri.cxx:853
Bool_t HasPath() const
Definition TUri.h:97
Bool_t SetScheme(const TString &scheme)
Set scheme component of URI:
Definition TUri.cxx:247
Bool_t IsRelative() const
Returns kTRUE if instance qualifies as relative-ref relative-ref = relative-part [ "?...
Definition TUri.cxx:216
static const TString RemoveDotSegments(const TString &)
This functions implements the "remove_dot_segments" routine of chapter 5.2.4 "for interpreting and re...
Definition TUri.cxx:158
Bool_t SetFragment(const TString &fragment)
Set fragment component of URI:
Definition TUri.cxx:497
Bool_t SetPath(const TString &path)
Set path component of URI:
Definition TUri.cxx:480
static Bool_t IsPathAbempty(const TString &)
Returns kTRUE if string qualifies as valid path-abempty component:
Definition TUri.cxx:869
Bool_t fHasPort
Definition TUri.h:66
Bool_t SetRelativePart(const TString &)
Returns kTRUE is string qualifies as relative-part:
Definition TUri.cxx:776
Bool_t IsAbsolute() const
Returns kTRUE if instance qualifies as absolute-URI absolute-URI = scheme ":" hier-part [ "?...
Definition TUri.cxx:206
Bool_t fHasFragment
Definition TUri.h:69
const TString GetHost() const
Definition TUri.h:85
static Bool_t IsFragment(const TString &)
Returns kTRUE if string qualifies as valid fragment component.
Definition TUri.cxx:515
TString fQuery
Definition TUri.h:60
Bool_t SetUserInfo(const TString &userinfo)
Set userinfo component of URI:
Definition TUri.cxx:404
static Bool_t IsQuery(const TString &)
Returns kTRUE if string qualifies as URI query:
Definition TUri.cxx:319
TString fPort
Definition TUri.h:58
Bool_t IsUri() const
Returns kTRUE if instance qualifies as URI URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] ...
Definition TUri.cxx:226
TString fScheme
Definition TUri.h:55
static const TString PctEncode(const TString &source)
Percent-encode and return the given string according to RFC 3986 in principle, this function cannot f...
Definition TUri.cxx:814
static const TString PctNormalise(const TString &source)
Normalise the percent-encoded parts of the string i.e.
Definition TUri.cxx:1056
const TString GetPath() const
Definition TUri.h:87
const TString GetHierPart() const
Definition TUri.cxx:659
void Normalise()
Syntax based normalisation according to RFC chapter 6.2.2.
Definition TUri.cxx:984
static Bool_t IsUserInfo(const TString &)
Return kTRUE is string qualifies as valid URI userinfo:
Definition TUri.cxx:427
const TString GetUri() const
Returns the whole URI - an implementation of chapter 5.3 component recomposition.
Definition TUri.cxx:139
Bool_t fHasHost
Definition TUri.h:65
const TString GetFragment() const
Definition TUri.h:89
static Bool_t IsPathRootless(const TString &)
Returns kTRUE if string qualifies as valid path-rootless component:
Definition TUri.cxx:912
static Bool_t IsHost(const TString &)
Returns kTRUE if string qualifies as valid host component: host = IP-literal / IPv4address / reg-name...
Definition TUri.cxx:838
static Bool_t IsScheme(const TString &)
Returns kTRUE if string qualifies as URI scheme:
Definition TUri.cxx:269
const TString GetQuery() const
Definition TUri.h:88
Bool_t SetAuthority(const TString &authority)
Set authority part of URI:
Definition TUri.cxx:337
Bool_t fHasScheme
Definition TUri.h:63
static TUri Transform(const TUri &reference, const TUri &base)
Transform a URI reference into its target URI using given a base URI.
Definition TUri.cxx:1121
TString fUserinfo
Definition TUri.h:56
void Reset()
Initialize this URI object.
Definition TUri.cxx:570
const TString GetAuthority() const
Returns the authority part of the instance:
Definition TUri.cxx:282
Bool_t HasFragment() const
Definition TUri.h:99
void Print(Option_t *option="") const override
Display function,.
Definition TUri.cxx:527
static Bool_t IsPort(const TString &)
Returns kTRUE if string qualifies as valid port component:
Definition TUri.cxx:936
static Bool_t IsAuthority(const TString &)
Returns kTRUE if string qualifies as valid URI authority:
Definition TUri.cxx:382
Bool_t HasRelativePart() const
Definition TUri.h:100
static Bool_t IsPathNoscheme(const TString &)
Returns kTRUE if string qualifies as valid path-noscheme component:
Definition TUri.cxx:899
Bool_t fHasQuery
Definition TUri.h:68
Bool_t HasQuery() const
Definition TUri.h:98
static const TString MergePaths(const TUri &reference, const TUri &base)
RFC 3986, 5.3.2.
Definition TUri.cxx:1179
static Bool_t IsPathAbsolute(const TString &)
Returns kTRUE if string qualifies as valid path-absolute component.
Definition TUri.cxx:884
static Bool_t IsUnreserved(const TString &string)
Returns kTRUE, if the given string does not contain RFC 3986 reserved characters.
Definition TUri.cxx:974
Bool_t SetPort(const TString &port)
Set port component of URI:
Definition TUri.cxx:459
TString fPath
Definition TUri.h:59
Bool_t SetQuery(const TString &path)
Set query component of URI:
Definition TUri.cxx:297
Bool_t HasScheme() const
Definition TUri.h:91
Bool_t HasHierPart() const
Definition TUri.h:92
TString fFragment
Definition TUri.h:61
static Bool_t IsHierPart(const TString &)
Returns kTRUE if string qualifies as hier-part:
Definition TUri.cxx:742
static Bool_t IsRelativePart(const TString &)
Returns kTRUE is string qualifies as relative-part:
Definition TUri.cxx:759
static Bool_t IsIpv4(const TString &)
Returns kTRUE, if string holds a valid IPv4 address currently only decimal variant supported.
Definition TUri.cxx:961
Bool_t IsReference() const
Returns kTRUE if instance qualifies as URI-reference URI-reference = URI / relative-ref cf.
Definition TUri.cxx:236
static Bool_t IsPathEmpty(const TString &)
Returns kTRUE if string qualifies as valid path-empty component:
Definition TUri.cxx:925
TUri & operator=(const TUri &rhs)
TUri assignment operator.
Definition TUri.cxx:88
Bool_t SetUri(const TString &uri)
Parse URI and set the member variables accordingly, returns kTRUE if URI validates,...
Definition TUri.cxx:600
Bool_t SetHost(const TString &host)
Set host component of URI:
Definition TUri.cxx:441
Bool_t SetHierPart(const TString &hier)
returns hier-part component of URI
Definition TUri.cxx:692
TUri()
Definition TUri.h:73
static const TString PctDecodeUnreserved(const TString &source)
Percent-decode the given string according to chapter 2.1 we assume a valid pct-encoded string.
Definition TUri.cxx:1011
Bool_t HasAuthority() const
Definition TUri.h:93
Bool_t fHasPath
Definition TUri.h:67
Bool_t fHasUserinfo
Definition TUri.h:64
TString fHost
Definition TUri.h:57
const TString GetScheme() const
Definition TUri.h:80
static const TString PctDecode(const TString &source)
Percent-decode the given string according to chapter 2.1 we assume a valid pct-encoded string.
Definition TUri.cxx:1085
static Bool_t IsRegName(const TString &)
Returns kTRUE if string qualifies as valid reg-name:
Definition TUri.cxx:949
return c1
Definition legend1.C:41