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
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #include "nsSecurityHeaderParser.h" |
michael@0 | 6 | #include "prlog.h" |
michael@0 | 7 | |
michael@0 | 8 | // The character classes in this file are informed by [RFC2616], Section 2.2. |
michael@0 | 9 | // signed char is a signed data type one byte (8 bits) wide, so its value can |
michael@0 | 10 | // never be greater than 127. The following implicitly makes use of this. |
michael@0 | 11 | |
michael@0 | 12 | // A token is one or more CHAR except CTLs or separators. |
michael@0 | 13 | // A CHAR is any US-ASCII character (octets 0 - 127). |
michael@0 | 14 | // A CTL is any US-ASCII control character (octets 0 - 31) and DEL (127). |
michael@0 | 15 | // A separator is one of ()<>@,;:\"/[]?={} as well as space and |
michael@0 | 16 | // horizontal-tab (32 and 9, respectively). |
michael@0 | 17 | // So, this returns true if chr is any octet 33-126 except ()<>@,;:\"/[]?={} |
michael@0 | 18 | bool |
michael@0 | 19 | IsTokenSymbol(signed char chr) { |
michael@0 | 20 | if (chr < 33 || chr == 127 || |
michael@0 | 21 | chr == '(' || chr == ')' || chr == '<' || chr == '>' || |
michael@0 | 22 | chr == '@' || chr == ',' || chr == ';' || chr == ':' || |
michael@0 | 23 | chr == '"' || chr == '/' || chr == '[' || chr == ']' || |
michael@0 | 24 | chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') { |
michael@0 | 25 | return false; |
michael@0 | 26 | } |
michael@0 | 27 | return true; |
michael@0 | 28 | } |
michael@0 | 29 | |
michael@0 | 30 | // A quoted-string consists of a quote (") followed by any amount of |
michael@0 | 31 | // qdtext or quoted-pair, followed by a quote. |
michael@0 | 32 | // qdtext is any TEXT except a quote. |
michael@0 | 33 | // TEXT is any 8-bit octet except CTLs, but including LWS. |
michael@0 | 34 | // quoted-pair is a backslash (\) followed by a CHAR. |
michael@0 | 35 | // So, it turns out, \ can't really be a qdtext symbol for our purposes. |
michael@0 | 36 | // This returns true if chr is any octet 9,10,13,32-126 except <"> or "\" |
michael@0 | 37 | bool |
michael@0 | 38 | IsQuotedTextSymbol(signed char chr) { |
michael@0 | 39 | return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) || |
michael@0 | 40 | chr == 0x9 || chr == 0xa || chr == 0xd); |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | // The octet following the "\" in a quoted pair can be anything 0-127. |
michael@0 | 44 | bool |
michael@0 | 45 | IsQuotedPairSymbol(signed char chr) { |
michael@0 | 46 | return (chr >= 0); |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | #if defined(PR_LOGGING) |
michael@0 | 50 | static PRLogModuleInfo * |
michael@0 | 51 | GetSHParserLog() |
michael@0 | 52 | { |
michael@0 | 53 | static PRLogModuleInfo *sSHParserLog; |
michael@0 | 54 | if (!sSHParserLog) { |
michael@0 | 55 | sSHParserLog = PR_NewLogModule("nsSecurityHeaderParser"); |
michael@0 | 56 | } |
michael@0 | 57 | return sSHParserLog; |
michael@0 | 58 | } |
michael@0 | 59 | #endif |
michael@0 | 60 | |
michael@0 | 61 | #define SHPARSERLOG(args) PR_LOG(GetSHParserLog(), PR_LOG_DEBUG, args) |
michael@0 | 62 | |
michael@0 | 63 | nsSecurityHeaderParser::nsSecurityHeaderParser(const char *aHeader) |
michael@0 | 64 | : mCursor(aHeader) |
michael@0 | 65 | , mError(false) |
michael@0 | 66 | { |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | nsSecurityHeaderParser::~nsSecurityHeaderParser() { |
michael@0 | 70 | nsSecurityHeaderDirective *directive; |
michael@0 | 71 | while ((directive = mDirectives.popFirst())) { |
michael@0 | 72 | delete directive; |
michael@0 | 73 | } |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | mozilla::LinkedList<nsSecurityHeaderDirective> * |
michael@0 | 77 | nsSecurityHeaderParser::GetDirectives() { |
michael@0 | 78 | return &mDirectives; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | nsresult |
michael@0 | 82 | nsSecurityHeaderParser::Parse() { |
michael@0 | 83 | MOZ_ASSERT(mDirectives.isEmpty()); |
michael@0 | 84 | SHPARSERLOG(("trying to parse '%s'", mCursor)); |
michael@0 | 85 | |
michael@0 | 86 | Header(); |
michael@0 | 87 | |
michael@0 | 88 | // if we didn't consume the entire input, we were unable to parse it => error |
michael@0 | 89 | if (mError || *mCursor) { |
michael@0 | 90 | return NS_ERROR_FAILURE; |
michael@0 | 91 | } else { |
michael@0 | 92 | return NS_OK; |
michael@0 | 93 | } |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | bool |
michael@0 | 97 | nsSecurityHeaderParser::Accept(char aChr) |
michael@0 | 98 | { |
michael@0 | 99 | if (*mCursor == aChr) { |
michael@0 | 100 | Advance(); |
michael@0 | 101 | return true; |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | return false; |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | bool |
michael@0 | 108 | nsSecurityHeaderParser::Accept(bool (*aClassifier) (signed char)) |
michael@0 | 109 | { |
michael@0 | 110 | if (aClassifier(*mCursor)) { |
michael@0 | 111 | Advance(); |
michael@0 | 112 | return true; |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | return false; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | void |
michael@0 | 119 | nsSecurityHeaderParser::Expect(char aChr) |
michael@0 | 120 | { |
michael@0 | 121 | if (*mCursor != aChr) { |
michael@0 | 122 | mError = true; |
michael@0 | 123 | } else { |
michael@0 | 124 | Advance(); |
michael@0 | 125 | } |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | void |
michael@0 | 129 | nsSecurityHeaderParser::Advance() |
michael@0 | 130 | { |
michael@0 | 131 | // Technically, 0 is valid in quoted-pair, but we were handed a |
michael@0 | 132 | // null-terminated const char *, so this doesn't handle that. |
michael@0 | 133 | if (*mCursor) { |
michael@0 | 134 | mOutput.Append(*mCursor); |
michael@0 | 135 | mCursor++; |
michael@0 | 136 | } else { |
michael@0 | 137 | mError = true; |
michael@0 | 138 | } |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | void |
michael@0 | 142 | nsSecurityHeaderParser::Header() |
michael@0 | 143 | { |
michael@0 | 144 | Directive(); |
michael@0 | 145 | while (Accept(';')) { |
michael@0 | 146 | Directive(); |
michael@0 | 147 | } |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | void |
michael@0 | 151 | nsSecurityHeaderParser::Directive() |
michael@0 | 152 | { |
michael@0 | 153 | mDirective = new nsSecurityHeaderDirective(); |
michael@0 | 154 | LWSMultiple(); |
michael@0 | 155 | DirectiveName(); |
michael@0 | 156 | LWSMultiple(); |
michael@0 | 157 | if (Accept('=')) { |
michael@0 | 158 | LWSMultiple(); |
michael@0 | 159 | DirectiveValue(); |
michael@0 | 160 | LWSMultiple(); |
michael@0 | 161 | } |
michael@0 | 162 | mDirectives.insertBack(mDirective); |
michael@0 | 163 | SHPARSERLOG(("read directive name '%s', value '%s'", |
michael@0 | 164 | mDirective->mName.Data(), mDirective->mValue.Data())); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | void |
michael@0 | 168 | nsSecurityHeaderParser::DirectiveName() |
michael@0 | 169 | { |
michael@0 | 170 | mOutput.Truncate(0); |
michael@0 | 171 | Token(); |
michael@0 | 172 | mDirective->mName.Assign(mOutput); |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | void |
michael@0 | 176 | nsSecurityHeaderParser::DirectiveValue() |
michael@0 | 177 | { |
michael@0 | 178 | mOutput.Truncate(0); |
michael@0 | 179 | if (Accept(IsTokenSymbol)) { |
michael@0 | 180 | Token(); |
michael@0 | 181 | mDirective->mValue.Assign(mOutput); |
michael@0 | 182 | } else if (Accept('"')) { |
michael@0 | 183 | // Accept advances the cursor if successful, which appends a character to |
michael@0 | 184 | // mOutput. The " is not part of what we want to capture, so truncate |
michael@0 | 185 | // mOutput again. |
michael@0 | 186 | mOutput.Truncate(0); |
michael@0 | 187 | QuotedString(); |
michael@0 | 188 | mDirective->mValue.Assign(mOutput); |
michael@0 | 189 | Expect('"'); |
michael@0 | 190 | } |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | void |
michael@0 | 194 | nsSecurityHeaderParser::Token() |
michael@0 | 195 | { |
michael@0 | 196 | while (Accept(IsTokenSymbol)); |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | void |
michael@0 | 200 | nsSecurityHeaderParser::QuotedString() |
michael@0 | 201 | { |
michael@0 | 202 | while (true) { |
michael@0 | 203 | if (Accept(IsQuotedTextSymbol)) { |
michael@0 | 204 | QuotedText(); |
michael@0 | 205 | } else if (Accept('\\')) { |
michael@0 | 206 | QuotedPair(); |
michael@0 | 207 | } else { |
michael@0 | 208 | break; |
michael@0 | 209 | } |
michael@0 | 210 | } |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | void |
michael@0 | 214 | nsSecurityHeaderParser::QuotedText() |
michael@0 | 215 | { |
michael@0 | 216 | while (Accept(IsQuotedTextSymbol)); |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | void |
michael@0 | 220 | nsSecurityHeaderParser::QuotedPair() |
michael@0 | 221 | { |
michael@0 | 222 | Accept(IsQuotedPairSymbol); |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | void |
michael@0 | 226 | nsSecurityHeaderParser::LWSMultiple() |
michael@0 | 227 | { |
michael@0 | 228 | while (true) { |
michael@0 | 229 | if (Accept('\r')) { |
michael@0 | 230 | LWSCRLF(); |
michael@0 | 231 | } else if (Accept(' ') || Accept('\t')) { |
michael@0 | 232 | LWS(); |
michael@0 | 233 | } else { |
michael@0 | 234 | break; |
michael@0 | 235 | } |
michael@0 | 236 | } |
michael@0 | 237 | } |
michael@0 | 238 | |
michael@0 | 239 | void |
michael@0 | 240 | nsSecurityHeaderParser::LWSCRLF() { |
michael@0 | 241 | Expect('\n'); |
michael@0 | 242 | if (!(Accept(' ') || Accept('\t'))) { |
michael@0 | 243 | mError = true; |
michael@0 | 244 | } |
michael@0 | 245 | LWS(); |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | void |
michael@0 | 249 | nsSecurityHeaderParser::LWS() |
michael@0 | 250 | { |
michael@0 | 251 | // Note that becaue of how we're called, we don't have to check for |
michael@0 | 252 | // the mandatory presense of at least one of SP or HT. |
michael@0 | 253 | while (Accept(' ') || Accept('\t')); |
michael@0 | 254 | } |