Line data Source code
1 : // Copyright (c) 2011-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/edhttp
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 :
20 : // self
21 : //
22 : #include "edhttp/string_part.h"
23 :
24 : #include "edhttp/exception.h"
25 :
26 :
27 : // advgetopt
28 : //
29 : #include <advgetopt/validator_double.h>
30 :
31 :
32 : // snaplogger
33 : //
34 : #include <snaplogger/message.h>
35 :
36 :
37 : // snapdev
38 : //
39 : #include <snapdev/trim_string.h>
40 :
41 :
42 : // last include
43 : //
44 : #include <snapdev/poison.h>
45 :
46 :
47 :
48 : namespace edhttp
49 : {
50 :
51 :
52 :
53 : /** \brief Create a named string_part.
54 : *
55 : * This function is used to create a valid string_part object.
56 : *
57 : * \param[in] name The name of the string_part object.
58 : *
59 : * \sa get_name()
60 : */
61 87 : string_part::string_part(std::string const & name)
62 174 : : f_name(name)
63 : {
64 87 : }
65 :
66 :
67 : /** \brief Retrieve the string_part name.
68 : *
69 : * The name of a string_part object cannot be changed once it was created.
70 : *
71 : * You may retrieve the name with this function, though.
72 : *
73 : * \bug
74 : * It is currently possible to create a string_part object without a name
75 : * so the class works with QVector.
76 : *
77 : * \return The name as passed in when create the string_part object.
78 : */
79 178 : std::string const & string_part::get_name() const
80 : {
81 178 : return f_name;
82 : }
83 :
84 :
85 : /** \brief Retrieve the value of this part.
86 : *
87 : * By default, a part is not expected to include a value, but there
88 : * are many strings in HTTP headers that accept a syntax where parameters
89 : * can be given a value. For example, in the Cache-Control field, we
90 : * can have a "max-age=123" parameter. This function returns the "123".
91 : * The name ("max-age") is returned by the get_name() function.
92 : *
93 : * In a weighted HTTP string such as a string of language definitions,
94 : * the named value has no value. It is expected to represent a flag
95 : * which is set (i.e. do not interpret a part with an empty string
96 : * as "false").
97 : *
98 : * \return The value of this part of the string.
99 : */
100 6 : std::string const & string_part::get_value() const
101 : {
102 6 : return f_value;
103 : }
104 :
105 :
106 : /** \brief This function is used to setup the value of a part.
107 : *
108 : * This function defines the value of a part. By default a part is just
109 : * defined and its value is the empty string (it is still viewed as being
110 : * "true", but without anything more than that.)
111 : *
112 : * The function is called by the parser when it finds a part name followed
113 : * by an equal sign.
114 : *
115 : * \param[in] value The new value of this part.
116 : */
117 8 : void string_part::set_value(std::string const & value)
118 : {
119 8 : f_value = value;
120 8 : }
121 :
122 :
123 : /** \brief Retrieve the level of this string_part object.
124 : *
125 : * This function retrieves the level of the string_part object. It is a floating
126 : * point value.
127 : *
128 : * The level is taken from the "q" parameter. For example, in:
129 : *
130 : * \code
131 : * fr; q=0.3
132 : * \endcode
133 : *
134 : * the level is viewed as 0.3.
135 : *
136 : * \return The string_part object level.
137 : */
138 86 : string_part::level_t string_part::get_level() const
139 : {
140 86 : return f_level;
141 : }
142 :
143 :
144 : /** \brief Change the level of this part.
145 : *
146 : * This function saves the new \p level parameter in this string_part object.
147 : * Items without a level (q=<value>) parameter are assigned the special
148 : * value DEFAULT_LEVEL, which is 1.0.
149 : *
150 : * \bug
151 : * The function does not limit the level. It is expected to be defined
152 : * between 0.0 and 1.0, though.
153 : *
154 : * \param[in] level The new string_part level.
155 : */
156 40 : void string_part::set_level(string_part::level_t const level)
157 : {
158 40 : f_level = level;
159 40 : }
160 :
161 :
162 : /** \brief Retrieve the value of a parameter.
163 : *
164 : * This function returns the value of a parameter given its name.
165 : *
166 : * If the parameter is not exist defined, then the function returns
167 : * an empty string. A parameter may exist and be set to the empty
168 : * string. There is no way to know at this point.
169 : *
170 : * \param[in] name The name of the parameter to retrieve.
171 : *
172 : * \return The value of the parameter or "" if undefined.
173 : */
174 65 : std::string string_part::get_parameter(std::string const & name) const
175 : {
176 65 : auto const it(f_param.find(name));
177 65 : if(it == f_param.end())
178 : {
179 54 : return std::string();
180 : }
181 11 : return it->second;
182 : }
183 :
184 :
185 : /** \brief Add a parameter.
186 : *
187 : * This function is used to add a parameter to the string_part object.
188 : *
189 : * A parameter has a name and a value.
190 : *
191 : * \param[in] name The name of the parameter to add.
192 : * \param[in] value The value of the parameter.
193 : */
194 54 : void string_part::add_parameter(std::string const & name, std::string const & value)
195 : {
196 54 : f_param[name] = value;
197 54 : }
198 :
199 :
200 : /** \brief Convert one part back into a weighted HTTP string.
201 : *
202 : * This function builds one part of a weighted HTTP string. The string
203 : * will look something like:
204 : *
205 : * \code
206 : * es; q=0.8
207 : * \endcode
208 : *
209 : * \return The part converted to one string.
210 : */
211 91 : std::string string_part::to_string() const
212 : {
213 91 : std::string result(f_name);
214 91 : if(!f_value.empty())
215 : {
216 8 : result += '=';
217 :
218 8 : char q(value_require_quotes(f_value));
219 6 : if(q == '?')
220 : {
221 2 : q = '"';
222 : }
223 6 : if(q == '\0')
224 : {
225 4 : result += f_value;
226 : }
227 : else
228 : {
229 2 : result += q;
230 2 : result += f_value;
231 2 : result += q;
232 : }
233 : }
234 :
235 143 : for(auto const & it : f_param)
236 : {
237 108 : std::string p(it.first);
238 54 : if(!it.second.empty())
239 : {
240 54 : p += '=';
241 :
242 54 : char q(value_require_quotes(it.second));
243 54 : if(q == '?')
244 : {
245 2 : q = '"';
246 : }
247 54 : if(q == '\0')
248 : {
249 50 : p += it.second;
250 : }
251 : else
252 : {
253 4 : p += q;
254 4 : p += it.second;
255 4 : p += q;
256 : }
257 : }
258 54 : result += "; ";
259 54 : result += p;
260 : }
261 :
262 89 : return result;
263 : }
264 :
265 :
266 : /** \brief Operator used to sort elements.
267 : *
268 : * This operator overload is used by the different sort algorithms
269 : * that we can apply against this type. In most cases, it is a
270 : * std::stable_sort(),
271 : *
272 : * The function compares the level of the two string_part objects involved.
273 : *
274 : * Note that we sort from the largest to the smallest level. In other
275 : * words, if this string_part has level 1.0 and \p rhs has level 0.5, the
276 : * function returns true (i.e. 1.0 > 0.5).
277 : *
278 : * \param[in] rhs The right hand side string_part object to compare against.
279 : *
280 : * \return true if this string_part is considered smaller than \p rhs.
281 : */
282 23 : bool string_part::operator < (string_part const & rhs) const
283 : {
284 23 : return f_level > rhs.f_level;
285 : }
286 :
287 :
288 : /** \brief Determine whether a string needs quoting.
289 : *
290 : * This function checks the characters in a string a decides whether it needs
291 : * quoting. Note that it works on the safe side and will request quotes in
292 : * more cases than required.
293 : *
294 : * The function returns one of:
295 : *
296 : * * `'\0'` -- no quoting is required
297 : * * `'?'` -- a character requires quoting, the type of quote is left to you.
298 : * * `'"'` -- the string includes `'\\''` so we need to use `'"'` to quote
299 : * * `'\\''` -- the string includes `'"'` so we need to use `'\\''` to quote
300 : *
301 : * \param[in] value The string to be checked. In general a part value.
302 : *
303 : * \return The quote to use or '?' or '\0'.
304 : */
305 62 : char string_part::value_require_quotes(std::string const & value)
306 : {
307 62 : char quote('\0');
308 311 : for(auto const v : value)
309 : {
310 251 : if((v < 'a' || v > 'z')
311 154 : && (v < 'A' || v > 'Z')
312 152 : && (v < '0' || v > '9')
313 58 : && v != '.'
314 23 : && v != '-'
315 23 : && v != '+'
316 23 : && v != '*'
317 23 : && v != '_')
318 : {
319 23 : if(v == '"')
320 : {
321 4 : if(quote != '\0' && quote != '\'' && quote != '?')
322 : {
323 1 : throw unquotable_string("string [" + value + "] includes single and double quotes.");
324 : }
325 3 : quote = '\'';
326 : }
327 19 : else if(v == '\'')
328 : {
329 3 : if(quote != '\0' && quote != '"' && quote != '?')
330 : {
331 1 : throw unquotable_string("string [" + value + "] includes single and double quotes.");
332 : }
333 2 : quote = '"';
334 : }
335 16 : else if(quote == '\0')
336 : {
337 5 : quote = '?';
338 : }
339 : }
340 : }
341 :
342 60 : return quote;
343 : }
344 :
345 :
346 :
347 : } // namespace edhttp
348 : // vim: ts=4 sw=4 et
|