tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / network / libnetapi / Url.cpp
blobe68b535d1e5677dfe52fbc91aee78558b744d53e
1 /*
2 * Copyright 2010 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Christophe Huriaux, c.huriaux@gmail.com
7 */
10 #include <Url.h>
12 #include <ctype.h>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <new>
17 #include <MimeType.h>
18 #include <Roster.h>
20 #include <ICUWrapper.h>
21 #include <RegExp.h>
23 #include <unicode/idna.h>
24 #include <unicode/stringpiece.h>
27 static const char* kArchivedUrl = "be:url string";
30 BUrl::BUrl(const char* url)
32 fUrlString(),
33 fProtocol(),
34 fUser(),
35 fPassword(),
36 fHost(),
37 fPort(0),
38 fPath(),
39 fRequest(),
40 fHasHost(false),
41 fHasFragment(false)
43 SetUrlString(url);
47 BUrl::BUrl(BMessage* archive)
49 fUrlString(),
50 fProtocol(),
51 fUser(),
52 fPassword(),
53 fHost(),
54 fPort(0),
55 fPath(),
56 fRequest(),
57 fHasHost(false),
58 fHasFragment(false)
60 BString url;
62 if (archive->FindString(kArchivedUrl, &url) == B_OK)
63 SetUrlString(url);
64 else
65 _ResetFields();
69 BUrl::BUrl(const BUrl& other)
71 BArchivable(),
72 fUrlString(),
73 fProtocol(other.fProtocol),
74 fUser(other.fUser),
75 fPassword(other.fPassword),
76 fHost(other.fHost),
77 fPort(other.fPort),
78 fPath(other.fPath),
79 fRequest(other.fRequest),
80 fFragment(other.fFragment),
81 fUrlStringValid(other.fUrlStringValid),
82 fAuthorityValid(other.fAuthorityValid),
83 fUserInfoValid(other.fUserInfoValid),
84 fHasProtocol(other.fHasProtocol),
85 fHasUserName(other.fHasUserName),
86 fHasPassword(other.fHasPassword),
87 fHasHost(other.fHasHost),
88 fHasPort(other.fHasPort),
89 fHasPath(other.fHasPath),
90 fHasRequest(other.fHasRequest),
91 fHasFragment(other.fHasFragment)
93 if (fUrlStringValid)
94 fUrlString = other.fUrlString;
96 if (fAuthorityValid)
97 fAuthority = other.fAuthority;
99 if (fUserInfoValid)
100 fUserInfo = other.fUserInfo;
105 BUrl::BUrl(const BUrl& base, const BString& location)
107 fUrlString(),
108 fProtocol(),
109 fUser(),
110 fPassword(),
111 fHost(),
112 fPort(0),
113 fPath(),
114 fRequest(),
115 fAuthorityValid(false),
116 fUserInfoValid(false),
117 fHasUserName(false),
118 fHasPassword(false),
119 fHasHost(false),
120 fHasPort(false),
121 fHasFragment(false)
123 // This implements the algorithm in RFC3986, Section 5.2.
125 BUrl relative(location);
126 if (relative.HasProtocol()) {
127 SetProtocol(relative.Protocol());
128 if (relative.HasAuthority())
129 SetAuthority(relative.Authority());
130 SetPath(relative.Path());
131 SetRequest(relative.Request());
132 } else {
133 if (relative.HasAuthority()) {
134 SetAuthority(relative.Authority());
135 SetPath(relative.Path());
136 SetRequest(relative.Request());
137 } else {
138 if (relative.Path().IsEmpty()) {
139 _SetPathUnsafe(base.Path());
140 if (relative.HasRequest())
141 SetRequest(relative.Request());
142 else
143 SetRequest(base.Request());
144 } else {
145 if (relative.Path()[0] == '/')
146 SetPath(relative.Path());
147 else {
148 BString path = base._MergePath(relative.Path());
149 SetPath(path);
151 SetRequest(relative.Request());
154 if (base.HasAuthority())
155 SetAuthority(base.Authority());
157 SetProtocol(base.Protocol());
160 if (relative.HasFragment())
161 SetFragment(relative.Fragment());
165 BUrl::BUrl()
167 fUrlString(),
168 fProtocol(),
169 fUser(),
170 fPassword(),
171 fHost(),
172 fPort(0),
173 fPath(),
174 fRequest(),
175 fHasHost(false),
176 fHasFragment(false)
178 _ResetFields();
182 BUrl::BUrl(const BPath& path)
184 fUrlString(),
185 fProtocol(),
186 fUser(),
187 fPassword(),
188 fHost(),
189 fPort(0),
190 fPath(),
191 fRequest(),
192 fHasHost(false),
193 fHasFragment(false)
195 SetUrlString(UrlEncode(path.Path(), true, true));
196 SetProtocol("file");
200 BUrl::~BUrl()
205 // #pragma mark URL fields modifiers
208 BUrl&
209 BUrl::SetUrlString(const BString& url)
211 _ExplodeUrlString(url);
212 return *this;
216 BUrl&
217 BUrl::SetProtocol(const BString& protocol)
219 fProtocol = protocol;
220 fHasProtocol = !fProtocol.IsEmpty();
221 fUrlStringValid = false;
222 return *this;
226 BUrl&
227 BUrl::SetUserName(const BString& user)
229 fUser = user;
230 fHasUserName = !fUser.IsEmpty();
231 fUrlStringValid = false;
232 fAuthorityValid = false;
233 fUserInfoValid = false;
234 return *this;
238 BUrl&
239 BUrl::SetPassword(const BString& password)
241 fPassword = password;
242 fHasPassword = !fPassword.IsEmpty();
243 fUrlStringValid = false;
244 fAuthorityValid = false;
245 fUserInfoValid = false;
246 return *this;
250 BUrl&
251 BUrl::SetHost(const BString& host)
253 fHost = host;
254 fHasHost = !fHost.IsEmpty();
255 fUrlStringValid = false;
256 fAuthorityValid = false;
257 return *this;
261 BUrl&
262 BUrl::SetPort(int port)
264 fPort = port;
265 fHasPort = (port != 0);
266 fUrlStringValid = false;
267 fAuthorityValid = false;
268 return *this;
272 BUrl&
273 BUrl::SetPath(const BString& path)
275 // Implements RFC3986 section 5.2.4, "Remove dot segments"
277 // 1.
278 BString output;
279 BString input(path);
281 // 2.
282 while(!input.IsEmpty())
284 // 2.A.
285 if (input.StartsWith("./"))
287 input.Remove(0, 2);
288 continue;
291 if (input.StartsWith("../"))
293 input.Remove(0, 3);
294 continue;
297 // 2.B.
298 if (input.StartsWith("/./"))
300 input.Remove(0, 2);
301 continue;
304 if (input == "/.")
306 input.Remove(1, 1);
307 continue;
310 // 2.C.
311 if (input.StartsWith("/../"))
313 input.Remove(0, 3);
314 output.Truncate(output.FindLast('/'));
315 continue;
318 if (input == "/..")
320 input.Remove(1, 2);
321 output.Truncate(output.FindLast('/'));
322 continue;
325 // 2.D.
326 if (input == "." || input == "..")
328 break;
331 if (input == "/.")
333 input.Remove(1, 1);
334 continue;
337 // 2.E.
338 int slashpos = input.FindFirst('/', 1);
339 if (slashpos > 0) {
340 output.Append(input, slashpos);
341 input.Remove(0, slashpos);
342 } else {
343 output.Append(input);
344 break;
348 _SetPathUnsafe(output);
349 return *this;
353 BUrl&
354 BUrl::SetRequest(const BString& request)
356 fRequest = request;
357 fHasRequest = !fRequest.IsEmpty();
358 fUrlStringValid = false;
359 return *this;
363 BUrl&
364 BUrl::SetFragment(const BString& fragment)
366 fFragment = fragment;
367 fHasFragment = true;
368 fUrlStringValid = false;
369 return *this;
373 // #pragma mark URL fields access
376 const BString&
377 BUrl::UrlString() const
379 if (!fUrlStringValid) {
380 fUrlString.Truncate(0);
382 if (HasProtocol()) {
383 fUrlString << fProtocol << ':';
384 if (HasAuthority())
385 fUrlString << "//";
388 fUrlString << Authority();
389 fUrlString << Path();
391 if (HasRequest())
392 fUrlString << '?' << fRequest;
394 if (HasFragment())
395 fUrlString << '#' << fFragment;
397 fUrlStringValid = true;
400 return fUrlString;
404 const BString&
405 BUrl::Protocol() const
407 return fProtocol;
411 const BString&
412 BUrl::UserName() const
414 return fUser;
418 const BString&
419 BUrl::Password() const
421 return fPassword;
425 const BString&
426 BUrl::UserInfo() const
428 if (!fUserInfoValid) {
429 fUserInfo = fUser;
431 if (HasPassword())
432 fUserInfo << ':' << fPassword;
434 fUserInfoValid = true;
437 return fUserInfo;
441 const BString&
442 BUrl::Host() const
444 return fHost;
449 BUrl::Port() const
451 return fPort;
455 const BString&
456 BUrl::Authority() const
458 if (!fAuthorityValid) {
459 fAuthority.Truncate(0);
461 if (HasUserInfo())
462 fAuthority << UserInfo() << '@';
463 fAuthority << Host();
465 if (HasPort())
466 fAuthority << ':' << fPort;
468 fAuthorityValid = true;
470 return fAuthority;
474 const BString&
475 BUrl::Path() const
477 return fPath;
481 const BString&
482 BUrl::Request() const
484 return fRequest;
488 const BString&
489 BUrl::Fragment() const
491 return fFragment;
495 // #pragma mark URL fields tests
498 bool
499 BUrl::IsValid() const
501 // TODO: Implement for real!
502 return fHasProtocol && (fHasHost || fHasPath);
506 bool
507 BUrl::HasProtocol() const
509 return fHasProtocol;
513 bool
514 BUrl::HasAuthority() const
516 return fHasHost || fHasUserName;
520 bool
521 BUrl::HasUserName() const
523 return fHasUserName;
527 bool
528 BUrl::HasPassword() const
530 return fHasPassword;
534 bool
535 BUrl::HasUserInfo() const
537 return fHasUserName || fHasPassword;
541 bool
542 BUrl::HasHost() const
544 return fHasHost;
548 bool
549 BUrl::HasPort() const
551 return fHasPort;
555 bool
556 BUrl::HasPath() const
558 return fHasPath;
562 bool
563 BUrl::HasRequest() const
565 return fHasRequest;
569 bool
570 BUrl::HasFragment() const
572 return fHasFragment;
576 // #pragma mark URL encoding/decoding of needed fields
579 void
580 BUrl::UrlEncode(bool strict)
582 fUser = _DoUrlEncodeChunk(fUser, strict);
583 fPassword = _DoUrlEncodeChunk(fPassword, strict);
584 fHost = _DoUrlEncodeChunk(fHost, strict);
585 fFragment = _DoUrlEncodeChunk(fFragment, strict);
586 fPath = _DoUrlEncodeChunk(fPath, strict, true);
590 void
591 BUrl::UrlDecode(bool strict)
593 fUser = _DoUrlDecodeChunk(fUser, strict);
594 fPassword = _DoUrlDecodeChunk(fPassword, strict);
595 fHost = _DoUrlDecodeChunk(fHost, strict);
596 fFragment = _DoUrlDecodeChunk(fFragment, strict);
597 fPath = _DoUrlDecodeChunk(fPath, strict);
601 status_t
602 BUrl::IDNAToAscii()
604 UErrorCode err = U_ZERO_ERROR;
605 icu::IDNA* converter = icu::IDNA::createUTS46Instance(0, err);
606 icu::IDNAInfo info;
608 BString result;
609 BStringByteSink sink(&result);
610 converter->nameToASCII_UTF8(icu::StringPiece(fHost.String()), sink, info,
611 err);
613 delete converter;
615 if (U_FAILURE(err))
616 return B_ERROR;
618 fHost = result;
619 return B_OK;
623 status_t
624 BUrl::IDNAToUnicode()
626 UErrorCode err = U_ZERO_ERROR;
627 icu::IDNA* converter = icu::IDNA::createUTS46Instance(0, err);
628 icu::IDNAInfo info;
630 BString result;
631 BStringByteSink sink(&result);
632 converter->nameToUnicodeUTF8(icu::StringPiece(fHost.String()), sink, info,
633 err);
635 delete converter;
637 if (U_FAILURE(err))
638 return B_ERROR;
640 fHost = result;
641 return B_OK;
645 // #pragma mark - utility functionality
648 bool
649 BUrl::HasPreferredApplication() const
651 BString appSignature = PreferredApplication();
652 BMimeType mime(appSignature.String());
654 if (appSignature.IFindFirst("application/") == 0
655 && mime.IsValid())
656 return true;
658 return false;
662 BString
663 BUrl::PreferredApplication() const
665 BString appSignature;
666 BMimeType mime(_UrlMimeType().String());
667 mime.GetPreferredApp(appSignature.LockBuffer(B_MIME_TYPE_LENGTH));
668 appSignature.UnlockBuffer();
670 return BString(appSignature);
674 status_t
675 BUrl::OpenWithPreferredApplication(bool onProblemAskUser) const
677 if (!IsValid())
678 return B_BAD_VALUE;
680 BString urlString = UrlString();
681 if (urlString.Length() > B_PATH_NAME_LENGTH) {
682 // TODO: BAlert
683 // if (onProblemAskUser)
684 // BAlert ... Too long URL!
685 #if DEBUG
686 fprintf(stderr, "URL too long");
687 #endif
688 return B_NAME_TOO_LONG;
691 char* argv[] = {
692 const_cast<char*>("BUrlInvokedApplication"),
693 const_cast<char*>(urlString.String()),
694 NULL
697 #if DEBUG
698 if (HasPreferredApplication())
699 printf("HasPreferredApplication() == true\n");
700 else
701 printf("HasPreferredApplication() == false\n");
702 #endif
704 status_t status = be_roster->Launch(_UrlMimeType().String(), 1, argv+1);
705 if (status != B_OK) {
706 #if DEBUG
707 fprintf(stderr, "Opening URL failed: %s\n", strerror(status));
708 #endif
711 return status;
715 // #pragma mark Url encoding/decoding of string
718 /*static*/ BString
719 BUrl::UrlEncode(const BString& url, bool strict, bool directory)
721 return _DoUrlEncodeChunk(url, strict, directory);
725 /*static*/ BString
726 BUrl::UrlDecode(const BString& url, bool strict)
728 return _DoUrlDecodeChunk(url, strict);
732 // #pragma mark BArchivable members
735 status_t
736 BUrl::Archive(BMessage* into, bool deep) const
738 status_t ret = BArchivable::Archive(into, deep);
740 if (ret == B_OK)
741 ret = into->AddString(kArchivedUrl, UrlString());
743 return ret;
747 /*static*/ BArchivable*
748 BUrl::Instantiate(BMessage* archive)
750 if (validate_instantiation(archive, "BUrl"))
751 return new(std::nothrow) BUrl(archive);
752 return NULL;
756 // #pragma mark URL comparison
759 bool
760 BUrl::operator==(BUrl& other) const
762 UrlString();
763 other.UrlString();
765 return fUrlString == other.fUrlString;
769 bool
770 BUrl::operator!=(BUrl& other) const
772 return !(*this == other);
776 // #pragma mark URL assignment
779 const BUrl&
780 BUrl::operator=(const BUrl& other)
782 fUrlStringValid = other.fUrlStringValid;
783 if (fUrlStringValid)
784 fUrlString = other.fUrlString;
786 fAuthorityValid = other.fAuthorityValid;
787 if (fAuthorityValid)
788 fAuthority = other.fAuthority;
790 fUserInfoValid = other.fUserInfoValid;
791 if (fUserInfoValid)
792 fUserInfo = other.fUserInfo;
794 fProtocol = other.fProtocol;
795 fUser = other.fUser;
796 fPassword = other.fPassword;
797 fHost = other.fHost;
798 fPort = other.fPort;
799 fPath = other.fPath;
800 fRequest = other.fRequest;
801 fFragment = other.fFragment;
803 fHasProtocol = other.fHasProtocol;
804 fHasUserName = other.fHasUserName;
805 fHasPassword = other.fHasPassword;
806 fHasHost = other.fHasHost;
807 fHasPort = other.fHasPort;
808 fHasPath = other.fHasPath;
809 fHasRequest = other.fHasRequest;
810 fHasFragment = other.fHasFragment;
812 return *this;
816 const BUrl&
817 BUrl::operator=(const BString& string)
819 SetUrlString(string);
820 return *this;
824 const BUrl&
825 BUrl::operator=(const char* string)
827 SetUrlString(string);
828 return *this;
832 // #pragma mark URL to string conversion
835 BUrl::operator const char*() const
837 return UrlString();
841 void
842 BUrl::_ResetFields()
844 fHasProtocol = false;
845 fHasUserName = false;
846 fHasPassword = false;
847 fHasHost = false;
848 fHasPort = false;
849 fHasPath = false;
850 fHasRequest = false;
851 fHasFragment = false;
853 fProtocol.Truncate(0);
854 fUser.Truncate(0);
855 fPassword.Truncate(0);
856 fHost.Truncate(0);
857 fPort = 0;
858 fPath.Truncate(0);
859 fRequest.Truncate(0);
860 fFragment.Truncate(0);
862 // Force re-generation of these fields
863 fUrlStringValid = false;
864 fUserInfoValid = false;
865 fAuthorityValid = false;
869 void
870 BUrl::_ExplodeUrlString(const BString& url)
872 // The regexp is provided in RFC3986 (URI generic syntax), Appendix B
873 static RegExp urlMatcher(
874 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?");
876 _ResetFields();
878 RegExp::MatchResult match = urlMatcher.Match(url.String());
880 if (!match.HasMatched())
881 return; // TODO error reporting
883 // Scheme/Protocol
884 url.CopyInto(fProtocol, match.GroupStartOffsetAt(1),
885 match.GroupEndOffsetAt(1) - match.GroupStartOffsetAt(1));
886 if (!_IsProtocolValid()) {
887 fHasProtocol = false;
888 fProtocol.Truncate(0);
889 } else
890 fHasProtocol = true;
892 // Authority (including user credentials, host, and port
893 if (match.GroupEndOffsetAt(2) - match.GroupStartOffsetAt(2) > 0)
895 url.CopyInto(fAuthority, match.GroupStartOffsetAt(3),
896 match.GroupEndOffsetAt(3) - match.GroupStartOffsetAt(3));
897 SetAuthority(fAuthority);
898 } else {
899 fHasHost = false;
900 fHasPort = false;
901 fHasUserName = false;
902 fHasPassword = false;
905 // Path
906 url.CopyInto(fPath, match.GroupStartOffsetAt(4),
907 match.GroupEndOffsetAt(4) - match.GroupStartOffsetAt(4));
908 if (!fPath.IsEmpty())
909 fHasPath = true;
911 // Query
912 if (match.GroupEndOffsetAt(5) - match.GroupStartOffsetAt(5) > 0)
914 url.CopyInto(fRequest, match.GroupStartOffsetAt(6),
915 match.GroupEndOffsetAt(6) - match.GroupStartOffsetAt(6));
916 fHasRequest = true;
917 } else {
918 fRequest = "";
919 fHasRequest = false;
922 // Fragment
923 if (match.GroupEndOffsetAt(7) - match.GroupStartOffsetAt(7) > 0)
925 url.CopyInto(fFragment, match.GroupStartOffsetAt(8),
926 match.GroupEndOffsetAt(8) - match.GroupStartOffsetAt(8));
927 fHasFragment = true;
928 } else {
929 fFragment = "";
930 fHasFragment = false;
935 BString
936 BUrl::_MergePath(const BString& relative) const
938 // This implements RFC3986, Section 5.2.3.
939 if (HasAuthority() && fPath == "")
941 BString result("/");
942 result << relative;
943 return result;
946 BString result(fPath);
947 result.Truncate(result.FindLast("/") + 1);
948 result << relative;
950 return result;
954 // This sets the path without normalizing it. If fed with a path that has . or
955 // .. segments, this would make the URL invalid.
956 void
957 BUrl::_SetPathUnsafe(const BString& path)
959 fPath = path;
960 fHasPath = true; // RFC says an empty path is still a path
961 fUrlStringValid = false;
965 void
966 BUrl::SetAuthority(const BString& authority)
968 fAuthority = authority;
970 fHasPort = false;
971 fHasUserName = false;
972 fHasPassword = false;
974 // An empty authority is still an authority, making it possible to have
975 // URLs such as file:///path/to/file.
976 // TODO however, there is no way to unset the authority once it is set...
977 // We may want to take a const char* parameter and allow NULL.
978 fHasHost = true;
980 if (fAuthority.IsEmpty())
981 return;
983 int32 userInfoEnd = fAuthority.FindFirst('@');
985 // URL contains userinfo field
986 if (userInfoEnd != -1) {
987 BString userInfo;
988 fAuthority.CopyInto(userInfo, 0, userInfoEnd);
990 int16 colonDelimiter = userInfo.FindFirst(':', 0);
992 if (colonDelimiter == 0) {
993 SetPassword(userInfo);
994 } else if (colonDelimiter != -1) {
995 userInfo.CopyInto(fUser, 0, colonDelimiter);
996 userInfo.CopyInto(fPassword, colonDelimiter + 1,
997 userInfo.Length() - colonDelimiter);
998 SetUserName(fUser);
999 SetPassword(fPassword);
1000 } else {
1001 SetUserName(fUser);
1006 // Extract the host part
1007 int16 hostEnd = fAuthority.FindFirst(':', userInfoEnd);
1008 userInfoEnd++;
1010 if (hostEnd < 0) {
1011 // no ':' found, the host extends to the end of the URL
1012 hostEnd = fAuthority.Length() + 1;
1015 // The host is likely to be present if an authority is
1016 // defined, but in some weird cases, it's not.
1017 if (hostEnd != userInfoEnd) {
1018 fAuthority.CopyInto(fHost, userInfoEnd, hostEnd - userInfoEnd);
1019 SetHost(fHost);
1022 // Extract the port part
1023 fPort = 0;
1024 if (fAuthority.ByteAt(hostEnd) == ':') {
1025 hostEnd++;
1026 int16 portEnd = fAuthority.Length();
1028 BString portString;
1029 fAuthority.CopyInto(portString, hostEnd, portEnd - hostEnd);
1030 fPort = atoi(portString.String());
1032 // Even if the port is invalid, the URL is considered to
1033 // have a port.
1034 fHasPort = portString.Length() > 0;
1039 /*static*/ BString
1040 BUrl::_DoUrlEncodeChunk(const BString& chunk, bool strict, bool directory)
1042 BString result;
1044 for (int32 i = 0; i < chunk.Length(); i++) {
1045 if (_IsUnreserved(chunk[i])
1046 || (directory && (chunk[i] == '/' || chunk[i] == '\\'))) {
1047 result << chunk[i];
1048 } else {
1049 if (chunk[i] == ' ' && !strict) {
1050 result << '+';
1051 // In non-strict mode, spaces are encoded by a plus sign
1052 } else {
1053 char hexString[5];
1054 snprintf(hexString, 5, "%X", chunk[i]);
1056 result << '%' << hexString;
1061 return result;
1065 /*static*/ BString
1066 BUrl::_DoUrlDecodeChunk(const BString& chunk, bool strict)
1068 BString result;
1070 for (int32 i = 0; i < chunk.Length(); i++) {
1071 if (chunk[i] == '+' && !strict)
1072 result << ' ';
1073 else {
1074 char decoded = 0;
1075 char* out = NULL;
1076 char hexString[3];
1078 if (chunk[i] == '%' && i < chunk.Length() - 2
1079 && isxdigit(chunk[i + 1]) && isxdigit(chunk[i+2])) {
1080 hexString[0] = chunk[i + 1];
1081 hexString[1] = chunk[i + 2];
1082 hexString[2] = 0;
1083 decoded = (char)strtol(hexString, &out, 16);
1086 if (out == hexString + 2) {
1087 i += 2;
1088 result << decoded;
1089 } else
1090 result << chunk[i];
1093 return result;
1097 bool
1098 BUrl::_IsProtocolValid()
1100 for (int8 index = 0; index < fProtocol.Length(); index++) {
1101 char c = fProtocol[index];
1103 if (index == 0 && !isalpha(c))
1104 return false;
1105 else if (!isalnum(c) && c != '+' && c != '-' && c != '.')
1106 return false;
1109 return fProtocol.Length() > 0;
1113 bool
1114 BUrl::_IsUnreserved(char c)
1116 return isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~';
1120 bool
1121 BUrl::_IsGenDelim(char c)
1123 return c == ':' || c == '/' || c == '?' || c == '#' || c == '['
1124 || c == ']' || c == '@';
1128 bool
1129 BUrl::_IsSubDelim(char c)
1131 return c == '!' || c == '$' || c == '&' || c == '\'' || c == '('
1132 || c == ')' || c == '*' || c == '+' || c == ',' || c == ';'
1133 || c == '=';
1137 BString
1138 BUrl::_UrlMimeType() const
1140 BString mime;
1141 mime << "application/x-vnd.Be.URL." << fProtocol;
1143 return BString(mime);