LCOV - code coverage report
Current view: top level - netwerk/mime - nsMIMEHeaderParamImpl.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 109 631 17.3 %
Date: 2017-07-14 16:53:18 Functions: 9 29 31.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set sw=4 ts=8 et tw=80 : */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include <string.h>
       8             : #include "prprf.h"
       9             : #include "plstr.h"
      10             : #include "plbase64.h"
      11             : #include "nsCRT.h"
      12             : #include "nsMemory.h"
      13             : #include "nsTArray.h"
      14             : #include "nsCOMPtr.h"
      15             : #include "nsEscape.h"
      16             : #include "nsIUTF8ConverterService.h"
      17             : #include "nsUConvCID.h"
      18             : #include "nsIServiceManager.h"
      19             : #include "nsMIMEHeaderParamImpl.h"
      20             : #include "nsReadableUtils.h"
      21             : #include "nsNativeCharsetUtils.h"
      22             : #include "nsError.h"
      23             : #include "mozilla/Encoding.h"
      24             : 
      25             : using mozilla::Encoding;
      26             : 
      27             : // static functions declared below are moved from mailnews/mime/src/comi18n.cpp
      28             : 
      29             : static char *DecodeQ(const char *, uint32_t);
      30             : static bool Is7bitNonAsciiString(const char *, uint32_t);
      31             : static void CopyRawHeader(const char *, uint32_t, const char *, nsACString &);
      32             : static nsresult DecodeRFC2047Str(const char *, const char *, bool, nsACString&);
      33             : static nsresult internalDecodeParameter(const nsACString&, const char*,
      34             :                                         const char*, bool, bool, nsACString&);
      35             : 
      36             : // XXX The chance of UTF-7 being used in the message header is really
      37             : // low, but in theory it's possible.
      38             : #define IS_7BIT_NON_ASCII_CHARSET(cset)            \
      39             :     (!nsCRT::strncasecmp((cset), "ISO-2022", 8) || \
      40             :      !nsCRT::strncasecmp((cset), "HZ-GB", 5)    || \
      41             :      !nsCRT::strncasecmp((cset), "UTF-7", 5))
      42             : 
      43          18 : NS_IMPL_ISUPPORTS(nsMIMEHeaderParamImpl, nsIMIMEHeaderParam)
      44             : 
      45             : NS_IMETHODIMP
      46           0 : nsMIMEHeaderParamImpl::GetParameter(const nsACString& aHeaderVal,
      47             :                                     const char *aParamName,
      48             :                                     const nsACString& aFallbackCharset,
      49             :                                     bool aTryLocaleCharset,
      50             :                                     char **aLang, nsAString& aResult)
      51             : {
      52           0 :   return DoGetParameter(aHeaderVal, aParamName, MIME_FIELD_ENCODING,
      53           0 :                         aFallbackCharset, aTryLocaleCharset, aLang, aResult);
      54             : }
      55             : 
      56             : NS_IMETHODIMP
      57          10 : nsMIMEHeaderParamImpl::GetParameterHTTP(const nsACString& aHeaderVal,
      58             :                                         const char *aParamName,
      59             :                                         const nsACString& aFallbackCharset,
      60             :                                         bool aTryLocaleCharset,
      61             :                                         char **aLang, nsAString& aResult)
      62             : {
      63          10 :   return DoGetParameter(aHeaderVal, aParamName, HTTP_FIELD_ENCODING,
      64          10 :                         aFallbackCharset, aTryLocaleCharset, aLang, aResult);
      65             : }
      66             : 
      67             : // XXX : aTryLocaleCharset is not yet effective.
      68             : nsresult
      69          10 : nsMIMEHeaderParamImpl::DoGetParameter(const nsACString& aHeaderVal,
      70             :                                       const char *aParamName,
      71             :                                       ParamDecoding aDecoding,
      72             :                                       const nsACString& aFallbackCharset,
      73             :                                       bool aTryLocaleCharset,
      74             :                                       char **aLang, nsAString& aResult)
      75             : {
      76          10 :     aResult.Truncate();
      77             :     nsresult rv;
      78             : 
      79             :     // get parameter (decode RFC 2231/5987 when applicable, as specified by
      80             :     // aDecoding (5987 being a subset of 2231) and return charset.)
      81          20 :     nsXPIDLCString med;
      82          20 :     nsXPIDLCString charset;
      83          30 :     rv = DoParameterInternal(PromiseFlatCString(aHeaderVal).get(), aParamName,
      84          20 :                              aDecoding, getter_Copies(charset), aLang,
      85          30 :                              getter_Copies(med));
      86          10 :     if (NS_FAILED(rv))
      87           5 :         return rv;
      88             : 
      89             :     // convert to UTF-8 after charset conversion and RFC 2047 decoding
      90             :     // if necessary.
      91             : 
      92          10 :     nsAutoCString str1;
      93           5 :     rv = internalDecodeParameter(med, charset.get(), nullptr, false,
      94             :                                  // was aDecoding == MIME_FIELD_ENCODING
      95             :                                  // see bug 875615
      96             :                                  true,
      97           5 :                                  str1);
      98           5 :     NS_ENSURE_SUCCESS(rv, rv);
      99             : 
     100           5 :     if (!aFallbackCharset.IsEmpty())
     101             :     {
     102           0 :         const Encoding* encoding = Encoding::ForLabel(aFallbackCharset);
     103           0 :         nsAutoCString str2;
     104             :         nsCOMPtr<nsIUTF8ConverterService>
     105           0 :           cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID));
     106           0 :         if (cvtUTF8 &&
     107           0 :             NS_SUCCEEDED(cvtUTF8->ConvertStringToUTF8(str1,
     108             :                 PromiseFlatCString(aFallbackCharset).get(), false,
     109             :                                    encoding != UTF_8_ENCODING,
     110             :                                    1, str2))) {
     111           0 :           CopyUTF8toUTF16(str2, aResult);
     112           0 :           return NS_OK;
     113             :         }
     114             :     }
     115             : 
     116           5 :     if (IsUTF8(str1)) {
     117           5 :       CopyUTF8toUTF16(str1, aResult);
     118           5 :       return NS_OK;
     119             :     }
     120             : 
     121           0 :     if (aTryLocaleCharset && !NS_IsNativeUTF8())
     122           0 :       return NS_CopyNativeToUnicode(str1, aResult);
     123             : 
     124           0 :     CopyASCIItoUTF16(str1, aResult);
     125           0 :     return NS_OK;
     126             : }
     127             : 
     128             : // remove backslash-encoded sequences from quoted-strings
     129             : // modifies string in place, potentially shortening it
     130           0 : void RemoveQuotedStringEscapes(char *src)
     131             : {
     132           0 :   char *dst = src;
     133             : 
     134           0 :   for (char *c = src; *c; ++c)
     135             :   {
     136           0 :     if (c[0] == '\\' && c[1])
     137             :     {
     138             :       // skip backslash if not at end
     139           0 :       ++c;
     140             :     }
     141           0 :     *dst++ = *c;
     142             :   }
     143           0 :   *dst = 0;
     144           0 : }
     145             : 
     146             : // true is character is a hex digit
     147           0 : bool IsHexDigit(char aChar)
     148             : {
     149           0 :   char c = aChar;
     150             : 
     151           0 :   return (c >= 'a' && c <= 'f') ||
     152           0 :          (c >= 'A' && c <= 'F') ||
     153           0 :          (c >= '0' && c <= '9');
     154             : }
     155             : 
     156             : // validate that a C String containing %-escapes is syntactically valid
     157           0 : bool IsValidPercentEscaped(const char *aValue, int32_t len)
     158             : {
     159           0 :   for (int32_t i = 0; i < len; i++) {
     160           0 :     if (aValue[i] == '%') {
     161           0 :       if (!IsHexDigit(aValue[i + 1]) || !IsHexDigit(aValue[i + 2])) {
     162           0 :         return false;
     163             :       }
     164             :     }
     165             :   }
     166           0 :   return true;
     167             : }
     168             : 
     169             : // Support for continuations (RFC 2231, Section 3)
     170             : 
     171             : // only a sane number supported
     172             : #define MAX_CONTINUATIONS 999
     173             : 
     174             : // part of a continuation
     175             : 
     176             : class Continuation {
     177             :   public:
     178           0 :     Continuation(const char *aValue, uint32_t aLength,
     179           0 :                  bool aNeedsPercentDecoding, bool aWasQuotedString) {
     180           0 :       value = aValue;
     181           0 :       length = aLength;
     182           0 :       needsPercentDecoding = aNeedsPercentDecoding;
     183           0 :       wasQuotedString = aWasQuotedString;
     184           0 :     }
     185           0 :     Continuation() {
     186             :       // empty constructor needed for nsTArray
     187           0 :       value = nullptr;
     188           0 :       length = 0;
     189           0 :       needsPercentDecoding = false;
     190           0 :       wasQuotedString = false;
     191           0 :     }
     192             :     ~Continuation() = default;
     193             : 
     194             :     const char *value;
     195             :     uint32_t length;
     196             :     bool needsPercentDecoding;
     197             :     bool wasQuotedString;
     198             : };
     199             : 
     200             : // combine segments into a single string, returning the allocated string
     201             : // (or nullptr) while emptying the list
     202           5 : char *combineContinuations(nsTArray<Continuation>& aArray)
     203             : {
     204             :   // Sanity check
     205           5 :   if (aArray.Length() == 0)
     206           5 :     return nullptr;
     207             : 
     208             :   // Get an upper bound for the length
     209           0 :   uint32_t length = 0;
     210           0 :   for (uint32_t i = 0; i < aArray.Length(); i++) {
     211           0 :     length += aArray[i].length;
     212             :   }
     213             : 
     214             :   // Allocate
     215           0 :   char *result = (char *) moz_xmalloc(length + 1);
     216             : 
     217             :   // Concatenate
     218           0 :   if (result) {
     219           0 :     *result = '\0';
     220             : 
     221           0 :     for (uint32_t i = 0; i < aArray.Length(); i++) {
     222           0 :       Continuation cont = aArray[i];
     223           0 :       if (! cont.value) break;
     224             : 
     225           0 :       char *c = result + strlen(result);
     226           0 :       strncat(result, cont.value, cont.length);
     227           0 :       if (cont.needsPercentDecoding) {
     228           0 :         nsUnescape(c);
     229             :       }
     230           0 :       if (cont.wasQuotedString) {
     231           0 :         RemoveQuotedStringEscapes(c);
     232             :       }
     233             :     }
     234             : 
     235             :     // return null if empty value
     236           0 :     if (*result == '\0') {
     237           0 :       free(result);
     238           0 :       result = nullptr;
     239             :     }
     240             :   } else {
     241             :     // Handle OOM
     242           0 :     NS_WARNING("Out of memory\n");
     243             :   }
     244             : 
     245           0 :   return result;
     246             : }
     247             : 
     248             : // add a continuation, return false on error if segment already has been seen
     249           0 : bool addContinuation(nsTArray<Continuation>& aArray, uint32_t aIndex,
     250             :                      const char *aValue, uint32_t aLength,
     251             :                      bool aNeedsPercentDecoding, bool aWasQuotedString)
     252             : {
     253           0 :   if (aIndex < aArray.Length() && aArray[aIndex].value) {
     254           0 :     NS_WARNING("duplicate RC2231 continuation segment #\n");
     255           0 :     return false;
     256             :   }
     257             : 
     258           0 :   if (aIndex > MAX_CONTINUATIONS) {
     259           0 :     NS_WARNING("RC2231 continuation segment # exceeds limit\n");
     260           0 :     return false;
     261             :   }
     262             : 
     263           0 :   if (aNeedsPercentDecoding && aWasQuotedString) {
     264           0 :     NS_WARNING("RC2231 continuation segment can't use percent encoding and quoted string form at the same time\n");
     265           0 :     return false;
     266             :   }
     267             : 
     268           0 :   Continuation cont(aValue, aLength, aNeedsPercentDecoding, aWasQuotedString);
     269             : 
     270           0 :   if (aArray.Length() <= aIndex) {
     271           0 :     aArray.SetLength(aIndex + 1);
     272             :   }
     273           0 :   aArray[aIndex] = cont;
     274             : 
     275           0 :   return true;
     276             : }
     277             : 
     278             : // parse a segment number; return -1 on error
     279           0 : int32_t parseSegmentNumber(const char *aValue, int32_t aLen)
     280             : {
     281           0 :   if (aLen < 1) {
     282           0 :     NS_WARNING("segment number missing\n");
     283           0 :     return -1;
     284             :   }
     285             : 
     286           0 :   if (aLen > 1 && aValue[0] == '0') {
     287           0 :     NS_WARNING("leading '0' not allowed in segment number\n");
     288           0 :     return -1;
     289             :   }
     290             : 
     291           0 :   int32_t segmentNumber = 0;
     292             : 
     293           0 :   for (int32_t i = 0; i < aLen; i++) {
     294           0 :     if (! (aValue[i] >= '0' && aValue[i] <= '9')) {
     295           0 :       NS_WARNING("invalid characters in segment number\n");
     296           0 :       return -1;
     297             :     }
     298             : 
     299           0 :     segmentNumber *= 10;
     300           0 :     segmentNumber += aValue[i] - '0';
     301           0 :     if (segmentNumber > MAX_CONTINUATIONS) {
     302           0 :       NS_WARNING("Segment number exceeds sane size\n");
     303           0 :       return -1;
     304             :     }
     305             :   }
     306             : 
     307           0 :   return segmentNumber;
     308             : }
     309             : 
     310             : // validate a given octet sequence for compliance with the specified
     311             : // encoding
     312           0 : bool IsValidOctetSequenceForCharset(nsACString& aCharset, const char *aOctets)
     313             : {
     314             :   nsCOMPtr<nsIUTF8ConverterService> cvtUTF8(do_GetService
     315           0 :     (NS_UTF8CONVERTERSERVICE_CONTRACTID));
     316           0 :   if (!cvtUTF8) {
     317           0 :     NS_WARNING("Can't get UTF8ConverterService\n");
     318           0 :     return false;
     319             :   }
     320             : 
     321           0 :   nsAutoCString tmpRaw;
     322           0 :   tmpRaw.Assign(aOctets);
     323           0 :   nsAutoCString tmpDecoded;
     324             : 
     325           0 :   nsresult rv = cvtUTF8->ConvertStringToUTF8(tmpRaw,
     326           0 :                                              PromiseFlatCString(aCharset).get(),
     327           0 :                                              false, false, 1, tmpDecoded);
     328             : 
     329           0 :   if (rv != NS_OK) {
     330             :     // we can't decode; charset may be unsupported, or the octet sequence
     331             :     // is broken (illegal or incomplete octet sequence contained)
     332           0 :     NS_WARNING("RFC2231/5987 parameter value does not decode according to specified charset\n");
     333           0 :     return false;
     334             :   }
     335             : 
     336           0 :   return true;
     337             : }
     338             : 
     339             : // moved almost verbatim from mimehdrs.cpp
     340             : // char *
     341             : // MimeHeaders_get_parameter (const char *header_value, const char *parm_name,
     342             : //                            char **charset, char **language)
     343             : //
     344             : // The format of these header lines  is
     345             : // <token> [ ';' <token> '=' <token-or-quoted-string> ]*
     346             : NS_IMETHODIMP
     347           0 : nsMIMEHeaderParamImpl::GetParameterInternal(const char *aHeaderValue,
     348             :                                             const char *aParamName,
     349             :                                             char **aCharset,
     350             :                                             char **aLang,
     351             :                                             char **aResult)
     352             : {
     353             :   return DoParameterInternal(aHeaderValue, aParamName, MIME_FIELD_ENCODING,
     354           0 :                              aCharset, aLang, aResult);
     355             : }
     356             : 
     357             : 
     358             : nsresult
     359          10 : nsMIMEHeaderParamImpl::DoParameterInternal(const char *aHeaderValue,
     360             :                                            const char *aParamName,
     361             :                                            ParamDecoding aDecoding,
     362             :                                            char **aCharset,
     363             :                                            char **aLang,
     364             :                                            char **aResult)
     365             : {
     366             : 
     367          10 :   if (!aHeaderValue ||  !*aHeaderValue || !aResult)
     368           0 :     return NS_ERROR_INVALID_ARG;
     369             : 
     370          10 :   *aResult = nullptr;
     371             : 
     372          10 :   if (aCharset) *aCharset = nullptr;
     373          10 :   if (aLang) *aLang = nullptr;
     374             : 
     375          20 :   nsAutoCString charset;
     376             : 
     377             :   // change to (aDecoding != HTTP_FIELD_ENCODING) when we want to disable
     378             :   // them for HTTP header fields later on, see bug 776324
     379          10 :   bool acceptContinuations = true;
     380             : 
     381          10 :   const char *str = aHeaderValue;
     382             : 
     383             :   // skip leading white space.
     384          10 :   for (; *str &&  nsCRT::IsAsciiSpace(*str); ++str)
     385             :     ;
     386          10 :   const char *start = str;
     387             : 
     388             :   // aParamName is empty. return the first (possibly) _unnamed_ 'parameter'
     389             :   // For instance, return 'inline' in the following case:
     390             :   // Content-Disposition: inline; filename=.....
     391          10 :   if (!aParamName || !*aParamName)
     392             :     {
     393          80 :       for (; *str && *str != ';' && !nsCRT::IsAsciiSpace(*str); ++str)
     394             :         ;
     395           5 :       if (str == start)
     396           0 :         return NS_ERROR_FIRST_HEADER_FIELD_COMPONENT_EMPTY;
     397             : 
     398           5 :       *aResult = (char *) nsMemory::Clone(start, (str - start) + 1);
     399           5 :       NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
     400           5 :       (*aResult)[str - start] = '\0';  // null-terminate
     401           5 :       return NS_OK;
     402             :     }
     403             : 
     404             :   /* Skip forward to first ';' */
     405          80 :   for (; *str && *str != ';' && *str != ','; ++str)
     406             :     ;
     407           5 :   if (*str)
     408           0 :     str++;
     409             :   /* Skip over following whitespace */
     410           5 :   for (; *str && nsCRT::IsAsciiSpace(*str); ++str)
     411             :     ;
     412             : 
     413             :   // Some broken http servers just specify parameters
     414             :   // like 'filename' without specifying disposition
     415             :   // method. Rewind to the first non-white-space
     416             :   // character.
     417             : 
     418           5 :   if (!*str)
     419           5 :     str = start;
     420             : 
     421             :   // RFC2231 - The legitimate parm format can be:
     422             :   // A. title=ThisIsTitle
     423             :   // B. title*=us-ascii'en-us'This%20is%20wierd.
     424             :   // C. title*0*=us-ascii'en'This%20is%20wierd.%20We
     425             :   //    title*1*=have%20to%20support%20this.
     426             :   //    title*2="Else..."
     427             :   // D. title*0="Hey, what you think you are doing?"
     428             :   //    title*1="There is no charset and lang info."
     429             :   // RFC5987: only A and B
     430             : 
     431             :   // collect results for the different algorithms (plain filename,
     432             :   // RFC5987/2231-encoded filename, + continuations) separately and decide
     433             :   // which to use at the end
     434           5 :   char *caseAResult = nullptr;
     435           5 :   char *caseBResult = nullptr;
     436           5 :   char *caseCDResult = nullptr;
     437             : 
     438             :   // collect continuation segments
     439          10 :   nsTArray<Continuation> segments;
     440             : 
     441             : 
     442             :   // our copies of the charset parameter, kept separately as they might
     443             :   // differ for the two formats
     444          10 :   nsDependentCSubstring charsetB, charsetCD;
     445             : 
     446          10 :   nsDependentCSubstring lang;
     447             : 
     448           5 :   int32_t paramLen = strlen(aParamName);
     449             : 
     450           5 :   while (*str) {
     451             :     // find name/value
     452             : 
     453           5 :     const char *nameStart = str;
     454           5 :     const char *nameEnd = nullptr;
     455           5 :     const char *valueStart = str;
     456           5 :     const char *valueEnd = nullptr;
     457           5 :     bool isQuotedString = false;
     458             : 
     459           5 :     NS_ASSERTION(!nsCRT::IsAsciiSpace(*str), "should be after whitespace.");
     460             : 
     461             :     // Skip forward to the end of this token.
     462          80 :     for (; *str && !nsCRT::IsAsciiSpace(*str) && *str != '=' && *str != ';'; str++)
     463             :       ;
     464           5 :     nameEnd = str;
     465             : 
     466           5 :     int32_t nameLen = nameEnd - nameStart;
     467             : 
     468             :     // Skip over whitespace, '=', and whitespace
     469           5 :     while (nsCRT::IsAsciiSpace(*str)) ++str;
     470           5 :     if (!*str) {
     471           5 :       break;
     472             :     }
     473           0 :     if (*str++ != '=') {
     474             :       // don't accept parameters without "="
     475           0 :       goto increment_str;
     476             :     }
     477           0 :     while (nsCRT::IsAsciiSpace(*str)) ++str;
     478             : 
     479           0 :     if (*str != '"') {
     480             :       // The value is a token, not a quoted string.
     481           0 :       valueStart = str;
     482           0 :       for (valueEnd = str;
     483           0 :            *valueEnd && !nsCRT::IsAsciiSpace (*valueEnd) && *valueEnd != ';';
     484             :            valueEnd++)
     485             :         ;
     486           0 :       str = valueEnd;
     487             :     } else {
     488           0 :       isQuotedString = true;
     489             : 
     490           0 :       ++str;
     491           0 :       valueStart = str;
     492           0 :       for (valueEnd = str; *valueEnd; ++valueEnd) {
     493           0 :         if (*valueEnd == '\\' && *(valueEnd + 1))
     494           0 :           ++valueEnd;
     495           0 :         else if (*valueEnd == '"')
     496           0 :           break;
     497             :       }
     498           0 :       str = valueEnd;
     499             :       // *valueEnd != null means that *valueEnd is quote character.
     500           0 :       if (*valueEnd)
     501           0 :         str++;
     502             :     }
     503             : 
     504             :     // See if this is the simplest case (case A above),
     505             :     // a 'single' line value with no charset and lang.
     506             :     // If so, copy it and return.
     507           0 :     if (nameLen == paramLen &&
     508           0 :         !nsCRT::strncasecmp(nameStart, aParamName, paramLen)) {
     509             : 
     510           0 :       if (caseAResult) {
     511             :         // we already have one caseA result, ignore subsequent ones
     512           0 :         goto increment_str;
     513             :       }
     514             : 
     515             :       // if the parameter spans across multiple lines we have to strip out the
     516             :       //     line continuation -- jht 4/29/98
     517           0 :       nsAutoCString tempStr(valueStart, valueEnd - valueStart);
     518           0 :       tempStr.StripCRLF();
     519           0 :       char *res = ToNewCString(tempStr);
     520           0 :       NS_ENSURE_TRUE(res, NS_ERROR_OUT_OF_MEMORY);
     521             : 
     522           0 :       if (isQuotedString)
     523           0 :         RemoveQuotedStringEscapes(res);
     524             : 
     525           0 :       caseAResult = res;
     526             :       // keep going, we may find a RFC 2231/5987 encoded alternative
     527             :     }
     528             :     // case B, C, and D
     529           0 :     else if (nameLen > paramLen &&
     530           0 :              !nsCRT::strncasecmp(nameStart, aParamName, paramLen) &&
     531           0 :              *(nameStart + paramLen) == '*') {
     532             : 
     533             :       // 1st char past '*'
     534           0 :       const char *cp = nameStart + paramLen + 1;
     535             : 
     536             :       // if param name ends in "*" we need do to RFC5987 "ext-value" decoding
     537           0 :       bool needExtDecoding = *(nameEnd - 1) == '*';
     538             : 
     539           0 :       bool caseB = nameLen == paramLen + 1;
     540           0 :       bool caseCStart = (*cp == '0') && needExtDecoding;
     541             : 
     542             :       // parse the segment number
     543           0 :       int32_t segmentNumber = -1;
     544           0 :       if (!caseB) {
     545           0 :         int32_t segLen = (nameEnd - cp) - (needExtDecoding ? 1 : 0);
     546           0 :         segmentNumber = parseSegmentNumber(cp, segLen);
     547             : 
     548           0 :         if (segmentNumber == -1) {
     549           0 :           acceptContinuations = false;
     550           0 :           goto increment_str;
     551             :         }
     552             :       }
     553             : 
     554             :       // CaseB and start of CaseC: requires charset and optional language
     555             :       // in quotes (quotes required even if lang is blank)
     556           0 :       if (caseB || (caseCStart && acceptContinuations)) {
     557             :         // look for single quotation mark(')
     558           0 :         const char *sQuote1 = PL_strchr(valueStart, 0x27);
     559           0 :         const char *sQuote2 = sQuote1 ? PL_strchr(sQuote1 + 1, 0x27) : nullptr;
     560             : 
     561             :         // Two single quotation marks must be present even in
     562             :         // absence of charset and lang.
     563           0 :         if (!sQuote1 || !sQuote2) {
     564           0 :           NS_WARNING("Mandatory two single quotes are missing in header parameter\n");
     565             :         }
     566             : 
     567           0 :         const char *charsetStart = nullptr;
     568           0 :         int32_t charsetLength = 0;
     569           0 :         const char *langStart = nullptr;
     570           0 :         int32_t langLength = 0;
     571           0 :         const char *rawValStart = nullptr;
     572           0 :         int32_t rawValLength = 0;
     573             : 
     574           0 :         if (sQuote2 && sQuote1) {
     575             :           // both delimiters present: charSet'lang'rawVal
     576           0 :           rawValStart = sQuote2 + 1;
     577           0 :           rawValLength = valueEnd - rawValStart;
     578             : 
     579           0 :           langStart = sQuote1 + 1;
     580           0 :           langLength = sQuote2 - langStart;
     581             : 
     582           0 :           charsetStart = valueStart;
     583           0 :           charsetLength = sQuote1 - charsetStart;
     584             :         }
     585           0 :         else if (sQuote1) {
     586             :           // one delimiter; assume charset'rawVal
     587           0 :           rawValStart = sQuote1 + 1;
     588           0 :           rawValLength = valueEnd - rawValStart;
     589             : 
     590           0 :           charsetStart = valueStart;
     591           0 :           charsetLength = sQuote1 - valueStart;
     592             :         }
     593             :         else {
     594             :           // no delimiter: just rawVal
     595           0 :           rawValStart = valueStart;
     596           0 :           rawValLength = valueEnd - valueStart;
     597             :         }
     598             : 
     599           0 :         if (langLength != 0) {
     600           0 :           lang.Assign(langStart, langLength);
     601             :         }
     602             : 
     603             :         // keep the charset for later
     604           0 :         if (caseB) {
     605           0 :           charsetB.Assign(charsetStart, charsetLength);
     606             :         } else {
     607             :           // if caseCorD
     608           0 :           charsetCD.Assign(charsetStart, charsetLength);
     609             :         }
     610             : 
     611             :         // non-empty value part
     612           0 :         if (rawValLength > 0) {
     613           0 :           if (!caseBResult && caseB) {
     614           0 :             if (!IsValidPercentEscaped(rawValStart, rawValLength)) {
     615           0 :               goto increment_str;
     616             :             }
     617             : 
     618             :             // allocate buffer for the raw value
     619           0 :             char *tmpResult = (char *) nsMemory::Clone(rawValStart, rawValLength + 1);
     620           0 :             if (!tmpResult) {
     621           0 :               goto increment_str;
     622             :             }
     623           0 :             *(tmpResult + rawValLength) = 0;
     624             : 
     625           0 :             nsUnescape(tmpResult);
     626           0 :             caseBResult = tmpResult;
     627             :           } else {
     628             :             // caseC
     629           0 :             bool added = addContinuation(segments, 0, rawValStart,
     630             :                                          rawValLength, needExtDecoding,
     631           0 :                                          isQuotedString);
     632             : 
     633           0 :             if (!added) {
     634             :               // continuation not added, stop processing them
     635           0 :               acceptContinuations = false;
     636             :             }
     637             :           }
     638           0 :         }
     639             :       }  // end of if-block :  title*0*=  or  title*=
     640             :       // caseD: a line of multiline param with no need for unescaping : title*[0-9]=
     641             :       // or 2nd or later lines of a caseC param : title*[1-9]*=
     642           0 :       else if (acceptContinuations && segmentNumber != -1) {
     643           0 :         uint32_t valueLength = valueEnd - valueStart;
     644             : 
     645           0 :         bool added = addContinuation(segments, segmentNumber, valueStart,
     646             :                                      valueLength, needExtDecoding,
     647           0 :                                      isQuotedString);
     648             : 
     649           0 :         if (!added) {
     650             :           // continuation not added, stop processing them
     651           0 :           acceptContinuations = false;
     652             :         }
     653             :       } // end of if-block :  title*[0-9]= or title*[1-9]*=
     654             :     }
     655             : 
     656             :     // str now points after the end of the value.
     657             :     //   skip over whitespace, ';', whitespace.
     658             : increment_str:
     659           0 :     while (nsCRT::IsAsciiSpace(*str)) ++str;
     660           0 :     if (*str == ';') {
     661           0 :       ++str;
     662             :     } else {
     663             :       // stop processing the header field; either we are done or the
     664             :       // separator was missing
     665           0 :       break;
     666             :     }
     667           0 :     while (nsCRT::IsAsciiSpace(*str)) ++str;
     668             :   }
     669             : 
     670           5 :   caseCDResult = combineContinuations(segments);
     671             : 
     672           5 :   if (caseBResult && !charsetB.IsEmpty()) {
     673             :     // check that the 2231/5987 result decodes properly given the
     674             :     // specified character set
     675           0 :     if (!IsValidOctetSequenceForCharset(charsetB, caseBResult))
     676           0 :       caseBResult = nullptr;
     677             :   }
     678             : 
     679           5 :   if (caseCDResult && !charsetCD.IsEmpty()) {
     680             :     // check that the 2231/5987 result decodes properly given the
     681             :     // specified character set
     682           0 :     if (!IsValidOctetSequenceForCharset(charsetCD, caseCDResult))
     683           0 :       caseCDResult = nullptr;
     684             :   }
     685             : 
     686           5 :   if (caseBResult) {
     687             :     // prefer simple 5987 format over 2231 with continuations
     688           0 :     *aResult = caseBResult;
     689           0 :     caseBResult = nullptr;
     690           0 :     charset.Assign(charsetB);
     691             :   }
     692           5 :   else if (caseCDResult) {
     693             :     // prefer 2231/5987 with or without continuations over plain format
     694           0 :     *aResult = caseCDResult;
     695           0 :     caseCDResult = nullptr;
     696           0 :     charset.Assign(charsetCD);
     697             :   }
     698           5 :   else if (caseAResult) {
     699           0 :     *aResult = caseAResult;
     700           0 :     caseAResult = nullptr;
     701             :   }
     702             : 
     703             :   // free unused stuff
     704           5 :   free(caseAResult);
     705           5 :   free(caseBResult);
     706           5 :   free(caseCDResult);
     707             : 
     708             :   // if we have a result
     709           5 :   if (*aResult) {
     710             :     // then return charset and lang as well
     711           0 :     if (aLang && !lang.IsEmpty()) {
     712           0 :       uint32_t len = lang.Length();
     713           0 :       *aLang = (char *) nsMemory::Clone(lang.BeginReading(), len + 1);
     714           0 :       if (*aLang) {
     715           0 :         *(*aLang + len) = 0;
     716             :       }
     717             :    }
     718           0 :     if (aCharset && !charset.IsEmpty()) {
     719           0 :       uint32_t len = charset.Length();
     720           0 :       *aCharset = (char *) nsMemory::Clone(charset.BeginReading(), len + 1);
     721           0 :       if (*aCharset) {
     722           0 :         *(*aCharset + len) = 0;
     723             :       }
     724             :     }
     725             :   }
     726             : 
     727           5 :   return *aResult ? NS_OK : NS_ERROR_INVALID_ARG;
     728             : }
     729             : 
     730             : nsresult
     731           5 : internalDecodeRFC2047Header(const char* aHeaderVal, const char* aDefaultCharset,
     732             :                             bool aOverrideCharset, bool aEatContinuations,
     733             :                             nsACString& aResult)
     734             : {
     735           5 :   aResult.Truncate();
     736           5 :   if (!aHeaderVal)
     737           0 :     return NS_ERROR_INVALID_ARG;
     738           5 :   if (!*aHeaderVal)
     739           0 :     return NS_OK;
     740             : 
     741             : 
     742             :   // If aHeaderVal is RFC 2047 encoded or is not a UTF-8 string  but
     743             :   // aDefaultCharset is specified, decodes RFC 2047 encoding and converts
     744             :   // to UTF-8. Otherwise, just strips away CRLF.
     745          15 :   if (PL_strstr(aHeaderVal, "=?") ||
     746           5 :       (aDefaultCharset && (!IsUTF8(nsDependentCString(aHeaderVal)) ||
     747           0 :       Is7bitNonAsciiString(aHeaderVal, strlen(aHeaderVal))))) {
     748           0 :     DecodeRFC2047Str(aHeaderVal, aDefaultCharset, aOverrideCharset, aResult);
     749          10 :   } else if (aEatContinuations &&
     750          10 :              (PL_strchr(aHeaderVal, '\n') || PL_strchr(aHeaderVal, '\r'))) {
     751           0 :     aResult = aHeaderVal;
     752             :   } else {
     753           5 :     aEatContinuations = false;
     754           5 :     aResult = aHeaderVal;
     755             :   }
     756             : 
     757           5 :   if (aEatContinuations) {
     758           0 :     nsAutoCString temp(aResult);
     759           0 :     temp.ReplaceSubstring("\n\t", " ");
     760           0 :     temp.ReplaceSubstring("\r\t", " ");
     761           0 :     temp.StripCRLF();
     762           0 :     aResult = temp;
     763             :   }
     764             : 
     765           5 :   return NS_OK;
     766             : }
     767             : 
     768             : NS_IMETHODIMP
     769           0 : nsMIMEHeaderParamImpl::DecodeRFC2047Header(const char* aHeaderVal,
     770             :                                            const char* aDefaultCharset,
     771             :                                            bool aOverrideCharset,
     772             :                                            bool aEatContinuations,
     773             :                                            nsACString& aResult)
     774             : {
     775           0 :   return internalDecodeRFC2047Header(aHeaderVal, aDefaultCharset,
     776             :                                      aOverrideCharset, aEatContinuations,
     777           0 :                                      aResult);
     778             : }
     779             : 
     780             : // true if the character is allowed in a RFC 5987 value
     781             : // see RFC 5987, Section 3.2.1, "attr-char"
     782           0 : bool IsRFC5987AttrChar(char aChar)
     783             : {
     784           0 :   char c = aChar;
     785             : 
     786           0 :   return (c >= 'a' && c <= 'z') ||
     787           0 :          (c >= 'A' && c <= 'Z') ||
     788           0 :          (c >= '0' && c <= '9') ||
     789           0 :          (c == '!' || c == '#' || c == '$' || c == '&' ||
     790           0 :           c == '+' || c == '-' || c == '.' || c == '^' ||
     791           0 :           c == '_' || c == '`' || c == '|' || c == '~');
     792             : }
     793             : 
     794             : // percent-decode a value
     795             : // returns false on failure
     796           0 : bool PercentDecode(nsACString& aValue)
     797             : {
     798           0 :   char *c = (char *) moz_xmalloc(aValue.Length() + 1);
     799           0 :   if (!c) {
     800           0 :     return false;
     801             :   }
     802             : 
     803           0 :   strcpy(c, PromiseFlatCString(aValue).get());
     804           0 :   nsUnescape(c);
     805           0 :   aValue.Assign(c);
     806           0 :   free(c);
     807             : 
     808           0 :   return true;
     809             : }
     810             : 
     811             : // Decode a parameter value using the encoding defined in RFC 5987
     812             : //
     813             : // charset  "'" [ language ] "'" value-chars
     814             : NS_IMETHODIMP
     815           0 : nsMIMEHeaderParamImpl::DecodeRFC5987Param(const nsACString& aParamVal,
     816             :                                           nsACString& aLang,
     817             :                                           nsAString& aResult)
     818             : {
     819           0 :   nsAutoCString charset;
     820           0 :   nsAutoCString language;
     821           0 :   nsAutoCString value;
     822             : 
     823           0 :   uint32_t delimiters = 0;
     824           0 :   const char *encoded = PromiseFlatCString(aParamVal).get();
     825           0 :   const char *c = encoded;
     826             : 
     827           0 :   while (*c) {
     828           0 :     char tc = *c++;
     829             : 
     830           0 :     if (tc == '\'') {
     831             :       // single quote
     832           0 :       delimiters++;
     833           0 :     } else if (((unsigned char)tc) >= 128) {
     834             :       // fail early, not ASCII
     835           0 :       NS_WARNING("non-US-ASCII character in RFC5987-encoded param");
     836           0 :       return NS_ERROR_INVALID_ARG;
     837             :     } else {
     838           0 :       if (delimiters == 0) {
     839             :         // valid characters are checked later implicitly
     840           0 :         charset.Append(tc);
     841           0 :       } else if (delimiters == 1) {
     842             :         // no value checking for now
     843           0 :         language.Append(tc);
     844           0 :       } else if (delimiters == 2) {
     845           0 :         if (IsRFC5987AttrChar(tc)) {
     846           0 :           value.Append(tc);
     847           0 :         } else if (tc == '%') {
     848           0 :           if (!IsHexDigit(c[0]) || !IsHexDigit(c[1])) {
     849             :             // we expect two more characters
     850           0 :             NS_WARNING("broken %-escape in RFC5987-encoded param");
     851           0 :             return NS_ERROR_INVALID_ARG;
     852             :           }
     853           0 :           value.Append(tc);
     854             :           // we consume two more
     855           0 :           value.Append(*c++);
     856           0 :           value.Append(*c++);
     857             :         } else {
     858             :           // character not allowed here
     859           0 :           NS_WARNING("invalid character in RFC5987-encoded param");
     860           0 :           return NS_ERROR_INVALID_ARG;
     861             :         }
     862             :       }
     863             :     }
     864             :   }
     865             : 
     866           0 :   if (delimiters != 2) {
     867           0 :     NS_WARNING("missing delimiters in RFC5987-encoded param");
     868           0 :     return NS_ERROR_INVALID_ARG;
     869             :   }
     870             : 
     871             :   // abort early for unsupported encodings
     872           0 :   if (!charset.LowerCaseEqualsLiteral("utf-8")) {
     873           0 :     NS_WARNING("unsupported charset in RFC5987-encoded param");
     874           0 :     return NS_ERROR_INVALID_ARG;
     875             :   }
     876             : 
     877             :   // percent-decode
     878           0 :   if (!PercentDecode(value)) {
     879           0 :     return NS_ERROR_OUT_OF_MEMORY;
     880             :   }
     881             : 
     882             :   // return the encoding
     883           0 :   aLang.Assign(language);
     884             : 
     885             :   // finally convert octet sequence to UTF-8 and be done
     886           0 :   nsresult rv = NS_OK;
     887             :   nsCOMPtr<nsIUTF8ConverterService> cvtUTF8 =
     888           0 :     do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv);
     889           0 :   NS_ENSURE_SUCCESS(rv, rv);
     890             : 
     891           0 :   nsAutoCString utf8;
     892           0 :   rv = cvtUTF8->ConvertStringToUTF8(value, charset.get(), true, false, 1, utf8);
     893           0 :   NS_ENSURE_SUCCESS(rv, rv);
     894             : 
     895           0 :   CopyUTF8toUTF16(utf8, aResult);
     896           0 :   return NS_OK;
     897             : }
     898             : 
     899             : nsresult
     900           5 : internalDecodeParameter(const nsACString& aParamValue, const char* aCharset,
     901             :                         const char* aDefaultCharset, bool aOverrideCharset,
     902             :                         bool aDecode2047, nsACString& aResult)
     903             : {
     904           5 :   aResult.Truncate();
     905             :   // If aCharset is given, aParamValue was obtained from RFC2231/5987
     906             :   // encoding and we're pretty sure that it's in aCharset.
     907           5 :   if (aCharset && *aCharset)
     908             :   {
     909           0 :     nsCOMPtr<nsIUTF8ConverterService> cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID));
     910           0 :     if (cvtUTF8)
     911           0 :       return cvtUTF8->ConvertStringToUTF8(aParamValue, aCharset,
     912           0 :           true, true, 1, aResult);
     913             :   }
     914             : 
     915          10 :   const nsCString& param = PromiseFlatCString(aParamValue);
     916          10 :   nsAutoCString unQuoted;
     917           5 :   nsACString::const_iterator s, e;
     918           5 :   param.BeginReading(s);
     919           5 :   param.EndReading(e);
     920             : 
     921             :   // strip '\' when used to quote CR, LF, '"' and '\'
     922         155 :   for ( ; s != e; ++s) {
     923          75 :     if ((*s == '\\')) {
     924           0 :       if (++s == e) {
     925           0 :         --s; // '\' is at the end. move back and append '\'.
     926             :       }
     927           0 :       else if (*s != nsCRT::CR && *s != nsCRT::LF && *s != '"' && *s != '\\') {
     928           0 :         --s; // '\' is not foll. by CR,LF,'"','\'. move back and append '\'
     929             :       }
     930             :       // else : skip '\' and append the quoted character.
     931             :     }
     932          75 :     unQuoted.Append(*s);
     933             :   }
     934             : 
     935           5 :   aResult = unQuoted;
     936           5 :   nsresult rv = NS_OK;
     937             : 
     938           5 :   if (aDecode2047) {
     939          10 :     nsAutoCString decoded;
     940             : 
     941             :     // Try RFC 2047 encoding, instead.
     942           5 :     rv = internalDecodeRFC2047Header(unQuoted.get(), aDefaultCharset,
     943           5 :                                      aOverrideCharset, true, decoded);
     944             : 
     945           5 :     if (NS_SUCCEEDED(rv) && !decoded.IsEmpty())
     946           5 :       aResult = decoded;
     947             :   }
     948             : 
     949           5 :   return rv;
     950             : }
     951             : 
     952             : NS_IMETHODIMP
     953           0 : nsMIMEHeaderParamImpl::DecodeParameter(const nsACString& aParamValue,
     954             :                                        const char* aCharset,
     955             :                                        const char* aDefaultCharset,
     956             :                                        bool aOverrideCharset,
     957             :                                        nsACString& aResult)
     958             : {
     959           0 :   return internalDecodeParameter(aParamValue, aCharset, aDefaultCharset,
     960           0 :                                  aOverrideCharset, true, aResult);
     961             : }
     962             : 
     963             : #define ISHEXCHAR(c) \
     964             :         ((0x30 <= uint8_t(c) && uint8_t(c) <= 0x39)  ||  \
     965             :          (0x41 <= uint8_t(c) && uint8_t(c) <= 0x46)  ||  \
     966             :          (0x61 <= uint8_t(c) && uint8_t(c) <= 0x66))
     967             : 
     968             : // Decode Q encoding (RFC 2047).
     969             : // static
     970           0 : char *DecodeQ(const char *in, uint32_t length)
     971             : {
     972           0 :   char *out, *dest = nullptr;
     973             : 
     974           0 :   out = dest = (char*) calloc(length + 1, sizeof(char));
     975           0 :   if (dest == nullptr)
     976           0 :     return nullptr;
     977           0 :   while (length > 0) {
     978           0 :     unsigned c = 0;
     979           0 :     switch (*in) {
     980             :     case '=':
     981             :       // check if |in| in the form of '=hh'  where h is [0-9a-fA-F].
     982           0 :       if (length < 3 || !ISHEXCHAR(in[1]) || !ISHEXCHAR(in[2]))
     983             :         goto badsyntax;
     984           0 :       PR_sscanf(in + 1, "%2X", &c);
     985           0 :       *out++ = (char) c;
     986           0 :       in += 3;
     987           0 :       length -= 3;
     988           0 :       break;
     989             : 
     990             :     case '_':
     991           0 :       *out++ = ' ';
     992           0 :       in++;
     993           0 :       length--;
     994           0 :       break;
     995             : 
     996             :     default:
     997           0 :       if (*in & 0x80) goto badsyntax;
     998           0 :       *out++ = *in++;
     999           0 :       length--;
    1000             :     }
    1001             :   }
    1002           0 :   *out++ = '\0';
    1003             : 
    1004           0 :   for (out = dest; *out ; ++out) {
    1005           0 :     if (*out == '\t')
    1006           0 :       *out = ' ';
    1007             :   }
    1008             : 
    1009           0 :   return dest;
    1010             : 
    1011             :  badsyntax:
    1012           0 :   free(dest);
    1013           0 :   return nullptr;
    1014             : }
    1015             : 
    1016             : // check if input is HZ (a 7bit encoding for simplified Chinese : RFC 1842))
    1017             : // or has  ESC which may be an  indication that  it's in one of many ISO
    1018             : // 2022 7bit  encodings (e.g. ISO-2022-JP(-2)/CN : see RFC 1468, 1922, 1554).
    1019             : // static
    1020           0 : bool Is7bitNonAsciiString(const char *input, uint32_t len)
    1021             : {
    1022             :   int32_t c;
    1023             : 
    1024             :   enum { hz_initial, // No HZ seen yet
    1025             :          hz_escaped, // Inside an HZ ~{ escape sequence
    1026             :          hz_seen, // Have seen at least one complete HZ sequence
    1027             :          hz_notpresent // Have seen something that is not legal HZ
    1028             :   } hz_state;
    1029             : 
    1030           0 :   hz_state = hz_initial;
    1031           0 :   while (len) {
    1032           0 :     c = uint8_t(*input++);
    1033           0 :     len--;
    1034           0 :     if (c & 0x80) return false;
    1035           0 :     if (c == 0x1B) return true;
    1036           0 :     if (c == '~') {
    1037           0 :       switch (hz_state) {
    1038             :       case hz_initial:
    1039             :       case hz_seen:
    1040           0 :         if (*input == '{') {
    1041           0 :           hz_state = hz_escaped;
    1042           0 :         } else if (*input == '~') {
    1043             :           // ~~ is the HZ encoding of ~.  Skip over second ~ as well
    1044           0 :           hz_state = hz_seen;
    1045           0 :           input++;
    1046           0 :           len--;
    1047             :         } else {
    1048           0 :           hz_state = hz_notpresent;
    1049             :         }
    1050           0 :         break;
    1051             : 
    1052             :       case hz_escaped:
    1053           0 :         if (*input == '}') hz_state = hz_seen;
    1054           0 :         break;
    1055             :       default:
    1056           0 :         break;
    1057             :       }
    1058             :     }
    1059             :   }
    1060           0 :   return hz_state == hz_seen;
    1061             : }
    1062             : 
    1063             : #define REPLACEMENT_CHAR "\357\277\275" // EF BF BD (UTF-8 encoding of U+FFFD)
    1064             : 
    1065             : // copy 'raw' sequences of octets in aInput to aOutput.
    1066             : // If aDefaultCharset is specified, the input is assumed to be in the
    1067             : // charset and converted to UTF-8. Otherwise, a blind copy is made.
    1068             : // If aDefaultCharset is specified, but the conversion to UTF-8
    1069             : // is not successful, each octet is replaced by Unicode replacement
    1070             : // chars. *aOutput is advanced by the number of output octets.
    1071             : // static
    1072           0 : void CopyRawHeader(const char *aInput, uint32_t aLen,
    1073             :                    const char *aDefaultCharset, nsACString &aOutput)
    1074             : {
    1075             :   int32_t c;
    1076             : 
    1077             :   // If aDefaultCharset is not specified, make a blind copy.
    1078           0 :   if (!aDefaultCharset || !*aDefaultCharset) {
    1079           0 :     aOutput.Append(aInput, aLen);
    1080           0 :     return;
    1081             :   }
    1082             : 
    1083             :   // Copy as long as it's US-ASCII.  An ESC may indicate ISO 2022
    1084             :   // A ~ may indicate it is HZ
    1085           0 :   while (aLen && (c = uint8_t(*aInput++)) != 0x1B && c != '~' && !(c & 0x80)) {
    1086           0 :     aOutput.Append(char(c));
    1087           0 :     aLen--;
    1088             :   }
    1089           0 :   if (!aLen) {
    1090           0 :     return;
    1091             :   }
    1092           0 :   aInput--;
    1093             : 
    1094             :   // skip ASCIIness/UTF8ness test if aInput is supected to be a 7bit non-ascii
    1095             :   // string and aDefaultCharset is a 7bit non-ascii charset.
    1096           0 :   bool skipCheck = (c == 0x1B || c == '~') &&
    1097           0 :                      IS_7BIT_NON_ASCII_CHARSET(aDefaultCharset);
    1098             : 
    1099             :   // If not UTF-8, treat as default charset
    1100             :   nsCOMPtr<nsIUTF8ConverterService>
    1101           0 :     cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID));
    1102           0 :   nsAutoCString utf8Text;
    1103           0 :   if (cvtUTF8 &&
    1104           0 :       NS_SUCCEEDED(
    1105             :       cvtUTF8->ConvertStringToUTF8(Substring(aInput, aInput + aLen),
    1106             :                                    aDefaultCharset, skipCheck, true, 1,
    1107             :                                    utf8Text))) {
    1108           0 :     aOutput.Append(utf8Text);
    1109             :   } else { // replace each octet with Unicode replacement char in UTF-8.
    1110           0 :     for (uint32_t i = 0; i < aLen; i++) {
    1111           0 :       c = uint8_t(*aInput++);
    1112           0 :       if (c & 0x80)
    1113           0 :         aOutput.Append(REPLACEMENT_CHAR);
    1114             :       else
    1115           0 :         aOutput.Append(char(c));
    1116             :     }
    1117             :   }
    1118             : }
    1119             : 
    1120           0 : nsresult DecodeQOrBase64Str(const char *aEncoded, size_t aLen, char aQOrBase64,
    1121             :                             const char *aCharset, nsACString &aResult)
    1122             : {
    1123             :   char *decodedText;
    1124           0 :   NS_ASSERTION(aQOrBase64 == 'Q' || aQOrBase64 == 'B', "Should be 'Q' or 'B'");
    1125           0 :   if(aQOrBase64 == 'Q')
    1126           0 :     decodedText = DecodeQ(aEncoded, aLen);
    1127           0 :   else if (aQOrBase64 == 'B') {
    1128           0 :     decodedText = PL_Base64Decode(aEncoded, aLen, nullptr);
    1129             :   } else {
    1130           0 :     return NS_ERROR_INVALID_ARG;
    1131             :   }
    1132             : 
    1133           0 :   if (!decodedText) {
    1134           0 :     return NS_ERROR_INVALID_ARG;
    1135             :   }
    1136             : 
    1137             :   nsresult rv;
    1138             :   nsCOMPtr<nsIUTF8ConverterService>
    1139           0 :     cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv));
    1140           0 :   nsAutoCString utf8Text;
    1141           0 :   if (NS_SUCCEEDED(rv)) {
    1142             :     // skip ASCIIness/UTF8ness test if aCharset is 7bit non-ascii charset.
    1143           0 :     rv = cvtUTF8->ConvertStringToUTF8(nsDependentCString(decodedText),
    1144             :                                       aCharset,
    1145           0 :                                       IS_7BIT_NON_ASCII_CHARSET(aCharset),
    1146           0 :                                       true, 1, utf8Text);
    1147             :   }
    1148           0 :   free(decodedText);
    1149           0 :   if (NS_FAILED(rv)) {
    1150           0 :     return rv;
    1151             :   }
    1152           0 :   aResult.Append(utf8Text);
    1153             : 
    1154           0 :   return NS_OK;
    1155             : }
    1156             : 
    1157             : static const char especials[] = R"(()<>@,;:\"/[]?.=)";
    1158             : 
    1159             : // |decode_mime_part2_str| taken from comi18n.c
    1160             : // Decode RFC2047-encoded words in the input and convert the result to UTF-8.
    1161             : // If aOverrideCharset is true, charset in RFC2047-encoded words is
    1162             : // ignored and aDefaultCharset is assumed, instead. aDefaultCharset
    1163             : // is also used to convert raw octets (without RFC 2047 encoding) to UTF-8.
    1164             : //static
    1165           0 : nsresult DecodeRFC2047Str(const char *aHeader, const char *aDefaultCharset,
    1166             :                           bool aOverrideCharset, nsACString &aResult)
    1167             : {
    1168           0 :   const char *p, *q = nullptr, *r;
    1169             :   const char *begin; // tracking pointer for where we are in the input buffer
    1170           0 :   int32_t isLastEncodedWord = 0;
    1171             :   const char *charsetStart, *charsetEnd;
    1172           0 :   nsAutoCString prevCharset, curCharset;
    1173           0 :   nsAutoCString encodedText;
    1174           0 :   char prevEncoding = '\0', curEncoding;
    1175             :   nsresult rv;
    1176             : 
    1177           0 :   begin = aHeader;
    1178             : 
    1179             :   // To avoid buffer realloc, if possible, set capacity in advance. No
    1180             :   // matter what,  more than 3x expansion can never happen for all charsets
    1181             :   // supported by Mozilla. SCSU/BCSU with the sliding window set to a
    1182             :   // non-BMP block may be exceptions, but Mozilla does not support them.
    1183             :   // Neither any known mail/news program use them. Even if there's, we're
    1184             :   // safe because we don't use a raw *char any more.
    1185           0 :   aResult.SetCapacity(3 * strlen(aHeader));
    1186             : 
    1187           0 :   while ((p = PL_strstr(begin, "=?")) != nullptr) {
    1188           0 :     if (isLastEncodedWord) {
    1189             :       // See if it's all whitespace.
    1190           0 :       for (q = begin; q < p; ++q) {
    1191           0 :         if (!PL_strchr(" \t\r\n", *q)) break;
    1192             :       }
    1193             :     }
    1194             : 
    1195           0 :     if (!isLastEncodedWord || q < p) {
    1196           0 :       if (!encodedText.IsEmpty()) {
    1197           0 :         rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
    1198           0 :                                 prevEncoding, prevCharset.get(), aResult);
    1199           0 :         if (NS_FAILED(rv)) {
    1200           0 :           aResult.Append(encodedText);
    1201             :         }
    1202           0 :         encodedText.Truncate();
    1203           0 :         prevCharset.Truncate();
    1204           0 :         prevEncoding = '\0';
    1205             :       }
    1206             :       // copy the part before the encoded-word
    1207           0 :       CopyRawHeader(begin, p - begin, aDefaultCharset, aResult);
    1208           0 :       begin = p;
    1209             :     }
    1210             : 
    1211           0 :     p += 2;
    1212             : 
    1213             :     // Get charset info
    1214           0 :     charsetStart = p;
    1215           0 :     charsetEnd = nullptr;
    1216           0 :     for (q = p; *q != '?'; q++) {
    1217           0 :       if (*q <= ' ' || PL_strchr(especials, *q)) {
    1218           0 :         goto badsyntax;
    1219             :       }
    1220             : 
    1221             :       // RFC 2231 section 5
    1222           0 :       if (!charsetEnd && *q == '*') {
    1223           0 :         charsetEnd = q;
    1224             :       }
    1225             :     }
    1226           0 :     if (!charsetEnd) {
    1227           0 :       charsetEnd = q;
    1228             :     }
    1229             : 
    1230           0 :     q++;
    1231           0 :     curEncoding = nsCRT::ToUpper(*q);
    1232           0 :     if (curEncoding != 'Q' && curEncoding != 'B')
    1233           0 :       goto badsyntax;
    1234             : 
    1235           0 :     if (q[1] != '?')
    1236           0 :       goto badsyntax;
    1237             : 
    1238             :     // loop-wise, keep going until we hit "?=".  the inner check handles the
    1239             :     //  nul terminator should the string terminate before we hit the right
    1240             :     //  marker.  (And the r[1] will never reach beyond the end of the string
    1241             :     //  because *r != '?' is true if r is the nul character.)
    1242           0 :     for (r = q + 2; *r != '?' || r[1] != '='; r++) {
    1243           0 :       if (*r < ' ') goto badsyntax;
    1244             :     }
    1245           0 :     if (r == q + 2) {
    1246             :         // it's empty, skip
    1247           0 :         begin = r + 2;
    1248           0 :         isLastEncodedWord = 1;
    1249           0 :         continue;
    1250             :     }
    1251             : 
    1252           0 :     curCharset.Assign(charsetStart, charsetEnd - charsetStart);
    1253             :     // Override charset if requested.  Never override labeled UTF-8.
    1254             :     // Use default charset instead of UNKNOWN-8BIT
    1255           0 :     if ((aOverrideCharset && 0 != nsCRT::strcasecmp(curCharset.get(), "UTF-8"))
    1256           0 :     || (aDefaultCharset && 0 == nsCRT::strcasecmp(curCharset.get(), "UNKNOWN-8BIT"))
    1257             :     ) {
    1258           0 :       curCharset = aDefaultCharset;
    1259             :     }
    1260             : 
    1261             :     const char *R;
    1262           0 :     R = r;
    1263           0 :     if (curEncoding == 'B') {
    1264             :       // bug 227290. ignore an extraneous '=' at the end.
    1265             :       // (# of characters in B-encoded part has to be a multiple of 4)
    1266           0 :       int32_t n = r - (q + 2);
    1267           0 :       R -= (n % 4 == 1 && !PL_strncmp(r - 3, "===", 3)) ? 1 : 0;
    1268             :     }
    1269             :     // Bug 493544. Don't decode the encoded text until it ends
    1270           0 :     if (R[-1] != '='
    1271           0 :       && (prevCharset.IsEmpty()
    1272           0 :         || (curCharset == prevCharset && curEncoding == prevEncoding))
    1273             :     ) {
    1274           0 :       encodedText.Append(q + 2, R - (q + 2));
    1275           0 :       prevCharset = curCharset;
    1276           0 :       prevEncoding = curEncoding;
    1277             : 
    1278           0 :       begin = r + 2;
    1279           0 :       isLastEncodedWord = 1;
    1280           0 :       continue;
    1281             :     }
    1282             : 
    1283             :     bool bDecoded; // If the current line has been decoded.
    1284           0 :     bDecoded = false;
    1285           0 :     if (!encodedText.IsEmpty()) {
    1286           0 :       if (curCharset == prevCharset && curEncoding == prevEncoding) {
    1287           0 :         encodedText.Append(q + 2, R - (q + 2));
    1288           0 :         bDecoded = true;
    1289             :       }
    1290           0 :       rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
    1291           0 :                               prevEncoding, prevCharset.get(), aResult);
    1292           0 :       if (NS_FAILED(rv)) {
    1293           0 :         aResult.Append(encodedText);
    1294             :       }
    1295           0 :       encodedText.Truncate();
    1296           0 :       prevCharset.Truncate();
    1297           0 :       prevEncoding = '\0';
    1298             :     }
    1299           0 :     if (!bDecoded) {
    1300           0 :       rv = DecodeQOrBase64Str(q + 2, R - (q + 2), curEncoding,
    1301           0 :                               curCharset.get(), aResult);
    1302           0 :       if (NS_FAILED(rv)) {
    1303           0 :         aResult.Append(encodedText);
    1304             :       }
    1305             :     }
    1306             : 
    1307           0 :     begin = r + 2;
    1308           0 :     isLastEncodedWord = 1;
    1309           0 :     continue;
    1310             : 
    1311             :   badsyntax:
    1312           0 :     if (!encodedText.IsEmpty()) {
    1313           0 :       rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
    1314           0 :                               prevEncoding, prevCharset.get(), aResult);
    1315           0 :       if (NS_FAILED(rv)) {
    1316           0 :         aResult.Append(encodedText);
    1317             :       }
    1318           0 :       encodedText.Truncate();
    1319           0 :       prevCharset.Truncate();
    1320             :     }
    1321             :     // copy the part before the encoded-word
    1322           0 :     aResult.Append(begin, p - begin);
    1323           0 :     begin = p;
    1324           0 :     isLastEncodedWord = 0;
    1325             :   }
    1326             : 
    1327           0 :   if (!encodedText.IsEmpty()) {
    1328           0 :     rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
    1329           0 :                             prevEncoding, prevCharset.get(), aResult);
    1330           0 :     if (NS_FAILED(rv)) {
    1331           0 :       aResult.Append(encodedText);
    1332             :     }
    1333             :   }
    1334             : 
    1335             :   // put the tail back
    1336           0 :   CopyRawHeader(begin, strlen(begin), aDefaultCharset, aResult);
    1337             : 
    1338           0 :   nsAutoCString tempStr(aResult);
    1339           0 :   tempStr.ReplaceChar('\t', ' ');
    1340           0 :   aResult = tempStr;
    1341             : 
    1342           0 :   return NS_OK;
    1343             : }

Generated by: LCOV version 1.13