#include "cgiparam.h" #include <library/cpp/string_utils/scan/scan.h> #include <library/cpp/string_utils/quote/quote.h> #include <util/generic/singleton.h> TCgiParameters::TCgiParameters(std::initializer_list<std::pair<TString, TString>> il) { for (const auto& item : il) { insert(item); } } const TString& TCgiParameters::Get(const TStringBuf name, size_t numOfValue) const noexcept { const auto it = Find(name, numOfValue); return end() == it ? Default<TString>() : it->second; } bool TCgiParameters::Erase(const TStringBuf name, size_t pos) { const auto pair = equal_range(name); for (auto it = pair.first; it != pair.second; ++it, --pos) { if (0 == pos) { erase(it); return true; } } return false; } bool TCgiParameters::Erase(const TStringBuf name, const TStringBuf val) { const auto pair = equal_range(name); bool found = false; for (auto it = pair.first; it != pair.second;) { if (val == it->second) { it = erase(it); found = true; } else { ++it; } } return found; } size_t TCgiParameters::EraseAll(const TStringBuf name) { size_t num = 0; const auto pair = equal_range(name); for (auto it = pair.first; it != pair.second; erase(it++), ++num) ; return num; } void TCgiParameters::JoinUnescaped(const TStringBuf key, char sep, TStringBuf val) { const auto pair = equal_range(key); auto it = pair.first; if (it == pair.second) { // not found if (val.IsInited()) { emplace_hint(it, TString(key), TString(val)); } } else { TString& dst = it->second; for (++it; it != pair.second; erase(it++)) { dst += sep; dst.AppendNoAlias(it->second.data(), it->second.size()); } if (val.IsInited()) { dst += sep; dst += val; } } } static inline TString DoUnescape(const TStringBuf s) { TString res; res.reserve(CgiUnescapeBufLen(s.size())); res.ReserveAndResize(CgiUnescape(res.begin(), s).size()); return res; } void TCgiParameters::InsertEscaped(const TStringBuf name, const TStringBuf value) { InsertUnescaped(DoUnescape(name), DoUnescape(value)); } template <bool addAll, class F> static inline void DoScan(const TStringBuf s, F& f) { ScanKeyValue<addAll, '&', '='>(s, f); } struct TAddEscaped { TCgiParameters* C; inline void operator()(const TStringBuf key, const TStringBuf val) { C->InsertEscaped(key, val); } }; void TCgiParameters::Scan(const TStringBuf query, bool form) { Flush(); form ? ScanAdd(query) : ScanAddAll(query); } void TCgiParameters::ScanAdd(const TStringBuf query) { TAddEscaped f = {this}; DoScan<false>(query, f); } void TCgiParameters::ScanAddUnescaped(const TStringBuf query) { auto f = [this](const TStringBuf key, const TStringBuf val) { this->InsertUnescaped(key, val); }; DoScan<false>(query, f); } void TCgiParameters::ScanAddAllUnescaped(const TStringBuf query) { auto f = [this](const TStringBuf key, const TStringBuf val) { this->InsertUnescaped(key, val); }; DoScan<true>(query, f); } void TCgiParameters::ScanAddAll(const TStringBuf query) { TAddEscaped f = {this}; DoScan<true>(query, f); } TString TCgiParameters::Print() const { TString res; res.reserve(PrintSize()); const char* end = Print(res.begin()); res.ReserveAndResize(end - res.data()); return res; } char* TCgiParameters::Print(char* res) const { if (empty()) { return res; } for (auto i = begin();;) { res = CGIEscape(res, i->first); *res++ = '='; res = CGIEscape(res, i->second); if (++i == end()) { break; } *res++ = '&'; } return res; } size_t TCgiParameters::PrintSize() const noexcept { size_t res = size(); // for '&' for (const auto& i : *this) { res += CgiEscapeBufLen(i.first.size() + i.second.size()); // extra zero will be used for '=' } return res; } TString TCgiParameters::QuotedPrint(const char* safe) const { if (empty()) { return TString(); } TString res; res.ReserveAndResize(PrintSize()); char* ptr = res.begin(); for (auto i = begin();;) { ptr = Quote(ptr, i->first, safe); *ptr++ = '='; ptr = Quote(ptr, i->second, safe); if (++i == end()) { break; } *ptr++ = '&'; } res.ReserveAndResize(ptr - res.data()); return res; } TCgiParameters::const_iterator TCgiParameters::Find(const TStringBuf name, size_t pos) const noexcept { const auto pair = equal_range(name); for (auto it = pair.first; it != pair.second; ++it, --pos) { if (0 == pos) { return it; } } return end(); } bool TCgiParameters::Has(const TStringBuf name, const TStringBuf value) const noexcept { const auto pair = equal_range(name); for (auto it = pair.first; it != pair.second; ++it) { if (value == it->second) { return true; } } return false; } TQuickCgiParam::TQuickCgiParam(const TStringBuf cgiParamStr) { UnescapeBuf.reserve(CgiUnescapeBufLen(cgiParamStr.size())); char* buf = UnescapeBuf.begin(); auto f = [this, &buf](const TStringBuf key, const TStringBuf val) { TStringBuf name = CgiUnescapeBuf(buf, key); buf += name.size() + 1; TStringBuf value = CgiUnescapeBuf(buf, val); buf += value.size() + 1; Y_ASSERT(buf <= UnescapeBuf.begin() + UnescapeBuf.capacity() + 1 /*trailing zero*/); emplace(name, value); }; DoScan<false>(cgiParamStr, f); if (buf != UnescapeBuf.begin()) { UnescapeBuf.ReserveAndResize(buf - UnescapeBuf.begin() - 1 /*trailing zero*/); } } const TStringBuf& TQuickCgiParam::Get(const TStringBuf name, size_t pos) const noexcept { const auto pair = equal_range(name); for (auto it = pair.first; it != pair.second; ++it, --pos) { if (0 == pos) { return it->second; } } return Default<TStringBuf>(); } bool TQuickCgiParam::Has(const TStringBuf name, const TStringBuf value) const noexcept { const auto pair = equal_range(name); for (auto it = pair.first; it != pair.second; ++it) { if (value == it->second) { return true; } } return false; }