#include "parse.h" #include "common.h" #include "encode.h" namespace NUri { const TParseFlags TParser::FieldFlags[] = { TParseFlags(0 // FieldScheme | TFeature::FeatureToLower, 0) , TParseFlags(0 // FieldUsername | TFeature::FeatureDecodeANY | TFeature::FeaturesDecode | TFeature::FeatureEncodePercent, 0 | TFeature::FeatureToLower) , TParseFlags(0 // FieldPassword | TFeature::FeatureDecodeANY | TFeature::FeaturesDecode | TFeature::FeatureEncodePercent, 0 | TFeature::FeatureToLower) , TParseFlags(0 // FieldHost | TFeature::FeatureToLower | TFeature::FeatureUpperEncoded | (TFeature::FeaturesMaybeEncode & ~TFeature::FeatureEncodeExtendedDelim), 0 | TFeature::FeaturesMaybeDecode) , TParseFlags(0 // FieldPort , 0) , TParseFlags(0 // FieldPath | TFeature::FeaturesEncodePChar | TFeature::FeaturePathOperation, 0 | TFeature::FeatureToLower | TFeature::FeatureEncodeSpaceAsPlus) , TParseFlags(0 // FieldQuery | TFeature::FeaturesEncodePChar | TFeature::FeatureEncodeSpaceAsPlus, 0 | TFeature::FeatureToLower) , TParseFlags(0 // FieldFragment | TFeature::FeaturesEncodePChar, 0 | TFeature::FeatureToLower | TFeature::FeatureEncodeSpaceAsPlus) , TParseFlags(0 // FieldHashBang | TFeature::FeaturesEncodePChar, 0 | TFeature::FeatureToLower | TFeature::FeatureEncodeSpaceAsPlus)}; namespace NParse { void TRange::AddRange(const TRange& range, ui64 mask) { FlagsAllPlaintext |= range.FlagsAllPlaintext; // update only if flags apply here mask &= range.FlagsEncodeMasked; if (0 == mask) return; FlagsEncodeMasked |= mask; if (mask & TFeature::FeaturesMaybeEncode) Encode += range.Encode; if (mask & TFeature::FeaturesDecode) Decode += range.Decode; } } void TParser::copyRequirementsImpl(const char* ptr) { Y_ASSERT(0 != CurRange.FlagsAllPlaintext); Y_UNUSED(ptr); #ifdef DO_PRN PrintHead(ptr, __FUNCTION__) << " all=[" << IntToString<16>(CurRange.FlagsAllPlaintext) << "] enc=[" << IntToString<16>(CurRange.FlagsEncodeMasked) << " & " << IntToString<16>(Flags.Allow | Flags.Extra) << "]"; PrintTail(CurRange.Beg, ptr); #endif for (int i = 0; i < TField::FieldUrlMAX; ++i) { const TField::EField fld = TField::EField(i); TSection& section = Sections[fld]; // update only sections in progress if (nullptr == section.Beg) continue; // and overlapping with the range if (nullptr != section.End && section.End < CurRange.Beg) continue; #ifdef DO_PRN PrintHead(ptr, __FUNCTION__, fld) << " all=[" << IntToString<16>(CurRange.FlagsAllPlaintext) << "] enc=[" << IntToString<16>(CurRange.FlagsEncodeMasked) << " & " << IntToString<16>(GetFieldFlags(fld)) << "]"; PrintTail(section.Beg, ptr); #endif section.AddRange(CurRange, GetFieldFlags(fld)); } CurRange.Reset(); } void TParser::PctEndImpl(const char* ptr) { #ifdef DO_PRN PrintHead(PctBegin, __FUNCTION__); PrintTail(PctBegin, ptr); #else Y_UNUSED(ptr); #endif setRequirement(PctBegin, TEncoder::GetFlags('%').FeatFlags); PctBegin = nullptr; } void TParser::HexSet(const char* ptr) { Y_ASSERT(nullptr != PctBegin); #ifdef DO_PRN PrintHead(ptr, __FUNCTION__); PrintTail(PctBegin, ptr + 1); #endif PctBegin = nullptr; const unsigned char ch = HexValue; ui64 flags = TEncoder::GetFlags('%').FeatFlags | TEncoder::GetFlags(ch).FeatFlags; setRequirementExcept(ptr, flags, TFeature::FeaturesMaybeEncode); } TState::EParsed TParser::ParseImpl() { #ifdef DO_PRN PrintHead(UriStr.data(), "[Parsing]") << "URL"; PrintTail(UriStr); #endif const bool ok = doParse(UriStr.data(), UriStr.length()); #ifdef DO_PRN Cdbg << (ok ? "[Parsed]" : "[Failed]"); for (int idx = 0; idx < TField::FieldUrlMAX; ++idx) { const TSection& section = Sections[idx]; if (section.IsSet()) Cdbg << ' ' << TField::EField(idx) << "=[" << section.Get() << ']'; } Cdbg << Endl; #endif if (!ok) { if (!(Flags & TFeature::FeatureTryToFix) || !Sections[TField::FieldFrag].Beg) return TState::ParsedBadFormat; //Here: error was in fragment, just ignore it ResetSection(TField::FieldFrag); } if ((Flags & TFeature::FeatureDenyNetworkPath) && IsNetPath()) return TState::ParsedBadFormat; const TSection& scheme = Sections[TField::FieldScheme]; Scheme = scheme.IsSet() ? TSchemeInfo::GetKind(scheme.Get()) : TScheme::SchemeEmpty; const TSchemeInfo& schemeInfo = TSchemeInfo::Get(Scheme); if (IsRootless()) { // opaque case happens if (schemeInfo.FldReq & TField::FlagHost) return TState::ParsedBadFormat; if (TScheme::SchemeEmpty == Scheme) return TState::ParsedBadScheme; if (Flags & TFeature::FeatureAllowRootless) return TState::ParsedOK; if (!(Flags & TFeature::FeatureSchemeFlexible)) return TState::ParsedBadScheme; return TState::ParsedRootless; } checkSectionCollision(TField::FieldUser, TField::FieldHost); checkSectionCollision(TField::FieldPass, TField::FieldPort); if (0 == (Flags & TFeature::FeatureAuthSupported)) if (Sections[TField::FieldUser].IsSet() || Sections[TField::FieldPass].IsSet()) return TState::ParsedBadAuth; TSection& host = Sections[TField::FieldHost]; if (host.IsSet()) for (; host.End != host.Beg && '.' == host.End[-1];) --host.End; if (scheme.IsSet()) { ui64 wantCareFlags = 0; switch (Scheme) { case TScheme::SchemeHTTP: break; case TScheme::SchemeEmpty: Scheme = TScheme::SchemeUnknown; [[fallthrough]]; case TScheme::SchemeUnknown: wantCareFlags = TFeature::FeatureSchemeFlexible | TFeature::FeatureNoRelPath; break; default: wantCareFlags = TFeature::FeatureSchemeFlexible | TFeature::FeatureSchemeKnown; break; } if (0 != wantCareFlags && 0 == (Flags & wantCareFlags)) return TState::ParsedBadScheme; if ((schemeInfo.FldReq & TField::FlagHost) || (Flags & TFeature::FeatureRemoteOnly)) if (!host.IsSet() || 0 == host.Len()) return TState::ParsedBadFormat; } return TState::ParsedOK; } }