michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsSecurityHeaderParser.h" michael@0: #include "prlog.h" michael@0: michael@0: // The character classes in this file are informed by [RFC2616], Section 2.2. michael@0: // signed char is a signed data type one byte (8 bits) wide, so its value can michael@0: // never be greater than 127. The following implicitly makes use of this. michael@0: michael@0: // A token is one or more CHAR except CTLs or separators. michael@0: // A CHAR is any US-ASCII character (octets 0 - 127). michael@0: // A CTL is any US-ASCII control character (octets 0 - 31) and DEL (127). michael@0: // A separator is one of ()<>@,;:\"/[]?={} as well as space and michael@0: // horizontal-tab (32 and 9, respectively). michael@0: // So, this returns true if chr is any octet 33-126 except ()<>@,;:\"/[]?={} michael@0: bool michael@0: IsTokenSymbol(signed char chr) { michael@0: if (chr < 33 || chr == 127 || michael@0: chr == '(' || chr == ')' || chr == '<' || chr == '>' || michael@0: chr == '@' || chr == ',' || chr == ';' || chr == ':' || michael@0: chr == '"' || chr == '/' || chr == '[' || chr == ']' || michael@0: chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // A quoted-string consists of a quote (") followed by any amount of michael@0: // qdtext or quoted-pair, followed by a quote. michael@0: // qdtext is any TEXT except a quote. michael@0: // TEXT is any 8-bit octet except CTLs, but including LWS. michael@0: // quoted-pair is a backslash (\) followed by a CHAR. michael@0: // So, it turns out, \ can't really be a qdtext symbol for our purposes. michael@0: // This returns true if chr is any octet 9,10,13,32-126 except <"> or "\" michael@0: bool michael@0: IsQuotedTextSymbol(signed char chr) { michael@0: return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) || michael@0: chr == 0x9 || chr == 0xa || chr == 0xd); michael@0: } michael@0: michael@0: // The octet following the "\" in a quoted pair can be anything 0-127. michael@0: bool michael@0: IsQuotedPairSymbol(signed char chr) { michael@0: return (chr >= 0); michael@0: } michael@0: michael@0: #if defined(PR_LOGGING) michael@0: static PRLogModuleInfo * michael@0: GetSHParserLog() michael@0: { michael@0: static PRLogModuleInfo *sSHParserLog; michael@0: if (!sSHParserLog) { michael@0: sSHParserLog = PR_NewLogModule("nsSecurityHeaderParser"); michael@0: } michael@0: return sSHParserLog; michael@0: } michael@0: #endif michael@0: michael@0: #define SHPARSERLOG(args) PR_LOG(GetSHParserLog(), PR_LOG_DEBUG, args) michael@0: michael@0: nsSecurityHeaderParser::nsSecurityHeaderParser(const char *aHeader) michael@0: : mCursor(aHeader) michael@0: , mError(false) michael@0: { michael@0: } michael@0: michael@0: nsSecurityHeaderParser::~nsSecurityHeaderParser() { michael@0: nsSecurityHeaderDirective *directive; michael@0: while ((directive = mDirectives.popFirst())) { michael@0: delete directive; michael@0: } michael@0: } michael@0: michael@0: mozilla::LinkedList * michael@0: nsSecurityHeaderParser::GetDirectives() { michael@0: return &mDirectives; michael@0: } michael@0: michael@0: nsresult michael@0: nsSecurityHeaderParser::Parse() { michael@0: MOZ_ASSERT(mDirectives.isEmpty()); michael@0: SHPARSERLOG(("trying to parse '%s'", mCursor)); michael@0: michael@0: Header(); michael@0: michael@0: // if we didn't consume the entire input, we were unable to parse it => error michael@0: if (mError || *mCursor) { michael@0: return NS_ERROR_FAILURE; michael@0: } else { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsSecurityHeaderParser::Accept(char aChr) michael@0: { michael@0: if (*mCursor == aChr) { michael@0: Advance(); michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsSecurityHeaderParser::Accept(bool (*aClassifier) (signed char)) michael@0: { michael@0: if (aClassifier(*mCursor)) { michael@0: Advance(); michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::Expect(char aChr) michael@0: { michael@0: if (*mCursor != aChr) { michael@0: mError = true; michael@0: } else { michael@0: Advance(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::Advance() michael@0: { michael@0: // Technically, 0 is valid in quoted-pair, but we were handed a michael@0: // null-terminated const char *, so this doesn't handle that. michael@0: if (*mCursor) { michael@0: mOutput.Append(*mCursor); michael@0: mCursor++; michael@0: } else { michael@0: mError = true; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::Header() michael@0: { michael@0: Directive(); michael@0: while (Accept(';')) { michael@0: Directive(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::Directive() michael@0: { michael@0: mDirective = new nsSecurityHeaderDirective(); michael@0: LWSMultiple(); michael@0: DirectiveName(); michael@0: LWSMultiple(); michael@0: if (Accept('=')) { michael@0: LWSMultiple(); michael@0: DirectiveValue(); michael@0: LWSMultiple(); michael@0: } michael@0: mDirectives.insertBack(mDirective); michael@0: SHPARSERLOG(("read directive name '%s', value '%s'", michael@0: mDirective->mName.Data(), mDirective->mValue.Data())); michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::DirectiveName() michael@0: { michael@0: mOutput.Truncate(0); michael@0: Token(); michael@0: mDirective->mName.Assign(mOutput); michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::DirectiveValue() michael@0: { michael@0: mOutput.Truncate(0); michael@0: if (Accept(IsTokenSymbol)) { michael@0: Token(); michael@0: mDirective->mValue.Assign(mOutput); michael@0: } else if (Accept('"')) { michael@0: // Accept advances the cursor if successful, which appends a character to michael@0: // mOutput. The " is not part of what we want to capture, so truncate michael@0: // mOutput again. michael@0: mOutput.Truncate(0); michael@0: QuotedString(); michael@0: mDirective->mValue.Assign(mOutput); michael@0: Expect('"'); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::Token() michael@0: { michael@0: while (Accept(IsTokenSymbol)); michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::QuotedString() michael@0: { michael@0: while (true) { michael@0: if (Accept(IsQuotedTextSymbol)) { michael@0: QuotedText(); michael@0: } else if (Accept('\\')) { michael@0: QuotedPair(); michael@0: } else { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::QuotedText() michael@0: { michael@0: while (Accept(IsQuotedTextSymbol)); michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::QuotedPair() michael@0: { michael@0: Accept(IsQuotedPairSymbol); michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::LWSMultiple() michael@0: { michael@0: while (true) { michael@0: if (Accept('\r')) { michael@0: LWSCRLF(); michael@0: } else if (Accept(' ') || Accept('\t')) { michael@0: LWS(); michael@0: } else { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::LWSCRLF() { michael@0: Expect('\n'); michael@0: if (!(Accept(' ') || Accept('\t'))) { michael@0: mError = true; michael@0: } michael@0: LWS(); michael@0: } michael@0: michael@0: void michael@0: nsSecurityHeaderParser::LWS() michael@0: { michael@0: // Note that becaue of how we're called, we don't have to check for michael@0: // the mandatory presense of at least one of SP or HT. michael@0: while (Accept(' ') || Accept('\t')); michael@0: }