Back to index

lightning-sunbird  0.9+nobinonly
nsStandardURL.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim:set ts=4 sw=4 sts=4 et cindent: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Darin Fisher <darin@netscape.com> (original author)
00025  *   Andreas Otte <andreas.otte@debitel.net>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsStandardURL.h"
00042 #include "nsDependentSubstring.h"
00043 #include "nsReadableUtils.h"
00044 #include "nsCRT.h"
00045 #include "nsEscape.h"
00046 #include "nsILocalFile.h"
00047 #include "nsIObjectInputStream.h"
00048 #include "nsIObjectOutputStream.h"
00049 #include "nsICharsetConverterManager.h"
00050 #include "nsIPrefService.h"
00051 #include "nsIPrefBranch.h"
00052 #include "nsIPrefBranch2.h"
00053 #include "nsIIDNService.h"
00054 #include "nsNetUtil.h"
00055 #include "prlog.h"
00056 #include "nsAutoPtr.h"
00057 
00058 static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID);
00059 static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
00060 
00061 nsIIDNService *nsStandardURL::gIDN = nsnull;
00062 nsICharsetConverterManager *nsStandardURL::gCharsetMgr = nsnull;
00063 PRBool nsStandardURL::gInitialized = PR_FALSE;
00064 PRBool nsStandardURL::gEscapeUTF8 = PR_TRUE;
00065 PRBool nsStandardURL::gAlwaysEncodeInUTF8 = PR_TRUE;
00066 PRBool nsStandardURL::gShowPunycode = PR_FALSE;
00067 nsIPrefBranch *nsStandardURL::gIDNWhitelistPrefBranch = nsnull;
00068 
00069 #if defined(PR_LOGGING)
00070 //
00071 // setenv NSPR_LOG_MODULES nsStandardURL:5
00072 //
00073 static PRLogModuleInfo *gStandardURLLog;
00074 #endif
00075 #define LOG(args)     PR_LOG(gStandardURLLog, PR_LOG_DEBUG, args)
00076 #define LOG_ENABLED() PR_LOG_TEST(gStandardURLLog, PR_LOG_DEBUG)
00077 
00078 //----------------------------------------------------------------------------
00079 
00080 #define ENSURE_MUTABLE() \
00081   PR_BEGIN_MACRO \
00082     if (!mMutable) { \
00083         NS_ERROR("attempt to modify an immutable nsStandardURL"); \
00084         return NS_ERROR_ABORT; \
00085     } \
00086   PR_END_MACRO
00087 
00088 //----------------------------------------------------------------------------
00089 
00090 static nsresult
00091 EncodeString(nsIUnicodeEncoder *encoder, const nsAFlatString &str, nsACString &result)
00092 {
00093     nsresult rv;
00094     PRInt32 len = str.Length();
00095     PRInt32 maxlen;
00096 
00097     rv = encoder->GetMaxLength(str.get(), len, &maxlen);
00098     if (NS_FAILED(rv))
00099         return rv;
00100 
00101     char buf[256], *p = buf;
00102     if (PRUint32(maxlen) > sizeof(buf) - 1) {
00103         p = (char *) malloc(maxlen + 1);
00104         if (!p)
00105             return NS_ERROR_OUT_OF_MEMORY;
00106     }
00107 
00108     rv = encoder->Convert(str.get(), &len, p, &maxlen);
00109     if (NS_FAILED(rv))
00110         goto end;
00111     if (rv == NS_ERROR_UENC_NOMAPPING) {
00112         NS_WARNING("unicode conversion failed");
00113         rv = NS_ERROR_UNEXPECTED;
00114         goto end;
00115     }
00116     p[maxlen] = 0;
00117     result = p;
00118 
00119     rv = encoder->Finish(p, &len);
00120     if (NS_FAILED(rv))
00121         goto end;
00122     p[len] = 0;
00123     result += p;
00124 
00125 end:
00126     encoder->Reset();
00127 
00128     if (p != buf)
00129         free(p);
00130     return rv;
00131 }
00132 
00133 //----------------------------------------------------------------------------
00134 // nsStandardURL::nsPrefObserver
00135 //----------------------------------------------------------------------------
00136 
00137 #define NS_NET_PREF_ESCAPEUTF8         "network.standard-url.escape-utf8"
00138 #define NS_NET_PREF_ENABLEIDN          "network.enableIDN"
00139 #define NS_NET_PREF_ALWAYSENCODEINUTF8 "network.standard-url.encode-utf8"
00140 #define NS_NET_PREF_SHOWPUNYCODE       "network.IDN_show_punycode"
00141 #define NS_NET_PREF_IDNWHITELIST       "network.IDN.whitelist."
00142 
00143 NS_IMPL_ISUPPORTS1(nsStandardURL::nsPrefObserver, nsIObserver)
00144 
00145 NS_IMETHODIMP nsStandardURL::
00146 nsPrefObserver::Observe(nsISupports *subject,
00147                         const char *topic,
00148                         const PRUnichar *data)
00149 {
00150     if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
00151         nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(subject) );
00152         if (prefBranch) {
00153             PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
00154         } 
00155     }
00156     return NS_OK;
00157 }
00158 
00159 //----------------------------------------------------------------------------
00160 // nsStandardURL::nsSegmentEncoder
00161 //----------------------------------------------------------------------------
00162 
00163 nsStandardURL::
00164 nsSegmentEncoder::nsSegmentEncoder(const char *charset)
00165     : mCharset(charset)
00166 {
00167 }
00168 
00169 PRInt32 nsStandardURL::
00170 nsSegmentEncoder::EncodeSegmentCount(const char *str,
00171                                      const URLSegment &seg,
00172                                      PRInt16 mask,
00173                                      nsAFlatCString &result,
00174                                      PRBool &appended)
00175 {
00176     appended = PR_FALSE;
00177     if (!str)
00178         return 0;
00179     PRInt32 len = 0;
00180     if (seg.mLen > 0) {
00181         PRUint32 pos = seg.mPos;
00182         len = seg.mLen;
00183 
00184         // first honor the origin charset if appropriate. as an optimization,
00185         // only do this if the segment is non-ASCII.  Further, if mCharset is
00186         // null or the empty string then the origin charset is UTF-8 and there
00187         // is nothing to do.
00188         nsCAutoString encBuf;
00189         if (mCharset && *mCharset && !nsCRT::IsAscii(str + pos, len)) {
00190             // we have to encode this segment
00191             if (mEncoder || InitUnicodeEncoder()) {
00192                 NS_ConvertUTF8toUCS2 ucsBuf(Substring(str + pos, str + pos + len));
00193                 if (NS_SUCCEEDED(EncodeString(mEncoder, ucsBuf, encBuf))) {
00194                     str = encBuf.get();
00195                     pos = 0;
00196                     len = encBuf.Length();
00197                 }
00198                 // else some failure occured... assume UTF-8 is ok.
00199             }
00200         }
00201 
00202         // escape per RFC2396 unless UTF-8 and allowed by preferences
00203         PRInt16 escapeFlags = (gEscapeUTF8 || mEncoder) ? 0 : esc_OnlyASCII;
00204 
00205         PRUint32 initLen = result.Length();
00206 
00207         // now perform any required escaping
00208         if (NS_EscapeURL(str + pos, len, mask | escapeFlags, result)) {
00209             len = result.Length() - initLen;
00210             appended = PR_TRUE;
00211         }
00212         else if (str == encBuf.get()) {
00213             result += encBuf; // append only!!
00214             len = encBuf.Length();
00215             appended = PR_TRUE;
00216         }
00217     }
00218     return len;
00219 }
00220 
00221 const nsACString &nsStandardURL::
00222 nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString &str,
00223                                 PRInt16 mask,
00224                                 nsAFlatCString &result)
00225 {
00226     const char *text;
00227     PRBool encoded;
00228     EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded);
00229     if (encoded)
00230         return result;
00231     return str;
00232 }
00233 
00234 PRBool nsStandardURL::
00235 nsSegmentEncoder::InitUnicodeEncoder()
00236 {
00237     NS_ASSERTION(!mEncoder, "Don't call this if we have an encoder already!");
00238     nsresult rv;
00239     if (!gCharsetMgr) {
00240         rv = CallGetService("@mozilla.org/charset-converter-manager;1",
00241                             &gCharsetMgr);
00242         if (NS_FAILED(rv)) {
00243             NS_ERROR("failed to get charset-converter-manager");
00244             return PR_FALSE;
00245         }
00246     }
00247 
00248     rv = gCharsetMgr->GetUnicodeEncoder(mCharset, getter_AddRefs(mEncoder));
00249     if (NS_FAILED(rv)) {
00250         NS_ERROR("failed to get unicode encoder");
00251         mEncoder = 0; // just in case
00252         return PR_FALSE;
00253     }
00254 
00255     return PR_TRUE;
00256 }
00257 
00258 #define GET_SEGMENT_ENCODER(name) \
00259     nsSegmentEncoder name(gAlwaysEncodeInUTF8 ? nsnull : mOriginCharset.get())
00260 
00261 //----------------------------------------------------------------------------
00262 // nsStandardURL <public>
00263 //----------------------------------------------------------------------------
00264 
00265 nsStandardURL::nsStandardURL(PRBool aSupportsFileURL)
00266     : mDefaultPort(-1)
00267     , mPort(-1)
00268     , mHostA(nsnull)
00269     , mHostEncoding(eEncoding_ASCII)
00270     , mSpecEncoding(eEncoding_Unknown)
00271     , mURLType(URLTYPE_STANDARD)
00272     , mMutable(PR_TRUE)
00273     , mSupportsFileURL(aSupportsFileURL)
00274 {
00275 #if defined(PR_LOGGING)
00276     if (!gStandardURLLog)
00277         gStandardURLLog = PR_NewLogModule("nsStandardURL");
00278 #endif
00279 
00280     LOG(("Creating nsStandardURL @%p\n", this));
00281 
00282     if (!gInitialized) {
00283         gInitialized = PR_TRUE;
00284         InitGlobalObjects();
00285     }
00286 
00287     // default parser in case nsIStandardURL::Init is never called
00288     mParser = net_GetStdURLParser();
00289 }
00290 
00291 nsStandardURL::~nsStandardURL()
00292 {
00293     LOG(("Destroying nsStandardURL @%p\n", this));
00294 
00295     CRTFREEIF(mHostA);
00296 }
00297 
00298 void
00299 nsStandardURL::InitGlobalObjects()
00300 {
00301     nsCOMPtr<nsIPrefBranch2> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) );
00302     if (prefBranch) {
00303         nsCOMPtr<nsIObserver> obs( new nsPrefObserver() );
00304         prefBranch->AddObserver(NS_NET_PREF_ESCAPEUTF8, obs.get(), PR_FALSE); 
00305         prefBranch->AddObserver(NS_NET_PREF_ALWAYSENCODEINUTF8, obs.get(), PR_FALSE);
00306         prefBranch->AddObserver(NS_NET_PREF_ENABLEIDN, obs.get(), PR_FALSE); 
00307         prefBranch->AddObserver(NS_NET_PREF_SHOWPUNYCODE, obs.get(), PR_FALSE); 
00308 
00309         PrefsChanged(prefBranch, nsnull);
00310 
00311         nsCOMPtr<nsIPrefService> prefs = do_QueryInterface(prefBranch); 
00312         if (prefs) {
00313             nsCOMPtr<nsIPrefBranch> branch;
00314            if (NS_SUCCEEDED(prefs->GetBranch( NS_NET_PREF_IDNWHITELIST,
00315                                               getter_AddRefs(branch) )))
00316                NS_ADDREF(gIDNWhitelistPrefBranch = branch);
00317         }
00318     }
00319 }
00320 
00321 void
00322 nsStandardURL::ShutdownGlobalObjects()
00323 {
00324     NS_IF_RELEASE(gIDN);
00325     NS_IF_RELEASE(gCharsetMgr);
00326     NS_IF_RELEASE(gIDNWhitelistPrefBranch);
00327 }
00328 
00329 //----------------------------------------------------------------------------
00330 // nsStandardURL <private>
00331 //----------------------------------------------------------------------------
00332 
00333 void
00334 nsStandardURL::Clear()
00335 {
00336     mSpec.Truncate();
00337 
00338     mPort = -1;
00339 
00340     mAuthority.Reset();
00341     mUsername.Reset();
00342     mPassword.Reset();
00343     mHost.Reset();
00344     mHostEncoding = eEncoding_ASCII;
00345 
00346     mPath.Reset();
00347     mFilepath.Reset();
00348     mDirectory.Reset();
00349     mBasename.Reset();
00350 
00351     mExtension.Reset();
00352     mParam.Reset();
00353     mQuery.Reset();
00354     mRef.Reset();
00355 
00356     InvalidateCache();
00357 }
00358 
00359 void
00360 nsStandardURL::InvalidateCache(PRBool invalidateCachedFile)
00361 {
00362     if (invalidateCachedFile)
00363         mFile = 0;
00364     CRTFREEIF(mHostA);
00365     mSpecEncoding = eEncoding_Unknown;
00366 }
00367 
00368 PRBool
00369 nsStandardURL::EscapeIPv6(const char *host, nsCString &result)
00370 {
00371     // Escape IPv6 address literal by surrounding it with []'s
00372     if (host && (host[0] != '[') && PL_strchr(host, ':')) {
00373         result.Assign('[');
00374         result.Append(host);
00375         result.Append(']');
00376         return PR_TRUE;
00377     }
00378     return PR_FALSE;
00379 }
00380 
00381 PRBool
00382 nsStandardURL::NormalizeIDN(const nsCSubstring &host, nsCString &result)
00383 {
00384     // If host is ACE, then convert to UTF-8.  Else, if host is already UTF-8,
00385     // then make sure it is normalized per IDN.
00386 
00387     // this function returns PR_TRUE iff it writes something to |result|.
00388 
00389     // NOTE: As a side-effect this function sets mHostEncoding.  While it would
00390     // be nice to avoid side-effects in this function, the implementation of
00391     // this function is already somewhat bound to the behavior of the
00392     // callsites.  Anyways, this function exists to avoid code duplication, so
00393     // side-effects abound :-/
00394 
00395     NS_ASSERTION(mHostEncoding == eEncoding_ASCII, "unexpected default encoding");
00396 
00397     if (IsASCII(host)) {
00398         PRBool isACE;
00399         if (gIDN &&
00400             NS_SUCCEEDED(gIDN->IsACE(host, &isACE)) && isACE &&
00401             NS_SUCCEEDED(ACEtoDisplayIDN(host, result))) {
00402             mHostEncoding = eEncoding_UTF8;
00403             return PR_TRUE;
00404         }
00405     }
00406     else {
00407         mHostEncoding = eEncoding_UTF8;
00408         if (gIDN && NS_SUCCEEDED(UTF8toDisplayIDN(host, result))) {
00409             // normalization could result in an ASCII only hostname
00410             if (IsASCII(result))
00411                 mHostEncoding = eEncoding_ASCII;
00412             return PR_TRUE;
00413         }
00414     }
00415 
00416     result.Truncate();
00417     return PR_FALSE;
00418 }
00419 
00420 void
00421 nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path)
00422 {
00423     net_CoalesceDirs(coalesceFlag, path);
00424     PRInt32 newLen = strlen(path);
00425     if (newLen < mPath.mLen) {
00426         PRInt32 diff = newLen - mPath.mLen;
00427         mPath.mLen = newLen;
00428         mDirectory.mLen += diff;
00429         mFilepath.mLen += diff;
00430         ShiftFromBasename(diff);
00431     }
00432 }
00433 
00434 PRUint32
00435 nsStandardURL::AppendSegmentToBuf(char *buf, PRUint32 i, const char *str, URLSegment &seg, const nsCString *escapedStr, PRBool useEscaped)
00436 {
00437     if (seg.mLen > 0) {
00438         if (useEscaped) {
00439             seg.mLen = escapedStr->Length();
00440             memcpy(buf + i, escapedStr->get(), seg.mLen);
00441         }
00442         else
00443             memcpy(buf + i, str + seg.mPos, seg.mLen);
00444         seg.mPos = i;
00445         i += seg.mLen;
00446     }
00447     return i;
00448 }
00449 
00450 PRUint32
00451 nsStandardURL::AppendToBuf(char *buf, PRUint32 i, const char *str, PRUint32 len)
00452 {
00453     memcpy(buf + i, str, len);
00454     return i + len;
00455 }
00456 
00457 // basic algorithm:
00458 //  1- escape url segments (for improved GetSpec efficiency)
00459 //  2- allocate spec buffer
00460 //  3- write url segments
00461 //  4- update url segment positions and lengths
00462 nsresult
00463 nsStandardURL::BuildNormalizedSpec(const char *spec)
00464 {
00465     // Assumptions: all member URLSegments must be relative the |spec| argument
00466     // passed to this function.
00467 
00468     // buffers for holding escaped url segments (these will remain empty unless
00469     // escaping is required).
00470     nsCAutoString encUsername, encPassword, encHost, encDirectory,
00471       encBasename, encExtension, encParam, encQuery, encRef;
00472     PRBool useEncUsername, useEncPassword, useEncHost, useEncDirectory,
00473       useEncBasename, useEncExtension, useEncParam, useEncQuery, useEncRef;
00474 
00475     //
00476     // escape each URL segment, if necessary, and calculate approximate normalized
00477     // spec length.
00478     //
00479     PRUint32 approxLen = 3; // includes room for "://"
00480 
00481     // the scheme is already ASCII
00482     if (mScheme.mLen > 0)
00483         approxLen += mScheme.mLen;
00484 
00485     // encode URL segments; convert UTF-8 to origin charset and possibly escape.
00486     // results written to encXXX variables only if |spec| is not already in the
00487     // appropriate encoding.
00488     {
00489         GET_SEGMENT_ENCODER(encoder);
00490         approxLen += encoder.EncodeSegmentCount(spec, mUsername,  esc_Username,      encUsername,  useEncUsername);
00491         approxLen += encoder.EncodeSegmentCount(spec, mPassword,  esc_Password,      encPassword,  useEncPassword);
00492         approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory,     encDirectory, useEncDirectory);
00493         approxLen += encoder.EncodeSegmentCount(spec, mBasename,  esc_FileBaseName,  encBasename,  useEncBasename);
00494         approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension);
00495         approxLen += encoder.EncodeSegmentCount(spec, mParam,     esc_Param,         encParam,     useEncParam);
00496         approxLen += encoder.EncodeSegmentCount(spec, mQuery,     esc_Query,         encQuery,     useEncQuery);
00497         approxLen += encoder.EncodeSegmentCount(spec, mRef,       esc_Ref,           encRef,       useEncRef);
00498     }
00499 
00500     // do not escape the hostname, if IPv6 address literal, mHost will
00501     // already point to a [ ] delimited IPv6 address literal.
00502     // However, perform Unicode normalization on it, as IDN does.
00503     mHostEncoding = eEncoding_ASCII;
00504     if (mHost.mLen > 0) {
00505         const nsCSubstring& tempHost =
00506             Substring(spec + mHost.mPos, spec + mHost.mPos + mHost.mLen);
00507         if (tempHost.FindChar('\0') != kNotFound)
00508             return NS_ERROR_MALFORMED_URI;  // null embedded in hostname
00509         if ((useEncHost = NormalizeIDN(tempHost, encHost)))
00510             approxLen += encHost.Length();
00511         else
00512             approxLen += mHost.mLen;
00513     }
00514 
00515     //
00516     // generate the normalized URL string
00517     //
00518     if (!EnsureStringLength(mSpec, approxLen + 32))
00519         return NS_ERROR_OUT_OF_MEMORY;
00520     char *buf;
00521     mSpec.BeginWriting(buf);
00522     PRUint32 i = 0;
00523 
00524     if (mScheme.mLen > 0) {
00525         i = AppendSegmentToBuf(buf, i, spec, mScheme);
00526         net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
00527         i = AppendToBuf(buf, i, "://", 3);
00528     }
00529 
00530     // record authority starting position
00531     mAuthority.mPos = i;
00532 
00533     // append authority
00534     if (mUsername.mLen > 0) {
00535         i = AppendSegmentToBuf(buf, i, spec, mUsername, &encUsername, useEncUsername);
00536         if (mPassword.mLen >= 0) {
00537             buf[i++] = ':';
00538             i = AppendSegmentToBuf(buf, i, spec, mPassword, &encPassword, useEncPassword);
00539         }
00540         buf[i++] = '@';
00541     }
00542     if (mHost.mLen > 0) {
00543         i = AppendSegmentToBuf(buf, i, spec, mHost, &encHost, useEncHost);
00544         net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
00545         if (mPort != -1 && mPort != mDefaultPort) {
00546             nsCAutoString portbuf;
00547             portbuf.AppendInt(mPort);
00548             buf[i++] = ':';
00549             i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length());
00550         }
00551     }
00552 
00553     // record authority length
00554     mAuthority.mLen = i - mAuthority.mPos;
00555 
00556     // path must always start with a "/"
00557     if (mPath.mLen <= 0) {
00558         LOG(("setting path=/"));
00559         mDirectory.mPos = mFilepath.mPos = mPath.mPos = i;
00560         mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1;
00561         // basename must exist, even if empty (bug 113508)
00562         mBasename.mPos = i+1;
00563         mBasename.mLen = 0;
00564         buf[i++] = '/';
00565     }
00566     else {
00567         PRUint32 leadingSlash = 0;
00568         if (spec[mPath.mPos] != '/') {
00569             LOG(("adding leading slash to path\n"));
00570             leadingSlash = 1;
00571             buf[i++] = '/';
00572         }
00573 
00574         // record corrected (file)path starting position
00575         mPath.mPos = mFilepath.mPos = i - leadingSlash;
00576 
00577         i = AppendSegmentToBuf(buf, i, spec, mDirectory, &encDirectory, useEncDirectory);
00578 
00579         // the directory must end with a '/'
00580         if (buf[i-1] != '/') {
00581             buf[i++] = '/';
00582             mDirectory.mLen++;
00583         }
00584 
00585         i = AppendSegmentToBuf(buf, i, spec, mBasename, &encBasename, useEncBasename);
00586 
00587         // make corrections to directory segment if leadingSlash
00588         if (leadingSlash) {
00589             mDirectory.mPos = mPath.mPos;
00590             if (mDirectory.mLen >= 0)
00591                 mDirectory.mLen += leadingSlash;
00592             else
00593                 mDirectory.mLen = 1;
00594         }
00595 
00596         if (mExtension.mLen >= 0) {
00597             buf[i++] = '.';
00598             i = AppendSegmentToBuf(buf, i, spec, mExtension, &encExtension, useEncExtension);
00599         }
00600         // calculate corrected filepath length
00601         mFilepath.mLen = i - mFilepath.mPos;
00602 
00603         if (mParam.mLen >= 0) {
00604             buf[i++] = ';';
00605             i = AppendSegmentToBuf(buf, i, spec, mParam, &encParam, useEncParam);
00606         }
00607         if (mQuery.mLen >= 0) {
00608             buf[i++] = '?';
00609             i = AppendSegmentToBuf(buf, i, spec, mQuery, &encQuery, useEncQuery);
00610         }
00611         if (mRef.mLen >= 0) {
00612             buf[i++] = '#';
00613             i = AppendSegmentToBuf(buf, i, spec, mRef, &encRef, useEncRef);
00614         }
00615         // calculate corrected path length
00616         mPath.mLen = i - mPath.mPos;
00617     }
00618 
00619     buf[i] = '\0';
00620 
00621     if (mDirectory.mLen > 1) {
00622         netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
00623         if (SegmentIs(buf,mScheme,"ftp")) {
00624             coalesceFlag = (netCoalesceFlags) (coalesceFlag 
00625                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
00626                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
00627         }
00628         CoalescePath(coalesceFlag, buf + mDirectory.mPos);
00629     }
00630     mSpec.SetLength(strlen(buf));
00631     NS_ASSERTION(mSpec.Length() <= approxLen+32, "We've overflowed the mSpec buffer!");
00632     return NS_OK;
00633 }
00634 
00635 PRBool
00636 nsStandardURL::SegmentIs(const URLSegment &seg, const char *val)
00637 {
00638     // one or both may be null
00639     if (!val || mSpec.IsEmpty())
00640         return (!val && (mSpec.IsEmpty() || seg.mLen < 0));
00641     if (seg.mLen < 0)
00642         return PR_FALSE;
00643     // if the first |seg.mLen| chars of |val| match, then |val| must
00644     // also be null terminated at |seg.mLen|.
00645     return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen)
00646         && (val[seg.mLen] == '\0');
00647 }
00648 
00649 PRBool
00650 nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val)
00651 {
00652     // one or both may be null
00653     if (!val || !spec)
00654         return (!val && (!spec || seg.mLen < 0));
00655     if (seg.mLen < 0)
00656         return PR_FALSE;
00657     // if the first |seg.mLen| chars of |val| match, then |val| must
00658     // also be null terminated at |seg.mLen|.
00659     return !strncmp(spec + seg.mPos, val, seg.mLen)
00660         && (val[seg.mLen] == '\0');
00661 }
00662 
00663 PRBool
00664 nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2)
00665 {
00666     if (seg1.mLen != seg2.mLen)
00667         return PR_FALSE;
00668     if (seg1.mLen == -1 || (!val && mSpec.IsEmpty()))
00669         return PR_TRUE; // both are empty
00670     return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); 
00671 }
00672 
00673 PRInt32
00674 nsStandardURL::ReplaceSegment(PRUint32 pos, PRUint32 len, const char *val, PRUint32 valLen)
00675 {
00676     if (val && valLen) {
00677         if (len == 0)
00678             mSpec.Insert(val, pos, valLen);
00679         else
00680             mSpec.Replace(pos, len, nsDependentCString(val, valLen));
00681         return valLen - len;
00682     }
00683 
00684     // else remove the specified segment
00685     mSpec.Cut(pos, len);
00686     return -PRInt32(len);
00687 }
00688 
00689 PRInt32
00690 nsStandardURL::ReplaceSegment(PRUint32 pos, PRUint32 len, const nsACString &val)
00691 {
00692     if (len == 0)
00693         mSpec.Insert(val, pos);
00694     else
00695         mSpec.Replace(pos, len, val);
00696     return val.Length() - len;
00697 }
00698 
00699 nsresult
00700 nsStandardURL::ParseURL(const char *spec, PRInt32 specLen)
00701 {
00702     nsresult rv;
00703 
00704     //
00705     // parse given URL string
00706     //
00707     rv = mParser->ParseURL(spec, specLen,
00708                            &mScheme.mPos, &mScheme.mLen,
00709                            &mAuthority.mPos, &mAuthority.mLen,
00710                            &mPath.mPos, &mPath.mLen);
00711     if (NS_FAILED(rv)) return rv;
00712 
00713 #ifdef DEBUG
00714     if (mScheme.mLen <= 0) {
00715         printf("spec=%s\n", spec);
00716         NS_WARNING("malformed url: no scheme");
00717     }
00718 #endif
00719      
00720     if (mAuthority.mLen > 0) {
00721         rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen,
00722                                      &mUsername.mPos, &mUsername.mLen,
00723                                      &mPassword.mPos, &mPassword.mLen,
00724                                      &mHost.mPos, &mHost.mLen,
00725                                      &mPort);
00726         if (NS_FAILED(rv)) return rv;
00727 
00728         mUsername.mPos += mAuthority.mPos;
00729         mPassword.mPos += mAuthority.mPos;
00730         mHost.mPos += mAuthority.mPos;
00731     }
00732 
00733     if (mPath.mLen > 0)
00734         rv = ParsePath(spec, mPath.mPos, mPath.mLen);
00735 
00736     return rv;
00737 }
00738 
00739 nsresult
00740 nsStandardURL::ParsePath(const char *spec, PRUint32 pathPos, PRInt32 pathLen)
00741 {
00742     nsresult rv = mParser->ParsePath(spec + pathPos, pathLen,
00743                                      &mFilepath.mPos, &mFilepath.mLen,
00744                                      &mParam.mPos, &mParam.mLen,
00745                                      &mQuery.mPos, &mQuery.mLen,
00746                                      &mRef.mPos, &mRef.mLen);
00747     if (NS_FAILED(rv)) return rv;
00748 
00749     mFilepath.mPos += pathPos;
00750     mParam.mPos += pathPos;
00751     mQuery.mPos += pathPos;
00752     mRef.mPos += pathPos;
00753 
00754     if (mFilepath.mLen > 0) {
00755         rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen,
00756                                     &mDirectory.mPos, &mDirectory.mLen,
00757                                     &mBasename.mPos, &mBasename.mLen,
00758                                     &mExtension.mPos, &mExtension.mLen);
00759         if (NS_FAILED(rv)) return rv;
00760 
00761         mDirectory.mPos += mFilepath.mPos;
00762         mBasename.mPos += mFilepath.mPos;
00763         mExtension.mPos += mFilepath.mPos;
00764     }
00765     return NS_OK;
00766 }
00767 
00768 char *
00769 nsStandardURL::AppendToSubstring(PRUint32 pos,
00770                                  PRInt32 len,
00771                                  const char *tail,
00772                                  PRInt32 tailLen)
00773 {
00774     if (tailLen < 0)
00775         tailLen = strlen(tail);
00776 
00777     char *result = (char *) malloc(len + tailLen + 1);
00778     if (result) {
00779         memcpy(result, mSpec.get() + pos, len);
00780         memcpy(result + len, tail, tailLen);
00781         result[len + tailLen] = '\0';
00782     }
00783     return result;
00784 }
00785 
00786 nsresult
00787 nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg)
00788 {
00789     nsresult rv;
00790 
00791     rv = stream->Read32(&seg.mPos);
00792     if (NS_FAILED(rv)) return rv;
00793 
00794     rv = stream->Read32((PRUint32 *) &seg.mLen);
00795     if (NS_FAILED(rv)) return rv;
00796 
00797     return NS_OK;
00798 }
00799 
00800 nsresult
00801 nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg)
00802 {
00803     nsresult rv;
00804 
00805     rv = stream->Write32(seg.mPos);
00806     if (NS_FAILED(rv)) return rv;
00807 
00808     rv = stream->Write32(PRUint32(seg.mLen));
00809     if (NS_FAILED(rv)) return rv;
00810 
00811     return NS_OK;
00812 }
00813 
00814 /* static */ void
00815 nsStandardURL::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
00816 {
00817     PRBool val;
00818 
00819     LOG(("nsStandardURL::PrefsChanged [pref=%s]\n", pref));
00820 
00821 #define PREF_CHANGED(p) ((pref == nsnull) || !strcmp(pref, p))
00822 #define GOT_PREF(p, b) (NS_SUCCEEDED(prefs->GetBoolPref(p, &b)))
00823 
00824     if (PREF_CHANGED(NS_NET_PREF_ENABLEIDN)) {
00825         NS_IF_RELEASE(gIDN);
00826         if (GOT_PREF(NS_NET_PREF_ENABLEIDN, val) && val) {
00827             // initialize IDN
00828             nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID));
00829             if (serv)
00830                 NS_ADDREF(gIDN = serv.get());
00831         }
00832         LOG(("IDN support %s\n", gIDN ? "enabled" : "disabled"));
00833     }
00834     
00835     if (PREF_CHANGED(NS_NET_PREF_ESCAPEUTF8)) {
00836         if (GOT_PREF(NS_NET_PREF_ESCAPEUTF8, val))
00837             gEscapeUTF8 = val;
00838         LOG(("escape UTF-8 %s\n", gEscapeUTF8 ? "enabled" : "disabled"));
00839     }
00840         
00841     if (PREF_CHANGED(NS_NET_PREF_ALWAYSENCODEINUTF8)) {
00842         if (GOT_PREF(NS_NET_PREF_ALWAYSENCODEINUTF8, val))
00843             gAlwaysEncodeInUTF8 = val;
00844         LOG(("encode in UTF-8 %s\n", gAlwaysEncodeInUTF8 ? "enabled" : "disabled"));
00845     }
00846 
00847     if (PREF_CHANGED(NS_NET_PREF_SHOWPUNYCODE)) {
00848         if (GOT_PREF(NS_NET_PREF_SHOWPUNYCODE, val))
00849             gShowPunycode = val;
00850         LOG(("show punycode %s\n", gShowPunycode ? "enabled" : "disabled"));
00851     }
00852 #undef PREF_CHANGED
00853 #undef GOT_PREF
00854 }
00855 
00856 /* static */ nsresult
00857 nsStandardURL::ACEtoDisplayIDN(const nsCSubstring &host, nsCString &result)
00858 {
00859     if (gShowPunycode || !IsInWhitelist(host)) {
00860         result = host;
00861         return NS_OK;
00862     }
00863 
00864     return gIDN->ConvertACEtoUTF8(host, result);
00865 }
00866 
00867 /* static */ nsresult
00868 nsStandardURL::UTF8toDisplayIDN(const nsCSubstring &host, nsCString &result)
00869 {
00870     // We have to normalize the hostname before testing against the domain
00871     // whitelist.  See bug 315411.
00872 
00873     nsCAutoString temp;
00874     if (gShowPunycode || NS_FAILED(gIDN->Normalize(host, temp)))
00875         return gIDN->ConvertUTF8toACE(host, result);
00876 
00877     PRBool isACE = PR_FALSE;
00878     gIDN->IsACE(temp, &isACE);
00879 
00880     // If host is converted to ACE by the normalizer, then the host may contain
00881     // unsafe characters.  See bug 283016, bug 301694, and bug 309311.
00882  
00883     if (!isACE && !IsInWhitelist(temp))
00884         return gIDN->ConvertUTF8toACE(temp, result);
00885 
00886     result = temp;
00887     return NS_OK;
00888 }
00889 
00890 /* static */ PRBool
00891 nsStandardURL::IsInWhitelist(const nsCSubstring &host)
00892 {
00893     PRInt32 pos; 
00894     PRBool safe;
00895 
00896     // XXX This code uses strings inefficiently.
00897 
00898     if (gIDNWhitelistPrefBranch && 
00899         (pos = nsCAutoString(host).RFind(".")) != kNotFound &&
00900         NS_SUCCEEDED(gIDNWhitelistPrefBranch->
00901                      GetBoolPref(nsCAutoString(Substring(host, pos + 1)).get(),
00902                                  &safe)))
00903         return safe;
00904 
00905     return PR_FALSE;
00906 }
00907 
00908 //----------------------------------------------------------------------------
00909 // nsStandardURL::nsISupports
00910 //----------------------------------------------------------------------------
00911 
00912 NS_IMPL_ADDREF(nsStandardURL)
00913 NS_IMPL_RELEASE(nsStandardURL)
00914 
00915 NS_INTERFACE_MAP_BEGIN(nsStandardURL)
00916     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL)
00917     NS_INTERFACE_MAP_ENTRY(nsIURI)
00918     NS_INTERFACE_MAP_ENTRY(nsIURL)
00919     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL)
00920     NS_INTERFACE_MAP_ENTRY(nsIStandardURL)
00921     NS_INTERFACE_MAP_ENTRY(nsISerializable)
00922     NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
00923     // see nsStandardURL::Equals
00924     if (aIID.Equals(kThisImplCID))
00925         foundInterface = NS_STATIC_CAST(nsIURI *, this);
00926     else
00927 NS_INTERFACE_MAP_END
00928 
00929 //----------------------------------------------------------------------------
00930 // nsStandardURL::nsIURI
00931 //----------------------------------------------------------------------------
00932 
00933 // result may contain unescaped UTF-8 characters
00934 NS_IMETHODIMP
00935 nsStandardURL::GetSpec(nsACString &result)
00936 {
00937     result = mSpec;
00938     return NS_OK;
00939 }
00940 
00941 // result may contain unescaped UTF-8 characters
00942 NS_IMETHODIMP
00943 nsStandardURL::GetPrePath(nsACString &result)
00944 {
00945     result = Prepath();
00946     return NS_OK;
00947 }
00948 
00949 // result is strictly US-ASCII
00950 NS_IMETHODIMP
00951 nsStandardURL::GetScheme(nsACString &result)
00952 {
00953     result = Scheme();
00954     return NS_OK;
00955 }
00956 
00957 // result may contain unescaped UTF-8 characters
00958 NS_IMETHODIMP
00959 nsStandardURL::GetUserPass(nsACString &result)
00960 {
00961     result = Userpass();
00962     return NS_OK;
00963 }
00964 
00965 // result may contain unescaped UTF-8 characters
00966 NS_IMETHODIMP
00967 nsStandardURL::GetUsername(nsACString &result)
00968 {
00969     result = Username();
00970     return NS_OK;
00971 }
00972 
00973 // result may contain unescaped UTF-8 characters
00974 NS_IMETHODIMP
00975 nsStandardURL::GetPassword(nsACString &result)
00976 {
00977     result = Password();
00978     return NS_OK;
00979 }
00980 
00981 NS_IMETHODIMP
00982 nsStandardURL::GetHostPort(nsACString &result)
00983 {
00984     result = Hostport();
00985     return NS_OK;
00986 }
00987 
00988 NS_IMETHODIMP
00989 nsStandardURL::GetHost(nsACString &result)
00990 {
00991     result = Host();
00992     return NS_OK;
00993 }
00994 
00995 NS_IMETHODIMP
00996 nsStandardURL::GetPort(PRInt32 *result)
00997 {
00998     *result = mPort;
00999     return NS_OK;
01000 }
01001 
01002 // result may contain unescaped UTF-8 characters
01003 NS_IMETHODIMP
01004 nsStandardURL::GetPath(nsACString &result)
01005 {
01006     result = Path();
01007     return NS_OK;
01008 }
01009 
01010 // result is ASCII
01011 NS_IMETHODIMP
01012 nsStandardURL::GetAsciiSpec(nsACString &result)
01013 {
01014     if (mSpecEncoding == eEncoding_Unknown) {
01015         if (IsASCII(mSpec))
01016             mSpecEncoding = eEncoding_ASCII;
01017         else
01018             mSpecEncoding = eEncoding_UTF8;
01019     }
01020 
01021     if (mSpecEncoding == eEncoding_ASCII) {
01022         result = mSpec;
01023         return NS_OK;
01024     }
01025 
01026     // try to guess the capacity required for result...
01027     result.SetCapacity(mSpec.Length() + PR_MIN(32, mSpec.Length()/10));
01028 
01029     result = Substring(mSpec, 0, mScheme.mLen + 3);
01030 
01031     NS_EscapeURL(Userpass(PR_TRUE), esc_OnlyNonASCII | esc_AlwaysCopy, result);
01032 
01033     // get escaped host
01034     nsCAutoString escHostport;
01035     if (mHost.mLen > 0) {
01036         // this doesn't fail
01037         (void) GetAsciiHost(escHostport);
01038 
01039         // escHostport = "hostA" + ":port"
01040         PRUint32 pos = mHost.mPos + mHost.mLen;
01041         if (pos < mPath.mPos)
01042             escHostport += Substring(mSpec, pos, mPath.mPos - pos);
01043     }
01044     result += escHostport;
01045 
01046     NS_EscapeURL(Path(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
01047     return NS_OK;
01048 }
01049 
01050 // result is ASCII
01051 NS_IMETHODIMP
01052 nsStandardURL::GetAsciiHost(nsACString &result)
01053 {
01054     if (mHostEncoding == eEncoding_ASCII) {
01055         result = Host();
01056         return NS_OK;
01057     }
01058 
01059     // perhaps we have it cached...
01060     if (mHostA) {
01061         result = mHostA;
01062         return NS_OK;
01063     }
01064 
01065     if (gIDN) {
01066         nsresult rv;
01067         rv = gIDN->ConvertUTF8toACE(Host(), result);
01068         if (NS_SUCCEEDED(rv)) {
01069             mHostA = ToNewCString(result);
01070             return NS_OK;
01071         }
01072         NS_WARNING("nsIDNService::ConvertUTF8toACE failed");
01073     }
01074 
01075     // something went wrong... guess all we can do is URL escape :-/
01076     NS_EscapeURL(Host(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
01077     return NS_OK;
01078 }
01079 
01080 NS_IMETHODIMP
01081 nsStandardURL::GetOriginCharset(nsACString &result)
01082 {
01083     if (mOriginCharset.IsEmpty())
01084         result.AssignLiteral("UTF-8");
01085     else
01086         result = mOriginCharset;
01087     return NS_OK;
01088 }
01089 
01090 NS_IMETHODIMP
01091 nsStandardURL::SetSpec(const nsACString &input)
01092 {
01093     ENSURE_MUTABLE();
01094 
01095     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
01096     const char *spec = flat.get();
01097     PRInt32 specLength = flat.Length();
01098 
01099     LOG(("nsStandardURL::SetSpec [spec=%s]\n", spec));
01100 
01101     Clear();
01102 
01103     if (!spec || !*spec)
01104         return NS_OK;
01105 
01106     // filter out unexpected chars "\r\n\t" if necessary
01107     nsCAutoString buf1;
01108     if (net_FilterURIString(spec, buf1)) {
01109         spec = buf1.get();
01110         specLength = buf1.Length();
01111     }
01112 
01113     // parse the given URL...
01114     nsresult rv = ParseURL(spec, specLength);
01115     if (NS_FAILED(rv)) return rv;
01116 
01117     // finally, use the URLSegment member variables to build a normalized
01118     // copy of |spec|
01119     rv = BuildNormalizedSpec(spec);
01120 
01121 #if defined(PR_LOGGING)
01122     if (LOG_ENABLED()) {
01123         LOG((" spec      = %s\n", mSpec.get()));
01124         LOG((" port      = %d\n", mPort));
01125         LOG((" scheme    = (%u,%d)\n", mScheme.mPos,    mScheme.mLen));
01126         LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen));
01127         LOG((" username  = (%u,%d)\n", mUsername.mPos,  mUsername.mLen));
01128         LOG((" password  = (%u,%d)\n", mPassword.mPos,  mPassword.mLen));
01129         LOG((" hostname  = (%u,%d)\n", mHost.mPos,      mHost.mLen));
01130         LOG((" path      = (%u,%d)\n", mPath.mPos,      mPath.mLen));
01131         LOG((" filepath  = (%u,%d)\n", mFilepath.mPos,  mFilepath.mLen));
01132         LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen));
01133         LOG((" basename  = (%u,%d)\n", mBasename.mPos,  mBasename.mLen));
01134         LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen));
01135         LOG((" param     = (%u,%d)\n", mParam.mPos,     mParam.mLen));
01136         LOG((" query     = (%u,%d)\n", mQuery.mPos,     mQuery.mLen));
01137         LOG((" ref       = (%u,%d)\n", mRef.mPos,       mRef.mLen));
01138     }
01139 #endif
01140     return rv;
01141 }
01142 
01143 NS_IMETHODIMP
01144 nsStandardURL::SetScheme(const nsACString &input)
01145 {
01146     ENSURE_MUTABLE();
01147 
01148     const nsPromiseFlatCString &scheme = PromiseFlatCString(input);
01149 
01150     LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get()));
01151 
01152     if (scheme.IsEmpty()) {
01153         NS_ERROR("cannot remove the scheme from an url");
01154         return NS_ERROR_UNEXPECTED;
01155     }
01156     if (mScheme.mLen < 0) {
01157         NS_ERROR("uninitialized");
01158         return NS_ERROR_NOT_INITIALIZED;
01159     }
01160 
01161     if (!net_IsValidScheme(scheme)) {
01162         NS_ERROR("the given url scheme contains invalid characters");
01163         return NS_ERROR_UNEXPECTED;
01164     }
01165 
01166     InvalidateCache();
01167 
01168     PRInt32 shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme);
01169 
01170     if (shift) {
01171         mScheme.mLen = scheme.Length();
01172         ShiftFromAuthority(shift);
01173     }
01174 
01175     // ensure new scheme is lowercase
01176     //
01177     // XXX the string code unfortunately doesn't provide a ToLowerCase
01178     //     that operates on a substring.
01179     net_ToLowerCase((char *) mSpec.get(), mScheme.mLen);
01180     return NS_OK;
01181 }
01182 
01183 NS_IMETHODIMP
01184 nsStandardURL::SetUserPass(const nsACString &input)
01185 {
01186     ENSURE_MUTABLE();
01187 
01188     const nsPromiseFlatCString &userpass = PromiseFlatCString(input);
01189 
01190     LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get()));
01191 
01192     if (mURLType == URLTYPE_NO_AUTHORITY) {
01193         if (userpass.IsEmpty())
01194             return NS_OK;
01195         NS_ERROR("cannot set user:pass on no-auth url");
01196         return NS_ERROR_UNEXPECTED;
01197     }
01198     if (mAuthority.mLen < 0) {
01199         NS_ERROR("uninitialized");
01200         return NS_ERROR_NOT_INITIALIZED;
01201     }
01202 
01203     InvalidateCache();
01204 
01205     if (userpass.IsEmpty()) {
01206         // remove user:pass
01207         if (mUsername.mLen > 0) {
01208             if (mPassword.mLen > 0)
01209                 mUsername.mLen += (mPassword.mLen + 1);
01210             mUsername.mLen++;
01211             mSpec.Cut(mUsername.mPos, mUsername.mLen);
01212             mAuthority.mLen -= mUsername.mLen;
01213             ShiftFromHost(-mUsername.mLen);
01214             mUsername.mLen = -1;
01215             mPassword.mLen = -1;
01216         }
01217         return NS_OK;
01218     }
01219 
01220     NS_ASSERTION(mHost.mLen >= 0, "uninitialized");
01221 
01222     nsresult rv;
01223     PRUint32 usernamePos, passwordPos;
01224     PRInt32 usernameLen, passwordLen;
01225 
01226     rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(),
01227                                 &usernamePos, &usernameLen,
01228                                 &passwordPos, &passwordLen);
01229     if (NS_FAILED(rv)) return rv;
01230 
01231     // build new user:pass in |buf|
01232     nsCAutoString buf;
01233     if (usernameLen > 0) {
01234         GET_SEGMENT_ENCODER(encoder);
01235         PRBool ignoredOut;
01236         usernameLen = encoder.EncodeSegmentCount(userpass.get(),
01237                                                  URLSegment(usernamePos,
01238                                                             usernameLen),
01239                                                  esc_Username | esc_AlwaysCopy,
01240                                                  buf, ignoredOut);
01241         if (passwordLen >= 0) {
01242             buf.Append(':');
01243             passwordLen = encoder.EncodeSegmentCount(userpass.get(),
01244                                                      URLSegment(passwordPos,
01245                                                                 passwordLen),
01246                                                      esc_Password |
01247                                                      esc_AlwaysCopy, buf,
01248                                                      ignoredOut);
01249         }
01250         if (mUsername.mLen < 0)
01251             buf.Append('@');
01252     }
01253 
01254     PRUint32 shift = 0;
01255 
01256     if (mUsername.mLen < 0) {
01257         // no existing user:pass
01258         if (!buf.IsEmpty()) {
01259             mSpec.Insert(buf, mHost.mPos);
01260             mUsername.mPos = mHost.mPos;
01261             shift = buf.Length();
01262         }
01263     }
01264     else {
01265         // replace existing user:pass
01266         PRUint32 userpassLen = mUsername.mLen;
01267         if (mPassword.mLen >= 0)
01268             userpassLen += (mPassword.mLen + 1);
01269         mSpec.Replace(mUsername.mPos, userpassLen, buf);
01270         shift = buf.Length() - userpassLen;
01271     }
01272     if (shift) {
01273         ShiftFromHost(shift);
01274         mAuthority.mLen += shift;
01275     }
01276     // update positions and lengths
01277     mUsername.mLen = usernameLen;
01278     mPassword.mLen = passwordLen;
01279     if (passwordLen)
01280         mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
01281     return NS_OK;
01282 }
01283 
01284 NS_IMETHODIMP
01285 nsStandardURL::SetUsername(const nsACString &input)
01286 {
01287     ENSURE_MUTABLE();
01288 
01289     const nsPromiseFlatCString &username = PromiseFlatCString(input);
01290 
01291     LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get()));
01292 
01293     if (mURLType == URLTYPE_NO_AUTHORITY) {
01294         if (username.IsEmpty())
01295             return NS_OK;
01296         NS_ERROR("cannot set username on no-auth url");
01297         return NS_ERROR_UNEXPECTED;
01298     }
01299 
01300     if (username.IsEmpty())
01301         return SetUserPass(username);
01302 
01303     InvalidateCache();
01304 
01305     // escape username if necessary
01306     nsCAutoString buf;
01307     GET_SEGMENT_ENCODER(encoder);
01308     const nsACString &escUsername =
01309         encoder.EncodeSegment(username, esc_Username, buf);
01310 
01311     PRInt32 shift;
01312 
01313     if (mUsername.mLen < 0) {
01314         mUsername.mPos = mAuthority.mPos;
01315         mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos);
01316         shift = escUsername.Length() + 1;
01317     }
01318     else
01319         shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername);
01320 
01321     if (shift) {
01322         mUsername.mLen = escUsername.Length();
01323         mAuthority.mLen += shift;
01324         ShiftFromPassword(shift);
01325     }
01326     return NS_OK;
01327 }
01328 
01329 NS_IMETHODIMP
01330 nsStandardURL::SetPassword(const nsACString &input)
01331 {
01332     ENSURE_MUTABLE();
01333 
01334     const nsPromiseFlatCString &password = PromiseFlatCString(input);
01335 
01336     LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get()));
01337 
01338     if (mURLType == URLTYPE_NO_AUTHORITY) {
01339         if (password.IsEmpty())
01340             return NS_OK;
01341         NS_ERROR("cannot set password on no-auth url");
01342         return NS_ERROR_UNEXPECTED;
01343     }
01344     if (mUsername.mLen <= 0) {
01345         NS_ERROR("cannot set password without existing username");
01346         return NS_ERROR_FAILURE;
01347     }
01348 
01349     InvalidateCache();
01350 
01351     if (password.IsEmpty()) {
01352         if (mPassword.mLen >= 0) {
01353             // cut(":password")
01354             mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1);
01355             ShiftFromHost(-(mPassword.mLen + 1));
01356             mAuthority.mLen -= (mPassword.mLen + 1);
01357             mPassword.mLen = -1;
01358         }
01359         return NS_OK;
01360     }
01361 
01362     // escape password if necessary
01363     nsCAutoString buf;
01364     GET_SEGMENT_ENCODER(encoder);
01365     const nsACString &escPassword =
01366         encoder.EncodeSegment(password, esc_Password, buf);
01367 
01368     PRInt32 shift;
01369 
01370     if (mPassword.mLen < 0) {
01371         mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
01372         mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1);
01373         shift = escPassword.Length() + 1;
01374     }
01375     else
01376         shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword);
01377 
01378     if (shift) {
01379         mPassword.mLen = escPassword.Length();
01380         mAuthority.mLen += shift;
01381         ShiftFromHost(shift);
01382     }
01383     return NS_OK;
01384 }
01385 
01386 NS_IMETHODIMP
01387 nsStandardURL::SetHostPort(const nsACString &value)
01388 {
01389     ENSURE_MUTABLE();
01390 
01391     // XXX needs implementation!!
01392     NS_NOTREACHED("not implemented");
01393     return NS_ERROR_NOT_IMPLEMENTED;
01394 }
01395 
01396 NS_IMETHODIMP
01397 nsStandardURL::SetHost(const nsACString &input)
01398 {
01399     ENSURE_MUTABLE();
01400 
01401     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
01402     const char *host = flat.get();
01403 
01404     LOG(("nsStandardURL::SetHost [host=%s]\n", host));
01405 
01406     if (mURLType == URLTYPE_NO_AUTHORITY) {
01407         if (flat.IsEmpty())
01408             return NS_OK;
01409         NS_ERROR("cannot set host on no-auth url");
01410         return NS_ERROR_UNEXPECTED;
01411     }
01412 
01413     if (host && strlen(host) < flat.Length())
01414         return NS_ERROR_MALFORMED_URI; // found embedded null
01415 
01416     InvalidateCache();
01417     mHostEncoding = eEncoding_ASCII;
01418 
01419     if (!(host && *host)) {
01420         // remove existing hostname
01421         if (mHost.mLen > 0) {
01422             // remove entire authority
01423             mSpec.Cut(mAuthority.mPos, mAuthority.mLen);
01424             ShiftFromPath(-mAuthority.mLen);
01425             mAuthority.mLen = 0;
01426             mUsername.mLen = -1;
01427             mPassword.mLen = -1;
01428             mHost.mLen = -1;
01429             mPort = -1;
01430         }
01431         return NS_OK;
01432     }
01433 
01434     // handle IPv6 unescaped address literal
01435     PRInt32 len;
01436     nsCAutoString hostBuf;
01437     if (EscapeIPv6(host, hostBuf)) {
01438         host = hostBuf.get();
01439         len = hostBuf.Length();
01440     }
01441     else if (NormalizeIDN(flat, hostBuf)) {
01442         host = hostBuf.get();
01443         len = hostBuf.Length();
01444     }
01445     else
01446         len = flat.Length();
01447 
01448     if (mHost.mLen < 0) {
01449         mHost.mPos = mAuthority.mPos;
01450         mHost.mLen = 0;
01451     }
01452 
01453     PRInt32 shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
01454 
01455     if (shift) {
01456         mHost.mLen = len;
01457         mAuthority.mLen += shift;
01458         ShiftFromPath(shift);
01459     }
01460 
01461     // Now canonicalize the host to lowercase
01462     net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen);
01463 
01464     return NS_OK;
01465 }
01466              
01467 NS_IMETHODIMP
01468 nsStandardURL::SetPort(PRInt32 port)
01469 {
01470     ENSURE_MUTABLE();
01471 
01472     LOG(("nsStandardURL::SetPort [port=%d]\n", port));
01473 
01474     if ((port == mPort) || (mPort == -1 && port == mDefaultPort))
01475         return NS_OK;
01476 
01477     if (mURLType == URLTYPE_NO_AUTHORITY) {
01478         NS_ERROR("cannot set port on no-auth url");
01479         return NS_ERROR_UNEXPECTED;
01480     }
01481 
01482     InvalidateCache();
01483 
01484     if (mPort == -1) {
01485         // need to insert the port number in the URL spec
01486         nsCAutoString buf;
01487         buf.Assign(':');
01488         buf.AppendInt(port);
01489         mSpec.Insert(buf, mHost.mPos + mHost.mLen);
01490         mAuthority.mLen += buf.Length();
01491         ShiftFromPath(buf.Length());
01492     }
01493     else if (port == -1 || port == mDefaultPort) {
01494         // need to remove the port number from the URL spec
01495         PRUint32 start = mHost.mPos + mHost.mLen;
01496         PRUint32 lengthToCut = mPath.mPos - start;
01497         mSpec.Cut(start, lengthToCut);
01498         mAuthority.mLen -= lengthToCut;
01499         ShiftFromPath(-lengthToCut);
01500     }
01501     else {
01502         // need to replace the existing port
01503         nsCAutoString buf;
01504         buf.AppendInt(port);
01505         PRUint32 start = mHost.mPos + mHost.mLen + 1;
01506         PRUint32 length = mPath.mPos - start;
01507         mSpec.Replace(start, length, buf);
01508         if (buf.Length() != length) {
01509             mAuthority.mLen += buf.Length() - length;
01510             ShiftFromPath(buf.Length() - length);
01511         }
01512     }
01513 
01514     mPort = port;
01515     return NS_OK;
01516 }
01517 
01518 NS_IMETHODIMP
01519 nsStandardURL::SetPath(const nsACString &input)
01520 {
01521     ENSURE_MUTABLE();
01522 
01523     const nsPromiseFlatCString &path = PromiseFlatCString(input);
01524 
01525     LOG(("nsStandardURL::SetPath [path=%s]\n", path.get()));
01526 
01527     InvalidateCache();
01528 
01529     if (!path.IsEmpty()) {
01530         nsCAutoString spec;
01531 
01532         spec.Assign(mSpec.get(), mPath.mPos);
01533         if (path.First() != '/')
01534             spec.Append('/');
01535         spec.Append(path);
01536 
01537         return SetSpec(spec);
01538     }
01539     else if (mPath.mLen > 1) {
01540         mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1);
01541         // these contain only a '/'
01542         mPath.mLen = 1;
01543         mDirectory.mLen = 1;
01544         mFilepath.mLen = 1;
01545         // these are no longer defined
01546         mBasename.mLen = -1;
01547         mExtension.mLen = -1;
01548         mParam.mLen = -1;
01549         mQuery.mLen = -1;
01550         mRef.mLen = -1;
01551     }
01552     return NS_OK;
01553 }
01554 
01555 NS_IMETHODIMP
01556 nsStandardURL::Equals(nsIURI *unknownOther, PRBool *result)
01557 {
01558     NS_ENSURE_ARG_POINTER(unknownOther);
01559     NS_PRECONDITION(result, "null pointer");
01560 
01561     nsRefPtr<nsStandardURL> otherPtr;
01562     nsresult rv = unknownOther->QueryInterface(kThisImplCID,
01563                                                getter_AddRefs(otherPtr));
01564 
01565     // Hack around issue with MSVC++ not allowing the nsDerivedSafe to access
01566     // the private members and not doing the implicit conversion to a raw
01567     // pointer.
01568     nsStandardURL* other = otherPtr;
01569     if (NS_FAILED(rv)) {
01570         *result = PR_FALSE;
01571         return NS_OK;
01572     }
01573 
01574     // First, check whether one URIs is an nsIFileURL while the other
01575     // is not.  If that's the case, they're different.
01576     if (mSupportsFileURL != other->mSupportsFileURL) {
01577         *result = PR_FALSE;
01578         return NS_OK;
01579     }
01580 
01581     // Next check parts of a URI that, if different, automatically make the
01582     // URIs different
01583     if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) ||
01584         // Check for host manually, since conversion to file will
01585         // ignore the host!
01586         !SegmentIs(mHost, other->mSpec.get(), other->mHost) ||
01587         !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) ||
01588         !SegmentIs(mRef, other->mSpec.get(), other->mRef) ||
01589         !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) ||
01590         !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) ||
01591         Port() != other->Port() ||
01592         !SegmentIs(mParam, other->mSpec.get(), other->mParam)) {
01593         // No need to compare files or other URI parts -- these are different
01594         // beasties
01595         *result = PR_FALSE;
01596         return NS_OK;
01597     }
01598     
01599     // Then check for exact identity of URIs.  If we have it, they're equal
01600     if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) &&
01601         SegmentIs(mBasename, other->mSpec.get(), other->mBasename) &&
01602         SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) {
01603         *result = PR_TRUE;
01604         return NS_OK;
01605     }
01606 
01607     // At this point, the URIs are not identical, but they only differ in the
01608     // directory/filename/extension.  If these are file URLs, then get the
01609     // corresponding file objects and compare those, since two filenames that
01610     // differ, eg, only in case could still be equal.
01611     if (mSupportsFileURL) {
01612         // Assume not equal for failure cases... but failures in GetFile are
01613         // really failures, more or less, so propagate them to caller.
01614         *result = PR_FALSE;
01615         
01616         rv = EnsureFile();
01617         if (NS_FAILED(rv)) {
01618             LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file",
01619                 this, mSpec.get()));
01620             return rv;
01621         }
01622         NS_ASSERTION(mFile, "EnsureFile() lied!");
01623         rv = other->EnsureFile();
01624         if (NS_FAILED(rv)) {
01625             LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file",
01626                 other, other->mSpec.get()));
01627             return rv;
01628         }
01629         NS_ASSERTION(other->mFile, "EnsureFile() lied!");
01630         return mFile->Equals(other->mFile, result);
01631     }
01632 
01633     // The URLs are not identical, and they do not correspond to the
01634     // same file, so they are different.
01635     *result = PR_FALSE;
01636 
01637     return NS_OK;
01638 }
01639 
01640 NS_IMETHODIMP
01641 nsStandardURL::SchemeIs(const char *scheme, PRBool *result)
01642 {
01643     NS_PRECONDITION(result, "null pointer");
01644 
01645     *result = SegmentIs(mScheme, scheme);
01646     return NS_OK;
01647 }
01648 
01649 /* virtual */ nsStandardURL*
01650 nsStandardURL::StartClone()
01651 {
01652     nsStandardURL *clone;
01653     NS_NEWXPCOM(clone, nsStandardURL);
01654     return clone;
01655 }
01656 
01657 NS_IMETHODIMP
01658 nsStandardURL::Clone(nsIURI **result)
01659 {
01660     nsStandardURL *clone = StartClone();
01661     if (!clone)
01662         return NS_ERROR_OUT_OF_MEMORY;
01663 
01664     clone->mSpec = mSpec;
01665     clone->mDefaultPort = mDefaultPort;
01666     clone->mPort = mPort;
01667     clone->mScheme = mScheme;
01668     clone->mAuthority = mAuthority;
01669     clone->mUsername = mUsername;
01670     clone->mPassword = mPassword;
01671     clone->mHost = mHost;
01672     clone->mPath = mPath;
01673     clone->mFilepath = mFilepath;
01674     clone->mDirectory = mDirectory;
01675     clone->mBasename = mBasename;
01676     clone->mExtension = mExtension;
01677     clone->mParam = mParam;
01678     clone->mQuery = mQuery;
01679     clone->mRef = mRef;
01680     clone->mOriginCharset = mOriginCharset;
01681     clone->mURLType = mURLType;
01682     clone->mParser = mParser;
01683     clone->mFile = mFile;
01684     clone->mHostA = mHostA ? nsCRT::strdup(mHostA) : nsnull;
01685     clone->mMutable = PR_TRUE;
01686     clone->mSupportsFileURL = mSupportsFileURL;
01687     clone->mHostEncoding = mHostEncoding;
01688     clone->mSpecEncoding = mSpecEncoding;
01689 
01690     NS_ADDREF(*result = clone);
01691     return NS_OK;
01692 }
01693 
01694 NS_IMETHODIMP
01695 nsStandardURL::Resolve(const nsACString &in, nsACString &out)
01696 {
01697     const nsPromiseFlatCString &flat = PromiseFlatCString(in);
01698     const char *relpath = flat.get();
01699 
01700     // filter out unexpected chars "\r\n\t" if necessary
01701     nsCAutoString buf;
01702     PRInt32 relpathLen;
01703     if (net_FilterURIString(relpath, buf)) {
01704         relpath = buf.get();
01705         relpathLen = buf.Length();
01706     } else
01707         relpathLen = flat.Length();
01708     
01709     // XXX hack hack hack
01710     char *p = nsnull;
01711     char **result = &p;
01712 
01713     LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n",
01714         this, mSpec.get(), relpath));
01715 
01716     NS_ASSERTION(mParser, "no parser: unitialized");
01717 
01718     // NOTE: there is no need for this function to produce normalized
01719     // output.  normalization will occur when the result is used to 
01720     // initialize a nsStandardURL object.
01721 
01722     if (mScheme.mLen < 0) {
01723         NS_ERROR("unable to Resolve URL: this URL not initialized");
01724         return NS_ERROR_NOT_INITIALIZED;
01725     }
01726 
01727     nsresult rv;
01728     URLSegment scheme;
01729     char *resultPath = nsnull;
01730     PRBool relative = PR_FALSE;
01731     PRUint32 offset = 0;
01732     netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
01733 
01734     // relative urls should never contain a host, so we always want to use
01735     // the noauth url parser.
01736     // use it to extract a possible scheme
01737     rv = mParser->ParseURL(relpath, 
01738                            relpathLen,
01739                            &scheme.mPos, &scheme.mLen,
01740                            nsnull, nsnull,
01741                            nsnull, nsnull);
01742 
01743     // if the parser fails (for example because there is no valid scheme)
01744     // reset the scheme and assume a relative url
01745     if (NS_FAILED(rv)) scheme.Reset(); 
01746 
01747     if (scheme.mLen >= 0) {
01748         // add some flags to coalesceFlag if it is an ftp-url
01749         // need this later on when coalescing the resulting URL
01750         if (SegmentIs(relpath, scheme, "ftp")) {
01751             coalesceFlag = (netCoalesceFlags) (coalesceFlag 
01752                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
01753                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
01754 
01755         }
01756         // this URL appears to be absolute
01757         // but try to find out more
01758         if (SegmentIs(mScheme,relpath,scheme)) {
01759             // mScheme and Scheme are the same 
01760             // but this can still be relative
01761             if (nsCRT::strncmp(relpath + scheme.mPos + scheme.mLen,
01762                                "://",3) == 0) {
01763                 // now this is really absolute
01764                 // because a :// follows the scheme 
01765                 *result = nsCRT::strdup(relpath);
01766             } else {         
01767                 // This is a deprecated form of relative urls like
01768                 // http:file or http:/path/file
01769                 // we will support it for now ...
01770                 relative = PR_TRUE;
01771                 offset = scheme.mLen + 1;
01772             }
01773         } else {
01774             // the schemes are not the same, we are also done
01775             // because we have to assume this is absolute 
01776             *result = nsCRT::strdup(relpath);
01777         }  
01778     } else {
01779         // add some flags to coalesceFlag if it is an ftp-url
01780         // need this later on when coalescing the resulting URL
01781         if (SegmentIs(mScheme,"ftp")) {
01782             coalesceFlag = (netCoalesceFlags) (coalesceFlag 
01783                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
01784                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
01785         }
01786         if (relpath[0] == '/' && relpath[1] == '/') {
01787             // this URL //host/path is almost absolute
01788             *result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath);
01789         } else {
01790             // then it must be relative 
01791             relative = PR_TRUE;
01792         }
01793     }
01794     if (relative) {
01795         PRUint32 len = 0;
01796         const char *realrelpath = relpath + offset;
01797         switch (*realrelpath) {
01798         case '/':
01799             // overwrite everything after the authority
01800             len = mAuthority.mPos + mAuthority.mLen;
01801             break;
01802         case '?':
01803             // overwrite the existing ?query and #ref
01804             if (mQuery.mLen >= 0)
01805                 len = mQuery.mPos - 1;
01806             else if (mRef.mLen >= 0)
01807                 len = mRef.mPos - 1;
01808             else
01809                 len = mPath.mPos + mPath.mLen;
01810             break;
01811         case '#':
01812         case '\0':
01813             // overwrite the existing #ref
01814             if (mRef.mLen < 0)
01815                 len = mPath.mPos + mPath.mLen;
01816             else
01817                 len = mRef.mPos - 1;
01818             break;
01819         default:
01820             if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) {
01821                 if (Filename().Equals(NS_LITERAL_CSTRING("%2F"),
01822                                       nsCaseInsensitiveCStringComparator())) {
01823                     // if ftp URL ends with %2F then simply
01824                     // append relative part because %2F also
01825                     // marks the root directory with ftp-urls
01826                     len = mFilepath.mPos + mFilepath.mLen;
01827                 } else {
01828                     // overwrite everything after the directory 
01829                     len = mDirectory.mPos + mDirectory.mLen;
01830                 }
01831             } else { 
01832                 // overwrite everything after the directory 
01833                 len = mDirectory.mPos + mDirectory.mLen;
01834             }
01835         }
01836         *result = AppendToSubstring(0, len, realrelpath);
01837         // locate result path
01838         resultPath = *result + mPath.mPos;
01839     }
01840     if (!*result)
01841         return NS_ERROR_OUT_OF_MEMORY;
01842 
01843     if (resultPath)
01844         net_CoalesceDirs(coalesceFlag, resultPath);
01845     else {
01846         // locate result path
01847         resultPath = PL_strstr(*result, "://");
01848         if (resultPath) {
01849             resultPath = PL_strchr(resultPath + 3, '/');
01850             if (resultPath)
01851                 net_CoalesceDirs(coalesceFlag,resultPath);
01852         }
01853     }
01854     // XXX avoid extra copy
01855     out = *result;
01856     free(*result);
01857     return NS_OK;
01858 }
01859 
01860 // result may contain unescaped UTF-8 characters
01861 NS_IMETHODIMP
01862 nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult)
01863 {
01864     NS_ENSURE_ARG_POINTER(uri2);
01865 
01866     // if uri's are equal, then return uri as is
01867     PRBool isEquals = PR_FALSE;
01868     if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
01869         return GetSpec(aResult);
01870 
01871     aResult.Truncate();
01872 
01873     // check pre-path; if they don't match, then return empty string
01874     nsStandardURL *stdurl2;
01875     nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
01876     isEquals = NS_SUCCEEDED(rv)
01877             && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)    
01878             && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
01879             && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
01880             && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
01881             && (Port() == stdurl2->Port());
01882     if (!isEquals)
01883     {
01884         if (NS_SUCCEEDED(rv))
01885             NS_RELEASE(stdurl2);
01886         return NS_OK;
01887     }
01888 
01889     // scan for first mismatched character
01890     const char *thisIndex, *thatIndex, *startCharPos;
01891     startCharPos = mSpec.get() + mDirectory.mPos;
01892     thisIndex = startCharPos;
01893     thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
01894     while ((*thisIndex == *thatIndex) && *thisIndex)
01895     {
01896         thisIndex++;
01897         thatIndex++;
01898     }
01899 
01900     // backup to just after previous slash so we grab an appropriate path
01901     // segment such as a directory (not partial segments)
01902     // todo:  also check for file matches which include '?', '#', and ';'
01903     while ((*(thisIndex-1) != '/') && (thisIndex != startCharPos))
01904         thisIndex--;
01905 
01906     // grab spec from beginning to thisIndex
01907     aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get());
01908 
01909     NS_RELEASE(stdurl2);
01910     return rv;
01911 }
01912 
01913 NS_IMETHODIMP
01914 nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult)
01915 {
01916     NS_ENSURE_ARG_POINTER(uri2);
01917 
01918     aResult.Truncate();
01919 
01920     // if uri's are equal, then return empty string
01921     PRBool isEquals = PR_FALSE;
01922     if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
01923         return NS_OK;
01924 
01925     nsStandardURL *stdurl2;
01926     nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
01927     isEquals = NS_SUCCEEDED(rv)
01928             && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)    
01929             && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
01930             && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
01931             && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
01932             && (Port() == stdurl2->Port());
01933     if (!isEquals)
01934     {
01935         if (NS_SUCCEEDED(rv))
01936             NS_RELEASE(stdurl2);
01937 
01938         return uri2->GetSpec(aResult);
01939     }
01940 
01941     // scan for first mismatched character
01942     const char *thisIndex, *thatIndex, *startCharPos;
01943     startCharPos = mSpec.get() + mDirectory.mPos;
01944     thisIndex = startCharPos;
01945     thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
01946 
01947 #ifdef XP_WIN
01948     PRBool isFileScheme = SegmentIs(mScheme, "file");
01949     if (isFileScheme)
01950     {
01951         // on windows, we need to match the first segment of the path
01952         // if these don't match then we need to return an absolute path
01953         // skip over any leading '/' in path
01954         while ((*thisIndex == *thatIndex) && (*thisIndex == '/'))
01955         {
01956             thisIndex++;
01957             thatIndex++;
01958         }
01959         // look for end of first segment
01960         while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/'))
01961         {
01962             thisIndex++;
01963             thatIndex++;
01964         }
01965 
01966         // if we didn't match through the first segment, return absolute path
01967         if ((*thisIndex != '/') || (*thatIndex != '/'))
01968         {
01969             NS_RELEASE(stdurl2);
01970             return uri2->GetSpec(aResult);
01971         }
01972     }
01973 #endif
01974 
01975     while ((*thisIndex == *thatIndex) && *thisIndex)
01976     {
01977         thisIndex++;
01978         thatIndex++;
01979     }
01980 
01981     // backup to just after previous slash so we grab an appropriate path
01982     // segment such as a directory (not partial segments)
01983     // todo:  also check for file matches with '#', '?' and ';'
01984     while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos))
01985         thatIndex--;
01986 
01987     // need to account for slashes and add corresponding "../"
01988     while (*thisIndex)
01989     {
01990         if (*thisIndex == '/')
01991             aResult.AppendLiteral("../");
01992 
01993         thisIndex++;
01994     }
01995 
01996     // grab spec from thisIndex to end
01997     PRUint32 startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get();
01998     aResult.Append(Substring(stdurl2->mSpec, startPos, 
01999                              stdurl2->mSpec.Length() - startPos));
02000 
02001     NS_RELEASE(stdurl2);
02002     return rv;
02003 }
02004 
02005 //----------------------------------------------------------------------------
02006 // nsStandardURL::nsIURL
02007 //----------------------------------------------------------------------------
02008 
02009 // result may contain unescaped UTF-8 characters
02010 NS_IMETHODIMP
02011 nsStandardURL::GetFilePath(nsACString &result)
02012 {
02013     result = Filepath();
02014     return NS_OK;
02015 }
02016 
02017 // result may contain unescaped UTF-8 characters
02018 NS_IMETHODIMP
02019 nsStandardURL::GetParam(nsACString &result)
02020 {
02021     result = Param();
02022     return NS_OK;
02023 }
02024 
02025 // result may contain unescaped UTF-8 characters
02026 NS_IMETHODIMP
02027 nsStandardURL::GetQuery(nsACString &result)
02028 {
02029     result = Query();
02030     return NS_OK;
02031 }
02032 
02033 // result may contain unescaped UTF-8 characters
02034 NS_IMETHODIMP
02035 nsStandardURL::GetRef(nsACString &result)
02036 {
02037     result = Ref();
02038     return NS_OK;
02039 }
02040 
02041 // result may contain unescaped UTF-8 characters
02042 NS_IMETHODIMP
02043 nsStandardURL::GetDirectory(nsACString &result)
02044 {
02045     result = Directory();
02046     return NS_OK;
02047 }
02048 
02049 // result may contain unescaped UTF-8 characters
02050 NS_IMETHODIMP
02051 nsStandardURL::GetFileName(nsACString &result)
02052 {
02053     result = Filename();
02054     return NS_OK;
02055 }
02056 
02057 // result may contain unescaped UTF-8 characters
02058 NS_IMETHODIMP
02059 nsStandardURL::GetFileBaseName(nsACString &result)
02060 {
02061     result = Basename();
02062     return NS_OK;
02063 }
02064 
02065 // result may contain unescaped UTF-8 characters
02066 NS_IMETHODIMP
02067 nsStandardURL::GetFileExtension(nsACString &result)
02068 {
02069     result = Extension();
02070     return NS_OK;
02071 }
02072 
02073 NS_IMETHODIMP
02074 nsStandardURL::SetFilePath(const nsACString &input)
02075 {
02076     ENSURE_MUTABLE();
02077 
02078     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
02079     const char *filepath = flat.get();
02080 
02081     LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath));
02082 
02083     // if there isn't a filepath, then there can't be anything
02084     // after the path either.  this url is likely uninitialized.
02085     if (mFilepath.mLen < 0)
02086         return SetPath(flat);
02087 
02088     if (filepath && *filepath) {
02089         nsCAutoString spec;
02090         PRUint32 dirPos, basePos, extPos;
02091         PRInt32 dirLen, baseLen, extLen;
02092         nsresult rv;
02093 
02094         rv = mParser->ParseFilePath(filepath, -1,
02095                                     &dirPos, &dirLen,
02096                                     &basePos, &baseLen,
02097                                     &extPos, &extLen);
02098         if (NS_FAILED(rv)) return rv;
02099 
02100         // build up new candidate spec
02101         spec.Assign(mSpec.get(), mPath.mPos);
02102 
02103         // ensure leading '/'
02104         if (filepath[dirPos] != '/')
02105             spec.Append('/');
02106 
02107         GET_SEGMENT_ENCODER(encoder);
02108 
02109         // append encoded filepath components
02110         if (dirLen > 0)
02111             encoder.EncodeSegment(Substring(filepath + dirPos,
02112                                             filepath + dirPos + dirLen),
02113                                   esc_Directory | esc_AlwaysCopy, spec);
02114         if (baseLen > 0)
02115             encoder.EncodeSegment(Substring(filepath + basePos,
02116                                             filepath + basePos + baseLen),
02117                                   esc_FileBaseName | esc_AlwaysCopy, spec);
02118         if (extLen >= 0) {
02119             spec.Append('.');
02120             if (extLen > 0)
02121                 encoder.EncodeSegment(Substring(filepath + extPos,
02122                                                 filepath + extPos + extLen),
02123                                       esc_FileExtension | esc_AlwaysCopy,
02124                                       spec);
02125         }
02126 
02127         // compute the ending position of the current filepath
02128         if (mFilepath.mLen >= 0) {
02129             PRUint32 end = mFilepath.mPos + mFilepath.mLen;
02130             if (mSpec.Length() > end)
02131                 spec.Append(mSpec.get() + end, mSpec.Length() - end);
02132         }
02133 
02134         return SetSpec(spec);
02135     }
02136     else if (mPath.mLen > 1) {
02137         mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1);
02138         // left shift param, query, and ref
02139         ShiftFromParam(1 - mFilepath.mLen);
02140         // these contain only a '/'
02141         mPath.mLen = 1;
02142         mDirectory.mLen = 1;
02143         mFilepath.mLen = 1;
02144         // these are no longer defined
02145         mBasename.mLen = -1;
02146         mExtension.mLen = -1;
02147     }
02148     return NS_OK;
02149 }
02150 
02151 NS_IMETHODIMP
02152 nsStandardURL::SetParam(const nsACString &input)
02153 {
02154     NS_NOTYETIMPLEMENTED("");
02155     return NS_ERROR_NOT_IMPLEMENTED;
02156 }
02157 
02158 NS_IMETHODIMP
02159 nsStandardURL::SetQuery(const nsACString &input)
02160 {
02161     ENSURE_MUTABLE();
02162 
02163     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
02164     const char *query = flat.get();
02165 
02166     LOG(("nsStandardURL::SetQuery [query=%s]\n", query));
02167 
02168     if (mPath.mLen < 0)
02169         return SetPath(flat);
02170 
02171     InvalidateCache();
02172 
02173     if (!query || !*query) {
02174         // remove existing query
02175         if (mQuery.mLen >= 0) {
02176             // remove query and leading '?'
02177             mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1);
02178             ShiftFromRef(-(mQuery.mLen + 1));
02179             mPath.mLen -= (mQuery.mLen + 1);
02180             mQuery.mPos = 0;
02181             mQuery.mLen = -1;
02182         }
02183         return NS_OK;
02184     }
02185 
02186     PRInt32 queryLen = strlen(query);
02187     if (query[0] == '?') {
02188         query++;
02189         queryLen--;
02190     }
02191 
02192     if (mQuery.mLen < 0) {
02193         if (mRef.mLen < 0)
02194             mQuery.mPos = mSpec.Length();
02195         else
02196             mQuery.mPos = mRef.mPos - 1;
02197         mSpec.Insert('?', mQuery.mPos);
02198         mQuery.mPos++;
02199         mQuery.mLen = 0;
02200         // the insertion pushes these out by 1
02201         mPath.mLen++;
02202         mRef.mPos++;
02203     }
02204 
02205     // encode query if necessary
02206     nsCAutoString buf;
02207     PRBool encoded;
02208     GET_SEGMENT_ENCODER(encoder);
02209     encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query,
02210                                buf, encoded);
02211     if (encoded) {
02212         query = buf.get();
02213         queryLen = buf.Length();
02214     }
02215 
02216     PRInt32 shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen);
02217 
02218     if (shift) {
02219         mQuery.mLen = queryLen;
02220         mPath.mLen += shift;
02221         ShiftFromRef(queryLen - mQuery.mLen);
02222     }
02223     return NS_OK;
02224 }
02225 
02226 NS_IMETHODIMP
02227 nsStandardURL::SetRef(const nsACString &input)
02228 {
02229     ENSURE_MUTABLE();
02230 
02231     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
02232     const char *ref = flat.get();
02233 
02234     LOG(("nsStandardURL::SetRef [ref=%s]\n", ref));
02235 
02236     if (mPath.mLen < 0)
02237         return SetPath(flat);
02238 
02239     InvalidateCache();
02240 
02241     if (!ref || !*ref) {
02242         // remove existing ref
02243         if (mRef.mLen >= 0) {
02244             // remove ref and leading '#'
02245             mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1);
02246             mPath.mLen -= (mRef.mLen + 1);
02247             mRef.mPos = 0;
02248             mRef.mLen = -1;
02249         }
02250         return NS_OK;
02251     }
02252             
02253     PRInt32 refLen = strlen(ref);
02254     if (ref[0] == '#') {
02255         ref++;
02256         refLen--;
02257     }
02258     
02259     if (mRef.mLen < 0) {
02260         mSpec.Append('#');
02261         ++mPath.mLen;  // Include the # in the path.
02262         mRef.mPos = mSpec.Length();
02263         mRef.mLen = 0;
02264     }
02265 
02266     // encode ref if necessary
02267     nsCAutoString buf;
02268     PRBool encoded;
02269     GET_SEGMENT_ENCODER(encoder);
02270     encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref,
02271                                buf, encoded);
02272     if (encoded) {
02273         ref = buf.get();
02274         refLen = buf.Length();
02275     }
02276 
02277     ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen);
02278     mPath.mLen += (refLen - mRef.mLen);
02279     mRef.mLen = refLen;
02280     return NS_OK;
02281 }
02282 
02283 NS_IMETHODIMP
02284 nsStandardURL::SetDirectory(const nsACString &input)
02285 {
02286     NS_NOTYETIMPLEMENTED("");
02287     return NS_ERROR_NOT_IMPLEMENTED;
02288 }
02289 
02290 NS_IMETHODIMP
02291 nsStandardURL::SetFileName(const nsACString &input)
02292 {
02293     ENSURE_MUTABLE();
02294 
02295     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
02296     const char *filename = flat.get();
02297 
02298     LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename));
02299 
02300     if (mPath.mLen < 0)
02301         return SetPath(flat);
02302 
02303     PRInt32 shift = 0;
02304 
02305     if (!(filename && *filename)) {
02306         // remove the filename
02307         if (mBasename.mLen > 0) {
02308             if (mExtension.mLen >= 0)
02309                 mBasename.mLen += (mExtension.mLen + 1);
02310             mSpec.Cut(mBasename.mPos, mBasename.mLen);
02311             shift = -mBasename.mLen;
02312             mBasename.mLen = 0;
02313             mExtension.mLen = -1;
02314         }
02315     }
02316     else {
02317         nsresult rv;
02318         URLSegment basename, extension;
02319 
02320         // let the parser locate the basename and extension
02321         rv = mParser->ParseFileName(filename, -1,
02322                                     &basename.mPos, &basename.mLen,
02323                                     &extension.mPos, &extension.mLen);
02324         if (NS_FAILED(rv)) return rv;
02325 
02326         if (basename.mLen < 0) {
02327             // remove existing filename
02328             if (mBasename.mLen >= 0) {
02329                 PRUint32 len = mBasename.mLen;
02330                 if (mExtension.mLen >= 0)
02331                     len += (mExtension.mLen + 1);
02332                 mSpec.Cut(mBasename.mPos, len);
02333                 shift = -PRInt32(len);
02334                 mBasename.mLen = 0;
02335                 mExtension.mLen = -1;
02336             }
02337         }
02338         else {
02339             nsCAutoString newFilename;
02340             PRBool ignoredOut;
02341             GET_SEGMENT_ENCODER(encoder);
02342             basename.mLen = encoder.EncodeSegmentCount(filename, basename,
02343                                                        esc_FileBaseName |
02344                                                        esc_AlwaysCopy,
02345                                                        newFilename,
02346                                                        ignoredOut);
02347             if (extension.mLen >= 0) {
02348                 newFilename.Append('.');
02349                 extension.mLen = encoder.EncodeSegmentCount(filename, extension,
02350                                                             esc_FileExtension |
02351                                                             esc_AlwaysCopy,
02352                                                             newFilename,
02353                                                             ignoredOut);
02354             }
02355 
02356             if (mBasename.mLen < 0) {
02357                 // insert new filename
02358                 mBasename.mPos = mDirectory.mPos + mDirectory.mLen;
02359                 mSpec.Insert(newFilename, mBasename.mPos);
02360                 shift = newFilename.Length();
02361             }
02362             else {
02363                 // replace existing filename
02364                 PRUint32 oldLen = PRUint32(mBasename.mLen);
02365                 if (mExtension.mLen >= 0)
02366                     oldLen += (mExtension.mLen + 1);
02367                 mSpec.Replace(mBasename.mPos, oldLen, newFilename);
02368                 shift = newFilename.Length() - oldLen;
02369             }
02370             
02371             mBasename.mLen = basename.mLen;
02372             mExtension.mLen = extension.mLen;
02373             if (mExtension.mLen >= 0)
02374                 mExtension.mPos = mBasename.mPos + mBasename.mLen + 1;
02375         }
02376     }
02377     if (shift) {
02378         ShiftFromParam(shift);
02379         mFilepath.mLen += shift;
02380         mPath.mLen += shift;
02381     }
02382     return NS_OK;
02383 }
02384 
02385 NS_IMETHODIMP
02386 nsStandardURL::SetFileBaseName(const nsACString &input)
02387 {
02388     nsCAutoString extension;
02389     nsresult rv = GetFileExtension(extension);
02390     NS_ENSURE_SUCCESS(rv, rv);
02391 
02392     nsCAutoString newFileName(input);
02393 
02394     if (!extension.IsEmpty()) {
02395         newFileName.Append('.');
02396         newFileName.Append(extension);
02397     }
02398 
02399     return SetFileName(newFileName);
02400 }
02401 
02402 NS_IMETHODIMP
02403 nsStandardURL::SetFileExtension(const nsACString &input)
02404 {
02405     nsCAutoString newFileName;
02406     nsresult rv = GetFileBaseName(newFileName);
02407     NS_ENSURE_SUCCESS(rv, rv);
02408 
02409     if (!input.IsEmpty()) {
02410         newFileName.Append('.');
02411         newFileName.Append(input);
02412     }
02413 
02414     return SetFileName(newFileName);
02415 }
02416 
02417 //----------------------------------------------------------------------------
02418 // nsStandardURL::nsIFileURL
02419 //----------------------------------------------------------------------------
02420 
02421 nsresult
02422 nsStandardURL::EnsureFile()
02423 {
02424     NS_PRECONDITION(mSupportsFileURL,
02425                     "EnsureFile() called on a URL that doesn't support files!");
02426     if (mFile) {
02427         // Nothing to do
02428         return NS_OK;
02429     }
02430 
02431     // Parse the spec if we don't have a cached result
02432     if (mSpec.IsEmpty()) {
02433         NS_ERROR("url not initialized");
02434         return NS_ERROR_NOT_INITIALIZED;
02435     }
02436 
02437     if (!SegmentIs(mScheme, "file")) {
02438         NS_ERROR("not a file URL");
02439         return NS_ERROR_FAILURE;
02440     }
02441 
02442     return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile));
02443 }
02444 
02445 NS_IMETHODIMP
02446 nsStandardURL::GetFile(nsIFile **result)
02447 {
02448     NS_PRECONDITION(mSupportsFileURL,
02449                     "GetFile() called on a URL that doesn't support files!");
02450     nsresult rv = EnsureFile();
02451     if (NS_FAILED(rv))
02452         return rv;
02453 
02454 #if defined(PR_LOGGING)
02455     if (LOG_ENABLED()) {
02456         nsCAutoString path;
02457         mFile->GetNativePath(path);
02458         LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n",
02459             this, mSpec.get(), path.get()));
02460     }
02461 #endif
02462 
02463     // clone the file, so the caller can modify it.
02464     // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the
02465     // nsIFile returned from this method; but it seems that some folks do
02466     // (see bug 161921). until we can be sure that all the consumers are
02467     // behaving themselves, we'll stay on the safe side and clone the file.
02468     // see bug 212724 about fixing the consumers.
02469     return mFile->Clone(result);
02470 }
02471 
02472 NS_IMETHODIMP
02473 nsStandardURL::SetFile(nsIFile *file)
02474 {
02475     ENSURE_MUTABLE();
02476 
02477     NS_PRECONDITION(file, "null pointer");
02478 
02479     nsresult rv;
02480     nsCAutoString url;
02481 
02482     rv = net_GetURLSpecFromFile(file, url);
02483     if (NS_FAILED(rv)) return rv;
02484 
02485     SetSpec(url);
02486 
02487     rv = Init(mURLType, mDefaultPort, url, nsnull, nsnull);
02488 
02489     // must clone |file| since its value is not guaranteed to remain constant
02490     if (NS_SUCCEEDED(rv)) {
02491         InvalidateCache();
02492         if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) {
02493             NS_WARNING("nsIFile::Clone failed");
02494             // failure to clone is not fatal (GetFile will generate mFile)
02495             mFile = 0;
02496         }
02497     }
02498     return rv;
02499 }
02500 
02501 //----------------------------------------------------------------------------
02502 // nsStandardURL::nsIStandardURL
02503 //----------------------------------------------------------------------------
02504 
02505 inline PRBool
02506 IsUTFCharset(const char *aCharset)
02507 {
02508     return ((aCharset[0] == 'U' || aCharset[0] == 'u') &&
02509             (aCharset[1] == 'T' || aCharset[1] == 't') &&
02510             (aCharset[2] == 'F' || aCharset[2] == 'f'));
02511 }
02512 
02513 NS_IMETHODIMP
02514 nsStandardURL::Init(PRUint32 urlType,
02515                     PRInt32 defaultPort,
02516                     const nsACString &spec,
02517                     const char *charset,
02518                     nsIURI *baseURI)
02519 {
02520     ENSURE_MUTABLE();
02521 
02522     InvalidateCache();
02523 
02524     switch (urlType) {
02525     case URLTYPE_STANDARD:
02526         mParser = net_GetStdURLParser();
02527         break;
02528     case URLTYPE_AUTHORITY:
02529         mParser = net_GetAuthURLParser();
02530         break;
02531     case URLTYPE_NO_AUTHORITY:
02532         mParser = net_GetNoAuthURLParser();
02533         break;
02534     default:
02535         NS_NOTREACHED("bad urlType");
02536         return NS_ERROR_INVALID_ARG;
02537     }
02538     mDefaultPort = defaultPort;
02539     mURLType = urlType;
02540 
02541     mOriginCharset.Truncate();
02542 
02543     if (charset == nsnull || *charset == '\0') {
02544         // check if baseURI provides an origin charset and use that.
02545         if (baseURI)
02546             baseURI->GetOriginCharset(mOriginCharset);
02547 
02548         // URI can't be encoded in UTF-16, UTF-16BE, UTF-16LE, UTF-32,
02549         // UTF-32-LE, UTF-32LE, UTF-32BE (yet?). Truncate mOriginCharset if
02550         // it starts with "utf" (since an empty mOriginCharset implies
02551         // UTF-8, this is safe even if mOriginCharset is UTF-8).
02552 
02553         if (mOriginCharset.Length() > 3 &&
02554             IsUTFCharset(mOriginCharset.get())) {
02555             mOriginCharset.Truncate();
02556         }
02557     }
02558     else if (!IsUTFCharset(charset)) {
02559         mOriginCharset = charset;
02560     }
02561 
02562     if (baseURI) {
02563         PRUint32 start, end;
02564         // pull out the scheme and where it ends
02565         nsresult rv = net_ExtractURLScheme(spec, &start, &end, nsnull);
02566         if (NS_SUCCEEDED(rv) && spec.Length() > end+2) {
02567             nsACString::const_iterator slash;
02568             spec.BeginReading(slash);
02569             slash.advance(end+1);
02570             // then check if // follows
02571             // if it follows, aSpec is really absolute ... 
02572             // ignore aBaseURI in this case
02573             if (*slash == '/' && *(++slash) == '/')
02574                 baseURI = nsnull;
02575         }
02576     }
02577 
02578     if (!baseURI)
02579         return SetSpec(spec);
02580 
02581     nsCAutoString buf;
02582     nsresult rv = baseURI->Resolve(spec, buf);
02583     if (NS_FAILED(rv)) return rv;
02584 
02585     return SetSpec(buf);
02586 }
02587 
02588 NS_IMETHODIMP
02589 nsStandardURL::GetMutable(PRBool *value)
02590 {
02591     *value = mMutable;
02592     return NS_OK;
02593 }
02594 
02595 NS_IMETHODIMP
02596 nsStandardURL::SetMutable(PRBool value)
02597 {
02598     mMutable = value;
02599     return NS_OK;
02600 }
02601 
02602 //----------------------------------------------------------------------------
02603 // nsStandardURL::nsISerializable
02604 //----------------------------------------------------------------------------
02605 
02606 NS_IMETHODIMP
02607 nsStandardURL::Read(nsIObjectInputStream *stream)
02608 {
02609     nsresult rv;
02610     
02611     PRUint32 urlType;
02612     rv = stream->Read32(&urlType);
02613     if (NS_FAILED(rv)) return rv;
02614     mURLType = urlType;
02615     switch (mURLType) {
02616       case URLTYPE_STANDARD:
02617         mParser = net_GetStdURLParser();
02618         break;
02619       case URLTYPE_AUTHORITY:
02620         mParser = net_GetAuthURLParser();
02621         break;
02622       case URLTYPE_NO_AUTHORITY:
02623         mParser = net_GetNoAuthURLParser();
02624         break;
02625       default:
02626         NS_NOTREACHED("bad urlType");
02627         return NS_ERROR_FAILURE;
02628     }
02629 
02630     rv = stream->Read32((PRUint32 *) &mPort);
02631     if (NS_FAILED(rv)) return rv;
02632 
02633     rv = stream->Read32((PRUint32 *) &mDefaultPort);
02634     if (NS_FAILED(rv)) return rv;
02635 
02636     rv = NS_ReadOptionalCString(stream, mSpec);
02637     if (NS_FAILED(rv)) return rv;
02638 
02639     rv = ReadSegment(stream, mScheme);
02640     if (NS_FAILED(rv)) return rv;
02641 
02642     rv = ReadSegment(stream, mAuthority);
02643     if (NS_FAILED(rv)) return rv;
02644 
02645     rv = ReadSegment(stream, mUsername);
02646     if (NS_FAILED(rv)) return rv;
02647 
02648     rv = ReadSegment(stream, mPassword);
02649     if (NS_FAILED(rv)) return rv;
02650 
02651     rv = ReadSegment(stream, mHost);
02652     if (NS_FAILED(rv)) return rv;
02653 
02654     rv = ReadSegment(stream, mPath);
02655     if (NS_FAILED(rv)) return rv;
02656 
02657     rv = ReadSegment(stream, mFilepath);
02658     if (NS_FAILED(rv)) return rv;
02659 
02660     rv = ReadSegment(stream, mDirectory);
02661     if (NS_FAILED(rv)) return rv;
02662 
02663     rv = ReadSegment(stream, mBasename);
02664     if (NS_FAILED(rv)) return rv;
02665 
02666     rv = ReadSegment(stream, mExtension);
02667     if (NS_FAILED(rv)) return rv;
02668 
02669     rv = ReadSegment(stream, mParam);
02670     if (NS_FAILED(rv)) return rv;
02671 
02672     rv = ReadSegment(stream, mQuery);
02673     if (NS_FAILED(rv)) return rv;
02674 
02675     rv = ReadSegment(stream, mRef);
02676     if (NS_FAILED(rv)) return rv;
02677 
02678     rv = NS_ReadOptionalCString(stream, mOriginCharset);
02679     if (NS_FAILED(rv)) return rv;
02680 
02681     return NS_OK;
02682 }
02683 
02684 NS_IMETHODIMP
02685 nsStandardURL::Write(nsIObjectOutputStream *stream)
02686 {
02687     nsresult rv;
02688 
02689     rv = stream->Write32(mURLType);
02690     if (NS_FAILED(rv)) return rv;
02691 
02692     rv = stream->Write32(PRUint32(mPort));
02693     if (NS_FAILED(rv)) return rv;
02694 
02695     rv = stream->Write32(PRUint32(mDefaultPort));
02696     if (NS_FAILED(rv)) return rv;
02697 
02698     rv = NS_WriteOptionalStringZ(stream, mSpec.get());
02699     if (NS_FAILED(rv)) return rv;
02700 
02701     rv = WriteSegment(stream, mScheme);
02702     if (NS_FAILED(rv)) return rv;
02703 
02704     rv = WriteSegment(stream, mAuthority);
02705     if (NS_FAILED(rv)) return rv;
02706 
02707     rv = WriteSegment(stream, mUsername);
02708     if (NS_FAILED(rv)) return rv;
02709 
02710     rv = WriteSegment(stream, mPassword);
02711     if (NS_FAILED(rv)) return rv;
02712 
02713     rv = WriteSegment(stream, mHost);
02714     if (NS_FAILED(rv)) return rv;
02715 
02716     rv = WriteSegment(stream, mPath);
02717     if (NS_FAILED(rv)) return rv;
02718 
02719     rv = WriteSegment(stream, mFilepath);
02720     if (NS_FAILED(rv)) return rv;
02721 
02722     rv = WriteSegment(stream, mDirectory);
02723     if (NS_FAILED(rv)) return rv;
02724 
02725     rv = WriteSegment(stream, mBasename);
02726     if (NS_FAILED(rv)) return rv;
02727 
02728     rv = WriteSegment(stream, mExtension);
02729     if (NS_FAILED(rv)) return rv;
02730 
02731     rv = WriteSegment(stream, mParam);
02732     if (NS_FAILED(rv)) return rv;
02733 
02734     rv = WriteSegment(stream, mQuery);
02735     if (NS_FAILED(rv)) return rv;
02736 
02737     rv = WriteSegment(stream, mRef);
02738     if (NS_FAILED(rv)) return rv;
02739 
02740     rv = NS_WriteOptionalStringZ(stream, mOriginCharset.get());
02741     if (NS_FAILED(rv)) return rv;
02742 
02743     return NS_OK;
02744 }
02745 
02746 //----------------------------------------------------------------------------
02747 // nsStandardURL::nsIClassInfo
02748 //----------------------------------------------------------------------------
02749 
02750 NS_IMETHODIMP 
02751 nsStandardURL::GetInterfaces(PRUint32 *count, nsIID * **array)
02752 {
02753     *count = 0;
02754     *array = nsnull;
02755     return NS_OK;
02756 }
02757 
02758 NS_IMETHODIMP 
02759 nsStandardURL::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
02760 {
02761     *_retval = nsnull;
02762     return NS_OK;
02763 }
02764 
02765 NS_IMETHODIMP 
02766 nsStandardURL::GetContractID(char * *aContractID)
02767 {
02768     *aContractID = nsnull;
02769     return NS_OK;
02770 }
02771 
02772 NS_IMETHODIMP 
02773 nsStandardURL::GetClassDescription(char * *aClassDescription)
02774 {
02775     *aClassDescription = nsnull;
02776     return NS_OK;
02777 }
02778 
02779 NS_IMETHODIMP 
02780 nsStandardURL::GetClassID(nsCID * *aClassID)
02781 {
02782     *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
02783     if (!*aClassID)
02784         return NS_ERROR_OUT_OF_MEMORY;
02785     return GetClassIDNoAlloc(*aClassID);
02786 }
02787 
02788 NS_IMETHODIMP 
02789 nsStandardURL::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
02790 {
02791     *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
02792     return NS_OK;
02793 }
02794 
02795 NS_IMETHODIMP 
02796 nsStandardURL::GetFlags(PRUint32 *aFlags)
02797 {
02798     *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
02799     return NS_OK;
02800 }
02801 
02802 NS_IMETHODIMP 
02803 nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
02804 {
02805     *aClassIDNoAlloc = kStandardURLCID;
02806     return NS_OK;
02807 }
02808