/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_tools.hxx"
#include <tools/urlobj.hxx>
#include <tools/debug.hxx>
#include <tools/inetmime.hxx>
#include "com/sun/star/uno/Reference.hxx"
#include "com/sun/star/util/XStringWidth.hpp"
#include "osl/diagnose.h"
#include "osl/file.hxx"
#include "rtl/string.h"
#include "rtl/textenc.h"
#include "rtl/ustring.hxx"
#include "sal/types.h"

#ifndef INCLUDED_ALGORITHM
#include <algorithm>
#define INCLUDED_ALGORITHM
#endif
#ifndef INCLUDED_LIMITS
#include <limits>
#define INCLUDED_LIMITS
#endif

#include <string.h>

namespace unnamed_tools_urlobj {} using namespace unnamed_tools_urlobj;
	// unnamed namespaces don't work well yet...

using namespace com::sun;

//============================================================================
//
//	INetURLObject
//
//============================================================================

/* The URI grammar (using RFC 2234 conventions).

   Constructs of the form
	   {reference <rule1> using rule2}
   stand for a rule matching the given rule1 specified in the given reference,
   encoded to URI syntax using rule2 (as specified in this URI grammar).


   ; RFC 1738, RFC 2396, RFC 2732, private
   login = [user [":" password] "@"] hostport
   user = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ";" / "=" / "_" / "~")
   password = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ";" / "=" / "_" / "~")
   hostport = host [":" port]
   host = incomplete-hostname / hostname / IPv4address / IPv6reference
   incomplete-hostname = *(domainlabel ".") domainlabel
   hostname = *(domainlabel ".") toplabel ["."]
   domainlabel = alphanum [*(alphanum / "-") alphanum]
   toplabel = ALPHA [*(alphanum / "-") alphanum]
   IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
   IPv6reference = "[" hexpart [":" IPv4address] "]"
   hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq])
   hexseq = hex4 *(":" hex4)
   hex4 = 1*4HEXDIG
   port = *DIGIT
   escaped = "%" HEXDIG HEXDIG
   reserved = "$" / "&" / "+" / "," / "/" / ":" / ";" / "=" / "?" / "@" / "[" / "]"
   mark = "!" / "'" / "(" / ")" / "*" / "-" / "." / "_" / "~"
   alphanum = ALPHA / DIGIT
   unreserved = alphanum / mark
   uric = escaped / reserved / unreserved
   pchar = escaped / unreserved / "$" / "&" / "+" / "," / ":" / "=" / "@"


   ; RFC 1738, RFC 2396
   ftp-url = "FTP://" login ["/" segment *("/" segment) [";TYPE=" ("A" / "D" / "I")]]
   segment = *pchar


   ; RFC 1738, RFC 2396
   http-url = "HTTP://" hostport ["/" segment *("/" segment) ["?" *uric]]
   segment = *(pchar / ";")


   ; RFC 1738, RFC 2396, <http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q188997&>
   file-url = "FILE://" [host / "LOCALHOST" / netbios-name] ["/" segment *("/" segment)]
   segment = *pchar
   netbios-name = 1*{<alphanum / "!" / "#" / "$" / "%" / "&" / "'" / "(" / ")" / "-" / "." / "@" / "^" / "_" / "{" / "}" / "~"> using (escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "-" / "." / "@" / "_" / "~")}


   ; RFC 2368, RFC 2396
   mailto-url = "MAILTO:" [to] [headers]
   to = {RFC 822 <#mailbox> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")}
   headers = "?" header *("&" header)
   header = hname "=" hvalue
   hname = {RFC 822 <field-name> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")} / "BODY"
   hvalue = {RFC 822 <field-body> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")}


   ; private (see RFC 1738, RFC 2396)
   vnd-sun-star-webdav-url = "VND.SUN.STAR.WEBDAV://" hostport ["/" segment *("/" segment) ["?" *uric]]
   segment = *(pchar / ";")


   ; RFC 1738, RFC 2396, RFC 2732
   news-url = "NEWS:" grouppart
   grouppart = "*" / group / article
   group = alpha *(alphanum / "+" / "-" / "." / "_")
   article = 1*(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "?" / "_" / "~") "@" host


   ; private
   private-url = "PRIVATE:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")


   ; private
   vnd-sun-star-help-url = "VND.SUN.STAR.HELP://" name *("/" segment) ["?" *uric]
   name = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")
   segment = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")


   ; private
   https-url = "HTTPS://" hostport ["/" segment *("/" segment) ["?" *uric]]
   segment = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")


   ; private
   slot-url = "SLOT:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")


   ; private
   macro-url = "MACRO:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")


   ; private
   javascript-url = "JAVASCRIPT:" *uric


   ; private (see RFC 2192)
   imap-url = "IMAP://" user [";AUTH=" auth] "@" hostport "/" segment *("/" segment) ["/;UID=" nz_number]
   user = 1*{RFC 2060 <CHAR8> using (escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "=" / "_" / "~")}
   auth = {RFC 2060 <atom> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "+" / "," / "-" / "." / "=" / "_" / "~")}
   segment = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / "=" / "@" / "_" / "~")
   nz_number = {RFC 2060 <nz_number> using *DIGIT}


   ; private
   pop3-url = "POP3://" login ["/" ["<" *uric ">"]]


   ; RFC 2397
   data-url = "DATA:" [mediatype] [";BASE64"] "," *uric
   mediatype = [type "/" subtype] *(";" attribute "=" value)
   type = {RFC 2045 <type> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")}
   subtype = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")}
   attribute = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")}
   value = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")}


   ; RFC 2392, RFC 2396
   cid-url = "CID:" {RFC 822 <addr-spec> using *uric}


   ; private
   out-url = "OUT:///~" name ["/" *uric]
   name = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "?" / "@" / "_" / "~"


   ; private
   vnd-sun-star-hier-url = "VND.SUN.STAR.HIER:" ["//"reg_name] *("/" *pchar)
   reg_name = 1*(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")

   ; private
   vim-url = "VIM://" +vimc [":" *vimc] ["/" [("INBOX" message) / ("NEWSGROUPS" ["/" [+vimc message]])]]
   message = ["/" [+vimc [":" +DIGIT "." +DIGIT "." +DIGIT]]]
   vimc = ("=" HEXDIG HEXDIG) / alphanum


   ; private
   uno-url = ".UNO:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")


   ; private
   component-url = ".COMPONENT:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")


   ; private
   vnd-sun-star-pkg-url = "VND.SUN.STAR.PKG://" reg_name *("/" *pchar) ["?" *uric]
   reg_name = 1*(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")


   ; RFC 2255
   ldap-url = "LDAP://" [hostport] ["/" [dn ["?" [attrdesct *("," attrdesc)] ["?" ["base" / "one" / "sub"] ["?" [filter] ["?" extension *("," extension)]]]]]]
   dn = {RFC 2253 <distinguishedName> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")}
   attrdesc = {RFC 2251 <AttributeDescription> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")}
   filter = {RFC 2254 <filter> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")}
   extension = ["!"] ["X-"] extoken ["=" exvalue]
   extoken = {RFC 2252 <oid> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")}
   exvalue = {RFC 2251 <LDAPString> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")}


   ; private
   db-url = "DB:" *uric


   ; private
   vnd-sun-star-cmd-url = "VND.SUN.STAR.CMD:" opaque_part
   opaque_part = uric_no_slash *uric
   uric_no_slash = unreserved / escaped / ";" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / ","


   ; private
   vnd-sun-star-url = "VND.SUN.STAR.ODMA:" ["/" *uric_no_slash]
   uric_no_slash = unreserved / escaped / ";" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / ","


   ; RFC 1738
   telnet-url = "TELNET://" login ["/"]


   ; private
   vnd-sun-star-expand-url = "VND.SUN.STAR.EXPAND:" opaque_part
   opaque_part = uric_no_slash *uric
   uric_no_slash = unreserved / escaped / ";" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / ","


   ; private
   vnd-sun-star-tdoc-url = "VND.SUN.STAR.TDOC:/" segment *("/" segment)
   segment = *pchar


   ; private
   unknown-url = scheme ":" 1*uric
   scheme = ALPHA *(alphanum / "+" / "-" / ".")


   ; private (http://ubiqx.org/cifs/Appendix-D.html):
   smb-url = "SMB://" login ["/" segment *("/" segment) ["?" *uric]]
   segment = *(pchar / ";")
 */

//============================================================================
inline sal_Int32 INetURLObject::SubString::clear()
{
	sal_Int32 nDelta = -m_nLength;
	m_nBegin = -1;
	m_nLength = 0;
	return nDelta;
}

inline sal_Int32 INetURLObject::SubString::set(rtl::OUStringBuffer & rString,
									   rtl::OUString const & rSubString)
{
	rtl::OUString sTemp(rString.makeStringAndClear());
	sal_Int32 nDelta = set(sTemp, rSubString);
	rString.append(sTemp);
	return nDelta;
}

inline sal_Int32 INetURLObject::SubString::set(rtl::OUString & rString,
									   rtl::OUString const & rSubString)
{
	sal_Int32 nDelta = rSubString.getLength() - m_nLength;

	rString = rString.replaceAt(m_nBegin, m_nLength, rSubString);

	m_nLength = rSubString.getLength();
	return nDelta;
}

inline sal_Int32 INetURLObject::SubString::set(rtl::OUStringBuffer & rString,
									   rtl::OUString const & rSubString,
											   sal_Int32 nTheBegin)
{
	m_nBegin = nTheBegin;
	return set(rString, rSubString);
}

//============================================================================
inline void INetURLObject::SubString::operator +=(sal_Int32 nDelta)
{
	if (isPresent())
		m_nBegin = m_nBegin + nDelta;
}

//============================================================================
int INetURLObject::SubString::compare(SubString const & rOther,
                                      rtl::OUStringBuffer const & rThisString,
                                      rtl::OUStringBuffer const & rOtherString) const
{
    sal_Int32 len = std::min(m_nLength, rOther.m_nLength);
    sal_Unicode const * p1 = rThisString.getStr() + m_nBegin;
    sal_Unicode const * end = p1 + len;
    sal_Unicode const * p2 = rOtherString.getStr() + rOther.m_nBegin;
    while (p1 != end) {
        if (*p1 < *p2) {
            return -1;
        } else if (*p1 > *p2) {
            return 1;
        }
        ++p1;
        ++p2;
    }
    return m_nLength < rOther.m_nLength ? -1
        : m_nLength > rOther.m_nLength ? 1
        : 0;
}

//============================================================================
struct INetURLObject::SchemeInfo
{
	sal_Char const * m_pScheme;
	sal_Char const * m_pPrefix;
	sal_uInt16 m_nDefaultPort;
	bool m_bAuthority;
	bool m_bUser;
	bool m_bAuth;
	bool m_bPassword;
	bool m_bHost;
	bool m_bPort;
	bool m_bHierarchical;
	bool m_bQuery;
};

//============================================================================
struct INetURLObject::PrefixInfo
{
	enum Kind { OFFICIAL, INTERNAL, EXTERNAL, ALIAS }; // order is important!

	sal_Char const * m_pPrefix;
	sal_Char const * m_pTranslatedPrefix;
	INetProtocol m_eScheme;
	Kind m_eKind;
};

//============================================================================
static INetURLObject::SchemeInfo const aSchemeInfoMap[INET_PROT_END]
	= { { "", "", 0, false, false, false, false, false, false, false,
		  false },
		{ "ftp", "ftp://", 21, true, true, false, true, true, true, true,
		  false },
		{ "http", "http://", 80, true, false, false, false, true, true,
		  true, true },
		{ "file", "file://", 0, true, false, false, false, true, false,
		  true, false },
		{ "mailto", "mailto:", 0, false, false, false, false, false,
		  false, false, true },
		{ "vnd.sun.star.webdav", "vnd.sun.star.webdav://", 80, true, false,
		  false, false, true, true, true, true },
		{ "news", "news:", 0, false, false, false, false, false, false, false,
          false },
		{ "private", "private:", 0, false, false, false, false, false,
		  false, false, true },
		{ "vnd.sun.star.help", "vnd.sun.star.help://", 0, true, false, false,
		  false, false, false, true, true },
		{ "https", "https://", 443, true, false, false, false, true, true,
		  true, true },
		{ "slot", "slot:", 0, false, false, false, false, false, false,
		  false, true },
		{ "macro", "macro:", 0, false, false, false, false, false, false,
		  false, true },
		{ "javascript", "javascript:", 0, false, false, false, false,
		  false, false, false, false },
		{ "imap", "imap://", 143, true, true, true, false, true, true,
		  true, false },
		{ "pop3", "pop3://", 110, true, true, false, true, true, true,
		  false, false },
		{ "data", "data:", 0, false, false, false, false, false, false,
		  false, false },
		{ "cid", "cid:", 0, false, false, false, false, false, false,
		  false, false },
		{ "out", "out://", 0, true, false, false, false, false, false,
		  false, false },
		{ "vnd.sun.star.hier", "vnd.sun.star.hier:", 0, true, false, false,
		  false, false, false, true, false },
		{ "vim", "vim://", 0, true, true, false, true, false, false, true,
		  false },
		{ ".uno", ".uno:", 0, false, false, false, false, false, false,
		  false, true },
		{ ".component", ".component:", 0, false, false, false, false,
		  false, false, false, true },
		{ "vnd.sun.star.pkg", "vnd.sun.star.pkg://", 0, true, false, false,
		  false, false, false, true, true },
		{ "ldap", "ldap://", 389, true, false, false, false, true, true,
		  false, true },
		{ "db", "db:", 0, false, false, false, false, false, false, false,
		  false },
		{ "vnd.sun.star.cmd", "vnd.sun.star.cmd:", 0, false, false, false,
		  false, false, false, false, false },
		{ "vnd.sun.star.odma", "vnd.sun.star.odma:", 0, false, false, false,
		  false, false, false, true, false },
		{ "telnet", "telnet://", 23, true, true, false, true, true, true, true,
          false },
		{ "vnd.sun.star.expand", "vnd.sun.star.expand:", 0, false, false, false,
		  false, false, false, false, false },
        { "vnd.sun.star.tdoc", "vnd.sun.star.tdoc:", 0, false, false, false,
          false, false, false, true, false },
        { "", "", 0, false, false, false, false, true, true, true, false },
        { "smb", "smb://", 139, true, true, false, true, true, true, true,
          true },
		{ "hid", "hid:", 0, false, false, false, false, false, false,
        false, true } };

// static
inline INetURLObject::SchemeInfo const &
INetURLObject::getSchemeInfo(INetProtocol eTheScheme)
{
	return aSchemeInfoMap[eTheScheme];
};

//============================================================================
inline INetURLObject::SchemeInfo const & INetURLObject::getSchemeInfo() const
{
	return getSchemeInfo(m_eScheme);
}

//============================================================================
// static
inline void INetURLObject::appendEscape(rtl::OUStringBuffer & rTheText,
										sal_Char cEscapePrefix,
										sal_uInt32 nOctet)
{
	rTheText.append(sal_Unicode(cEscapePrefix));
	rTheText.append(sal_Unicode(INetMIME::getHexDigit(int(nOctet >> 4))));
	rTheText.append(sal_Unicode(INetMIME::getHexDigit(int(nOctet & 15))));
}

//============================================================================
namespace unnamed_tools_urlobj {

enum
{
	PA = INetURLObject::PART_OBSOLETE_NORMAL,
	PB = INetURLObject::PART_OBSOLETE_FILE,
	PC = INetURLObject::PART_OBSOLETE_PARAM,
	PD = INetURLObject::PART_USER_PASSWORD,
	PE = INetURLObject::PART_IMAP_ACHAR,
	PF = INetURLObject::PART_VIM,
	PG = INetURLObject::PART_HOST_EXTRA,
	PH = INetURLObject::PART_FPATH,
	PI = INetURLObject::PART_AUTHORITY,
	PJ = INetURLObject::PART_PATH_SEGMENTS_EXTRA,
	PK = INetURLObject::PART_REL_SEGMENT_EXTRA,
	PL = INetURLObject::PART_URIC,
	PM = INetURLObject::PART_HTTP_PATH,
	PN = INetURLObject::PART_FILE_SEGMENT_EXTRA,
	PO = INetURLObject::PART_MESSAGE_ID,
	PP = INetURLObject::PART_MESSAGE_ID_PATH,
	PQ = INetURLObject::PART_MAILTO,
	PR = INetURLObject::PART_PATH_BEFORE_QUERY,
	PS = INetURLObject::PART_PCHAR,
	PT = INetURLObject::PART_FRAGMENT,
	PU = INetURLObject::PART_VISIBLE,
	PV = INetURLObject::PART_VISIBLE_NONSPECIAL,
	PW = INetURLObject::PART_CREATEFRAGMENT,
	PX = INetURLObject::PART_UNO_PARAM_VALUE,
	PY = INetURLObject::PART_UNAMBIGUOUS,
	PZ = INetURLObject::PART_URIC_NO_SLASH,
    P1 = INetURLObject::PART_HTTP_QUERY,
    P2 = INetURLObject::PART_NEWS_ARTICLE_LOCALPART
};

static sal_uInt32 const aMustEncodeMap[128]
	= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*   */	                                                                        PY,
/* ! */	      PC+PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* " */	                                                            PU+PV      +PY,
/* # */	                                                            PU,
/* $ */	         PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* % */	                                                            PU,
/* & */ PA+PB+PC+PD+PE      +PH+PI+PJ+PK+PL+PM+PN+PO+PP   +PR+PS+PT+PU+PV+PW+PX   +PZ+P1+P2,
/* ' */	         PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* ( */	         PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* ) */	         PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* * */ PA+PB+PC+PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* + */ PA+PB+PC+PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX   +PZ+P1+P2,
/* , */ PA+PB+PC+PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW      +PZ+P1+P2,
/* - */ PA+PB+PC+PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* . */ PA+PB+PC+PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* / */ PA+PB+PC            +PH   +PJ   +PL+PM      +PP+PQ+PR   +PT+PU+PV   +PX         +P2,
/* 0 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 1 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 2 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 3 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 4 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 5 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 6 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 7 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 8 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* 9 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* : */	   PB+PC            +PH+PI+PJ   +PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX   +PZ+P1+P2,
/* ; */	      PC+PD            +PI+PJ+PK+PL+PM   +PO+PP+PQ+PR   +PT+PU   +PW      +PZ+P1+P2,
/* < */	      PC                                 +PO+PP            +PU+PV      +PY,
/* = */ PA+PB+PC+PD+PE      +PH+PI+PJ+PK+PL+PM+PN         +PR+PS+PT+PU+PV+PW      +PZ+P1+P2,
/* > */	      PC                                 +PO+PP            +PU+PV      +PY,
/* ? */	      PC                        +PL                     +PT+PU   +PW+PX   +PZ   +P2,
/* @ */	      PC            +PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1,
/* A */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* B */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* C */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* D */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* E */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* F */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* G */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* H */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* I */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* J */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* K */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* L */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* M */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* N */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* O */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* P */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* Q */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* R */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* S */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* T */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* U */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* V */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* W */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* X */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* Y */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* Z */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* [ */	                                 PL                        +PU+PV   +PX,
/* \ */	   PB                                                      +PU+PV      +PY,
/* ] */	                                 PL                        +PU+PV   +PX,
/* ^ */	                                                            PU+PV      +PY,
/* _ */ PA+PB+PC+PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* ` */	                                                            PU+PV      +PY,
/* a */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* b */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* c */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* d */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* e */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* f */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* g */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* h */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* i */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* j */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* k */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* l */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* m */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* n */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* o */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* p */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* q */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* r */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* s */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* t */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* u */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* v */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* w */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* x */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* y */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* z */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2,
/* { */	                                                            PU+PV      +PY,
/* | */	   PB+PC                              +PN               +PT+PU+PV      +PY,
/* } */	                                                            PU+PV      +PY,
/* ~ */ PA+PB+PC+PD+PE      +PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ  +P2,
		0 };

inline bool mustEncode(sal_uInt32 nUTF32, INetURLObject::Part ePart)
{
	return !INetMIME::isUSASCII(nUTF32) || !(aMustEncodeMap[nUTF32] & ePart);
}

}

//============================================================================
void INetURLObject::setInvalid()
{
	m_aAbsURIRef.setLength(0);
	m_eScheme = INET_PROT_NOT_VALID;
    m_aScheme.clear();
	m_aUser.clear();
	m_aAuth.clear();
	m_aHost.clear();
	m_aPort.clear();
	m_aPath.clear();
	m_aQuery.clear();
	m_aFragment.clear();
}

//============================================================================

namespace unnamed_tools_urlobj {

INetURLObject::FSysStyle
guessFSysStyleByCounting(sal_Unicode const * pBegin,
						 sal_Unicode const * pEnd,
						 INetURLObject::FSysStyle eStyle)
{
	DBG_ASSERT(eStyle
			       & (INetURLObject::FSYS_UNX
					      | INetURLObject::FSYS_DOS
					      | INetURLObject::FSYS_MAC),
			   "guessFSysStyleByCounting(): Bad style");
	DBG_ASSERT(std::numeric_limits< sal_Int32 >::min() < pBegin - pEnd
			   && pEnd - pBegin <= std::numeric_limits< sal_Int32 >::max(),
			   "guessFSysStyleByCounting(): Too big");
	sal_Int32 nSlashCount
		= eStyle & INetURLObject::FSYS_UNX ?
		      0 : std::numeric_limits< sal_Int32 >::min();
	sal_Int32 nBackslashCount
		= eStyle & INetURLObject::FSYS_DOS ?
		      0 : std::numeric_limits< sal_Int32 >::min();
	sal_Int32 nColonCount
		= eStyle & INetURLObject::FSYS_MAC ?
		      0 : std::numeric_limits< sal_Int32 >::min();
	while (pBegin != pEnd)
		switch (*pBegin++)
		{
			case '/':
				++nSlashCount;
				break;

			case '\\':
				++nBackslashCount;
				break;

			case ':':
				++nColonCount;
				break;
		}
	return nSlashCount >= nBackslashCount ?
		       nSlashCount >= nColonCount ?
		           INetURLObject::FSYS_UNX : INetURLObject::FSYS_MAC :
		       nBackslashCount >= nColonCount ?
		           INetURLObject::FSYS_DOS : INetURLObject::FSYS_MAC;
}

rtl::OUString parseScheme(
    sal_Unicode const ** begin, sal_Unicode const * end,
    sal_uInt32 fragmentDelimiter)
{
    sal_Unicode const * p = *begin;
    if (p != end && INetMIME::isAlpha(*p)) {
        do {
            ++p;
        } while (p != end
                 && (INetMIME::isAlphanumeric(*p) || *p == '+' || *p == '-'
                     || *p == '.'));
        // #i34835# To avoid problems with Windows file paths like "C:\foo",
        // do not accept generic schemes that are only one character long:
        if (end - p > 1 && p[0] == ':' && p[1] != fragmentDelimiter
            && p - *begin >= 2)
        {
			rtl::OUString scheme(
                rtl::OUString(*begin, p - *begin).toAsciiLowerCase());
            *begin = p + 1;
            return scheme;
        }
    }
    return rtl::OUString();
}

}

bool INetURLObject::setAbsURIRef(rtl::OUString const & rTheAbsURIRef,
								 bool bOctets,
								 EncodeMechanism eMechanism,
								 rtl_TextEncoding eCharset,
								 bool bSmart,
								 FSysStyle eStyle)
{
	sal_Unicode const * pPos = rTheAbsURIRef.getStr();
	sal_Unicode const * pEnd = pPos + rTheAbsURIRef.getLength();

	setInvalid();

	sal_uInt32 nFragmentDelimiter = '#';

	rtl::OUStringBuffer aSynAbsURIRef;

	// Parse <scheme>:
	sal_Unicode const * p = pPos;
	PrefixInfo const * pPrefix = getPrefix(p, pEnd);
	if (pPrefix)
	{
		pPos = p;
		m_eScheme = pPrefix->m_eScheme;

		rtl::OUString sTemp(rtl::OUString::createFromAscii(pPrefix->m_eKind
										         >= PrefixInfo::EXTERNAL ?
										     pPrefix->m_pTranslatedPrefix :
										     pPrefix->m_pPrefix));
		aSynAbsURIRef.append(sTemp);
        m_aScheme = SubString( 0, sTemp.indexOf(static_cast< sal_Unicode >(':')) );
	}
	else
	{
		if (bSmart)
		{
			// For scheme detection, the first (if any) of the following
			// productions that matches the input string (and for which the
			// appropriate style bit is set in eStyle, if applicable)
			// determines the scheme. The productions use the auxiliary rules
			//
			//	  domain = label *("." label)
			//	  label = alphanum [*(alphanum / "-") alphanum]
			//	  alphanum = ALPHA / DIGIT
			//	  IPv6reference = "[" IPv6address "]"
			//	  IPv6address = hexpart [":" IPv4address]
			//	  IPv4address = 1*3DIGIT 3("." 1*3DIGIT)
			//	  hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq])
			//	  hexseq = hex4 *(":" hex4)
			//	  hex4 = 1*4HEXDIG
			//	  UCS4 = <any UCS4 character>
			//
			// 1st Production (known scheme):
			//	  <one of the known schemes, ignoring case> ":" *UCS4
			//
			// 2nd Production (mailto):
			//	  domain "@" domain
			//
			// 3rd Production (ftp):
			//	  "FTP" 2*("." label) ["/" *UCS4]
			//
			// 4th Production (http):
			//	  label 2*("." label) ["/" *UCS4]
			//
			// 5th Production (file):
			//	  "//" (domain / IPv6reference) ["/" *UCS4]
			//
			// 6th Production (Unix file):
			//    "/" *UCS4
			//
			// 7th Production (UNC file; FSYS_DOS only):
			//	  "\\" domain ["\" *UCS4]
			//
			// 8th Production (Unix-like DOS file; FSYS_DOS only):
			//	  ALPHA ":" ["/" *UCS4]
			//
			// 9th Production (DOS file; FSYS_DOS only):
			//	  ALPHA ":" ["\" *UCS4]
			//
			// For the 'non URL' file productions 6--9, the interpretation of
			// the input as a (degenerate) URI is turned off, i.e., escape
			// sequences and fragments are never detected as such, but are
			// taken as literal characters.

			sal_Unicode const * p1 = pPos;
			if (eStyle & FSYS_DOS
				&& pEnd - p1 >= 2
				&& INetMIME::isAlpha(p1[0])
				&& p1[1] == ':'
                && (pEnd - p1 == 2 || p1[2] == '/' || p1[2] == '\\'))
			{
				m_eScheme = INET_PROT_FILE; // 8th, 9th
				eMechanism = ENCODE_ALL;
				nFragmentDelimiter = 0x80000000;
			}
			else if (pEnd - p1 >= 2 && p1[0] == '/' && p1[1] == '/')
			{
				p1 += 2;
				if ((scanDomain(p1, pEnd) > 0 || scanIPv6reference(p1, pEnd))
					&& (p1 == pEnd || *p1 == '/'))
					m_eScheme = INET_PROT_FILE; // 5th
			}
			else if (p1 != pEnd && *p1 == '/')
			{
				m_eScheme = INET_PROT_FILE; // 6th
				eMechanism = ENCODE_ALL;
				nFragmentDelimiter = 0x80000000;
			}
			else if (eStyle & FSYS_DOS
					 && pEnd - p1 >= 2
					 && p1[0] == '\\'
					 && p1[1] == '\\')
			{
				p1 += 2;
                sal_Int32 n = rtl_ustr_indexOfChar_WithLength(
                    p1, pEnd - p1, '\\');
                sal_Unicode const * pe = n == -1 ? pEnd : p1 + n;
				if (
                    parseHostOrNetBiosName(
                        p1, pe, bOctets, ENCODE_ALL, RTL_TEXTENCODING_DONTKNOW,
                        true, NULL) ||
                    (scanDomain(p1, pe) > 0 && p1 == pe)
                   )
				{
					m_eScheme = INET_PROT_FILE; // 7th
					eMechanism = ENCODE_ALL;
					nFragmentDelimiter = 0x80000000;
				}
			}
			else
			{
				sal_Unicode const * pDomainEnd = p1;
				sal_uInt32 nLabels = scanDomain(pDomainEnd, pEnd);
				if (nLabels > 0 && pDomainEnd != pEnd && *pDomainEnd == '@')
				{
					++pDomainEnd;
					if (scanDomain(pDomainEnd, pEnd) > 0
						&& pDomainEnd == pEnd)
						m_eScheme = INET_PROT_MAILTO; // 2nd
				}
				else if (nLabels >= 3
						 && (pDomainEnd == pEnd || *pDomainEnd == '/'))
					m_eScheme
						= pDomainEnd - p1 >= 4
                          && (p1[0] == 'f' || p1[0] == 'F')
                          && (p1[1] == 't' || p1[1] == 'T')
                          && (p1[2] == 'p' || p1[2] == 'P')
						  && p1[3] == '.' ?
						      INET_PROT_FTP : INET_PROT_HTTP; // 3rd, 4th
			}
		}

		rtl::OUString aSynScheme;
        if (m_eScheme == INET_PROT_NOT_VALID) {
            sal_Unicode const * p1 = pPos;
            aSynScheme = parseScheme(&p1, pEnd, nFragmentDelimiter);
            if (aSynScheme.getLength() > 0)
            {
                m_eScheme = INET_PROT_GENERIC;
                pPos = p1;
            }
        }

        if (bSmart && m_eScheme == INET_PROT_NOT_VALID && pPos != pEnd
            && *pPos != nFragmentDelimiter)
        {
            m_eScheme = m_eSmartScheme;
        }

		if (m_eScheme == INET_PROT_NOT_VALID)
		{
			setInvalid();
			return false;
		}

        if (m_eScheme != INET_PROT_GENERIC) {
            aSynScheme = rtl::OUString::createFromAscii(getSchemeInfo().m_pScheme);
        }
        m_aScheme.set(aSynAbsURIRef, aSynScheme, aSynAbsURIRef.getLength());
        aSynAbsURIRef.append(sal_Unicode(':'));
	}

	sal_Char cEscapePrefix = getEscapePrefix();
	sal_uInt32 nSegmentDelimiter = '/';
	sal_uInt32 nAltSegmentDelimiter = 0x80000000;
	bool bSkippedInitialSlash = false;

	// Parse //<user>;AUTH=<auth>@<host>:<port> or
	// //<user>:<password>@<host>:<port> or
    // //<reg_name>
	if (getSchemeInfo().m_bAuthority)
	{
		sal_Unicode const * pUserInfoBegin = 0;
		sal_Unicode const * pUserInfoEnd = 0;
		sal_Unicode const * pHostPortBegin = 0;
		sal_Unicode const * pHostPortEnd = 0;

		switch (m_eScheme)
		{
			case INET_PROT_VND_SUN_STAR_HELP:
			{
				if (pEnd - pPos < 2 || *pPos++ != '/' || *pPos++ != '/')
				{
					setInvalid();
					return false;
				}
                aSynAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
				rtl::OUStringBuffer aSynAuthority;
				while (pPos < pEnd
					   && *pPos != '/' && *pPos != '?'
					   && *pPos != nFragmentDelimiter)
				{
					EscapeType eEscapeType;
					sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
												 cEscapePrefix, eMechanism,
												 eCharset, eEscapeType);
					appendUCS4(aSynAuthority, nUTF32, eEscapeType, bOctets,
							   PART_AUTHORITY, cEscapePrefix, eCharset,
							   false);
				}
				m_aHost.set(aSynAbsURIRef,
							aSynAuthority.makeStringAndClear(),
							aSynAbsURIRef.getLength());
					// misusing m_aHost to store the authority
				break;
			}

            case INET_PROT_VND_SUN_STAR_HIER:
            {
                if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
                {
                    pPos += 2;
                    aSynAbsURIRef.
                        appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
                    rtl::OUStringBuffer aSynAuthority;
                    while (pPos < pEnd
                           && *pPos != '/' && *pPos != '?'
                           && *pPos != nFragmentDelimiter)
                    {
                        EscapeType eEscapeType;
                        sal_uInt32 nUTF32 = getUTF32(pPos,
                                                     pEnd,
                                                     bOctets,
                                                     cEscapePrefix,
                                                     eMechanism,
                                                     eCharset,
                                                     eEscapeType);
                        appendUCS4(aSynAuthority,
                                   nUTF32,
                                   eEscapeType,
                                   bOctets,
                                   PART_AUTHORITY,
                                   cEscapePrefix,
                                   eCharset,
                                   false);
                    }
                    if (aSynAuthority.getLength() == 0)
                    {
                        setInvalid();
                        return false;
                    }
                    m_aHost.set(aSynAbsURIRef,
                                aSynAuthority.makeStringAndClear(),
                                aSynAbsURIRef.getLength());
                        // misusing m_aHost to store the authority
                }
                break;
            }

			case INET_PROT_VND_SUN_STAR_PKG:
			{
				if (pEnd - pPos < 2 || *pPos++ != '/' || *pPos++ != '/')
				{
					setInvalid();
					return false;
				}
                aSynAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
				rtl::OUStringBuffer aSynAuthority;
				while (pPos < pEnd
					   && *pPos != '/' && *pPos != '?'
					   && *pPos != nFragmentDelimiter)
				{
					EscapeType eEscapeType;
					sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
												 cEscapePrefix, eMechanism,
												 eCharset, eEscapeType);
					appendUCS4(aSynAuthority, nUTF32, eEscapeType, bOctets,
							   PART_AUTHORITY, cEscapePrefix, eCharset,
							   false);
				}
				if (aSynAuthority.getLength() == 0)
				{
					setInvalid();
					return false;
				}
				m_aHost.set(aSynAbsURIRef,
							aSynAuthority.makeStringAndClear(),
							aSynAbsURIRef.getLength());
					// misusing m_aHost to store the authority
				break;
			}

			case INET_PROT_FILE:
				if (bSmart)
				{
					// The first of the following seven productions that
					// matches the rest of the input string (and for which the
					// appropriate style bit is set in eStyle, if applicable)
					// determines the used notation.  The productions use the
					// auxiliary rules
					//
					//	  domain = label *("." label)
					//	  label = alphanum [*(alphanum / "-") alphanum]
					//	  alphanum = ALPHA / DIGIT
					//	  IPv6reference = "[" IPv6address "]"
					//	  IPv6address = hexpart [":" IPv4address]
					//	  IPv4address = 1*3DIGIT 3("." 1*3DIGIT)
					//	  hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq])
					//	  hexseq = hex4 *(":" hex4)
					//	  hex4 = 1*4HEXDIG
					//	  path = <any UCS4 character except "#">
					//	  UCS4 = <any UCS4 character>

					// 1st Production (URL):
					//	  "//" [domain / IPv6reference] ["/" *path]
					//		  ["#" *UCS4]
					//	becomes
					//	  "file://" domain "/" *path ["#" *UCS4]
					if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
					{
						sal_Unicode const * p1 = pPos + 2;
                        while (p1 != pEnd && *p1 != '/' &&
                               *p1 != nFragmentDelimiter)
                        {
                            ++p1;
                        }
                        if (parseHostOrNetBiosName(
                                pPos + 2, p1, bOctets, ENCODE_ALL,
                                RTL_TEXTENCODING_DONTKNOW, true, NULL))
						{
                            aSynAbsURIRef.
                                appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
							pHostPortBegin = pPos + 2;
							pHostPortEnd = p1;
							pPos = p1;
							break;
						}
					}

                    // 2nd Production (MS IE generated 1; FSYS_DOS only):
                    //    "//" ALPHA ":" ["/" *path] ["#" *UCS4]
					//	becomes
					//	  "file:///" ALPHA ":" ["/" *path] ["#" *UCS4]
					//	replacing "\" by "/" within <*path>
                    //
                    // 3rd Production (MS IE generated 2; FSYS_DOS only):
                    //    "//" ALPHA ":" ["\" *path] ["#" *UCS4]
					//	becomes
					//	  "file:///" ALPHA ":" ["/" *path] ["#" *UCS4]
					//	replacing "\" by "/" within <*path>
                    //
					// 4th Production (misscounted slashes):
					//	  "//" *path ["#" *UCS4]
					//	becomes
					//	  "file:///" *path ["#" *UCS4]
					if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
					{
                        aSynAbsURIRef.
                            appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
						pPos += 2;
						bSkippedInitialSlash = true;
                        if ((eStyle & FSYS_DOS) != 0
                            && pEnd - pPos >= 2
                            && INetMIME::isAlpha(pPos[0])
                            && pPos[1] == ':'
                            && (pEnd - pPos == 2
                                || pPos[2] == '/' || pPos[2] == '\\'))
                            nAltSegmentDelimiter = '\\';
						break;
					}

					// 5th Production (Unix):
					//	  "/" *path ["#" *UCS4]
					//	becomes
					//	  "file:///" *path ["#" *UCS4]
					if (pPos < pEnd && *pPos == '/')
                    {
                        aSynAbsURIRef.
                            appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
						break;
                    }

					// 6th Production (UNC; FSYS_DOS only):
					//	  "\\" domain ["\" *path] ["#" *UCS4]
					//	becomes
					//	  "file://" domain "/" *path ["#" *UCS4]
					//	replacing "\" by "/" within <*path>
					if (eStyle & FSYS_DOS
						&& pEnd - pPos >= 2
						&& pPos[0] == '\\'
						&& pPos[1] == '\\')
					{
						sal_Unicode const * p1 = pPos + 2;
                        sal_Unicode const * pe = p1;
                        while (pe < pEnd && *pe != '\\' &&
                               *pe != nFragmentDelimiter)
                        {
                            ++pe;
                        }
                        if (
                             parseHostOrNetBiosName(
                                p1, pe, bOctets, ENCODE_ALL,
                                RTL_TEXTENCODING_DONTKNOW, true, NULL) ||
                             (scanDomain(p1, pe) > 0 && p1 == pe)
                           )
						{
                            aSynAbsURIRef.
                                appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
							pHostPortBegin = pPos + 2;
							pHostPortEnd = pe;
							pPos = pe;
							nSegmentDelimiter = '\\';
							break;
						}
					}

					// 7th Production (Unix-like DOS; FSYS_DOS only):
					//	  ALPHA ":" ["/" *path] ["#" *UCS4]
					//	becomes
					//	  "file:///" ALPHA ":" ["/" *path] ["#" *UCS4]
					//	replacing "\" by "/" within <*path>
					//
					// 8th Production (DOS; FSYS_DOS only):
					//	  ALPHA ":" ["\" *path] ["#" *UCS4]
					//	becomes
					//	  "file:///" ALPHA ":" ["/" *path] ["#" *UCS4]
					//	replacing "\" by "/" within <*path>
					if (eStyle & FSYS_DOS
						&& pEnd - pPos >= 2
						&& INetMIME::isAlpha(pPos[0])
						&& pPos[1] == ':'
						&& (pEnd - pPos == 2
                            || pPos[2] == '/'
                            || pPos[2] == '\\'))
					{
                        aSynAbsURIRef.
                            appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
						nAltSegmentDelimiter = '\\';
						bSkippedInitialSlash = true;
						break;
					}

					// 9th Production (any):
					//	  *path ["#" *UCS4]
					//	becomes
					//	  "file:///" *path ["#" *UCS4]
					//	replacing the delimiter by "/" within <*path>.	The
					//	delimiter is that character from the set { "/", "\",
					//	":" } which appears most often in <*path> (if FSYS_UNX
					//  is not among the style bits, "/" is removed from the
					//  set; if FSYS_DOS is not among the style bits, "\" is
					//  removed from the set; if FSYS_MAC is not among the
					//  style bits, ":" is removed from the set).  If two or
					//	more characters appear the same number of times, the
					//	character mentioned first in that set is chosen.  If
					//	the first character of <*path> is the delimiter, that
					//	character is not copied.
					if (eStyle & (FSYS_UNX | FSYS_DOS | FSYS_MAC))
					{
                        aSynAbsURIRef.
                            appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
						switch (guessFSysStyleByCounting(pPos, pEnd, eStyle))
						{
							case FSYS_UNX:
								nSegmentDelimiter = '/';
								break;

							case FSYS_DOS:
								nSegmentDelimiter = '\\';
								break;

							case FSYS_MAC:
								nSegmentDelimiter = ':';
								break;

							default:
								DBG_ERROR(
									"INetURLObject::setAbsURIRef():"
									    " Bad guessFSysStyleByCounting");
								break;
						}
						bSkippedInitialSlash
							= pPos != pEnd && *pPos != nSegmentDelimiter;
						break;
					}
				}
			default:
			{
                // For INET_PROT_FILE, allow an empty authority ("//") to be
                // missing if the following path starts with an explicit "/"
                // (Java is notorious in generating such file URLs, so be
                // liberal here):
				if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
					pPos += 2;
                else if (!bSmart
                         && !(m_eScheme == INET_PROT_FILE
                              && pPos != pEnd && *pPos == '/'))
				{
					setInvalid();
					return false;
				}
                aSynAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));

				sal_Unicode const * pAuthority = pPos;
				sal_uInt32 c = getSchemeInfo().m_bQuery ? '?' : 0x80000000;
				while (pPos < pEnd && *pPos != '/' && *pPos != c
					   && *pPos != nFragmentDelimiter)
					++pPos;
				if (getSchemeInfo().m_bUser)
					if (getSchemeInfo().m_bHost)
					{
						sal_Unicode const * p1 = pAuthority;
						while (p1 < pPos && *p1 != '@')
							++p1;
						if (p1 == pPos)
						{
							pHostPortBegin = pAuthority;
							pHostPortEnd = pPos;
						}
						else
						{
							pUserInfoBegin = pAuthority;
							pUserInfoEnd = p1;
							pHostPortBegin = p1 + 1;
							pHostPortEnd = pPos;
						}
					}
					else
					{
						pUserInfoBegin = pAuthority;
						pUserInfoEnd = pPos;
					}
				else if (getSchemeInfo().m_bHost) 
				{
					pHostPortBegin = pAuthority;
					pHostPortEnd = pPos;
				}
				else if (pPos != pAuthority)
				{
					setInvalid();
					return false;
				}
				break;
			}
		}

		if (pUserInfoBegin)
		{
			Part ePart = m_eScheme == INET_PROT_IMAP ?
							 PART_IMAP_ACHAR :
						 m_eScheme == INET_PROT_VIM ?
							 PART_VIM :
							 PART_USER_PASSWORD;
			bool bSupportsPassword = getSchemeInfo().m_bPassword;
			bool bSupportsAuth
				= !bSupportsPassword && getSchemeInfo().m_bAuth;
			bool bHasAuth = false;
			rtl::OUStringBuffer aSynUser;
			sal_Unicode const * p1 = pUserInfoBegin;
			while (p1 < pUserInfoEnd)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd, bOctets,
											 cEscapePrefix, eMechanism,
											 eCharset, eEscapeType);
				if (eEscapeType == ESCAPE_NO)
                {
					if (nUTF32 == ':' && bSupportsPassword)
					{
						bHasAuth = true;
						break;
					}
					else if (nUTF32 == ';' && bSupportsAuth
							 && pUserInfoEnd - p1
									> RTL_CONSTASCII_LENGTH("auth=")
							 && INetMIME::equalIgnoreCase(
									p1,
									p1 + RTL_CONSTASCII_LENGTH("auth="),
									"auth="))
					{
						p1 += RTL_CONSTASCII_LENGTH("auth=");
						bHasAuth = true;
						break;
					}
                }
				appendUCS4(aSynUser, nUTF32, eEscapeType, bOctets, ePart,
						   cEscapePrefix, eCharset, false);
			}
			m_aUser.set(aSynAbsURIRef, aSynUser.makeStringAndClear(), 
				aSynAbsURIRef.getLength());
			if (bHasAuth)
            {
				if (bSupportsPassword)
				{
					aSynAbsURIRef.append(sal_Unicode(':'));
					rtl::OUStringBuffer aSynAuth;
					while (p1 < pUserInfoEnd)
					{
						EscapeType eEscapeType;
						sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd, bOctets,
													 cEscapePrefix,
													 eMechanism, eCharset,
													 eEscapeType);
						appendUCS4(aSynAuth, nUTF32, eEscapeType, bOctets,
								   ePart, cEscapePrefix, eCharset, false);
					}
					m_aAuth.set(aSynAbsURIRef, aSynAuth.makeStringAndClear(), 
						aSynAbsURIRef.getLength());
				}
				else
				{
					aSynAbsURIRef.
						appendAscii(RTL_CONSTASCII_STRINGPARAM(";AUTH="));
					rtl::OUStringBuffer aSynAuth;
					while (p1 < pUserInfoEnd)
					{
						EscapeType eEscapeType;
						sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd, bOctets,
													 cEscapePrefix,
													 eMechanism, eCharset,
													 eEscapeType);
						if (!INetMIME::isIMAPAtomChar(nUTF32))
						{
							setInvalid();
							return false;
						}
						appendUCS4(aSynAuth, nUTF32, eEscapeType, bOctets,
								   ePart, cEscapePrefix, eCharset, false);
					}
					m_aAuth.set(aSynAbsURIRef, aSynAuth.makeStringAndClear(), 
						aSynAbsURIRef.getLength());
				}
            }
			if (pHostPortBegin)
				aSynAbsURIRef.append(sal_Unicode('@'));
		}

		if (pHostPortBegin)
		{
			sal_Unicode const * pPort = pHostPortEnd;
			if ( getSchemeInfo().m_bPort && pHostPortBegin < pHostPortEnd )
			{
				sal_Unicode const * p1 = pHostPortEnd - 1;
				while (p1 > pHostPortBegin && INetMIME::isDigit(*p1))
					--p1;
				if (*p1 == ':')
					pPort = p1;
			}
            bool bNetBiosName = false;
			switch (m_eScheme)
			{
				case INET_PROT_FILE:
					// If the host equals "LOCALHOST" (unencoded and ignoring
					// case), turn it into an empty host:
					if (INetMIME::equalIgnoreCase(pHostPortBegin, pPort,
												  "localhost"))
						pHostPortBegin = pPort;
                    bNetBiosName = true;
					break;

				case INET_PROT_LDAP:
                case INET_PROT_SMB:
					if (pHostPortBegin == pPort && pPort != pHostPortEnd)
					{
						setInvalid();
						return false;
					}
					break;
				default:
					if (pHostPortBegin == pPort)
					{
						setInvalid();
						return false;
					}
					break;
			}
			rtl::OUStringBuffer aSynHost;
			if (!parseHostOrNetBiosName(
                    pHostPortBegin, pPort, bOctets, eMechanism, eCharset,
                    bNetBiosName, &aSynHost))
			{
				setInvalid();
				return false;
			}
			m_aHost.set(aSynAbsURIRef, aSynHost.makeStringAndClear(), 
				aSynAbsURIRef.getLength());
			if (pPort != pHostPortEnd)
			{
				aSynAbsURIRef.append(sal_Unicode(':'));
				m_aPort.set(aSynAbsURIRef, 
					rtl::OUString(pPort + 1, pHostPortEnd - (pPort + 1)),
					aSynAbsURIRef.getLength());
			}
		}
	}

	// Parse <path>
	rtl::OUStringBuffer aSynPath;
	if (!parsePath(m_eScheme, &pPos, pEnd, bOctets, eMechanism, eCharset,
				   bSkippedInitialSlash, nSegmentDelimiter,
				   nAltSegmentDelimiter,
				   getSchemeInfo().m_bQuery ? '?' : 0x80000000,
				   nFragmentDelimiter, aSynPath))
	{
		setInvalid();
		return false;
	}
	m_aPath.set(aSynAbsURIRef, aSynPath.makeStringAndClear(), 
		aSynAbsURIRef.getLength());

	// Parse ?<query>
	if (getSchemeInfo().m_bQuery && pPos < pEnd && *pPos == '?')
	{
		aSynAbsURIRef.append(sal_Unicode('?'));
		rtl::OUStringBuffer aSynQuery;
		for (++pPos; pPos < pEnd && *pPos != nFragmentDelimiter;)
		{
			EscapeType eEscapeType;
			sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, cEscapePrefix,
										 eMechanism, eCharset, eEscapeType);
			appendUCS4(aSynQuery, nUTF32, eEscapeType, bOctets,
					   PART_URIC, cEscapePrefix, eCharset, true);
		}
		m_aQuery.set(aSynAbsURIRef, aSynQuery.makeStringAndClear(), 
			aSynAbsURIRef.getLength());
	}

	// Parse #<fragment>
	if (pPos < pEnd && *pPos == nFragmentDelimiter)
	{
		aSynAbsURIRef.append(sal_Unicode(nFragmentDelimiter));
		rtl::OUStringBuffer aSynFragment;
		for (++pPos; pPos < pEnd;)
		{
			EscapeType eEscapeType;
			sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, cEscapePrefix,
										 eMechanism, eCharset, eEscapeType);
			appendUCS4(aSynFragment, nUTF32, eEscapeType, bOctets, PART_URIC,
					   cEscapePrefix, eCharset, true);
		}
		m_aFragment.set(aSynAbsURIRef, aSynFragment.makeStringAndClear(), 
			aSynAbsURIRef.getLength());
	}

	if (pPos != pEnd)
	{
		setInvalid();
		return false;
	}

	m_aAbsURIRef = aSynAbsURIRef;

	return true;
}

//============================================================================
bool INetURLObject::convertRelToAbs(rtl::OUString const & rTheRelURIRef,
									bool bOctets,
									INetURLObject & rTheAbsURIRef,
									bool & rWasAbsolute,
									EncodeMechanism eMechanism,
									rtl_TextEncoding eCharset,
									bool bIgnoreFragment, bool bSmart,
									bool bRelativeNonURIs, FSysStyle eStyle)
	const
{
	sal_Unicode const * p = rTheRelURIRef.getStr();
	sal_Unicode const * pEnd = p + rTheRelURIRef.getLength();

	sal_Unicode const * pPrefixBegin = p;
	PrefixInfo const * pPrefix = getPrefix(pPrefixBegin, pEnd);
    bool hasScheme = pPrefix != 0;
    if (!hasScheme) {
        pPrefixBegin = p;
        hasScheme = parseScheme(&pPrefixBegin, pEnd, '#').getLength() > 0;
    }

	sal_uInt32 nSegmentDelimiter = '/';
	sal_uInt32 nQueryDelimiter
		= !bSmart || getSchemeInfo().m_bQuery ? '?' : 0x80000000;
	sal_uInt32 nFragmentDelimiter = '#';
	Part ePart = PART_VISIBLE;

	if (!hasScheme && bSmart)
	{
		// If the input matches any of the following productions (for which
		// the appropriate style bit is set in eStyle), it is assumed to be an
		// absolute file system path, rather than a relative URI reference.
		// (This is only a subset of the productions used for scheme detection
		// in INetURLObject::setAbsURIRef(), because most of those productions
		// interfere with the syntax of relative URI references.)  The
		// productions use the auxiliary rules
		//
		//	  domain = label *("." label)
		//	  label = alphanum [*(alphanum / "-") alphanum]
		//	  alphanum = ALPHA / DIGIT
		//	  UCS4 = <any UCS4 character>
		//
		// 1st Production (UNC file; FSYS_DOS only):
		//	  "\\" domain ["\" *UCS4]
		//
		// 2nd Production (Unix-like DOS file; FSYS_DOS only):
		//	  ALPHA ":" ["/" *UCS4]
		//
		// 3rd Production (DOS file; FSYS_DOS only):
		//	  ALPHA ":" ["\" *UCS4]
		if (eStyle & FSYS_DOS)
		{
			bool bFSys = false;
			sal_Unicode const * q = p;
			if (pEnd - q >= 2
				&& INetMIME::isAlpha(q[0])
				&& q[1] == ':'
                && (pEnd - q == 2 || q[2] == '/' || q[2] == '\\'))
				bFSys = true; // 2nd, 3rd
			else if (pEnd - q >= 2 && q[0] == '\\' && q[1] == '\\')
			{
				q += 2;
                sal_Int32 n = rtl_ustr_indexOfChar_WithLength(
                    q, pEnd - q, '\\');
                sal_Unicode const * qe = n == -1 ? pEnd : q + n;
                if (parseHostOrNetBiosName(
                        q, qe, bOctets, ENCODE_ALL, RTL_TEXTENCODING_DONTKNOW,
                        true, NULL))
                {
					bFSys = true; // 1st
                }
			}
			if (bFSys)
			{
				INetURLObject aNewURI;
				aNewURI.setAbsURIRef(rTheRelURIRef, bOctets, eMechanism,
									 eCharset, true, eStyle);
				if (!aNewURI.HasError())
				{
					rTheAbsURIRef = aNewURI;
					rWasAbsolute = true;
					return true;
				}
			}
		}

		// When the base URL is a file URL, accept relative file system paths
		// using "\" or ":" as delimiter (and ignoring URI conventions for "%"
		// and "#"), as well as relative URIs using "/" as delimiter:
		if (m_eScheme == INET_PROT_FILE)
			switch (guessFSysStyleByCounting(p, pEnd, eStyle))
			{
				case FSYS_UNX:
					nSegmentDelimiter = '/';
					break;

				case FSYS_DOS:
					nSegmentDelimiter = '\\';
					bRelativeNonURIs = true;
					break;

				case FSYS_MAC:
					nSegmentDelimiter = ':';
					bRelativeNonURIs = true;
					break;

				default:
					DBG_ERROR("INetURLObject::convertRelToAbs():"
							      " Bad guessFSysStyleByCounting");
					break;
			}

		if (bRelativeNonURIs)
		{
			eMechanism = ENCODE_ALL;
			nQueryDelimiter = 0x80000000;
			nFragmentDelimiter = 0x80000000;
			ePart = PART_VISIBLE_NONSPECIAL;
		}
	}

	// If the relative URI has the same scheme as the base URI, and that
	// scheme is hierarchical, then ignore its presence in the relative
	// URI in order to be backward compatible (cf. RFC 2396 section 5.2
	// step 3):
	if (pPrefix && pPrefix->m_eScheme == m_eScheme
		&& getSchemeInfo().m_bHierarchical)
	{
		hasScheme = false;
		while (p != pEnd && *p++ != ':') ;
	}
	rWasAbsolute = hasScheme;

	// Fast solution for non-relative URIs:
	if (hasScheme)
	{
		INetURLObject aNewURI(rTheRelURIRef, eMechanism, eCharset);
		if (aNewURI.HasError())
		{
			rWasAbsolute = false;
			return false;
		}

		if (bIgnoreFragment)
			aNewURI.clearFragment();
		rTheAbsURIRef = aNewURI;
		return true;
	}

	enum State { STATE_AUTH, STATE_ABS_PATH, STATE_REL_PATH, STATE_FRAGMENT,
				 STATE_DONE };

	rtl::OUStringBuffer aSynAbsURIRef;
	// make sure that the scheme is copied for generic schemes: getSchemeInfo().m_pScheme
	// is empty ("") in that case, so take the scheme from m_aAbsURIRef
	if (m_eScheme != INET_PROT_GENERIC)
	{
		aSynAbsURIRef.appendAscii(getSchemeInfo().m_pScheme);
	}
	else
	{
		sal_Unicode const * pSchemeBegin
			= m_aAbsURIRef.getStr();
		sal_Unicode const * pSchemeEnd = pSchemeBegin;
		while (pSchemeEnd[0] != ':')
		{
			++pSchemeEnd;
		}
		aSynAbsURIRef.append(pSchemeBegin, pSchemeEnd - pSchemeBegin);
	}
	aSynAbsURIRef.append(sal_Unicode(':'));

	sal_Char cEscapePrefix = getEscapePrefix();

	State eState = STATE_AUTH;
	bool bSameDoc = true;

	if (getSchemeInfo().m_bAuthority)
    {
		if (pEnd - p >= 2 && p[0] == '/' && p[1] == '/')
		{
			aSynAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
			p += 2;
			eState = STATE_ABS_PATH;
			bSameDoc = false;
			while (p != pEnd)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32
					= getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism,
							   eCharset, eEscapeType);
				if (eEscapeType == ESCAPE_NO)
                {
					if (nUTF32 == nSegmentDelimiter)
						break;
					else if (nUTF32 == nFragmentDelimiter)
					{
						eState = STATE_FRAGMENT;
						break;
					}
                }
				appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets,
						   PART_VISIBLE, cEscapePrefix, eCharset, true);
			}
		}
		else
		{
			SubString aAuthority(getAuthority());
			aSynAbsURIRef.append(m_aAbsURIRef.getStr()
								     + aAuthority.getBegin(),
								 aAuthority.getLength());
		}
    }

	if (eState == STATE_AUTH)
    {
		if (p == pEnd)
			eState = STATE_DONE;
		else if (*p == nFragmentDelimiter)
		{
			++p;
			eState = STATE_FRAGMENT;
		}
		else if (*p == nSegmentDelimiter)
		{
			++p;
			eState = STATE_ABS_PATH;
			bSameDoc = false;
		}
		else
		{
			eState = STATE_REL_PATH;
			bSameDoc = false;
		}
    }

	if (eState == STATE_ABS_PATH)
	{
		aSynAbsURIRef.append(sal_Unicode('/'));
		eState = STATE_DONE;
		while (p != pEnd)
		{
			EscapeType eEscapeType;
			sal_uInt32 nUTF32
				= getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism,
						   eCharset, eEscapeType);
			if (eEscapeType == ESCAPE_NO)
            {
				if (nUTF32 == nFragmentDelimiter)
				{
					eState = STATE_FRAGMENT;
					break;
				}
				else if (nUTF32 == nSegmentDelimiter)
					nUTF32 = '/';
            }
			appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets, ePart,
					   cEscapePrefix, eCharset, true);
		}
	}
	else if (eState == STATE_REL_PATH)
	{
		if (!getSchemeInfo().m_bHierarchical)
		{
            // Detect cases where a relative input could not be made absolute
            // because the given base URL is broken (most probably because it is
            // empty):
            OSL_ASSERT(!HasError());
			rWasAbsolute = false;
			return false;
		}

		sal_Unicode const * pBasePathBegin
			= m_aAbsURIRef.getStr() + m_aPath.getBegin();
		sal_Unicode const * pBasePathEnd
			= pBasePathBegin + m_aPath.getLength();
		while (pBasePathEnd != pBasePathBegin)
			if (*(--pBasePathEnd) == '/')
			{
				++pBasePathEnd;
				break;
			}

		sal_Int32 nPathBegin = aSynAbsURIRef.getLength();
		aSynAbsURIRef.append(pBasePathBegin, pBasePathEnd - pBasePathBegin);
		DBG_ASSERT(aSynAbsURIRef.getLength() > nPathBegin
				 && aSynAbsURIRef.charAt(aSynAbsURIRef.getLength() - 1) == '/',
				 "INetURLObject::convertRelToAbs(): Bad base path");

		while (p != pEnd && *p != nQueryDelimiter && *p != nFragmentDelimiter)
		{
			if (*p == '.')
            {
				if (pEnd - p == 1
					|| p[1] == nSegmentDelimiter
					|| p[1] == nQueryDelimiter
					|| p[1] == nFragmentDelimiter)
				{
					++p;
					if (p != pEnd && *p == nSegmentDelimiter)
						++p;
					continue;
				}
				else if (pEnd - p >= 2
						 && p[1] == '.'
						 && (pEnd - p == 2
							 || p[2] == nSegmentDelimiter
							 || p[2] == nQueryDelimiter
							 || p[2] == nFragmentDelimiter)
						 && aSynAbsURIRef.getLength() - nPathBegin > 1)
				{
					p += 2;
					if (p != pEnd && *p == nSegmentDelimiter)
						++p;

					sal_Int32 i = aSynAbsURIRef.getLength() - 2;
					while (i > nPathBegin && aSynAbsURIRef.charAt(i) != '/')
						--i;
					aSynAbsURIRef.setLength(i + 1);
					DBG_ASSERT(
						aSynAbsURIRef.getLength() > nPathBegin
						&& aSynAbsURIRef.charAt(aSynAbsURIRef.getLength() - 1)
						       == '/',
						"INetURLObject::convertRelToAbs(): Bad base path");
					continue;
				}
            }

			while (p != pEnd
				   && *p != nSegmentDelimiter
				   && *p != nQueryDelimiter
				   && *p != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32
					= getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism,
							   eCharset, eEscapeType);
				appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets, ePart,
						   cEscapePrefix, eCharset, true);
			}
			if (p != pEnd && *p == nSegmentDelimiter)
			{
				aSynAbsURIRef.append(sal_Unicode('/'));
				++p;
			}
		}

		while (p != pEnd && *p != nFragmentDelimiter)
		{
			EscapeType eEscapeType;
			sal_uInt32 nUTF32
				= getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism,
						   eCharset, eEscapeType);
			appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets, ePart,
					   cEscapePrefix, eCharset, true);
		}

		if (p == pEnd)
			eState = STATE_DONE;
		else
		{
			++p;
			eState = STATE_FRAGMENT;
		}
	}
	else if (bSameDoc)
	{
		aSynAbsURIRef.append(m_aAbsURIRef.getStr() + m_aPath.getBegin(),
							 m_aPath.getLength());
		if (m_aQuery.isPresent())
			aSynAbsURIRef.append(m_aAbsURIRef.getStr()
								     + m_aQuery.getBegin() - 1,
								 m_aQuery.getLength() + 1);
	}

	if (eState == STATE_FRAGMENT && !bIgnoreFragment)
	{
		aSynAbsURIRef.append(sal_Unicode('#'));
		while (p != pEnd)
		{
			EscapeType eEscapeType;
			sal_uInt32 nUTF32
				= getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism,
						   eCharset, eEscapeType);
			appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets,
					   PART_VISIBLE, cEscapePrefix, eCharset, true);
		}
	}

	INetURLObject aNewURI(aSynAbsURIRef.makeStringAndClear());
	if (aNewURI.HasError())
	{
        // Detect cases where a relative input could not be made absolute
        // because the given base URL is broken (most probably because it is
        // empty):
        OSL_ASSERT(!HasError());
		rWasAbsolute = false;
		return false;
	}

	rTheAbsURIRef = aNewURI;
	return true;
}

//============================================================================
bool INetURLObject::convertAbsToRel(rtl::OUString const & rTheAbsURIRef,
									bool bOctets, rtl::OUString & rTheRelURIRef,
									EncodeMechanism eEncodeMechanism,
									DecodeMechanism eDecodeMechanism,
									rtl_TextEncoding eCharset,
									FSysStyle eStyle) const
{
	// Check for hierarchical base URL:
	if (!getSchemeInfo().m_bHierarchical)
	{
		rTheRelURIRef
			= decode(rTheAbsURIRef,
					 getEscapePrefix(CompareProtocolScheme(rTheAbsURIRef)),
					 eDecodeMechanism, eCharset);
		return false;
	}

	// Convert the input (absolute or relative URI ref) to an absolute URI
    // ref:
	INetURLObject aSubject;
	bool bWasAbsolute;
	if (!convertRelToAbs(rTheAbsURIRef, bOctets, aSubject, bWasAbsolute,
						 eEncodeMechanism, eCharset, false, false, false,
						 eStyle))
	{
		rTheRelURIRef
			= decode(rTheAbsURIRef,
					 getEscapePrefix(CompareProtocolScheme(rTheAbsURIRef)),
					 eDecodeMechanism, eCharset);
		return false;
	}

	// Check for differing scheme or authority parts:
    if ((m_aScheme.compare(
             aSubject.m_aScheme, m_aAbsURIRef, aSubject.m_aAbsURIRef)
         != 0)
        || (m_aUser.compare(
                aSubject.m_aUser, m_aAbsURIRef, aSubject.m_aAbsURIRef)
            != 0)
        || (m_aAuth.compare(
                aSubject.m_aAuth, m_aAbsURIRef, aSubject.m_aAbsURIRef)
            != 0)
        || (m_aHost.compare(
                aSubject.m_aHost, m_aAbsURIRef, aSubject.m_aAbsURIRef)
            != 0)
        || (m_aPort.compare(
                aSubject.m_aPort, m_aAbsURIRef, aSubject.m_aAbsURIRef)
            != 0))
	{
		rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset);
		return false;
	}

	sal_Unicode const * pBasePathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pBasePathEnd = pBasePathBegin + m_aPath.getLength();
	sal_Unicode const * pSubjectPathBegin
		= aSubject.m_aAbsURIRef.getStr() + aSubject.m_aPath.getBegin();
	sal_Unicode const * pSubjectPathEnd
		= pSubjectPathBegin + aSubject.m_aPath.getLength();

	// Make nMatch point past the last matching slash, or past the end of the
	// paths, in case they are equal:
	sal_Unicode const * pSlash = 0;
	sal_Unicode const * p1 = pBasePathBegin;
	sal_Unicode const * p2 = pSubjectPathBegin;
	for (;;)
	{
		if (p1 == pBasePathEnd || p2 == pSubjectPathEnd)
		{
			if (p1 == pBasePathEnd && p2 == pSubjectPathEnd)
				pSlash = p1;
			break;
		}

		sal_Unicode c = *p1++;
		if (c != *p2++)
			break;
		if (c == '/')
			pSlash = p1;
	}
	if (!pSlash)
	{
		// One of the paths does not start with '/':
		rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset);
		return false;
	}
	sal_Int32 nMatch = pSlash - pBasePathBegin;

    // If the two URLs are DOS file URLs starting with different volumes
    // (e.g., file:///a:/... and file:///b:/...), the subject is not made
    // relative (it could be, but some people do not like that):
    if (m_eScheme == INET_PROT_FILE
        && nMatch <= 1
        && hasDosVolume(eStyle)
        && aSubject.hasDosVolume(eStyle)) //TODO! ok to use eStyle for these?
	{
		rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset);
		return false;
	}

	// For every slash in the base path after nMatch, a prefix of "../" is
	// added to the new relative URL (if the common prefix of the two paths is
	// only "/"---but see handling of file URLs above---, the complete subject
	// path could go into the new relative URL instead, but some people don't
	// like that):
	rtl::OUStringBuffer aSynRelURIRef;
//	if (nMatch <= 1) nMatch = 0; else // see comment above
	for (sal_Unicode const * p = pBasePathBegin + nMatch; p != pBasePathEnd;
		 ++p)
	{
		if (*p == '/')
			aSynRelURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("../"));
	}

	// If the new relative URL would start with "//" (i.e., it would be
	// mistaken for a relative URL starting with an authority part), or if the
	// new relative URL would neither be empty nor start with <"/"> nor start
	// with <1*rseg> (i.e., it could be mistaken for an absolute URL starting
	// with a scheme part), then the new relative URL is prefixed with "./":
	if (aSynRelURIRef.getLength() == 0)
    {
		if (pSubjectPathEnd - pSubjectPathBegin >= nMatch + 2
			&& pSubjectPathBegin[nMatch] == '/'
			&& pSubjectPathBegin[nMatch + 1] == '/')
        {
			aSynRelURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("./"));
        }
		else
        {
			for (sal_Unicode const * p = pSubjectPathBegin + nMatch;
				 p != pSubjectPathEnd && *p != '/'; ++p)
            {
				if (mustEncode(*p, PART_REL_SEGMENT_EXTRA))
				{
					aSynRelURIRef.
						appendAscii(RTL_CONSTASCII_STRINGPARAM("./"));
					break;
				}
            }
        }
    }

	// The remainder of the subject path, starting at nMatch, is appended to
	// the new relative URL:
	sal_Char cEscapePrefix = getEscapePrefix();
	aSynRelURIRef.append(decode(pSubjectPathBegin + nMatch, pSubjectPathEnd,
							cEscapePrefix, eDecodeMechanism, eCharset));

	// If the subject has defined query or fragment parts, they are appended
	// to the new relative URL:
	if (aSubject.m_aQuery.isPresent())
	{
		aSynRelURIRef.append(sal_Unicode('?'));
		aSynRelURIRef.append(aSubject.decode(aSubject.m_aQuery, cEscapePrefix,
										 eDecodeMechanism, eCharset));
	}
	if (aSubject.m_aFragment.isPresent())
	{
		aSynRelURIRef.append(sal_Unicode('#'));
		aSynRelURIRef.append(aSubject.decode(aSubject.m_aFragment, 
			cEscapePrefix, eDecodeMechanism, eCharset));
	}

	rTheRelURIRef = aSynRelURIRef.makeStringAndClear();
	return true;
}

//============================================================================
// static
bool INetURLObject::convertIntToExt(rtl::OUString const & rTheIntURIRef,
									bool bOctets, rtl::OUString & rTheExtURIRef,
									DecodeMechanism eDecodeMechanism,
									rtl_TextEncoding eCharset)
{
	sal_Char cEscapePrefix
		= getEscapePrefix(CompareProtocolScheme(rTheIntURIRef));
	rtl::OUString aSynExtURIRef(encodeText(rTheIntURIRef, bOctets, PART_VISIBLE,
									   cEscapePrefix, NOT_CANONIC, eCharset,
									   true));
	sal_Unicode const * pBegin = aSynExtURIRef.getStr();
	sal_Unicode const * pEnd = pBegin + aSynExtURIRef.getLength();
	sal_Unicode const * p = pBegin;
	PrefixInfo const * pPrefix = getPrefix(p, pEnd);
	bool bConvert = pPrefix && pPrefix->m_eKind == PrefixInfo::INTERNAL;
	if (bConvert)
	{
		aSynExtURIRef = 
			aSynExtURIRef.replaceAt(0, p - pBegin,
				rtl::OUString::createFromAscii(pPrefix->m_pTranslatedPrefix));
	}
	rTheExtURIRef = decode(aSynExtURIRef, cEscapePrefix, eDecodeMechanism,
						   eCharset);
	return bConvert;
}

//============================================================================
// static
bool INetURLObject::convertExtToInt(rtl::OUString const & rTheExtURIRef,
									bool bOctets, rtl::OUString & rTheIntURIRef,
									DecodeMechanism eDecodeMechanism,
									rtl_TextEncoding eCharset)
{
	sal_Char cEscapePrefix
		= getEscapePrefix(CompareProtocolScheme(rTheExtURIRef));
	rtl::OUString aSynIntURIRef(encodeText(rTheExtURIRef, bOctets, PART_VISIBLE,
									   cEscapePrefix, NOT_CANONIC, eCharset,
									   true));
	sal_Unicode const * pBegin = aSynIntURIRef.getStr();
	sal_Unicode const * pEnd = pBegin + aSynIntURIRef.getLength();
	sal_Unicode const * p = pBegin;
	PrefixInfo const * pPrefix = getPrefix(p, pEnd);
	bool bConvert = pPrefix && pPrefix->m_eKind == PrefixInfo::EXTERNAL;
	if (bConvert)
	{
		aSynIntURIRef = 
			aSynIntURIRef.replaceAt(0, p - pBegin,
				rtl::OUString::createFromAscii(pPrefix->m_pTranslatedPrefix));
	}
	rTheIntURIRef = decode(aSynIntURIRef, cEscapePrefix, eDecodeMechanism,
						   eCharset);
	return bConvert;
}

//============================================================================
// static
INetURLObject::PrefixInfo const *
INetURLObject::getPrefix(sal_Unicode const *& rBegin,
						 sal_Unicode const * pEnd)
{
	static PrefixInfo const aMap[]
		= { // dummy entry at front needed, because pLast may point here:
			{ 0, 0, INET_PROT_NOT_VALID, PrefixInfo::INTERNAL },
			{ ".component:", "staroffice.component:", INET_PROT_COMPONENT,
			  PrefixInfo::INTERNAL },
			{ ".uno:", "staroffice.uno:", INET_PROT_UNO,
			  PrefixInfo::INTERNAL },
			{ "cid:", 0, INET_PROT_CID, PrefixInfo::OFFICIAL },
			{ "data:", 0, INET_PROT_DATA, PrefixInfo::OFFICIAL },
			{ "db:", "staroffice.db:", INET_PROT_DB, PrefixInfo::INTERNAL },
			{ "file:", 0, INET_PROT_FILE, PrefixInfo::OFFICIAL },
			{ "ftp:", 0, INET_PROT_FTP, PrefixInfo::OFFICIAL },
			{ "hid:", "staroffice.hid:", INET_PROT_HID,
			  PrefixInfo::INTERNAL },
			{ "http:", 0, INET_PROT_HTTP, PrefixInfo::OFFICIAL },
			{ "https:", 0, INET_PROT_HTTPS, PrefixInfo::OFFICIAL },
			{ "imap:", 0, INET_PROT_IMAP, PrefixInfo::OFFICIAL },
			{ "javascript:", 0, INET_PROT_JAVASCRIPT, PrefixInfo::OFFICIAL },
			{ "ldap:", 0, INET_PROT_LDAP, PrefixInfo::OFFICIAL },
			{ "macro:", "staroffice.macro:", INET_PROT_MACRO,
			  PrefixInfo::INTERNAL },
			{ "mailto:", 0, INET_PROT_MAILTO, PrefixInfo::OFFICIAL },
			{ "news:", 0, INET_PROT_NEWS, PrefixInfo::OFFICIAL },
			{ "out:", "staroffice.out:", INET_PROT_OUT,
			  PrefixInfo::INTERNAL },
			{ "pop3:", "staroffice.pop3:", INET_PROT_POP3,
			  PrefixInfo::INTERNAL },
			{ "private:", "staroffice.private:", INET_PROT_PRIV_SOFFICE,
			  PrefixInfo::INTERNAL },
			{ "private:factory/", "staroffice.factory:",
			  INET_PROT_PRIV_SOFFICE, PrefixInfo::INTERNAL },
			{ "private:helpid/", "staroffice.helpid:", INET_PROT_PRIV_SOFFICE,
			  PrefixInfo::INTERNAL },
			{ "private:java/", "staroffice.java:", INET_PROT_PRIV_SOFFICE,
			  PrefixInfo::INTERNAL },
			{ "private:searchfolder:", "staroffice.searchfolder:",
			  INET_PROT_PRIV_SOFFICE, PrefixInfo::INTERNAL },
			{ "private:trashcan:", "staroffice.trashcan:",
			  INET_PROT_PRIV_SOFFICE, PrefixInfo::INTERNAL },
			{ "slot:", "staroffice.slot:", INET_PROT_SLOT,
			  PrefixInfo::INTERNAL },
            { "smb:", 0, INET_PROT_SMB, PrefixInfo::OFFICIAL },
			{ "staroffice.component:", ".component:", INET_PROT_COMPONENT,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.db:", "db:", INET_PROT_DB, PrefixInfo::EXTERNAL },
			{ "staroffice.factory:", "private:factory/",
			  INET_PROT_PRIV_SOFFICE, PrefixInfo::EXTERNAL },
			{ "staroffice.helpid:", "private:helpid/", INET_PROT_PRIV_SOFFICE,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.hid:", "hid:", INET_PROT_HID,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.java:", "private:java/", INET_PROT_PRIV_SOFFICE,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.macro:", "macro:", INET_PROT_MACRO,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.out:", "out:", INET_PROT_OUT,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.pop3:", "pop3:", INET_PROT_POP3,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.private:", "private:", INET_PROT_PRIV_SOFFICE,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.searchfolder:", "private:searchfolder:",
			  INET_PROT_PRIV_SOFFICE, PrefixInfo::EXTERNAL },
			{ "staroffice.slot:", "slot:", INET_PROT_SLOT,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.trashcan:", "private:trashcan:",
			  INET_PROT_PRIV_SOFFICE, PrefixInfo::EXTERNAL },
			{ "staroffice.uno:", ".uno:", INET_PROT_UNO,
			  PrefixInfo::EXTERNAL },
			{ "staroffice.vim:", "vim:", INET_PROT_VIM,
			  PrefixInfo::EXTERNAL },
			{ "staroffice:", "private:", INET_PROT_PRIV_SOFFICE,
			  PrefixInfo::EXTERNAL },
            { "telnet:", 0, INET_PROT_TELNET, PrefixInfo::OFFICIAL },
			{ "vim:", "staroffice.vim:", INET_PROT_VIM,
			  PrefixInfo::INTERNAL },
			{ "vnd.sun.star.cmd:", 0, INET_PROT_VND_SUN_STAR_CMD,
			  PrefixInfo::OFFICIAL },
			{ "vnd.sun.star.expand:", 0, INET_PROT_VND_SUN_STAR_EXPAND,
			  PrefixInfo::OFFICIAL },
			{ "vnd.sun.star.help:", 0, INET_PROT_VND_SUN_STAR_HELP,
			  PrefixInfo::OFFICIAL },
			{ "vnd.sun.star.hier:", 0, INET_PROT_VND_SUN_STAR_HIER,
			  PrefixInfo::OFFICIAL },
			{ "vnd.sun.star.odma:", 0, INET_PROT_VND_SUN_STAR_ODMA,
			  PrefixInfo::OFFICIAL },
			{ "vnd.sun.star.pkg:", 0, INET_PROT_VND_SUN_STAR_PKG,
			  PrefixInfo::OFFICIAL },
            { "vnd.sun.star.tdoc:", 0, INET_PROT_VND_SUN_STAR_TDOC,
              PrefixInfo::OFFICIAL },
			{ "vnd.sun.star.webdav:", 0, INET_PROT_VND_SUN_STAR_WEBDAV,
			  PrefixInfo::OFFICIAL } };
	PrefixInfo const * pFirst = aMap + 1;
	PrefixInfo const * pLast = aMap + sizeof aMap / sizeof (PrefixInfo) - 1;
	PrefixInfo const * pMatch = 0;
	sal_Unicode const * pMatched = rBegin;
	sal_Unicode const * p = rBegin;
	sal_Int32 i = 0;
	for (; pFirst < pLast; ++i)
	{
		if (pFirst->m_pPrefix[i] == '\0')
		{
			pMatch = pFirst++;
			pMatched = p;
		}
		if (p >= pEnd)
			break;
		sal_uInt32 nChar = INetMIME::toLowerCase(*p++);
		while (pFirst <= pLast && sal_uChar(pFirst->m_pPrefix[i]) < nChar)
			++pFirst;
		while (pFirst <= pLast && sal_uChar(pLast->m_pPrefix[i]) > nChar)
			--pLast;
	}
	if (pFirst == pLast)
	{
		sal_Char const * q = pFirst->m_pPrefix + i;
		while (p < pEnd && *q != '\0'
			   && INetMIME::toLowerCase(*p) == sal_uChar(*q))
		{
			++p;
			++q;
		}
		if (*q == '\0')
		{
			rBegin = p;
			return pFirst;
		}
	}
	rBegin = pMatched;
	return pMatch;
}

//============================================================================
sal_Int32 INetURLObject::getAuthorityBegin() const
{
	DBG_ASSERT(getSchemeInfo().m_bAuthority,
			   "INetURLObject::getAuthority(): Bad scheme");
	sal_Int32 nBegin;
	if (m_aUser.isPresent())
		nBegin = m_aUser.getBegin();
	else if (m_aHost.isPresent())
		nBegin = m_aHost.getBegin();
	else
		nBegin = m_aPath.getBegin();
	nBegin -= RTL_CONSTASCII_LENGTH("//");
	DBG_ASSERT(m_aAbsURIRef.charAt(nBegin) == '/'
			   && m_aAbsURIRef.charAt(nBegin + 1) == '/',
			   "INetURLObject::getAuthority(): Bad authority");
    return nBegin;
}

//============================================================================
INetURLObject::SubString INetURLObject::getAuthority() const
{
    sal_Int32 nBegin = getAuthorityBegin();
	sal_Int32 nEnd = m_aPort.isPresent() ? m_aPort.getEnd() :
					  m_aHost.isPresent() ? m_aHost.getEnd() :
					  m_aAuth.isPresent() ? m_aAuth.getEnd() :
		              m_aUser.isPresent() ? m_aUser.getEnd() :
		                  nBegin + RTL_CONSTASCII_LENGTH("//");
	return SubString(nBegin, nEnd - nBegin);
}

//============================================================================
bool INetURLObject::setUser(rtl::OUString const & rTheUser,
							bool bOctets, EncodeMechanism eMechanism,
							rtl_TextEncoding eCharset)
{
	if (
         !getSchemeInfo().m_bUser || 
         (m_eScheme == INET_PROT_IMAP && rTheUser.getLength() == 0)
       )
    {
		return false;
    }

	rtl::OUString aNewUser(encodeText(rTheUser, bOctets,
								  m_eScheme == INET_PROT_IMAP ?
									  PART_IMAP_ACHAR :
								  m_eScheme == INET_PROT_VIM ?
									  PART_VIM :
									  PART_USER_PASSWORD,
								  getEscapePrefix(), eMechanism, eCharset,
								  false));
	sal_Int32 nDelta;
	if (m_aUser.isPresent())
		nDelta = m_aUser.set(m_aAbsURIRef, aNewUser);
	else if (m_aHost.isPresent())
	{
		m_aAbsURIRef.insert(m_aHost.getBegin(), sal_Unicode('@'));
		nDelta = m_aUser.set(m_aAbsURIRef, aNewUser, m_aHost.getBegin()) + 1;
	}
	else if (getSchemeInfo().m_bHost)
		return false;
	else
		nDelta = m_aUser.set(m_aAbsURIRef, aNewUser, m_aPath.getBegin());
	m_aAuth += nDelta;
	m_aHost += nDelta;
	m_aPort += nDelta;
	m_aPath += nDelta;
	m_aQuery += nDelta;
	m_aFragment += nDelta;
	return true;
}

namespace
{
	void lcl_Erase(rtl::OUStringBuffer &rBuf, sal_Int32 index, sal_Int32 count)
	{
		rtl::OUString sTemp(rBuf.makeStringAndClear());
		rBuf.append(sTemp.replaceAt(index, count, rtl::OUString()));
	}
}

//============================================================================
bool INetURLObject::clearPassword()
{
	if (!getSchemeInfo().m_bPassword)
		return false;
	if (m_aAuth.isPresent())
	{
		lcl_Erase(m_aAbsURIRef, m_aAuth.getBegin() - 1, 
			m_aAuth.getLength() + 1);
		sal_Int32 nDelta = m_aAuth.clear() - 1;
		m_aHost += nDelta;
		m_aPort += nDelta;
		m_aPath += nDelta;
		m_aQuery += nDelta;
		m_aFragment += nDelta;
	}
	return true;
}

//============================================================================
bool INetURLObject::setPassword(rtl::OUString const & rThePassword, 
								bool bOctets, EncodeMechanism eMechanism,
								rtl_TextEncoding eCharset)
{
	if (!getSchemeInfo().m_bPassword)
		return false;
	rtl::OUString aNewAuth(encodeText(rThePassword, bOctets,
								  m_eScheme == INET_PROT_VIM ?
									  PART_VIM : PART_USER_PASSWORD,
								  getEscapePrefix(), eMechanism, eCharset,
								  false));
	sal_Int32 nDelta;
	if (m_aAuth.isPresent())
		nDelta = m_aAuth.set(m_aAbsURIRef, aNewAuth);
	else if (m_aUser.isPresent())
	{
		m_aAbsURIRef.insert(m_aUser.getEnd(), sal_Unicode(':'));
		nDelta
			= m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aUser.getEnd() + 1) + 1;
	}
	else if (m_aHost.isPresent())
	{
		m_aAbsURIRef.insert(m_aHost.getBegin(),
			rtl::OUString::createFromAscii(":@"));
		m_aUser.set(m_aAbsURIRef, rtl::OUString(), m_aHost.getBegin());
		nDelta
			= m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aHost.getBegin() + 1) + 2;
	}
	else if (getSchemeInfo().m_bHost)
		return false;
	else
	{
		m_aAbsURIRef.insert(m_aPath.getBegin(), sal_Unicode(':'));
		m_aUser.set(m_aAbsURIRef, rtl::OUString(), m_aPath.getBegin());
		nDelta
			= m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aPath.getBegin() + 1) + 1;
	}
	m_aHost += nDelta;
	m_aPort += nDelta;
	m_aPath += nDelta;
	m_aQuery += nDelta;
	m_aFragment += nDelta;
	return true;
}

//============================================================================
// static
bool INetURLObject::parseHost(
    sal_Unicode const *& rBegin, sal_Unicode const * pEnd,
    rtl::OUString & rCanonic)
{
    // RFC 2373 is inconsistent about how to write an IPv6 address in which an
    // IPv4 address directly follows the abbreviating "::".  The ABNF in
    // Appendix B suggests ":::13.1.68.3", while an example in 2.2/3 explicitly
    // mentions "::13:1.68.3".  This algorithm accepts both variants:
	enum State { STATE_INITIAL, STATE_LABEL, STATE_LABEL_HYPHEN,
				 STATE_LABEL_DOT, STATE_TOPLABEL, STATE_TOPLABEL_HYPHEN,
				 STATE_TOPLABEL_DOT, STATE_IP4, STATE_IP4_DOT, STATE_IP6,
				 STATE_IP6_COLON, STATE_IP6_2COLON, STATE_IP6_3COLON,
				 STATE_IP6_HEXSEQ1, STATE_IP6_HEXSEQ1_COLON,
				 STATE_IP6_HEXSEQ1_MAYBE_IP4, STATE_IP6_HEXSEQ2,
				 STATE_IP6_HEXSEQ2_COLON, STATE_IP6_HEXSEQ2_MAYBE_IP4,
				 STATE_IP6_IP4, STATE_IP6_IP4_DOT, STATE_IP6_DONE };
	rtl::OUStringBuffer aTheCanonic;
	sal_uInt32 nNumber = 0;
	int nDigits = 0;
	int nOctets = 0;
	State eState = STATE_INITIAL;
	sal_Unicode const * p = rBegin;
	for (; p != pEnd; ++p)
		switch (eState)
		{
			case STATE_INITIAL:
				if (*p == '[')
				{
					aTheCanonic.append(sal_Unicode('['));
					eState = STATE_IP6;
				}
				else if (INetMIME::isAlpha(*p))
					eState = STATE_TOPLABEL;
				else if (INetMIME::isDigit(*p))
				{
					nNumber = INetMIME::getWeight(*p);
					nDigits = 1;
					nOctets = 1;
					eState = STATE_IP4;
				}
				else
					goto done;
				break;

			case STATE_LABEL:
				if (*p == '.')
					eState = STATE_LABEL_DOT;
				else if (*p == '-')
					eState = STATE_LABEL_HYPHEN;
				else if (!INetMIME::isAlphanumeric(*p))
					goto done;
				break;

			case STATE_LABEL_HYPHEN:
				if (INetMIME::isAlphanumeric(*p))
					eState = STATE_LABEL;
				else if (*p != '-')
					goto done;
				break;

			case STATE_LABEL_DOT:
				if (INetMIME::isAlpha(*p))
					eState = STATE_TOPLABEL;
				else if (INetMIME::isDigit(*p))
					eState = STATE_LABEL;
				else
					goto done;
				break;

			case STATE_TOPLABEL:
				if (*p == '.')
					eState = STATE_TOPLABEL_DOT;
				else if (*p == '-')
					eState = STATE_TOPLABEL_HYPHEN;
				else if (!INetMIME::isAlphanumeric(*p))
					goto done;
				break;

			case STATE_TOPLABEL_HYPHEN:
				if (INetMIME::isAlphanumeric(*p))
					eState = STATE_TOPLABEL;
				else if (*p != '-')
					goto done;
				break;

			case STATE_TOPLABEL_DOT:
				if (INetMIME::isAlpha(*p))
					eState = STATE_TOPLABEL;
				else if (INetMIME::isDigit(*p))
					eState = STATE_LABEL;
				else
					goto done;
				break;

			case STATE_IP4:
				if (*p == '.')
					if (nOctets < 4)
					{
						aTheCanonic.append(
							rtl::OUString::valueOf(sal_Int32(nNumber)));
						aTheCanonic.append(sal_Unicode('.'));
						++nOctets;
						eState = STATE_IP4_DOT;
					}
					else
						eState = STATE_LABEL_DOT;
				else if (*p == '-')
					eState = STATE_LABEL_HYPHEN;
				else if (INetMIME::isAlpha(*p))
					eState = STATE_LABEL;
				else if (INetMIME::isDigit(*p))
					if (nDigits < 3)
					{
						nNumber = 10 * nNumber + INetMIME::getWeight(*p);
						++nDigits;
					}
					else
						eState = STATE_LABEL;
				else
					goto done;
				break;

			case STATE_IP4_DOT:
				if (INetMIME::isAlpha(*p))
					eState = STATE_TOPLABEL;
				else if (INetMIME::isDigit(*p))
				{
					nNumber = INetMIME::getWeight(*p);
					nDigits = 1;
					eState = STATE_IP4;
				}
				else
					goto done;
				break;

			case STATE_IP6:
				if (*p == ':')
					eState = STATE_IP6_COLON;
				else if (INetMIME::isHexDigit(*p))
				{
					nNumber = INetMIME::getHexWeight(*p);
					nDigits = 1;
					eState = STATE_IP6_HEXSEQ1;
				}
				else
					goto done;
				break;

			case STATE_IP6_COLON:
				if (*p == ':')
				{
					aTheCanonic.appendAscii(RTL_CONSTASCII_STRINGPARAM("::"));
					eState = STATE_IP6_2COLON;
				}
				else
					goto done;
				break;

			case STATE_IP6_2COLON:
				if (*p == ']')
					eState = STATE_IP6_DONE;
				else if (*p == ':')
				{
					aTheCanonic.append(sal_Unicode(':'));
					eState = STATE_IP6_3COLON;
				}
                else if (INetMIME::isDigit(*p))
                {
                    nNumber = INetMIME::getWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_HEXSEQ2_MAYBE_IP4;
                }
				else if (INetMIME::isHexDigit(*p))
				{
					nNumber = INetMIME::getHexWeight(*p);
					nDigits = 1;
					eState = STATE_IP6_HEXSEQ2;
				}
				else
					goto done;
				break;

			case STATE_IP6_3COLON:
				if (INetMIME::isDigit(*p))
				{
					nNumber = INetMIME::getWeight(*p);
					nDigits = 1;
					nOctets = 1;
					eState = STATE_IP6_IP4;
				}
				else
					goto done;
				break;

			case STATE_IP6_HEXSEQ1:
				if (*p == ']')
				{
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber), 16));
					eState = STATE_IP6_DONE;
				}
				else if (*p == ':')
				{
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber), 16));
					aTheCanonic.append(sal_Unicode(':'));
					eState = STATE_IP6_HEXSEQ1_COLON;
				}
				else if (INetMIME::isHexDigit(*p) && nDigits < 4)
				{
					nNumber = 16 * nNumber + INetMIME::getHexWeight(*p);
					++nDigits;
				}
				else
					goto done;
				break;

			case STATE_IP6_HEXSEQ1_COLON:
				if (*p == ':')
				{
					aTheCanonic.append(sal_Unicode(':'));
					eState = STATE_IP6_2COLON;
				}
				else if (INetMIME::isDigit(*p))
				{
					nNumber = INetMIME::getWeight(*p);
					nDigits = 1;
					eState = STATE_IP6_HEXSEQ1_MAYBE_IP4;
				}
				else if (INetMIME::isHexDigit(*p))
				{
					nNumber = INetMIME::getHexWeight(*p);
					nDigits = 1;
					eState = STATE_IP6_HEXSEQ1;
				}
				else
					goto done;
				break;

			case STATE_IP6_HEXSEQ1_MAYBE_IP4:
				if (*p == ']')
				{
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber), 16));
					eState = STATE_IP6_DONE;
				}
				else if (*p == ':')
				{
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber), 16));
					aTheCanonic.append(sal_Unicode(':'));
					eState = STATE_IP6_HEXSEQ1_COLON;
				}
				else if (*p == '.')
				{
					nNumber = 100 * (nNumber >> 8) + 10 * (nNumber >> 4 & 15)
								  + (nNumber & 15);
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber)));
					aTheCanonic.append(sal_Unicode('.'));
					nOctets = 2;
					eState = STATE_IP6_IP4_DOT;
				}
				else if (INetMIME::isDigit(*p) && nDigits < 3)
				{
					nNumber = 16 * nNumber + INetMIME::getWeight(*p);
					++nDigits;
				}
				else if (INetMIME::isHexDigit(*p) && nDigits < 4)
				{
					nNumber = 16 * nNumber + INetMIME::getHexWeight(*p);
					++nDigits;
					eState = STATE_IP6_HEXSEQ1;
				}
				else
					goto done;
				break;

			case STATE_IP6_HEXSEQ2:
				if (*p == ']')
				{
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber), 16));
					eState = STATE_IP6_DONE;
				}
				else if (*p == ':')
				{
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber), 16));
					aTheCanonic.append(sal_Unicode(':'));
					eState = STATE_IP6_HEXSEQ2_COLON;
				}
				else if (INetMIME::isHexDigit(*p) && nDigits < 4)
				{
					nNumber = 16 * nNumber + INetMIME::getHexWeight(*p);
					++nDigits;
				}
				else
					goto done;
				break;

			case STATE_IP6_HEXSEQ2_COLON:
				if (INetMIME::isDigit(*p))
				{
					nNumber = INetMIME::getWeight(*p);
					nDigits = 1;
					eState = STATE_IP6_HEXSEQ2_MAYBE_IP4;
				}
				else if (INetMIME::isHexDigit(*p))
				{
					nNumber = INetMIME::getHexWeight(*p);
					nDigits = 1;
					eState = STATE_IP6_HEXSEQ2;
				}
				else
					goto done;
				break;

			case STATE_IP6_HEXSEQ2_MAYBE_IP4:
				if (*p == ']')
				{
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber), 16));
					eState = STATE_IP6_DONE;
				}
				else if (*p == ':')
				{
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber), 16));
					aTheCanonic.append(sal_Unicode(':'));
					eState = STATE_IP6_HEXSEQ2_COLON;
				}
				else if (*p == '.')
				{
					nNumber = 100 * (nNumber >> 8) + 10 * (nNumber >> 4 & 15)
								  + (nNumber & 15);
					aTheCanonic.append(
						rtl::OUString::valueOf(sal_Int32(nNumber)));
					aTheCanonic.append(sal_Unicode('.'));
					nOctets = 2;
					eState = STATE_IP6_IP4_DOT;
				}
				else if (INetMIME::isDigit(*p) && nDigits < 3)
				{
					nNumber = 16 * nNumber + INetMIME::getWeight(*p);
					++nDigits;
				}
				else if (INetMIME::isHexDigit(*p) && nDigits < 4)
				{
					nNumber = 16 * nNumber + INetMIME::getHexWeight(*p);
					++nDigits;
					eState = STATE_IP6_HEXSEQ2;
				}
				else
					goto done;
				break;

			case STATE_IP6_IP4:
				if (*p == ']')
					if (nOctets == 4)
					{
						aTheCanonic.append(
							rtl::OUString::valueOf(sal_Int32(nNumber)));
						eState = STATE_IP6_DONE;
					}
					else
						goto done;
				else if (*p == '.')
					if (nOctets < 4)
					{
						aTheCanonic.append(
							rtl::OUString::valueOf(sal_Int32(nNumber)));
						aTheCanonic.append(sal_Unicode('.'));
						++nOctets;
						eState = STATE_IP6_IP4_DOT;
					}
					else
						goto done;
				else if (INetMIME::isDigit(*p) && nDigits < 3)
				{
					nNumber = 10 * nNumber + INetMIME::getWeight(*p);
					++nDigits;
				}
				else
					goto done;
				break;

			case STATE_IP6_IP4_DOT:
				if (INetMIME::isDigit(*p))
				{
					nNumber = INetMIME::getWeight(*p);
					nDigits = 1;
					eState = STATE_IP6_IP4;
				}
				else
					goto done;
				break;

            case STATE_IP6_DONE:
                goto done;
		}
 done:
	switch (eState)
	{
		case STATE_LABEL:
		case STATE_TOPLABEL:
		case STATE_TOPLABEL_DOT:
			aTheCanonic.setLength(0);
			aTheCanonic.append(rBegin, p - rBegin);
			rBegin = p;
			rCanonic = aTheCanonic.makeStringAndClear();
			return true;

		case STATE_IP4:
			if (nOctets == 4)
			{
				aTheCanonic.append(
					rtl::OUString::valueOf(sal_Int32(nNumber)));
				rBegin = p;
				rCanonic = aTheCanonic.makeStringAndClear();
				return true;
			}
			return false;

		case STATE_IP6_DONE:
			aTheCanonic.append(sal_Unicode(']'));
			rBegin = p;
			rCanonic = aTheCanonic.makeStringAndClear();
			return true;

        default:
            return false;
	}
}

//============================================================================
// static
bool INetURLObject::parseHostOrNetBiosName(
    sal_Unicode const * pBegin, sal_Unicode const * pEnd, bool bOctets,
    EncodeMechanism eMechanism, rtl_TextEncoding eCharset, bool bNetBiosName,
    rtl::OUStringBuffer* pCanonic)
{
	rtl::OUString aTheCanonic;
    if (pBegin < pEnd)
    {
        sal_Unicode const * p = pBegin;
        if (!parseHost(p, pEnd, aTheCanonic) || p != pEnd)
        {
            if (bNetBiosName)
            {
                rtl::OUStringBuffer buf;
                while (pBegin < pEnd)
                {
                    EscapeType eEscapeType;
                    sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd, bOctets, '%',
                                                 eMechanism, eCharset,
                                                 eEscapeType);
                    if (!INetMIME::isVisible(nUTF32))
                        return false;
                    if (!INetMIME::isAlphanumeric(nUTF32))
                        switch (nUTF32)
                        {
                        case '"':
                        case '*':
                        case '+':
                        case ',':
                        case '/':
                        case ':':
                        case ';':
                        case '<':
                        case '=':
                        case '>':
                        case '?':
                        case '[':
                        case '\\':
                        case ']':
                        case '`':
                        case '|':
                            return false;;
                        }
                    if (pCanonic != NULL) {
                        appendUCS4(
                            buf, nUTF32, eEscapeType, bOctets, PART_URIC, '%',
                            eCharset, true);
                    }
                }
                aTheCanonic = buf.makeStringAndClear();
            }
            else
                return false;
        }
    }
    if (pCanonic != NULL) {
        *pCanonic = aTheCanonic;
    }
    return true;
}

//============================================================================
// static
rtl::OUString INetURLObject::encodeHostPort(rtl::OUString const & rTheHostPort,
										bool bOctets,
										EncodeMechanism eMechanism,
										rtl_TextEncoding eCharset)
{
	sal_Int32 nPort = rTheHostPort.getLength();
	if (nPort != 0)
	{
		sal_Int32 i = nPort - 1;
		while (i != 0 && INetMIME::isDigit(rTheHostPort.getStr()[i]))
			--i;
		if (rTheHostPort.getStr()[i] == ':')
			nPort = i;
	}
	rtl::OUString aResult(encodeText(rTheHostPort.copy(0, nPort), bOctets,
								 PART_HOST_EXTRA, '%', eMechanism, eCharset,
								 true));
	aResult += rTheHostPort.copy(nPort);
	return aResult;
}

//============================================================================
bool INetURLObject::setHost(rtl::OUString const & rTheHost, bool bOctets,
							EncodeMechanism eMechanism,
							rtl_TextEncoding eCharset)
{
	if (!getSchemeInfo().m_bHost)
		return false;
	rtl::OUStringBuffer aSynHost(rTheHost);
    bool bNetBiosName = false;
	switch (m_eScheme)
	{
		case INET_PROT_FILE:
			{
				rtl::OUString sTemp(aSynHost);
				if (sTemp.equalsIgnoreAsciiCaseAsciiL(
					RTL_CONSTASCII_STRINGPARAM("localhost")))
				{
					aSynHost.setLength(0);
				}
            	bNetBiosName = true;
			}
			break;
		case INET_PROT_LDAP:
			if (aSynHost.getLength() == 0 && m_aPort.isPresent())
				return false;
			break;

		default:
			if (aSynHost.getLength() == 0)
				return false;
			break;
	}
    if (!parseHostOrNetBiosName(
            aSynHost.getStr(), aSynHost.getStr() + aSynHost.getLength(),
            bOctets, eMechanism, eCharset, bNetBiosName, &aSynHost))
        return false;
	sal_Int32 nDelta = m_aHost.set(m_aAbsURIRef, aSynHost.makeStringAndClear());
	m_aPort += nDelta;
	m_aPath += nDelta;
	m_aQuery += nDelta;
	m_aFragment += nDelta;
	return true;
}

//============================================================================
// static
bool INetURLObject::parsePath(INetProtocol eScheme,
                              sal_Unicode const ** pBegin,
							  sal_Unicode const * pEnd,
							  bool bOctets,
							  EncodeMechanism eMechanism,
							  rtl_TextEncoding eCharset,
							  bool bSkippedInitialSlash,
							  sal_uInt32 nSegmentDelimiter,
							  sal_uInt32 nAltSegmentDelimiter,
							  sal_uInt32 nQueryDelimiter,
							  sal_uInt32 nFragmentDelimiter,
							  rtl::OUStringBuffer &rSynPath)
{
	DBG_ASSERT(pBegin, "INetURLObject::parsePath(): Null output param");

	sal_Unicode const * pPos = *pBegin;
	rtl::OUStringBuffer aTheSynPath;

	switch (eScheme)
	{
		case INET_PROT_NOT_VALID:
			return false;

		case INET_PROT_FTP:
		case INET_PROT_IMAP:
			if (pPos < pEnd && *pPos != '/')
				return false;
			while (pPos < pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_HTTP_PATH, '%', eCharset, true);
			}
			if (aTheSynPath.getLength() == 0)
				aTheSynPath.append(sal_Unicode('/'));
			break;

		case INET_PROT_HTTP:
		case INET_PROT_VND_SUN_STAR_WEBDAV:
		case INET_PROT_HTTPS:
        case INET_PROT_SMB:
			if (pPos < pEnd && *pPos != '/')
				return false;
			while (pPos < pEnd && *pPos != nQueryDelimiter
				   && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_HTTP_PATH, '%', eCharset, true);
			}
			if (aTheSynPath.getLength() == 0)
				aTheSynPath.append(sal_Unicode('/'));
			break;

		case INET_PROT_FILE:
		{
			if (bSkippedInitialSlash)
				aTheSynPath.append(sal_Unicode('/'));
			else if (pPos < pEnd
					 && *pPos != nSegmentDelimiter
					 && *pPos != nAltSegmentDelimiter)
				return false;
			while (pPos < pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				if (eEscapeType == ESCAPE_NO)
                {
					if (nUTF32 == nSegmentDelimiter
						|| nUTF32 == nAltSegmentDelimiter)
					{
						aTheSynPath.append(sal_Unicode('/'));
						continue;
					}
					else if (nUTF32 == '|'
							 && (pPos == pEnd
								 || *pPos == nFragmentDelimiter
								 || *pPos == nSegmentDelimiter
								 || *pPos == nAltSegmentDelimiter)
							 && aTheSynPath.getLength() == 2
							 && INetMIME::isAlpha(aTheSynPath.charAt(1)))
					{
						// A first segment of <ALPHA "|"> is translated to
						// <ALPHA ":">:
						aTheSynPath.append(sal_Unicode(':'));
						continue;
					}
                }
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_PCHAR, '%', eCharset, true);
			}
			if (aTheSynPath.getLength() == 0)
				aTheSynPath.append(sal_Unicode('/'));
			break;
		}

		case INET_PROT_MAILTO:
			while (pPos < pEnd && *pPos != nQueryDelimiter
				   && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_MAILTO, '%', eCharset, true);
			}
			break;

		case INET_PROT_NEWS:
            if (pPos == pEnd || *pPos == nQueryDelimiter
                || *pPos == nFragmentDelimiter)
                return false;

            // Match <"*">:
            if (*pPos == '*'
                && (pEnd - pPos == 1 || pPos[1] == nQueryDelimiter
                    || pPos[1] == nFragmentDelimiter))
            {
                ++pPos;
                aTheSynPath.append(sal_Unicode('*'));
                break;
            }

            // Match <group>:
            if (INetMIME::isAlpha(*pPos))
                for (sal_Unicode const * p = pPos + 1;; ++p)
                    if (p == pEnd || *p == nQueryDelimiter
                        || *p == nFragmentDelimiter)
                    {
                        aTheSynPath.setLength(0);
                        aTheSynPath.append(pPos, p - pPos);
                        pPos = p;
                        goto done;
                    }
                    else if (!INetMIME::isAlphanumeric(*p) && *p != '+'
                             && *p != '-' && *p != '.' && *p != '_')
                        break;

            // Match <article>:
            for (;;)
            {
                if (pPos == pEnd || *pPos == nQueryDelimiter
                    || *pPos == nFragmentDelimiter)
                    return false;
                if (*pPos == '@')
                    break;
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, '%',
                                             eMechanism, eCharset, eEscapeType);
                appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
                           PART_NEWS_ARTICLE_LOCALPART, '%', eCharset, true);
            }
            if (aTheSynPath.getLength() == 0)
                return false;
            ++pPos;
            aTheSynPath.append(sal_Unicode('@'));
            {
                sal_Unicode const * p = pPos;
                while (p < pEnd && *pPos != nQueryDelimiter
                       && *pPos != nFragmentDelimiter)
                    ++p;
                rtl::OUString aCanonic;
                if (!parseHost(pPos, p, aCanonic))
                    return false;
                aTheSynPath.append(aCanonic);
            }

        done:
            break;

		case INET_PROT_POP3:
			while (pPos < pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_MESSAGE_ID_PATH, '%', eCharset,
						   true);
			}
			break;

		case INET_PROT_PRIV_SOFFICE:
		case INET_PROT_SLOT:
        case INET_PROT_HID:
		case INET_PROT_MACRO:
		case INET_PROT_UNO:
		case INET_PROT_COMPONENT:
		case INET_PROT_LDAP:
			while (pPos < pEnd && *pPos != nQueryDelimiter
				   && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_PATH_BEFORE_QUERY, '%', eCharset,
						   true);
			}
			break;

		case INET_PROT_VND_SUN_STAR_HELP:
			if (pPos == pEnd
                || *pPos == nQueryDelimiter
                || *pPos == nFragmentDelimiter)
				aTheSynPath.append(sal_Unicode('/'));
			else
			{
				if (*pPos != '/')
					return false;
                while (pPos < pEnd && *pPos != nQueryDelimiter
                       && *pPos != nFragmentDelimiter)
                {
                    EscapeType eEscapeType;
                    sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
                                                 '%', eMechanism,
                                                 eCharset, eEscapeType);
                    appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
                               PART_HTTP_PATH, '%', eCharset, true);
                }
            }
			break;

		case INET_PROT_JAVASCRIPT:
		case INET_PROT_DATA:
		case INET_PROT_CID:
		case INET_PROT_DB:
			while (pPos < pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_URIC, '%', eCharset, true);
			}
			break;

		case INET_PROT_OUT:
			if (pEnd - pPos < 2 || *pPos++ != '/' || *pPos++ != '~')
				return false;
			aTheSynPath.appendAscii(RTL_CONSTASCII_STRINGPARAM("/~"));
			while (pPos < pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_URIC, '%', eCharset, true);
			}
			break;

		case INET_PROT_VND_SUN_STAR_HIER:
		case INET_PROT_VND_SUN_STAR_PKG:
			if (pPos < pEnd && *pPos != '/'
                && *pPos != nQueryDelimiter && *pPos != nFragmentDelimiter)
				return false;
			while (pPos < pEnd && *pPos != nQueryDelimiter
				   && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				if (eEscapeType == ESCAPE_NO && nUTF32 == '/')
					aTheSynPath.append(sal_Unicode('/'));
				else
					appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
							   PART_PCHAR, '%', eCharset, false);
			}
			if (aTheSynPath.getLength() == 0)
				aTheSynPath.append(sal_Unicode('/'));
			break;

		case INET_PROT_VIM:
		{
/* test had to be taken out to make parsePath static; ok since INET_PROT_VIM is
   obsolete, anyway
			if (m_aUser.isEmpty())
				return false;
*/
			sal_Unicode const * pPathEnd = pPos;
			while (pPathEnd < pEnd && *pPathEnd != nFragmentDelimiter)
				++pPathEnd;
			aTheSynPath.append(sal_Unicode('/'));
			if (pPos == pPathEnd)
				break;
			else if (*pPos++ != '/')
				return false;
			if (pPos == pPathEnd)
				break;
			while (pPos < pPathEnd && *pPos != '/')
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pPathEnd, bOctets,
											 '=', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath,
						   eEscapeType == ESCAPE_NO ?
							   INetMIME::toLowerCase(nUTF32) : nUTF32,
						   eEscapeType, bOctets, PART_VIM, '=',
						   eCharset, false);
			}
			bool bInbox;
			rtl::OUString sCompare(aTheSynPath);
			if (sCompare.equalsAscii("/inbox"))
				bInbox = true;
			else if (sCompare.equalsAscii("/newsgroups"))
				bInbox = false;
			else
				return false;
			aTheSynPath.append(sal_Unicode('/'));
			if (pPos == pPathEnd)
				break;
			else if (*pPos++ != '/')
				return false;
			if (!bInbox)
			{
				bool bEmpty = true;
				while (pPos < pPathEnd && *pPos != '/')
				{
					EscapeType eEscapeType;
					sal_uInt32 nUTF32 = getUTF32(pPos, pPathEnd, bOctets,
												 '=', eMechanism,
												 eCharset, eEscapeType);
					appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
							   PART_VIM, '=', eCharset, false);
					bEmpty = false;
				}
				if (bEmpty)
					return false;
				aTheSynPath.append(sal_Unicode('/'));
				if (pPos == pPathEnd)
					break;
				else if (*pPos++ != '/')
					return false;
			}
			bool bEmpty = true;
			while (pPos < pPathEnd && *pPos != ':')
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pPathEnd, bOctets,
											 '=', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_VIM, '=', eCharset, false);
				bEmpty = false;
			}
			if (bEmpty)
				return false;
			if (pPos == pPathEnd)
				break;
			else if (*pPos++ != ':')
				return false;
			aTheSynPath.append(sal_Unicode(':'));
			for (int i = 0; i < 3; ++i)
			{
				if (i != 0)
				{
					if (pPos == pPathEnd || *pPos++ != '.')
						return false;
					aTheSynPath.append(sal_Unicode('.'));
				}
				bEmpty = true;
				while (pPos < pPathEnd && *pPos != '.')
				{
					EscapeType eEscapeType;
					sal_uInt32 nUTF32 = getUTF32(pPos, pPathEnd, bOctets,
												 '=', eMechanism,
												 eCharset, eEscapeType);
					if (!INetMIME::isDigit(nUTF32))
						return false;
					aTheSynPath.append(sal_Unicode(nUTF32));
					bEmpty = false;
				}
				if (bEmpty)
					return false;
			}
			if (pPos != pPathEnd)
				return false;
			break;
		}

		case INET_PROT_VND_SUN_STAR_CMD:
		case INET_PROT_VND_SUN_STAR_EXPAND:
		{
			if (pPos == pEnd || *pPos == nFragmentDelimiter)
				return false;
			Part ePart = PART_URIC_NO_SLASH;
			while (pPos != pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, ePart,
						   '%', eCharset, true);
				ePart = PART_URIC;
			}
			break;
		}

        case INET_PROT_VND_SUN_STAR_ODMA:
			if (pPos < pEnd)
            {
                if (*pPos == '/')
                    ++pPos;
                else
                    return false;
            }
            aTheSynPath.append(sal_Unicode('/'));
			while (pPos < pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_URIC_NO_SLASH, '%', eCharset, true);
			}
			break;

        case INET_PROT_TELNET:
            if (pPos < pEnd)
            {
                if (*pPos != '/' || pEnd - pPos > 1)
                    return false;
                ++pPos;
            }
            aTheSynPath.append(sal_Unicode('/'));
            break;

		case INET_PROT_VND_SUN_STAR_TDOC:
			if (pPos == pEnd || *pPos != '/')
				return false;
			while (pPos < pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				if (eEscapeType == ESCAPE_NO && nUTF32 == '/')
					aTheSynPath.append(sal_Unicode('/'));
				else
					appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
							   PART_PCHAR, '%', eCharset, false);
			}
			break;

        case INET_PROT_GENERIC:
			while (pPos < pEnd && *pPos != nFragmentDelimiter)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets,
											 '%', eMechanism,
											 eCharset, eEscapeType);
				appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets,
						   PART_URIC, '%', eCharset, true);
			}
			if (aTheSynPath.getLength() == 0)
                return false;
			break;
        default:
            OSL_ASSERT(false);
            break;
	}

	*pBegin = pPos;
	rSynPath = aTheSynPath;
	return true;
}

//============================================================================
bool INetURLObject::setPath(rtl::OUString const & rThePath, bool bOctets,
							EncodeMechanism eMechanism,
							rtl_TextEncoding eCharset)
{
	rtl::OUStringBuffer aSynPath;
	sal_Unicode const * p = rThePath.getStr();
	sal_Unicode const * pEnd = p + rThePath.getLength();
	if (!parsePath(m_eScheme, &p, pEnd, bOctets, eMechanism, eCharset, false,
                   '/', 0x80000000, 0x80000000, 0x80000000, aSynPath)
		|| p != pEnd)
		return false;
	sal_Int32 nDelta = m_aPath.set(m_aAbsURIRef, aSynPath.makeStringAndClear());
	m_aQuery += nDelta;
	m_aFragment += nDelta;
	return true;
}

//============================================================================
bool INetURLObject::checkHierarchical() const {
    if (m_eScheme == INET_PROT_VND_SUN_STAR_EXPAND) {
        OSL_ENSURE(
            false, "INetURLObject::checkHierarchical vnd.sun.star.expand");
        return true;
    } else {
        return getSchemeInfo().m_bHierarchical;
    }
}

//============================================================================
bool INetURLObject::appendSegment(rtl::OUString const & rTheSegment, 
								  bool bOctets, EncodeMechanism eMechanism,
								  rtl_TextEncoding eCharset)
{
	return insertName(rTheSegment, bOctets, false, LAST_SEGMENT, true,
					  eMechanism, eCharset);
}

//============================================================================
INetURLObject::SubString INetURLObject::getSegment(sal_Int32 nIndex,
												   bool bIgnoreFinalSlash)
	const
{
	DBG_ASSERT(nIndex >= 0 || nIndex == LAST_SEGMENT,
			   "INetURLObject::getSegment(): Bad index");

	if (!checkHierarchical())
		return SubString();

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
	sal_Unicode const * pSegBegin;
	sal_Unicode const * pSegEnd;
	if (nIndex == LAST_SEGMENT)
	{
		pSegEnd = pPathEnd;
		if (bIgnoreFinalSlash && pSegEnd > pPathBegin && pSegEnd[-1] == '/')
			--pSegEnd;
        if (pSegEnd <= pPathBegin)
            return SubString();
		pSegBegin = pSegEnd - 1;
		while (pSegBegin > pPathBegin && *pSegBegin != '/')
			--pSegBegin;
	}
	else
	{
		pSegBegin = pPathBegin;
		while (nIndex-- > 0)
			do
			{
				++pSegBegin;
				if (pSegBegin >= pPathEnd)
					return SubString();
			}
			while (*pSegBegin != '/');
		pSegEnd = pSegBegin + 1;
		while (pSegEnd < pPathEnd && *pSegEnd != '/')
			++pSegEnd;
	}

	return SubString(pSegBegin - m_aAbsURIRef.getStr(),
					 pSegEnd - pSegBegin);
}

//============================================================================
bool INetURLObject::insertName(rtl::OUString const & rTheName, bool bOctets,
							   bool bAppendFinalSlash, sal_Int32 nIndex,
							   bool bIgnoreFinalSlash,
							   EncodeMechanism eMechanism,
							   rtl_TextEncoding eCharset)
{
	DBG_ASSERT(nIndex >= 0 || nIndex == LAST_SEGMENT,
			   "INetURLObject::insertName(): Bad index");

	if (!checkHierarchical())
		return false;

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
	sal_Unicode const * pPrefixEnd;
    bool bInsertSlash;
	sal_Unicode const * pSuffixBegin;
	if (nIndex == LAST_SEGMENT)
	{
		pPrefixEnd = pPathEnd;
		if (bIgnoreFinalSlash && pPrefixEnd > pPathBegin &&
            pPrefixEnd[-1] == '/')
        {
			--pPrefixEnd;
        }
        bInsertSlash = bAppendFinalSlash;
        pSuffixBegin = pPathEnd;
	}
    else if (nIndex == 0)
    {
        pPrefixEnd = pPathBegin;
        bInsertSlash =
            (pPathBegin < pPathEnd && *pPathBegin != '/') ||
            (pPathBegin == pPathEnd && bAppendFinalSlash);
        pSuffixBegin =
            (pPathEnd - pPathBegin == 1 && *pPathBegin == '/' &&
             !bAppendFinalSlash && bIgnoreFinalSlash)
            ? pPathEnd : pPathBegin;
    }
	else
	{
		pPrefixEnd = pPathBegin;
		sal_Unicode const * pEnd = pPathEnd;
		if (bIgnoreFinalSlash && pEnd > pPathBegin && pEnd[-1] == '/')
			--pEnd;
        bool bSkip = pPrefixEnd < pEnd && *pPrefixEnd == '/';
        bInsertSlash = false;
        pSuffixBegin = pPathEnd;
 		while (nIndex-- > 0)
			for (;;)
			{
                if (bSkip)
                    ++pPrefixEnd;
                bSkip = true;
				if (pPrefixEnd >= pEnd)
                {
					if (nIndex == 0)
					{
                        bInsertSlash = bAppendFinalSlash;
						break;
					}
					else
						return false;
                }
				if (*pPrefixEnd == '/')
				{
					pSuffixBegin = pPrefixEnd;
					break;
				}
			}
	}

	rtl::OUStringBuffer aNewPath;
	aNewPath.append(pPathBegin, pPrefixEnd - pPathBegin);
    aNewPath.append(sal_Unicode('/'));
	aNewPath.append(encodeText(rTheName, bOctets, PART_PCHAR, getEscapePrefix(),
						   eMechanism, eCharset, true));
    if (bInsertSlash) {
        aNewPath.append(sal_Unicode('/'));
    }
    aNewPath.append(pSuffixBegin, pPathEnd - pSuffixBegin);

	return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, 
		RTL_TEXTENCODING_UTF8);
}

//============================================================================
bool INetURLObject::clearQuery()
{
	if (HasError())
		return false;
	if (m_aQuery.isPresent())
	{
		lcl_Erase(m_aAbsURIRef, m_aQuery.getBegin() - 1, 
			m_aQuery.getLength() + 1);
		m_aFragment += m_aQuery.clear() - 1;
	}
	return false;
}

//============================================================================
bool INetURLObject::setQuery(rtl::OUString const & rTheQuery, bool bOctets,
							 EncodeMechanism eMechanism,
							 rtl_TextEncoding eCharset)
{
	if (!getSchemeInfo().m_bQuery)
		return false;
	rtl::OUString aNewQuery(encodeText(rTheQuery, bOctets, PART_URIC,
								   getEscapePrefix(), eMechanism, eCharset,
								   true));
	sal_Int32 nDelta;
	if (m_aQuery.isPresent())
		nDelta = m_aQuery.set(m_aAbsURIRef, aNewQuery);
	else
	{
		m_aAbsURIRef.insert(m_aPath.getEnd(), sal_Unicode('?'));
		nDelta = m_aQuery.set(m_aAbsURIRef, aNewQuery, m_aPath.getEnd() + 1)
					 + 1;
	}
	m_aFragment += nDelta;
	return true;
}

//============================================================================
bool INetURLObject::clearFragment()
{
	if (HasError())
		return false;
	if (m_aFragment.isPresent())
	{
		m_aAbsURIRef.setLength(m_aFragment.getBegin() - 1);
		m_aFragment.clear();
	}
	return true;
}

//============================================================================
bool INetURLObject::setFragment(rtl::OUString const & rTheFragment, 
								bool bOctets, EncodeMechanism eMechanism,
								rtl_TextEncoding eCharset)
{
	if (HasError())
		return false;
	rtl::OUString aNewFragment(encodeText(rTheFragment, bOctets, PART_URIC,
									  getEscapePrefix(), eMechanism,
									  eCharset, true));
	if (m_aFragment.isPresent())
		m_aFragment.set(m_aAbsURIRef, aNewFragment);
	else
	{
		m_aAbsURIRef.append(sal_Unicode('#'));
		m_aFragment.set(m_aAbsURIRef, aNewFragment, m_aAbsURIRef.getLength());
	}
	return true;
}

//============================================================================
INetURLObject::FTPType INetURLObject::getFTPType() const
{
	if (m_eScheme == INET_PROT_FTP
		&& m_aPath.getLength() >= RTL_CONSTASCII_LENGTH(";type=") + 1
		&& rtl::OUString(m_aAbsURIRef).copy(
			m_aPath.getEnd() - (RTL_CONSTASCII_LENGTH(";type=") + 1),
			RTL_CONSTASCII_LENGTH(";type=")).equalsIgnoreAsciiCaseAscii(";type="))
		switch (m_aAbsURIRef.charAt(m_aPath.getEnd()))
		{
			case 'A':
			case 'a':
				return FTP_TYPE_A;

			case 'D':
			case 'd':
				return FTP_TYPE_D;

			case 'I':
			case 'i':
				return FTP_TYPE_I;
		}
	return FTP_TYPE_NONE;
}

//============================================================================
bool INetURLObject::hasDosVolume(FSysStyle eStyle) const
{
    sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    return (eStyle & FSYS_DOS) != 0
           && m_aPath.getLength() >= 3
           && p[0] == '/'
           && INetMIME::isAlpha(p[1])
           && p[2] == ':'
           && (m_aPath.getLength() == 3 || p[3] == '/');
}

//============================================================================
sal_uInt32 INetURLObject::getIMAPUID() const
{
	if (m_eScheme == INET_PROT_IMAP
		&& m_aPath.getLength() >= RTL_CONSTASCII_LENGTH("/;uid=") + 1)
	{
		sal_Unicode const * pBegin = m_aAbsURIRef.getStr()
										 + m_aPath.getBegin()
										 + RTL_CONSTASCII_LENGTH("/;uid=");
		sal_Unicode const * pEnd = pBegin + m_aPath.getLength();
		sal_Unicode const * p = pEnd;
		while (p > pBegin && INetMIME::isDigit(p[-1]))
			--p;
		if (p < pEnd && *--p != '0'
			&& rtl::OUString(m_aAbsURIRef).copy(
				p - RTL_CONSTASCII_LENGTH("/;uid=") - m_aAbsURIRef.getStr(),
				RTL_CONSTASCII_LENGTH("/;uid=")).equalsIgnoreAsciiCaseAscii("/;uid=")
		   )
		{
			sal_uInt32 nUID;
			if (INetMIME::scanUnsigned(p, pEnd, false, nUID))
				return nUID;
		}
	}
	return 0;
}

//============================================================================
// static
rtl::OUString INetURLObject::encodeText(sal_Unicode const * pBegin,
									sal_Unicode const * pEnd, bool bOctets,
									Part ePart, sal_Char cEscapePrefix,
									EncodeMechanism eMechanism,
									rtl_TextEncoding eCharset,
									bool bKeepVisibleEscapes)
{
	rtl::OUStringBuffer aResult;
	while (pBegin < pEnd)
	{
		EscapeType eEscapeType;
		sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd, bOctets, cEscapePrefix,
									 eMechanism, eCharset, eEscapeType);
		appendUCS4(aResult, nUTF32, eEscapeType, bOctets, ePart,
				   cEscapePrefix, eCharset, bKeepVisibleEscapes);
	}
	return aResult.makeStringAndClear();
}

//============================================================================
// static
rtl::OUString INetURLObject::decode(sal_Unicode const * pBegin,
								sal_Unicode const * pEnd,
								sal_Char cEscapePrefix,
								DecodeMechanism eMechanism,
								rtl_TextEncoding eCharset)
{
	switch (eMechanism)
	{
		case NO_DECODE:
			return rtl::OUString(pBegin, pEnd - pBegin);

		case DECODE_TO_IURI:
			eCharset = RTL_TEXTENCODING_UTF8;
			break;

        default:
            break;
	}
	rtl::OUStringBuffer aResult;
	while (pBegin < pEnd)
	{
		EscapeType eEscapeType;
		sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd, false, cEscapePrefix,
									 WAS_ENCODED, eCharset, eEscapeType);
		switch (eEscapeType)
		{
			case ESCAPE_NO:
				aResult.append(sal_Unicode(nUTF32));
				break;

			case ESCAPE_OCTET:
				appendEscape(aResult, cEscapePrefix, nUTF32);
				break;

			case ESCAPE_UTF32:
				if (
                     INetMIME::isUSASCII(nUTF32) && 
                     (
                       eMechanism == DECODE_TO_IURI ||
                       (
                         eMechanism == DECODE_UNAMBIGUOUS &&
                         mustEncode(nUTF32, PART_UNAMBIGUOUS)
                       )
                     )
                   )
                {
					appendEscape(aResult, cEscapePrefix, nUTF32);
                }
				else
					aResult.append(sal_Unicode(nUTF32));
				break;
		}
	}
	return aResult.makeStringAndClear();
}

//============================================================================
rtl::OUString INetURLObject::GetURLNoPass(DecodeMechanism eMechanism,
									  rtl_TextEncoding eCharset) const
{
	INetURLObject aTemp(*this);
	aTemp.clearPassword();
	return aTemp.GetMainURL(eMechanism, eCharset);
}

//============================================================================
rtl::OUString INetURLObject::GetURLNoMark(DecodeMechanism eMechanism,
									  rtl_TextEncoding eCharset) const
{
	INetURLObject aTemp(*this);
	aTemp.clearFragment();
	return aTemp.GetMainURL(eMechanism, eCharset);
}

//============================================================================
rtl::OUString
INetURLObject::getAbbreviated(
    star::uno::Reference< star::util::XStringWidth > const & rStringWidth,
    sal_Int32 nWidth,
    DecodeMechanism eMechanism,
    rtl_TextEncoding eCharset)
    const
{
    OSL_ENSURE(rStringWidth.is(), "specification violation");
    sal_Char cEscapePrefix = getEscapePrefix();
    rtl::OUStringBuffer aBuffer;
	// make sure that the scheme is copied for generic schemes: getSchemeInfo().m_pScheme
	// is empty ("") in that case, so take the scheme from m_aAbsURIRef
	if (m_eScheme != INET_PROT_GENERIC)
	{
		aBuffer.appendAscii(getSchemeInfo().m_pScheme);
	}
	else
	{
		if (m_aAbsURIRef)
		{
			sal_Unicode const * pSchemeBegin
				= m_aAbsURIRef.getStr();
			sal_Unicode const * pSchemeEnd = pSchemeBegin;

			while (pSchemeEnd[0] != ':')
			{
				++pSchemeEnd;
			}
			aBuffer.append(pSchemeBegin, pSchemeEnd - pSchemeBegin);
		}
	}
    aBuffer.append(static_cast< sal_Unicode >(':'));
    bool bAuthority = getSchemeInfo().m_bAuthority;
    sal_Unicode const * pCoreBegin
        = m_aAbsURIRef.getStr() + (bAuthority ? getAuthorityBegin() :
                                                   m_aPath.getBegin());
    sal_Unicode const * pCoreEnd
        = m_aAbsURIRef.getStr() + m_aPath.getBegin() + m_aPath.getLength();
    bool bSegment = false;
    if (getSchemeInfo().m_bHierarchical)
    {
        rtl::OUString aRest;
        if (m_aQuery.isPresent())
            aRest = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("?..."));
        else if (m_aFragment.isPresent())
            aRest = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("#..."));
        rtl::OUStringBuffer aTrailer;
        sal_Unicode const * pBegin = pCoreBegin;
        sal_Unicode const * pEnd = pCoreEnd;
        sal_Unicode const * pPrefixBegin = pBegin;
        sal_Unicode const * pSuffixEnd = pEnd;
        bool bPrefix = true;
        bool bSuffix = true;
        do
        {
            if (bSuffix)
            {
                sal_Unicode const * p = pSuffixEnd - 1;
                if (pSuffixEnd == pCoreEnd && *p == '/')
                    --p;
                while (*p != '/')
                    --p;
                if (bAuthority && p == pCoreBegin + 1)
                    --p;
                rtl::OUString
                    aSegment(decode(p + (p == pBegin && pBegin != pCoreBegin ?
                                             1 : 0),
                                    pSuffixEnd,
                                    cEscapePrefix,
                                    eMechanism,
                                    eCharset));
                pSuffixEnd = p;
                rtl::OUStringBuffer aResult(aBuffer);
                if (pSuffixEnd != pBegin)
                    aResult.appendAscii(RTL_CONSTASCII_STRINGPARAM("..."));
                aResult.append(aSegment);
                aResult.append(aTrailer);
                aResult.append(aRest);
                if (rStringWidth->
                            queryStringWidth(aResult.makeStringAndClear())
                        <= nWidth)
                {
                    aTrailer.insert(0, aSegment);
                    bSegment = true;
                    pEnd = pSuffixEnd;
                }
                else
                    bSuffix = false;
                if (pPrefixBegin > pSuffixEnd)
                    pPrefixBegin = pSuffixEnd;
                if (pBegin == pEnd)
                    break;
            }
            if (bPrefix)
            {
                sal_Unicode const * p
                    = pPrefixBegin
                          + (bAuthority && pPrefixBegin == pCoreBegin ? 2 :
                                                                        1);
                OSL_ASSERT(p <= pEnd);
                while (p < pEnd && *p != '/')
                    ++p;
                if (p == pCoreEnd - 1 && *p == '/')
                    ++p;
                rtl::OUString
                    aSegment(decode(pPrefixBegin
                                        + (pPrefixBegin == pCoreBegin ? 0 :
                                                                        1),
                                    p == pEnd ? p : p + 1,
                                    cEscapePrefix,
                                    eMechanism,
                                    eCharset));
                pPrefixBegin = p;
                rtl::OUStringBuffer aResult(aBuffer);
                aResult.append(aSegment);
                if (pPrefixBegin != pEnd)
                    aResult.appendAscii(RTL_CONSTASCII_STRINGPARAM("..."));
                aResult.append(aTrailer);
                aResult.append(aRest);
                if (rStringWidth->
                            queryStringWidth(aResult.makeStringAndClear())
                        <= nWidth)
                {
                    aBuffer.append(aSegment);
                    bSegment = true;
                    pBegin = pPrefixBegin;
                }
                else
                    bPrefix = false;
                if (pPrefixBegin > pSuffixEnd)
                    pSuffixEnd = pPrefixBegin;
                if (pBegin == pEnd)
                    break;
            }
        }
        while (bPrefix || bSuffix);
        if (bSegment)
        {
            if (pPrefixBegin != pBegin || pSuffixEnd != pEnd)
                aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("..."));
            aBuffer.append(aTrailer);
        }
    }
    if (!bSegment)
        aBuffer.append(decode(pCoreBegin,
                              pCoreEnd,
                              cEscapePrefix,
                              eMechanism,
                              eCharset));
    if (m_aQuery.isPresent())
    {
        aBuffer.append(static_cast< sal_Unicode >('?'));
        aBuffer.append(decode(m_aQuery, cEscapePrefix, eMechanism, eCharset));
    }
    if (m_aFragment.isPresent())
    {
        aBuffer.append(static_cast< sal_Unicode >('#'));
        aBuffer.
            append(decode(m_aFragment, cEscapePrefix, eMechanism, eCharset));
    }
    if (aBuffer.getLength() != 0)
    {
        rtl::OUStringBuffer aResult(aBuffer);
        if (rStringWidth->queryStringWidth(aResult.makeStringAndClear())
                > nWidth)
            for (sal_Int32 i = aBuffer.getLength();;)
            {
                if (i == 0)
                {
                    aBuffer.setLength(aBuffer.getLength() - 1);
                    if (aBuffer.getLength() == 0)
                        break;
                }
                else
                {
                    aBuffer.setLength(--i);
                    aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("..."));
                }
                aResult = aBuffer;
                if (rStringWidth->
                            queryStringWidth(aResult.makeStringAndClear())
                        <= nWidth)
                    break;
            }
    }
    return aBuffer.makeStringAndClear();
}

//============================================================================
bool INetURLObject::operator ==(INetURLObject const & rObject) const
{
	if (m_eScheme != rObject.m_eScheme)
		return false;
	if (m_eScheme == INET_PROT_NOT_VALID)
		return (m_aAbsURIRef == rObject.m_aAbsURIRef) != false;
    if ((m_aScheme.compare(
             rObject.m_aScheme, m_aAbsURIRef, rObject.m_aAbsURIRef)
         != 0)
        || GetUser(NO_DECODE) != rObject.GetUser(NO_DECODE)
		|| GetPass(NO_DECODE) != rObject.GetPass(NO_DECODE)
		|| !GetHost(NO_DECODE).equalsIgnoreAsciiCase(
			rObject.GetHost(NO_DECODE))
		|| GetPort() != rObject.GetPort()
		|| HasParam() != rObject.HasParam()
		|| GetParam(NO_DECODE) != rObject.GetParam(NO_DECODE)
		|| GetMsgId(NO_DECODE) != rObject.GetMsgId(NO_DECODE))
		return false;
	rtl::OUString aPath1(GetURLPath(NO_DECODE));
	rtl::OUString aPath2(rObject.GetURLPath(NO_DECODE));
	switch (m_eScheme)
	{
		case INET_PROT_FILE:
		{
			// If the URL paths of two file URLs only differ in that one has a
			// final '/' and the other has not, take the two paths as
			// equivalent (this could be usefull for other schemes, too):
			sal_Int32 nLength = aPath1.getLength();
			switch (nLength - aPath2.getLength())
			{
				case -1:
					if (aPath2.getStr()[nLength] != '/')
						return false;
					break;

				case 0:
					break;

				case 1:
					if (aPath1.getStr()[--nLength] != '/')
						return false;
					break;

				default:
					return false;
			}
			return aPath1.compareTo(aPath2, nLength) == 0;
		}

		default:
			return (aPath1 == aPath2) != false;
	}
}

//============================================================================
bool INetURLObject::operator <(INetURLObject const & rObject) const
{
    sal_Int32 nCompare = m_aScheme.compare(
        rObject.m_aScheme, m_aAbsURIRef, rObject.m_aAbsURIRef);
    if (nCompare < 0) {
        return true;
    } else if (nCompare > 0) {
        return false;
    }
	sal_uInt32 nPort1 = GetPort();
	sal_uInt32 nPort2 = rObject.GetPort();
	if (nPort1 < nPort2)
		return true;
	else if (nPort1 > nPort2)
		return false;
	nCompare = GetUser(NO_DECODE).compareTo(rObject.GetUser(NO_DECODE));
	if (nCompare < 0)
		return true;
	else if (nCompare > 0)
		return false;
	nCompare = GetPass(NO_DECODE).compareTo(rObject.GetPass(NO_DECODE));
	if (nCompare < 0)
		return true;
	else if (nCompare > 0)
		return false;
	nCompare = GetHost(NO_DECODE).compareTo(rObject.GetHost(NO_DECODE));
	if (nCompare < 0)
		return true;
	else if (nCompare > 0)
		return false;
	const rtl::OUString &rPath1(GetURLPath(NO_DECODE));
	const rtl::OUString &rPath2(rObject.GetURLPath(NO_DECODE));
	nCompare = rPath1.compareTo(rPath2);
	if (nCompare < 0)
		return true;
	else if (nCompare > 0)
		return false;
	nCompare = GetParam(NO_DECODE).compareTo(rObject.GetParam(NO_DECODE));
	if (nCompare < 0)
		return true;
	else if (nCompare > 0)
		return false;
	return GetMsgId(NO_DECODE).compareTo(rObject.GetMsgId(NO_DECODE)) < 0;
}

//============================================================================
bool INetURLObject::ConcatData(INetProtocol eTheScheme,
							   rtl::OUString const & rTheUser,
							   rtl::OUString const & rThePassword,
							   rtl::OUString const & rTheHost,
							   sal_uInt32 nThePort,
							   rtl::OUString const & rThePath,
							   EncodeMechanism eMechanism,
							   rtl_TextEncoding eCharset)
{
	setInvalid();
	m_eScheme = eTheScheme;
	if (HasError() || m_eScheme == INET_PROT_GENERIC)
		return false;
	m_aAbsURIRef.setLength(0);
	m_aAbsURIRef.appendAscii(getSchemeInfo().m_pScheme);
	m_aAbsURIRef.append(sal_Unicode(':'));
	if (getSchemeInfo().m_bAuthority)
	{
		m_aAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
		bool bUserInfo = false;
		if (getSchemeInfo().m_bUser)
		{
			if (m_eScheme == INET_PROT_IMAP && rTheUser.getLength() == 0)
			{
				setInvalid();
				return false;
			}
			if (rTheUser.getLength() != 0)
			{
				m_aUser.set(m_aAbsURIRef,
							encodeText(rTheUser, false,
									   m_eScheme == INET_PROT_IMAP ?
										   PART_IMAP_ACHAR :
									   m_eScheme == INET_PROT_VIM ?
										   PART_VIM :
										   PART_USER_PASSWORD,
									   getEscapePrefix(), eMechanism,
									   eCharset, false),
							m_aAbsURIRef.getLength());
				bUserInfo = true;
			}
		}
		else if (rTheUser.getLength() != 0)
		{
			setInvalid();
			return false;
		}
		if (rThePassword.getLength() != 0)
        {
			if (getSchemeInfo().m_bPassword)
			{
				m_aAbsURIRef.append(sal_Unicode(':'));
				m_aAuth.set(m_aAbsURIRef,
							encodeText(rThePassword, false,
									   m_eScheme == INET_PROT_VIM ?
										   PART_VIM : PART_USER_PASSWORD,
									   getEscapePrefix(), eMechanism,
									   eCharset, false),
							m_aAbsURIRef.getLength());
				bUserInfo = true;
			}
			else
			{
				setInvalid();
				return false;
			}
        }
		if (bUserInfo && getSchemeInfo().m_bHost)
			m_aAbsURIRef.append(sal_Unicode('@'));
		if (getSchemeInfo().m_bHost)
		{
			rtl::OUStringBuffer aSynHost(rTheHost);
            bool bNetBiosName = false;
			switch (m_eScheme)
			{
				case INET_PROT_FILE:
					{
				        rtl::OUString sTemp(aSynHost);
				        if (sTemp.equalsIgnoreAsciiCaseAsciiL(
					        RTL_CONSTASCII_STRINGPARAM("localhost")))
						{
						    aSynHost.setLength(0);
						}
                        bNetBiosName = true;
					}
					break;

				case INET_PROT_LDAP:
					if (aSynHost.getLength() == 0 && nThePort != 0)
					{
						setInvalid();
						return false;
					}
					break;

				default:
					if (aSynHost.getLength() == 0)
					{
						setInvalid();
						return false;
					}
					break;
			}
            if (!parseHostOrNetBiosName(
                    aSynHost.getStr(), aSynHost.getStr() + aSynHost.getLength(),
                    false, eMechanism, eCharset, bNetBiosName, &aSynHost))
            {
                setInvalid();
                return false;
            }
			m_aHost.set(m_aAbsURIRef, aSynHost.makeStringAndClear(), 
				m_aAbsURIRef.getLength());
			if (nThePort != 0)
			{
				if (getSchemeInfo().m_bPort)
				{
					m_aAbsURIRef.append(sal_Unicode(':'));
					m_aPort.set(m_aAbsURIRef,
								rtl::OUString::valueOf(sal_Int64(nThePort)),
								m_aAbsURIRef.getLength());
				}
				else
				{
					setInvalid();
					return false;
				}
			}
		}
		else if (rTheHost.getLength() != 0 || nThePort != 0)
		{
			setInvalid();
			return false;
		}
	}
	rtl::OUStringBuffer aSynPath;
	sal_Unicode const * p = rThePath.getStr();
	sal_Unicode const * pEnd = p + rThePath.getLength();
	if (!parsePath(m_eScheme, &p, pEnd, false, eMechanism, eCharset, false, '/',
				   0x80000000, 0x80000000, 0x80000000, aSynPath)
		|| p != pEnd)
    {
        setInvalid();
		return false;
    }
	m_aPath.set(m_aAbsURIRef, aSynPath.makeStringAndClear(), 
		m_aAbsURIRef.getLength());
	return true;
}

//============================================================================
// static
rtl::OUString INetURLObject::GetAbsURL(rtl::OUString const & rTheBaseURIRef,
                                       rtl::OUString const & rTheRelURIRef,
                                       bool bIgnoreFragment,
                                       EncodeMechanism eEncodeMechanism,
                                       DecodeMechanism eDecodeMechanism,
                                       rtl_TextEncoding eCharset,
                                       FSysStyle eStyle)
{
	// Backwards compatibility:
	if (rTheRelURIRef.getLength() == 0 || rTheRelURIRef[0] == '#')
		return rTheRelURIRef;

	INetURLObject aTheAbsURIRef;
	bool bWasAbsolute;
	return INetURLObject(rTheBaseURIRef, eEncodeMechanism, eCharset).
            convertRelToAbs(rTheRelURIRef, false, aTheAbsURIRef,
                            bWasAbsolute, eEncodeMechanism,
                            eCharset, bIgnoreFragment, false,
                            false, eStyle)
		   || eEncodeMechanism != WAS_ENCODED
		   || eDecodeMechanism != DECODE_TO_IURI
		   || eCharset != RTL_TEXTENCODING_UTF8 ?
		       aTheAbsURIRef.GetMainURL(eDecodeMechanism, eCharset) :
		       rTheRelURIRef;
}

//============================================================================
rtl::OUString INetURLObject::getExternalURL(DecodeMechanism eMechanism,
										rtl_TextEncoding eCharset) const
{
	rtl::OUString aTheExtURIRef;
	translateToExternal(
        rtl::OUString(m_aAbsURIRef), aTheExtURIRef, eMechanism, eCharset);
	return aTheExtURIRef;
}

//============================================================================
// static
rtl::OUString INetURLObject::GetScheme(INetProtocol eTheScheme)
{
	return rtl::OUString::createFromAscii(getSchemeInfo(eTheScheme).m_pPrefix);
}

//============================================================================
// static
INetProtocol INetURLObject::CompareProtocolScheme(rtl::OUString const &
													  rTheAbsURIRef)
{
	sal_Unicode const * p = rTheAbsURIRef.getStr();
	PrefixInfo const * pPrefix = getPrefix(p, p + rTheAbsURIRef.getLength());
	return pPrefix ? pPrefix->m_eScheme : INET_PROT_NOT_VALID;
}

//============================================================================
bool INetURLObject::hasPassword() const
{
	return m_aAuth.isPresent() && getSchemeInfo().m_bPassword;
}

//============================================================================
void INetURLObject::makeAuthCanonic()
{
	if (m_eScheme == INET_PROT_IMAP && m_aAuth.getLength() == 1
		&& m_aAbsURIRef.charAt(m_aAuth.getBegin()) == '*')
	{
		lcl_Erase(m_aAbsURIRef, m_aAuth.getBegin()
							   - RTL_CONSTASCII_LENGTH(";AUTH="),
						   RTL_CONSTASCII_LENGTH(";AUTH=*"));
		sal_Int32 nDelta = m_aAuth.clear() - RTL_CONSTASCII_LENGTH(";AUTH=");
		m_aPath += nDelta;
		m_aQuery += nDelta;
		m_aFragment += nDelta;
	}
}

//============================================================================
rtl::OUString INetURLObject::GetHostPort(DecodeMechanism eMechanism,
									 rtl_TextEncoding eCharset)
{
	// Check because PROT_VND_SUN_STAR_HELP, PROT_VND_SUN_STAR_HIER, and
    // PROT_VND_SUN_STAR_PKG misuse m_aHost:
	if (!getSchemeInfo().m_bHost)
		return rtl::OUString();
	rtl::OUStringBuffer aHostPort(decode(m_aHost, getEscapePrefix(), 
		eMechanism, eCharset));
	if (m_aPort.isPresent())
	{
		aHostPort.append(sal_Unicode(':'));
		aHostPort.append(decode(m_aPort, getEscapePrefix(), 
			eMechanism, eCharset));
	}
	return aHostPort.makeStringAndClear();
}

//============================================================================
sal_uInt32 INetURLObject::GetPort() const
{
	if (m_aPort.isPresent())
	{
		sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPort.getBegin();
		sal_Unicode const * pEnd = p + m_aPort.getLength();
		sal_uInt32 nThePort;
		if (INetMIME::scanUnsigned(p, pEnd, true, nThePort) && p == pEnd)
			return nThePort;
	}
	return 0;
}

//============================================================================
bool INetURLObject::SetPort(sal_uInt32 nThePort)
{
	if (getSchemeInfo().m_bPort && m_aHost.isPresent())
	{
		rtl::OUString aNewPort(rtl::OUString::valueOf(sal_Int64(nThePort)));
		sal_Int32 nDelta;
		if (m_aPort.isPresent())
			nDelta = m_aPort.set(m_aAbsURIRef, aNewPort);
		else
		{
			m_aAbsURIRef.insert(m_aHost.getEnd(), sal_Unicode(':'));
			nDelta = m_aPort.set(m_aAbsURIRef, aNewPort, m_aHost.getEnd() + 1)
						 + 1;
		}
		m_aPath += nDelta;
		m_aQuery += nDelta;
		m_aFragment += nDelta;
		return true;
	}
	return false;
}

//============================================================================
void INetURLObject::makePortCanonic()
{
	if (m_aPort.isPresent())
	{
		sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPort.getBegin();
		sal_Unicode const * pEnd = p + m_aPort.getLength();
		sal_uInt32 nThePort;
		if (INetMIME::scanUnsigned(p, pEnd, true, nThePort) && p == pEnd)
		{
			sal_Int32 nDelta;
			if (nThePort != 0 && nThePort == getSchemeInfo().m_nDefaultPort)
			{
				lcl_Erase(m_aAbsURIRef, m_aPort.getBegin() - 1,
								   m_aPort.getLength() + 1);
				nDelta = m_aPort.clear() - 1;
			}
			else
				nDelta = m_aPort.set(m_aAbsURIRef,
								 rtl::OUString::valueOf(sal_Int64(nThePort)));
			m_aPath += nDelta;
			m_aQuery += nDelta;
			m_aFragment += nDelta;
		}
	}
}

//============================================================================
sal_Int32 INetURLObject::getSegmentCount(bool bIgnoreFinalSlash) const
{
    if (!checkHierarchical())
		return 0;

	sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pEnd = p + m_aPath.getLength();
	if (bIgnoreFinalSlash && pEnd > p && pEnd[-1] == '/')
		--pEnd;
	sal_Int32 n = p == pEnd || *p == '/' ? 0 : 1;
	while (p != pEnd)
		if (*p++ == '/')
			++n;
	return n;
}

//============================================================================
bool INetURLObject::removeSegment(sal_Int32 nIndex, bool bIgnoreFinalSlash)
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return false;

	rtl::OUStringBuffer aNewPath;
	aNewPath.append(m_aAbsURIRef.getStr() + m_aPath.getBegin(),
					   aSegment.getBegin() - m_aPath.getBegin());
	if (bIgnoreFinalSlash && aSegment.getEnd() == m_aPath.getEnd())
		aNewPath.append(sal_Unicode('/'));
	else
		aNewPath.append(m_aAbsURIRef.getStr() + aSegment.getEnd(),
						m_aPath.getEnd() - aSegment.getEnd());
	if (aNewPath.getLength() == 0 && !aSegment.isEmpty() &&
        m_aAbsURIRef[aSegment.getBegin()] == '/')
    {
		aNewPath.append(sal_Unicode('/'));
    }

	return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, 
		RTL_TEXTENCODING_UTF8);
}

//============================================================================
rtl::OUString INetURLObject::getName(sal_Int32 nIndex, bool bIgnoreFinalSlash,
								 DecodeMechanism eMechanism,
								 rtl_TextEncoding eCharset) const
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return rtl::OUString();

	sal_Unicode const * pSegBegin
		= m_aAbsURIRef.getStr() + aSegment.getBegin();
	sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();

    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
	sal_Unicode const * p = pSegBegin;
	while (p != pSegEnd && *p != ';')
		++p;

	return decode(pSegBegin, p, getEscapePrefix(), eMechanism, eCharset);
}

//============================================================================
bool INetURLObject::setName(rtl::OUString const & rTheName, sal_Int32 nIndex,
							bool bIgnoreFinalSlash,
							EncodeMechanism eMechanism,
							rtl_TextEncoding eCharset)
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return false;

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
	sal_Unicode const * pSegBegin
		= m_aAbsURIRef.getStr() + aSegment.getBegin();
	sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();

    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
	sal_Unicode const * p = pSegBegin;
	while (p != pSegEnd && *p != ';')
		++p;

	rtl::OUStringBuffer aNewPath;
	aNewPath.append(pPathBegin, pSegBegin - pPathBegin);
	aNewPath.append(encodeText(rTheName, false, PART_PCHAR, getEscapePrefix(),
		eMechanism, eCharset, true));
	aNewPath.append(p, pPathEnd - p);

	return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, 
		RTL_TEXTENCODING_UTF8);
}

//============================================================================
bool INetURLObject::hasExtension(sal_Int32 nIndex, bool bIgnoreFinalSlash)
	const
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return false;

	sal_Unicode const * pSegBegin
		= m_aAbsURIRef.getStr() + aSegment.getBegin();
	sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();

    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
	for (sal_Unicode const * p = pSegBegin; p != pSegEnd && *p != ';'; ++p)
		if (*p == '.' && p != pSegBegin)
			return true;
	return false;
}

//============================================================================
rtl::OUString INetURLObject::getBase(sal_Int32 nIndex, bool bIgnoreFinalSlash,
								 DecodeMechanism eMechanism,
								 rtl_TextEncoding eCharset) const
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return rtl::OUString();

	sal_Unicode const * pSegBegin
		= m_aAbsURIRef.getStr() + aSegment.getBegin();
	sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();

    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
	sal_Unicode const * pExtension = 0;
	sal_Unicode const * p = pSegBegin;
	for (; p != pSegEnd && *p != ';'; ++p)
		if (*p == '.' && p != pSegBegin)
			pExtension = p;
	if (!pExtension)
		pExtension = p;

	return decode(pSegBegin, pExtension, getEscapePrefix(), eMechanism,
				  eCharset);
}

//============================================================================
bool INetURLObject::setBase(rtl::OUString const & rTheBase, sal_Int32 nIndex,
							bool bIgnoreFinalSlash,
							EncodeMechanism eMechanism,
							rtl_TextEncoding eCharset)
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return false;

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
	sal_Unicode const * pSegBegin
		= m_aAbsURIRef.getStr() + aSegment.getBegin();
	sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();

    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
	sal_Unicode const * pExtension = 0;
	sal_Unicode const * p = pSegBegin;
	for (; p != pSegEnd && *p != ';'; ++p)
		if (*p == '.' && p != pSegBegin)
			pExtension = p;
	if (!pExtension)
		pExtension = p;

	rtl::OUStringBuffer aNewPath;
	aNewPath.append(pPathBegin, pSegBegin - pPathBegin);
	aNewPath.append(encodeText(rTheBase, false, PART_PCHAR, getEscapePrefix(),
		eMechanism, eCharset, true));
	aNewPath.append(pExtension, pPathEnd - pExtension);

	return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, 
		RTL_TEXTENCODING_UTF8);
}

//============================================================================
rtl::OUString INetURLObject::getExtension(sal_Int32 nIndex,
									  bool bIgnoreFinalSlash,
									  DecodeMechanism eMechanism,
									  rtl_TextEncoding eCharset) const
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return rtl::OUString();

	sal_Unicode const * pSegBegin
		= m_aAbsURIRef.getStr() + aSegment.getBegin();
	sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();

    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
	sal_Unicode const * pExtension = 0;
	sal_Unicode const * p = pSegBegin;
	for (; p != pSegEnd && *p != ';'; ++p)
		if (*p == '.' && p != pSegBegin)
			pExtension = p;

	if (!pExtension)
		return rtl::OUString();

	return decode(pExtension + 1, p, getEscapePrefix(), eMechanism, eCharset);
}

//============================================================================
bool INetURLObject::setExtension(rtl::OUString const & rTheExtension,
								 sal_Int32 nIndex, bool bIgnoreFinalSlash,
								 EncodeMechanism eMechanism,
								 rtl_TextEncoding eCharset)
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return false;

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
	sal_Unicode const * pSegBegin
		= m_aAbsURIRef.getStr() + aSegment.getBegin();
	sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();

    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
	sal_Unicode const * pExtension = 0;
	sal_Unicode const * p = pSegBegin;
	for (; p != pSegEnd && *p != ';'; ++p)
		if (*p == '.' && p != pSegBegin)
			pExtension = p;
	if (!pExtension)
		pExtension = p;

	rtl::OUStringBuffer aNewPath;
	aNewPath.append(pPathBegin, pExtension - pPathBegin);
	aNewPath.append(sal_Unicode('.'));
	aNewPath.append(encodeText(rTheExtension, false, PART_PCHAR,
		getEscapePrefix(), eMechanism, eCharset, true));
	aNewPath.append(p, pPathEnd - p);

	return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, 
		RTL_TEXTENCODING_UTF8);
}

//============================================================================
bool INetURLObject::removeExtension(sal_Int32 nIndex, bool bIgnoreFinalSlash)
{
	SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
	if (!aSegment.isPresent())
		return false;

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
	sal_Unicode const * pSegBegin
		= m_aAbsURIRef.getStr() + aSegment.getBegin();
	sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();

    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
	sal_Unicode const * pExtension = 0;
	sal_Unicode const * p = pSegBegin;
	for (; p != pSegEnd && *p != ';'; ++p)
		if (*p == '.' && p != pSegBegin)
			pExtension = p;
	if (!pExtension)
		return true;

	rtl::OUStringBuffer aNewPath;
	aNewPath.append(pPathBegin, pExtension - pPathBegin);
	aNewPath.append(p, pPathEnd - p);

	return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, 
		RTL_TEXTENCODING_UTF8);
}

//============================================================================
bool INetURLObject::hasFinalSlash() const
{
	if (!checkHierarchical())
		return false;

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    return pPathEnd > pPathBegin && pPathEnd[-1] == '/';
}

//============================================================================
bool INetURLObject::setFinalSlash()
{
	if (!checkHierarchical())
		return false;

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
	if (pPathEnd > pPathBegin && pPathEnd[-1] == '/')
		return true;

	rtl::OUStringBuffer aNewPath;
	aNewPath.append(pPathBegin, pPathEnd - pPathBegin);
	aNewPath.append(sal_Unicode('/'));

	return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, 
		RTL_TEXTENCODING_UTF8);
}

//============================================================================
bool INetURLObject::removeFinalSlash()
{
	if (!checkHierarchical())
		return false;

	sal_Unicode const * pPathBegin
		= m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
	if (pPathEnd <= pPathBegin || pPathEnd[-1] != '/')
		return true;

	--pPathEnd;
	if (pPathEnd == pPathBegin && *pPathBegin == '/')
		return false;
	rtl::OUString aNewPath(pPathBegin, pPathEnd - pPathBegin);

	return setPath(aNewPath, false, NOT_CANONIC, RTL_TEXTENCODING_UTF8);
}

//============================================================================
// static
rtl::OUString INetURLObject::createFragment(rtl::OUString const & rText)
{
	rtl::OUString aFragment(rText);
	for (sal_Int32 i = 0; i < aFragment.getLength();)
	{
		sal_Unicode c = aFragment.getStr()[i];
		if (mustEncode(c, PART_CREATEFRAGMENT))
			aFragment = aFragment.replaceAt(i, 1, rtl::OUString());
		else
			++i;
	}
	return aFragment;
}

//============================================================================
bool INetURLObject::setFSysPath(rtl::OUString const & rFSysPath, 
	FSysStyle eStyle)
{
	sal_Unicode const * pFSysBegin = rFSysPath.getStr();
	sal_Unicode const * pFSysEnd = pFSysBegin + rFSysPath.getLength();

	switch ((eStyle & FSYS_VOS ? 1 : 0)
			    + (eStyle & FSYS_UNX ? 1 : 0)
			    + (eStyle & FSYS_DOS ? 1 : 0)
			    + (eStyle & FSYS_MAC ? 1 : 0))
	{
		case 0:
			return false;

		case 1:
			break;

		default:
			if (eStyle & FSYS_VOS
				&& pFSysEnd - pFSysBegin >= 2
				&& pFSysBegin[0] == '/'
				&& pFSysBegin[1] == '/')
			{
				if (pFSysEnd - pFSysBegin >= 3
					&& pFSysBegin[2] == '.'
					&& (pFSysEnd - pFSysBegin == 3 || pFSysBegin[3] == '/'))
				{
					eStyle = FSYS_VOS; // Production T1
					break;
				}

				sal_Unicode const * p = pFSysBegin + 2;
				rtl::OUString aHost;
				if (parseHost(p, pFSysEnd, aHost)
					&& (p == pFSysEnd || *p == '/'))
				{
					eStyle = FSYS_VOS; // Production T2
					break;
				}
			}

			if (eStyle & FSYS_DOS
				&& pFSysEnd - pFSysBegin >= 2
				&& pFSysBegin[0] == '\\'
				&& pFSysBegin[1] == '\\')
			{
				sal_Unicode const * p = pFSysBegin + 2;
				rtl::OUString aHost;
				if (parseHost(p, pFSysEnd, aHost)
					&& (p == pFSysEnd || *p == '\\'))
				{
					eStyle = FSYS_DOS; // Production T3
					break;
				}
			}

			if (eStyle & FSYS_DOS
				&& pFSysEnd - pFSysBegin >= 2
				&& INetMIME::isAlpha(pFSysBegin[0])
				&& pFSysBegin[1] == ':'
				&& (pFSysEnd - pFSysBegin == 2
                    || pFSysBegin[2] == '/'
                    || pFSysBegin[2] == '\\'))
			{
				eStyle = FSYS_DOS; // Productions T4, T5
				break;
			}

			if (!(eStyle & (FSYS_UNX | FSYS_DOS | FSYS_MAC)))
				return false;

			eStyle = guessFSysStyleByCounting(pFSysBegin, pFSysEnd, eStyle);
			    // Production T6
			break;
	}

	rtl::OUStringBuffer aSynAbsURIRef(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("file://")));

	switch (eStyle)
	{
		case FSYS_VOS:
		{
			sal_Unicode const * p = pFSysBegin;
			if (pFSysEnd - p < 2 || *p++ != '/' || *p++ != '/')
				return false;
			if (p != pFSysEnd && *p == '.'
				&& (pFSysEnd - p == 1 || p[1] == '/'))
				++p;
			for (; p != pFSysEnd; ++p)
				switch (*p)
				{
					case '#':
					case '%':
						appendEscape(aSynAbsURIRef, '%', *p);
						break;

					default:
						aSynAbsURIRef.append(*p);
						break;
				}
			break;
		}

		case FSYS_UNX:
		{
			sal_Unicode const * p = pFSysBegin;
			if (p != pFSysEnd && *p != '/')
				return false;
			for (; p != pFSysEnd; ++p)
				switch (*p)
				{
					case '|':
					case '#':
					case '%':
						appendEscape(aSynAbsURIRef, '%', *p);
						break;

					default:
						aSynAbsURIRef.append(*p);
						break;
				}
			break;
		}

		case FSYS_DOS:
		{
			sal_uInt32 nAltDelimiter = 0x80000000;
			sal_Unicode const * p = pFSysBegin;
			if (pFSysEnd - p >= 3 && p[0] == '\\' && p[1] == '\\')
				p += 2;
			else
			{
				aSynAbsURIRef.append(sal_Unicode('/'));
				if (pFSysEnd - p >= 2
                    && INetMIME::isAlpha(p[0])
					&& p[1] == ':'
                    && (pFSysEnd - p == 2 || p[2] == '\\' || p[2] == '/'))
					nAltDelimiter = '/';
			}
			for (; p != pFSysEnd; ++p)
				if (*p == '\\' || *p == nAltDelimiter)
					aSynAbsURIRef.append(sal_Unicode('/'));
				else
					switch (*p)
					{
						case '/':
						case '#':
						case '%':
							appendEscape(aSynAbsURIRef, '%', *p);
							break;

						default:
							aSynAbsURIRef.append(*p);
							break;
					}
			break;
		}

		case FSYS_MAC:
			aSynAbsURIRef.append(sal_Unicode('/'));
			{for (sal_Unicode const * p = pFSysBegin; p != pFSysEnd; ++p)
				switch (*p)
				{
					case ':':
						aSynAbsURIRef.append(sal_Unicode('/'));
						break;

					case '/':
					case '|':
					case '#':
					case '%':
						appendEscape(aSynAbsURIRef, '%', *p);
						break;

					default:
						aSynAbsURIRef.append(*p);
						break;
				}
			}
			break;

        default:
            OSL_ASSERT(false);
            break;
	}

	INetURLObject aTemp(aSynAbsURIRef.makeStringAndClear(), WAS_ENCODED, 
		RTL_TEXTENCODING_UTF8);
	if (aTemp.HasError())
		return false;

	*this = aTemp;
	return true;
}

//============================================================================
rtl::OUString INetURLObject::getFSysPath(FSysStyle eStyle,
									 sal_Unicode * pDelimiter) const
{
	if (m_eScheme != INET_PROT_FILE)
		return rtl::OUString();

	if ((eStyle & FSYS_VOS ? 1 : 0)
		        + (eStyle & FSYS_UNX ? 1 : 0)
			    + (eStyle & FSYS_DOS ? 1 : 0)
		        + (eStyle & FSYS_MAC ? 1 : 0)
		    > 1)
	{
		eStyle = eStyle & FSYS_VOS
			     && m_aHost.isPresent()
			     && m_aHost.getLength() > 0 ?
			         FSYS_VOS :
				 hasDosVolume(eStyle)
                 || ((eStyle & FSYS_DOS) != 0
                    && m_aHost.isPresent()
                    && m_aHost.getLength() > 0) ?
				     FSYS_DOS :
			     eStyle & FSYS_UNX
			     && (!m_aHost.isPresent() || m_aHost.getLength() == 0) ?
			         FSYS_UNX :
			         FSysStyle(0);
	}

	switch (eStyle)
	{
		case FSYS_VOS:
		{
			if (pDelimiter)
				*pDelimiter = '/';

			rtl::OUStringBuffer aSynFSysPath;
			aSynFSysPath.appendAscii(RTL_CONSTASCII_STRINGPARAM("//"));
			if (m_aHost.isPresent() && m_aHost.getLength() > 0)
				aSynFSysPath.append(decode(m_aHost, '%', DECODE_WITH_CHARSET,
									   RTL_TEXTENCODING_UTF8));
			else
				aSynFSysPath.append(sal_Unicode('.'));
			aSynFSysPath.append(decode(m_aPath, '%', DECODE_WITH_CHARSET,
								   RTL_TEXTENCODING_UTF8));
			return aSynFSysPath.makeStringAndClear();
		}

		case FSYS_UNX:
		{
			if (m_aHost.isPresent() && m_aHost.getLength() > 0)
				return rtl::OUString();

			if (pDelimiter)
				*pDelimiter = '/';

			return decode(m_aPath, '%', DECODE_WITH_CHARSET,
						  RTL_TEXTENCODING_UTF8);
		}

		case FSYS_DOS:
		{
			if (pDelimiter)
				*pDelimiter = '\\';

			rtl::OUStringBuffer aSynFSysPath;
			if (m_aHost.isPresent() && m_aHost.getLength() > 0)
			{
				aSynFSysPath.appendAscii(RTL_CONSTASCII_STRINGPARAM("\\\\"));
				aSynFSysPath.append(decode(m_aHost, '%', DECODE_WITH_CHARSET,
									   RTL_TEXTENCODING_UTF8));
				aSynFSysPath.append(sal_Unicode('\\'));
			}
			sal_Unicode const * p
				= m_aAbsURIRef.getStr() + m_aPath.getBegin();
			sal_Unicode const * pEnd = p + m_aPath.getLength();
			DBG_ASSERT(p < pEnd && *p == '/',
					   "INetURLObject::getFSysPath(): Bad path");
			++p;
			while (p < pEnd)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(p, pEnd, false, '%', WAS_ENCODED,
											 RTL_TEXTENCODING_UTF8,
											 eEscapeType);
				if (eEscapeType == ESCAPE_NO && nUTF32 == '/')
					aSynFSysPath.append(sal_Unicode('\\'));
				else
					aSynFSysPath.appendUtf32(nUTF32);
			}
			return aSynFSysPath.makeStringAndClear();
		}

		case FSYS_MAC:
		{
			if (m_aHost.isPresent() && m_aHost.getLength() > 0)
				return rtl::OUString();

			if (pDelimiter)
				*pDelimiter = ':';

			rtl::OUStringBuffer aSynFSysPath;
			sal_Unicode const * p
				= m_aAbsURIRef.getStr() + m_aPath.getBegin();
			sal_Unicode const * pEnd = p + m_aPath.getLength();
			DBG_ASSERT(p < pEnd && *p == '/',
					   "INetURLObject::getFSysPath(): Bad path");
			++p;
			while (p < pEnd)
			{
				EscapeType eEscapeType;
				sal_uInt32 nUTF32 = getUTF32(p, pEnd, false, '%', WAS_ENCODED,
											 RTL_TEXTENCODING_UTF8,
											 eEscapeType);
				if (eEscapeType == ESCAPE_NO && nUTF32 == '/')
					aSynFSysPath.append(sal_Unicode(':'));
				else
					aSynFSysPath.appendUtf32(nUTF32);
			}
			return aSynFSysPath.makeStringAndClear();
		}

        default:
            return rtl::OUString();
	}
}

//============================================================================
bool INetURLObject::HasMsgId() const
{
	if (m_eScheme != INET_PROT_POP3)
		return false;
	sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pEnd = p + m_aPath.getLength();
	for (; p < pEnd; ++p)
		if (*p == '<')
			return true;
	return false;
}

//============================================================================
rtl::OUString INetURLObject::GetMsgId(DecodeMechanism eMechanism,
								  rtl_TextEncoding eCharset) const
{
	if (m_eScheme != INET_PROT_POP3)
		return rtl::OUString();
	sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin();
	sal_Unicode const * pEnd = p + m_aPath.getLength();
	for (; p < pEnd; ++p)
		if (*p == '<')
			return decode(p, pEnd, getEscapePrefix(), eMechanism, eCharset);
	return rtl::OUString();
}

//============================================================================
// static
void INetURLObject::appendUCS4Escape(rtl::OUStringBuffer & rTheText,
									 sal_Char cEscapePrefix, sal_uInt32 nUCS4)
{
	DBG_ASSERT(nUCS4 < 0x80000000,
			   "INetURLObject::appendUCS4Escape(): Bad char");
	if (nUCS4 < 0x80)
		appendEscape(rTheText, cEscapePrefix, nUCS4);
	else if (nUCS4 < 0x800)
	{
		appendEscape(rTheText, cEscapePrefix, nUCS4 >> 6 | 0xC0);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80);
	}
	else if (nUCS4 < 0x10000)
	{
		appendEscape(rTheText, cEscapePrefix, nUCS4 >> 12 | 0xE0);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 6 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80);
	}
	else if (nUCS4 < 0x200000)
	{
		appendEscape(rTheText, cEscapePrefix, nUCS4 >> 18 | 0xF0);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 12 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 6 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80);
	}
	else if (nUCS4 < 0x4000000)
	{
		appendEscape(rTheText, cEscapePrefix, nUCS4 >> 24 | 0xF8);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 18 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 12 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 6 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80);
	}
	else
	{
		appendEscape(rTheText, cEscapePrefix, nUCS4 >> 30 | 0xFC);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 24 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 18 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 12 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 6 & 0x3F) | 0x80);
		appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80);
	}
}

//============================================================================
// static
void INetURLObject::appendUCS4(rtl::OUStringBuffer& rTheText, sal_uInt32 nUCS4,
							   EscapeType eEscapeType, bool bOctets,
							   Part ePart, sal_Char cEscapePrefix,
							   rtl_TextEncoding eCharset,
							   bool bKeepVisibleEscapes)
{
	bool bEscape;
	rtl_TextEncoding eTargetCharset = RTL_TEXTENCODING_DONTKNOW;
	switch (eEscapeType)
	{
		case ESCAPE_NO:
			if (mustEncode(nUCS4, ePart))
			{
				bEscape = true;
				eTargetCharset = bOctets ? RTL_TEXTENCODING_ISO_8859_1 :
										   RTL_TEXTENCODING_UTF8;
			}
			else
				bEscape = false;
			break;

		case ESCAPE_OCTET:
			bEscape = true;
			eTargetCharset = RTL_TEXTENCODING_ISO_8859_1;
			break;

		case ESCAPE_UTF32:
			if (mustEncode(nUCS4, ePart))
			{
				bEscape = true;
				eTargetCharset = eCharset;
			}
			else if (bKeepVisibleEscapes && INetMIME::isVisible(nUCS4))
			{
				bEscape = true;
				eTargetCharset = RTL_TEXTENCODING_ASCII_US;
			}
			else
				bEscape = false;
			break;
		default:
			bEscape = false;
	}

	if (bEscape)
	{
		switch (eTargetCharset)
		{
			default:
				DBG_ERROR("INetURLObject::appendUCS4(): Unsupported charset");
			case RTL_TEXTENCODING_ASCII_US:
			case RTL_TEXTENCODING_ISO_8859_1:
				appendEscape(rTheText, cEscapePrefix, nUCS4);
				break;

			case RTL_TEXTENCODING_UTF8:
				appendUCS4Escape(rTheText, cEscapePrefix, nUCS4);
				break;
		}
	}
	else
		rTheText.append(sal_Unicode(nUCS4));
}

//============================================================================
// static
sal_uInt32 INetURLObject::getUTF32(sal_Unicode const *& rBegin,
								   sal_Unicode const * pEnd, bool bOctets,
								   sal_Char cEscapePrefix,
								   EncodeMechanism eMechanism,
								   rtl_TextEncoding eCharset,
								   EscapeType & rEscapeType)
{
	DBG_ASSERT(rBegin < pEnd, "INetURLObject::getUTF32(): Bad sequence");
	sal_uInt32 nUTF32 = bOctets ? *rBegin++ :
								  INetMIME::getUTF32Character(rBegin, pEnd);
	switch (eMechanism)
	{
		case ENCODE_ALL:
			rEscapeType = ESCAPE_NO;
			break;

		case WAS_ENCODED:
		{
			int nWeight1;
			int nWeight2;
			if (nUTF32 == sal_uChar(cEscapePrefix) && rBegin + 1 < pEnd
				&& (nWeight1 = INetMIME::getHexWeight(rBegin[0])) >= 0
				&& (nWeight2 = INetMIME::getHexWeight(rBegin[1])) >= 0)
			{
				rBegin += 2;
				nUTF32 = nWeight1 << 4 | nWeight2;
				switch (eCharset)
				{
					default:
						DBG_ERROR(
							"INetURLObject::getUTF32(): Unsupported charset");
					case RTL_TEXTENCODING_ASCII_US:
						rEscapeType = INetMIME::isUSASCII(nUTF32) ?
										  ESCAPE_UTF32 : ESCAPE_OCTET;
						break;

					case RTL_TEXTENCODING_ISO_8859_1:
						rEscapeType = ESCAPE_UTF32;
						break;

					case RTL_TEXTENCODING_UTF8:
						if (INetMIME::isUSASCII(nUTF32))
							rEscapeType = ESCAPE_UTF32;
						else
						{
							if (nUTF32 >= 0xC0 && nUTF32 <= 0xF4)
							{
								sal_uInt32 nEncoded;
								int nShift;
								sal_uInt32 nMin;
								if (nUTF32 <= 0xDF)
								{
									nEncoded = (nUTF32 & 0x1F) << 6;
									nShift = 0;
									nMin = 0x80;
								}
								else if (nUTF32 <= 0xEF)
								{
									nEncoded = (nUTF32 & 0x0F) << 12;
									nShift = 6;
									nMin = 0x800;
								}
								else
								{
									nEncoded = (nUTF32 & 0x07) << 18;
									nShift = 12;
									nMin = 0x10000;
								}
								sal_Unicode const * p = rBegin;
								bool bUTF8 = true;
								for (;;)
								{
									if (pEnd - p < 3
										|| p[0] != cEscapePrefix
										|| (nWeight1
											   = INetMIME::getHexWeight(p[1]))
											   < 8
										|| nWeight1 > 11
										|| (nWeight2
											   = INetMIME::getHexWeight(p[2]))
										       < 0)
									{
										bUTF8 = false;
										break;
									}
									p += 3;
									nEncoded
										|= ((nWeight1 & 3) << 4 | nWeight2)
											   << nShift;
									if (nShift == 0)
										break;
									nShift -= 6;
								}
								if (bUTF8 && nEncoded >= nMin
									&& !INetMIME::isHighSurrogate(nEncoded)
									&& !INetMIME::isLowSurrogate(nEncoded)
									&& nEncoded <= 0x10FFFF)
								{
									rBegin = p;
									nUTF32 = nEncoded;
									rEscapeType = ESCAPE_UTF32;
									break;
								}
							}
							rEscapeType = ESCAPE_OCTET;
						}
						break;
				}
			}
			else
				rEscapeType = ESCAPE_NO;
			break;
		}

		case NOT_CANONIC:
		{
			int nWeight1;
			int nWeight2;
			if (nUTF32 == sal_uChar(cEscapePrefix) && rBegin + 1 < pEnd
				&& ((nWeight1 = INetMIME::getHexWeight(rBegin[0])) >= 0)
				&& ((nWeight2 = INetMIME::getHexWeight(rBegin[1])) >= 0))
			{
				rBegin += 2;
				nUTF32 = nWeight1 << 4 | nWeight2;
				rEscapeType = ESCAPE_OCTET;
			}
			else
				rEscapeType = ESCAPE_NO;
			break;
		}
	}
	return nUTF32;
}

//============================================================================
// static
sal_uInt32 INetURLObject::scanDomain(sal_Unicode const *& rBegin,
									 sal_Unicode const * pEnd,
									 bool bEager)
{
	enum State { STATE_DOT, STATE_LABEL, STATE_HYPHEN };
	State eState = STATE_DOT;
	sal_Int32 nLabels = 0;
	sal_Unicode const * pLastAlphanumeric = 0;
	for (sal_Unicode const * p = rBegin;; ++p)
		switch (eState)
		{
			case STATE_DOT:
				if (p != pEnd && INetMIME::isAlphanumeric(*p))
				{
					++nLabels;
					eState = STATE_LABEL;
					break;
				}
				if (bEager || nLabels == 0)
					return 0;
				rBegin = p - 1;
				return nLabels;

			case STATE_LABEL:
				if (p != pEnd)
                {
					if (INetMIME::isAlphanumeric(*p))
						break;
					else if (*p == '.')
					{
						eState = STATE_DOT;
						break;
					}
					else if (*p == '-')
					{
						pLastAlphanumeric = p;
						eState = STATE_HYPHEN;
						break;
					}
                }
				rBegin = p;
				return nLabels;

			case STATE_HYPHEN:
				if (p != pEnd)
                {
					if (INetMIME::isAlphanumeric(*p))
					{
						eState = STATE_LABEL;
						break;
					}
					else if (*p == '-')
						break;
                }
				if (bEager)
					return 0;
				rBegin = pLastAlphanumeric;
				return nLabels;
		}
}

//============================================================================
// static
bool INetURLObject::scanIPv6reference(sal_Unicode const *& rBegin,
									  sal_Unicode const * pEnd)
{
    if (rBegin != pEnd && *rBegin == '[') {
        sal_Unicode const * p = rBegin + 1;
        //TODO: check for valid IPv6address (RFC 2373):
        while (p != pEnd && (INetMIME::isHexDigit(*p) || *p == ':' || *p == '.'))
        {
            ++p;
        }
        if (p != pEnd && *p == ']') {
            rBegin = p + 1;
            return true;
        }
    }
    return false;
}

//============================================================================
rtl::OUString INetURLObject::GetPartBeforeLastName(DecodeMechanism eMechanism,
											   rtl_TextEncoding eCharset)
	const
{
	if (!checkHierarchical())
		return rtl::OUString();
	INetURLObject aTemp(*this);
	aTemp.clearFragment();
	aTemp.clearQuery();
	aTemp.removeSegment(LAST_SEGMENT, false);
	aTemp.setFinalSlash();
	return aTemp.GetMainURL(eMechanism, eCharset);
}

//============================================================================
rtl::OUString INetURLObject::GetLastName(DecodeMechanism eMechanism,
									 rtl_TextEncoding eCharset) const
{
	return getName(LAST_SEGMENT, true, eMechanism, eCharset);
}

//============================================================================
rtl::OUString INetURLObject::GetFileExtension(DecodeMechanism eMechanism,
										  rtl_TextEncoding eCharset) const
{
	return getExtension(LAST_SEGMENT, false, eMechanism, eCharset);
}

//============================================================================
bool INetURLObject::CutLastName()
{
	INetURLObject aTemp(*this);
	aTemp.clearFragment();
	aTemp.clearQuery();
	if (!aTemp.removeSegment(LAST_SEGMENT, false))
		return false;
	*this = aTemp;
	return true;
}

//============================================================================
rtl::OUString INetURLObject::PathToFileName() const
{
	if (m_eScheme != INET_PROT_FILE)
		return rtl::OUString();
	rtl::OUString aSystemPath;
	if (osl::FileBase::getSystemPathFromFileURL(
				decode(m_aAbsURIRef.getStr(),
					   m_aAbsURIRef.getStr() + m_aPath.getEnd(),
					   getEscapePrefix(), NO_DECODE, RTL_TEXTENCODING_UTF8),
				aSystemPath)
			!= osl::FileBase::E_None)
		return rtl::OUString();
    return aSystemPath;
}

//============================================================================
rtl::OUString INetURLObject::GetFull() const
{
	INetURLObject aTemp(*this);
	aTemp.removeFinalSlash();
	return aTemp.PathToFileName();
}

//============================================================================
rtl::OUString INetURLObject::GetPath() const
{
	INetURLObject aTemp(*this);
	aTemp.removeSegment(LAST_SEGMENT, true);
	aTemp.removeFinalSlash();
	return aTemp.PathToFileName();
}

//============================================================================
void INetURLObject::SetBase(rtl::OUString const & rTheBase)
{
	setBase(rTheBase, LAST_SEGMENT, true, ENCODE_ALL);
}

//============================================================================
rtl::OUString INetURLObject::GetBase() const
{
	return getBase(LAST_SEGMENT, true, DECODE_WITH_CHARSET);
}

//============================================================================
void INetURLObject::SetName(rtl::OUString const & rTheName,
							EncodeMechanism eMechanism,
							rtl_TextEncoding eCharset)
{
	INetURLObject aTemp(*this);
	if (aTemp.removeSegment(LAST_SEGMENT, true)
		&& aTemp.insertName(rTheName, false, LAST_SEGMENT, true, eMechanism,
							eCharset))
		*this = aTemp;
}

//============================================================================
rtl::OUString INetURLObject::CutName(DecodeMechanism eMechanism,
								 rtl_TextEncoding eCharset)
{
	rtl::OUString aTheName(getName(LAST_SEGMENT, true, eMechanism, eCharset));
	return removeSegment(LAST_SEGMENT, true) ? aTheName : rtl::OUString();
}

//============================================================================
void INetURLObject::SetExtension(rtl::OUString const & rTheExtension,
								 EncodeMechanism eMechanism,
								 rtl_TextEncoding eCharset)
{
	setExtension(rTheExtension, LAST_SEGMENT, false, eMechanism, eCharset);
}

//============================================================================
rtl::OUString INetURLObject::CutExtension(DecodeMechanism eMechanism,
									  rtl_TextEncoding eCharset)
{
	rtl::OUString aTheExtension(getExtension(LAST_SEGMENT, false, eMechanism,
										 eCharset));
	return removeExtension(LAST_SEGMENT, false) 
		? aTheExtension : rtl::OUString();
}

//============================================================================
bool INetURLObject::IsCaseSensitive() const
{
	return true;
}
