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 #include "tscore/ink_platform.h"
25 #include "HttpCompat.h"
26 #include "HdrUtils.h"
27
28 //////////////////////////////////////////////////////////////////////////////
29 //
30 // HttpCompat::parse_tok_list
31 //
32 // Takes a string containing an HTTP list broken on the separator
33 // character <sep>, and returns a StrList object containing a
34 // dynamically allocated list of elements. This is essentially a
35 // fancy strtok that runs to completion and hands you back all tokens.
36 //
37 // The routine either allocates and copies each string token, or
38 // just maintains the point to the raw text token, depending on the
39 // mode of the StrList object.
40 //
41 //////////////////////////////////////////////////////////////////////////////
42
43 void
parse_tok_list(StrList * list,int trim_quotes,const char * string,char sep)44 HttpCompat::parse_tok_list(StrList *list, int trim_quotes, const char *string, char sep)
45 {
46 if (string == nullptr) {
47 return;
48 }
49 HttpCompat::parse_tok_list(list, trim_quotes, string, static_cast<int>(strlen(string)), sep);
50 }
51
52 void
parse_tok_list(StrList * list,int trim_quotes,const char * string,int len,char sep)53 HttpCompat::parse_tok_list(StrList *list, int trim_quotes, const char *string, int len, char sep)
54 {
55 int in_quote;
56 const char quot = '\"';
57 const char *s, *e, *l, *s_before_skipping_ws;
58 int index, byte_length, hit_sep;
59
60 if ((string == nullptr) || (list == nullptr) || (sep == NUL)) {
61 return;
62 }
63
64 s = string;
65 l = s + len - 1;
66 index = 0;
67
68 hit_sep = 0;
69 s_before_skipping_ws = s;
70
71 while (s <= l) {
72 //////////////////////////////////////////////////////////
73 // find the start of the first token, skipping over any //
74 // whitespace or empty tokens, to leave <s> pointing at //
75 // a NUL, a character, or a double quote. //
76 //////////////////////////////////////////////////////////
77
78 while ((s <= l) && ParseRules::is_ws(*s)) {
79 ++s; // skip whitespace
80 }
81
82 //////////////////////////////////////////////////////////
83 // if we are pointing at a separator, this was an empty //
84 // token, so add the empty token, and continue parsing. //
85 //////////////////////////////////////////////////////////
86
87 if ((s <= l) && (*s == sep)) {
88 list->append_string(s_before_skipping_ws, 0);
89 ++index;
90 s_before_skipping_ws = s + 1;
91 s = s_before_skipping_ws;
92 hit_sep = 1;
93 continue;
94 }
95 //////////////////////////////////////////////////////////////////
96 // at this point, <s> points to EOS, a double quote, or another //
97 // character --- if EOS, then break out of the loop, and either //
98 // tack on a final empty token if we had a trailing separator, //
99 // or just exit. //
100 //////////////////////////////////////////////////////////////////
101
102 if (s > l) {
103 break;
104 }
105
106 ///////////////////////////////////////////////////////////////////
107 // we are pointing to the first character of a token now, either //
108 // a character, or a double quote --- the next step is to scan //
109 // for the next separator or end of string, being careful not to //
110 // include separators inside quotes. //
111 ///////////////////////////////////////////////////////////////////
112
113 #define is_unquoted_separator(c) ((c == sep) && !in_quote)
114
115 if (*s == quot) {
116 in_quote = 1;
117 e = s + 1; // start after quote
118 if (trim_quotes) {
119 ++s; // trim starting quote
120 }
121 } else {
122 in_quote = 0;
123 e = s;
124 }
125
126 while ((e <= l) && !is_unquoted_separator(*e)) {
127 if (*e == quot) {
128 in_quote = !in_quote;
129 }
130 e++;
131 }
132
133 ///////////////////////////////////////////////////////////////////////
134 // we point one char past the last character of string, or an //
135 // unquoted separator --- so back up into any previous whitespace or //
136 // quote, leaving <e> pointed 1 char after the last token character. //
137 ///////////////////////////////////////////////////////////////////////
138
139 hit_sep = (e <= l); // must have hit a separator if still inside string
140
141 s_before_skipping_ws = e + 1; // where to start next time
142 while ((e > s) && ParseRules::is_ws(*(e - 1))) {
143 --e; // eat trailing ws
144 }
145 if ((e > s) && (*(e - 1) == quot) && trim_quotes) {
146 --e; // eat trailing quote
147 }
148
149 /////////////////////////////////////////////////////////////////////
150 // now <e> points to the character AFTER the last character of the //
151 // field, either a separator, a quote, or a NUL (other other char //
152 // after the last char in the string. //
153 /////////////////////////////////////////////////////////////////////
154
155 byte_length = static_cast<int>(e - s);
156 ink_assert(byte_length >= 0);
157
158 ///////////////////////////////////////////
159 // add the text to the list, and move on //
160 ///////////////////////////////////////////
161
162 list->append_string(s, byte_length);
163 s = s_before_skipping_ws; // where to start next time
164 ++index;
165 }
166
167 ////////////////////////////////////////////////////////////////////////////
168 // fall out of loop when at end of string --- three possibilities: //
169 // (1) at end of string after final token ("a,b,c" or "a,b,c ") //
170 // (2) at end of string after final separator ("a,b,c," or "a,b,c, ") //
171 // (3) at end of string before any tokens ("" or " ") //
172 // for cases (2) & (3), we want to return an empty token //
173 ////////////////////////////////////////////////////////////////////////////
174
175 if (hit_sep || (index == 0)) {
176 ink_assert(s == l + 1);
177 list->append_string(s_before_skipping_ws, 0);
178 ++index;
179 }
180 }
181
182 //////////////////////////////////////////////////////////////////////////////
183 //
184 // bool HttpCompat::lookup_param_in_strlist(
185 // StrList *param_list, char *param_name,
186 // char *param_val, int param_val_length)
187 //
188 // Takes a list of parameter strings, and searches each parameter list
189 // element for the name <param_name>, and if followed by '=' and a value,
190 // the value string is stored in <param_val> up to <param_val_length>
191 // bytes minus 1 character for trailing NUL.
192 //
193 // This routine can be used to search for charset=XXX, Q=XXX, and other
194 // kinds of parameters. The param list can be constructed using the
195 // parse_comma_list and parse_semicolon_list functions.
196 //
197 // The routine returns true if there was a match, false otherwise.
198 //
199 //////////////////////////////////////////////////////////////////////////////
200
201 bool
lookup_param_in_strlist(StrList * param_list,const char * param_name,char * param_val,int param_val_length)202 HttpCompat::lookup_param_in_strlist(StrList *param_list, const char *param_name, char *param_val, int param_val_length)
203 {
204 int cnt;
205 const char *s, *t;
206 Str *param;
207 bool is_match;
208
209 for (param = param_list->head; param != nullptr; param = param->next) {
210 /////////////////////////////////////////////////////
211 // compare this parameter to the target param_name //
212 /////////////////////////////////////////////////////
213
214 s = param->str; // source str
215 t = param_name; // target str
216 while (*s && *t && (ParseRules::ink_tolower(*s) == ParseRules::ink_tolower(*t))) {
217 ++s;
218 ++t;
219 }
220
221 ////////////////////////////////////////////////////////////////
222 // match if target string empty, and if current string empty, //
223 // or points to space or '=' character. //
224 ////////////////////////////////////////////////////////////////
225
226 is_match = ((!*t) && ((!*s) || ParseRules::is_ws(*s) || (*s == '=')));
227
228 /////////////////////////////////////////////////////////////
229 // copy text after '=' into param_val, up to length limits //
230 /////////////////////////////////////////////////////////////
231
232 if (is_match) {
233 param_val[0] = '\0';
234
235 while (*s && ParseRules::is_ws(*s)) {
236 s++; // skip white
237 }
238 if (*s == '=') {
239 ++s; // skip '='
240 while (*s && ParseRules::is_ws(*s)) {
241 s++; // skip white
242 }
243
244 for (cnt = 0; *s && (cnt < param_val_length - 1); s++, cnt++) {
245 param_val[cnt] = *s;
246 }
247 if (cnt < param_val_length) {
248 param_val[cnt++] = '\0';
249 }
250 }
251 return (true);
252 }
253 }
254
255 return (false);
256 }
257
258 //////////////////////////////////////////////////////////////////////////////
259 //
260 // bool HttpCompat::lookup_param_in_semicolon_string(
261 // char *semicolon_string, int semicolon_string_len,
262 // char *param_name, char *param_val, int param_val_length)
263 //
264 // Takes a semicolon-separated string of parameters, and searches
265 // for a parameter named <param_name>, as in lookup_param_in_strlist.
266 //
267 // The routine returns true if there was a match, false otherwise.
268 // If multiple parameters will be searched for in the same string,
269 // use lookup_param_in_strlist(), so the string is not tokenized
270 // multiple times.
271 //
272 //////////////////////////////////////////////////////////////////////////////
273
274 bool
lookup_param_in_semicolon_string(const char * semicolon_string,int semicolon_string_len,const char * param_name,char * param_val,int param_val_length)275 HttpCompat::lookup_param_in_semicolon_string(const char *semicolon_string, int semicolon_string_len, const char *param_name,
276 char *param_val, int param_val_length)
277 {
278 StrList l;
279 bool result;
280
281 parse_semicolon_list(&l, semicolon_string, semicolon_string_len);
282 result = lookup_param_in_strlist(&l, param_name, param_val, param_val_length);
283 return (result);
284 }
285
286 //////////////////////////////////////////////////////////////////////////////
287 //
288 // void HttpCompat::parse_mime_type(
289 // char *mime_string, char *type, char *subtype,
290 // int type_len, int subtype_len)
291 //
292 // This routine takes a pointer to a MIME type, and decomposes it
293 // into type and subtype fields, skipping over spaces, and placing
294 // the decomposed values into <type> and <subtype>. The length
295 // fields describe the lengths of the type and subtype buffers,
296 // including the trailing NUL characters.
297 //
298 //////////////////////////////////////////////////////////////////////////////
299
300 void
parse_mime_type(const char * mime_string,char * type,char * subtype,int type_len,int subtype_len)301 HttpCompat::parse_mime_type(const char *mime_string, char *type, char *subtype, int type_len, int subtype_len)
302 {
303 const char *s, *e;
304 char *d;
305
306 *type = *subtype = '\0';
307
308 /////////////////////
309 // skip whitespace //
310 /////////////////////
311
312 for (s = mime_string; *s && ParseRules::is_ws(*s); s++) {
313 ;
314 }
315
316 ///////////////////////////////////////////////////////////////////////
317 // scan type (until NUL, out of room, comma/semicolon, space, slash) //
318 ///////////////////////////////////////////////////////////////////////
319
320 d = type;
321 e = type + type_len;
322 while (*s && (d < e - 1) && (!ParseRules::is_ws(*s)) && (*s != ';') && (*s != ',') && (*s != '/')) {
323 *d++ = *s++;
324 }
325 *d++ = '\0';
326
327 //////////////////////////////////////////////////////////////
328 // skip remainder of text and space, then slash, then space //
329 //////////////////////////////////////////////////////////////
330
331 while (*s && (*s != ';') && (*s != ',') && (*s != '/')) {
332 ++s;
333 }
334 if (*s == '/') {
335 ++s;
336 }
337 while (*s && ParseRules::is_ws(*s)) {
338 ++s;
339 }
340
341 //////////////////////////////////////////////////////////////////////////
342 // scan subtype (until NUL, out of room, comma/semicolon, space, slash) //
343 //////////////////////////////////////////////////////////////////////////
344
345 d = subtype;
346 e = subtype + subtype_len;
347 while (*s && (d < e - 1) && (!ParseRules::is_ws(*s)) && (*s != ';') && (*s != ',') && (*s != '/')) {
348 *d++ = *s++;
349 }
350 *d++ = '\0';
351 }
352
353 void
parse_mime_type_with_len(const char * mime_string,int mime_string_len,char * type,char * subtype,int type_len,int subtype_len)354 HttpCompat::parse_mime_type_with_len(const char *mime_string, int mime_string_len, char *type, char *subtype, int type_len,
355 int subtype_len)
356 {
357 const char *s, *s_toofar, *e;
358 char *d;
359
360 *type = *subtype = '\0';
361 s_toofar = mime_string + mime_string_len;
362
363 /////////////////////
364 // skip whitespace //
365 /////////////////////
366
367 for (s = mime_string; (s < s_toofar) && ParseRules::is_ws(*s); s++) {
368 ;
369 }
370
371 ///////////////////////////////////////////////////////////////////////
372 // scan type (until NUL, out of room, comma/semicolon, space, slash) //
373 ///////////////////////////////////////////////////////////////////////
374
375 d = type;
376 e = type + type_len;
377 while ((s < s_toofar) && (d < e - 1) && (!ParseRules::is_ws(*s)) && (*s != ';') && (*s != ',') && (*s != '/')) {
378 *d++ = *s++;
379 }
380 *d++ = '\0';
381
382 //////////////////////////////////////////////////////////////
383 // skip remainder of text and space, then slash, then space //
384 //////////////////////////////////////////////////////////////
385
386 while ((s < s_toofar) && (*s != ';') && (*s != ',') && (*s != '/')) {
387 ++s;
388 }
389 if ((s < s_toofar) && (*s == '/')) {
390 ++s;
391 }
392 while ((s < s_toofar) && ParseRules::is_ws(*s)) {
393 ++s;
394 }
395
396 //////////////////////////////////////////////////////////////////////////
397 // scan subtype (until NUL, out of room, comma/semicolon, space, slash) //
398 //////////////////////////////////////////////////////////////////////////
399
400 d = subtype;
401 e = subtype + subtype_len;
402 while ((s < s_toofar) && (d < e - 1) && (!ParseRules::is_ws(*s)) && (*s != ';') && (*s != ',') && (*s != '/')) {
403 *d++ = *s++;
404 }
405 *d++ = '\0';
406 }
407
408 //////////////////////////////////////////////////////////////////////////////
409 //
410 // bool HttpCompat::do_vary_header_values_match(MIMEField *hv1, MIMEField *hv2)
411 //
412 // This routine takes two HTTP header fields and determines
413 // if their values "match", as in section 14.43 of RFC2068:
414 //
415 // "When the cache receives a subsequent request whose Request-URI
416 // specifies one or more cache entries including a Vary header, the
417 // cache MUST NOT use such a cache entry to construct a response to
418 // the new request unless all of the headers named in the cached
419 // Vary header are present in the new request, and all of the stored
420 // selecting request-headers from the previous request match the
421 // corresponding headers in the new request.
422 //
423 // The selecting request-headers from two requests are defined to
424 // match if and only if the selecting request-headers in the first
425 // request can be transformed to the selecting request-headers in
426 // the second request by adding or removing linear whitespace (LWS)
427 // at places where this is allowed by the corresponding BNF, and/or
428 // combining multiple message-header fields with the same field
429 // name following the rules about message headers in section 4.2."
430 //
431 //////////////////////////////////////////////////////////////////////////////
432 bool
do_vary_header_values_match(MIMEField * hdr1,MIMEField * hdr2)433 HttpCompat::do_vary_header_values_match(MIMEField *hdr1, MIMEField *hdr2)
434 {
435 // If both headers are missing, the headers match.
436 if (!hdr1 && !hdr2) {
437 return true;
438 }
439
440 // If one header is missing, the headers do not match.
441 if (!hdr1 || !hdr2) {
442 return false;
443 }
444
445 // Make sure both headers have the same number of comma-
446 // separated elements.
447 HdrCsvIter iter1, iter2;
448 if (iter1.count_values(hdr1) != iter2.count_values(hdr2)) {
449 return false;
450 }
451
452 int hdr1_val_len, hdr2_val_len;
453 const char *hdr1_val = iter1.get_first(hdr1, &hdr1_val_len);
454 const char *hdr2_val = iter2.get_first(hdr2, &hdr2_val_len);
455
456 while (hdr1_val || hdr2_val) {
457 if (!hdr1_val || !hdr2_val || hdr1_val_len != hdr2_val_len ||
458 ParseRules::strncasecmp_eow(hdr1_val, hdr2_val, hdr1_val_len) == false) {
459 return false;
460 }
461
462 hdr1_val = iter1.get_next(&hdr1_val_len);
463 hdr2_val = iter2.get_next(&hdr2_val_len);
464 }
465
466 return true;
467 }
468
469 //////////////////////////////////////////////////////////////////////////////
470 //
471 // float HttpCompat::find_Q_param_in_strlist(StrList *strlist);
472 //
473 // Takes a StrList formed from semicolon-parsing a value, and returns
474 // the value of the Q directive, or 1.0 by default.
475 //
476 //////////////////////////////////////////////////////////////////////////////
477
478 float
find_Q_param_in_strlist(StrList * strlist)479 HttpCompat::find_Q_param_in_strlist(StrList *strlist)
480 {
481 float f, this_q;
482 char q_string[8];
483
484 this_q = 1.0;
485 if (HttpCompat::lookup_param_in_strlist(strlist, (char *)"q", q_string, sizeof(q_string))) {
486 // coverity[secure_coding]
487 if (sscanf(q_string, "%f", &f) == 1) { // parse q
488 this_q = (f < 0 ? 0 : (f > 1 ? 1 : f));
489 }
490 }
491
492 return (this_q);
493 }
494
495 //////////////////////////////////////////////////////////////////////////////
496 //
497 // float HttpCompat::match_accept_language
498 //
499 // This routine returns the resulting Q factor from matching the
500 // content language tag <lang_str> against the Accept-Language value
501 // string <acpt_str>.
502 //
503 // It also returns the index of the particular accept list piece
504 // that matches, and the length of the accept list piece that matches,
505 // in case you later want to resolve quality ties by position in the
506 // list, or by length of match. In general, you want to sort the
507 // results of this call first by chosen Q, then by matching_length
508 // (longer is better), then by matching_index (lower is better).
509 // The first matching_index value is index 1.
510 //
511 //////////////////////////////////////////////////////////////////////////////
512
513 static inline bool
does_language_range_match(const char * pattern,int pattern_len,const char * tag,int tag_len)514 does_language_range_match(const char *pattern, int pattern_len, const char *tag, int tag_len)
515 {
516 bool match;
517
518 while (pattern_len && tag_len && (ParseRules::ink_tolower(*pattern) == ParseRules::ink_tolower(*tag))) {
519 ++pattern;
520 ++tag;
521 --pattern_len;
522 --tag_len;
523 }
524
525 // matches if range equals tag, or if range is a lang prefix of tag
526 if ((((pattern_len == 0) && (tag_len == 0)) || ((pattern_len == 0) && (*tag == '-')))) {
527 match = true;
528 } else {
529 match = false;
530 }
531
532 return (match);
533 }
534
535 float
match_accept_language(const char * lang_str,int lang_len,StrList * acpt_lang_list,int * matching_length,int * matching_index,bool ignore_wildcards)536 HttpCompat::match_accept_language(const char *lang_str, int lang_len, StrList *acpt_lang_list, int *matching_length,
537 int *matching_index, bool ignore_wildcards)
538 {
539 float Q, Q_wild;
540 Str *a_value;
541
542 Q = -1; // will never be returned as -1
543 Q_wild = -1; // will never be returned as -1
544 int match_count = 0;
545 int wild_match_count = 0;
546 int longest_match_len = 0;
547
548 int index = 0;
549 int Q_index = 0;
550 int Q_wild_index = 0;
551
552 *matching_index = 0;
553 *matching_length = 0;
554
555 ///////////////////////////////////////////////////////
556 // rip the accept string into comma-separated values //
557 ///////////////////////////////////////////////////////
558 if (acpt_lang_list->count == 0) {
559 return (0.0);
560 }
561
562 ////////////////////////////////////////
563 // loop over each Accept-Language tag //
564 ////////////////////////////////////////
565 for (a_value = acpt_lang_list->head; a_value; a_value = a_value->next) {
566 ++index;
567
568 if (a_value->len == 0) {
569 continue; // blank tag
570 }
571
572 ///////////////////////////////////////////////////////////
573 // now rip the Accept-Language tag into head and Q parts //
574 ///////////////////////////////////////////////////////////
575 StrList a_param_list(false);
576 HttpCompat::parse_semicolon_list(&a_param_list, a_value->str, static_cast<int>(a_value->len));
577 if (!a_param_list.head) {
578 continue;
579 }
580
581 /////////////////////////////////////////////////////////////////////
582 // This algorithm is a bit weird --- the resulting Q factor is //
583 // the Q value corresponding to the LONGEST range field that //
584 // matched, or if none matched, then the Q value of any asterisk. //
585 // Also, if the lang value is "", meaning that no Content-Language //
586 // was specified, this document matches all accept headers. //
587 /////////////////////////////////////////////////////////////////////
588 const char *atag_str = a_param_list.head->str;
589 int atag_len = static_cast<int>(a_param_list.head->len);
590
591 float tq = HttpCompat::find_Q_param_in_strlist(&a_param_list);
592
593 if ((atag_len == 1) && (atag_str[0] == '*')) // wildcard
594 {
595 ++wild_match_count;
596 if (tq > Q_wild) {
597 Q_wild = tq;
598 Q_wild_index = index;
599 }
600 } else if (does_language_range_match(atag_str, atag_len, lang_str, lang_len)) {
601 ++match_count;
602 if (atag_len > longest_match_len) {
603 longest_match_len = atag_len;
604 Q = tq;
605 Q_index = index;
606 } else if (atag_len == longest_match_len) // if tie, pick higher Q
607 {
608 if (tq > Q) {
609 Q = tq;
610 Q_index = index;
611 }
612 }
613 }
614 }
615
616 if ((ignore_wildcards == false) && wild_match_count && !match_count) {
617 *matching_index = Q_wild_index;
618 *matching_length = 1;
619 return (Q_wild);
620 } else if (match_count > 0) // real match
621 {
622 *matching_index = Q_index;
623 *matching_length = longest_match_len;
624 return (Q);
625 } else // no match
626 {
627 *matching_index = 0;
628 *matching_length = 0;
629 return (0.0);
630 }
631 }
632
633 //////////////////////////////////////////////////////////////////////////////
634 //
635 // float HttpCompat::match_accept_charset
636 //
637 // This routine returns the resulting Q factor from matching the
638 // content language tag <lang_str> against the Accept-Language value
639 // string <acpt_str>.
640 //
641 // It also returns the index of the particular accept list piece
642 // that matches, and the length of the accept list piece that matches,
643 // in case you later want to resolve quality ties by position in the
644 // list, or by length of match. In general, you want to sort the
645 // results of this call first by chosen Q, then by matching_length
646 // (longer is better), then by matching_index (lower is better).
647 // The first matching_index value is index 1.
648 //
649 //////////////////////////////////////////////////////////////////////////////
650
651 // FIX: not implemented!
652
653 float
match_accept_charset(const char * charset_str,int charset_len,StrList * acpt_charset_list,int * matching_index,bool ignore_wildcards)654 HttpCompat::match_accept_charset(const char *charset_str, int charset_len, StrList *acpt_charset_list, int *matching_index,
655 bool ignore_wildcards)
656 {
657 float Q, Q_wild;
658 Str *a_value;
659
660 Q = -1; // will never be returned as -1
661 Q_wild = -1; // will never be returned as -1
662 int match_count = 0;
663 int wild_match_count = 0;
664
665 int index = 0;
666 int Q_index = 0;
667 int Q_wild_index = 0;
668
669 *matching_index = 0;
670
671 ///////////////////////////////////////////////////////
672 // rip the accept string into comma-separated values //
673 ///////////////////////////////////////////////////////
674 if (acpt_charset_list->count == 0) {
675 return (0.0);
676 }
677
678 ///////////////////////////////////////
679 // loop over each Accept-Charset tag //
680 ///////////////////////////////////////
681 for (a_value = acpt_charset_list->head; a_value; a_value = a_value->next) {
682 ++index;
683 if (a_value->len == 0) {
684 continue; // blank tag
685 }
686
687 //////////////////////////////////////////////////////////
688 // now rip the Accept-Charset tag into head and Q parts //
689 //////////////////////////////////////////////////////////
690 StrList a_param_list(false);
691 HttpCompat::parse_semicolon_list(&a_param_list, a_value->str, static_cast<int>(a_value->len));
692 if (!a_param_list.head) {
693 continue;
694 }
695
696 ///////////////////////////////////////////////////////////////
697 // see if the Accept-Charset tag matches the current charset //
698 ///////////////////////////////////////////////////////////////
699 const char *atag_str = a_param_list.head->str;
700 int atag_len = static_cast<int>(a_param_list.head->len);
701 float tq = HttpCompat::find_Q_param_in_strlist(&a_param_list);
702
703 if ((atag_len == 1) && (atag_str[0] == '*')) // wildcard
704 {
705 ++wild_match_count;
706 if (tq > Q_wild) {
707 Q_wild = tq;
708 Q_wild_index = index;
709 }
710 } else if ((atag_len == charset_len) && (strncasecmp(atag_str, charset_str, charset_len) == 0)) {
711 ++match_count;
712 if (tq > Q) {
713 Q = tq;
714 Q_index = index;
715 }
716 }
717 }
718
719 if ((ignore_wildcards == false) && wild_match_count && !match_count) {
720 *matching_index = Q_wild_index;
721 return (Q_wild);
722 } else if (match_count > 0) // real match
723 {
724 *matching_index = Q_index;
725 return (Q);
726 } else // no match
727 {
728 *matching_index = 0;
729 return (0.0);
730 }
731 }
732