145939109SStephan Aßmus/*
28f30879bSAndrew Lindesay * Copyright 2010-2018 Haiku Inc. All rights reserved.
345939109SStephan Aßmus * Distributed under the terms of the MIT License.
445939109SStephan Aßmus *
545939109SStephan Aßmus * Authors:
645939109SStephan Aßmus *		Christophe Huriaux, c.huriaux@gmail.com
7fa2dd9c4SAndrew Lindesay *		Andrew Lindesay, apl@lindesay.co.nz
845939109SStephan Aßmus */
945939109SStephan Aßmus
1045939109SStephan Aßmus
11400153ebSAdrien Destugues#include <Url.h>
12400153ebSAdrien Destugues
1345939109SStephan Aßmus#include <ctype.h>
1445939109SStephan Aßmus#include <cstdio>
1545939109SStephan Aßmus#include <cstdlib>
1645939109SStephan Aßmus#include <new>
1745939109SStephan Aßmus
1872f6b787SIngo Weinhold#include <MimeType.h>
1972f6b787SIngo Weinhold#include <Roster.h>
2072f6b787SIngo Weinhold
21fa2dd9c4SAndrew Lindesay#ifdef HAIKU_TARGET_PLATFORM_HAIKU
22fa2dd9c4SAndrew Lindesay	#include <ICUWrapper.h>
23fa2dd9c4SAndrew Lindesay#endif
24400153ebSAdrien Destugues
25fa2dd9c4SAndrew Lindesay#ifdef HAIKU_TARGET_PLATFORM_HAIKU
26fa2dd9c4SAndrew Lindesay	#include <unicode/idna.h>
27fa2dd9c4SAndrew Lindesay	#include <unicode/stringpiece.h>
28fa2dd9c4SAndrew Lindesay#endif
299bf4e994SAdrien Destugues
3045939109SStephan Aßmus
3145939109SStephan Aßmusstatic const char* kArchivedUrl = "be:url string";
3245939109SStephan Aßmus
33a609673cSAndrew Lindesay/*! These flags can be combined to control the parse process. */
34a609673cSAndrew Lindesay
35a609673cSAndrew Lindesayconst uint32 PARSE_NO_MASK_BIT				= 0x00000000;
36a609673cSAndrew Lindesayconst uint32 PARSE_RAW_PATH_MASK_BIT		= 0x00000001;
37a609673cSAndrew Lindesay
3845939109SStephan Aßmus
3945939109SStephan AßmusBUrl::BUrl(const char* url)
4097ddf901SJérôme Duval	:
4145939109SStephan Aßmus	fUrlString(),
4245939109SStephan Aßmus	fProtocol(),
4345939109SStephan Aßmus	fUser(),
4445939109SStephan Aßmus	fPassword(),
4545939109SStephan Aßmus	fHost(),
4645939109SStephan Aßmus	fPort(0),
4745939109SStephan Aßmus	fPath(),
4845939109SStephan Aßmus	fRequest(),
49cd805f67SAdrien Destugues	fHasHost(false),
50b5bde4adSAdrien Destugues	fHasFragment(false)
5145939109SStephan Aßmus{
5245939109SStephan Aßmus	SetUrlString(url);
5345939109SStephan Aßmus}
5445939109SStephan Aßmus
5545939109SStephan Aßmus
5645939109SStephan AßmusBUrl::BUrl(BMessage* archive)
5797ddf901SJérôme Duval	:
5845939109SStephan Aßmus	fUrlString(),
5945939109SStephan Aßmus	fProtocol(),
6045939109SStephan Aßmus	fUser(),
6145939109SStephan Aßmus	fPassword(),
6245939109SStephan Aßmus	fHost(),
6345939109SStephan Aßmus	fPort(0),
6445939109SStephan Aßmus	fPath(),
6545939109SStephan Aßmus	fRequest(),
66cd805f67SAdrien Destugues	fHasHost(false),
67b5bde4adSAdrien Destugues	fHasFragment(false)
6845939109SStephan Aßmus{
6945939109SStephan Aßmus	BString url;
7045939109SStephan Aßmus
7145939109SStephan Aßmus	if (archive->FindString(kArchivedUrl, &url) == B_OK)
7245939109SStephan Aßmus		SetUrlString(url);
73b3d13a00SAdrien Destugues	else
74b3d13a00SAdrien Destugues		_ResetFields();
7545939109SStephan Aßmus}
7645939109SStephan Aßmus
7745939109SStephan Aßmus
7845939109SStephan AßmusBUrl::BUrl(const BUrl& other)
7997ddf901SJérôme Duval	:
8045939109SStephan Aßmus	BArchivable(),
8145939109SStephan Aßmus	fUrlString(),
8221f8e588SAdrien Destugues	fProtocol(other.fProtocol),
8321f8e588SAdrien Destugues	fUser(other.fUser),
8421f8e588SAdrien Destugues	fPassword(other.fPassword),
8521f8e588SAdrien Destugues	fHost(other.fHost),
8621f8e588SAdrien Destugues	fPort(other.fPort),
8721f8e588SAdrien Destugues	fPath(other.fPath),
8821f8e588SAdrien Destugues	fRequest(other.fRequest),
8921f8e588SAdrien Destugues	fFragment(other.fFragment),
9021f8e588SAdrien Destugues	fUrlStringValid(other.fUrlStringValid),
9121f8e588SAdrien Destugues	fAuthorityValid(other.fAuthorityValid),
9221f8e588SAdrien Destugues	fUserInfoValid(other.fUserInfoValid),
9321f8e588SAdrien Destugues	fHasProtocol(other.fHasProtocol),
9421f8e588SAdrien Destugues	fHasUserName(other.fHasUserName),
9521f8e588SAdrien Destugues	fHasPassword(other.fHasPassword),
9621f8e588SAdrien Destugues	fHasHost(other.fHasHost),
9721f8e588SAdrien Destugues	fHasPort(other.fHasPort),
9821f8e588SAdrien Destugues	fHasPath(other.fHasPath),
9921f8e588SAdrien Destugues	fHasRequest(other.fHasRequest),
10021f8e588SAdrien Destugues	fHasFragment(other.fHasFragment)
10145939109SStephan Aßmus{
10221f8e588SAdrien Destugues	if (fUrlStringValid)
1037603710eSAdrien Destugues		fUrlString = other.fUrlString;
10421f8e588SAdrien Destugues
10521f8e588SAdrien Destugues	if (fAuthorityValid)
1067603710eSAdrien Destugues		fAuthority = other.fAuthority;
10721f8e588SAdrien Destugues
10821f8e588SAdrien Destugues	if (fUserInfoValid)
1097603710eSAdrien Destugues		fUserInfo = other.fUserInfo;
11021f8e588SAdrien Destugues
11145939109SStephan Aßmus}
11245939109SStephan Aßmus
11345939109SStephan Aßmus
114ced0e0beSAdrien DestuguesBUrl::BUrl(const BUrl& base, const BString& location)
11597ddf901SJérôme Duval	:
11645939109SStephan Aßmus	fUrlString(),
11745939109SStephan Aßmus	fProtocol(),
11845939109SStephan Aßmus	fUser(),
11945939109SStephan Aßmus	fPassword(),
12045939109SStephan Aßmus	fHost(),
12145939109SStephan Aßmus	fPort(0),
12245939109SStephan Aßmus	fPath(),
12345939109SStephan Aßmus	fRequest(),
1244e14963bSAdrien Destugues	fAuthorityValid(false),
1254e14963bSAdrien Destugues	fUserInfoValid(false),
1264e14963bSAdrien Destugues	fHasUserName(false),
1274e14963bSAdrien Destugues	fHasPassword(false),
128cd805f67SAdrien Destugues	fHasHost(false),
1294e14963bSAdrien Destugues	fHasPort(false),
130b5bde4adSAdrien Destugues	fHasFragment(false)
13145939109SStephan Aßmus{
132ced0e0beSAdrien Destugues	// This implements the algorithm in RFC3986, Section 5.2.
13345939109SStephan Aßmus
134a609673cSAndrew Lindesay	BUrl relative;
135a609673cSAndrew Lindesay	relative._ExplodeUrlString(location, PARSE_RAW_PATH_MASK_BIT);
136a609673cSAndrew Lindesay		// This parse will leave the path 'raw' so that it still carries any
137a609673cSAndrew Lindesay		// special sequences such as '..' and '.' in it.  This way it can be
138a609673cSAndrew Lindesay		// later combined with the base.
139a609673cSAndrew Lindesay
14097ddf901SJérôme Duval	if (relative.HasProtocol()) {
141ced0e0beSAdrien Destugues		SetProtocol(relative.Protocol());
142b5bde4adSAdrien Destugues		if (relative.HasAuthority())
143b5bde4adSAdrien Destugues			SetAuthority(relative.Authority());
144b5bde4adSAdrien Destugues		SetPath(relative.Path());
145ced0e0beSAdrien Destugues		SetRequest(relative.Request());
146ced0e0beSAdrien Destugues	} else {
14797ddf901SJérôme Duval		if (relative.HasAuthority()) {
148ced0e0beSAdrien Destugues			SetAuthority(relative.Authority());
149b5bde4adSAdrien Destugues			SetPath(relative.Path());
150ced0e0beSAdrien Destugues			SetRequest(relative.Request());
151ced0e0beSAdrien Destugues		} else {
15297ddf901SJérôme Duval			if (relative.Path().IsEmpty()) {
153b5bde4adSAdrien Destugues				_SetPathUnsafe(base.Path());
15497ddf901SJérôme Duval				if (relative.HasRequest())
155ced0e0beSAdrien Destugues					SetRequest(relative.Request());
156ced0e0beSAdrien Destugues				else
157e29e0a0dSAdrien Destugues					SetRequest(base.Request());
158ced0e0beSAdrien Destugues			} else {
159ced0e0beSAdrien Destugues				if (relative.Path()[0] == '/')
160ced0e0beSAdrien Destugues					SetPath(relative.Path());
161ced0e0beSAdrien Destugues				else {
162b5bde4adSAdrien Destugues					BString path = base._MergePath(relative.Path());
163ced0e0beSAdrien Destugues					SetPath(path);
164ced0e0beSAdrien Destugues				}
165ced0e0beSAdrien Destugues				SetRequest(relative.Request());
166ced0e0beSAdrien Destugues			}
167b5bde4adSAdrien Destugues
168b5bde4adSAdrien Destugues			if (base.HasAuthority())
169b5bde4adSAdrien Destugues				SetAuthority(base.Authority());
170ced0e0beSAdrien Destugues		}
171ced0e0beSAdrien Destugues		SetProtocol(base.Protocol());
172ced0e0beSAdrien Destugues	}
17345939109SStephan Aßmus
174b5bde4adSAdrien Destugues	if (relative.HasFragment())
175b5bde4adSAdrien Destugues		SetFragment(relative.Fragment());
17645939109SStephan Aßmus}
17745939109SStephan Aßmus
17845939109SStephan Aßmus
179ced0e0beSAdrien DestuguesBUrl::BUrl()
180ced0e0beSAdrien Destugues	:
181ced0e0beSAdrien Destugues	fUrlString(),
182ced0e0beSAdrien Destugues	fProtocol(),
183ced0e0beSAdrien Destugues	fUser(),
184ced0e0beSAdrien Destugues	fPassword(),
185ced0e0beSAdrien Destugues	fHost(),
186ced0e0beSAdrien Destugues	fPort(0),
187ced0e0beSAdrien Destugues	fPath(),
188ced0e0beSAdrien Destugues	fRequest(),
189cd805f67SAdrien Destugues	fHasHost(false),
190b5bde4adSAdrien Destugues	fHasFragment(false)
191ced0e0beSAdrien Destugues{
192b3d13a00SAdrien Destugues	_ResetFields();
193ced0e0beSAdrien Destugues}
19445939109SStephan Aßmus
19545939109SStephan Aßmus
196158ae743SAdrien DestuguesBUrl::BUrl(const BPath& path)
197158ae743SAdrien Destugues	:
198158ae743SAdrien Destugues	fUrlString(),
199158ae743SAdrien Destugues	fProtocol(),
200158ae743SAdrien Destugues	fUser(),
201158ae743SAdrien Destugues	fPassword(),
202158ae743SAdrien Destugues	fHost(),
203158ae743SAdrien Destugues	fPort(0),
204158ae743SAdrien Destugues	fPath(),
205158ae743SAdrien Destugues	fRequest(),
206158ae743SAdrien Destugues	fHasHost(false),
207158ae743SAdrien Destugues	fHasFragment(false)
208158ae743SAdrien Destugues{
209158ae743SAdrien Destugues	SetUrlString(UrlEncode(path.Path(), true, true));
210158ae743SAdrien Destugues	SetProtocol("file");
211158ae743SAdrien Destugues}
212158ae743SAdrien Destugues
213158ae743SAdrien Destugues
214ced0e0beSAdrien DestuguesBUrl::~BUrl()
215a5ac24f0SAdrien Destugues{
216ced0e0beSAdrien Destugues}
217a5ac24f0SAdrien Destugues
218a5ac24f0SAdrien Destugues
219ced0e0beSAdrien Destugues// #pragma mark URL fields modifiers
220a5ac24f0SAdrien Destugues
221a5ac24f0SAdrien Destugues
22245939109SStephan AßmusBUrl&
22345939109SStephan AßmusBUrl::SetUrlString(const BString& url)
22445939109SStephan Aßmus{
225a609673cSAndrew Lindesay	_ExplodeUrlString(url, PARSE_NO_MASK_BIT);
22645939109SStephan Aßmus	return *this;
22745939109SStephan Aßmus}
22845939109SStephan Aßmus
22945939109SStephan Aßmus
23045939109SStephan AßmusBUrl&
23145939109SStephan AßmusBUrl::SetProtocol(const BString& protocol)
23245939109SStephan Aßmus{
23345939109SStephan Aßmus	fProtocol = protocol;
234ced0e0beSAdrien Destugues	fHasProtocol = !fProtocol.IsEmpty();
23545939109SStephan Aßmus	fUrlStringValid = false;
23645939109SStephan Aßmus	return *this;
23745939109SStephan Aßmus}
23845939109SStephan Aßmus
23945939109SStephan Aßmus
24045939109SStephan AßmusBUrl&
24145939109SStephan AßmusBUrl::SetUserName(const BString& user)
24245939109SStephan Aßmus{
24345939109SStephan Aßmus	fUser = user;
244ced0e0beSAdrien Destugues	fHasUserName = !fUser.IsEmpty();
24545939109SStephan Aßmus	fUrlStringValid = false;
24645939109SStephan Aßmus	fAuthorityValid = false;
24745939109SStephan Aßmus	fUserInfoValid = false;
24845939109SStephan Aßmus	return *this;
24945939109SStephan Aßmus}
25045939109SStephan Aßmus
25145939109SStephan Aßmus
25245939109SStephan AßmusBUrl&
25345939109SStephan AßmusBUrl::SetPassword(const BString& password)
25445939109SStephan Aßmus{
25545939109SStephan Aßmus	fPassword = password;
256ced0e0beSAdrien Destugues	fHasPassword = !fPassword.IsEmpty();
25745939109SStephan Aßmus	fUrlStringValid = false;
25845939109SStephan Aßmus	fAuthorityValid = false;
25945939109SStephan Aßmus	fUserInfoValid = false;
26045939109SStephan Aßmus	return *this;
26145939109SStephan Aßmus}
26245939109SStephan Aßmus
26345939109SStephan Aßmus
26445939109SStephan AßmusBUrl&
26545939109SStephan AßmusBUrl::SetHost(const BString& host)
26645939109SStephan Aßmus{
26745939109SStephan Aßmus	fHost = host;
268ced0e0beSAdrien Destugues	fHasHost = !fHost.IsEmpty();
26945939109SStephan Aßmus	fUrlStringValid = false;
27045939109SStephan Aßmus	fAuthorityValid = false;
27145939109SStephan Aßmus	return *this;
27245939109SStephan Aßmus}
27345939109SStephan Aßmus
27445939109SStephan Aßmus
27545939109SStephan AßmusBUrl&
27645939109SStephan AßmusBUrl::SetPort(int port)
27745939109SStephan Aßmus{
27845939109SStephan Aßmus	fPort = port;
279ced0e0beSAdrien Destugues	fHasPort = (port != 0);
28045939109SStephan Aßmus	fUrlStringValid = false;
28145939109SStephan Aßmus	fAuthorityValid = false;
28245939109SStephan Aßmus	return *this;
28345939109SStephan Aßmus}
28445939109SStephan Aßmus
28545939109SStephan Aßmus
286a609673cSAndrew Lindesayvoid
287a609673cSAndrew LindesayBUrl::_RemoveLastPathComponent(BString& path)
288a609673cSAndrew Lindesay{
289a609673cSAndrew Lindesay	int32 outputLastSlashIdx = path.FindLast('/');
290a609673cSAndrew Lindesay
291a609673cSAndrew Lindesay	if (outputLastSlashIdx == B_ERROR)
292a609673cSAndrew Lindesay		path.Truncate(0);
293a609673cSAndrew Lindesay	else
294a609673cSAndrew Lindesay		path.Truncate(outputLastSlashIdx);
295a609673cSAndrew Lindesay}
296a609673cSAndrew Lindesay
297a609673cSAndrew Lindesay
29845939109SStephan AßmusBUrl&
29945939109SStephan AßmusBUrl::SetPath(const BString& path)
30045939109SStephan Aßmus{
301b5bde4adSAdrien Destugues	// Implements RFC3986 section 5.2.4, "Remove dot segments"
302b5bde4adSAdrien Destugues
303b5bde4adSAdrien Destugues	// 1.
304b5bde4adSAdrien Destugues	BString output;
305b5bde4adSAdrien Destugues	BString input(path);
306b5bde4adSAdrien Destugues
307b5bde4adSAdrien Destugues	// 2.
308cd6365c7SJérôme Duval	while (!input.IsEmpty()) {
309b5bde4adSAdrien Destugues		// 2.A.
310cd6365c7SJérôme Duval		if (input.StartsWith("./")) {
311b5bde4adSAdrien Destugues			input.Remove(0, 2);
312b5bde4adSAdrien Destugues			continue;
313b5bde4adSAdrien Destugues		}
314b5bde4adSAdrien Destugues
315cd6365c7SJérôme Duval		if (input.StartsWith("../")) {
316b5bde4adSAdrien Destugues			input.Remove(0, 3);
317b5bde4adSAdrien Destugues			continue;
318b5bde4adSAdrien Destugues		}
319b5bde4adSAdrien Destugues
320b5bde4adSAdrien Destugues		// 2.B.
321cd6365c7SJérôme Duval		if (input.StartsWith("/./")) {
322b5bde4adSAdrien Destugues			input.Remove(0, 2);
323b5bde4adSAdrien Destugues			continue;
324b5bde4adSAdrien Destugues		}
325b5bde4adSAdrien Destugues
326cd6365c7SJérôme Duval		if (input == "/.") {
327b5bde4adSAdrien Destugues			input.Remove(1, 1);
328b5bde4adSAdrien Destugues			continue;
329b5bde4adSAdrien Destugues		}
330b5bde4adSAdrien Destugues
331b5bde4adSAdrien Destugues		// 2.C.
332cd6365c7SJérôme Duval		if (input.StartsWith("/../")) {
333b5bde4adSAdrien Destugues			input.Remove(0, 3);
334a609673cSAndrew Lindesay			_RemoveLastPathComponent(output);
335b5bde4adSAdrien Destugues			continue;
336b5bde4adSAdrien Destugues		}
337b5bde4adSAdrien Destugues
338cd6365c7SJérôme Duval		if (input == "/..") {
339b5bde4adSAdrien Destugues			input.Remove(1, 2);
340a609673cSAndrew Lindesay			_RemoveLastPathComponent(output);
341b5bde4adSAdrien Destugues			continue;
342b5bde4adSAdrien Destugues		}
343b5bde4adSAdrien Destugues
344b5bde4adSAdrien Destugues		// 2.D.
345cd6365c7SJérôme Duval		if (input == "." || input == "..") {
346b5bde4adSAdrien Destugues			break;
347b5bde4adSAdrien Destugues		}
348b5bde4adSAdrien Destugues
349cd6365c7SJérôme Duval		if (input == "/.") {
350b5bde4adSAdrien Destugues			input.Remove(1, 1);
351b5bde4adSAdrien Destugues			continue;
352b5bde4adSAdrien Destugues		}
353b5bde4adSAdrien Destugues
354b5bde4adSAdrien Destugues		// 2.E.
355b5bde4adSAdrien Destugues		int slashpos = input.FindFirst('/', 1);
356b5bde4adSAdrien Destugues		if (slashpos > 0) {
357b5bde4adSAdrien Destugues			output.Append(input, slashpos);
358b5bde4adSAdrien Destugues			input.Remove(0, slashpos);
359b5bde4adSAdrien Destugues		} else {
360b5bde4adSAdrien Destugues			output.Append(input);
361b5bde4adSAdrien Destugues			break;
362b5bde4adSAdrien Destugues		}
363b5bde4adSAdrien Destugues	}
364b5bde4adSAdrien Destugues
365b5bde4adSAdrien Destugues	_SetPathUnsafe(output);
36645939109SStephan Aßmus	return *this;
36745939109SStephan Aßmus}
36845939109SStephan Aßmus
36945939109SStephan Aßmus
37045939109SStephan AßmusBUrl&
37145939109SStephan AßmusBUrl::SetRequest(const BString& request)
37245939109SStephan Aßmus{
37345939109SStephan Aßmus	fRequest = request;
374ced0e0beSAdrien Destugues	fHasRequest = !fRequest.IsEmpty();
37545939109SStephan Aßmus	fUrlStringValid = false;
37645939109SStephan Aßmus	return *this;
37745939109SStephan Aßmus}
37845939109SStephan Aßmus
37945939109SStephan Aßmus
38045939109SStephan AßmusBUrl&
38145939109SStephan AßmusBUrl::SetFragment(const BString& fragment)
38245939109SStephan Aßmus{
38345939109SStephan Aßmus	fFragment = fragment;
384b5bde4adSAdrien Destugues	fHasFragment = true;
38545939109SStephan Aßmus	fUrlStringValid = false;
38645939109SStephan Aßmus	return *this;
38745939109SStephan Aßmus}
38845939109SStephan Aßmus
38945939109SStephan Aßmus
39045939109SStephan Aßmus// #pragma mark URL fields access
39145939109SStephan Aßmus
39245939109SStephan Aßmus
39345939109SStephan Aßmusconst BString&
39445939109SStephan AßmusBUrl::UrlString() const
39545939109SStephan Aßmus{
39645939109SStephan Aßmus	if (!fUrlStringValid) {
39745939109SStephan Aßmus		fUrlString.Truncate(0);
39845939109SStephan Aßmus
39945939109SStephan Aßmus		if (HasProtocol()) {
40045939109SStephan Aßmus			fUrlString << fProtocol << ':';
40145939109SStephan Aßmus		}
40245939109SStephan Aßmus
403f4db7fdcSAdrien Destugues		if (HasAuthority()) {
404f4db7fdcSAdrien Destugues			fUrlString << "//";
405f4db7fdcSAdrien Destugues			fUrlString << Authority();
406f4db7fdcSAdrien Destugues		}
40745939109SStephan Aßmus		fUrlString << Path();
40845939109SStephan Aßmus
40945939109SStephan Aßmus		if (HasRequest())
41045939109SStephan Aßmus			fUrlString << '?' << fRequest;
41145939109SStephan Aßmus
41245939109SStephan Aßmus		if (HasFragment())
41345939109SStephan Aßmus			fUrlString << '#' << fFragment;
41445939109SStephan Aßmus
41545939109SStephan Aßmus		fUrlStringValid = true;
41645939109SStephan Aßmus	}
41745939109SStephan Aßmus
41845939109SStephan Aßmus	return fUrlString;
41945939109SStephan Aßmus}
42045939109SStephan Aßmus
42145939109SStephan Aßmus
42245939109SStephan Aßmusconst BString&
42345939109SStephan AßmusBUrl::Protocol() const
42445939109SStephan Aßmus{
42545939109SStephan Aßmus	return fProtocol;
42645939109SStephan Aßmus}
42745939109SStephan Aßmus
42845939109SStephan Aßmus
42945939109SStephan Aßmusconst BString&
43045939109SStephan AßmusBUrl::UserName() const
43145939109SStephan Aßmus{
43245939109SStephan Aßmus	return fUser;
43345939109SStephan Aßmus}
43445939109SStephan Aßmus
43545939109SStephan Aßmus
43645939109SStephan Aßmusconst BString&
43745939109SStephan AßmusBUrl::Password() const
43845939109SStephan Aßmus{
43945939109SStephan Aßmus	return fPassword;
44045939109SStephan Aßmus}
44145939109SStephan Aßmus
44245939109SStephan Aßmus
44345939109SStephan Aßmusconst BString&
44445939109SStephan AßmusBUrl::UserInfo() const
44545939109SStephan Aßmus{
44645939109SStephan Aßmus	if (!fUserInfoValid) {
44745939109SStephan Aßmus		fUserInfo = fUser;
44845939109SStephan Aßmus
44945939109SStephan Aßmus		if (HasPassword())
45045939109SStephan Aßmus			fUserInfo << ':' << fPassword;
45145939109SStephan Aßmus
45245939109SStephan Aßmus		fUserInfoValid = true;
45345939109SStephan Aßmus	}
45445939109SStephan Aßmus
45545939109SStephan Aßmus	return fUserInfo;
45645939109SStephan Aßmus}
45745939109SStephan Aßmus
45845939109SStephan Aßmus
45945939109SStephan Aßmusconst BString&
46045939109SStephan AßmusBUrl::Host() const
46145939109SStephan Aßmus{
46245939109SStephan Aßmus	return fHost;
46345939109SStephan Aßmus}
46445939109SStephan Aßmus
46545939109SStephan Aßmus
46645939109SStephan Aßmusint
46745939109SStephan AßmusBUrl::Port() const
46845939109SStephan Aßmus{
46945939109SStephan Aßmus	return fPort;
47045939109SStephan Aßmus}
47145939109SStephan Aßmus
47245939109SStephan Aßmus
47345939109SStephan Aßmusconst BString&
47445939109SStephan AßmusBUrl::Authority() const
47545939109SStephan Aßmus{
47645939109SStephan Aßmus	if (!fAuthorityValid) {
47745939109SStephan Aßmus		fAuthority.Truncate(0);
47845939109SStephan Aßmus
47945939109SStephan Aßmus		if (HasUserInfo())
48045939109SStephan Aßmus			fAuthority << UserInfo() << '@';
48145939109SStephan Aßmus		fAuthority << Host();
48245939109SStephan Aßmus
48345939109SStephan Aßmus		if (HasPort())
48445939109SStephan Aßmus			fAuthority << ':' << fPort;
48597ddf901SJérôme Duval
48645939109SStephan Aßmus		fAuthorityValid = true;
48745939109SStephan Aßmus	}
48845939109SStephan Aßmus	return fAuthority;
48945939109SStephan Aßmus}
49045939109SStephan Aßmus
49145939109SStephan Aßmus
49245939109SStephan Aßmusconst BString&
49345939109SStephan AßmusBUrl::Path() const
49445939109SStephan Aßmus{
49545939109SStephan Aßmus	return fPath;
49645939109SStephan Aßmus}
49745939109SStephan Aßmus
49845939109SStephan Aßmus
49945939109SStephan Aßmusconst BString&
50045939109SStephan AßmusBUrl::Request() const
50145939109SStephan Aßmus{
50245939109SStephan Aßmus	return fRequest;
50345939109SStephan Aßmus}
50445939109SStephan Aßmus
50545939109SStephan Aßmus
50645939109SStephan Aßmusconst BString&
50745939109SStephan AßmusBUrl::Fragment() const
50845939109SStephan Aßmus{
50945939109SStephan Aßmus	return fFragment;
51045939109SStephan Aßmus}
51145939109SStephan Aßmus
51245939109SStephan Aßmus
51345939109SStephan Aßmus// #pragma mark URL fields tests
51445939109SStephan Aßmus
51545939109SStephan Aßmus
51645939109SStephan Aßmusbool
51745939109SStephan AßmusBUrl::IsValid() const
51845939109SStephan Aßmus{
519fa2dd9c4SAndrew Lindesay	if (!fHasProtocol)
520fa2dd9c4SAndrew Lindesay		return false;
521fa2dd9c4SAndrew Lindesay
5228f30879bSAndrew Lindesay	if (!_IsProtocolValid())
5238f30879bSAndrew Lindesay		return false;
5248f30879bSAndrew Lindesay
5258f30879bSAndrew Lindesay	// it is possible that there can be an authority but no host.
5268f30879bSAndrew Lindesay	// wierd://tea:tree@/x
5278f30879bSAndrew Lindesay	if (HasHost() && !(fHost.IsEmpty() && HasAuthority()) && !_IsHostValid())
5288f30879bSAndrew Lindesay		return false;
5298f30879bSAndrew Lindesay
530cf657294SAndrew Lindesay	if (fProtocol == "http" || fProtocol == "https" || fProtocol == "ftp"
531cf657294SAndrew Lindesay		|| fProtocol == "ipp" || fProtocol == "afp" || fProtocol == "telnet"
532cf657294SAndrew Lindesay		|| fProtocol == "gopher" || fProtocol == "nntp" || fProtocol == "sftp"
533cf657294SAndrew Lindesay		|| fProtocol == "finger" || fProtocol == "pop" || fProtocol == "imap") {
5348f30879bSAndrew Lindesay		return HasHost() && !fHost.IsEmpty();
535cf657294SAndrew Lindesay	}
536cf657294SAndrew Lindesay
537cf657294SAndrew Lindesay	if (fProtocol == "file")
538cf657294SAndrew Lindesay		return fHasPath;
539fa2dd9c4SAndrew Lindesay
540cf657294SAndrew Lindesay	return true;
54145939109SStephan Aßmus}
54245939109SStephan Aßmus
54345939109SStephan Aßmus
54445939109SStephan Aßmusbool
54545939109SStephan AßmusBUrl::HasProtocol() const
54645939109SStephan Aßmus{
54745939109SStephan Aßmus	return fHasProtocol;
54845939109SStephan Aßmus}
54945939109SStephan Aßmus
55045939109SStephan Aßmus
55145939109SStephan Aßmusbool
55245939109SStephan AßmusBUrl::HasAuthority() const
55345939109SStephan Aßmus{
554cd805f67SAdrien Destugues	return fHasHost || fHasUserName;
55545939109SStephan Aßmus}
55645939109SStephan Aßmus
55745939109SStephan Aßmus
55845939109SStephan Aßmusbool
55945939109SStephan AßmusBUrl::HasUserName() const
56045939109SStephan Aßmus{
56145939109SStephan Aßmus	return fHasUserName;
56245939109SStephan Aßmus}
56345939109SStephan Aßmus
56445939109SStephan Aßmus
56545939109SStephan Aßmusbool
56645939109SStephan AßmusBUrl::HasPassword() const
56745939109SStephan Aßmus{
56845939109SStephan Aßmus	return fHasPassword;
56945939109SStephan Aßmus}
57045939109SStephan Aßmus
57145939109SStephan Aßmus
57245939109SStephan Aßmusbool
57345939109SStephan AßmusBUrl::HasUserInfo() const
57445939109SStephan Aßmus{
575cd805f67SAdrien Destugues	return fHasUserName || fHasPassword;
57645939109SStephan Aßmus}
57745939109SStephan Aßmus
57845939109SStephan Aßmus
57945939109SStephan Aßmusbool
58045939109SStephan AßmusBUrl::HasHost() const
58145939109SStephan Aßmus{
58245939109SStephan Aßmus	return fHasHost;
58345939109SStephan Aßmus}
58445939109SStephan Aßmus
58545939109SStephan Aßmus
58645939109SStephan Aßmusbool
58745939109SStephan AßmusBUrl::HasPort() const
58845939109SStephan Aßmus{
58945939109SStephan Aßmus	return fHasPort;
59045939109SStephan Aßmus}
59145939109SStephan Aßmus
59245939109SStephan Aßmus
59345939109SStephan Aßmusbool
59445939109SStephan AßmusBUrl::HasPath() const
59545939109SStephan Aßmus{
59645939109SStephan Aßmus	return fHasPath;
59745939109SStephan Aßmus}
59845939109SStephan Aßmus
59945939109SStephan Aßmus
60045939109SStephan Aßmusbool
60145939109SStephan AßmusBUrl::HasRequest() const
60245939109SStephan Aßmus{
60345939109SStephan Aßmus	return fHasRequest;
60445939109SStephan Aßmus}
60545939109SStephan Aßmus
60645939109SStephan Aßmus
60745939109SStephan Aßmusbool
60845939109SStephan AßmusBUrl::HasFragment() const
60945939109SStephan Aßmus{
61045939109SStephan Aßmus	return fHasFragment;
61145939109SStephan Aßmus}
61245939109SStephan Aßmus
61345939109SStephan Aßmus
61445939109SStephan Aßmus// #pragma mark URL encoding/decoding of needed fields
61545939109SStephan Aßmus
61645939109SStephan Aßmus
61745939109SStephan Aßmusvoid
61845939109SStephan AßmusBUrl::UrlEncode(bool strict)
61945939109SStephan Aßmus{
62045939109SStephan Aßmus	fUser = _DoUrlEncodeChunk(fUser, strict);
62145939109SStephan Aßmus	fPassword = _DoUrlEncodeChunk(fPassword, strict);
62245939109SStephan Aßmus	fHost = _DoUrlEncodeChunk(fHost, strict);
62345939109SStephan Aßmus	fFragment = _DoUrlEncodeChunk(fFragment, strict);
62445939109SStephan Aßmus	fPath = _DoUrlEncodeChunk(fPath, strict, true);
62545939109SStephan Aßmus}
62645939109SStephan Aßmus
62745939109SStephan Aßmus
62845939109SStephan Aßmusvoid
62945939109SStephan AßmusBUrl::UrlDecode(bool strict)
63045939109SStephan Aßmus{
63145939109SStephan Aßmus	fUser = _DoUrlDecodeChunk(fUser, strict);
63245939109SStephan Aßmus	fPassword = _DoUrlDecodeChunk(fPassword, strict);
63345939109SStephan Aßmus	fHost = _DoUrlDecodeChunk(fHost, strict);
63445939109SStephan Aßmus	fFragment = _DoUrlDecodeChunk(fFragment, strict);
63545939109SStephan Aßmus	fPath = _DoUrlDecodeChunk(fPath, strict);
63645939109SStephan Aßmus}
63745939109SStephan Aßmus
63845939109SStephan Aßmus
639fa2dd9c4SAndrew Lindesay#ifdef HAIKU_TARGET_PLATFORM_HAIKU
6409bf4e994SAdrien Destuguesstatus_t
6419bf4e994SAdrien DestuguesBUrl::IDNAToAscii()
6429bf4e994SAdrien Destugues{
6439bf4e994SAdrien Destugues	UErrorCode err = U_ZERO_ERROR;
6449bf4e994SAdrien Destugues	icu::IDNA* converter = icu::IDNA::createUTS46Instance(0, err);
6459bf4e994SAdrien Destugues	icu::IDNAInfo info;
6469bf4e994SAdrien Destugues
6479bf4e994SAdrien Destugues	BString result;
6489bf4e994SAdrien Destugues	BStringByteSink sink(&result);
6499bf4e994SAdrien Destugues	converter->nameToASCII_UTF8(icu::StringPiece(fHost.String()), sink, info,
6509bf4e994SAdrien Destugues		err);
6519bf4e994SAdrien Destugues
6529bf4e994SAdrien Destugues	delete converter;
6539bf4e994SAdrien Destugues
6549bf4e994SAdrien Destugues	if (U_FAILURE(err))
6559bf4e994SAdrien Destugues		return B_ERROR;
6569bf4e994SAdrien Destugues
6579bf4e994SAdrien Destugues	fHost = result;
6589bf4e994SAdrien Destugues	return B_OK;
6599bf4e994SAdrien Destugues}
660fa2dd9c4SAndrew Lindesay#endif
6619bf4e994SAdrien Destugues
6629bf4e994SAdrien Destugues
663fa2dd9c4SAndrew Lindesay#ifdef HAIKU_TARGET_PLATFORM_HAIKU
6649bf4e994SAdrien Destuguesstatus_t
6659bf4e994SAdrien DestuguesBUrl::IDNAToUnicode()
6669bf4e994SAdrien Destugues{
6679bf4e994SAdrien Destugues	UErrorCode err = U_ZERO_ERROR;
6689bf4e994SAdrien Destugues	icu::IDNA* converter = icu::IDNA::createUTS46Instance(0, err);
6699bf4e994SAdrien Destugues	icu::IDNAInfo info;
6709bf4e994SAdrien Destugues
6719bf4e994SAdrien Destugues	BString result;
6729bf4e994SAdrien Destugues	BStringByteSink sink(&result);
6739bf4e994SAdrien Destugues	converter->nameToUnicodeUTF8(icu::StringPiece(fHost.String()), sink, info,
6749bf4e994SAdrien Destugues		err);
6759bf4e994SAdrien Destugues
6769bf4e994SAdrien Destugues	delete converter;
6779bf4e994SAdrien Destugues
6789bf4e994SAdrien Destugues	if (U_FAILURE(err))
6799bf4e994SAdrien Destugues		return B_ERROR;
6809bf4e994SAdrien Destugues
6819bf4e994SAdrien Destugues	fHost = result;
6829bf4e994SAdrien Destugues	return B_OK;
6839bf4e994SAdrien Destugues}
684fa2dd9c4SAndrew Lindesay#endif
6859bf4e994SAdrien Destugues
6869bf4e994SAdrien Destugues
68772f6b787SIngo Weinhold// #pragma mark - utility functionality
68872f6b787SIngo Weinhold
68972f6b787SIngo Weinhold
690fa2dd9c4SAndrew Lindesay#ifdef HAIKU_TARGET_PLATFORM_HAIKU
69172f6b787SIngo Weinholdbool
69272f6b787SIngo WeinholdBUrl::HasPreferredApplication() const
69372f6b787SIngo Weinhold{
69472f6b787SIngo Weinhold	BString appSignature = PreferredApplication();
69572f6b787SIngo Weinhold	BMimeType mime(appSignature.String());
69672f6b787SIngo Weinhold
69772f6b787SIngo Weinhold	if (appSignature.IFindFirst("application/") == 0
69872f6b787SIngo Weinhold		&& mime.IsValid())
69972f6b787SIngo Weinhold		return true;
70072f6b787SIngo Weinhold
70172f6b787SIngo Weinhold	return false;
70272f6b787SIngo Weinhold}
703fa2dd9c4SAndrew Lindesay#endif
70472f6b787SIngo Weinhold
70572f6b787SIngo Weinhold
706fa2dd9c4SAndrew Lindesay#ifdef HAIKU_TARGET_PLATFORM_HAIKU
70772f6b787SIngo WeinholdBString
70872f6b787SIngo WeinholdBUrl::PreferredApplication() const
70972f6b787SIngo Weinhold{
71072f6b787SIngo Weinhold	BString appSignature;
71172f6b787SIngo Weinhold	BMimeType mime(_UrlMimeType().String());
71272f6b787SIngo Weinhold	mime.GetPreferredApp(appSignature.LockBuffer(B_MIME_TYPE_LENGTH));
71372f6b787SIngo Weinhold	appSignature.UnlockBuffer();
71472f6b787SIngo Weinhold
71572f6b787SIngo Weinhold	return BString(appSignature);
71672f6b787SIngo Weinhold}
717fa2dd9c4SAndrew Lindesay#endif
71872f6b787SIngo Weinhold
71972f6b787SIngo Weinhold
720fa2dd9c4SAndrew Lindesay#ifdef HAIKU_TARGET_PLATFORM_HAIKU
72172f6b787SIngo Weinholdstatus_t
72272f6b787SIngo WeinholdBUrl::OpenWithPreferredApplication(bool onProblemAskUser) const
72372f6b787SIngo Weinhold{
72472f6b787SIngo Weinhold	if (!IsValid())
72572f6b787SIngo Weinhold		return B_BAD_VALUE;
72672f6b787SIngo Weinhold
72772f6b787SIngo Weinhold	BString urlString = UrlString();
72872f6b787SIngo Weinhold	if (urlString.Length() > B_PATH_NAME_LENGTH) {
72972f6b787SIngo Weinhold		// TODO: BAlert
73072f6b787SIngo Weinhold		//	if (onProblemAskUser)
73172f6b787SIngo Weinhold		//		BAlert ... Too long URL!
73272f6b787SIngo Weinhold#if DEBUG
73372f6b787SIngo Weinhold		fprintf(stderr, "URL too long");
73472f6b787SIngo Weinhold#endif
73572f6b787SIngo Weinhold		return B_NAME_TOO_LONG;
73672f6b787SIngo Weinhold	}
73772f6b787SIngo Weinhold
73872f6b787SIngo Weinhold	char* argv[] = {
73972f6b787SIngo Weinhold		const_cast<char*>("BUrlInvokedApplication"),
74072f6b787SIngo Weinhold		const_cast<char*>(urlString.String()),
74172f6b787SIngo Weinhold		NULL
74272f6b787SIngo Weinhold	};
74372f6b787SIngo Weinhold
74472f6b787SIngo Weinhold#if DEBUG
74572f6b787SIngo Weinhold	if (HasPreferredApplication())
74672f6b787SIngo Weinhold		printf("HasPreferredApplication() == true\n");
74772f6b787SIngo Weinhold	else
74872f6b787SIngo Weinhold		printf("HasPreferredApplication() == false\n");
74972f6b787SIngo Weinhold#endif
75072f6b787SIngo Weinhold
75172f6b787SIngo Weinhold	status_t status = be_roster->Launch(_UrlMimeType().String(), 1, argv+1);
75272f6b787SIngo Weinhold	if (status != B_OK) {
75372f6b787SIngo Weinhold#if DEBUG
75472f6b787SIngo Weinhold		fprintf(stderr, "Opening URL failed: %s\n", strerror(status));
75572f6b787SIngo Weinhold#endif
75672f6b787SIngo Weinhold	}
75772f6b787SIngo Weinhold
75872f6b787SIngo Weinhold	return status;
75972f6b787SIngo Weinhold}
760fa2dd9c4SAndrew Lindesay#endif
76172f6b787SIngo Weinhold
76272f6b787SIngo Weinhold
76345939109SStephan Aßmus// #pragma mark Url encoding/decoding of string
76445939109SStephan Aßmus
76545939109SStephan Aßmus
76645939109SStephan Aßmus/*static*/ BString
76745939109SStephan AßmusBUrl::UrlEncode(const BString& url, bool strict, bool directory)
76845939109SStephan Aßmus{
76945939109SStephan Aßmus	return _DoUrlEncodeChunk(url, strict, directory);
77045939109SStephan Aßmus}
77145939109SStephan Aßmus
77245939109SStephan Aßmus
77345939109SStephan Aßmus/*static*/ BString
77445939109SStephan AßmusBUrl::UrlDecode(const BString& url, bool strict)
77545939109SStephan Aßmus{
77645939109SStephan Aßmus	return _DoUrlDecodeChunk(url, strict);
77745939109SStephan Aßmus}
77845939109SStephan Aßmus
77945939109SStephan Aßmus
78045939109SStephan Aßmus// #pragma mark BArchivable members
78145939109SStephan Aßmus
78245939109SStephan Aßmus
78345939109SStephan Aßmusstatus_t
78445939109SStephan AßmusBUrl::Archive(BMessage* into, bool deep) const
78545939109SStephan Aßmus{
78645939109SStephan Aßmus	status_t ret = BArchivable::Archive(into, deep);
78745939109SStephan Aßmus
78845939109SStephan Aßmus	if (ret == B_OK)
78945939109SStephan Aßmus		ret = into->AddString(kArchivedUrl, UrlString());
79097ddf901SJérôme Duval
79145939109SStephan Aßmus	return ret;
79245939109SStephan Aßmus}
79345939109SStephan Aßmus
79445939109SStephan Aßmus
79545939109SStephan Aßmus/*static*/ BArchivable*
79645939109SStephan AßmusBUrl::Instantiate(BMessage* archive)
79745939109SStephan Aßmus{
79845939109SStephan Aßmus	if (validate_instantiation(archive, "BUrl"))
79945939109SStephan Aßmus		return new(std::nothrow) BUrl(archive);
80045939109SStephan Aßmus	return NULL;
80145939109SStephan Aßmus}
80245939109SStephan Aßmus
80345939109SStephan Aßmus
80445939109SStephan Aßmus// #pragma mark URL comparison
80545939109SStephan Aßmus
80645939109SStephan Aßmus
80745939109SStephan Aßmusbool
80845939109SStephan AßmusBUrl::operator==(BUrl& other) const
80945939109SStephan Aßmus{
81045939109SStephan Aßmus	UrlString();
81145939109SStephan Aßmus	other.UrlString();
81297ddf901SJérôme Duval
81345939109SStephan Aßmus	return fUrlString == other.fUrlString;
81445939109SStephan Aßmus}
81545939109SStephan Aßmus
81645939109SStephan Aßmus
81745939109SStephan Aßmusbool
81845939109SStephan AßmusBUrl::operator!=(BUrl& other) const
81945939109SStephan Aßmus{
82045939109SStephan Aßmus	return !(*this == other);
82145939109SStephan Aßmus}
82245939109SStephan Aßmus
82345939109SStephan Aßmus
82445939109SStephan Aßmus// #pragma mark URL assignment
82545939109SStephan Aßmus
82645939109SStephan Aßmus
82745939109SStephan Aßmusconst BUrl&
82845939109SStephan AßmusBUrl::operator=(const BUrl& other)
82945939109SStephan Aßmus{
8307603710eSAdrien Destugues	fUrlStringValid = other.fUrlStringValid;
83145939109SStephan Aßmus	if (fUrlStringValid)
8327603710eSAdrien Destugues		fUrlString = other.fUrlString;
83397ddf901SJérôme Duval
8347603710eSAdrien Destugues	fAuthorityValid = other.fAuthorityValid;
83545939109SStephan Aßmus	if (fAuthorityValid)
8367603710eSAdrien Destugues		fAuthority = other.fAuthority;
83797ddf901SJérôme Duval
8387603710eSAdrien Destugues	fUserInfoValid = other.fUserInfoValid;
83945939109SStephan Aßmus	if (fUserInfoValid)
8407603710eSAdrien Destugues		fUserInfo = other.fUserInfo;
8417603710eSAdrien Destugues
8427603710eSAdrien Destugues	fProtocol = other.fProtocol;
8437603710eSAdrien Destugues	fUser = other.fUser;
8447603710eSAdrien Destugues	fPassword = other.fPassword;
8457603710eSAdrien Destugues	fHost = other.fHost;
8467603710eSAdrien Destugues	fPort = other.fPort;
8477603710eSAdrien Destugues	fPath = other.fPath;
8487603710eSAdrien Destugues	fRequest = other.fRequest;
8497603710eSAdrien Destugues	fFragment = other.fFragment;
8507603710eSAdrien Destugues
8517603710eSAdrien Destugues	fHasProtocol = other.fHasProtocol;
8527603710eSAdrien Destugues	fHasUserName = other.fHasUserName;
8537603710eSAdrien Destugues	fHasPassword = other.fHasPassword;
8547603710eSAdrien Destugues	fHasHost = other.fHasHost;
8557603710eSAdrien Destugues	fHasPort = other.fHasPort;
8567603710eSAdrien Destugues	fHasPath = other.fHasPath;
8577603710eSAdrien Destugues	fHasRequest = other.fHasRequest;
8587603710eSAdrien Destugues	fHasFragment = other.fHasFragment;
85997ddf901SJérôme Duval
86045939109SStephan Aßmus	return *this;
86145939109SStephan Aßmus}
86245939109SStephan Aßmus
86345939109SStephan Aßmus
86445939109SStephan Aßmusconst BUrl&
86545939109SStephan AßmusBUrl::operator=(const BString& string)
86645939109SStephan Aßmus{
86745939109SStephan Aßmus	SetUrlString(string);
86845939109SStephan Aßmus	return *this;
86945939109SStephan Aßmus}
87045939109SStephan Aßmus
87145939109SStephan Aßmus
87245939109SStephan Aßmusconst BUrl&
87345939109SStephan AßmusBUrl::operator=(const char* string)
87445939109SStephan Aßmus{
87545939109SStephan Aßmus	SetUrlString(string);
87645939109SStephan Aßmus	return *this;
87745939109SStephan Aßmus}
87845939109SStephan Aßmus
87945939109SStephan Aßmus
88045939109SStephan Aßmus// #pragma mark URL to string conversion
88145939109SStephan Aßmus
88245939109SStephan Aßmus
88345939109SStephan AßmusBUrl::operator const char*() const
88445939109SStephan Aßmus{
88545939109SStephan Aßmus	return UrlString();
88645939109SStephan Aßmus}
88745939109SStephan Aßmus
88845939109SStephan Aßmus
88945939109SStephan Aßmusvoid
89045939109SStephan AßmusBUrl::_ResetFields()
89145939109SStephan Aßmus{
89245939109SStephan Aßmus	fHasProtocol = false;
89345939109SStephan Aßmus	fHasUserName = false;
89445939109SStephan Aßmus	fHasPassword = false;
89545939109SStephan Aßmus	fHasHost = false;
89645939109SStephan Aßmus	fHasPort = false;
89745939109SStephan Aßmus	fHasPath = false;
89845939109SStephan Aßmus	fHasRequest = false;
89945939109SStephan Aßmus	fHasFragment = false;
90045939109SStephan Aßmus
90145939109SStephan Aßmus	fProtocol.Truncate(0);
90245939109SStephan Aßmus	fUser.Truncate(0);
90345939109SStephan Aßmus	fPassword.Truncate(0);
90445939109SStephan Aßmus	fHost.Truncate(0);
90545939109SStephan Aßmus	fPort = 0;
90645939109SStephan Aßmus	fPath.Truncate(0);
90745939109SStephan Aßmus	fRequest.Truncate(0);
90845939109SStephan Aßmus	fFragment.Truncate(0);
90945939109SStephan Aßmus
91045939109SStephan Aßmus	// Force re-generation of these fields
91145939109SStephan Aßmus	fUrlStringValid = false;
91245939109SStephan Aßmus	fUserInfoValid = false;
91345939109SStephan Aßmus	fAuthorityValid = false;
91445939109SStephan Aßmus}
91545939109SStephan Aßmus
91645939109SStephan Aßmus
917fa2dd9c4SAndrew Lindesaybool
918fa2dd9c4SAndrew LindesayBUrl::_ContainsDelimiter(const BString& url)
919fa2dd9c4SAndrew Lindesay{
920fa2dd9c4SAndrew Lindesay	int32 len = url.Length();
921fa2dd9c4SAndrew Lindesay
922fa2dd9c4SAndrew Lindesay	for (int32 i = 0; i < len; i++) {
923fa2dd9c4SAndrew Lindesay		switch (url[i]) {
924fa2dd9c4SAndrew Lindesay			case ' ':
925fa2dd9c4SAndrew Lindesay			case '\n':
926fa2dd9c4SAndrew Lindesay			case '\t':
927fa2dd9c4SAndrew Lindesay			case '\r':
928fa2dd9c4SAndrew Lindesay			case '<':
929fa2dd9c4SAndrew Lindesay			case '>':
930fa2dd9c4SAndrew Lindesay			case '"':
931fa2dd9c4SAndrew Lindesay				return true;
932fa2dd9c4SAndrew Lindesay		}
933fa2dd9c4SAndrew Lindesay	}
934fa2dd9c4SAndrew Lindesay
935fa2dd9c4SAndrew Lindesay	return false;
936fa2dd9c4SAndrew Lindesay}
937fa2dd9c4SAndrew Lindesay
938fa2dd9c4SAndrew Lindesay
939cf657294SAndrew Lindesayenum explode_url_parse_state {
940cf657294SAndrew Lindesay	EXPLODE_PROTOCOL,
941cf657294SAndrew Lindesay	EXPLODE_PROTOCOLTERMINATOR,
942cf657294SAndrew Lindesay	EXPLODE_AUTHORITYORPATH,
943cf657294SAndrew Lindesay	EXPLODE_AUTHORITY,
944cf657294SAndrew Lindesay	EXPLODE_PATH,
945cf657294SAndrew Lindesay	EXPLODE_REQUEST, // query
946cf657294SAndrew Lindesay	EXPLODE_FRAGMENT,
947cf657294SAndrew Lindesay	EXPLODE_COMPLETE
948cf657294SAndrew Lindesay};
949cf657294SAndrew Lindesay
950cf657294SAndrew Lindesay
951cf657294SAndrew Lindesaytypedef bool (*explode_char_match_fn)(char c);
952cf657294SAndrew Lindesay
953cf657294SAndrew Lindesay
954cf657294SAndrew Lindesaystatic bool
955cf657294SAndrew Lindesayexplode_is_protocol_char(char c)
95645939109SStephan Aßmus{
957cf657294SAndrew Lindesay	return isalnum(c) || c == '+' || c == '.' || c == '-';
958cf657294SAndrew Lindesay}
959cf657294SAndrew Lindesay
960cf657294SAndrew Lindesay
961cf657294SAndrew Lindesaystatic bool
962cf657294SAndrew Lindesayexplode_is_authority_char(char c)
963cf657294SAndrew Lindesay{
964cf657294SAndrew Lindesay	return !(c == '/' || c == '?' || c == '#');
965cf657294SAndrew Lindesay}
966cf657294SAndrew Lindesay
967cf657294SAndrew Lindesay
968cf657294SAndrew Lindesaystatic bool
969cf657294SAndrew Lindesayexplode_is_path_char(char c)
970cf657294SAndrew Lindesay{
971cf657294SAndrew Lindesay	return !(c == '#' || c == '?');
972cf657294SAndrew Lindesay}
973cf657294SAndrew Lindesay
974cf657294SAndrew Lindesay
975cf657294SAndrew Lindesaystatic bool
976cf657294SAndrew Lindesayexplode_is_request_char(char c)
977cf657294SAndrew Lindesay{
978cf657294SAndrew Lindesay	return c != '#';
979cf657294SAndrew Lindesay}
980cf657294SAndrew Lindesay
981cf657294SAndrew Lindesay
982cf657294SAndrew Lindesaystatic int32
983cf657294SAndrew Lindesaychar_offset_until_fn_false(const char* url, int32 len, int32 offset,
984cf657294SAndrew Lindesay	explode_char_match_fn fn)
985cf657294SAndrew Lindesay{
986cf657294SAndrew Lindesay	while (offset < len && fn(url[offset]))
987cf657294SAndrew Lindesay		offset++;
988cf657294SAndrew Lindesay
989cf657294SAndrew Lindesay	return offset;
990cf657294SAndrew Lindesay}
99145939109SStephan Aßmus
992cf657294SAndrew Lindesay/*
993cf657294SAndrew Lindesay * This function takes a URL in string-form and parses the components of the URL out.
994cf657294SAndrew Lindesay */
995cf657294SAndrew Lindesaystatus_t
996a609673cSAndrew LindesayBUrl::_ExplodeUrlString(const BString& url, uint32 flags)
997cf657294SAndrew Lindesay{
99845939109SStephan Aßmus	_ResetFields();
99945939109SStephan Aßmus
1000fa2dd9c4SAndrew Lindesay	// RFC3986, Appendix C; the URL should not contain whitespace or delimiters
1001fa2dd9c4SAndrew Lindesay	// by this point.
1002cf657294SAndrew Lindesay
1003fa2dd9c4SAndrew Lindesay	if (_ContainsDelimiter(url))
1004cf657294SAndrew Lindesay		return B_BAD_VALUE;
1005fa2dd9c4SAndrew Lindesay
1006cf657294SAndrew Lindesay	explode_url_parse_state state = EXPLODE_PROTOCOL;
1007cf657294SAndrew Lindesay	int32 offset = 0;
1008cf657294SAndrew Lindesay	int32 length = url.Length();
10093cc5e76fSAndrew Lindesay	bool forceHasHost = false;
1010cf657294SAndrew Lindesay	const char *url_c = url.String();
101145939109SStephan Aßmus
1012cf657294SAndrew Lindesay	// The regexp is provided in RFC3986 (URI generic syntax), Appendix B
1013cf657294SAndrew Lindesay	// ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?
1014cf657294SAndrew Lindesay	// The ensuing logic attempts to simulate the behaviour of extracting the groups
1015cf657294SAndrew Lindesay	// from the string without requiring a group-capable regex engine.
1016cf657294SAndrew Lindesay
1017f4db7fdcSAdrien Destugues	while (offset < length) {
1018cf657294SAndrew Lindesay		switch (state) {
1019cf657294SAndrew Lindesay
1020cf657294SAndrew Lindesay			case EXPLODE_PROTOCOL:
1021cf657294SAndrew Lindesay			{
1022cf657294SAndrew Lindesay				int32 end_protocol = char_offset_until_fn_false(url_c, length,
1023cf657294SAndrew Lindesay					offset, explode_is_protocol_char);
1024cf657294SAndrew Lindesay
1025cf657294SAndrew Lindesay				if (end_protocol < length) {
1026cf657294SAndrew Lindesay					SetProtocol(BString(&url_c[offset], end_protocol - offset));
1027cf657294SAndrew Lindesay					state = EXPLODE_PROTOCOLTERMINATOR;
1028cf657294SAndrew Lindesay					offset = end_protocol;
1029cf657294SAndrew Lindesay				} else {
1030f4db7fdcSAdrien Destugues					// No protocol was found, try parsing from the string
1031f4db7fdcSAdrien Destugues					// start, beginning with authority or path
1032f4db7fdcSAdrien Destugues					SetProtocol("");
1033f4db7fdcSAdrien Destugues					offset = 0;
1034f4db7fdcSAdrien Destugues					state = EXPLODE_AUTHORITYORPATH;
1035cf657294SAndrew Lindesay				}
1036cf657294SAndrew Lindesay				break;
1037cf657294SAndrew Lindesay			}
103845939109SStephan Aßmus
1039cf657294SAndrew Lindesay			case EXPLODE_PROTOCOLTERMINATOR:
1040cf657294SAndrew Lindesay			{
1041cf657294SAndrew Lindesay				if (url[offset] == ':') {
1042cf657294SAndrew Lindesay					offset++;
1043cf657294SAndrew Lindesay				} else {
1044f4db7fdcSAdrien Destugues					// No protocol was found, try parsing from the string
1045f4db7fdcSAdrien Destugues					// start, beginning with authority or path
1046f4db7fdcSAdrien Destugues					SetProtocol("");
1047f4db7fdcSAdrien Destugues					offset = 0;
1048cf657294SAndrew Lindesay				}
1049f4db7fdcSAdrien Destugues				state = EXPLODE_AUTHORITYORPATH;
1050cf657294SAndrew Lindesay				break;
1051cf657294SAndrew Lindesay			}
1052fa2dd9c4SAndrew Lindesay
1053cf657294SAndrew Lindesay			case EXPLODE_AUTHORITYORPATH:
1054cf657294SAndrew Lindesay			{
1055f4db7fdcSAdrien Destugues				// The authority must start with //. If it isn't there, skip
1056f4db7fdcSAdrien Destugues				// to parsing the path.
1057cf657294SAndrew Lindesay				if (strncmp(&url_c[offset], "//", 2) == 0) {
1058cf657294SAndrew Lindesay					state = EXPLODE_AUTHORITY;
10593cc5e76fSAndrew Lindesay					// if we see the // then this would imply that a host is
10603cc5e76fSAndrew Lindesay					// to be rendered even if no host has been parsed.
10613cc5e76fSAndrew Lindesay					forceHasHost = true;
1062cf657294SAndrew Lindesay					offset += 2;
1063cf657294SAndrew Lindesay				} else {
1064cf657294SAndrew Lindesay					state = EXPLODE_PATH;
1065cf657294SAndrew Lindesay				}
1066cf657294SAndrew Lindesay				break;
1067cf657294SAndrew Lindesay			}
1068400153ebSAdrien Destugues
1069cf657294SAndrew Lindesay			case EXPLODE_AUTHORITY:
1070cf657294SAndrew Lindesay			{
1071cf657294SAndrew Lindesay				int end_authority = char_offset_until_fn_false(url_c, length,
1072cf657294SAndrew Lindesay					offset, explode_is_authority_char);
1073cf657294SAndrew Lindesay				SetAuthority(BString(&url_c[offset], end_authority - offset));
1074cf657294SAndrew Lindesay				state = EXPLODE_PATH;
1075cf657294SAndrew Lindesay				offset = end_authority;
1076cf657294SAndrew Lindesay				break;
1077cf657294SAndrew Lindesay			}
1078400153ebSAdrien Destugues
1079cf657294SAndrew Lindesay			case EXPLODE_PATH:
1080cf657294SAndrew Lindesay			{
1081cf657294SAndrew Lindesay				int end_path = char_offset_until_fn_false(url_c, length, offset,
1082cf657294SAndrew Lindesay					explode_is_path_char);
1083a609673cSAndrew Lindesay				BString path(&url_c[offset], end_path - offset);
1084a609673cSAndrew Lindesay
1085a609673cSAndrew Lindesay				if ((flags & PARSE_RAW_PATH_MASK_BIT) == 0)
1086a609673cSAndrew Lindesay					SetPath(path);
1087a609673cSAndrew Lindesay				else
1088a609673cSAndrew Lindesay					_SetPathUnsafe(path);
1089cf657294SAndrew Lindesay				state = EXPLODE_REQUEST;
1090cf657294SAndrew Lindesay				offset = end_path;
1091cf657294SAndrew Lindesay				break;
1092cf657294SAndrew Lindesay			}
1093400153ebSAdrien Destugues
1094cf657294SAndrew Lindesay			case EXPLODE_REQUEST: // query
1095cf657294SAndrew Lindesay			{
1096cf657294SAndrew Lindesay				if (url_c[offset] == '?') {
1097cf657294SAndrew Lindesay					offset++;
1098cf657294SAndrew Lindesay					int end_request = char_offset_until_fn_false(url_c, length,
1099cf657294SAndrew Lindesay						offset, explode_is_request_char);
1100cf657294SAndrew Lindesay					SetRequest(BString(&url_c[offset], end_request - offset));
1101cf657294SAndrew Lindesay					offset = end_request;
11023cc5e76fSAndrew Lindesay					// if there is a "?" in the parse then it is clear that
11033cc5e76fSAndrew Lindesay					// there is a 'request' / query present regardless if there
11043cc5e76fSAndrew Lindesay					// are any valid key-value pairs.
11053cc5e76fSAndrew Lindesay					fHasRequest = true;
1106cf657294SAndrew Lindesay				}
1107cf657294SAndrew Lindesay				state = EXPLODE_FRAGMENT;
1108cf657294SAndrew Lindesay				break;
1109cf657294SAndrew Lindesay			}
1110cf657294SAndrew Lindesay
1111cf657294SAndrew Lindesay			case EXPLODE_FRAGMENT:
1112cf657294SAndrew Lindesay			{
1113cf657294SAndrew Lindesay				if (url_c[offset] == '#') {
1114cf657294SAndrew Lindesay					offset++;
1115cf657294SAndrew Lindesay					SetFragment(BString(&url_c[offset], length - offset));
1116cf657294SAndrew Lindesay					offset = length;
1117cf657294SAndrew Lindesay				}
1118cf657294SAndrew Lindesay				state = EXPLODE_COMPLETE;
1119cf657294SAndrew Lindesay				break;
1120cf657294SAndrew Lindesay			}
1121cf657294SAndrew Lindesay
1122cf657294SAndrew Lindesay			case EXPLODE_COMPLETE:
1123cf657294SAndrew Lindesay				// should never be reached - keeps the compiler happy
1124cf657294SAndrew Lindesay				break;
1125cf657294SAndrew Lindesay
1126cf657294SAndrew Lindesay		}
1127b5bde4adSAdrien Destugues	}
1128400153ebSAdrien Destugues
11293cc5e76fSAndrew Lindesay	if (forceHasHost)
11303cc5e76fSAndrew Lindesay		fHasHost = true;
11313cc5e76fSAndrew Lindesay
1132cf657294SAndrew Lindesay	return B_OK;
1133b5bde4adSAdrien Destugues}
1134b5bde4adSAdrien Destugues
1135b5bde4adSAdrien Destugues
1136b5bde4adSAdrien DestuguesBString
1137b5bde4adSAdrien DestuguesBUrl::_MergePath(const BString& relative) const
1138b5bde4adSAdrien Destugues{
1139b5bde4adSAdrien Destugues	// This implements RFC3986, Section 5.2.3.
1140cd6365c7SJérôme Duval	if (HasAuthority() && fPath == "") {
1141b5bde4adSAdrien Destugues		BString result("/");
1142b5bde4adSAdrien Destugues		result << relative;
1143b5bde4adSAdrien Destugues		return result;
1144b5bde4adSAdrien Destugues	}
1145b5bde4adSAdrien Destugues
1146a609673cSAndrew Lindesay	int32 lastSlashIndex = fPath.FindLast("/");
1147a609673cSAndrew Lindesay
1148a609673cSAndrew Lindesay	if (lastSlashIndex == B_ERROR)
1149a609673cSAndrew Lindesay		return relative;
1150a609673cSAndrew Lindesay
1151a609673cSAndrew Lindesay	BString result;
1152a609673cSAndrew Lindesay	result.SetTo(fPath, lastSlashIndex + 1);
1153b5bde4adSAdrien Destugues	result << relative;
1154b5bde4adSAdrien Destugues
1155b5bde4adSAdrien Destugues	return result;
1156b5bde4adSAdrien Destugues}
1157b5bde4adSAdrien Destugues
1158b5bde4adSAdrien Destugues
1159b5bde4adSAdrien Destugues// This sets the path without normalizing it. If fed with a path that has . or
1160b5bde4adSAdrien Destugues// .. segments, this would make the URL invalid.
1161b5bde4adSAdrien Destuguesvoid
1162b5bde4adSAdrien DestuguesBUrl::_SetPathUnsafe(const BString& path)
1163b5bde4adSAdrien Destugues{
1164b5bde4adSAdrien Destugues	fPath = path;
1165b5bde4adSAdrien Destugues	fHasPath = true; // RFC says an empty path is still a path
1166b5bde4adSAdrien Destugues	fUrlStringValid = false;
116745939109SStephan Aßmus}
116845939109SStephan Aßmus
116945939109SStephan Aßmus
1170cf657294SAndrew Lindesayenum authority_parse_state {
1171cf657294SAndrew Lindesay	AUTHORITY_USERNAME,
1172cf657294SAndrew Lindesay	AUTHORITY_PASSWORD,
1173cf657294SAndrew Lindesay	AUTHORITY_HOST,
1174cf657294SAndrew Lindesay	AUTHORITY_PORT,
1175cf657294SAndrew Lindesay	AUTHORITY_COMPLETE
1176cf657294SAndrew Lindesay};
1177cf657294SAndrew Lindesay
117845939109SStephan Aßmusvoid
1179ced0e0beSAdrien DestuguesBUrl::SetAuthority(const BString& authority)
118045939109SStephan Aßmus{
1181ced0e0beSAdrien Destugues	fAuthority = authority;
1182ced0e0beSAdrien Destugues
1183cf657294SAndrew Lindesay	fUser.Truncate(0);
1184cf657294SAndrew Lindesay	fPassword.Truncate(0);
1185cf657294SAndrew Lindesay	fHost.Truncate(0);
1186cf657294SAndrew Lindesay	fPort = 0;
1187ced0e0beSAdrien Destugues	fHasPort = false;
1188cd805f67SAdrien Destugues	fHasUserName = false;
1189cd805f67SAdrien Destugues	fHasPassword = false;
1190ced0e0beSAdrien Destugues
1191cf657294SAndrew Lindesay	bool hasUsernamePassword = B_ERROR != fAuthority.FindFirst('@');
1192cf657294SAndrew Lindesay	authority_parse_state state = AUTHORITY_USERNAME;
1193cf657294SAndrew Lindesay	int32 offset = 0;
1194cf657294SAndrew Lindesay	int32 length = authority.Length();
1195cf657294SAndrew Lindesay	const char *authority_c = authority.String();
1196cd805f67SAdrien Destugues
1197cf657294SAndrew Lindesay	while (AUTHORITY_COMPLETE != state && offset < length) {
119845939109SStephan Aßmus
1199cf657294SAndrew Lindesay		switch (state) {
120045939109SStephan Aßmus
1201cf657294SAndrew Lindesay			case AUTHORITY_USERNAME:
1202cf657294SAndrew Lindesay			{
1203cf657294SAndrew Lindesay				if (hasUsernamePassword) {
1204cf657294SAndrew Lindesay					int32 end_username = char_offset_until_fn_false(
12058f30879bSAndrew Lindesay						authority_c, length, offset, _IsUsernameChar);
120645939109SStephan Aßmus
1207cf657294SAndrew Lindesay					SetUserName(BString(&authority_c[offset],
1208cf657294SAndrew Lindesay						end_username - offset));
120945939109SStephan Aßmus
1210cf657294SAndrew Lindesay					state = AUTHORITY_PASSWORD;
1211cf657294SAndrew Lindesay					offset = end_username;
1212cf657294SAndrew Lindesay				} else {
1213cf657294SAndrew Lindesay					state = AUTHORITY_HOST;
1214cf657294SAndrew Lindesay				}
1215cf657294SAndrew Lindesay				break;
1216cf657294SAndrew Lindesay			}
121745939109SStephan Aßmus
1218cf657294SAndrew Lindesay			case AUTHORITY_PASSWORD:
1219cf657294SAndrew Lindesay			{
1220cf657294SAndrew Lindesay				if (hasUsernamePassword && ':' == authority[offset]) {
1221cf657294SAndrew Lindesay					offset++; // move past the delimiter
1222cf657294SAndrew Lindesay					int32 end_password = char_offset_until_fn_false(
12238f30879bSAndrew Lindesay						authority_c, length, offset, _IsPasswordChar);
1224cf657294SAndrew Lindesay
1225cf657294SAndrew Lindesay					SetPassword(BString(&authority_c[offset],
1226cf657294SAndrew Lindesay						end_password - offset));
1227cf657294SAndrew Lindesay
1228cf657294SAndrew Lindesay					offset = end_password;
1229cf657294SAndrew Lindesay				}
1230cf657294SAndrew Lindesay
1231cf657294SAndrew Lindesay				// if the host was preceded by a username + password couple
1232cf657294SAndrew Lindesay				// then there will be an '@' delimiter to avoid.
1233cf657294SAndrew Lindesay
1234cf657294SAndrew Lindesay				if (authority_c[offset] == '@') {
1235cf657294SAndrew Lindesay					offset++;
1236cf657294SAndrew Lindesay				}
1237cf657294SAndrew Lindesay
1238cf657294SAndrew Lindesay				state = AUTHORITY_HOST;
1239cf657294SAndrew Lindesay				break;
1240cf657294SAndrew Lindesay			}
1241cf657294SAndrew Lindesay
1242cf657294SAndrew Lindesay			case AUTHORITY_HOST:
1243cf657294SAndrew Lindesay			{
1244cf657294SAndrew Lindesay
1245cf657294SAndrew Lindesay				// the host may be enclosed within brackets in order to express
1246cf657294SAndrew Lindesay				// an IPV6 address.
1247cf657294SAndrew Lindesay
1248cf657294SAndrew Lindesay				if (authority_c[offset] == '[') {
1249cf657294SAndrew Lindesay					int32 end_ipv6_host = char_offset_until_fn_false(
12508f30879bSAndrew Lindesay						authority_c, length, offset + 1, _IsIPV6Char);
1251cf657294SAndrew Lindesay
1252cf657294SAndrew Lindesay					if (authority_c[end_ipv6_host] == ']') {
1253cf657294SAndrew Lindesay						SetHost(BString(&authority_c[offset],
1254cf657294SAndrew Lindesay							(end_ipv6_host - offset) + 1));
1255cf657294SAndrew Lindesay						state = AUTHORITY_PORT;
1256cf657294SAndrew Lindesay						offset = end_ipv6_host + 1;
1257cf657294SAndrew Lindesay					}
1258cf657294SAndrew Lindesay				}
125945939109SStephan Aßmus
1260cf657294SAndrew Lindesay				// if an IPV6 host was not found.
126145939109SStephan Aßmus
1262cf657294SAndrew Lindesay				if (AUTHORITY_HOST == state) {
1263cf657294SAndrew Lindesay					int32 end_host = char_offset_until_fn_false(
12648f30879bSAndrew Lindesay						authority_c, length, offset, _IsHostChar);
1265cf657294SAndrew Lindesay
1266cf657294SAndrew Lindesay					SetHost(BString(&authority_c[offset], end_host - offset));
1267cf657294SAndrew Lindesay					state = AUTHORITY_PORT;
1268cf657294SAndrew Lindesay					offset = end_host;
1269cf657294SAndrew Lindesay				}
1270cf657294SAndrew Lindesay
1271cf657294SAndrew Lindesay				break;
1272cf657294SAndrew Lindesay			}
1273cf657294SAndrew Lindesay
1274cf657294SAndrew Lindesay			case AUTHORITY_PORT:
1275cf657294SAndrew Lindesay			{
1276cf657294SAndrew Lindesay				if (authority_c[offset] == ':') {
1277cf657294SAndrew Lindesay					offset++;
1278cf657294SAndrew Lindesay					int32 end_port = char_offset_until_fn_false(
12798f30879bSAndrew Lindesay						authority_c, length, offset, _IsPortChar);
1280cf657294SAndrew Lindesay					SetPort(atoi(&authority_c[offset]));
1281cf657294SAndrew Lindesay					offset = end_port;
1282cf657294SAndrew Lindesay				}
1283cf657294SAndrew Lindesay
1284cf657294SAndrew Lindesay				state = AUTHORITY_COMPLETE;
1285cf657294SAndrew Lindesay
1286cf657294SAndrew Lindesay				break;
1287cf657294SAndrew Lindesay			}
1288cf657294SAndrew Lindesay
1289cf657294SAndrew Lindesay			case AUTHORITY_COMPLETE:
1290cf657294SAndrew Lindesay				// should never be reached - keeps the compiler happy
1291cf657294SAndrew Lindesay				break;
1292fa2dd9c4SAndrew Lindesay		}
129345939109SStephan Aßmus	}
1294fa2dd9c4SAndrew Lindesay
1295cf657294SAndrew Lindesay	// An empty authority is still an authority, making it possible to have
1296cf657294SAndrew Lindesay	// URLs such as file:///path/to/file.
1297cf657294SAndrew Lindesay	// TODO however, there is no way to unset the authority once it is set...
1298cf657294SAndrew Lindesay	// We may want to take a const char* parameter and allow NULL.
1299cf657294SAndrew Lindesay	fHasHost = true;
130045939109SStephan Aßmus}
130145939109SStephan Aßmus
130245939109SStephan Aßmus
130345939109SStephan Aßmus/*static*/ BString
130445939109SStephan AßmusBUrl::_DoUrlEncodeChunk(const BString& chunk, bool strict, bool directory)
130545939109SStephan Aßmus{
130645939109SStephan Aßmus	BString result;
130797ddf901SJérôme Duval
130845939109SStephan Aßmus	for (int32 i = 0; i < chunk.Length(); i++) {
130945939109SStephan Aßmus		if (_IsUnreserved(chunk[i])
1310754bbf48SJérôme Duval				|| (directory && (chunk[i] == '/' || chunk[i] == '\\'))) {
131145939109SStephan Aßmus			result << chunk[i];
1312754bbf48SJérôme Duval		} else {
131345939109SStephan Aßmus			if (chunk[i] == ' ' && !strict) {
131445939109SStephan Aßmus				result << '+';
131545939109SStephan Aßmus					// In non-strict mode, spaces are encoded by a plus sign
131645939109SStephan Aßmus			} else {
131745939109SStephan Aßmus				char hexString[5];
131845939109SStephan Aßmus				snprintf(hexString, 5, "%X", chunk[i]);
131997ddf901SJérôme Duval
132045939109SStephan Aßmus				result << '%' << hexString;
132145939109SStephan Aßmus			}
132245939109SStephan Aßmus		}
132345939109SStephan Aßmus	}
132497ddf901SJérôme Duval
132545939109SStephan Aßmus	return result;
132645939109SStephan Aßmus}
132745939109SStephan Aßmus
132845939109SStephan Aßmus
132945939109SStephan Aßmus/*static*/ BString
133045939109SStephan AßmusBUrl::_DoUrlDecodeChunk(const BString& chunk, bool strict)
133145939109SStephan Aßmus{
133245939109SStephan Aßmus	BString result;
133397ddf901SJérôme Duval
133445939109SStephan Aßmus	for (int32 i = 0; i < chunk.Length(); i++) {
133545939109SStephan Aßmus		if (chunk[i] == '+' && !strict)
133645939109SStephan Aßmus			result << ' ';
133745939109SStephan Aßmus		else {
1338eec76268SAdrien Destugues			char decoded = 0;
1339a4e5af66SAdrien Destugues			char* out = NULL;
1340a4e5af66SAdrien Destugues			char hexString[3];
1341a4e5af66SAdrien Destugues
1342a4e5af66SAdrien Destugues			if (chunk[i] == '%' && i < chunk.Length() - 2
1343a4e5af66SAdrien Destugues				&& isxdigit(chunk[i + 1]) && isxdigit(chunk[i+2])) {
1344a4e5af66SAdrien Destugues				hexString[0] = chunk[i + 1];
1345a4e5af66SAdrien Destugues				hexString[1] = chunk[i + 2];
1346a4e5af66SAdrien Destugues				hexString[2] = 0;
1347eec76268SAdrien Destugues				decoded = (char)strtol(hexString, &out, 16);
1348eec76268SAdrien Destugues			}
134997ddf901SJérôme Duval
1350a4e5af66SAdrien Destugues			if (out == hexString + 2) {
1351a4e5af66SAdrien Destugues				i += 2;
1352eec76268SAdrien Destugues				result << decoded;
1353a4e5af66SAdrien Destugues			} else
1354eec76268SAdrien Destugues				result << chunk[i];
1355fa2dd9c4SAndrew Lindesay		}
135645939109SStephan Aßmus	}
135745939109SStephan Aßmus	return result;
135845939109SStephan Aßmus}
135945939109SStephan Aßmus
136045939109SStephan Aßmus
136145939109SStephan Aßmusbool
13628f30879bSAndrew LindesayBUrl::_IsHostIPV6Valid(size_t offset, int32 length) const
13638f30879bSAndrew Lindesay{
13648f30879bSAndrew Lindesay	for (int32 i = 0; i < length; i++) {
13658f30879bSAndrew Lindesay		char c = fHost[offset + i];
13668f30879bSAndrew Lindesay		if (!_IsIPV6Char(c))
13678f30879bSAndrew Lindesay			return false;
13688f30879bSAndrew Lindesay	}
13698f30879bSAndrew Lindesay
13708f30879bSAndrew Lindesay	return length > 0;
13718f30879bSAndrew Lindesay}
13728f30879bSAndrew Lindesay
13738f30879bSAndrew Lindesay
13748f30879bSAndrew Lindesaybool
13758f30879bSAndrew LindesayBUrl::_IsHostValid() const
13768f30879bSAndrew Lindesay{
13778f30879bSAndrew Lindesay	if (fHost.StartsWith("[") && fHost.EndsWith("]"))
13788f30879bSAndrew Lindesay		return _IsHostIPV6Valid(1, fHost.Length() - 2);
13798f30879bSAndrew Lindesay
13808f30879bSAndrew Lindesay	bool lastWasDot = false;
13818f30879bSAndrew Lindesay
13828f30879bSAndrew Lindesay	for (int32 i = 0; i < fHost.Length(); i++) {
13838f30879bSAndrew Lindesay		char c = fHost[i];
13848f30879bSAndrew Lindesay
13858f30879bSAndrew Lindesay		if (c == '.') {
13868f30879bSAndrew Lindesay			if (lastWasDot || i == 0)
13878f30879bSAndrew Lindesay				return false;
13888f30879bSAndrew Lindesay			lastWasDot = true;
13898f30879bSAndrew Lindesay		} else {
13908f30879bSAndrew Lindesay			lastWasDot = false;
13918f30879bSAndrew Lindesay		}
13928f30879bSAndrew Lindesay
13938f30879bSAndrew Lindesay		if (!_IsHostChar(c) && c != '.') {
13948f30879bSAndrew Lindesay			// the underscore is technically not allowed, but occurs sometimes
13958f30879bSAndrew Lindesay			// in the wild.
13968f30879bSAndrew Lindesay			return false;
13978f30879bSAndrew Lindesay		}
13988f30879bSAndrew Lindesay	}
13998f30879bSAndrew Lindesay
14008f30879bSAndrew Lindesay	return true;
14018f30879bSAndrew Lindesay}
14028f30879bSAndrew Lindesay
14038f30879bSAndrew Lindesay
14048f30879bSAndrew Lindesaybool
14058f30879bSAndrew LindesayBUrl::_IsProtocolValid() const
140645939109SStephan Aßmus{
140745939109SStephan Aßmus	for (int8 index = 0; index < fProtocol.Length(); index++) {
140845939109SStephan Aßmus		char c = fProtocol[index];
140945939109SStephan Aßmus
141045939109SStephan Aßmus		if (index == 0 && !isalpha(c))
141145939109SStephan Aßmus			return false;
141245939109SStephan Aßmus		else if (!isalnum(c) && c != '+' && c != '-' && c != '.')
141345939109SStephan Aßmus			return false;
141445939109SStephan Aßmus	}
141545939109SStephan Aßmus
14168f30879bSAndrew Lindesay	return !fProtocol.IsEmpty();
141745939109SStephan Aßmus}
141845939109SStephan Aßmus
141945939109SStephan Aßmus
142045939109SStephan Aßmusbool
142145939109SStephan AßmusBUrl::_IsUnreserved(char c)
142245939109SStephan Aßmus{
1423754bbf48SJérôme Duval	return isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~';
142445939109SStephan Aßmus}
142545939109SStephan Aßmus
142645939109SStephan Aßmus
142745939109SStephan Aßmusbool
142845939109SStephan AßmusBUrl::_IsGenDelim(char c)
142945939109SStephan Aßmus{
1430754bbf48SJérôme Duval	return c == ':' || c == '/' || c == '?' || c == '#' || c == '['
1431754bbf48SJérôme Duval		|| c == ']' || c == '@';
143245939109SStephan Aßmus}
143345939109SStephan Aßmus
143445939109SStephan Aßmus
143545939109SStephan Aßmusbool
143645939109SStephan AßmusBUrl::_IsSubDelim(char c)
143745939109SStephan Aßmus{
1438754bbf48SJérôme Duval	return c == '!' || c == '$' || c == '&' || c == '\'' || c == '('
143945939109SStephan Aßmus		|| c == ')' || c == '*' || c == '+' || c == ',' || c == ';'
1440754bbf48SJérôme Duval		|| c == '=';
144145939109SStephan Aßmus}
144272f6b787SIngo Weinhold
144372f6b787SIngo Weinhold
14448f30879bSAndrew Lindesaybool
14458f30879bSAndrew LindesayBUrl::_IsUsernameChar(char c)
14468f30879bSAndrew Lindesay{
14478f30879bSAndrew Lindesay	return !(c == ':' || c == '@');
14488f30879bSAndrew Lindesay}
14498f30879bSAndrew Lindesay
14508f30879bSAndrew Lindesay
14518f30879bSAndrew Lindesaybool
14528f30879bSAndrew LindesayBUrl::_IsPasswordChar(char c)
14538f30879bSAndrew Lindesay{
14548f30879bSAndrew Lindesay	return !(c == '@');
14558f30879bSAndrew Lindesay}
14568f30879bSAndrew Lindesay
14578f30879bSAndrew Lindesay
14588f30879bSAndrew Lindesaybool
14598f30879bSAndrew LindesayBUrl::_IsHostChar(char c)
14608f30879bSAndrew Lindesay{
14618f30879bSAndrew Lindesay	return ((uint8) c) > 127 || isalnum(c) || c == '-' || c == '_' || c == '.'
14628f30879bSAndrew Lindesay		|| c == '%';
14638f30879bSAndrew Lindesay}
14648f30879bSAndrew Lindesay
14658f30879bSAndrew Lindesay
14668f30879bSAndrew Lindesaybool
14678f30879bSAndrew LindesayBUrl::_IsPortChar(char c)
14688f30879bSAndrew Lindesay{
14698f30879bSAndrew Lindesay	return isdigit(c);
14708f30879bSAndrew Lindesay}
14718f30879bSAndrew Lindesay
14728f30879bSAndrew Lindesay
14738f30879bSAndrew Lindesaybool
14748f30879bSAndrew LindesayBUrl::_IsIPV6Char(char c)
14758f30879bSAndrew Lindesay{
14768f30879bSAndrew Lindesay	return c == ':' || isxdigit(c);
14778f30879bSAndrew Lindesay}
14788f30879bSAndrew Lindesay
14798f30879bSAndrew Lindesay
148072f6b787SIngo WeinholdBString
148172f6b787SIngo WeinholdBUrl::_UrlMimeType() const
148272f6b787SIngo Weinhold{
148372f6b787SIngo Weinhold	BString mime;
148472f6b787SIngo Weinhold	mime << "application/x-vnd.Be.URL." << fProtocol;
148572f6b787SIngo Weinhold
148672f6b787SIngo Weinhold	return BString(mime);
148772f6b787SIngo Weinhold}
1488