Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 "prlog.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
19 IsTokenSymbol(signed char chr) {
20 if (chr < 33 || chr == 127 ||
21 chr == '(' || chr == ')' || chr == '<' || chr == '>' ||
22 chr == '@' || chr == ',' || chr == ';' || chr == ':' ||
23 chr == '"' || chr == '/' || chr == '[' || chr == ']' ||
24 chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') {
25 return false;
26 }
27 return true;
28 }
30 // A quoted-string consists of a quote (") followed by any amount of
31 // qdtext or quoted-pair, followed by a quote.
32 // qdtext is any TEXT except a quote.
33 // TEXT is any 8-bit octet except CTLs, but including LWS.
34 // quoted-pair is a backslash (\) followed by a CHAR.
35 // So, it turns out, \ can't really be a qdtext symbol for our purposes.
36 // This returns true if chr is any octet 9,10,13,32-126 except <"> or "\"
37 bool
38 IsQuotedTextSymbol(signed char chr) {
39 return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) ||
40 chr == 0x9 || chr == 0xa || chr == 0xd);
41 }
43 // The octet following the "\" in a quoted pair can be anything 0-127.
44 bool
45 IsQuotedPairSymbol(signed char chr) {
46 return (chr >= 0);
47 }
49 #if defined(PR_LOGGING)
50 static PRLogModuleInfo *
51 GetSHParserLog()
52 {
53 static PRLogModuleInfo *sSHParserLog;
54 if (!sSHParserLog) {
55 sSHParserLog = PR_NewLogModule("nsSecurityHeaderParser");
56 }
57 return sSHParserLog;
58 }
59 #endif
61 #define SHPARSERLOG(args) PR_LOG(GetSHParserLog(), PR_LOG_DEBUG, args)
63 nsSecurityHeaderParser::nsSecurityHeaderParser(const char *aHeader)
64 : mCursor(aHeader)
65 , mError(false)
66 {
67 }
69 nsSecurityHeaderParser::~nsSecurityHeaderParser() {
70 nsSecurityHeaderDirective *directive;
71 while ((directive = mDirectives.popFirst())) {
72 delete directive;
73 }
74 }
76 mozilla::LinkedList<nsSecurityHeaderDirective> *
77 nsSecurityHeaderParser::GetDirectives() {
78 return &mDirectives;
79 }
81 nsresult
82 nsSecurityHeaderParser::Parse() {
83 MOZ_ASSERT(mDirectives.isEmpty());
84 SHPARSERLOG(("trying to parse '%s'", mCursor));
86 Header();
88 // if we didn't consume the entire input, we were unable to parse it => error
89 if (mError || *mCursor) {
90 return NS_ERROR_FAILURE;
91 } else {
92 return NS_OK;
93 }
94 }
96 bool
97 nsSecurityHeaderParser::Accept(char aChr)
98 {
99 if (*mCursor == aChr) {
100 Advance();
101 return true;
102 }
104 return false;
105 }
107 bool
108 nsSecurityHeaderParser::Accept(bool (*aClassifier) (signed char))
109 {
110 if (aClassifier(*mCursor)) {
111 Advance();
112 return true;
113 }
115 return false;
116 }
118 void
119 nsSecurityHeaderParser::Expect(char aChr)
120 {
121 if (*mCursor != aChr) {
122 mError = true;
123 } else {
124 Advance();
125 }
126 }
128 void
129 nsSecurityHeaderParser::Advance()
130 {
131 // Technically, 0 is valid in quoted-pair, but we were handed a
132 // null-terminated const char *, so this doesn't handle that.
133 if (*mCursor) {
134 mOutput.Append(*mCursor);
135 mCursor++;
136 } else {
137 mError = true;
138 }
139 }
141 void
142 nsSecurityHeaderParser::Header()
143 {
144 Directive();
145 while (Accept(';')) {
146 Directive();
147 }
148 }
150 void
151 nsSecurityHeaderParser::Directive()
152 {
153 mDirective = new nsSecurityHeaderDirective();
154 LWSMultiple();
155 DirectiveName();
156 LWSMultiple();
157 if (Accept('=')) {
158 LWSMultiple();
159 DirectiveValue();
160 LWSMultiple();
161 }
162 mDirectives.insertBack(mDirective);
163 SHPARSERLOG(("read directive name '%s', value '%s'",
164 mDirective->mName.Data(), mDirective->mValue.Data()));
165 }
167 void
168 nsSecurityHeaderParser::DirectiveName()
169 {
170 mOutput.Truncate(0);
171 Token();
172 mDirective->mName.Assign(mOutput);
173 }
175 void
176 nsSecurityHeaderParser::DirectiveValue()
177 {
178 mOutput.Truncate(0);
179 if (Accept(IsTokenSymbol)) {
180 Token();
181 mDirective->mValue.Assign(mOutput);
182 } else if (Accept('"')) {
183 // Accept advances the cursor if successful, which appends a character to
184 // mOutput. The " is not part of what we want to capture, so truncate
185 // mOutput again.
186 mOutput.Truncate(0);
187 QuotedString();
188 mDirective->mValue.Assign(mOutput);
189 Expect('"');
190 }
191 }
193 void
194 nsSecurityHeaderParser::Token()
195 {
196 while (Accept(IsTokenSymbol));
197 }
199 void
200 nsSecurityHeaderParser::QuotedString()
201 {
202 while (true) {
203 if (Accept(IsQuotedTextSymbol)) {
204 QuotedText();
205 } else if (Accept('\\')) {
206 QuotedPair();
207 } else {
208 break;
209 }
210 }
211 }
213 void
214 nsSecurityHeaderParser::QuotedText()
215 {
216 while (Accept(IsQuotedTextSymbol));
217 }
219 void
220 nsSecurityHeaderParser::QuotedPair()
221 {
222 Accept(IsQuotedPairSymbol);
223 }
225 void
226 nsSecurityHeaderParser::LWSMultiple()
227 {
228 while (true) {
229 if (Accept('\r')) {
230 LWSCRLF();
231 } else if (Accept(' ') || Accept('\t')) {
232 LWS();
233 } else {
234 break;
235 }
236 }
237 }
239 void
240 nsSecurityHeaderParser::LWSCRLF() {
241 Expect('\n');
242 if (!(Accept(' ') || Accept('\t'))) {
243 mError = true;
244 }
245 LWS();
246 }
248 void
249 nsSecurityHeaderParser::LWS()
250 {
251 // Note that becaue of how we're called, we don't have to check for
252 // the mandatory presense of at least one of SP or HT.
253 while (Accept(' ') || Accept('\t'));
254 }