xref: /haiku/src/kits/support/Url.cpp (revision 3cc5e76f2d754d213cb896d1cc0dea5d43854b13)
12c26ad4bSAdrien Destugues /*
28f30879bSAndrew Lindesay  * Copyright 2010-2018 Haiku Inc. All rights reserved.
32c26ad4bSAdrien Destugues  * Distributed under the terms of the MIT License.
42c26ad4bSAdrien Destugues  *
52c26ad4bSAdrien Destugues  * Authors:
62c26ad4bSAdrien Destugues  *		Christophe Huriaux, c.huriaux@gmail.com
72c26ad4bSAdrien Destugues  *		Andrew Lindesay, apl@lindesay.co.nz
82c26ad4bSAdrien Destugues  */
92c26ad4bSAdrien Destugues 
102c26ad4bSAdrien Destugues 
112c26ad4bSAdrien Destugues #include <Url.h>
122c26ad4bSAdrien Destugues 
132c26ad4bSAdrien Destugues #include <ctype.h>
142c26ad4bSAdrien Destugues #include <cstdio>
152c26ad4bSAdrien Destugues #include <cstdlib>
162c26ad4bSAdrien Destugues #include <new>
172c26ad4bSAdrien Destugues 
182c26ad4bSAdrien Destugues #include <MimeType.h>
192c26ad4bSAdrien Destugues #include <Roster.h>
202c26ad4bSAdrien Destugues 
212c26ad4bSAdrien Destugues #ifdef HAIKU_TARGET_PLATFORM_HAIKU
222c26ad4bSAdrien Destugues 	#include <ICUWrapper.h>
232c26ad4bSAdrien Destugues #endif
242c26ad4bSAdrien Destugues 
252c26ad4bSAdrien Destugues #ifdef HAIKU_TARGET_PLATFORM_HAIKU
262c26ad4bSAdrien Destugues 	#include <unicode/idna.h>
272c26ad4bSAdrien Destugues 	#include <unicode/stringpiece.h>
282c26ad4bSAdrien Destugues #endif
292c26ad4bSAdrien Destugues 
302c26ad4bSAdrien Destugues 
312c26ad4bSAdrien Destugues static const char* kArchivedUrl = "be:url string";
322c26ad4bSAdrien Destugues 
332c26ad4bSAdrien Destugues 
342c26ad4bSAdrien Destugues BUrl::BUrl(const char* url)
352c26ad4bSAdrien Destugues 	:
362c26ad4bSAdrien Destugues 	fUrlString(),
372c26ad4bSAdrien Destugues 	fProtocol(),
382c26ad4bSAdrien Destugues 	fUser(),
392c26ad4bSAdrien Destugues 	fPassword(),
402c26ad4bSAdrien Destugues 	fHost(),
412c26ad4bSAdrien Destugues 	fPort(0),
422c26ad4bSAdrien Destugues 	fPath(),
432c26ad4bSAdrien Destugues 	fRequest(),
442c26ad4bSAdrien Destugues 	fHasHost(false),
452c26ad4bSAdrien Destugues 	fHasFragment(false)
462c26ad4bSAdrien Destugues {
472c26ad4bSAdrien Destugues 	SetUrlString(url);
482c26ad4bSAdrien Destugues }
492c26ad4bSAdrien Destugues 
502c26ad4bSAdrien Destugues 
512c26ad4bSAdrien Destugues BUrl::BUrl(BMessage* archive)
522c26ad4bSAdrien Destugues 	:
532c26ad4bSAdrien Destugues 	fUrlString(),
542c26ad4bSAdrien Destugues 	fProtocol(),
552c26ad4bSAdrien Destugues 	fUser(),
562c26ad4bSAdrien Destugues 	fPassword(),
572c26ad4bSAdrien Destugues 	fHost(),
582c26ad4bSAdrien Destugues 	fPort(0),
592c26ad4bSAdrien Destugues 	fPath(),
602c26ad4bSAdrien Destugues 	fRequest(),
612c26ad4bSAdrien Destugues 	fHasHost(false),
622c26ad4bSAdrien Destugues 	fHasFragment(false)
632c26ad4bSAdrien Destugues {
642c26ad4bSAdrien Destugues 	BString url;
652c26ad4bSAdrien Destugues 
662c26ad4bSAdrien Destugues 	if (archive->FindString(kArchivedUrl, &url) == B_OK)
672c26ad4bSAdrien Destugues 		SetUrlString(url);
682c26ad4bSAdrien Destugues 	else
692c26ad4bSAdrien Destugues 		_ResetFields();
702c26ad4bSAdrien Destugues }
712c26ad4bSAdrien Destugues 
722c26ad4bSAdrien Destugues 
732c26ad4bSAdrien Destugues BUrl::BUrl(const BUrl& other)
742c26ad4bSAdrien Destugues 	:
752c26ad4bSAdrien Destugues 	BArchivable(),
762c26ad4bSAdrien Destugues 	fUrlString(),
772c26ad4bSAdrien Destugues 	fProtocol(other.fProtocol),
782c26ad4bSAdrien Destugues 	fUser(other.fUser),
792c26ad4bSAdrien Destugues 	fPassword(other.fPassword),
802c26ad4bSAdrien Destugues 	fHost(other.fHost),
812c26ad4bSAdrien Destugues 	fPort(other.fPort),
822c26ad4bSAdrien Destugues 	fPath(other.fPath),
832c26ad4bSAdrien Destugues 	fRequest(other.fRequest),
842c26ad4bSAdrien Destugues 	fFragment(other.fFragment),
852c26ad4bSAdrien Destugues 	fUrlStringValid(other.fUrlStringValid),
862c26ad4bSAdrien Destugues 	fAuthorityValid(other.fAuthorityValid),
872c26ad4bSAdrien Destugues 	fUserInfoValid(other.fUserInfoValid),
882c26ad4bSAdrien Destugues 	fHasProtocol(other.fHasProtocol),
892c26ad4bSAdrien Destugues 	fHasUserName(other.fHasUserName),
902c26ad4bSAdrien Destugues 	fHasPassword(other.fHasPassword),
912c26ad4bSAdrien Destugues 	fHasHost(other.fHasHost),
922c26ad4bSAdrien Destugues 	fHasPort(other.fHasPort),
932c26ad4bSAdrien Destugues 	fHasPath(other.fHasPath),
942c26ad4bSAdrien Destugues 	fHasRequest(other.fHasRequest),
952c26ad4bSAdrien Destugues 	fHasFragment(other.fHasFragment)
962c26ad4bSAdrien Destugues {
972c26ad4bSAdrien Destugues 	if (fUrlStringValid)
982c26ad4bSAdrien Destugues 		fUrlString = other.fUrlString;
992c26ad4bSAdrien Destugues 
1002c26ad4bSAdrien Destugues 	if (fAuthorityValid)
1012c26ad4bSAdrien Destugues 		fAuthority = other.fAuthority;
1022c26ad4bSAdrien Destugues 
1032c26ad4bSAdrien Destugues 	if (fUserInfoValid)
1042c26ad4bSAdrien Destugues 		fUserInfo = other.fUserInfo;
1052c26ad4bSAdrien Destugues 
1062c26ad4bSAdrien Destugues }
1072c26ad4bSAdrien Destugues 
1082c26ad4bSAdrien Destugues 
1092c26ad4bSAdrien Destugues BUrl::BUrl(const BUrl& base, const BString& location)
1102c26ad4bSAdrien Destugues 	:
1112c26ad4bSAdrien Destugues 	fUrlString(),
1122c26ad4bSAdrien Destugues 	fProtocol(),
1132c26ad4bSAdrien Destugues 	fUser(),
1142c26ad4bSAdrien Destugues 	fPassword(),
1152c26ad4bSAdrien Destugues 	fHost(),
1162c26ad4bSAdrien Destugues 	fPort(0),
1172c26ad4bSAdrien Destugues 	fPath(),
1182c26ad4bSAdrien Destugues 	fRequest(),
1192c26ad4bSAdrien Destugues 	fAuthorityValid(false),
1202c26ad4bSAdrien Destugues 	fUserInfoValid(false),
1212c26ad4bSAdrien Destugues 	fHasUserName(false),
1222c26ad4bSAdrien Destugues 	fHasPassword(false),
1232c26ad4bSAdrien Destugues 	fHasHost(false),
1242c26ad4bSAdrien Destugues 	fHasPort(false),
1252c26ad4bSAdrien Destugues 	fHasFragment(false)
1262c26ad4bSAdrien Destugues {
1272c26ad4bSAdrien Destugues 	// This implements the algorithm in RFC3986, Section 5.2.
1282c26ad4bSAdrien Destugues 
1292c26ad4bSAdrien Destugues 	BUrl relative(location);
1302c26ad4bSAdrien Destugues 	if (relative.HasProtocol()) {
1312c26ad4bSAdrien Destugues 		SetProtocol(relative.Protocol());
1322c26ad4bSAdrien Destugues 		if (relative.HasAuthority())
1332c26ad4bSAdrien Destugues 			SetAuthority(relative.Authority());
1342c26ad4bSAdrien Destugues 		SetPath(relative.Path());
1352c26ad4bSAdrien Destugues 		SetRequest(relative.Request());
1362c26ad4bSAdrien Destugues 	} else {
1372c26ad4bSAdrien Destugues 		if (relative.HasAuthority()) {
1382c26ad4bSAdrien Destugues 			SetAuthority(relative.Authority());
1392c26ad4bSAdrien Destugues 			SetPath(relative.Path());
1402c26ad4bSAdrien Destugues 			SetRequest(relative.Request());
1412c26ad4bSAdrien Destugues 		} else {
1422c26ad4bSAdrien Destugues 			if (relative.Path().IsEmpty()) {
1432c26ad4bSAdrien Destugues 				_SetPathUnsafe(base.Path());
1442c26ad4bSAdrien Destugues 				if (relative.HasRequest())
1452c26ad4bSAdrien Destugues 					SetRequest(relative.Request());
1462c26ad4bSAdrien Destugues 				else
1472c26ad4bSAdrien Destugues 					SetRequest(base.Request());
1482c26ad4bSAdrien Destugues 			} else {
1492c26ad4bSAdrien Destugues 				if (relative.Path()[0] == '/')
1502c26ad4bSAdrien Destugues 					SetPath(relative.Path());
1512c26ad4bSAdrien Destugues 				else {
1522c26ad4bSAdrien Destugues 					BString path = base._MergePath(relative.Path());
1532c26ad4bSAdrien Destugues 					SetPath(path);
1542c26ad4bSAdrien Destugues 				}
1552c26ad4bSAdrien Destugues 				SetRequest(relative.Request());
1562c26ad4bSAdrien Destugues 			}
1572c26ad4bSAdrien Destugues 
1582c26ad4bSAdrien Destugues 			if (base.HasAuthority())
1592c26ad4bSAdrien Destugues 				SetAuthority(base.Authority());
1602c26ad4bSAdrien Destugues 		}
1612c26ad4bSAdrien Destugues 		SetProtocol(base.Protocol());
1622c26ad4bSAdrien Destugues 	}
1632c26ad4bSAdrien Destugues 
1642c26ad4bSAdrien Destugues 	if (relative.HasFragment())
1652c26ad4bSAdrien Destugues 		SetFragment(relative.Fragment());
1662c26ad4bSAdrien Destugues }
1672c26ad4bSAdrien Destugues 
1682c26ad4bSAdrien Destugues 
1692c26ad4bSAdrien Destugues BUrl::BUrl()
1702c26ad4bSAdrien Destugues 	:
1712c26ad4bSAdrien Destugues 	fUrlString(),
1722c26ad4bSAdrien Destugues 	fProtocol(),
1732c26ad4bSAdrien Destugues 	fUser(),
1742c26ad4bSAdrien Destugues 	fPassword(),
1752c26ad4bSAdrien Destugues 	fHost(),
1762c26ad4bSAdrien Destugues 	fPort(0),
1772c26ad4bSAdrien Destugues 	fPath(),
1782c26ad4bSAdrien Destugues 	fRequest(),
1792c26ad4bSAdrien Destugues 	fHasHost(false),
1802c26ad4bSAdrien Destugues 	fHasFragment(false)
1812c26ad4bSAdrien Destugues {
1822c26ad4bSAdrien Destugues 	_ResetFields();
1832c26ad4bSAdrien Destugues }
1842c26ad4bSAdrien Destugues 
1852c26ad4bSAdrien Destugues 
1862c26ad4bSAdrien Destugues BUrl::BUrl(const BPath& path)
1872c26ad4bSAdrien Destugues 	:
1882c26ad4bSAdrien Destugues 	fUrlString(),
1892c26ad4bSAdrien Destugues 	fProtocol(),
1902c26ad4bSAdrien Destugues 	fUser(),
1912c26ad4bSAdrien Destugues 	fPassword(),
1922c26ad4bSAdrien Destugues 	fHost(),
1932c26ad4bSAdrien Destugues 	fPort(0),
1942c26ad4bSAdrien Destugues 	fPath(),
1952c26ad4bSAdrien Destugues 	fRequest(),
1962c26ad4bSAdrien Destugues 	fHasHost(false),
1972c26ad4bSAdrien Destugues 	fHasFragment(false)
1982c26ad4bSAdrien Destugues {
1992c26ad4bSAdrien Destugues 	SetUrlString(UrlEncode(path.Path(), true, true));
2002c26ad4bSAdrien Destugues 	SetProtocol("file");
2012c26ad4bSAdrien Destugues }
2022c26ad4bSAdrien Destugues 
2032c26ad4bSAdrien Destugues 
2042c26ad4bSAdrien Destugues BUrl::~BUrl()
2052c26ad4bSAdrien Destugues {
2062c26ad4bSAdrien Destugues }
2072c26ad4bSAdrien Destugues 
2082c26ad4bSAdrien Destugues 
2092c26ad4bSAdrien Destugues // #pragma mark URL fields modifiers
2102c26ad4bSAdrien Destugues 
2112c26ad4bSAdrien Destugues 
2122c26ad4bSAdrien Destugues BUrl&
2132c26ad4bSAdrien Destugues BUrl::SetUrlString(const BString& url)
2142c26ad4bSAdrien Destugues {
2152c26ad4bSAdrien Destugues 	_ExplodeUrlString(url);
2162c26ad4bSAdrien Destugues 	return *this;
2172c26ad4bSAdrien Destugues }
2182c26ad4bSAdrien Destugues 
2192c26ad4bSAdrien Destugues 
2202c26ad4bSAdrien Destugues BUrl&
2212c26ad4bSAdrien Destugues BUrl::SetProtocol(const BString& protocol)
2222c26ad4bSAdrien Destugues {
2232c26ad4bSAdrien Destugues 	fProtocol = protocol;
2242c26ad4bSAdrien Destugues 	fHasProtocol = !fProtocol.IsEmpty();
2252c26ad4bSAdrien Destugues 	fUrlStringValid = false;
2262c26ad4bSAdrien Destugues 	return *this;
2272c26ad4bSAdrien Destugues }
2282c26ad4bSAdrien Destugues 
2292c26ad4bSAdrien Destugues 
2302c26ad4bSAdrien Destugues BUrl&
2312c26ad4bSAdrien Destugues BUrl::SetUserName(const BString& user)
2322c26ad4bSAdrien Destugues {
2332c26ad4bSAdrien Destugues 	fUser = user;
2342c26ad4bSAdrien Destugues 	fHasUserName = !fUser.IsEmpty();
2352c26ad4bSAdrien Destugues 	fUrlStringValid = false;
2362c26ad4bSAdrien Destugues 	fAuthorityValid = false;
2372c26ad4bSAdrien Destugues 	fUserInfoValid = false;
2382c26ad4bSAdrien Destugues 	return *this;
2392c26ad4bSAdrien Destugues }
2402c26ad4bSAdrien Destugues 
2412c26ad4bSAdrien Destugues 
2422c26ad4bSAdrien Destugues BUrl&
2432c26ad4bSAdrien Destugues BUrl::SetPassword(const BString& password)
2442c26ad4bSAdrien Destugues {
2452c26ad4bSAdrien Destugues 	fPassword = password;
2462c26ad4bSAdrien Destugues 	fHasPassword = !fPassword.IsEmpty();
2472c26ad4bSAdrien Destugues 	fUrlStringValid = false;
2482c26ad4bSAdrien Destugues 	fAuthorityValid = false;
2492c26ad4bSAdrien Destugues 	fUserInfoValid = false;
2502c26ad4bSAdrien Destugues 	return *this;
2512c26ad4bSAdrien Destugues }
2522c26ad4bSAdrien Destugues 
2532c26ad4bSAdrien Destugues 
2542c26ad4bSAdrien Destugues BUrl&
2552c26ad4bSAdrien Destugues BUrl::SetHost(const BString& host)
2562c26ad4bSAdrien Destugues {
2572c26ad4bSAdrien Destugues 	fHost = host;
2582c26ad4bSAdrien Destugues 	fHasHost = !fHost.IsEmpty();
2592c26ad4bSAdrien Destugues 	fUrlStringValid = false;
2602c26ad4bSAdrien Destugues 	fAuthorityValid = false;
2612c26ad4bSAdrien Destugues 	return *this;
2622c26ad4bSAdrien Destugues }
2632c26ad4bSAdrien Destugues 
2642c26ad4bSAdrien Destugues 
2652c26ad4bSAdrien Destugues BUrl&
2662c26ad4bSAdrien Destugues BUrl::SetPort(int port)
2672c26ad4bSAdrien Destugues {
2682c26ad4bSAdrien Destugues 	fPort = port;
2692c26ad4bSAdrien Destugues 	fHasPort = (port != 0);
2702c26ad4bSAdrien Destugues 	fUrlStringValid = false;
2712c26ad4bSAdrien Destugues 	fAuthorityValid = false;
2722c26ad4bSAdrien Destugues 	return *this;
2732c26ad4bSAdrien Destugues }
2742c26ad4bSAdrien Destugues 
2752c26ad4bSAdrien Destugues 
2762c26ad4bSAdrien Destugues BUrl&
2772c26ad4bSAdrien Destugues BUrl::SetPath(const BString& path)
2782c26ad4bSAdrien Destugues {
2792c26ad4bSAdrien Destugues 	// Implements RFC3986 section 5.2.4, "Remove dot segments"
2802c26ad4bSAdrien Destugues 
2812c26ad4bSAdrien Destugues 	// 1.
2822c26ad4bSAdrien Destugues 	BString output;
2832c26ad4bSAdrien Destugues 	BString input(path);
2842c26ad4bSAdrien Destugues 
2852c26ad4bSAdrien Destugues 	// 2.
286cd6365c7SJérôme Duval 	while (!input.IsEmpty()) {
2872c26ad4bSAdrien Destugues 		// 2.A.
288cd6365c7SJérôme Duval 		if (input.StartsWith("./")) {
2892c26ad4bSAdrien Destugues 			input.Remove(0, 2);
2902c26ad4bSAdrien Destugues 			continue;
2912c26ad4bSAdrien Destugues 		}
2922c26ad4bSAdrien Destugues 
293cd6365c7SJérôme Duval 		if (input.StartsWith("../")) {
2942c26ad4bSAdrien Destugues 			input.Remove(0, 3);
2952c26ad4bSAdrien Destugues 			continue;
2962c26ad4bSAdrien Destugues 		}
2972c26ad4bSAdrien Destugues 
2982c26ad4bSAdrien Destugues 		// 2.B.
299cd6365c7SJérôme Duval 		if (input.StartsWith("/./")) {
3002c26ad4bSAdrien Destugues 			input.Remove(0, 2);
3012c26ad4bSAdrien Destugues 			continue;
3022c26ad4bSAdrien Destugues 		}
3032c26ad4bSAdrien Destugues 
304cd6365c7SJérôme Duval 		if (input == "/.") {
3052c26ad4bSAdrien Destugues 			input.Remove(1, 1);
3062c26ad4bSAdrien Destugues 			continue;
3072c26ad4bSAdrien Destugues 		}
3082c26ad4bSAdrien Destugues 
3092c26ad4bSAdrien Destugues 		// 2.C.
310cd6365c7SJérôme Duval 		if (input.StartsWith("/../")) {
3112c26ad4bSAdrien Destugues 			input.Remove(0, 3);
3122c26ad4bSAdrien Destugues 			output.Truncate(output.FindLast('/'));
3132c26ad4bSAdrien Destugues 			continue;
3142c26ad4bSAdrien Destugues 		}
3152c26ad4bSAdrien Destugues 
316cd6365c7SJérôme Duval 		if (input == "/..") {
3172c26ad4bSAdrien Destugues 			input.Remove(1, 2);
3182c26ad4bSAdrien Destugues 			output.Truncate(output.FindLast('/'));
3192c26ad4bSAdrien Destugues 			continue;
3202c26ad4bSAdrien Destugues 		}
3212c26ad4bSAdrien Destugues 
3222c26ad4bSAdrien Destugues 		// 2.D.
323cd6365c7SJérôme Duval 		if (input == "." || input == "..") {
3242c26ad4bSAdrien Destugues 			break;
3252c26ad4bSAdrien Destugues 		}
3262c26ad4bSAdrien Destugues 
327cd6365c7SJérôme Duval 		if (input == "/.") {
3282c26ad4bSAdrien Destugues 			input.Remove(1, 1);
3292c26ad4bSAdrien Destugues 			continue;
3302c26ad4bSAdrien Destugues 		}
3312c26ad4bSAdrien Destugues 
3322c26ad4bSAdrien Destugues 		// 2.E.
3332c26ad4bSAdrien Destugues 		int slashpos = input.FindFirst('/', 1);
3342c26ad4bSAdrien Destugues 		if (slashpos > 0) {
3352c26ad4bSAdrien Destugues 			output.Append(input, slashpos);
3362c26ad4bSAdrien Destugues 			input.Remove(0, slashpos);
3372c26ad4bSAdrien Destugues 		} else {
3382c26ad4bSAdrien Destugues 			output.Append(input);
3392c26ad4bSAdrien Destugues 			break;
3402c26ad4bSAdrien Destugues 		}
3412c26ad4bSAdrien Destugues 	}
3422c26ad4bSAdrien Destugues 
3432c26ad4bSAdrien Destugues 	_SetPathUnsafe(output);
3442c26ad4bSAdrien Destugues 	return *this;
3452c26ad4bSAdrien Destugues }
3462c26ad4bSAdrien Destugues 
3472c26ad4bSAdrien Destugues 
3482c26ad4bSAdrien Destugues BUrl&
3492c26ad4bSAdrien Destugues BUrl::SetRequest(const BString& request)
3502c26ad4bSAdrien Destugues {
3512c26ad4bSAdrien Destugues 	fRequest = request;
3522c26ad4bSAdrien Destugues 	fHasRequest = !fRequest.IsEmpty();
3532c26ad4bSAdrien Destugues 	fUrlStringValid = false;
3542c26ad4bSAdrien Destugues 	return *this;
3552c26ad4bSAdrien Destugues }
3562c26ad4bSAdrien Destugues 
3572c26ad4bSAdrien Destugues 
3582c26ad4bSAdrien Destugues BUrl&
3592c26ad4bSAdrien Destugues BUrl::SetFragment(const BString& fragment)
3602c26ad4bSAdrien Destugues {
3612c26ad4bSAdrien Destugues 	fFragment = fragment;
3622c26ad4bSAdrien Destugues 	fHasFragment = true;
3632c26ad4bSAdrien Destugues 	fUrlStringValid = false;
3642c26ad4bSAdrien Destugues 	return *this;
3652c26ad4bSAdrien Destugues }
3662c26ad4bSAdrien Destugues 
3672c26ad4bSAdrien Destugues 
3682c26ad4bSAdrien Destugues // #pragma mark URL fields access
3692c26ad4bSAdrien Destugues 
3702c26ad4bSAdrien Destugues 
3712c26ad4bSAdrien Destugues const BString&
3722c26ad4bSAdrien Destugues BUrl::UrlString() const
3732c26ad4bSAdrien Destugues {
3742c26ad4bSAdrien Destugues 	if (!fUrlStringValid) {
3752c26ad4bSAdrien Destugues 		fUrlString.Truncate(0);
3762c26ad4bSAdrien Destugues 
3772c26ad4bSAdrien Destugues 		if (HasProtocol()) {
3782c26ad4bSAdrien Destugues 			fUrlString << fProtocol << ':';
3792c26ad4bSAdrien Destugues 		}
3802c26ad4bSAdrien Destugues 
3812c26ad4bSAdrien Destugues 		if (HasAuthority()) {
3822c26ad4bSAdrien Destugues 			fUrlString << "//";
3832c26ad4bSAdrien Destugues 			fUrlString << Authority();
3842c26ad4bSAdrien Destugues 		}
3852c26ad4bSAdrien Destugues 		fUrlString << Path();
3862c26ad4bSAdrien Destugues 
3872c26ad4bSAdrien Destugues 		if (HasRequest())
3882c26ad4bSAdrien Destugues 			fUrlString << '?' << fRequest;
3892c26ad4bSAdrien Destugues 
3902c26ad4bSAdrien Destugues 		if (HasFragment())
3912c26ad4bSAdrien Destugues 			fUrlString << '#' << fFragment;
3922c26ad4bSAdrien Destugues 
3932c26ad4bSAdrien Destugues 		fUrlStringValid = true;
3942c26ad4bSAdrien Destugues 	}
3952c26ad4bSAdrien Destugues 
3962c26ad4bSAdrien Destugues 	return fUrlString;
3972c26ad4bSAdrien Destugues }
3982c26ad4bSAdrien Destugues 
3992c26ad4bSAdrien Destugues 
4002c26ad4bSAdrien Destugues const BString&
4012c26ad4bSAdrien Destugues BUrl::Protocol() const
4022c26ad4bSAdrien Destugues {
4032c26ad4bSAdrien Destugues 	return fProtocol;
4042c26ad4bSAdrien Destugues }
4052c26ad4bSAdrien Destugues 
4062c26ad4bSAdrien Destugues 
4072c26ad4bSAdrien Destugues const BString&
4082c26ad4bSAdrien Destugues BUrl::UserName() const
4092c26ad4bSAdrien Destugues {
4102c26ad4bSAdrien Destugues 	return fUser;
4112c26ad4bSAdrien Destugues }
4122c26ad4bSAdrien Destugues 
4132c26ad4bSAdrien Destugues 
4142c26ad4bSAdrien Destugues const BString&
4152c26ad4bSAdrien Destugues BUrl::Password() const
4162c26ad4bSAdrien Destugues {
4172c26ad4bSAdrien Destugues 	return fPassword;
4182c26ad4bSAdrien Destugues }
4192c26ad4bSAdrien Destugues 
4202c26ad4bSAdrien Destugues 
4212c26ad4bSAdrien Destugues const BString&
4222c26ad4bSAdrien Destugues BUrl::UserInfo() const
4232c26ad4bSAdrien Destugues {
4242c26ad4bSAdrien Destugues 	if (!fUserInfoValid) {
4252c26ad4bSAdrien Destugues 		fUserInfo = fUser;
4262c26ad4bSAdrien Destugues 
4272c26ad4bSAdrien Destugues 		if (HasPassword())
4282c26ad4bSAdrien Destugues 			fUserInfo << ':' << fPassword;
4292c26ad4bSAdrien Destugues 
4302c26ad4bSAdrien Destugues 		fUserInfoValid = true;
4312c26ad4bSAdrien Destugues 	}
4322c26ad4bSAdrien Destugues 
4332c26ad4bSAdrien Destugues 	return fUserInfo;
4342c26ad4bSAdrien Destugues }
4352c26ad4bSAdrien Destugues 
4362c26ad4bSAdrien Destugues 
4372c26ad4bSAdrien Destugues const BString&
4382c26ad4bSAdrien Destugues BUrl::Host() const
4392c26ad4bSAdrien Destugues {
4402c26ad4bSAdrien Destugues 	return fHost;
4412c26ad4bSAdrien Destugues }
4422c26ad4bSAdrien Destugues 
4432c26ad4bSAdrien Destugues 
4442c26ad4bSAdrien Destugues int
4452c26ad4bSAdrien Destugues BUrl::Port() const
4462c26ad4bSAdrien Destugues {
4472c26ad4bSAdrien Destugues 	return fPort;
4482c26ad4bSAdrien Destugues }
4492c26ad4bSAdrien Destugues 
4502c26ad4bSAdrien Destugues 
4512c26ad4bSAdrien Destugues const BString&
4522c26ad4bSAdrien Destugues BUrl::Authority() const
4532c26ad4bSAdrien Destugues {
4542c26ad4bSAdrien Destugues 	if (!fAuthorityValid) {
4552c26ad4bSAdrien Destugues 		fAuthority.Truncate(0);
4562c26ad4bSAdrien Destugues 
4572c26ad4bSAdrien Destugues 		if (HasUserInfo())
4582c26ad4bSAdrien Destugues 			fAuthority << UserInfo() << '@';
4592c26ad4bSAdrien Destugues 		fAuthority << Host();
4602c26ad4bSAdrien Destugues 
4612c26ad4bSAdrien Destugues 		if (HasPort())
4622c26ad4bSAdrien Destugues 			fAuthority << ':' << fPort;
4632c26ad4bSAdrien Destugues 
4642c26ad4bSAdrien Destugues 		fAuthorityValid = true;
4652c26ad4bSAdrien Destugues 	}
4662c26ad4bSAdrien Destugues 	return fAuthority;
4672c26ad4bSAdrien Destugues }
4682c26ad4bSAdrien Destugues 
4692c26ad4bSAdrien Destugues 
4702c26ad4bSAdrien Destugues const BString&
4712c26ad4bSAdrien Destugues BUrl::Path() const
4722c26ad4bSAdrien Destugues {
4732c26ad4bSAdrien Destugues 	return fPath;
4742c26ad4bSAdrien Destugues }
4752c26ad4bSAdrien Destugues 
4762c26ad4bSAdrien Destugues 
4772c26ad4bSAdrien Destugues const BString&
4782c26ad4bSAdrien Destugues BUrl::Request() const
4792c26ad4bSAdrien Destugues {
4802c26ad4bSAdrien Destugues 	return fRequest;
4812c26ad4bSAdrien Destugues }
4822c26ad4bSAdrien Destugues 
4832c26ad4bSAdrien Destugues 
4842c26ad4bSAdrien Destugues const BString&
4852c26ad4bSAdrien Destugues BUrl::Fragment() const
4862c26ad4bSAdrien Destugues {
4872c26ad4bSAdrien Destugues 	return fFragment;
4882c26ad4bSAdrien Destugues }
4892c26ad4bSAdrien Destugues 
4902c26ad4bSAdrien Destugues 
4912c26ad4bSAdrien Destugues // #pragma mark URL fields tests
4922c26ad4bSAdrien Destugues 
4932c26ad4bSAdrien Destugues 
4942c26ad4bSAdrien Destugues bool
4952c26ad4bSAdrien Destugues BUrl::IsValid() const
4962c26ad4bSAdrien Destugues {
4972c26ad4bSAdrien Destugues 	if (!fHasProtocol)
4982c26ad4bSAdrien Destugues 		return false;
4992c26ad4bSAdrien Destugues 
5008f30879bSAndrew Lindesay 	if (!_IsProtocolValid())
5018f30879bSAndrew Lindesay 		return false;
5028f30879bSAndrew Lindesay 
5038f30879bSAndrew Lindesay 	// it is possible that there can be an authority but no host.
5048f30879bSAndrew Lindesay 	// wierd://tea:tree@/x
5058f30879bSAndrew Lindesay 	if (HasHost() && !(fHost.IsEmpty() && HasAuthority()) && !_IsHostValid())
5068f30879bSAndrew Lindesay 		return false;
5078f30879bSAndrew Lindesay 
5082c26ad4bSAdrien Destugues 	if (fProtocol == "http" || fProtocol == "https" || fProtocol == "ftp"
5092c26ad4bSAdrien Destugues 		|| fProtocol == "ipp" || fProtocol == "afp" || fProtocol == "telnet"
5102c26ad4bSAdrien Destugues 		|| fProtocol == "gopher" || fProtocol == "nntp" || fProtocol == "sftp"
5112c26ad4bSAdrien Destugues 		|| fProtocol == "finger" || fProtocol == "pop" || fProtocol == "imap") {
5128f30879bSAndrew Lindesay 		return HasHost() && !fHost.IsEmpty();
5132c26ad4bSAdrien Destugues 	}
5142c26ad4bSAdrien Destugues 
5152c26ad4bSAdrien Destugues 	if (fProtocol == "file")
5162c26ad4bSAdrien Destugues 		return fHasPath;
5172c26ad4bSAdrien Destugues 
5182c26ad4bSAdrien Destugues 	return true;
5192c26ad4bSAdrien Destugues }
5202c26ad4bSAdrien Destugues 
5212c26ad4bSAdrien Destugues 
5222c26ad4bSAdrien Destugues bool
5232c26ad4bSAdrien Destugues BUrl::HasProtocol() const
5242c26ad4bSAdrien Destugues {
5252c26ad4bSAdrien Destugues 	return fHasProtocol;
5262c26ad4bSAdrien Destugues }
5272c26ad4bSAdrien Destugues 
5282c26ad4bSAdrien Destugues 
5292c26ad4bSAdrien Destugues bool
5302c26ad4bSAdrien Destugues BUrl::HasAuthority() const
5312c26ad4bSAdrien Destugues {
5322c26ad4bSAdrien Destugues 	return fHasHost || fHasUserName;
5332c26ad4bSAdrien Destugues }
5342c26ad4bSAdrien Destugues 
5352c26ad4bSAdrien Destugues 
5362c26ad4bSAdrien Destugues bool
5372c26ad4bSAdrien Destugues BUrl::HasUserName() const
5382c26ad4bSAdrien Destugues {
5392c26ad4bSAdrien Destugues 	return fHasUserName;
5402c26ad4bSAdrien Destugues }
5412c26ad4bSAdrien Destugues 
5422c26ad4bSAdrien Destugues 
5432c26ad4bSAdrien Destugues bool
5442c26ad4bSAdrien Destugues BUrl::HasPassword() const
5452c26ad4bSAdrien Destugues {
5462c26ad4bSAdrien Destugues 	return fHasPassword;
5472c26ad4bSAdrien Destugues }
5482c26ad4bSAdrien Destugues 
5492c26ad4bSAdrien Destugues 
5502c26ad4bSAdrien Destugues bool
5512c26ad4bSAdrien Destugues BUrl::HasUserInfo() const
5522c26ad4bSAdrien Destugues {
5532c26ad4bSAdrien Destugues 	return fHasUserName || fHasPassword;
5542c26ad4bSAdrien Destugues }
5552c26ad4bSAdrien Destugues 
5562c26ad4bSAdrien Destugues 
5572c26ad4bSAdrien Destugues bool
5582c26ad4bSAdrien Destugues BUrl::HasHost() const
5592c26ad4bSAdrien Destugues {
5602c26ad4bSAdrien Destugues 	return fHasHost;
5612c26ad4bSAdrien Destugues }
5622c26ad4bSAdrien Destugues 
5632c26ad4bSAdrien Destugues 
5642c26ad4bSAdrien Destugues bool
5652c26ad4bSAdrien Destugues BUrl::HasPort() const
5662c26ad4bSAdrien Destugues {
5672c26ad4bSAdrien Destugues 	return fHasPort;
5682c26ad4bSAdrien Destugues }
5692c26ad4bSAdrien Destugues 
5702c26ad4bSAdrien Destugues 
5712c26ad4bSAdrien Destugues bool
5722c26ad4bSAdrien Destugues BUrl::HasPath() const
5732c26ad4bSAdrien Destugues {
5742c26ad4bSAdrien Destugues 	return fHasPath;
5752c26ad4bSAdrien Destugues }
5762c26ad4bSAdrien Destugues 
5772c26ad4bSAdrien Destugues 
5782c26ad4bSAdrien Destugues bool
5792c26ad4bSAdrien Destugues BUrl::HasRequest() const
5802c26ad4bSAdrien Destugues {
5812c26ad4bSAdrien Destugues 	return fHasRequest;
5822c26ad4bSAdrien Destugues }
5832c26ad4bSAdrien Destugues 
5842c26ad4bSAdrien Destugues 
5852c26ad4bSAdrien Destugues bool
5862c26ad4bSAdrien Destugues BUrl::HasFragment() const
5872c26ad4bSAdrien Destugues {
5882c26ad4bSAdrien Destugues 	return fHasFragment;
5892c26ad4bSAdrien Destugues }
5902c26ad4bSAdrien Destugues 
5912c26ad4bSAdrien Destugues 
5922c26ad4bSAdrien Destugues // #pragma mark URL encoding/decoding of needed fields
5932c26ad4bSAdrien Destugues 
5942c26ad4bSAdrien Destugues 
5952c26ad4bSAdrien Destugues void
5962c26ad4bSAdrien Destugues BUrl::UrlEncode(bool strict)
5972c26ad4bSAdrien Destugues {
5982c26ad4bSAdrien Destugues 	fUser = _DoUrlEncodeChunk(fUser, strict);
5992c26ad4bSAdrien Destugues 	fPassword = _DoUrlEncodeChunk(fPassword, strict);
6002c26ad4bSAdrien Destugues 	fHost = _DoUrlEncodeChunk(fHost, strict);
6012c26ad4bSAdrien Destugues 	fFragment = _DoUrlEncodeChunk(fFragment, strict);
6022c26ad4bSAdrien Destugues 	fPath = _DoUrlEncodeChunk(fPath, strict, true);
6032c26ad4bSAdrien Destugues }
6042c26ad4bSAdrien Destugues 
6052c26ad4bSAdrien Destugues 
6062c26ad4bSAdrien Destugues void
6072c26ad4bSAdrien Destugues BUrl::UrlDecode(bool strict)
6082c26ad4bSAdrien Destugues {
6092c26ad4bSAdrien Destugues 	fUser = _DoUrlDecodeChunk(fUser, strict);
6102c26ad4bSAdrien Destugues 	fPassword = _DoUrlDecodeChunk(fPassword, strict);
6112c26ad4bSAdrien Destugues 	fHost = _DoUrlDecodeChunk(fHost, strict);
6122c26ad4bSAdrien Destugues 	fFragment = _DoUrlDecodeChunk(fFragment, strict);
6132c26ad4bSAdrien Destugues 	fPath = _DoUrlDecodeChunk(fPath, strict);
6142c26ad4bSAdrien Destugues }
6152c26ad4bSAdrien Destugues 
6162c26ad4bSAdrien Destugues 
6172c26ad4bSAdrien Destugues #ifdef HAIKU_TARGET_PLATFORM_HAIKU
6182c26ad4bSAdrien Destugues status_t
6192c26ad4bSAdrien Destugues BUrl::IDNAToAscii()
6202c26ad4bSAdrien Destugues {
6212c26ad4bSAdrien Destugues 	UErrorCode err = U_ZERO_ERROR;
6222c26ad4bSAdrien Destugues 	icu::IDNA* converter = icu::IDNA::createUTS46Instance(0, err);
6232c26ad4bSAdrien Destugues 	icu::IDNAInfo info;
6242c26ad4bSAdrien Destugues 
6252c26ad4bSAdrien Destugues 	BString result;
6262c26ad4bSAdrien Destugues 	BStringByteSink sink(&result);
6272c26ad4bSAdrien Destugues 	converter->nameToASCII_UTF8(icu::StringPiece(fHost.String()), sink, info,
6282c26ad4bSAdrien Destugues 		err);
6292c26ad4bSAdrien Destugues 
6302c26ad4bSAdrien Destugues 	delete converter;
6312c26ad4bSAdrien Destugues 
6322c26ad4bSAdrien Destugues 	if (U_FAILURE(err))
6332c26ad4bSAdrien Destugues 		return B_ERROR;
6342c26ad4bSAdrien Destugues 
6352c26ad4bSAdrien Destugues 	fHost = result;
6362c26ad4bSAdrien Destugues 	return B_OK;
6372c26ad4bSAdrien Destugues }
6382c26ad4bSAdrien Destugues #endif
6392c26ad4bSAdrien Destugues 
6402c26ad4bSAdrien Destugues 
6412c26ad4bSAdrien Destugues #ifdef HAIKU_TARGET_PLATFORM_HAIKU
6422c26ad4bSAdrien Destugues status_t
6432c26ad4bSAdrien Destugues BUrl::IDNAToUnicode()
6442c26ad4bSAdrien Destugues {
6452c26ad4bSAdrien Destugues 	UErrorCode err = U_ZERO_ERROR;
6462c26ad4bSAdrien Destugues 	icu::IDNA* converter = icu::IDNA::createUTS46Instance(0, err);
6472c26ad4bSAdrien Destugues 	icu::IDNAInfo info;
6482c26ad4bSAdrien Destugues 
6492c26ad4bSAdrien Destugues 	BString result;
6502c26ad4bSAdrien Destugues 	BStringByteSink sink(&result);
6512c26ad4bSAdrien Destugues 	converter->nameToUnicodeUTF8(icu::StringPiece(fHost.String()), sink, info,
6522c26ad4bSAdrien Destugues 		err);
6532c26ad4bSAdrien Destugues 
6542c26ad4bSAdrien Destugues 	delete converter;
6552c26ad4bSAdrien Destugues 
6562c26ad4bSAdrien Destugues 	if (U_FAILURE(err))
6572c26ad4bSAdrien Destugues 		return B_ERROR;
6582c26ad4bSAdrien Destugues 
6592c26ad4bSAdrien Destugues 	fHost = result;
6602c26ad4bSAdrien Destugues 	return B_OK;
6612c26ad4bSAdrien Destugues }
6622c26ad4bSAdrien Destugues #endif
6632c26ad4bSAdrien Destugues 
6642c26ad4bSAdrien Destugues 
6652c26ad4bSAdrien Destugues // #pragma mark - utility functionality
6662c26ad4bSAdrien Destugues 
6672c26ad4bSAdrien Destugues 
6682c26ad4bSAdrien Destugues #ifdef HAIKU_TARGET_PLATFORM_HAIKU
6692c26ad4bSAdrien Destugues bool
6702c26ad4bSAdrien Destugues BUrl::HasPreferredApplication() const
6712c26ad4bSAdrien Destugues {
6722c26ad4bSAdrien Destugues 	BString appSignature = PreferredApplication();
6732c26ad4bSAdrien Destugues 	BMimeType mime(appSignature.String());
6742c26ad4bSAdrien Destugues 
6752c26ad4bSAdrien Destugues 	if (appSignature.IFindFirst("application/") == 0
6762c26ad4bSAdrien Destugues 		&& mime.IsValid())
6772c26ad4bSAdrien Destugues 		return true;
6782c26ad4bSAdrien Destugues 
6792c26ad4bSAdrien Destugues 	return false;
6802c26ad4bSAdrien Destugues }
6812c26ad4bSAdrien Destugues #endif
6822c26ad4bSAdrien Destugues 
6832c26ad4bSAdrien Destugues 
6842c26ad4bSAdrien Destugues #ifdef HAIKU_TARGET_PLATFORM_HAIKU
6852c26ad4bSAdrien Destugues BString
6862c26ad4bSAdrien Destugues BUrl::PreferredApplication() const
6872c26ad4bSAdrien Destugues {
6882c26ad4bSAdrien Destugues 	BString appSignature;
6892c26ad4bSAdrien Destugues 	BMimeType mime(_UrlMimeType().String());
6902c26ad4bSAdrien Destugues 	mime.GetPreferredApp(appSignature.LockBuffer(B_MIME_TYPE_LENGTH));
6912c26ad4bSAdrien Destugues 	appSignature.UnlockBuffer();
6922c26ad4bSAdrien Destugues 
6932c26ad4bSAdrien Destugues 	return BString(appSignature);
6942c26ad4bSAdrien Destugues }
6952c26ad4bSAdrien Destugues #endif
6962c26ad4bSAdrien Destugues 
6972c26ad4bSAdrien Destugues 
6982c26ad4bSAdrien Destugues #ifdef HAIKU_TARGET_PLATFORM_HAIKU
6992c26ad4bSAdrien Destugues status_t
7002c26ad4bSAdrien Destugues BUrl::OpenWithPreferredApplication(bool onProblemAskUser) const
7012c26ad4bSAdrien Destugues {
7022c26ad4bSAdrien Destugues 	if (!IsValid())
7032c26ad4bSAdrien Destugues 		return B_BAD_VALUE;
7042c26ad4bSAdrien Destugues 
7052c26ad4bSAdrien Destugues 	BString urlString = UrlString();
7062c26ad4bSAdrien Destugues 	if (urlString.Length() > B_PATH_NAME_LENGTH) {
7072c26ad4bSAdrien Destugues 		// TODO: BAlert
7082c26ad4bSAdrien Destugues 		//	if (onProblemAskUser)
7092c26ad4bSAdrien Destugues 		//		BAlert ... Too long URL!
7102c26ad4bSAdrien Destugues #if DEBUG
7112c26ad4bSAdrien Destugues 		fprintf(stderr, "URL too long");
7122c26ad4bSAdrien Destugues #endif
7132c26ad4bSAdrien Destugues 		return B_NAME_TOO_LONG;
7142c26ad4bSAdrien Destugues 	}
7152c26ad4bSAdrien Destugues 
7162c26ad4bSAdrien Destugues 	char* argv[] = {
7172c26ad4bSAdrien Destugues 		const_cast<char*>("BUrlInvokedApplication"),
7182c26ad4bSAdrien Destugues 		const_cast<char*>(urlString.String()),
7192c26ad4bSAdrien Destugues 		NULL
7202c26ad4bSAdrien Destugues 	};
7212c26ad4bSAdrien Destugues 
7222c26ad4bSAdrien Destugues #if DEBUG
7232c26ad4bSAdrien Destugues 	if (HasPreferredApplication())
7242c26ad4bSAdrien Destugues 		printf("HasPreferredApplication() == true\n");
7252c26ad4bSAdrien Destugues 	else
7262c26ad4bSAdrien Destugues 		printf("HasPreferredApplication() == false\n");
7272c26ad4bSAdrien Destugues #endif
7282c26ad4bSAdrien Destugues 
7292c26ad4bSAdrien Destugues 	status_t status = be_roster->Launch(_UrlMimeType().String(), 1, argv+1);
7302c26ad4bSAdrien Destugues 	if (status != B_OK) {
7312c26ad4bSAdrien Destugues #if DEBUG
7322c26ad4bSAdrien Destugues 		fprintf(stderr, "Opening URL failed: %s\n", strerror(status));
7332c26ad4bSAdrien Destugues #endif
7342c26ad4bSAdrien Destugues 	}
7352c26ad4bSAdrien Destugues 
7362c26ad4bSAdrien Destugues 	return status;
7372c26ad4bSAdrien Destugues }
7382c26ad4bSAdrien Destugues #endif
7392c26ad4bSAdrien Destugues 
7402c26ad4bSAdrien Destugues 
7412c26ad4bSAdrien Destugues // #pragma mark Url encoding/decoding of string
7422c26ad4bSAdrien Destugues 
7432c26ad4bSAdrien Destugues 
7442c26ad4bSAdrien Destugues /*static*/ BString
7452c26ad4bSAdrien Destugues BUrl::UrlEncode(const BString& url, bool strict, bool directory)
7462c26ad4bSAdrien Destugues {
7472c26ad4bSAdrien Destugues 	return _DoUrlEncodeChunk(url, strict, directory);
7482c26ad4bSAdrien Destugues }
7492c26ad4bSAdrien Destugues 
7502c26ad4bSAdrien Destugues 
7512c26ad4bSAdrien Destugues /*static*/ BString
7522c26ad4bSAdrien Destugues BUrl::UrlDecode(const BString& url, bool strict)
7532c26ad4bSAdrien Destugues {
7542c26ad4bSAdrien Destugues 	return _DoUrlDecodeChunk(url, strict);
7552c26ad4bSAdrien Destugues }
7562c26ad4bSAdrien Destugues 
7572c26ad4bSAdrien Destugues 
7582c26ad4bSAdrien Destugues // #pragma mark BArchivable members
7592c26ad4bSAdrien Destugues 
7602c26ad4bSAdrien Destugues 
7612c26ad4bSAdrien Destugues status_t
7622c26ad4bSAdrien Destugues BUrl::Archive(BMessage* into, bool deep) const
7632c26ad4bSAdrien Destugues {
7642c26ad4bSAdrien Destugues 	status_t ret = BArchivable::Archive(into, deep);
7652c26ad4bSAdrien Destugues 
7662c26ad4bSAdrien Destugues 	if (ret == B_OK)
7672c26ad4bSAdrien Destugues 		ret = into->AddString(kArchivedUrl, UrlString());
7682c26ad4bSAdrien Destugues 
7692c26ad4bSAdrien Destugues 	return ret;
7702c26ad4bSAdrien Destugues }
7712c26ad4bSAdrien Destugues 
7722c26ad4bSAdrien Destugues 
7732c26ad4bSAdrien Destugues /*static*/ BArchivable*
7742c26ad4bSAdrien Destugues BUrl::Instantiate(BMessage* archive)
7752c26ad4bSAdrien Destugues {
7762c26ad4bSAdrien Destugues 	if (validate_instantiation(archive, "BUrl"))
7772c26ad4bSAdrien Destugues 		return new(std::nothrow) BUrl(archive);
7782c26ad4bSAdrien Destugues 	return NULL;
7792c26ad4bSAdrien Destugues }
7802c26ad4bSAdrien Destugues 
7812c26ad4bSAdrien Destugues 
7822c26ad4bSAdrien Destugues // #pragma mark URL comparison
7832c26ad4bSAdrien Destugues 
7842c26ad4bSAdrien Destugues 
7852c26ad4bSAdrien Destugues bool
7862c26ad4bSAdrien Destugues BUrl::operator==(BUrl& other) const
7872c26ad4bSAdrien Destugues {
7882c26ad4bSAdrien Destugues 	UrlString();
7892c26ad4bSAdrien Destugues 	other.UrlString();
7902c26ad4bSAdrien Destugues 
7912c26ad4bSAdrien Destugues 	return fUrlString == other.fUrlString;
7922c26ad4bSAdrien Destugues }
7932c26ad4bSAdrien Destugues 
7942c26ad4bSAdrien Destugues 
7952c26ad4bSAdrien Destugues bool
7962c26ad4bSAdrien Destugues BUrl::operator!=(BUrl& other) const
7972c26ad4bSAdrien Destugues {
7982c26ad4bSAdrien Destugues 	return !(*this == other);
7992c26ad4bSAdrien Destugues }
8002c26ad4bSAdrien Destugues 
8012c26ad4bSAdrien Destugues 
8022c26ad4bSAdrien Destugues // #pragma mark URL assignment
8032c26ad4bSAdrien Destugues 
8042c26ad4bSAdrien Destugues 
8052c26ad4bSAdrien Destugues const BUrl&
8062c26ad4bSAdrien Destugues BUrl::operator=(const BUrl& other)
8072c26ad4bSAdrien Destugues {
8082c26ad4bSAdrien Destugues 	fUrlStringValid = other.fUrlStringValid;
8092c26ad4bSAdrien Destugues 	if (fUrlStringValid)
8102c26ad4bSAdrien Destugues 		fUrlString = other.fUrlString;
8112c26ad4bSAdrien Destugues 
8122c26ad4bSAdrien Destugues 	fAuthorityValid = other.fAuthorityValid;
8132c26ad4bSAdrien Destugues 	if (fAuthorityValid)
8142c26ad4bSAdrien Destugues 		fAuthority = other.fAuthority;
8152c26ad4bSAdrien Destugues 
8162c26ad4bSAdrien Destugues 	fUserInfoValid = other.fUserInfoValid;
8172c26ad4bSAdrien Destugues 	if (fUserInfoValid)
8182c26ad4bSAdrien Destugues 		fUserInfo = other.fUserInfo;
8192c26ad4bSAdrien Destugues 
8202c26ad4bSAdrien Destugues 	fProtocol = other.fProtocol;
8212c26ad4bSAdrien Destugues 	fUser = other.fUser;
8222c26ad4bSAdrien Destugues 	fPassword = other.fPassword;
8232c26ad4bSAdrien Destugues 	fHost = other.fHost;
8242c26ad4bSAdrien Destugues 	fPort = other.fPort;
8252c26ad4bSAdrien Destugues 	fPath = other.fPath;
8262c26ad4bSAdrien Destugues 	fRequest = other.fRequest;
8272c26ad4bSAdrien Destugues 	fFragment = other.fFragment;
8282c26ad4bSAdrien Destugues 
8292c26ad4bSAdrien Destugues 	fHasProtocol = other.fHasProtocol;
8302c26ad4bSAdrien Destugues 	fHasUserName = other.fHasUserName;
8312c26ad4bSAdrien Destugues 	fHasPassword = other.fHasPassword;
8322c26ad4bSAdrien Destugues 	fHasHost = other.fHasHost;
8332c26ad4bSAdrien Destugues 	fHasPort = other.fHasPort;
8342c26ad4bSAdrien Destugues 	fHasPath = other.fHasPath;
8352c26ad4bSAdrien Destugues 	fHasRequest = other.fHasRequest;
8362c26ad4bSAdrien Destugues 	fHasFragment = other.fHasFragment;
8372c26ad4bSAdrien Destugues 
8382c26ad4bSAdrien Destugues 	return *this;
8392c26ad4bSAdrien Destugues }
8402c26ad4bSAdrien Destugues 
8412c26ad4bSAdrien Destugues 
8422c26ad4bSAdrien Destugues const BUrl&
8432c26ad4bSAdrien Destugues BUrl::operator=(const BString& string)
8442c26ad4bSAdrien Destugues {
8452c26ad4bSAdrien Destugues 	SetUrlString(string);
8462c26ad4bSAdrien Destugues 	return *this;
8472c26ad4bSAdrien Destugues }
8482c26ad4bSAdrien Destugues 
8492c26ad4bSAdrien Destugues 
8502c26ad4bSAdrien Destugues const BUrl&
8512c26ad4bSAdrien Destugues BUrl::operator=(const char* string)
8522c26ad4bSAdrien Destugues {
8532c26ad4bSAdrien Destugues 	SetUrlString(string);
8542c26ad4bSAdrien Destugues 	return *this;
8552c26ad4bSAdrien Destugues }
8562c26ad4bSAdrien Destugues 
8572c26ad4bSAdrien Destugues 
8582c26ad4bSAdrien Destugues // #pragma mark URL to string conversion
8592c26ad4bSAdrien Destugues 
8602c26ad4bSAdrien Destugues 
8612c26ad4bSAdrien Destugues BUrl::operator const char*() const
8622c26ad4bSAdrien Destugues {
8632c26ad4bSAdrien Destugues 	return UrlString();
8642c26ad4bSAdrien Destugues }
8652c26ad4bSAdrien Destugues 
8662c26ad4bSAdrien Destugues 
8672c26ad4bSAdrien Destugues void
8682c26ad4bSAdrien Destugues BUrl::_ResetFields()
8692c26ad4bSAdrien Destugues {
8702c26ad4bSAdrien Destugues 	fHasProtocol = false;
8712c26ad4bSAdrien Destugues 	fHasUserName = false;
8722c26ad4bSAdrien Destugues 	fHasPassword = false;
8732c26ad4bSAdrien Destugues 	fHasHost = false;
8742c26ad4bSAdrien Destugues 	fHasPort = false;
8752c26ad4bSAdrien Destugues 	fHasPath = false;
8762c26ad4bSAdrien Destugues 	fHasRequest = false;
8772c26ad4bSAdrien Destugues 	fHasFragment = false;
8782c26ad4bSAdrien Destugues 
8792c26ad4bSAdrien Destugues 	fProtocol.Truncate(0);
8802c26ad4bSAdrien Destugues 	fUser.Truncate(0);
8812c26ad4bSAdrien Destugues 	fPassword.Truncate(0);
8822c26ad4bSAdrien Destugues 	fHost.Truncate(0);
8832c26ad4bSAdrien Destugues 	fPort = 0;
8842c26ad4bSAdrien Destugues 	fPath.Truncate(0);
8852c26ad4bSAdrien Destugues 	fRequest.Truncate(0);
8862c26ad4bSAdrien Destugues 	fFragment.Truncate(0);
8872c26ad4bSAdrien Destugues 
8882c26ad4bSAdrien Destugues 	// Force re-generation of these fields
8892c26ad4bSAdrien Destugues 	fUrlStringValid = false;
8902c26ad4bSAdrien Destugues 	fUserInfoValid = false;
8912c26ad4bSAdrien Destugues 	fAuthorityValid = false;
8922c26ad4bSAdrien Destugues }
8932c26ad4bSAdrien Destugues 
8942c26ad4bSAdrien Destugues 
8952c26ad4bSAdrien Destugues bool
8962c26ad4bSAdrien Destugues BUrl::_ContainsDelimiter(const BString& url)
8972c26ad4bSAdrien Destugues {
8982c26ad4bSAdrien Destugues 	int32 len = url.Length();
8992c26ad4bSAdrien Destugues 
9002c26ad4bSAdrien Destugues 	for (int32 i = 0; i < len; i++) {
9012c26ad4bSAdrien Destugues 		switch (url[i]) {
9022c26ad4bSAdrien Destugues 			case ' ':
9032c26ad4bSAdrien Destugues 			case '\n':
9042c26ad4bSAdrien Destugues 			case '\t':
9052c26ad4bSAdrien Destugues 			case '\r':
9062c26ad4bSAdrien Destugues 			case '<':
9072c26ad4bSAdrien Destugues 			case '>':
9082c26ad4bSAdrien Destugues 			case '"':
9092c26ad4bSAdrien Destugues 				return true;
9102c26ad4bSAdrien Destugues 		}
9112c26ad4bSAdrien Destugues 	}
9122c26ad4bSAdrien Destugues 
9132c26ad4bSAdrien Destugues 	return false;
9142c26ad4bSAdrien Destugues }
9152c26ad4bSAdrien Destugues 
9162c26ad4bSAdrien Destugues 
9172c26ad4bSAdrien Destugues enum explode_url_parse_state {
9182c26ad4bSAdrien Destugues 	EXPLODE_PROTOCOL,
9192c26ad4bSAdrien Destugues 	EXPLODE_PROTOCOLTERMINATOR,
9202c26ad4bSAdrien Destugues 	EXPLODE_AUTHORITYORPATH,
9212c26ad4bSAdrien Destugues 	EXPLODE_AUTHORITY,
9222c26ad4bSAdrien Destugues 	EXPLODE_PATH,
9232c26ad4bSAdrien Destugues 	EXPLODE_REQUEST, // query
9242c26ad4bSAdrien Destugues 	EXPLODE_FRAGMENT,
9252c26ad4bSAdrien Destugues 	EXPLODE_COMPLETE
9262c26ad4bSAdrien Destugues };
9272c26ad4bSAdrien Destugues 
9282c26ad4bSAdrien Destugues 
9292c26ad4bSAdrien Destugues typedef bool (*explode_char_match_fn)(char c);
9302c26ad4bSAdrien Destugues 
9312c26ad4bSAdrien Destugues 
9322c26ad4bSAdrien Destugues static bool
9332c26ad4bSAdrien Destugues explode_is_protocol_char(char c)
9342c26ad4bSAdrien Destugues {
9352c26ad4bSAdrien Destugues 	return isalnum(c) || c == '+' || c == '.' || c == '-';
9362c26ad4bSAdrien Destugues }
9372c26ad4bSAdrien Destugues 
9382c26ad4bSAdrien Destugues 
9392c26ad4bSAdrien Destugues static bool
9402c26ad4bSAdrien Destugues explode_is_authority_char(char c)
9412c26ad4bSAdrien Destugues {
9422c26ad4bSAdrien Destugues 	return !(c == '/' || c == '?' || c == '#');
9432c26ad4bSAdrien Destugues }
9442c26ad4bSAdrien Destugues 
9452c26ad4bSAdrien Destugues 
9462c26ad4bSAdrien Destugues static bool
9472c26ad4bSAdrien Destugues explode_is_path_char(char c)
9482c26ad4bSAdrien Destugues {
9492c26ad4bSAdrien Destugues 	return !(c == '#' || c == '?');
9502c26ad4bSAdrien Destugues }
9512c26ad4bSAdrien Destugues 
9522c26ad4bSAdrien Destugues 
9532c26ad4bSAdrien Destugues static bool
9542c26ad4bSAdrien Destugues explode_is_request_char(char c)
9552c26ad4bSAdrien Destugues {
9562c26ad4bSAdrien Destugues 	return c != '#';
9572c26ad4bSAdrien Destugues }
9582c26ad4bSAdrien Destugues 
9592c26ad4bSAdrien Destugues 
9602c26ad4bSAdrien Destugues static int32
9612c26ad4bSAdrien Destugues char_offset_until_fn_false(const char* url, int32 len, int32 offset,
9622c26ad4bSAdrien Destugues 	explode_char_match_fn fn)
9632c26ad4bSAdrien Destugues {
9642c26ad4bSAdrien Destugues 	while (offset < len && fn(url[offset]))
9652c26ad4bSAdrien Destugues 		offset++;
9662c26ad4bSAdrien Destugues 
9672c26ad4bSAdrien Destugues 	return offset;
9682c26ad4bSAdrien Destugues }
9692c26ad4bSAdrien Destugues 
9702c26ad4bSAdrien Destugues /*
9712c26ad4bSAdrien Destugues  * This function takes a URL in string-form and parses the components of the URL out.
9722c26ad4bSAdrien Destugues  */
9732c26ad4bSAdrien Destugues status_t
9742c26ad4bSAdrien Destugues BUrl::_ExplodeUrlString(const BString& url)
9752c26ad4bSAdrien Destugues {
9762c26ad4bSAdrien Destugues 	_ResetFields();
9772c26ad4bSAdrien Destugues 
9782c26ad4bSAdrien Destugues 	// RFC3986, Appendix C; the URL should not contain whitespace or delimiters
9792c26ad4bSAdrien Destugues 	// by this point.
9802c26ad4bSAdrien Destugues 
9812c26ad4bSAdrien Destugues 	if (_ContainsDelimiter(url))
9822c26ad4bSAdrien Destugues 		return B_BAD_VALUE;
9832c26ad4bSAdrien Destugues 
9842c26ad4bSAdrien Destugues 	explode_url_parse_state state = EXPLODE_PROTOCOL;
9852c26ad4bSAdrien Destugues 	int32 offset = 0;
9862c26ad4bSAdrien Destugues 	int32 length = url.Length();
987*3cc5e76fSAndrew Lindesay 	bool forceHasHost = false;
9882c26ad4bSAdrien Destugues 	const char *url_c = url.String();
9892c26ad4bSAdrien Destugues 
9902c26ad4bSAdrien Destugues 	// The regexp is provided in RFC3986 (URI generic syntax), Appendix B
9912c26ad4bSAdrien Destugues 	// ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?
9922c26ad4bSAdrien Destugues 	// The ensuing logic attempts to simulate the behaviour of extracting the groups
9932c26ad4bSAdrien Destugues 	// from the string without requiring a group-capable regex engine.
9942c26ad4bSAdrien Destugues 
9952c26ad4bSAdrien Destugues 	while (offset < length) {
9962c26ad4bSAdrien Destugues 		switch (state) {
9972c26ad4bSAdrien Destugues 
9982c26ad4bSAdrien Destugues 			case EXPLODE_PROTOCOL:
9992c26ad4bSAdrien Destugues 			{
10002c26ad4bSAdrien Destugues 				int32 end_protocol = char_offset_until_fn_false(url_c, length,
10012c26ad4bSAdrien Destugues 					offset, explode_is_protocol_char);
10022c26ad4bSAdrien Destugues 
10032c26ad4bSAdrien Destugues 				if (end_protocol < length) {
10042c26ad4bSAdrien Destugues 					SetProtocol(BString(&url_c[offset], end_protocol - offset));
10052c26ad4bSAdrien Destugues 					state = EXPLODE_PROTOCOLTERMINATOR;
10062c26ad4bSAdrien Destugues 					offset = end_protocol;
10072c26ad4bSAdrien Destugues 				} else {
10082c26ad4bSAdrien Destugues 					// No protocol was found, try parsing from the string
10092c26ad4bSAdrien Destugues 					// start, beginning with authority or path
10102c26ad4bSAdrien Destugues 					SetProtocol("");
10112c26ad4bSAdrien Destugues 					offset = 0;
10122c26ad4bSAdrien Destugues 					state = EXPLODE_AUTHORITYORPATH;
10132c26ad4bSAdrien Destugues 				}
10142c26ad4bSAdrien Destugues 				break;
10152c26ad4bSAdrien Destugues 			}
10162c26ad4bSAdrien Destugues 
10172c26ad4bSAdrien Destugues 			case EXPLODE_PROTOCOLTERMINATOR:
10182c26ad4bSAdrien Destugues 			{
10192c26ad4bSAdrien Destugues 				if (url[offset] == ':') {
10202c26ad4bSAdrien Destugues 					offset++;
10212c26ad4bSAdrien Destugues 				} else {
10222c26ad4bSAdrien Destugues 					// No protocol was found, try parsing from the string
10232c26ad4bSAdrien Destugues 					// start, beginning with authority or path
10242c26ad4bSAdrien Destugues 					SetProtocol("");
10252c26ad4bSAdrien Destugues 					offset = 0;
10262c26ad4bSAdrien Destugues 				}
10272c26ad4bSAdrien Destugues 				state = EXPLODE_AUTHORITYORPATH;
10282c26ad4bSAdrien Destugues 				break;
10292c26ad4bSAdrien Destugues 			}
10302c26ad4bSAdrien Destugues 
10312c26ad4bSAdrien Destugues 			case EXPLODE_AUTHORITYORPATH:
10322c26ad4bSAdrien Destugues 			{
10332c26ad4bSAdrien Destugues 				// The authority must start with //. If it isn't there, skip
10342c26ad4bSAdrien Destugues 				// to parsing the path.
10352c26ad4bSAdrien Destugues 				if (strncmp(&url_c[offset], "//", 2) == 0) {
10362c26ad4bSAdrien Destugues 					state = EXPLODE_AUTHORITY;
1037*3cc5e76fSAndrew Lindesay 					// if we see the // then this would imply that a host is
1038*3cc5e76fSAndrew Lindesay 					// to be rendered even if no host has been parsed.
1039*3cc5e76fSAndrew Lindesay 					forceHasHost = true;
10402c26ad4bSAdrien Destugues 					offset += 2;
10412c26ad4bSAdrien Destugues 				} else {
10422c26ad4bSAdrien Destugues 					state = EXPLODE_PATH;
10432c26ad4bSAdrien Destugues 				}
10442c26ad4bSAdrien Destugues 				break;
10452c26ad4bSAdrien Destugues 			}
10462c26ad4bSAdrien Destugues 
10472c26ad4bSAdrien Destugues 			case EXPLODE_AUTHORITY:
10482c26ad4bSAdrien Destugues 			{
10492c26ad4bSAdrien Destugues 				int end_authority = char_offset_until_fn_false(url_c, length,
10502c26ad4bSAdrien Destugues 					offset, explode_is_authority_char);
10512c26ad4bSAdrien Destugues 				SetAuthority(BString(&url_c[offset], end_authority - offset));
10522c26ad4bSAdrien Destugues 				state = EXPLODE_PATH;
10532c26ad4bSAdrien Destugues 				offset = end_authority;
10542c26ad4bSAdrien Destugues 				break;
10552c26ad4bSAdrien Destugues 			}
10562c26ad4bSAdrien Destugues 
10572c26ad4bSAdrien Destugues 			case EXPLODE_PATH:
10582c26ad4bSAdrien Destugues 			{
10592c26ad4bSAdrien Destugues 				int end_path = char_offset_until_fn_false(url_c, length, offset,
10602c26ad4bSAdrien Destugues 					explode_is_path_char);
10612c26ad4bSAdrien Destugues 				SetPath(BString(&url_c[offset], end_path - offset));
10622c26ad4bSAdrien Destugues 				state = EXPLODE_REQUEST;
10632c26ad4bSAdrien Destugues 				offset = end_path;
10642c26ad4bSAdrien Destugues 				break;
10652c26ad4bSAdrien Destugues 			}
10662c26ad4bSAdrien Destugues 
10672c26ad4bSAdrien Destugues 			case EXPLODE_REQUEST: // query
10682c26ad4bSAdrien Destugues 			{
10692c26ad4bSAdrien Destugues 				if (url_c[offset] == '?') {
10702c26ad4bSAdrien Destugues 					offset++;
10712c26ad4bSAdrien Destugues 					int end_request = char_offset_until_fn_false(url_c, length,
10722c26ad4bSAdrien Destugues 						offset, explode_is_request_char);
10732c26ad4bSAdrien Destugues 					SetRequest(BString(&url_c[offset], end_request - offset));
10742c26ad4bSAdrien Destugues 					offset = end_request;
1075*3cc5e76fSAndrew Lindesay 					// if there is a "?" in the parse then it is clear that
1076*3cc5e76fSAndrew Lindesay 					// there is a 'request' / query present regardless if there
1077*3cc5e76fSAndrew Lindesay 					// are any valid key-value pairs.
1078*3cc5e76fSAndrew Lindesay 					fHasRequest = true;
10792c26ad4bSAdrien Destugues 				}
10802c26ad4bSAdrien Destugues 				state = EXPLODE_FRAGMENT;
10812c26ad4bSAdrien Destugues 				break;
10822c26ad4bSAdrien Destugues 			}
10832c26ad4bSAdrien Destugues 
10842c26ad4bSAdrien Destugues 			case EXPLODE_FRAGMENT:
10852c26ad4bSAdrien Destugues 			{
10862c26ad4bSAdrien Destugues 				if (url_c[offset] == '#') {
10872c26ad4bSAdrien Destugues 					offset++;
10882c26ad4bSAdrien Destugues 					SetFragment(BString(&url_c[offset], length - offset));
10892c26ad4bSAdrien Destugues 					offset = length;
10902c26ad4bSAdrien Destugues 				}
10912c26ad4bSAdrien Destugues 				state = EXPLODE_COMPLETE;
10922c26ad4bSAdrien Destugues 				break;
10932c26ad4bSAdrien Destugues 			}
10942c26ad4bSAdrien Destugues 
10952c26ad4bSAdrien Destugues 			case EXPLODE_COMPLETE:
10962c26ad4bSAdrien Destugues 				// should never be reached - keeps the compiler happy
10972c26ad4bSAdrien Destugues 				break;
10982c26ad4bSAdrien Destugues 
10992c26ad4bSAdrien Destugues 		}
11002c26ad4bSAdrien Destugues 	}
11012c26ad4bSAdrien Destugues 
1102*3cc5e76fSAndrew Lindesay 	if (forceHasHost)
1103*3cc5e76fSAndrew Lindesay 		fHasHost = true;
1104*3cc5e76fSAndrew Lindesay 
11052c26ad4bSAdrien Destugues 	return B_OK;
11062c26ad4bSAdrien Destugues }
11072c26ad4bSAdrien Destugues 
11082c26ad4bSAdrien Destugues 
11092c26ad4bSAdrien Destugues BString
11102c26ad4bSAdrien Destugues BUrl::_MergePath(const BString& relative) const
11112c26ad4bSAdrien Destugues {
11122c26ad4bSAdrien Destugues 	// This implements RFC3986, Section 5.2.3.
1113cd6365c7SJérôme Duval 	if (HasAuthority() && fPath == "") {
11142c26ad4bSAdrien Destugues 		BString result("/");
11152c26ad4bSAdrien Destugues 		result << relative;
11162c26ad4bSAdrien Destugues 		return result;
11172c26ad4bSAdrien Destugues 	}
11182c26ad4bSAdrien Destugues 
11192c26ad4bSAdrien Destugues 	BString result(fPath);
11202c26ad4bSAdrien Destugues 	result.Truncate(result.FindLast("/") + 1);
11212c26ad4bSAdrien Destugues 	result << relative;
11222c26ad4bSAdrien Destugues 
11232c26ad4bSAdrien Destugues 	return result;
11242c26ad4bSAdrien Destugues }
11252c26ad4bSAdrien Destugues 
11262c26ad4bSAdrien Destugues 
11272c26ad4bSAdrien Destugues // This sets the path without normalizing it. If fed with a path that has . or
11282c26ad4bSAdrien Destugues // .. segments, this would make the URL invalid.
11292c26ad4bSAdrien Destugues void
11302c26ad4bSAdrien Destugues BUrl::_SetPathUnsafe(const BString& path)
11312c26ad4bSAdrien Destugues {
11322c26ad4bSAdrien Destugues 	fPath = path;
11332c26ad4bSAdrien Destugues 	fHasPath = true; // RFC says an empty path is still a path
11342c26ad4bSAdrien Destugues 	fUrlStringValid = false;
11352c26ad4bSAdrien Destugues }
11362c26ad4bSAdrien Destugues 
11372c26ad4bSAdrien Destugues 
11382c26ad4bSAdrien Destugues enum authority_parse_state {
11392c26ad4bSAdrien Destugues 	AUTHORITY_USERNAME,
11402c26ad4bSAdrien Destugues 	AUTHORITY_PASSWORD,
11412c26ad4bSAdrien Destugues 	AUTHORITY_HOST,
11422c26ad4bSAdrien Destugues 	AUTHORITY_PORT,
11432c26ad4bSAdrien Destugues 	AUTHORITY_COMPLETE
11442c26ad4bSAdrien Destugues };
11452c26ad4bSAdrien Destugues 
11462c26ad4bSAdrien Destugues void
11472c26ad4bSAdrien Destugues BUrl::SetAuthority(const BString& authority)
11482c26ad4bSAdrien Destugues {
11492c26ad4bSAdrien Destugues 	fAuthority = authority;
11502c26ad4bSAdrien Destugues 
11512c26ad4bSAdrien Destugues 	fUser.Truncate(0);
11522c26ad4bSAdrien Destugues 	fPassword.Truncate(0);
11532c26ad4bSAdrien Destugues 	fHost.Truncate(0);
11542c26ad4bSAdrien Destugues 	fPort = 0;
11552c26ad4bSAdrien Destugues 	fHasPort = false;
11562c26ad4bSAdrien Destugues 	fHasUserName = false;
11572c26ad4bSAdrien Destugues 	fHasPassword = false;
11582c26ad4bSAdrien Destugues 
11592c26ad4bSAdrien Destugues 	bool hasUsernamePassword = B_ERROR != fAuthority.FindFirst('@');
11602c26ad4bSAdrien Destugues 	authority_parse_state state = AUTHORITY_USERNAME;
11612c26ad4bSAdrien Destugues 	int32 offset = 0;
11622c26ad4bSAdrien Destugues 	int32 length = authority.Length();
11632c26ad4bSAdrien Destugues 	const char *authority_c = authority.String();
11642c26ad4bSAdrien Destugues 
11652c26ad4bSAdrien Destugues 	while (AUTHORITY_COMPLETE != state && offset < length) {
11662c26ad4bSAdrien Destugues 
11672c26ad4bSAdrien Destugues 		switch (state) {
11682c26ad4bSAdrien Destugues 
11692c26ad4bSAdrien Destugues 			case AUTHORITY_USERNAME:
11702c26ad4bSAdrien Destugues 			{
11712c26ad4bSAdrien Destugues 				if (hasUsernamePassword) {
11722c26ad4bSAdrien Destugues 					int32 end_username = char_offset_until_fn_false(
11738f30879bSAndrew Lindesay 						authority_c, length, offset, _IsUsernameChar);
11742c26ad4bSAdrien Destugues 
11752c26ad4bSAdrien Destugues 					SetUserName(BString(&authority_c[offset],
11762c26ad4bSAdrien Destugues 						end_username - offset));
11772c26ad4bSAdrien Destugues 
11782c26ad4bSAdrien Destugues 					state = AUTHORITY_PASSWORD;
11792c26ad4bSAdrien Destugues 					offset = end_username;
11802c26ad4bSAdrien Destugues 				} else {
11812c26ad4bSAdrien Destugues 					state = AUTHORITY_HOST;
11822c26ad4bSAdrien Destugues 				}
11832c26ad4bSAdrien Destugues 				break;
11842c26ad4bSAdrien Destugues 			}
11852c26ad4bSAdrien Destugues 
11862c26ad4bSAdrien Destugues 			case AUTHORITY_PASSWORD:
11872c26ad4bSAdrien Destugues 			{
11882c26ad4bSAdrien Destugues 				if (hasUsernamePassword && ':' == authority[offset]) {
11892c26ad4bSAdrien Destugues 					offset++; // move past the delimiter
11902c26ad4bSAdrien Destugues 					int32 end_password = char_offset_until_fn_false(
11918f30879bSAndrew Lindesay 						authority_c, length, offset, _IsPasswordChar);
11922c26ad4bSAdrien Destugues 
11932c26ad4bSAdrien Destugues 					SetPassword(BString(&authority_c[offset],
11942c26ad4bSAdrien Destugues 						end_password - offset));
11952c26ad4bSAdrien Destugues 
11962c26ad4bSAdrien Destugues 					offset = end_password;
11972c26ad4bSAdrien Destugues 				}
11982c26ad4bSAdrien Destugues 
11992c26ad4bSAdrien Destugues 				// if the host was preceded by a username + password couple
12002c26ad4bSAdrien Destugues 				// then there will be an '@' delimiter to avoid.
12012c26ad4bSAdrien Destugues 
12022c26ad4bSAdrien Destugues 				if (authority_c[offset] == '@') {
12032c26ad4bSAdrien Destugues 					offset++;
12042c26ad4bSAdrien Destugues 				}
12052c26ad4bSAdrien Destugues 
12062c26ad4bSAdrien Destugues 				state = AUTHORITY_HOST;
12072c26ad4bSAdrien Destugues 				break;
12082c26ad4bSAdrien Destugues 			}
12092c26ad4bSAdrien Destugues 
12102c26ad4bSAdrien Destugues 			case AUTHORITY_HOST:
12112c26ad4bSAdrien Destugues 			{
12122c26ad4bSAdrien Destugues 
12132c26ad4bSAdrien Destugues 				// the host may be enclosed within brackets in order to express
12142c26ad4bSAdrien Destugues 				// an IPV6 address.
12152c26ad4bSAdrien Destugues 
12162c26ad4bSAdrien Destugues 				if (authority_c[offset] == '[') {
12172c26ad4bSAdrien Destugues 					int32 end_ipv6_host = char_offset_until_fn_false(
12188f30879bSAndrew Lindesay 						authority_c, length, offset + 1, _IsIPV6Char);
12192c26ad4bSAdrien Destugues 
12202c26ad4bSAdrien Destugues 					if (authority_c[end_ipv6_host] == ']') {
12212c26ad4bSAdrien Destugues 						SetHost(BString(&authority_c[offset],
12222c26ad4bSAdrien Destugues 							(end_ipv6_host - offset) + 1));
12232c26ad4bSAdrien Destugues 						state = AUTHORITY_PORT;
12242c26ad4bSAdrien Destugues 						offset = end_ipv6_host + 1;
12252c26ad4bSAdrien Destugues 					}
12262c26ad4bSAdrien Destugues 				}
12272c26ad4bSAdrien Destugues 
12282c26ad4bSAdrien Destugues 				// if an IPV6 host was not found.
12292c26ad4bSAdrien Destugues 
12302c26ad4bSAdrien Destugues 				if (AUTHORITY_HOST == state) {
12312c26ad4bSAdrien Destugues 					int32 end_host = char_offset_until_fn_false(
12328f30879bSAndrew Lindesay 						authority_c, length, offset, _IsHostChar);
12332c26ad4bSAdrien Destugues 
12342c26ad4bSAdrien Destugues 					SetHost(BString(&authority_c[offset], end_host - offset));
12352c26ad4bSAdrien Destugues 					state = AUTHORITY_PORT;
12362c26ad4bSAdrien Destugues 					offset = end_host;
12372c26ad4bSAdrien Destugues 				}
12382c26ad4bSAdrien Destugues 
12392c26ad4bSAdrien Destugues 				break;
12402c26ad4bSAdrien Destugues 			}
12412c26ad4bSAdrien Destugues 
12422c26ad4bSAdrien Destugues 			case AUTHORITY_PORT:
12432c26ad4bSAdrien Destugues 			{
12442c26ad4bSAdrien Destugues 				if (authority_c[offset] == ':') {
12452c26ad4bSAdrien Destugues 					offset++;
12462c26ad4bSAdrien Destugues 					int32 end_port = char_offset_until_fn_false(
12478f30879bSAndrew Lindesay 						authority_c, length, offset, _IsPortChar);
12482c26ad4bSAdrien Destugues 					SetPort(atoi(&authority_c[offset]));
12492c26ad4bSAdrien Destugues 					offset = end_port;
12502c26ad4bSAdrien Destugues 				}
12512c26ad4bSAdrien Destugues 
12522c26ad4bSAdrien Destugues 				state = AUTHORITY_COMPLETE;
12532c26ad4bSAdrien Destugues 
12542c26ad4bSAdrien Destugues 				break;
12552c26ad4bSAdrien Destugues 			}
12562c26ad4bSAdrien Destugues 
12572c26ad4bSAdrien Destugues 			case AUTHORITY_COMPLETE:
12582c26ad4bSAdrien Destugues 				// should never be reached - keeps the compiler happy
12592c26ad4bSAdrien Destugues 				break;
12602c26ad4bSAdrien Destugues 		}
12612c26ad4bSAdrien Destugues 	}
12622c26ad4bSAdrien Destugues 
12632c26ad4bSAdrien Destugues 	// An empty authority is still an authority, making it possible to have
12642c26ad4bSAdrien Destugues 	// URLs such as file:///path/to/file.
12652c26ad4bSAdrien Destugues 	// TODO however, there is no way to unset the authority once it is set...
12662c26ad4bSAdrien Destugues 	// We may want to take a const char* parameter and allow NULL.
12672c26ad4bSAdrien Destugues 	fHasHost = true;
12682c26ad4bSAdrien Destugues }
12692c26ad4bSAdrien Destugues 
12702c26ad4bSAdrien Destugues 
12712c26ad4bSAdrien Destugues /*static*/ BString
12722c26ad4bSAdrien Destugues BUrl::_DoUrlEncodeChunk(const BString& chunk, bool strict, bool directory)
12732c26ad4bSAdrien Destugues {
12742c26ad4bSAdrien Destugues 	BString result;
12752c26ad4bSAdrien Destugues 
12762c26ad4bSAdrien Destugues 	for (int32 i = 0; i < chunk.Length(); i++) {
12772c26ad4bSAdrien Destugues 		if (_IsUnreserved(chunk[i])
12782c26ad4bSAdrien Destugues 				|| (directory && (chunk[i] == '/' || chunk[i] == '\\'))) {
12792c26ad4bSAdrien Destugues 			result << chunk[i];
12802c26ad4bSAdrien Destugues 		} else {
12812c26ad4bSAdrien Destugues 			if (chunk[i] == ' ' && !strict) {
12822c26ad4bSAdrien Destugues 				result << '+';
12832c26ad4bSAdrien Destugues 					// In non-strict mode, spaces are encoded by a plus sign
12842c26ad4bSAdrien Destugues 			} else {
12852c26ad4bSAdrien Destugues 				char hexString[5];
12862c26ad4bSAdrien Destugues 				snprintf(hexString, 5, "%X", chunk[i]);
12872c26ad4bSAdrien Destugues 
12882c26ad4bSAdrien Destugues 				result << '%' << hexString;
12892c26ad4bSAdrien Destugues 			}
12902c26ad4bSAdrien Destugues 		}
12912c26ad4bSAdrien Destugues 	}
12922c26ad4bSAdrien Destugues 
12932c26ad4bSAdrien Destugues 	return result;
12942c26ad4bSAdrien Destugues }
12952c26ad4bSAdrien Destugues 
12962c26ad4bSAdrien Destugues 
12972c26ad4bSAdrien Destugues /*static*/ BString
12982c26ad4bSAdrien Destugues BUrl::_DoUrlDecodeChunk(const BString& chunk, bool strict)
12992c26ad4bSAdrien Destugues {
13002c26ad4bSAdrien Destugues 	BString result;
13012c26ad4bSAdrien Destugues 
13022c26ad4bSAdrien Destugues 	for (int32 i = 0; i < chunk.Length(); i++) {
13032c26ad4bSAdrien Destugues 		if (chunk[i] == '+' && !strict)
13042c26ad4bSAdrien Destugues 			result << ' ';
13052c26ad4bSAdrien Destugues 		else {
13062c26ad4bSAdrien Destugues 			char decoded = 0;
13072c26ad4bSAdrien Destugues 			char* out = NULL;
13082c26ad4bSAdrien Destugues 			char hexString[3];
13092c26ad4bSAdrien Destugues 
13102c26ad4bSAdrien Destugues 			if (chunk[i] == '%' && i < chunk.Length() - 2
13112c26ad4bSAdrien Destugues 				&& isxdigit(chunk[i + 1]) && isxdigit(chunk[i+2])) {
13122c26ad4bSAdrien Destugues 				hexString[0] = chunk[i + 1];
13132c26ad4bSAdrien Destugues 				hexString[1] = chunk[i + 2];
13142c26ad4bSAdrien Destugues 				hexString[2] = 0;
13152c26ad4bSAdrien Destugues 				decoded = (char)strtol(hexString, &out, 16);
13162c26ad4bSAdrien Destugues 			}
13172c26ad4bSAdrien Destugues 
13182c26ad4bSAdrien Destugues 			if (out == hexString + 2) {
13192c26ad4bSAdrien Destugues 				i += 2;
13202c26ad4bSAdrien Destugues 				result << decoded;
13212c26ad4bSAdrien Destugues 			} else
13222c26ad4bSAdrien Destugues 				result << chunk[i];
13232c26ad4bSAdrien Destugues 		}
13242c26ad4bSAdrien Destugues 	}
13252c26ad4bSAdrien Destugues 	return result;
13262c26ad4bSAdrien Destugues }
13272c26ad4bSAdrien Destugues 
13282c26ad4bSAdrien Destugues 
13292c26ad4bSAdrien Destugues bool
13308f30879bSAndrew Lindesay BUrl::_IsHostIPV6Valid(size_t offset, int32 length) const
13318f30879bSAndrew Lindesay {
13328f30879bSAndrew Lindesay 	for (int32 i = 0; i < length; i++) {
13338f30879bSAndrew Lindesay 		char c = fHost[offset + i];
13348f30879bSAndrew Lindesay 		if (!_IsIPV6Char(c))
13358f30879bSAndrew Lindesay 			return false;
13368f30879bSAndrew Lindesay 	}
13378f30879bSAndrew Lindesay 
13388f30879bSAndrew Lindesay 	return length > 0;
13398f30879bSAndrew Lindesay }
13408f30879bSAndrew Lindesay 
13418f30879bSAndrew Lindesay 
13428f30879bSAndrew Lindesay bool
13438f30879bSAndrew Lindesay BUrl::_IsHostValid() const
13448f30879bSAndrew Lindesay {
13458f30879bSAndrew Lindesay 	if (fHost.StartsWith("[") && fHost.EndsWith("]"))
13468f30879bSAndrew Lindesay 		return _IsHostIPV6Valid(1, fHost.Length() - 2);
13478f30879bSAndrew Lindesay 
13488f30879bSAndrew Lindesay 	bool lastWasDot = false;
13498f30879bSAndrew Lindesay 
13508f30879bSAndrew Lindesay 	for (int32 i = 0; i < fHost.Length(); i++) {
13518f30879bSAndrew Lindesay 		char c = fHost[i];
13528f30879bSAndrew Lindesay 
13538f30879bSAndrew Lindesay 		if (c == '.') {
13548f30879bSAndrew Lindesay 			if (lastWasDot || i == 0)
13558f30879bSAndrew Lindesay 				return false;
13568f30879bSAndrew Lindesay 			lastWasDot = true;
13578f30879bSAndrew Lindesay 		} else {
13588f30879bSAndrew Lindesay 			lastWasDot = false;
13598f30879bSAndrew Lindesay 		}
13608f30879bSAndrew Lindesay 
13618f30879bSAndrew Lindesay 		if (!_IsHostChar(c) && c != '.') {
13628f30879bSAndrew Lindesay 			// the underscore is technically not allowed, but occurs sometimes
13638f30879bSAndrew Lindesay 			// in the wild.
13648f30879bSAndrew Lindesay 			return false;
13658f30879bSAndrew Lindesay 		}
13668f30879bSAndrew Lindesay 	}
13678f30879bSAndrew Lindesay 
13688f30879bSAndrew Lindesay 	return true;
13698f30879bSAndrew Lindesay }
13708f30879bSAndrew Lindesay 
13718f30879bSAndrew Lindesay 
13728f30879bSAndrew Lindesay bool
13738f30879bSAndrew Lindesay BUrl::_IsProtocolValid() const
13742c26ad4bSAdrien Destugues {
13752c26ad4bSAdrien Destugues 	for (int8 index = 0; index < fProtocol.Length(); index++) {
13762c26ad4bSAdrien Destugues 		char c = fProtocol[index];
13772c26ad4bSAdrien Destugues 
13782c26ad4bSAdrien Destugues 		if (index == 0 && !isalpha(c))
13792c26ad4bSAdrien Destugues 			return false;
13802c26ad4bSAdrien Destugues 		else if (!isalnum(c) && c != '+' && c != '-' && c != '.')
13812c26ad4bSAdrien Destugues 			return false;
13822c26ad4bSAdrien Destugues 	}
13832c26ad4bSAdrien Destugues 
13848f30879bSAndrew Lindesay 	return !fProtocol.IsEmpty();
13852c26ad4bSAdrien Destugues }
13862c26ad4bSAdrien Destugues 
13872c26ad4bSAdrien Destugues 
13882c26ad4bSAdrien Destugues bool
13892c26ad4bSAdrien Destugues BUrl::_IsUnreserved(char c)
13902c26ad4bSAdrien Destugues {
13912c26ad4bSAdrien Destugues 	return isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~';
13922c26ad4bSAdrien Destugues }
13932c26ad4bSAdrien Destugues 
13942c26ad4bSAdrien Destugues 
13952c26ad4bSAdrien Destugues bool
13962c26ad4bSAdrien Destugues BUrl::_IsGenDelim(char c)
13972c26ad4bSAdrien Destugues {
13982c26ad4bSAdrien Destugues 	return c == ':' || c == '/' || c == '?' || c == '#' || c == '['
13992c26ad4bSAdrien Destugues 		|| c == ']' || c == '@';
14002c26ad4bSAdrien Destugues }
14012c26ad4bSAdrien Destugues 
14022c26ad4bSAdrien Destugues 
14032c26ad4bSAdrien Destugues bool
14042c26ad4bSAdrien Destugues BUrl::_IsSubDelim(char c)
14052c26ad4bSAdrien Destugues {
14062c26ad4bSAdrien Destugues 	return c == '!' || c == '$' || c == '&' || c == '\'' || c == '('
14072c26ad4bSAdrien Destugues 		|| c == ')' || c == '*' || c == '+' || c == ',' || c == ';'
14082c26ad4bSAdrien Destugues 		|| c == '=';
14092c26ad4bSAdrien Destugues }
14102c26ad4bSAdrien Destugues 
14112c26ad4bSAdrien Destugues 
14128f30879bSAndrew Lindesay bool
14138f30879bSAndrew Lindesay BUrl::_IsUsernameChar(char c)
14148f30879bSAndrew Lindesay {
14158f30879bSAndrew Lindesay 	return !(c == ':' || c == '@');
14168f30879bSAndrew Lindesay }
14178f30879bSAndrew Lindesay 
14188f30879bSAndrew Lindesay 
14198f30879bSAndrew Lindesay bool
14208f30879bSAndrew Lindesay BUrl::_IsPasswordChar(char c)
14218f30879bSAndrew Lindesay {
14228f30879bSAndrew Lindesay 	return !(c == '@');
14238f30879bSAndrew Lindesay }
14248f30879bSAndrew Lindesay 
14258f30879bSAndrew Lindesay 
14268f30879bSAndrew Lindesay bool
14278f30879bSAndrew Lindesay BUrl::_IsHostChar(char c)
14288f30879bSAndrew Lindesay {
14298f30879bSAndrew Lindesay 	return ((uint8) c) > 127 || isalnum(c) || c == '-' || c == '_' || c == '.'
14308f30879bSAndrew Lindesay 		|| c == '%';
14318f30879bSAndrew Lindesay }
14328f30879bSAndrew Lindesay 
14338f30879bSAndrew Lindesay 
14348f30879bSAndrew Lindesay bool
14358f30879bSAndrew Lindesay BUrl::_IsPortChar(char c)
14368f30879bSAndrew Lindesay {
14378f30879bSAndrew Lindesay 	return isdigit(c);
14388f30879bSAndrew Lindesay }
14398f30879bSAndrew Lindesay 
14408f30879bSAndrew Lindesay 
14418f30879bSAndrew Lindesay bool
14428f30879bSAndrew Lindesay BUrl::_IsIPV6Char(char c)
14438f30879bSAndrew Lindesay {
14448f30879bSAndrew Lindesay 	return c == ':' || isxdigit(c);
14458f30879bSAndrew Lindesay }
14468f30879bSAndrew Lindesay 
14478f30879bSAndrew Lindesay 
14482c26ad4bSAdrien Destugues BString
14492c26ad4bSAdrien Destugues BUrl::_UrlMimeType() const
14502c26ad4bSAdrien Destugues {
14512c26ad4bSAdrien Destugues 	BString mime;
14522c26ad4bSAdrien Destugues 	mime << "application/x-vnd.Be.URL." << fProtocol;
14532c26ad4bSAdrien Destugues 
14542c26ad4bSAdrien Destugues 	return BString(mime);
14552c26ad4bSAdrien Destugues }
1456