/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Andreas Otte. * Portions created by the Initial Developer are Copyright (C) 2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsURLHelper.h" #include "prprf.h" #include "nsCRT.h" #include "nsMemory.h" #include "nsIIOService.h" #include "nsIURI.h" #include "nsEscape.h" #include "netCore.h" #if defined(XP_WIN) #include // ::IsDBCSLeadByte need #endif #if 0 /* extracts first number from a string and assumes that this is the port number*/ PRInt32 ExtractPortFrom(const nsACString &src) { // search for digits up to a slash or the string ends const nsPromiseFlatCString flat( PromiseFlatCString(src) ); const char* port = flat.get(); PRInt32 returnValue = -1; // skip leading white space while (nsCRT::IsAsciiSpace(*port)) port++; char c; while ((c = *port++) != '\0') { // stop if slash or ? or # reached if (c == '/' || c == '?' || c == '#') break; else if (!nsCRT::IsAsciiDigit(c)) return returnValue; } return (0 < PR_sscanf(flat.get(), "%d", &returnValue)) ? returnValue : -1; } #endif /* extract string from other string */ nsresult ExtractString(char* i_Src, char* *o_Dest, PRUint32 length) { NS_PRECONDITION( (nsnull != i_Src), "Extract called on empty string!"); CRTFREEIF(*o_Dest); *o_Dest = PL_strndup(i_Src, length); return (*o_Dest ? NS_OK : NS_ERROR_OUT_OF_MEMORY); } /* duplicate string */ nsresult DupString(char* *o_Dest, const char* i_Src) { if (!o_Dest) return NS_ERROR_NULL_POINTER; if (i_Src) { *o_Dest = nsCRT::strdup(i_Src); return (*o_Dest == nsnull) ? NS_ERROR_OUT_OF_MEMORY : NS_OK; } else { *o_Dest = nsnull; return NS_OK; } } // Replace all /./ with a / while resolving relative URLs // But only till #? void CoalesceDirsRel(char* io_Path) { /* Stolen from the old netlib's mkparse.c. * * modifies a url of the form /foo/../foo1 -> /foo1 * and /foo/./foo1 -> /foo/foo1 * and /foo/foo1/.. -> /foo/ */ char *fwdPtr = io_Path; char *urlPtr = io_Path; for(; (*fwdPtr != '\0') && (*fwdPtr != '?') && (*fwdPtr != '#'); ++fwdPtr) { #if defined(XP_WIN) // At first, If this is DBCS character, it skips next character. if (::IsDBCSLeadByte(*fwdPtr) && *(fwdPtr+1) != '\0') { *urlPtr++ = *fwdPtr++; *urlPtr++ = *fwdPtr; continue; } #endif if (*fwdPtr == '/' && *(fwdPtr+1) == '.' && *(fwdPtr+2) == '/' ) { // remove . followed by slash fwdPtr += 1; } else if(*fwdPtr == '/' && *(fwdPtr+1) == '.' && *(fwdPtr+2) == '.' && (*(fwdPtr+3) == '/' || *(fwdPtr+3) == '\0' || // This will take care of *(fwdPtr+3) == '?' || // something like foo/bar/..#sometag *(fwdPtr+3) == '#')) { // remove foo/.. // reverse the urlPtr to the previous slash if(urlPtr != io_Path) urlPtr--; // we must be going back at least by one for(;*urlPtr != '/' && urlPtr != io_Path; urlPtr--) ; // null body // forward the fwd_prt past the ../ fwdPtr += 2; // special case if we have reached the end to preserve the last / if (*fwdPtr == '.' && *(fwdPtr+1) == '\0') urlPtr +=1; } else { // copy the url incrementaly *urlPtr++ = *fwdPtr; } } // Copy remaining stuff past the #?; for (; *fwdPtr != '\0'; ++fwdPtr) { *urlPtr++ = *fwdPtr; } *urlPtr = '\0'; // terminate the url /* * Now lets remove trailing . case * /foo/foo1/. -> /foo/foo1/ */ if ((urlPtr > (io_Path+1)) && (*(urlPtr-1) == '.') && (*(urlPtr-2) == '/')) *(urlPtr-1) = '\0'; } // Replace all /./ with a / while resolving absolute URLs // But only till #? void CoalesceDirsAbs(char* io_Path) { /* Stolen from the old netlib's mkparse.c. * * modifies a url of the form /foo/../foo1 -> /foo1 * and /foo/./foo1 -> /foo/foo1 * and /foo/foo1/.. -> /foo/ */ char *fwdPtr = io_Path; char *urlPtr = io_Path; PRUint32 traversal = 0; for(; (*fwdPtr != '\0') && (*fwdPtr != '?') && (*fwdPtr != '#'); ++fwdPtr) { #if defined(XP_WIN) // At first, If this is DBCS character, it skips next character. if (::IsDBCSLeadByte(*fwdPtr) && *(fwdPtr+1) != '\0') { *urlPtr++ = *fwdPtr++; *urlPtr++ = *fwdPtr; continue; } #endif if (*fwdPtr == '/' && *(fwdPtr+1) == '.' && *(fwdPtr+2) == '/' ) { // remove . followed by slash ++fwdPtr; } else if(*fwdPtr == '/' && *(fwdPtr+1) == '.' && *(fwdPtr+2) == '.' && (*(fwdPtr+3) == '/' || *(fwdPtr+3) == '\0' || // This will take care of *(fwdPtr+3) == '?' || // something like foo/bar/..#sometag *(fwdPtr+3) == '#')) { // remove foo/.. // reverse the urlPtr to the previous slash if possible if(traversal > 0 ) { if (urlPtr != io_Path) urlPtr--; // we must be going back at least by one for(;*urlPtr != '/' && urlPtr != io_Path; urlPtr--) ; // null body --traversal; // count back // forward the fwdPtr past the ../ fwdPtr += 2; // special case if we have reached the end // to preserve the last / if (*fwdPtr == '.' && *(fwdPtr+1) == '\0') ++urlPtr; } else { // there are to much /.. in this path, just copy them instead. // forward the urlPtr past the /.. and copying it *urlPtr++ = *fwdPtr; ++fwdPtr; *urlPtr++ = *fwdPtr; ++fwdPtr; *urlPtr++ = *fwdPtr; } } else { if(*fwdPtr == '/' && *(fwdPtr+1) != '.') { // count the hierachie traversal++; } // copy the url incrementaly *urlPtr++ = *fwdPtr; } } // Copy remaining stuff past the #?; for (; *fwdPtr != '\0'; ++fwdPtr) { *urlPtr++ = *fwdPtr; } *urlPtr = '\0'; // terminate the url /* * Now lets remove trailing . case * /foo/foo1/. -> /foo/foo1/ */ if ((urlPtr > (io_Path+1)) && (*(urlPtr-1) == '.') && (*(urlPtr-2) == '/')) *(urlPtr-1) = '\0'; } static inline void ToLower(char &c) { if ((unsigned)(c - 'A') <= (unsigned)('Z' - 'A')) c += 'a' - 'A'; } void ToLowerCase(char *str, PRUint32 length) { for (char *end = str + length; str < end; ++str) ToLower(*str); } void ToLowerCase(char *str) { for (; *str; ++str) ToLower(*str); } /* Extract URI-Scheme if possible */ nsresult ExtractURLScheme(const nsACString &inURI, PRUint32 *startPos, PRUint32 *endPos, nsACString *scheme) { // search for something up to a colon, and call it the scheme const nsPromiseFlatCString flatURI( PromiseFlatCString(inURI) ); const char* uri_start = flatURI.get(); const char* uri = uri_start; // skip leading white space while (nsCRT::IsAsciiSpace(*uri)) uri++; PRUint32 start = uri - uri_start; if (startPos) { *startPos = start; } PRUint32 length = 0; char c; while ((c = *uri++) != '\0') { // First char must be Alpha if (length == 0 && nsCRT::IsAsciiAlpha(c)) { length++; } // Next chars can be alpha + digit + some special chars else if (length > 0 && (nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) || c == '+' || c == '.' || c == '-')) { length++; } // stop if colon reached but not as first char else if (c == ':' && length > 0) { if (endPos) { *endPos = start + length + 1; } if (scheme) scheme->Assign(Substring(inURI, start, length)); return NS_OK; } else break; } return NS_ERROR_MALFORMED_URI; } PRBool IsValidScheme(const char *scheme, PRUint32 schemeLen) { // first char much be alpha if (!nsCRT::IsAsciiAlpha(*scheme)) return PR_FALSE; for (; schemeLen && *scheme; ++scheme, --schemeLen) { if (!(nsCRT::IsAsciiAlpha(*scheme) || nsCRT::IsAsciiDigit(*scheme) || *scheme == '+' || *scheme == '.' || *scheme == '-')) return PR_FALSE; } return PR_TRUE; } nsresult ResolveRelativePath(const nsACString &relativePath, const nsACString &basePath, nsACString &result) { nsCAutoString name; nsCAutoString path(basePath); PRBool needsDelim = PR_FALSE; if ( !path.IsEmpty() ) { PRUnichar last = path.Last(); needsDelim = !(last == '/' || last == '\\' ); } nsACString::const_iterator beg, end; relativePath.BeginReading(beg); relativePath.EndReading(end); PRBool stop = PR_FALSE; char c; for (; !stop; ++beg) { c = (beg == end) ? '\0' : *beg; //printf("%c [name=%s] [path=%s]\n", c, name.get(), path.get()); switch (c) { case '\0': case '#': case ';': case '?': stop = PR_TRUE; // fall through... case '/': case '\\': // delimiter found if (name.Equals("..")) { // pop path // If we already have the delim at end, then // skip over that when searching for next one to the left PRInt32 offset = path.Length() - (needsDelim ? 1 : 2); PRInt32 pos = path.RFind("/", PR_FALSE, offset); if (pos > 0) path.Truncate(pos + 1); else return NS_ERROR_MALFORMED_URI; } else if (name.Equals(".") || name.Equals("")) { // do nothing } else { // append name to path if (needsDelim) path += "/"; path += name; needsDelim = PR_TRUE; } name = ""; break; default: // append char to name name += c; } } // append anything left on relativePath (e.g. #..., ;..., ?...) if (c != '\0') path += Substring(--beg, end); result = path; return NS_OK; }