1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #pragma once
25 
26 #include <list>
27 
28 #include <cstring>
29 #include "ComponentBase.h"
30 #include "StringHash.h"
31 #include "HttpHeader.h"
32 #include "Utils.h"
33 
34 namespace EsiLib
35 {
36 class Variables : private ComponentBase
37 {
38 public:
Variables(const char * debug_tag,ComponentBase::Debug debug_func,ComponentBase::Error error_func,Utils::HeaderValueList allowlistCookies)39   Variables(const char *debug_tag, ComponentBase::Debug debug_func, ComponentBase::Error error_func,
40             Utils::HeaderValueList allowlistCookies)
41     : ComponentBase(debug_tag, debug_func, error_func),
42       _headers_parsed(false),
43       _query_string(""),
44       _query_string_parsed(false),
45       _cookie_jar_created(false)
46   {
47     _allowlistCookies.insert(_allowlistCookies.end(), allowlistCookies.begin(), allowlistCookies.end());
48   };
49 
50   /** currently 'host', 'referer', 'accept-language', 'cookie' and 'user-agent' headers are parsed */
51   void populate(const HttpHeader &header);
52 
53   void
populate(const HttpHeaderList & headers)54   populate(const HttpHeaderList &headers)
55   {
56     for (const auto &header : headers) {
57       populate(header);
58     }
59   };
60 
61   void
62   populate(const char *query_string, int query_string_len = -1)
63   {
64     if (query_string && (query_string_len != 0)) {
65       if (query_string_len == -1) {
66         query_string_len = strlen(query_string);
67       }
68       if (_query_string_parsed) {
69         _parseQueryString(query_string, query_string_len);
70       } else {
71         _query_string.assign(query_string, query_string_len);
72       }
73     }
74   }
75 
76   /** returns value of specified variable; empty string returned for unknown variable; key
77    * has to be prefixed with 'http_' string for all variable names except 'query_string' */
78   const std::string &getValue(const std::string &name) const;
79 
80   /** convenient alternative for method above */
81   const std::string &
82   getValue(const char *name, int name_len = -1) const
83   {
84     if (!name) {
85       return EMPTY_STRING;
86     }
87     std::string var_name;
88     if (name_len == -1) {
89       var_name.assign(name);
90     } else {
91       var_name.assign(name, name_len);
92     }
93     return getValue(var_name);
94   }
95 
96   void clear();
97 
~Variables()98   ~Variables() override { _releaseCookieJar(); };
99 
100   // noncopyable
101   Variables(const Variables &) = delete;            // non-copyable
102   Variables &operator=(const Variables &) = delete; // non-copyable
103 
104 private:
105   static const std::string EMPTY_STRING;
106   static const std::string TRUE_STRING;
107   static const std::string VENDOR_STRING;
108   static const std::string VERSION_STRING;
109   static const std::string PLATFORM_STRING;
110 
111   enum SimpleHeader {
112     HTTP_HOST    = 0,
113     HTTP_REFERER = 1,
114   };
115   static const std::string SIMPLE_HEADERS[]; // indices should map to enum values above
116 
117   enum SpecialHeader {
118     HTTP_ACCEPT_LANGUAGE = 0,
119     HTTP_COOKIE          = 1,
120     HTTP_USER_AGENT      = 2,
121     QUERY_STRING         = 3,
122     HTTP_HEADER          = 4,
123   };
124   static const std::string SPECIAL_HEADERS[]; // indices should map to enum values above
125 
126   // normalized versions of the headers above; indices should correspond correctly
127   static const std::string NORM_SIMPLE_HEADERS[];
128   static const std::string NORM_SPECIAL_HEADERS[]; // indices should again map to enum values
129 
130   static const int N_SIMPLE_HEADERS  = HTTP_REFERER + 1;
131   static const int N_SPECIAL_HEADERS = HTTP_HEADER + 1;
132 
133   StringHash _simple_data;
134   StringHash _dict_data[N_SPECIAL_HEADERS];
135 
136   inline std::string &_toUpperCase(std::string &str) const;
137   inline int _searchHeaders(const std::string headers[], const char *name, int name_len) const;
138   bool _parseDictVariable(const std::string &variable, const char *&header, int &header_len, const char *&attr,
139                           int &attr_len) const;
140   void _parseCookieString(const char *str, int str_len);
141   void _parseUserAgentString(const char *str, int str_len);
142   void _parseAcceptLangString(const char *str, int str_len);
143   inline void _parseSimpleHeader(SimpleHeader hdr, const std::string &value);
144   inline void _parseSimpleHeader(SimpleHeader hdr, const char *value, int value_len);
145   void _parseSpecialHeader(SpecialHeader hdr, const char *value, int value_len);
146   void _parseCachedHeaders();
147 
148   inline void _insert(StringHash &hash, const std::string &key, const std::string &value);
149 
150   Utils::HeaderValueList _cached_simple_headers[N_SIMPLE_HEADERS];
151   Utils::HeaderValueList _cached_special_headers[N_SPECIAL_HEADERS];
152 
153   Utils::HeaderValueList _allowlistCookies;
154   std::string _cookie_str;
155   bool _headers_parsed;
156   std::string _query_string;
157   bool _query_string_parsed;
158 
159   void _parseHeader(const char *name, int name_len, const char *value, int value_len);
160   void _parseQueryString(const char *query_string, int query_string_len);
161 
162   StringKeyHash<StringHash> _sub_cookies;
163   bool _cookie_jar_created;
164 
165   void _parseSubCookies();
166 
167   inline void
_releaseCookieJar()168   _releaseCookieJar()
169   {
170     if (_cookie_jar_created) {
171       _sub_cookies.clear();
172       _cookie_jar_created = false;
173     }
174   }
175 
176   mutable std::string _cached_sub_cookie_value;
177   const std::string &_getSubCookieValue(const std::string &cookie_str, size_t cookie_part_divider) const;
178 };
179 }; // namespace EsiLib
180