1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsSecurityHeaderParser.h"
6 #include "mozilla/Logging.h"
8 // The character classes in this file are informed by [RFC2616], Section 2.2.
9 // signed char is a signed data type one byte (8 bits) wide, so its value can
10 // never be greater than 127. The following implicitly makes use of this.
12 // A token is one or more CHAR except CTLs or separators.
13 // A CHAR is any US-ASCII character (octets 0 - 127).
14 // A CTL is any US-ASCII control character (octets 0 - 31) and DEL (127).
15 // A separator is one of ()<>@,;:\"/[]?={} as well as space and
16 // horizontal-tab (32 and 9, respectively).
17 // So, this returns true if chr is any octet 33-126 except ()<>@,;:\"/[]?={}
18 bool IsTokenSymbol(signed char chr
) {
19 if (chr
< 33 || chr
== 127 || chr
== '(' || chr
== ')' || chr
== '<' ||
20 chr
== '>' || chr
== '@' || chr
== ',' || chr
== ';' || chr
== ':' ||
21 chr
== '"' || chr
== '/' || chr
== '[' || chr
== ']' || chr
== '?' ||
22 chr
== '=' || chr
== '{' || chr
== '}' || chr
== '\\') {
28 // A quoted-string consists of a quote (") followed by any amount of
29 // qdtext or quoted-pair, followed by a quote.
30 // qdtext is any TEXT except a quote.
31 // TEXT is any 8-bit octet except CTLs, but including LWS.
32 // quoted-pair is a backslash (\) followed by a CHAR.
33 // So, it turns out, \ can't really be a qdtext symbol for our purposes.
34 // This returns true if chr is any octet 9,10,13,32-126 except <"> or "\"
35 bool IsQuotedTextSymbol(signed char chr
) {
36 return ((chr
>= 32 && chr
!= '"' && chr
!= '\\' && chr
!= 127) ||
37 chr
== 0x9 || chr
== 0xa || chr
== 0xd);
40 // The octet following the "\" in a quoted pair can be anything 0-127.
41 bool IsQuotedPairSymbol(signed char chr
) { return (chr
>= 0); }
43 static mozilla::LazyLogModule
sSHParserLog("nsSecurityHeaderParser");
45 #define SHPARSERLOG(args) MOZ_LOG(sSHParserLog, mozilla::LogLevel::Debug, args)
47 nsSecurityHeaderParser::nsSecurityHeaderParser(const nsCString
& aHeader
)
48 : mCursor(aHeader
.get()), mDirective(nullptr), mError(false) {}
50 nsSecurityHeaderParser::~nsSecurityHeaderParser() {
51 nsSecurityHeaderDirective
* directive
;
52 while ((directive
= mDirectives
.popFirst())) {
57 mozilla::LinkedList
<nsSecurityHeaderDirective
>*
58 nsSecurityHeaderParser::GetDirectives() {
62 nsresult
nsSecurityHeaderParser::Parse() {
63 MOZ_ASSERT(mDirectives
.isEmpty());
64 SHPARSERLOG(("trying to parse '%s'", mCursor
));
68 // if we didn't consume the entire input, we were unable to parse it => error
69 if (mError
|| *mCursor
) {
70 return NS_ERROR_FAILURE
;
76 bool nsSecurityHeaderParser::Accept(char aChr
) {
77 if (*mCursor
== aChr
) {
85 bool nsSecurityHeaderParser::Accept(bool (*aClassifier
)(signed char)) {
86 if (aClassifier(*mCursor
)) {
94 void nsSecurityHeaderParser::Expect(char aChr
) {
95 if (*mCursor
!= aChr
) {
102 void nsSecurityHeaderParser::Advance() {
103 // Technically, 0 is valid in quoted-pair, but we were handed a
104 // null-terminated const char *, so this doesn't handle that.
106 mOutput
.Append(*mCursor
);
113 void nsSecurityHeaderParser::Header() {
115 while (Accept(';')) {
120 void nsSecurityHeaderParser::Directive() {
121 mDirective
= new nsSecurityHeaderDirective();
130 mDirectives
.insertBack(mDirective
);
131 if (mDirective
->mValue
.isSome()) {
132 SHPARSERLOG(("read directive name '%s', value '%s'",
133 mDirective
->mName
.Data(), mDirective
->mValue
->Data()));
136 ("read valueless directive name '%s'", mDirective
->mName
.Data()));
140 void nsSecurityHeaderParser::DirectiveName() {
143 mDirective
->mName
.Assign(mOutput
);
146 void nsSecurityHeaderParser::DirectiveValue() {
148 mDirective
->mValue
.emplace();
149 if (Accept(IsTokenSymbol
)) {
151 mDirective
->mValue
->Assign(mOutput
);
152 } else if (Accept('"')) {
153 // Accept advances the cursor if successful, which appends a character to
154 // mOutput. The " is not part of what we want to capture, so truncate
158 mDirective
->mValue
->Assign(mOutput
);
163 void nsSecurityHeaderParser::Token() { while (Accept(IsTokenSymbol
)); }
165 void nsSecurityHeaderParser::QuotedString() {
167 if (Accept(IsQuotedTextSymbol
)) {
169 } else if (Accept('\\')) {
177 void nsSecurityHeaderParser::QuotedText() {
178 while (Accept(IsQuotedTextSymbol
));
181 void nsSecurityHeaderParser::QuotedPair() { Accept(IsQuotedPairSymbol
); }
183 void nsSecurityHeaderParser::LWSMultiple() {
187 } else if (Accept(' ') || Accept('\t')) {
195 void nsSecurityHeaderParser::LWSCRLF() {
197 if (!(Accept(' ') || Accept('\t'))) {
203 void nsSecurityHeaderParser::LWS() {
204 // Note that becaue of how we're called, we don't have to check for
205 // the mandatory presense of at least one of SP or HT.
206 while (Accept(' ') || Accept('\t'));