summaryrefslogtreecommitdiffstats
path: root/netwerk/mime
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/mime/moz.build23
-rw-r--r--netwerk/mime/nsIMIMEHeaderParam.idl206
-rw-r--r--netwerk/mime/nsIMIMEInfo.idl369
-rw-r--r--netwerk/mime/nsIMIMEService.idl242
-rw-r--r--netwerk/mime/nsMIMEHeaderParamImpl.cpp1328
-rw-r--r--netwerk/mime/nsMIMEHeaderParamImpl.h42
-rw-r--r--netwerk/mime/nsMimeTypes.h279
7 files changed, 2489 insertions, 0 deletions
diff --git a/netwerk/mime/moz.build b/netwerk/mime/moz.build
new file mode 100644
index 0000000000..a7b62777f4
--- /dev/null
+++ b/netwerk/mime/moz.build
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ "nsIMIMEHeaderParam.idl",
+ "nsIMIMEInfo.idl",
+ "nsIMIMEService.idl",
+]
+
+XPIDL_MODULE = "mimetype"
+
+EXPORTS += [
+ "nsMimeTypes.h",
+]
+
+SOURCES += [
+ "nsMIMEHeaderParamImpl.cpp",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/netwerk/mime/nsIMIMEHeaderParam.idl b/netwerk/mime/nsIMIMEHeaderParam.idl
new file mode 100644
index 0000000000..2a1edf9594
--- /dev/null
+++ b/netwerk/mime/nsIMIMEHeaderParam.idl
@@ -0,0 +1,206 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This interface allows any module to access the routine
+ * for MIME header parameter parsing (RFC 2231/5987)
+ */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(9c9252a1-fdaf-40a2-9c2b-a3dc45e28dde)]
+interface nsIMIMEHeaderParam : nsISupports {
+
+ /**
+ * Given the value of a single header field (such as
+ * Content-Disposition and Content-Type) and the name of a parameter
+ * (e.g. filename, name, charset), returns the value of the parameter.
+ * The value is obtained by decoding RFC 2231/5987-style encoding,
+ * RFC 2047-style encoding, and converting to UniChar(UTF-16)
+ * from charset specified in RFC 2231/2047 encoding, UTF-8,
+ * <code>aFallbackCharset</code>, the locale charset as fallback if
+ * <code>TryLocaleCharset</code> is set, and null-padding as last resort
+ * if all else fails.
+ *
+ * <p>
+ * This method internally invokes <code>getParameterInternal</code>,
+ * However, it does not stop at decoding RFC 2231 (the task for
+ * <code>getParameterInternal</code> but tries to cope
+ * with several non-standard-compliant cases mentioned below.
+ *
+ * <p>
+ * Note that a lot of MUAs put RFC 2047-encoded parameters. Unfortunately,
+ * this includes Mozilla as of 2003-05-30. Even more standard-ignorant MUAs,
+ * web servers and application servers put 'raw 8bit characters'. This will
+ * try to cope with all these cases as gracefully as possible. Additionally,
+ * it returns the language tag if the parameter is encoded per RFC 2231 and
+ * includes lang.
+ *
+ * <p>
+ * Note that GetParameterHTTP skips some of the workarounds used for
+ * mail (MIME) header fields, and thus SHOULD be used from non-mail
+ * code.
+ *
+ *
+ * @param aHeaderVal a header string to get the value of a parameter
+ * from.
+ * @param aParamName the name of a MIME header parameter (e.g.
+ * filename, name, charset). If empty, returns
+ * the first (possibly) _unnamed_ 'parameter'.
+ * @param aFallbackCharset fallback charset to try if the string after
+ * RFC 2231/2047 decoding or the raw 8bit
+ * string is not UTF-8
+ * @param aTryLocaleCharset If set, makes yet another attempt
+ * with the locale charset.
+ * @param aLang If non-null, assigns it to a pointer
+ * to a string containing the value of language
+ * obtained from RFC 2231 parsing. Caller has to
+ * free it.
+ * @return the value of <code>aParamName</code> in Unichar(UTF-16).
+ */
+ AString getParameter(in ACString aHeaderVal,
+ in string aParamName,
+ in ACString aFallbackCharset,
+ in boolean aTryLocaleCharset,
+ out string aLang);
+
+
+ /**
+ * Like getParameter, but disabling encodings and workarounds specific to
+ * MIME (as opposed to HTTP).
+ */
+ AString getParameterHTTP(in ACString aHeaderVal,
+ in string aParamName,
+ in ACString aFallbackCharset,
+ in boolean aTryLocaleCharset,
+ out string aLang);
+
+ /**
+ * Given the value of a header field parameter using the encoding
+ * defined in RFC 5987, decode the value into a Unicode string, and extract
+ * the optional language parameter.
+ *
+ * <p>
+ * This function is purposefully picky; it will abort for all (most?)
+ * invalid inputs. This is by design. In particular, it does not support
+ * any character encodings other than UTF-8, in order not to promote
+ * non-interoperable usage.
+ *
+ * <p>
+ * Code that parses HTTP header fields (as opposed to MIME header fields)
+ * should use this function.
+ *
+ * @param aParamVal a header field parameter to decode.
+ * @param aLang will be set to the language part (possibly
+ * empty).
+ * @return the decoded parameter value.
+ */
+ AString decodeRFC5987Param(in ACString aParamVal,
+ out ACString aLang);
+
+ /**
+ * Given the value of a single header field (such as
+ * Content-Disposition and Content-Type) and the name of a parameter
+ * (e.g. filename, name, charset), returns the value of the parameter
+ * after decoding RFC 2231-style encoding.
+ * <p>
+ * For <strong>internal use only</strong>. The only other place where
+ * this needs to be invoked is |MimeHeaders_get_parameter| in
+ * mailnews/mime/src/mimehdrs.cpp defined as
+ * char * MimeHeaders_get_parameter (const char *header_value,
+ * const char *parm_name,
+ * char **charset, char **language)
+ *
+ * Otherwise, this method would have been made static.
+ *
+ * @param aHeaderVal a header string to get the value of a parameter from.
+ * @param aParamName the name of a MIME header parameter (e.g.
+ * filename, name, charset). If empty, returns
+ * the first (possibly) _unnamed_ 'parameter'.
+ * @param aCharset If non-null, it gets assigned a new pointer
+ * to a string containing the value of charset obtained
+ * from RFC 2231 parsing. Caller has to free it.
+ * @param aLang If non-null, it gets assigned a new pointer
+ * to a string containing the value of language obtained
+ * from RFC 2231 parsing. Caller has to free it.
+ * @return the value of <code>aParamName</code> after
+ * RFC 2231 decoding but without charset conversion.
+ */
+
+ [noscript]
+ string getParameterInternal(in string aHeaderVal,
+ in string aParamName,
+ out string aCharset,
+ out string aLang);
+
+
+ /**
+ * Given a header value, decodes RFC 2047-style encoding and
+ * returns the decoded header value in UTF-8 if either it's
+ * RFC-2047-encoded or aDefaultCharset is given. Otherwise,
+ * returns the input header value (in whatever encoding)
+ * as it is except that RFC 822 (using backslash) quotation and
+ * CRLF (if aEatContinuation is set) are stripped away
+ * <p>
+ * For internal use only. The only other place where this needs to be
+ * invoked is <code>MIME_DecodeMimeHeader</code> in
+ * mailnews/mime/src/mimehdrs.cpp defined as
+ * char * Mime_DecodeMimeHeader(char *header_val, const char *charset,
+ * bool override, bool eatcontinuation)
+ *
+ * @param aHeaderVal a header value to decode
+ * @param aDefaultCharset MIME charset to use in place of MIME charset
+ * specified in RFC 2047 style encoding
+ * when <code>aOverrideCharset</code> is set.
+ * @param aOverrideCharset When set, overrides MIME charset specified
+ * in RFC 2047 style encoding with <code>aDefaultCharset</code>
+ * @param aEatContinuation When set, removes CR/LF
+ * @return decoded header value
+ */
+ [noscript]
+ ACString decodeRFC2047Header(in string aHeaderVal,
+ in string aDefaultCharset,
+ in boolean aOverrideCharset,
+ in boolean aEatContinuation);
+
+
+ /**
+ * Given a header parameter, decodes RFC 2047 style encoding (if it's
+ * not obtained from RFC 2231 encoding), converts it to
+ * UTF-8 and returns the result in UTF-8 if an attempt to extract
+ * charset info. from a few different sources succeeds.
+ * Otherwise, returns the input header value (in whatever encoding)
+ * as it is except that RFC 822 (using backslash) quotation is
+ * stripped off.
+ * <p>
+ * For internal use only. The only other place where this needs to be
+ * invoked is <code>mime_decode_filename</code> in
+ * mailnews/mime/src/mimehdrs.cpp defined as
+ * char * mime_decode_filename(char *name, const char *charset,
+ * MimeDisplayOptions *opt)
+ *
+ * @param aParamValue the value of a parameter to decode and convert
+ * @param aCharset charset obtained from RFC 2231 decoding in which
+ * <code>aParamValue</code> is encoded. If null,
+ * indicates that it needs to try RFC 2047, instead.
+ * @param aDefaultCharset MIME charset to use when aCharset is null and
+ * cannot be obtained per RFC 2047 (most likely
+ * because 'bare' string is used.) Besides, it
+ * overrides aCharset/MIME charset obtained from
+ * RFC 2047 if <code>aOverrideCharset</code> is set.
+ * @param aOverrideCharset When set, overrides MIME charset specified
+ * in RFC 2047 style encoding with
+ * <code>aDefaultCharset</code>
+ * @return decoded parameter
+ */
+
+ [noscript]
+ ACString decodeParameter(in ACString aParamValue,
+ in string aCharset,
+ in string aDefaultCharset,
+ in boolean aOverrideCharset);
+};
diff --git a/netwerk/mime/nsIMIMEInfo.idl b/netwerk/mime/nsIMIMEInfo.idl
new file mode 100644
index 0000000000..a7ffcfe513
--- /dev/null
+++ b/netwerk/mime/nsIMIMEInfo.idl
@@ -0,0 +1,369 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+interface nsIFile;
+interface nsIUTF8StringEnumerator;
+interface nsIHandlerApp;
+interface nsIArray;
+interface nsIMutableArray;
+interface nsIInterfaceRequestor;
+webidl BrowsingContext;
+
+typedef long nsHandlerInfoAction;
+
+/**
+ * nsIHandlerInfo gives access to the information about how a given protocol
+ * scheme or MIME-type is handled.
+ */
+[scriptable, uuid(325e56a7-3762-4312-aec7-f1fcf84b4145)]
+interface nsIHandlerInfo : nsISupports {
+ /**
+ * The type of this handler info. For MIME handlers, this is the MIME type.
+ * For protocol handlers, it's the scheme.
+ *
+ * @return String representing the type.
+ */
+ readonly attribute ACString type;
+
+ /**
+ * A human readable description of the handler type
+ */
+ attribute AString description;
+
+ /**
+ * The application the user has said they want associated with this content
+ * type. This is not always guaranteed to be set!!
+ */
+ attribute nsIHandlerApp preferredApplicationHandler;
+
+ /**
+ * Applications that can handle this content type.
+ *
+ * The list will include the preferred handler, if any. Elements of this
+ * array are nsIHandlerApp objects, and this attribute will always reference
+ * an array, whether or not there are any possible handlers. If there are
+ * no possible handlers, the array will contain no elements, so just check
+ * its length (nsIArray::length) to see if there are any possible handlers.
+ */
+ readonly attribute nsIMutableArray possibleApplicationHandlers;
+
+ /**
+ * Indicates whether a default application handler exists,
+ * i.e. whether launchWithFile with action = useSystemDefault is possible
+ * and defaultDescription will contain usable information.
+ */
+ readonly attribute boolean hasDefaultHandler;
+
+ /**
+ * A pretty name description of the associated default application. Only
+ * usable if hasDefaultHandler is true.
+ */
+ readonly attribute AString defaultDescription;
+
+ /**
+ * Launches the application with the specified URI, in a way that
+ * depends on the value of preferredAction. preferredAction must be
+ * useHelperApp or useSystemDefault.
+ *
+ * @note Only the URI scheme is used to determine how to launch. This is
+ * essentially a pass-by-value operation. This means that in the case of
+ * a file: URI, the handler that is registered for file: will be launched
+ * and our code will not make any decision based on the content-type or
+ * extension, though the invoked file: handler is free to do so.
+ *
+ * @param aURI
+ * The URI to launch this application with
+ *
+ * @param aBrowsingContext
+ * The window to parent the dialog against, and, if a web handler
+ * is chosen, it is loaded in this window as well. See
+ * nsIHandlerApp.launchWithURI for more details.
+ *
+ * @throw NS_ERROR_INVALID_ARG if preferredAction is not valid for this
+ * call. Other exceptions may be thrown.
+ */
+ void launchWithURI(in nsIURI aURI,
+ [optional] in BrowsingContext aBrowsingContext);
+
+ /**
+ * preferredAction is how the user specified they would like to handle
+ * this content type: save to disk, use specified helper app, use OS
+ * default handler or handle using navigator; possible value constants
+ * listed below
+ */
+ attribute nsHandlerInfoAction preferredAction;
+
+ const long saveToDisk = 0;
+ /**
+ * Used to indicate that we know nothing about what to do with this. You
+ * could consider this to be not initialized.
+ */
+ const long alwaysAsk = 1;
+ const long useHelperApp = 2;
+ const long handleInternally = 3;
+ const long useSystemDefault = 4;
+
+ /**
+ * alwaysAskBeforeHandling: if true, we should always give the user a
+ * dialog asking how to dispose of this content.
+ */
+ attribute boolean alwaysAskBeforeHandling;
+};
+
+/**
+ * nsIMIMEInfo extends nsIHandlerInfo with a bunch of information specific to
+ * MIME content-types. There is a one-to-many relationship between MIME types
+ * and file extensions. This means that a MIMEInfo object may have multiple
+ * file extensions associated with it. However, the reverse is not true.
+ *
+ * MIMEInfo objects are generally retrieved from the MIME Service
+ * @see nsIMIMEService
+ */
+[scriptable, uuid(1c21acef-c7a1-40c6-9d40-a20480ee53a1)]
+interface nsIMIMEInfo : nsIHandlerInfo {
+ /**
+ * Gives you an array of file types associated with this type.
+ *
+ * @return Number of elements in the array.
+ * @return Array of extensions.
+ */
+ nsIUTF8StringEnumerator getFileExtensions();
+
+ /**
+ * Set File Extensions. Input is a comma delimited list of extensions.
+ */
+ void setFileExtensions(in AUTF8String aExtensions);
+
+ /**
+ * Returns whether or not the given extension is
+ * associated with this MIME info.
+ *
+ * @return TRUE if the association exists.
+ */
+ boolean extensionExists(in AUTF8String aExtension);
+
+ /**
+ * Append a given extension to the set of extensions
+ */
+ void appendExtension(in AUTF8String aExtension);
+
+ /**
+ * Returns the first extension association in
+ * the internal set of extensions.
+ *
+ * @return The first extension.
+ */
+ attribute AUTF8String primaryExtension;
+
+ /**
+ * The MIME type of this MIMEInfo.
+ *
+ * @return String representing the MIME type.
+ *
+ * @deprecated use nsIHandlerInfo::type instead.
+ */
+ readonly attribute ACString MIMEType;
+
+ /**
+ * Returns whether or not these two nsIMIMEInfos are logically
+ * equivalent.
+ *
+ * @returns PR_TRUE if the two are considered equal
+ */
+ boolean equals(in nsIMIMEInfo aMIMEInfo);
+
+ /**
+ * Returns a list of nsILocalHandlerApp objects containing
+ * handlers associated with this mimeinfo. Implemented per
+ * platform using information in this object to generate the
+ * best list. Typically used for an "open with" style user
+ * option.
+ *
+ * @return nsIArray of nsILocalHandlerApp
+ */
+ readonly attribute nsIArray possibleLocalHandlers;
+
+ /**
+ * Launches the application with the specified file, in a way that
+ * depends on the value of preferredAction. preferredAction must be
+ * useHelperApp or useSystemDefault.
+ *
+ * @param aFile The file to launch this application with.
+ *
+ * @throw NS_ERROR_INVALID_ARG if action is not valid for this function.
+ * Other exceptions may be thrown.
+ */
+ void launchWithFile(in nsIFile aFile);
+
+ /**
+ * Check if we ourselves are registered as the OS default for this type.
+ */
+ boolean isCurrentAppOSDefault();
+};
+
+/**
+ * nsIHandlerApp represents an external application that can handle content
+ * of some sort (either a MIME type or a protocol).
+ *
+ * FIXME: now that we've made nsIWebHandlerApp inherit from nsIHandlerApp,
+ * we should also try to make nsIWebContentHandlerInfo inherit from or possibly
+ * be replaced by nsIWebHandlerApp (bug 394710).
+ */
+[scriptable, uuid(8BDF20A4-9170-4548-AF52-78311A44F920)]
+interface nsIHandlerApp : nsISupports {
+
+ /**
+ * Human readable name for the handler
+ */
+ attribute AString name;
+
+ /**
+ * Detailed description for this handler. Suitable for
+ * a tooltip or short informative sentence.
+ */
+ attribute AString detailedDescription;
+
+ /**
+ * Whether or not the given handler app is logically equivalent to the
+ * invokant (i.e. they represent the same app).
+ *
+ * Two apps are the same if they are both either local or web handlers
+ * and their executables/URI templates and command line parameters are
+ * the same.
+ *
+ * @param aHandlerApp the handler app to compare to the invokant
+ *
+ * @returns true if the two are logically equivalent, false otherwise
+ */
+ boolean equals(in nsIHandlerApp aHandlerApp);
+
+ /**
+ * Launches the application with the specified URI.
+ *
+ * @param aURI
+ * The URI to launch this application with
+ *
+ * @param aBrowsingContext
+ *
+ * This represents the docshell to load the handler in and is passed
+ * through to nsIURILoader.openURI. If this parameter is null or
+ * not present, the web handler app implementation will attempt to
+ * find/create a place to load the handler and do so. As of this
+ * writing, it tries to load the web handler in a new window using
+ * nsIBrowserDOMWindow.openURI. In the future, it may attempt to
+ * have a more comprehensive strategy which could include handing
+ * off to the system default browser (bug 394479).
+ */
+ void launchWithURI(in nsIURI aURI,
+ [optional] in BrowsingContext aBrowsingContext);
+
+};
+
+/**
+ * nsILocalHandlerApp is a local OS-level executable
+ */
+[scriptable, uuid(D36B6329-52AE-4f45-80F4-B2536AE5F8B2)]
+interface nsILocalHandlerApp : nsIHandlerApp {
+
+ /**
+ * Pointer to the executable file used to handle content
+ */
+ attribute nsIFile executable;
+
+ /**
+ * Returns the current number of command line parameters.
+ */
+ readonly attribute unsigned long parameterCount;
+
+ /**
+ * Clears the current list of command line parameters.
+ */
+ void clearParameters();
+
+ /**
+ * Appends a command line parameter to the command line
+ * parameter list.
+ *
+ * @param param the parameter to add.
+ */
+ void appendParameter(in AString param);
+
+ /**
+ * Retrieves a specific command line parameter.
+ *
+ * @param param the index of the parameter to return.
+ *
+ * @return the parameter string.
+ *
+ * @throw NS_ERROR_INVALID_ARG if the index is out of range.
+ */
+ AString getParameter(in unsigned long parameterIndex);
+
+ /**
+ * Checks to see if a parameter exists in the command line
+ * parameter list.
+ *
+ * @param param the parameter to search for.
+ *
+ * @return TRUE if the parameter exists in the current list.
+ */
+ boolean parameterExists(in AString param);
+};
+
+/**
+ * nsIWebHandlerApp is a web-based handler, as speced by the WhatWG HTML5
+ * draft. Currently, only GET-based handlers are supported. At some point,
+ * we probably want to work with WhatWG to spec out and implement POST-based
+ * handlers as well.
+ */
+[scriptable, uuid(7521a093-c498-45ce-b462-df7ba0d882f6)]
+interface nsIWebHandlerApp : nsIHandlerApp {
+
+ /**
+ * Template used to construct the URI to GET. Template is expected to have
+ * a %s in it, and the escaped URI to be handled is inserted in place of
+ * that %s, as per the HTML5 spec.
+ */
+ attribute AUTF8String uriTemplate;
+};
+
+/**
+ * nsIDBusHandlerApp represents local applications launched by DBus a message
+ * invoking a method taking a single string argument descibing a URI
+ */
+[scriptable, uuid(1ffc274b-4cbf-4bb5-a635-05ad2cbb6534)]
+interface nsIDBusHandlerApp : nsIHandlerApp {
+
+ /**
+ * Service defines the dbus service that should handle this protocol.
+ * If its not set, NS_ERROR_FAILURE will be returned by LaunchWithURI
+ */
+ attribute AUTF8String service;
+
+ /**
+ * Objpath defines the object path of the dbus service that should handle
+ * this protocol. If its not set, NS_ERROR_FAILURE will be returned
+ * by LaunchWithURI
+ */
+ attribute AUTF8String objectPath;
+
+ /**
+ * DBusInterface defines the interface of the dbus service that should
+ * handle this protocol. If its not set, NS_ERROR_FAILURE will be
+ * returned by LaunchWithURI
+ */
+ attribute AUTF8String dBusInterface;
+
+ /**
+ * Method defines the dbus method that should be invoked to handle this
+ * protocol. If its not set, NS_ERROR_FAILURE will be returned by
+ * LaunchWithURI
+ */
+ attribute AUTF8String method;
+
+};
diff --git a/netwerk/mime/nsIMIMEService.idl b/netwerk/mime/nsIMIMEService.idl
new file mode 100644
index 0000000000..eedcb72949
--- /dev/null
+++ b/netwerk/mime/nsIMIMEService.idl
@@ -0,0 +1,242 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+interface nsIMIMEInfo;
+interface nsIURI;
+interface nsIChannel;
+
+%{C++
+#define NS_MIMESERVICE_CID \
+{ /* 03af31da-3109-11d3-8cd0-0060b0fc14a3 */ \
+ 0x03af31da, \
+ 0x3109, \
+ 0x11d3, \
+ {0x8c, 0xd0, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
+}
+%}
+
+/**
+ * The MIME service is responsible for mapping file extensions to MIME-types
+ * (see RFC 2045). It also provides access to nsIMIMEInfo interfaces and
+ * acts as a general convenience wrapper of nsIMIMEInfo interfaces.
+ *
+ * The MIME service maintains a database with a <b>one</b> MIME type <b>to many</b>
+ * file extensions rule. Adding the same file extension to multiple MIME types
+ * is illegal and behavior is undefined.
+ *
+ * @see nsIMIMEInfo
+ */
+[scriptable, main_process_scriptable_only, uuid(5b3675a1-02db-4f8f-a560-b34736635f47)]
+interface nsIMIMEService : nsISupports {
+ /**
+ * Retrieves an nsIMIMEInfo using both the extension
+ * and the type of a file. The type is given preference
+ * during the lookup. One of aMIMEType and aFileExt
+ * can be an empty string. At least one of aMIMEType and aFileExt
+ * must be nonempty.
+ */
+ nsIMIMEInfo getFromTypeAndExtension(in ACString aMIMEType, in AUTF8String aFileExt);
+
+ /**
+ * Retrieves a ACString representation of the MIME type
+ * associated with this file extension.
+ *
+ * @param A file extension (excluding the dot ('.')).
+ * @return The MIME type, if any.
+ */
+ ACString getTypeFromExtension(in AUTF8String aFileExt);
+
+ /**
+ * Retrieves a ACString representation of the MIME type
+ * associated with this URI. The association is purely
+ * file extension to MIME type based. No attempt to determine
+ * the type via server headers or byte scanning is made.
+ *
+ * @param The URI the user wants MIME info on.
+ * @return The MIME type, if any.
+ */
+ ACString getTypeFromURI(in nsIURI aURI);
+
+ /**
+ * Retrieves a ACString representation of the MIME type
+ * associated with this file extension. Only the default
+ * builtin list is examined. Unless you need a restricted
+ * set use getTypeFromURI.
+ *
+ * @param The URI the user wants MIME info on.
+ * @return The MIME type, if any.
+ */
+ ACString getDefaultTypeFromURI(in nsIURI aURI);
+
+ //
+ ACString getTypeFromFile(in nsIFile aFile);
+
+ /**
+ * Given a Type/Extension combination, returns the default extension
+ * for this type. This may be identical to the passed-in extension.
+ *
+ * @param aMIMEType The Type to get information on. Must not be empty.
+ * @param aFileExt File Extension. Can be empty.
+ */
+ AUTF8String getPrimaryExtension(in ACString aMIMEType, in AUTF8String aFileExt);
+
+ /*
+ * Returns an nsIMIMEInfo for the provided MIME type and extension
+ * obtained from an OS lookup. If no handler is found for the type and
+ * extension, returns a generic nsIMIMEInfo object. The MIME type and
+ * extension can be the empty string. When the type and extension don't
+ * map to the same handler, the semantics/resolution are platform
+ * specific. See the platform implementations for details.
+ *
+ * @param aType The MIME type to get handler information for.
+ * @param aFileExtension The filename extension to use either alone
+ * or with the MIME type to get handler information
+ * for. UTF-8 encoded.
+ * @param [out] aFound Out param indicating whether a MIMEInfo could
+ * be found for the provided type and/or extension.
+ * Set to false when neither extension nor the MIME
+ * type are mapped to a handler.
+ * @return A nsIMIMEInfo object. This function must return
+ * a MIMEInfo object if it can allocate one. The
+ * only justifiable reason for not returning one is
+ * an out-of-memory error.
+ */
+ nsIMIMEInfo getMIMEInfoFromOS(in ACString aType,
+ in ACString aFileExtension,
+ out boolean aFound);
+
+ /**
+ * Default filename validation for getValidFileName and
+ * validateFileNameForSaving where other flags are not true.
+ * That is, the extension is modified to fit the content type,
+ * duplicate whitespace is collapsed, and long filenames are
+ * truncated. A valid content type must be supplied. See the
+ * description of getValidFileName for more details about how
+ * the flags are used.
+ */
+ const long VALIDATE_DEFAULT = 0;
+
+ /**
+ * If true, then the filename is only validated to ensure that it is
+ * acceptable for the file system. If false, then the extension is also
+ * checked to ensure that it is valid for the content type. If the
+ * extension is not valid, the filename is modified to have the proper
+ * extension.
+ */
+ const long VALIDATE_SANITIZE_ONLY = 1;
+
+ /**
+ * Don't collapse strings of duplicate whitespace into a single string.
+ */
+ const long VALIDATE_DONT_COLLAPSE_WHITESPACE = 2;
+
+ /**
+ * Don't truncate long filenames.
+ */
+ const long VALIDATE_DONT_TRUNCATE = 4;
+
+ /**
+ * True to ignore the content type and guess the type from any existing
+ * extension instead. "application/octet-stream" is used as the default
+ * if there is no extension or there is no information available for
+ * the extension.
+ */
+ const long VALIDATE_GUESS_FROM_EXTENSION = 8;
+
+ /**
+ * If the filename is empty, return the empty filename
+ * without modification.
+ */
+ const long VALIDATE_ALLOW_EMPTY = 16;
+
+ /**
+ * Don't apply a default filename if the non-extension portion of the
+ * filename is empty.
+ */
+ const long VALIDATE_NO_DEFAULT_FILENAME = 32;
+
+ /**
+ * When the filename has an invalid extension, force the the filename to
+ * have a valid extension appended to the end of the filename when that
+ * extension would normally be ignored for the given content type. This
+ * primarily is used when saving pages to ensure that the html extension
+ * is applied over any extension that might have been generated from a
+ * page title.
+ */
+ const long VALIDATE_FORCE_APPEND_EXTENSION = 64;
+
+ /**
+ * Generate a valid filename from the channel that can be used to save
+ * the content of the channel to the local disk.
+ *
+ * The filename is determined from the content disposition, the filename
+ * of the uri, or a default filename. The following modifications are
+ * applied:
+ * - If the VALIDATE_SANITIZE_ONLY flag is not specified, then the
+ * extension of the filename is modified to suit the supplied content type.
+ * - Path separators (typically / and \) are replaced by underscores (_)
+ * - Characters that are not valid or would be confusing in filenames are
+ * replaced by spaces (*, :, etc)
+ * - Bidi related marks are replaced by underscores (_)
+ * - Whitespace and periods are removed from the beginning and end.
+ * - Unless VALIDATE_DONT_COLLAPSE_WHITESPACE is specified, multiple
+ * consecutive whitespace characters are collapsed to a single space
+ * character, either ' ' or an ideographic space 0x3000 if present.
+ * - Unless VALIDATE_DONT_TRUNCATE is specified, the filename is truncated
+ * to a maximum length, preserving the extension if possible.
+ * - Some filenames are invalid on certain platforms. These are replaced if
+ * possible.
+ *
+ * If the VALIDATE_NO_DEFAULT_FILENAME flag is not specified, and after the
+ * rules above are applied, the resulting filename is empty, a default
+ * filename is used.
+ *
+ * If the VALIDATE_ALLOW_EMPTY flag is specified, an empty string may be
+ * returned only if the filename could not be determined or was blank.
+ *
+ * If either the VALIDATE_SANITIZE_ONLY or VALIDATE_GUESS_FROM_EXTENSION flags
+ * are specified, then the content type may be empty. Otherwise, the type must
+ * not be empty.
+ *
+ * The aOriginalURI would be specified if the channel is for a local file but
+ * it was originally sourced from a different uri.
+ *
+ * When saving an image, use validateFileNameForSaving instead and
+ * pass the result of imgIRequest::GetFileName() as the filename to
+ * check.
+ *
+ * @param aChannel The channel of the content to save.
+ * @param aType The MIME type to use, which would usually be the
+ * same as the content type of the channel.
+ * @param aOriginalURL the source url of the file, but may be null.
+ * @param aFlags one or more of the flags above.
+ * @returns The resulting filename.
+ */
+ AString getValidFileName(in nsIChannel aChannel,
+ in ACString aType,
+ in nsIURI aOriginalURI,
+ in unsigned long aFlags);
+
+ /**
+ * Similar to getValidFileName, but used when a specific filename needs
+ * to be validated. The filename is modified as needed based on the
+ * content type in the same manner as getValidFileName.
+ *
+ * If the filename came from a uri, it should not be escaped, that is,
+ * any needed unescaping of the filename should happen before calling
+ * this method.
+ *
+ * @param aType The MIME type to use.
+ * @param aFlags one or more of the flags above.
+ * @param aFileName The filename to validate.
+ * @returns The validated filename.
+ */
+ AString validateFileNameForSaving(in AString aFileName,
+ in ACString aType,
+ in unsigned long aFlags);
+};
diff --git a/netwerk/mime/nsMIMEHeaderParamImpl.cpp b/netwerk/mime/nsMIMEHeaderParamImpl.cpp
new file mode 100644
index 0000000000..1053737dba
--- /dev/null
+++ b/netwerk/mime/nsMIMEHeaderParamImpl.cpp
@@ -0,0 +1,1328 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string.h>
+#include "prprf.h"
+#include "prmem.h"
+#include "plbase64.h"
+#include "nsCRT.h"
+#include "nsTArray.h"
+#include "nsEscape.h"
+#include "nsMIMEHeaderParamImpl.h"
+#include "nsNativeCharsetUtils.h"
+#include "mozilla/Encoding.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/Utf8.h"
+
+using mozilla::Encoding;
+using mozilla::IsAscii;
+using mozilla::IsUtf8;
+
+// static functions declared below are moved from mailnews/mime/src/comi18n.cpp
+
+static char* DecodeQ(const char*, uint32_t);
+static bool Is7bitNonAsciiString(const char*, uint32_t);
+static void CopyRawHeader(const char*, uint32_t, const nsACString&,
+ nsACString&);
+static nsresult DecodeRFC2047Str(const char*, const nsACString&, bool,
+ nsACString&);
+static nsresult internalDecodeParameter(const nsACString&, const nsACString&,
+ const nsACString&, bool, bool,
+ nsACString&);
+
+static nsresult ToUTF8(const nsACString& aString, const nsACString& aCharset,
+ bool aAllowSubstitution, nsACString& aResult) {
+ if (aCharset.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const auto* encoding = Encoding::ForLabelNoReplacement(aCharset);
+ if (!encoding) {
+ return NS_ERROR_UCONV_NOCONV;
+ }
+ if (aAllowSubstitution) {
+ nsresult rv = encoding->DecodeWithoutBOMHandling(aString, aResult);
+ if (NS_SUCCEEDED(rv)) {
+ return NS_OK;
+ }
+ return rv;
+ }
+ return encoding->DecodeWithoutBOMHandlingAndWithoutReplacement(aString,
+ aResult);
+}
+
+static nsresult ConvertStringToUTF8(const nsACString& aString,
+ const nsACString& aCharset, bool aSkipCheck,
+ bool aAllowSubstitution,
+ nsACString& aUTF8String) {
+ // return if ASCII only or valid UTF-8 providing that the ASCII/UTF-8
+ // check is requested. It may not be asked for if a caller suspects
+ // that the input is in non-ASCII 7bit charset (ISO-2022-xx, HZ) or
+ // it's in a charset other than UTF-8 that can be mistaken for UTF-8.
+ if (!aSkipCheck && (IsAscii(aString) || IsUtf8(aString))) {
+ aUTF8String = aString;
+ return NS_OK;
+ }
+
+ aUTF8String.Truncate();
+
+ nsresult rv = ToUTF8(aString, aCharset, aAllowSubstitution, aUTF8String);
+
+ // additional protection for cases where check is skipped and the input
+ // is actually in UTF-8 as opposed to aCharset. (i.e. caller's hunch
+ // was wrong.) We don't check ASCIIness assuming there's no charset
+ // incompatible with ASCII (we don't support EBCDIC).
+ if (aSkipCheck && NS_FAILED(rv) && IsUtf8(aString)) {
+ aUTF8String = aString;
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+// XXX The chance of UTF-7 being used in the message header is really
+// low, but in theory it's possible.
+#define IS_7BIT_NON_ASCII_CHARSET(cset) \
+ (!nsCRT::strncasecmp((cset), "ISO-2022", 8) || \
+ !nsCRT::strncasecmp((cset), "HZ-GB", 5) || \
+ !nsCRT::strncasecmp((cset), "UTF-7", 5))
+
+NS_IMPL_ISUPPORTS(nsMIMEHeaderParamImpl, nsIMIMEHeaderParam)
+
+NS_IMETHODIMP
+nsMIMEHeaderParamImpl::GetParameter(const nsACString& aHeaderVal,
+ const char* aParamName,
+ const nsACString& aFallbackCharset,
+ bool aTryLocaleCharset, char** aLang,
+ nsAString& aResult) {
+ return DoGetParameter(aHeaderVal, aParamName, MIME_FIELD_ENCODING,
+ aFallbackCharset, aTryLocaleCharset, aLang, aResult);
+}
+
+NS_IMETHODIMP
+nsMIMEHeaderParamImpl::GetParameterHTTP(const nsACString& aHeaderVal,
+ const char* aParamName,
+ const nsACString& aFallbackCharset,
+ bool aTryLocaleCharset, char** aLang,
+ nsAString& aResult) {
+ return DoGetParameter(aHeaderVal, aParamName, HTTP_FIELD_ENCODING,
+ aFallbackCharset, aTryLocaleCharset, aLang, aResult);
+}
+
+/* static */
+nsresult nsMIMEHeaderParamImpl::GetParameterHTTP(const nsACString& aHeaderVal,
+ const char* aParamName,
+ nsAString& aResult) {
+ return DoGetParameter(aHeaderVal, aParamName, HTTP_FIELD_ENCODING, ""_ns,
+ false, nullptr, aResult);
+}
+
+// XXX : aTryLocaleCharset is not yet effective.
+/* static */
+nsresult nsMIMEHeaderParamImpl::DoGetParameter(
+ const nsACString& aHeaderVal, const char* aParamName,
+ ParamDecoding aDecoding, const nsACString& aFallbackCharset,
+ bool aTryLocaleCharset, char** aLang, nsAString& aResult) {
+ aResult.Truncate();
+ nsresult rv;
+
+ // get parameter (decode RFC 2231/5987 when applicable, as specified by
+ // aDecoding (5987 being a subset of 2231) and return charset.)
+ nsCString med;
+ nsCString charset;
+ rv = DoParameterInternal(PromiseFlatCString(aHeaderVal).get(), aParamName,
+ aDecoding, getter_Copies(charset), aLang,
+ getter_Copies(med));
+ if (NS_FAILED(rv)) return rv;
+
+ // convert to UTF-8 after charset conversion and RFC 2047 decoding
+ // if necessary.
+
+ nsAutoCString str1;
+ rv = internalDecodeParameter(med, charset, ""_ns, false,
+ // was aDecoding == MIME_FIELD_ENCODING
+ // see bug 875615
+ true, str1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!aFallbackCharset.IsEmpty()) {
+ const Encoding* encoding = Encoding::ForLabel(aFallbackCharset);
+ nsAutoCString str2;
+ if (NS_SUCCEEDED(ConvertStringToUTF8(str1, aFallbackCharset, false,
+ encoding != UTF_8_ENCODING, str2))) {
+ CopyUTF8toUTF16(str2, aResult);
+ return NS_OK;
+ }
+ }
+
+ if (IsUtf8(str1)) {
+ CopyUTF8toUTF16(str1, aResult);
+ return NS_OK;
+ }
+
+ if (aTryLocaleCharset && !NS_IsNativeUTF8()) {
+ return NS_CopyNativeToUnicode(str1, aResult);
+ }
+
+ CopyASCIItoUTF16(str1, aResult);
+ return NS_OK;
+}
+
+// remove backslash-encoded sequences from quoted-strings
+// modifies string in place, potentially shortening it
+void RemoveQuotedStringEscapes(char* src) {
+ char* dst = src;
+
+ for (char* c = src; *c; ++c) {
+ if (c[0] == '\\' && c[1]) {
+ // skip backslash if not at end
+ ++c;
+ }
+ *dst++ = *c;
+ }
+ *dst = 0;
+}
+
+// true is character is a hex digit
+bool IsHexDigit(char aChar) {
+ char c = aChar;
+
+ return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
+ (c >= '0' && c <= '9');
+}
+
+// validate that a C String containing %-escapes is syntactically valid
+bool IsValidPercentEscaped(const char* aValue, int32_t len) {
+ for (int32_t i = 0; i < len; i++) {
+ if (aValue[i] == '%') {
+ if (!IsHexDigit(aValue[i + 1]) || !IsHexDigit(aValue[i + 2])) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Support for continuations (RFC 2231, Section 3)
+
+// only a sane number supported
+#define MAX_CONTINUATIONS 999
+
+// part of a continuation
+
+class Continuation {
+ public:
+ Continuation(const char* aValue, uint32_t aLength, bool aNeedsPercentDecoding,
+ bool aWasQuotedString) {
+ value = aValue;
+ length = aLength;
+ needsPercentDecoding = aNeedsPercentDecoding;
+ wasQuotedString = aWasQuotedString;
+ }
+ Continuation() {
+ // empty constructor needed for nsTArray
+ value = nullptr;
+ length = 0;
+ needsPercentDecoding = false;
+ wasQuotedString = false;
+ }
+ ~Continuation() = default;
+
+ const char* value;
+ uint32_t length;
+ bool needsPercentDecoding;
+ bool wasQuotedString;
+};
+
+// combine segments into a single string, returning the allocated string
+// (or nullptr) while emptying the list
+char* combineContinuations(nsTArray<Continuation>& aArray) {
+ // Sanity check
+ if (aArray.Length() == 0) return nullptr;
+
+ // Get an upper bound for the length
+ uint32_t length = 0;
+ for (uint32_t i = 0; i < aArray.Length(); i++) {
+ length += aArray[i].length;
+ }
+
+ // Allocate
+ char* result = (char*)moz_xmalloc(length + 1);
+
+ // Concatenate
+ *result = '\0';
+
+ for (uint32_t i = 0; i < aArray.Length(); i++) {
+ Continuation cont = aArray[i];
+ if (!cont.value) break;
+
+ char* c = result + strlen(result);
+ strncat(result, cont.value, cont.length);
+ if (cont.needsPercentDecoding) {
+ nsUnescape(c);
+ }
+ if (cont.wasQuotedString) {
+ RemoveQuotedStringEscapes(c);
+ }
+ }
+
+ // return null if empty value
+ if (*result == '\0') {
+ free(result);
+ result = nullptr;
+ }
+
+ return result;
+}
+
+// add a continuation, return false on error if segment already has been seen
+bool addContinuation(nsTArray<Continuation>& aArray, uint32_t aIndex,
+ const char* aValue, uint32_t aLength,
+ bool aNeedsPercentDecoding, bool aWasQuotedString) {
+ if (aIndex < aArray.Length() && aArray[aIndex].value) {
+ NS_WARNING("duplicate RC2231 continuation segment #\n");
+ return false;
+ }
+
+ if (aIndex > MAX_CONTINUATIONS) {
+ NS_WARNING("RC2231 continuation segment # exceeds limit\n");
+ return false;
+ }
+
+ if (aNeedsPercentDecoding && aWasQuotedString) {
+ NS_WARNING(
+ "RC2231 continuation segment can't use percent encoding and quoted "
+ "string form at the same time\n");
+ return false;
+ }
+
+ Continuation cont(aValue, aLength, aNeedsPercentDecoding, aWasQuotedString);
+
+ if (aArray.Length() <= aIndex) {
+ aArray.SetLength(aIndex + 1);
+ }
+ aArray[aIndex] = cont;
+
+ return true;
+}
+
+// parse a segment number; return -1 on error
+int32_t parseSegmentNumber(const char* aValue, int32_t aLen) {
+ if (aLen < 1) {
+ NS_WARNING("segment number missing\n");
+ return -1;
+ }
+
+ if (aLen > 1 && aValue[0] == '0') {
+ NS_WARNING("leading '0' not allowed in segment number\n");
+ return -1;
+ }
+
+ int32_t segmentNumber = 0;
+
+ for (int32_t i = 0; i < aLen; i++) {
+ if (!(aValue[i] >= '0' && aValue[i] <= '9')) {
+ NS_WARNING("invalid characters in segment number\n");
+ return -1;
+ }
+
+ segmentNumber *= 10;
+ segmentNumber += aValue[i] - '0';
+ if (segmentNumber > MAX_CONTINUATIONS) {
+ NS_WARNING("Segment number exceeds sane size\n");
+ return -1;
+ }
+ }
+
+ return segmentNumber;
+}
+
+// validate a given octet sequence for compliance with the specified
+// encoding
+bool IsValidOctetSequenceForCharset(const nsACString& aCharset,
+ const char* aOctets) {
+ nsAutoCString tmpRaw;
+ tmpRaw.Assign(aOctets);
+ nsAutoCString tmpDecoded;
+
+ nsresult rv = ConvertStringToUTF8(tmpRaw, aCharset, false, false, tmpDecoded);
+
+ if (rv != NS_OK) {
+ // we can't decode; charset may be unsupported, or the octet sequence
+ // is broken (illegal or incomplete octet sequence contained)
+ NS_WARNING(
+ "RFC2231/5987 parameter value does not decode according to specified "
+ "charset\n");
+ return false;
+ }
+
+ return true;
+}
+
+// moved almost verbatim from mimehdrs.cpp
+// char *
+// MimeHeaders_get_parameter (const char *header_value, const char *parm_name,
+// char **charset, char **language)
+//
+// The format of these header lines is
+// <token> [ ';' <token> '=' <token-or-quoted-string> ]*
+NS_IMETHODIMP
+nsMIMEHeaderParamImpl::GetParameterInternal(const char* aHeaderValue,
+ const char* aParamName,
+ char** aCharset, char** aLang,
+ char** aResult) {
+ return DoParameterInternal(aHeaderValue, aParamName, MIME_FIELD_ENCODING,
+ aCharset, aLang, aResult);
+}
+
+/* static */
+nsresult nsMIMEHeaderParamImpl::DoParameterInternal(
+ const char* aHeaderValue, const char* aParamName, ParamDecoding aDecoding,
+ char** aCharset, char** aLang, char** aResult) {
+ if (!aHeaderValue || !*aHeaderValue || !aResult) return NS_ERROR_INVALID_ARG;
+
+ *aResult = nullptr;
+
+ if (aCharset) *aCharset = nullptr;
+ if (aLang) *aLang = nullptr;
+
+ nsAutoCString charset;
+
+ // change to (aDecoding != HTTP_FIELD_ENCODING) when we want to disable
+ // them for HTTP header fields later on, see bug 776324
+ bool acceptContinuations = true;
+
+ const char* str = aHeaderValue;
+
+ // skip leading white space.
+ for (; *str && nsCRT::IsAsciiSpace(*str); ++str) {
+ ;
+ }
+ const char* start = str;
+
+ // aParamName is empty. return the first (possibly) _unnamed_ 'parameter'
+ // For instance, return 'inline' in the following case:
+ // Content-Disposition: inline; filename=.....
+ if (!aParamName || !*aParamName) {
+ for (; *str && *str != ';' && !nsCRT::IsAsciiSpace(*str); ++str) {
+ ;
+ }
+ if (str == start) return NS_ERROR_FIRST_HEADER_FIELD_COMPONENT_EMPTY;
+
+ *aResult = (char*)moz_xmemdup(start, (str - start) + 1);
+ (*aResult)[str - start] = '\0'; // null-terminate
+ return NS_OK;
+ }
+
+ /* Skip forward to first ';' */
+ for (; *str && *str != ';' && *str != ','; ++str) {
+ ;
+ }
+ if (*str) str++;
+ /* Skip over following whitespace */
+ for (; *str && nsCRT::IsAsciiSpace(*str); ++str) {
+ ;
+ }
+
+ // Some broken http servers just specify parameters
+ // like 'filename' without specifying disposition
+ // method. Rewind to the first non-white-space
+ // character.
+
+ if (!*str) str = start;
+
+ // RFC2231 - The legitimate parm format can be:
+ // A. title=ThisIsTitle
+ // B. title*=us-ascii'en-us'This%20is%20wierd.
+ // C. title*0*=us-ascii'en'This%20is%20wierd.%20We
+ // title*1*=have%20to%20support%20this.
+ // title*2="Else..."
+ // D. title*0="Hey, what you think you are doing?"
+ // title*1="There is no charset and lang info."
+ // RFC5987: only A and B
+
+ // collect results for the different algorithms (plain filename,
+ // RFC5987/2231-encoded filename, + continuations) separately and decide
+ // which to use at the end
+ char* caseAResult = nullptr;
+ char* caseBResult = nullptr;
+ char* caseCDResult = nullptr;
+
+ // collect continuation segments
+ nsTArray<Continuation> segments;
+
+ // our copies of the charset parameter, kept separately as they might
+ // differ for the two formats
+ nsDependentCSubstring charsetB, charsetCD;
+
+ nsDependentCSubstring lang;
+
+ int32_t paramLen = strlen(aParamName);
+
+ while (*str) {
+ // find name/value
+
+ const char* nameStart = str;
+ const char* nameEnd = nullptr;
+ const char* valueStart = nullptr;
+ const char* valueEnd = nullptr;
+ bool isQuotedString = false;
+
+ NS_ASSERTION(!nsCRT::IsAsciiSpace(*str), "should be after whitespace.");
+
+ // Skip forward to the end of this token.
+ for (; *str && !nsCRT::IsAsciiSpace(*str) && *str != '=' && *str != ';';
+ str++) {
+ ;
+ }
+ nameEnd = str;
+
+ int32_t nameLen = nameEnd - nameStart;
+
+ // Skip over whitespace, '=', and whitespace
+ while (nsCRT::IsAsciiSpace(*str)) ++str;
+ if (!*str) {
+ break;
+ }
+ if (*str != '=') {
+ // don't accept parameters without "="
+ goto increment_str;
+ }
+ // Skip over '=' only if it was actually there
+ str++;
+ while (nsCRT::IsAsciiSpace(*str)) ++str;
+
+ if (*str != '"') {
+ // The value is a token, not a quoted string.
+ valueStart = str;
+ for (valueEnd = str; *valueEnd && *valueEnd != ';'; valueEnd++) {
+ ;
+ }
+ // ignore trailing whitespace:
+ while (valueEnd > valueStart && nsCRT::IsAsciiSpace(*(valueEnd - 1))) {
+ valueEnd--;
+ }
+ str = valueEnd;
+ } else {
+ isQuotedString = true;
+
+ ++str;
+ valueStart = str;
+ for (valueEnd = str; *valueEnd; ++valueEnd) {
+ if (*valueEnd == '\\' && *(valueEnd + 1)) {
+ ++valueEnd;
+ } else if (*valueEnd == '"') {
+ break;
+ }
+ }
+ str = valueEnd;
+ // *valueEnd != null means that *valueEnd is quote character.
+ if (*valueEnd) str++;
+ }
+
+ // See if this is the simplest case (case A above),
+ // a 'single' line value with no charset and lang.
+ // If so, copy it and return.
+ if (nameLen == paramLen &&
+ !nsCRT::strncasecmp(nameStart, aParamName, paramLen)) {
+ if (caseAResult) {
+ // we already have one caseA result, ignore subsequent ones
+ goto increment_str;
+ }
+
+ // if the parameter spans across multiple lines we have to strip out the
+ // line continuation -- jht 4/29/98
+ nsAutoCString tempStr(valueStart, valueEnd - valueStart);
+ tempStr.StripCRLF();
+ char* res = ToNewCString(tempStr, mozilla::fallible);
+ NS_ENSURE_TRUE(res, NS_ERROR_OUT_OF_MEMORY);
+
+ if (isQuotedString) RemoveQuotedStringEscapes(res);
+
+ caseAResult = res;
+ // keep going, we may find a RFC 2231/5987 encoded alternative
+ }
+ // case B, C, and D
+ else if (nameLen > paramLen &&
+ !nsCRT::strncasecmp(nameStart, aParamName, paramLen) &&
+ *(nameStart + paramLen) == '*') {
+ // 1st char past '*'
+ const char* cp = nameStart + paramLen + 1;
+
+ // if param name ends in "*" we need do to RFC5987 "ext-value" decoding
+ bool needExtDecoding = *(nameEnd - 1) == '*';
+
+ bool caseB = nameLen == paramLen + 1;
+ bool caseCStart = (*cp == '0') && needExtDecoding;
+
+ // parse the segment number
+ int32_t segmentNumber = -1;
+ if (!caseB) {
+ int32_t segLen = (nameEnd - cp) - (needExtDecoding ? 1 : 0);
+ segmentNumber = parseSegmentNumber(cp, segLen);
+
+ if (segmentNumber == -1) {
+ acceptContinuations = false;
+ goto increment_str;
+ }
+ }
+
+ // CaseB and start of CaseC: requires charset and optional language
+ // in quotes (quotes required even if lang is blank)
+ if (caseB || (caseCStart && acceptContinuations)) {
+ // look for single quotation mark(')
+ const char* sQuote1 = strchr(valueStart, 0x27);
+ const char* sQuote2 = sQuote1 ? strchr(sQuote1 + 1, 0x27) : nullptr;
+
+ // Two single quotation marks must be present even in
+ // absence of charset and lang.
+ if (!sQuote1 || !sQuote2) {
+ NS_WARNING(
+ "Mandatory two single quotes are missing in header parameter\n");
+ }
+
+ const char* charsetStart = nullptr;
+ int32_t charsetLength = 0;
+ const char* langStart = nullptr;
+ int32_t langLength = 0;
+ const char* rawValStart = nullptr;
+ int32_t rawValLength = 0;
+
+ if (sQuote2 && sQuote1) {
+ // both delimiters present: charSet'lang'rawVal
+ rawValStart = sQuote2 + 1;
+ rawValLength = valueEnd - rawValStart;
+
+ langStart = sQuote1 + 1;
+ langLength = sQuote2 - langStart;
+
+ charsetStart = valueStart;
+ charsetLength = sQuote1 - charsetStart;
+ } else if (sQuote1) {
+ // one delimiter; assume charset'rawVal
+ rawValStart = sQuote1 + 1;
+ rawValLength = valueEnd - rawValStart;
+
+ charsetStart = valueStart;
+ charsetLength = sQuote1 - valueStart;
+ } else {
+ // no delimiter: just rawVal
+ rawValStart = valueStart;
+ rawValLength = valueEnd - valueStart;
+ }
+
+ if (langLength != 0) {
+ lang.Assign(langStart, langLength);
+ }
+
+ // keep the charset for later
+ if (caseB) {
+ charsetB.Assign(charsetStart, charsetLength);
+ } else {
+ // if caseCorD
+ charsetCD.Assign(charsetStart, charsetLength);
+ }
+
+ // non-empty value part
+ if (rawValLength > 0) {
+ if (!caseBResult && caseB) {
+ if (!IsValidPercentEscaped(rawValStart, rawValLength)) {
+ goto increment_str;
+ }
+
+ // allocate buffer for the raw value
+ char* tmpResult = (char*)moz_xmemdup(rawValStart, rawValLength + 1);
+ *(tmpResult + rawValLength) = 0;
+
+ nsUnescape(tmpResult);
+ caseBResult = tmpResult;
+ } else {
+ // caseC
+ bool added = addContinuation(segments, 0, rawValStart, rawValLength,
+ needExtDecoding, isQuotedString);
+
+ if (!added) {
+ // continuation not added, stop processing them
+ acceptContinuations = false;
+ }
+ }
+ }
+ } // end of if-block : title*0*= or title*=
+ // caseD: a line of multiline param with no need for unescaping :
+ // title*[0-9]= or 2nd or later lines of a caseC param : title*[1-9]*=
+ else if (acceptContinuations && segmentNumber != -1) {
+ uint32_t valueLength = valueEnd - valueStart;
+
+ bool added =
+ addContinuation(segments, segmentNumber, valueStart, valueLength,
+ needExtDecoding, isQuotedString);
+
+ if (!added) {
+ // continuation not added, stop processing them
+ acceptContinuations = false;
+ }
+ } // end of if-block : title*[0-9]= or title*[1-9]*=
+ }
+
+ // str now points after the end of the value.
+ // skip over whitespace, ';', whitespace.
+ increment_str:
+ while (nsCRT::IsAsciiSpace(*str)) ++str;
+ if (*str == ';') {
+ ++str;
+ } else {
+ // stop processing the header field; either we are done or the
+ // separator was missing
+ break;
+ }
+ while (nsCRT::IsAsciiSpace(*str)) ++str;
+ }
+
+ caseCDResult = combineContinuations(segments);
+
+ if (caseBResult && !charsetB.IsEmpty()) {
+ // check that the 2231/5987 result decodes properly given the
+ // specified character set
+ if (!IsValidOctetSequenceForCharset(charsetB, caseBResult)) {
+ caseBResult = nullptr;
+ }
+ }
+
+ if (caseCDResult && !charsetCD.IsEmpty()) {
+ // check that the 2231/5987 result decodes properly given the
+ // specified character set
+ if (!IsValidOctetSequenceForCharset(charsetCD, caseCDResult)) {
+ caseCDResult = nullptr;
+ }
+ }
+
+ if (caseBResult) {
+ // prefer simple 5987 format over 2231 with continuations
+ *aResult = caseBResult;
+ caseBResult = nullptr;
+ charset.Assign(charsetB);
+ } else if (caseCDResult) {
+ // prefer 2231/5987 with or without continuations over plain format
+ *aResult = caseCDResult;
+ caseCDResult = nullptr;
+ charset.Assign(charsetCD);
+ } else if (caseAResult) {
+ *aResult = caseAResult;
+ caseAResult = nullptr;
+ }
+
+ // free unused stuff
+ free(caseAResult);
+ free(caseBResult);
+ free(caseCDResult);
+
+ // if we have a result
+ if (*aResult) {
+ // then return charset and lang as well
+ if (aLang && !lang.IsEmpty()) {
+ uint32_t len = lang.Length();
+ *aLang = (char*)moz_xmemdup(lang.BeginReading(), len + 1);
+ *(*aLang + len) = 0;
+ }
+ if (aCharset && !charset.IsEmpty()) {
+ uint32_t len = charset.Length();
+ *aCharset = (char*)moz_xmemdup(charset.BeginReading(), len + 1);
+ *(*aCharset + len) = 0;
+ }
+ }
+
+ return *aResult ? NS_OK : NS_ERROR_INVALID_ARG;
+}
+
+nsresult internalDecodeRFC2047Header(const char* aHeaderVal,
+ const nsACString& aDefaultCharset,
+ bool aOverrideCharset,
+ bool aEatContinuations,
+ nsACString& aResult) {
+ aResult.Truncate();
+ if (!aHeaderVal) return NS_ERROR_INVALID_ARG;
+ if (!*aHeaderVal) return NS_OK;
+
+ // If aHeaderVal is RFC 2047 encoded or is not a UTF-8 string but
+ // aDefaultCharset is specified, decodes RFC 2047 encoding and converts
+ // to UTF-8. Otherwise, just strips away CRLF.
+ if (strstr(aHeaderVal, "=?") ||
+ (!aDefaultCharset.IsEmpty() &&
+ (!IsUtf8(nsDependentCString(aHeaderVal)) ||
+ Is7bitNonAsciiString(aHeaderVal, strlen(aHeaderVal))))) {
+ DecodeRFC2047Str(aHeaderVal, aDefaultCharset, aOverrideCharset, aResult);
+ } else if (aEatContinuations &&
+ (strchr(aHeaderVal, '\n') || strchr(aHeaderVal, '\r'))) {
+ aResult = aHeaderVal;
+ } else {
+ aEatContinuations = false;
+ aResult = aHeaderVal;
+ }
+
+ if (aEatContinuations) {
+ nsAutoCString temp(aResult);
+ temp.ReplaceSubstring("\n\t", " ");
+ temp.ReplaceSubstring("\r\t", " ");
+ temp.StripCRLF();
+ aResult = temp;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMIMEHeaderParamImpl::DecodeRFC2047Header(const char* aHeaderVal,
+ const char* aDefaultCharset,
+ bool aOverrideCharset,
+ bool aEatContinuations,
+ nsACString& aResult) {
+ return internalDecodeRFC2047Header(aHeaderVal, nsCString(aDefaultCharset),
+ aOverrideCharset, aEatContinuations,
+ aResult);
+}
+
+// true if the character is allowed in a RFC 5987 value
+// see RFC 5987, Section 3.2.1, "attr-char"
+bool IsRFC5987AttrChar(char aChar) {
+ char c = aChar;
+
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '!' || c == '#' || c == '$' || c == '&' || c == '+' ||
+ c == '-' || c == '.' || c == '^' || c == '_' || c == '`' ||
+ c == '|' || c == '~');
+}
+
+// percent-decode a value
+// returns false on failure
+bool PercentDecode(nsACString& aValue) {
+ char* c = (char*)moz_xmalloc(aValue.Length() + 1);
+
+ strcpy(c, PromiseFlatCString(aValue).get());
+ nsUnescape(c);
+ aValue.Assign(c);
+ free(c);
+
+ return true;
+}
+
+// Decode a parameter value using the encoding defined in RFC 5987
+//
+// charset "'" [ language ] "'" value-chars
+NS_IMETHODIMP
+nsMIMEHeaderParamImpl::DecodeRFC5987Param(const nsACString& aParamVal,
+ nsACString& aLang,
+ nsAString& aResult) {
+ nsAutoCString charset;
+ nsAutoCString language;
+ nsAutoCString value;
+
+ uint32_t delimiters = 0;
+ const nsCString& encoded = PromiseFlatCString(aParamVal);
+ const char* c = encoded.get();
+
+ while (*c) {
+ char tc = *c++;
+
+ if (tc == '\'') {
+ // single quote
+ delimiters++;
+ } else if (((unsigned char)tc) >= 128) {
+ // fail early, not ASCII
+ NS_WARNING("non-US-ASCII character in RFC5987-encoded param");
+ return NS_ERROR_INVALID_ARG;
+ } else {
+ if (delimiters == 0) {
+ // valid characters are checked later implicitly
+ charset.Append(tc);
+ } else if (delimiters == 1) {
+ // no value checking for now
+ language.Append(tc);
+ } else if (delimiters == 2) {
+ if (IsRFC5987AttrChar(tc)) {
+ value.Append(tc);
+ } else if (tc == '%') {
+ if (!IsHexDigit(c[0]) || !IsHexDigit(c[1])) {
+ // we expect two more characters
+ NS_WARNING("broken %-escape in RFC5987-encoded param");
+ return NS_ERROR_INVALID_ARG;
+ }
+ value.Append(tc);
+ // we consume two more
+ value.Append(*c++);
+ value.Append(*c++);
+ } else {
+ // character not allowed here
+ NS_WARNING("invalid character in RFC5987-encoded param");
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+ }
+ }
+
+ if (delimiters != 2) {
+ NS_WARNING("missing delimiters in RFC5987-encoded param");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // abort early for unsupported encodings
+ if (!charset.LowerCaseEqualsLiteral("utf-8")) {
+ NS_WARNING("unsupported charset in RFC5987-encoded param");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // percent-decode
+ if (!PercentDecode(value)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // return the encoding
+ aLang.Assign(language);
+
+ // finally convert octet sequence to UTF-8 and be done
+ nsAutoCString utf8;
+ nsresult rv = ConvertStringToUTF8(value, charset, true, false, utf8);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CopyUTF8toUTF16(utf8, aResult);
+ return NS_OK;
+}
+
+nsresult internalDecodeParameter(const nsACString& aParamValue,
+ const nsACString& aCharset,
+ const nsACString& aDefaultCharset,
+ bool aOverrideCharset, bool aDecode2047,
+ nsACString& aResult) {
+ aResult.Truncate();
+ // If aCharset is given, aParamValue was obtained from RFC2231/5987
+ // encoding and we're pretty sure that it's in aCharset.
+ if (!aCharset.IsEmpty()) {
+ return ConvertStringToUTF8(aParamValue, aCharset, true, true, aResult);
+ }
+
+ const nsCString& param = PromiseFlatCString(aParamValue);
+ nsAutoCString unQuoted;
+ nsACString::const_iterator s, e;
+ param.BeginReading(s);
+ param.EndReading(e);
+
+ // strip '\' when used to quote CR, LF, '"' and '\'
+ for (; s != e; ++s) {
+ if ((*s == '\\')) {
+ if (++s == e) {
+ --s; // '\' is at the end. move back and append '\'.
+ } else if (*s != nsCRT::CR && *s != nsCRT::LF && *s != '"' &&
+ *s != '\\') {
+ --s; // '\' is not foll. by CR,LF,'"','\'. move back and append '\'
+ }
+ // else : skip '\' and append the quoted character.
+ }
+ unQuoted.Append(*s);
+ }
+
+ aResult = unQuoted;
+ nsresult rv = NS_OK;
+
+ if (aDecode2047) {
+ nsAutoCString decoded;
+
+ // Try RFC 2047 encoding, instead.
+ rv = internalDecodeRFC2047Header(unQuoted.get(), aDefaultCharset,
+ aOverrideCharset, true, decoded);
+
+ if (NS_SUCCEEDED(rv) && !decoded.IsEmpty()) aResult = decoded;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMIMEHeaderParamImpl::DecodeParameter(const nsACString& aParamValue,
+ const char* aCharset,
+ const char* aDefaultCharset,
+ bool aOverrideCharset,
+ nsACString& aResult) {
+ return internalDecodeParameter(aParamValue, nsCString(aCharset),
+ nsCString(aDefaultCharset), aOverrideCharset,
+ true, aResult);
+}
+
+#define ISHEXCHAR(c) \
+ ((0x30 <= uint8_t(c) && uint8_t(c) <= 0x39) || \
+ (0x41 <= uint8_t(c) && uint8_t(c) <= 0x46) || \
+ (0x61 <= uint8_t(c) && uint8_t(c) <= 0x66))
+
+// Decode Q encoding (RFC 2047).
+// static
+char* DecodeQ(const char* in, uint32_t length) {
+ char *out, *dest = nullptr;
+
+ out = dest = (char*)calloc(length + 1, sizeof(char));
+ if (dest == nullptr) return nullptr;
+ while (length > 0) {
+ unsigned c = 0;
+ switch (*in) {
+ case '=':
+ // check if |in| in the form of '=hh' where h is [0-9a-fA-F].
+ if (length < 3 || !ISHEXCHAR(in[1]) || !ISHEXCHAR(in[2])) {
+ goto badsyntax;
+ }
+ PR_sscanf(in + 1, "%2X", &c);
+ *out++ = (char)c;
+ in += 3;
+ length -= 3;
+ break;
+
+ case '_':
+ *out++ = ' ';
+ in++;
+ length--;
+ break;
+
+ default:
+ if (*in & 0x80) goto badsyntax;
+ *out++ = *in++;
+ length--;
+ }
+ }
+ *out++ = '\0';
+
+ for (out = dest; *out; ++out) {
+ if (*out == '\t') *out = ' ';
+ }
+
+ return dest;
+
+badsyntax:
+ free(dest);
+ return nullptr;
+}
+
+// check if input is HZ (a 7bit encoding for simplified Chinese : RFC 1842))
+// or has ESC which may be an indication that it's in one of many ISO
+// 2022 7bit encodings (e.g. ISO-2022-JP(-2)/CN : see RFC 1468, 1922, 1554).
+// static
+bool Is7bitNonAsciiString(const char* input, uint32_t len) {
+ int32_t c;
+
+ enum {
+ hz_initial, // No HZ seen yet
+ hz_escaped, // Inside an HZ ~{ escape sequence
+ hz_seen, // Have seen at least one complete HZ sequence
+ hz_notpresent // Have seen something that is not legal HZ
+ } hz_state;
+
+ hz_state = hz_initial;
+ while (len) {
+ c = uint8_t(*input++);
+ len--;
+ if (c & 0x80) return false;
+ if (c == 0x1B) return true;
+ if (c == '~') {
+ switch (hz_state) {
+ case hz_initial:
+ case hz_seen:
+ if (*input == '{') {
+ hz_state = hz_escaped;
+ } else if (*input == '~') {
+ // ~~ is the HZ encoding of ~. Skip over second ~ as well
+ hz_state = hz_seen;
+ input++;
+ len--;
+ } else {
+ hz_state = hz_notpresent;
+ }
+ break;
+
+ case hz_escaped:
+ if (*input == '}') hz_state = hz_seen;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return hz_state == hz_seen;
+}
+
+#define REPLACEMENT_CHAR "\357\277\275" // EF BF BD (UTF-8 encoding of U+FFFD)
+
+// copy 'raw' sequences of octets in aInput to aOutput.
+// If aDefaultCharset is specified, the input is assumed to be in the
+// charset and converted to UTF-8. Otherwise, a blind copy is made.
+// If aDefaultCharset is specified, but the conversion to UTF-8
+// is not successful, each octet is replaced by Unicode replacement
+// chars. *aOutput is advanced by the number of output octets.
+// static
+void CopyRawHeader(const char* aInput, uint32_t aLen,
+ const nsACString& aDefaultCharset, nsACString& aOutput) {
+ int32_t c;
+
+ // If aDefaultCharset is not specified, make a blind copy.
+ if (aDefaultCharset.IsEmpty()) {
+ aOutput.Append(aInput, aLen);
+ return;
+ }
+
+ // Copy as long as it's US-ASCII. An ESC may indicate ISO 2022
+ // A ~ may indicate it is HZ
+ while (aLen && (c = uint8_t(*aInput++)) != 0x1B && c != '~' && !(c & 0x80)) {
+ aOutput.Append(char(c));
+ aLen--;
+ }
+ if (!aLen) {
+ return;
+ }
+ aInput--;
+
+ // skip ASCIIness/UTF8ness test if aInput is supected to be a 7bit non-ascii
+ // string and aDefaultCharset is a 7bit non-ascii charset.
+ bool skipCheck =
+ (c == 0x1B || c == '~') &&
+ IS_7BIT_NON_ASCII_CHARSET(PromiseFlatCString(aDefaultCharset).get());
+
+ // If not UTF-8, treat as default charset
+ nsAutoCString utf8Text;
+ if (NS_SUCCEEDED(ConvertStringToUTF8(Substring(aInput, aInput + aLen),
+ PromiseFlatCString(aDefaultCharset),
+ skipCheck, true, utf8Text))) {
+ aOutput.Append(utf8Text);
+ } else { // replace each octet with Unicode replacement char in UTF-8.
+ for (uint32_t i = 0; i < aLen; i++) {
+ c = uint8_t(*aInput++);
+ if (c & 0x80) {
+ aOutput.Append(REPLACEMENT_CHAR);
+ } else {
+ aOutput.Append(char(c));
+ }
+ }
+ }
+}
+
+nsresult DecodeQOrBase64Str(const char* aEncoded, size_t aLen, char aQOrBase64,
+ const nsACString& aCharset, nsACString& aResult) {
+ char* decodedText;
+ bool b64alloc = false;
+ NS_ASSERTION(aQOrBase64 == 'Q' || aQOrBase64 == 'B', "Should be 'Q' or 'B'");
+ if (aQOrBase64 == 'Q') {
+ decodedText = DecodeQ(aEncoded, aLen);
+ } else if (aQOrBase64 == 'B') {
+ decodedText = PL_Base64Decode(aEncoded, aLen, nullptr);
+ b64alloc = true;
+ } else {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (!decodedText) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsAutoCString utf8Text;
+ // skip ASCIIness/UTF8ness test if aCharset is 7bit non-ascii charset.
+ nsresult rv = ConvertStringToUTF8(
+ nsDependentCString(decodedText), aCharset,
+ IS_7BIT_NON_ASCII_CHARSET(PromiseFlatCString(aCharset).get()), true,
+ utf8Text);
+ if (b64alloc) {
+ PR_Free(decodedText);
+ } else {
+ free(decodedText);
+ }
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aResult.Append(utf8Text);
+
+ return NS_OK;
+}
+
+static const char especials[] = R"(()<>@,;:\"/[]?.=)";
+
+// |decode_mime_part2_str| taken from comi18n.c
+// Decode RFC2047-encoded words in the input and convert the result to UTF-8.
+// If aOverrideCharset is true, charset in RFC2047-encoded words is
+// ignored and aDefaultCharset is assumed, instead. aDefaultCharset
+// is also used to convert raw octets (without RFC 2047 encoding) to UTF-8.
+// static
+nsresult DecodeRFC2047Str(const char* aHeader,
+ const nsACString& aDefaultCharset,
+ bool aOverrideCharset, nsACString& aResult) {
+ const char *p, *q = nullptr, *r;
+ const char* begin; // tracking pointer for where we are in the input buffer
+ int32_t isLastEncodedWord = 0;
+ const char *charsetStart, *charsetEnd;
+ nsAutoCString prevCharset, curCharset;
+ nsAutoCString encodedText;
+ char prevEncoding = '\0', curEncoding;
+ nsresult rv;
+
+ begin = aHeader;
+
+ // To avoid buffer realloc, if possible, set capacity in advance. No
+ // matter what, more than 3x expansion can never happen for all charsets
+ // supported by Mozilla. SCSU/BCSU with the sliding window set to a
+ // non-BMP block may be exceptions, but Mozilla does not support them.
+ // Neither any known mail/news program use them. Even if there's, we're
+ // safe because we don't use a raw *char any more.
+ aResult.SetCapacity(3 * strlen(aHeader));
+
+ while ((p = strstr(begin, "=?")) != nullptr) {
+ if (isLastEncodedWord) {
+ // See if it's all whitespace.
+ for (q = begin; q < p; ++q) {
+ if (!strchr(" \t\r\n", *q)) {
+ break;
+ }
+ }
+ }
+
+ if (!isLastEncodedWord || q < p) {
+ if (!encodedText.IsEmpty()) {
+ rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
+ prevEncoding, prevCharset, aResult);
+ if (NS_FAILED(rv)) {
+ aResult.Append(encodedText);
+ }
+ encodedText.Truncate();
+ prevCharset.Truncate();
+ prevEncoding = '\0';
+ }
+ // copy the part before the encoded-word
+ CopyRawHeader(begin, p - begin, aDefaultCharset, aResult);
+ begin = p;
+ }
+
+ p += 2;
+
+ // Get charset info
+ charsetStart = p;
+ charsetEnd = nullptr;
+ for (q = p; *q != '?'; q++) {
+ if (*q <= ' ' || strchr(especials, *q)) {
+ goto badsyntax;
+ }
+
+ // RFC 2231 section 5
+ if (!charsetEnd && *q == '*') {
+ charsetEnd = q;
+ }
+ }
+ if (!charsetEnd) {
+ charsetEnd = q;
+ }
+
+ q++;
+ curEncoding = nsCRT::ToUpper(*q);
+ if (curEncoding != 'Q' && curEncoding != 'B') goto badsyntax;
+
+ if (q[1] != '?') goto badsyntax;
+
+ // loop-wise, keep going until we hit "?=". the inner check handles the
+ // nul terminator should the string terminate before we hit the right
+ // marker. (And the r[1] will never reach beyond the end of the string
+ // because *r != '?' is true if r is the nul character.)
+ for (r = q + 2; *r != '?' || r[1] != '='; r++) {
+ if (*r < ' ') goto badsyntax;
+ }
+ if (r == q + 2) {
+ // it's empty, skip
+ begin = r + 2;
+ isLastEncodedWord = 1;
+ continue;
+ }
+
+ curCharset.Assign(charsetStart, charsetEnd - charsetStart);
+ // Override charset if requested. Never override labeled UTF-8.
+ // Use default charset instead of UNKNOWN-8BIT
+ if ((aOverrideCharset &&
+ 0 != nsCRT::strcasecmp(curCharset.get(), "UTF-8")) ||
+ (!aDefaultCharset.IsEmpty() &&
+ 0 == nsCRT::strcasecmp(curCharset.get(), "UNKNOWN-8BIT"))) {
+ curCharset = aDefaultCharset;
+ }
+
+ const char* R;
+ R = r;
+ if (curEncoding == 'B') {
+ // bug 227290. ignore an extraneous '=' at the end.
+ // (# of characters in B-encoded part has to be a multiple of 4)
+ int32_t n = r - (q + 2);
+ R -= (n % 4 == 1 && !strncmp(r - 3, "===", 3)) ? 1 : 0;
+ }
+ // Bug 493544. Don't decode the encoded text until it ends
+ if (R[-1] != '=' &&
+ (prevCharset.IsEmpty() ||
+ (curCharset == prevCharset && curEncoding == prevEncoding))) {
+ encodedText.Append(q + 2, R - (q + 2));
+ prevCharset = curCharset;
+ prevEncoding = curEncoding;
+
+ begin = r + 2;
+ isLastEncodedWord = 1;
+ continue;
+ }
+
+ bool bDecoded; // If the current line has been decoded.
+ bDecoded = false;
+ if (!encodedText.IsEmpty()) {
+ if (curCharset == prevCharset && curEncoding == prevEncoding) {
+ encodedText.Append(q + 2, R - (q + 2));
+ bDecoded = true;
+ }
+ rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
+ prevEncoding, prevCharset, aResult);
+ if (NS_FAILED(rv)) {
+ aResult.Append(encodedText);
+ }
+ encodedText.Truncate();
+ prevCharset.Truncate();
+ prevEncoding = '\0';
+ }
+ if (!bDecoded) {
+ rv = DecodeQOrBase64Str(q + 2, R - (q + 2), curEncoding, curCharset,
+ aResult);
+ if (NS_FAILED(rv)) {
+ aResult.Append(encodedText);
+ }
+ }
+
+ begin = r + 2;
+ isLastEncodedWord = 1;
+ continue;
+
+ badsyntax:
+ if (!encodedText.IsEmpty()) {
+ rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
+ prevEncoding, prevCharset, aResult);
+ if (NS_FAILED(rv)) {
+ aResult.Append(encodedText);
+ }
+ encodedText.Truncate();
+ prevCharset.Truncate();
+ }
+ // copy the part before the encoded-word
+ aResult.Append(begin, p - begin);
+ begin = p;
+ isLastEncodedWord = 0;
+ }
+
+ if (!encodedText.IsEmpty()) {
+ rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
+ prevEncoding, prevCharset, aResult);
+ if (NS_FAILED(rv)) {
+ aResult.Append(encodedText);
+ }
+ }
+
+ // put the tail back
+ CopyRawHeader(begin, strlen(begin), aDefaultCharset, aResult);
+
+ nsAutoCString tempStr(aResult);
+ tempStr.ReplaceChar('\t', ' ');
+ aResult = tempStr;
+
+ return NS_OK;
+}
diff --git a/netwerk/mime/nsMIMEHeaderParamImpl.h b/netwerk/mime/nsMIMEHeaderParamImpl.h
new file mode 100644
index 0000000000..9fc14931e1
--- /dev/null
+++ b/netwerk/mime/nsMIMEHeaderParamImpl.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMIMEHeaderParam.h"
+
+#ifndef __nsmimeheaderparamimpl_h___
+# define __nsmimeheaderparamimpl_h___
+class nsMIMEHeaderParamImpl : public nsIMIMEHeaderParam {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMIMEHEADERPARAM
+
+ nsMIMEHeaderParamImpl() = default;
+
+ /**
+ * Identical to calling
+ * GetParameterHTTP(aHeaderVal, aParameterName, ""_ns, false,
+ * nullptr, aResult) See nsIMIMEHeaderParam.idl for more information.
+ */
+ static nsresult GetParameterHTTP(const nsACString& aHeaderVal,
+ const char* aParamName, nsAString& aResult);
+
+ private:
+ virtual ~nsMIMEHeaderParamImpl() = default;
+ enum ParamDecoding { MIME_FIELD_ENCODING = 1, HTTP_FIELD_ENCODING };
+
+ static nsresult DoGetParameter(const nsACString& aHeaderVal,
+ const char* aParamName,
+ ParamDecoding aDecoding,
+ const nsACString& aFallbackCharset,
+ bool aTryLocaleCharset, char** aLang,
+ nsAString& aResult);
+
+ static nsresult DoParameterInternal(const char* aHeaderValue,
+ const char* aParamName,
+ ParamDecoding aDecoding, char** aCharset,
+ char** aLang, char** aResult);
+};
+
+#endif
diff --git a/netwerk/mime/nsMimeTypes.h b/netwerk/mime/nsMimeTypes.h
new file mode 100644
index 0000000000..c327009c63
--- /dev/null
+++ b/netwerk/mime/nsMimeTypes.h
@@ -0,0 +1,279 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This interface allows any module to access the encoder/decoder
+ * routines for RFC822 headers. This will allow any mail/news module
+ * to call on these routines.
+ */
+#ifndef nsMimeTypes_h_
+#define nsMimeTypes_h_
+
+/* Defines for various MIME content-types and encodings.
+ Whenever you type in a content-type, you should use one of these defines
+ instead, to help catch typos, and make central management of them easier.
+ */
+
+#define ANY_WILDCARD "*/*"
+#define AUDIO_WILDCARD "audio/*"
+#define IMAGE_WILDCARD "image/*"
+
+#define APPLICATION_APPLEFILE "application/applefile"
+#define APPLICATION_BINHEX "application/mac-binhex40"
+#define APPLICATION_MACBINARY "application/x-macbinary"
+#define APPLICATION_COMPRESS "application/x-compress"
+#define APPLICATION_COMPRESS2 "application/compress"
+#define APPLICATION_FORTEZZA_CKL "application/x-fortezza-ckl"
+#define APPLICATION_FORTEZZA_KRL "application/x-fortezza-krl"
+#define APPLICATION_GZIP "application/x-gzip"
+#define APPLICATION_GZIP2 "application/gzip"
+#define APPLICATION_GZIP3 "application/x-gunzip"
+#define APPLICATION_BROTLI "application/brotli"
+#define APPLICATION_ZIP "application/zip"
+#define APPLICATION_HTTP_INDEX_FORMAT "application/http-index-format"
+#define APPLICATION_ECMASCRIPT "application/ecmascript"
+#define APPLICATION_JAVASCRIPT "application/javascript"
+#define APPLICATION_XJAVASCRIPT "application/x-javascript"
+#define APPLICATION_JSON "application/json"
+#define APPLICATION_NETSCAPE_REVOCATION "application/x-netscape-revocation"
+#define APPLICATION_NS_PROXY_AUTOCONFIG "application/x-ns-proxy-autoconfig"
+#define APPLICATION_NS_JAVASCRIPT_AUTOCONFIG "application/x-javascript-config"
+#define APPLICATION_OCTET_STREAM "application/octet-stream"
+#define APPLICATION_PGP "application/pgp"
+#define APPLICATION_PGP2 "application/x-pgp-message"
+#define APPLICATION_POSTSCRIPT "application/postscript"
+#define APPLICATION_PDF "application/pdf"
+#define APPLICATION_PRE_ENCRYPTED "application/pre-encrypted"
+#define APPLICATION_RDF "application/rdf+xml"
+#define APPLICATION_UUENCODE "application/x-uuencode"
+#define APPLICATION_UUENCODE2 "application/x-uue"
+#define APPLICATION_UUENCODE3 "application/uuencode"
+#define APPLICATION_UUENCODE4 "application/uue"
+#define APPLICATION_X509_CA_CERT "application/x-x509-ca-cert"
+#define APPLICATION_X509_SERVER_CERT "application/x-x509-server-cert"
+#define APPLICATION_X509_EMAIL_CERT "application/x-x509-email-cert"
+#define APPLICATION_X509_USER_CERT "application/x-x509-user-cert"
+#define APPLICATION_X509_CRL "application/x-pkcs7-crl"
+#define APPLICATION_XPKCS7_MIME "application/x-pkcs7-mime"
+#define APPLICATION_PKCS7_MIME "application/pkcs7-mime"
+#define APPLICATION_XPKCS7_SIGNATURE "application/x-pkcs7-signature"
+#define APPLICATION_PKCS7_SIGNATURE "application/pkcs7-signature"
+#define APPLICATION_WWW_FORM_URLENCODED "application/x-www-form-urlencoded"
+#define APPLICATION_OLEOBJECT "application/oleobject"
+#define APPLICATION_OLEOBJECT2 "application/x-oleobject"
+#define APPLICATION_JAVAARCHIVE "application/java-archive"
+#define APPLICATION_MARIMBA "application/marimba"
+#define APPLICATION_WEB_MANIFEST "application/manifest+json"
+#define APPLICATION_XMARIMBA "application/x-marimba"
+#define APPLICATION_XPINSTALL "application/x-xpinstall"
+#define APPLICATION_XML "application/xml"
+#define APPLICATION_XHTML_XML "application/xhtml+xml"
+#define APPLICATION_XSLT_XML "application/xslt+xml"
+#define APPLICATION_MATHML_XML "application/mathml+xml"
+#define APPLICATION_RDF_XML "application/rdf+xml"
+#define APPLICATION_WAPXHTML_XML "application/vnd.wap.xhtml+xml"
+#define APPLICATION_PACKAGE "application/package"
+#define APPLICATION_WASM "application/wasm"
+#define APPLICATION_MSEXCEL "application/msexcel"
+#define APPLICATION_MSPPT "application/mspowerpoint"
+#define APPLICATION_MSWORD "application/msword"
+#define APPLICATION_MSWORD_TEMPLATE "application/msword-template"
+#define APPLICATION_VND_CES_QUICKPOINT "application/vnd.ces-quickpoint"
+#define APPLICATION_VND_CES_QUICKSHEET "application/vnd.ces-quicksheet"
+#define APPLICATION_VND_CES_QUICKWORD "application/vnd.ces-quickword"
+#define APPLICATION_VND_MS_EXCEL "application/vnd.ms-excel"
+#define APPLICATION_VND_MS_EXCEL2 \
+ "application/vnd.ms-excel.sheet.macroenabled.12"
+#define APPLICATION_VND_MS_PPT "application/vnd.ms-powerpoint"
+#define APPLICATION_VND_MS_PPT2 \
+ "application/vnd.ms-powerpoint.presentation.macroenabled.12"
+#define APPLICATION_VND_MS_WORD "application/vnd.ms-word"
+#define APPLICATION_VND_MS_WORD2 "application/vnd.ms-word.document.12"
+#define APPLICATION_VND_MS_WORD3 \
+ "application/vnd.ms-word.document.macroenabled.12"
+#define APPLICATION_VND_MSWORD "application/vnd.msword"
+#define APPLICATION_VND_PRESENTATIONML_PRESENTATION \
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation"
+#define APPLICATION_VND_PRESENTATIONML_TEMPLATE \
+ "application/vnd.openxmlformats-officedocument.presentationml.template"
+#define APPLICATION_VND_SPREADSHEETML_SHEET \
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+#define APPLICATION_VND_SPREADSHEETML_TEMPLATE \
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.template"
+#define APPLICATION_VND_WORDPROCESSINGML_DOCUMENT \
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
+#define APPLICATION_VND_WORDPROCESSINGML_TEMPLATE \
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template"
+#define APPLICATION_VND_PRESENTATION_OPENXML \
+ "application/vnd.presentation-openxml"
+#define APPLICATION_VND_PRESENTATION_OPENXMLM \
+ "application/vnd.presentation-openxmlm"
+#define APPLICATION_VND_SPREADSHEET_OPENXML \
+ "application/vnd.spreadsheet-openxml"
+#define APPLICATION_VND_WORDPROSSING_OPENXML \
+ "application/vnd.wordprocessing-openxml"
+#define APPLICATION_XPROTOBUF "application/x-protobuf"
+#define APPLICATION_XPROTOBUFFER "application/x-protobuffer"
+
+#define AUDIO_BASIC "audio/basic"
+#define AUDIO_OGG "audio/ogg"
+#define AUDIO_WAV "audio/x-wav"
+#define AUDIO_WEBM "audio/webm"
+#define AUDIO_MP3 "audio/mpeg"
+#define AUDIO_MP4 "audio/mp4"
+#define AUDIO_AMR "audio/amr"
+#define AUDIO_FLAC "audio/flac"
+#define AUDIO_3GPP "audio/3gpp"
+#define AUDIO_3GPP2 "audio/3gpp2"
+#define AUDIO_MIDI "audio/x-midi"
+#define AUDIO_MATROSKA "audio/x-matroska"
+#define AUDIO_AAC "audio/aac"
+#define AUDIO_MPEG_TS "audio/mp2t"
+#define AUDIO_MPEG_URL "audio/mpegurl"
+
+#define BINARY_OCTET_STREAM "binary/octet-stream"
+
+#define IMAGE_GIF "image/gif"
+#define IMAGE_JPEG "image/jpeg"
+#define IMAGE_JPG "image/jpg"
+#define IMAGE_PJPEG "image/pjpeg"
+#define IMAGE_PNG "image/png"
+#define IMAGE_APNG "image/apng"
+#define IMAGE_X_PNG "image/x-png"
+#define IMAGE_PPM "image/x-portable-pixmap"
+#define IMAGE_XBM "image/x-xbitmap"
+#define IMAGE_XBM2 "image/x-xbm"
+#define IMAGE_XBM3 "image/xbm"
+#define IMAGE_ART "image/x-jg"
+#define IMAGE_TIFF "image/tiff"
+#define IMAGE_BMP "image/bmp"
+#define IMAGE_BMP_MS "image/x-ms-bmp"
+// This is used internally to represent Windows clipboard BMPs which remove
+// part of the header.
+#define IMAGE_BMP_MS_CLIPBOARD "image/x-ms-clipboard-bmp"
+#define IMAGE_ICO "image/x-icon"
+#define IMAGE_ICO_MS "image/vnd.microsoft.icon"
+#define IMAGE_ICON_MS "image/icon"
+#define IMAGE_MNG "video/x-mng"
+#define IMAGE_JNG "image/x-jng"
+#define IMAGE_SVG_XML "image/svg+xml"
+#define IMAGE_WEBP "image/webp"
+#define IMAGE_AVIF "image/avif"
+#define IMAGE_JXL "image/jxl"
+
+#define MESSAGE_EXTERNAL_BODY "message/external-body"
+#define MESSAGE_NEWS "message/news"
+#define MESSAGE_RFC822 "message/rfc822"
+
+#define MULTIPART_ALTERNATIVE "multipart/alternative"
+#define MULTIPART_APPLEDOUBLE "multipart/appledouble"
+#define MULTIPART_DIGEST "multipart/digest"
+#define MULTIPART_FORM_DATA "multipart/form-data"
+#define MULTIPART_HEADER_SET "multipart/header-set"
+#define MULTIPART_MIXED "multipart/mixed"
+#define MULTIPART_PARALLEL "multipart/parallel"
+#define MULTIPART_SIGNED "multipart/signed"
+#define MULTIPART_RELATED "multipart/related"
+#define MULTIPART_MIXED_REPLACE "multipart/x-mixed-replace"
+#define MULTIPART_BYTERANGES "multipart/byteranges"
+
+#define SUN_ATTACHMENT "x-sun-attachment"
+
+#define TEXT_ENRICHED "text/enriched"
+#define TEXT_CALENDAR "text/calendar"
+#define TEXT_HTML "text/html"
+#define TEXT_MDL "text/mdl"
+#define TEXT_PLAIN "text/plain"
+#define TEXT_RICHTEXT "text/richtext"
+#define TEXT_VCARD "text/vcard"
+#define TEXT_CSS "text/css"
+#define TEXT_JSSS "text/jsss"
+#define TEXT_JSON "text/json"
+#define TEXT_XML "text/xml"
+#define TEXT_RDF "text/rdf"
+#define TEXT_VTT "text/vtt"
+#define TEXT_ECMASCRIPT "text/ecmascript"
+#define TEXT_JAVASCRIPT "text/javascript"
+#define TEXT_XSL "text/xsl"
+#define TEXT_EVENT_STREAM "text/event-stream"
+#define TEXT_CACHE_MANIFEST "text/cache-manifest"
+#define TEXT_CSV "text/csv"
+
+#define VIDEO_MPEG "video/mpeg"
+#define VIDEO_MP4 "video/mp4"
+#define VIDEO_QUICKTIME "video/quicktime"
+#define VIDEO_RAW "video/x-raw-yuv"
+#define VIDEO_OGG "video/ogg"
+#define VIDEO_WEBM "video/webm"
+#define VIDEO_3GPP "video/3gpp"
+#define VIDEO_3GPP2 "video/3gpp2"
+#define VIDEO_MPEG_TS "video/mp2t"
+#define VIDEO_AVI "video/avi"
+#define VIDEO_MATROSKA "video/x-matroska"
+#define APPLICATION_OGG "application/ogg"
+#define APPLICATION_MPEGURL "application/vnd.apple.mpegurl"
+
+/* x-uuencode-apple-single. QuickMail made me do this. */
+#define UUENCODE_APPLE_SINGLE "x-uuencode-apple-single"
+
+/* The standard MIME message-content-encoding values:
+ */
+#define ENCODING_7BIT "7bit"
+#define ENCODING_8BIT "8bit"
+#define ENCODING_BINARY "binary"
+#define ENCODING_BASE64 "base64"
+#define ENCODING_QUOTED_PRINTABLE "quoted-printable"
+
+/* Some nonstandard encodings. Note that the names are TOTALLY RANDOM,
+ and code that looks for these in network-provided data must look for
+ all the possibilities.
+ */
+#define ENCODING_COMPRESS "x-compress"
+#define ENCODING_COMPRESS2 "compress"
+#define ENCODING_ZLIB "x-zlib"
+#define ENCODING_ZLIB2 "zlib"
+#define ENCODING_GZIP "x-gzip"
+#define ENCODING_GZIP2 "gzip"
+#define ENCODING_DEFLATE "x-deflate"
+#define ENCODING_DEFLATE2 "deflate"
+#define ENCODING_UUENCODE "x-uuencode"
+#define ENCODING_UUENCODE2 "x-uue"
+#define ENCODING_UUENCODE3 "uuencode"
+#define ENCODING_UUENCODE4 "uue"
+#define ENCODING_YENCODE "x-yencode"
+
+/* Some names of parameters that various MIME headers include.
+ */
+#define PARAM_PROTOCOL "protocol"
+#define PARAM_MICALG "micalg"
+#define PARAM_MICALG_MD2 "rsa-md2"
+#define PARAM_MICALG_MD5 "rsa-md5"
+#define PARAM_MICALG_MD5_2 "md5"
+#define PARAM_MICALG_SHA1 "sha1"
+#define PARAM_MICALG_SHA1_2 "sha-1"
+#define PARAM_MICALG_SHA1_3 "rsa-sha1"
+#define PARAM_MICALG_SHA1_4 "rsa-sha-1"
+#define PARAM_MICALG_SHA1_5 "rsa-sha"
+#define PARAM_MICALG_SHA256 "sha-256"
+#define PARAM_MICALG_SHA256_2 "sha256"
+#define PARAM_MICALG_SHA256_3 "2.16.840.1.101.3.4.2.1"
+#define PARAM_MICALG_SHA384 "sha-384"
+#define PARAM_MICALG_SHA384_2 "sha384"
+#define PARAM_MICALG_SHA384_3 "2.16.840.1.101.3.4.2.2"
+#define PARAM_MICALG_SHA512 "sha-512"
+#define PARAM_MICALG_SHA512_2 "sha512"
+#define PARAM_MICALG_SHA512_3 "2.16.840.1.101.3.4.2.3"
+#define PARAM_X_MAC_CREATOR "x-mac-creator"
+#define PARAM_X_MAC_TYPE "x-mac-type"
+#define PARAM_FORMAT "format"
+
+#define UNKNOWN_CONTENT_TYPE "application/x-unknown-content-type"
+#define APPLICATION_GUESS_FROM_EXT "application/x-vnd.mozilla.guess-from-ext"
+#define VIEWSOURCE_CONTENT_TYPE "application/x-view-source"
+
+#define APPLICATION_DIRECTORY \
+ "application/directory" /* text/x-vcard is synonym */
+
+#endif /* nsMimeTypes_h_ */