LCOV - code coverage report
Current view: directory - toolkit/components/url-classifier - nsUrlClassifierUtils.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 196 153 78.1 %
Date: 2012-06-02 Functions: 19 18 94.7 %

       1                 : /* ***** BEGIN LICENSE BLOCK *****
       2                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       3                 :  *
       4                 :  * The contents of this file are subject to the Mozilla Public License Version
       5                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       6                 :  * the License. You may obtain a copy of the License at
       7                 :  * http://www.mozilla.org/MPL/
       8                 :  *
       9                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      10                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      11                 :  * for the specific language governing rights and limitations under the
      12                 :  * License.
      13                 :  *
      14                 :  * The Original Code is Url Classifier code
      15                 :  *
      16                 :  * The Initial Developer of the Original Code is
      17                 :  * Google Inc.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *
      23                 :  * Alternatively, the contents of this file may be used under the terms of
      24                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      25                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      26                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      27                 :  * of those above. If you wish to allow use of your version of this file only
      28                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      29                 :  * use your version of this file under the terms of the MPL, indicate your
      30                 :  * decision by deleting the provisions above and replace them with the notice
      31                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      32                 :  * the provisions above, a recipient may use your version of this file under
      33                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      34                 :  *
      35                 :  * ***** END LICENSE BLOCK ***** */
      36                 : 
      37                 : #include "nsEscape.h"
      38                 : #include "nsString.h"
      39                 : #include "nsIURI.h"
      40                 : #include "nsNetUtil.h"
      41                 : #include "nsUrlClassifierUtils.h"
      42                 : #include "nsTArray.h"
      43                 : #include "nsReadableUtils.h"
      44                 : #include "plbase64.h"
      45                 : #include "prmem.h"
      46                 : #include "prprf.h"
      47                 : 
      48               0 : static char int_to_hex_digit(PRInt32 i)
      49                 : {
      50               0 :   NS_ASSERTION((i >= 0) && (i <= 15), "int too big in int_to_hex_digit");
      51               0 :   return static_cast<char>(((i < 10) ? (i + '0') : ((i - 10) + 'A')));
      52                 : }
      53                 : 
      54                 : static bool
      55               9 : IsDecimal(const nsACString & num)
      56                 : {
      57              25 :   for (PRUint32 i = 0; i < num.Length(); i++) {
      58              17 :     if (!isdigit(num[i])) {
      59               1 :       return false;
      60                 :     }
      61                 :   }
      62                 : 
      63               8 :   return true;
      64                 : }
      65                 : 
      66                 : static bool
      67               1 : IsHex(const nsACString & num)
      68                 : {
      69               1 :   if (num.Length() < 3) {
      70               1 :     return false;
      71                 :   }
      72                 : 
      73               0 :   if (num[0] != '0' || !(num[1] == 'x' || num[1] == 'X')) {
      74               0 :     return false;
      75                 :   }
      76                 : 
      77               0 :   for (PRUint32 i = 2; i < num.Length(); i++) {
      78               0 :     if (!isxdigit(num[i])) {
      79               0 :       return false;
      80                 :     }
      81                 :   }
      82                 : 
      83               0 :   return true;
      84                 : }
      85                 : 
      86                 : static bool
      87               9 : IsOctal(const nsACString & num)
      88                 : {
      89               9 :   if (num.Length() < 2) {
      90               5 :     return false;
      91                 :   }
      92                 : 
      93               4 :   if (num[0] != '0') {
      94               4 :     return false;
      95                 :   }
      96                 : 
      97               0 :   for (PRUint32 i = 1; i < num.Length(); i++) {
      98               0 :     if (!isdigit(num[i]) || num[i] == '8' || num[i] == '9') {
      99               0 :       return false;
     100                 :     }
     101                 :   }
     102                 : 
     103               0 :   return true;
     104                 : }
     105                 : 
     106               5 : nsUrlClassifierUtils::nsUrlClassifierUtils() : mEscapeCharmap(nsnull)
     107                 : {
     108               5 : }
     109                 : 
     110                 : nsresult
     111               5 : nsUrlClassifierUtils::Init()
     112                 : {
     113                 :   // Everything but alpha numerics, - and .
     114                 :   mEscapeCharmap = new Charmap(0xffffffff, 0xfc009fff, 0xf8000001, 0xf8000001,
     115               5 :                                0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff);
     116               5 :   if (!mEscapeCharmap)
     117               0 :     return NS_ERROR_OUT_OF_MEMORY;
     118               5 :   return NS_OK;
     119                 : }
     120                 : 
     121             830 : NS_IMPL_ISUPPORTS1(nsUrlClassifierUtils, nsIUrlClassifierUtils)
     122                 : 
     123                 : /////////////////////////////////////////////////////////////////////////////
     124                 : // nsIUrlClassifierUtils
     125                 : 
     126                 : NS_IMETHODIMP
     127             161 : nsUrlClassifierUtils::GetKeyForURI(nsIURI * uri, nsACString & _retval)
     128                 : {
     129             322 :   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
     130             161 :   if (!innerURI)
     131               0 :     innerURI = uri;
     132                 : 
     133             322 :   nsCAutoString host;
     134             161 :   innerURI->GetAsciiHost(host);
     135                 : 
     136             161 :   if (host.IsEmpty()) {
     137               0 :     return NS_ERROR_MALFORMED_URI;
     138                 :   }
     139                 : 
     140             161 :   nsresult rv = CanonicalizeHostname(host, _retval);
     141             161 :   NS_ENSURE_SUCCESS(rv, rv);
     142                 : 
     143             322 :   nsCAutoString path;
     144             161 :   rv = innerURI->GetPath(path);
     145             161 :   NS_ENSURE_SUCCESS(rv, rv);
     146                 : 
     147                 :   // strip out anchors
     148             161 :   PRInt32 ref = path.FindChar('#');
     149             161 :   if (ref != kNotFound)
     150               0 :     path.SetLength(ref);
     151                 : 
     152             322 :   nsCAutoString temp;
     153             161 :   rv = CanonicalizePath(path, temp);
     154             161 :   NS_ENSURE_SUCCESS(rv, rv);
     155                 : 
     156             161 :   _retval.Append(temp);
     157                 : 
     158             161 :   return NS_OK;
     159                 : }
     160                 : 
     161                 : /////////////////////////////////////////////////////////////////////////////
     162                 : // non-interface methods
     163                 : 
     164                 : nsresult
     165             161 : nsUrlClassifierUtils::CanonicalizeHostname(const nsACString & hostname,
     166                 :                                            nsACString & _retval)
     167                 : {
     168             322 :   nsCAutoString unescaped;
     169             322 :   if (!NS_UnescapeURL(PromiseFlatCString(hostname).get(),
     170             322 :                       PromiseFlatCString(hostname).Length(),
     171             161 :                       0, unescaped)) {
     172             161 :     unescaped.Assign(hostname);
     173                 :   }
     174                 : 
     175             322 :   nsCAutoString cleaned;
     176             161 :   CleanupHostname(unescaped, cleaned);
     177                 : 
     178             322 :   nsCAutoString temp;
     179             161 :   ParseIPAddress(cleaned, temp);
     180             161 :   if (!temp.IsEmpty()) {
     181               2 :     cleaned.Assign(temp);
     182                 :   }
     183                 : 
     184             161 :   ToLowerCase(cleaned);
     185             161 :   SpecialEncode(cleaned, false, _retval);
     186                 : 
     187             161 :   return NS_OK;
     188                 : }
     189                 : 
     190                 : 
     191                 : nsresult
     192             161 : nsUrlClassifierUtils::CanonicalizePath(const nsACString & path,
     193                 :                                        nsACString & _retval)
     194                 : {
     195             161 :   _retval.Truncate();
     196                 : 
     197             322 :   nsCAutoString decodedPath(path);
     198             322 :   nsCAutoString temp;
     199             322 :   while (NS_UnescapeURL(decodedPath.get(), decodedPath.Length(), 0, temp)) {
     200               0 :     decodedPath.Assign(temp);
     201               0 :     temp.Truncate();
     202                 :   }
     203                 : 
     204             161 :   SpecialEncode(decodedPath, true, _retval);
     205                 :   // XXX: lowercase the path?
     206                 : 
     207             161 :   return NS_OK;
     208                 : }
     209                 : 
     210                 : void
     211             161 : nsUrlClassifierUtils::CleanupHostname(const nsACString & hostname,
     212                 :                                       nsACString & _retval)
     213                 : {
     214             161 :   _retval.Truncate();
     215                 : 
     216             161 :   const char* curChar = hostname.BeginReading();
     217             161 :   const char* end = hostname.EndReading();
     218             161 :   char lastChar = '\0';
     219            1595 :   while (curChar != end) {
     220            1273 :     unsigned char c = static_cast<unsigned char>(*curChar);
     221            1273 :     if (c == '.' && (lastChar == '\0' || lastChar == '.')) {
     222                 :       // skip
     223                 :     } else {
     224            1273 :       _retval.Append(*curChar);
     225                 :     }
     226            1273 :     lastChar = c;
     227            1273 :     ++curChar;
     228                 :   }
     229                 : 
     230                 :   // cut off trailing dots
     231             322 :   while (_retval.Length() > 0 && _retval[_retval.Length() - 1] == '.') {
     232               0 :     _retval.SetLength(_retval.Length() - 1);
     233                 :   }
     234             161 : }
     235                 : 
     236                 : void
     237             161 : nsUrlClassifierUtils::ParseIPAddress(const nsACString & host,
     238                 :                                      nsACString & _retval)
     239                 : {
     240             161 :   _retval.Truncate();
     241             161 :   nsACString::const_iterator iter, end;
     242             161 :   host.BeginReading(iter);
     243             161 :   host.EndReading(end);
     244                 : 
     245             161 :   if (host.Length() <= 15) {
     246                 :     // The Windows resolver allows a 4-part dotted decimal IP address to
     247                 :     // have a space followed by any old rubbish, so long as the total length
     248                 :     // of the string doesn't get above 15 characters. So, "10.192.95.89 xy"
     249                 :     // is resolved to 10.192.95.89.
     250                 :     // If the string length is greater than 15 characters, e.g.
     251                 :     // "10.192.95.89 xy.wildcard.example.com", it will be resolved through
     252                 :     // DNS.
     253                 : 
     254             159 :     if (FindCharInReadable(' ', iter, end)) {
     255               0 :       end = iter;
     256                 :     }
     257                 :   }
     258                 : 
     259             359 :   for (host.BeginReading(iter); iter != end; iter++) {
     260             356 :     if (!(isxdigit(*iter) || *iter == 'x' || *iter == 'X' || *iter == '.')) {
     261                 :       // not an IP
     262             158 :       return;
     263                 :     }
     264                 :   }
     265                 : 
     266               3 :   host.BeginReading(iter);
     267               6 :   nsTArray<nsCString> parts;
     268               3 :   ParseString(PromiseFlatCString(Substring(iter, end)), '.', parts);
     269               3 :   if (parts.Length() > 4) {
     270                 :     return;
     271                 :   }
     272                 : 
     273                 :   // If any potentially-octal numbers (start with 0 but not hex) have
     274                 :   // non-octal digits, no part of the ip can be in octal
     275                 :   // XXX: this came from the old javascript implementation, is it really
     276                 :   // supposed to be like this?
     277               3 :   bool allowOctal = true;
     278                 :   PRUint32 i;
     279                 : 
     280              14 :   for (i = 0; i < parts.Length(); i++) {
     281              11 :     const nsCString& part = parts[i];
     282              11 :     if (part[0] == '0') {
     283               2 :       for (PRUint32 j = 1; j < part.Length(); j++) {
     284               0 :         if (part[j] == 'x') {
     285               0 :           break;
     286                 :         }
     287               0 :         if (part[j] == '8' || part[j] == '9') {
     288               0 :           allowOctal = false;
     289               0 :           break;
     290                 :         }
     291                 :       }
     292                 :     }
     293                 :   }
     294                 : 
     295              11 :   for (i = 0; i < parts.Length(); i++) {
     296              18 :     nsCAutoString canonical;
     297                 : 
     298               9 :     if (i == parts.Length() - 1) {
     299               2 :       CanonicalNum(parts[i], 5 - parts.Length(), allowOctal, canonical);
     300                 :     } else {
     301               7 :       CanonicalNum(parts[i], 1, allowOctal, canonical);
     302                 :     }
     303                 : 
     304               9 :     if (canonical.IsEmpty()) {
     305               1 :       _retval.Truncate();
     306                 :       return;
     307                 :     }
     308                 : 
     309               8 :     if (_retval.IsEmpty()) {
     310               2 :       _retval.Assign(canonical);
     311                 :     } else {
     312               6 :       _retval.Append('.');
     313               6 :       _retval.Append(canonical);
     314                 :     }
     315                 :   }
     316                 :   return;
     317                 : }
     318                 : 
     319                 : void
     320               9 : nsUrlClassifierUtils::CanonicalNum(const nsACString& num,
     321                 :                                    PRUint32 bytes,
     322                 :                                    bool allowOctal,
     323                 :                                    nsACString& _retval)
     324                 : {
     325               9 :   _retval.Truncate();
     326                 : 
     327               9 :   if (num.Length() < 1) {
     328               0 :     return;
     329                 :   }
     330                 : 
     331                 :   PRUint32 val;
     332               9 :   if (allowOctal && IsOctal(num)) {
     333               0 :     if (PR_sscanf(PromiseFlatCString(num).get(), "%o", &val) != 1) {
     334               0 :       return;
     335                 :     }
     336               9 :   } else if (IsDecimal(num)) {
     337               8 :     if (PR_sscanf(PromiseFlatCString(num).get(), "%u", &val) != 1) {
     338               0 :       return;
     339                 :     }
     340               1 :   } else if (IsHex(num)) {
     341               0 :   if (PR_sscanf(PromiseFlatCString(num).get(), num[1] == 'X' ? "0X%x" : "0x%x",
     342               0 :                 &val) != 1) {
     343               0 :       return;
     344                 :     }
     345                 :   } else {
     346               1 :     return;
     347                 :   }
     348                 : 
     349              24 :   while (bytes--) {
     350                 :     char buf[20];
     351               8 :     PR_snprintf(buf, sizeof(buf), "%u", val & 0xff);
     352               8 :     if (_retval.IsEmpty()) {
     353               8 :       _retval.Assign(buf);
     354                 :     } else {
     355               0 :       _retval = nsDependentCString(buf) + NS_LITERAL_CSTRING(".") + _retval;
     356                 :     }
     357               8 :     val >>= 8;
     358                 :   }
     359                 : }
     360                 : 
     361                 : // This function will encode all "special" characters in typical url
     362                 : // encoding, that is %hh where h is a valid hex digit.  It will also fold
     363                 : // any duplicated slashes.
     364                 : bool
     365             322 : nsUrlClassifierUtils::SpecialEncode(const nsACString & url,
     366                 :                                     bool foldSlashes,
     367                 :                                     nsACString & _retval)
     368                 : {
     369             322 :   bool changed = false;
     370             322 :   const char* curChar = url.BeginReading();
     371             322 :   const char* end = url.EndReading();
     372                 : 
     373             322 :   unsigned char lastChar = '\0';
     374            2284 :   while (curChar != end) {
     375            1640 :     unsigned char c = static_cast<unsigned char>(*curChar);
     376            1640 :     if (ShouldURLEscape(c)) {
     377               0 :       _retval.Append('%');
     378               0 :       _retval.Append(int_to_hex_digit(c / 16));
     379               0 :       _retval.Append(int_to_hex_digit(c % 16));
     380                 : 
     381               0 :       changed = true;
     382            1640 :     } else if (foldSlashes && (c == '/' && lastChar == '/')) {
     383                 :       // skip
     384                 :     } else {
     385            1640 :       _retval.Append(*curChar);
     386                 :     }
     387            1640 :     lastChar = c;
     388            1640 :     curChar++;
     389                 :   }
     390             322 :   return changed;
     391                 : }
     392                 : 
     393                 : bool
     394            1640 : nsUrlClassifierUtils::ShouldURLEscape(const unsigned char c) const
     395                 : {
     396            1640 :   return c <= 32 || c == '%' || c >=127;
     397                 : }
     398                 : 
     399                 : /* static */
     400                 : void
     401               7 : nsUrlClassifierUtils::UnUrlsafeBase64(nsACString &str)
     402                 : {
     403               7 :   nsACString::iterator iter, end;
     404               7 :   str.BeginWriting(iter);
     405               7 :   str.EndWriting(end);
     406             194 :   while (iter != end) {
     407             180 :     if (*iter == '-') {
     408               0 :       *iter = '+';
     409             180 :     } else if (*iter == '_') {
     410               0 :       *iter = '/';
     411                 :     }
     412             180 :     iter++;
     413                 :   }
     414               7 : }
     415                 : 
     416                 : /* static */
     417                 : nsresult
     418               2 : nsUrlClassifierUtils::DecodeClientKey(const nsACString &key,
     419                 :                                       nsACString &_retval)
     420                 : {
     421                 :   // Client key is sent in urlsafe base64, we need to decode it first.
     422               4 :   nsCAutoString base64(key);
     423               2 :   UnUrlsafeBase64(base64);
     424                 : 
     425                 :   // PL_Base64Decode doesn't null-terminate unless we let it allocate,
     426                 :   // so we need to calculate the length ourselves.
     427                 :   PRUint32 destLength;
     428               2 :   destLength = base64.Length();
     429               2 :   if (destLength > 0 && base64[destLength - 1] == '=') {
     430               2 :     if (destLength > 1 && base64[destLength - 2] == '=') {
     431               2 :       destLength -= 2;
     432                 :     } else {
     433               0 :       destLength -= 1;
     434                 :     }
     435                 :   }
     436                 : 
     437               2 :   destLength = ((destLength * 3) / 4);
     438               2 :   _retval.SetLength(destLength);
     439               2 :   if (destLength != _retval.Length())
     440               0 :     return NS_ERROR_OUT_OF_MEMORY;
     441                 : 
     442               2 :   if (!PL_Base64Decode(base64.BeginReading(), base64.Length(),
     443               2 :                        _retval.BeginWriting())) {
     444               0 :     return NS_ERROR_FAILURE;
     445                 :   }
     446                 : 
     447               2 :   return NS_OK;
     448                 : }

Generated by: LCOV version 1.7