Annotation of parser3/src/main/untaint.C, revision 1.182
1.7 paf 1: /** @file
1.8 paf 2: Parser: String class part: untaint mechanizm.
3:
1.180 moko 4: Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com)
1.177 moko 5: Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
1.103 paf 6: */
1.8 paf 7:
1.182 ! moko 8: volatile const char * IDENT_UNTAINT_C="$Id: untaint.C,v 1.181 2026/10/01 11:46:20 moko Exp $";
1.117 paf 9:
1.1 paf 10:
11: #include "pa_string.h"
12: #include "pa_hash.h"
13: #include "pa_exception.h"
1.13 paf 14: #include "pa_table.h"
1.32 paf 15: #include "pa_globals.h"
1.58 parser 16: #include "pa_dictionary.h"
1.66 parser 17: #include "pa_common.h"
1.85 paf 18: #include "pa_charset.h"
1.117 paf 19: #include "pa_request_charsets.h"
20: #include "pa_sapi.h"
1.1 paf 21:
1.117 paf 22: extern "C" { // author forgot to do that
1.174 moko 23: #include "../lib/cord/include/ec.h"
1.117 paf 24: }
1.91 paf 25:
1.117 paf 26: #include "pa_sql_connection.h"
1.91 paf 27:
1.117 paf 28: // defines
29:
30: #undef CORD_ec_append
31: // redefining to intercept flushes and implement whitespace optimization
32: // of all consequent white space chars leaving only first one
33: #define CORD_ec_append(x, c) \
34: { \
35: bool skip=false; \
36: if(optimize) switch(c) { \
1.131 paf 37: case ' ': case '\n': case '\t': \
1.117 paf 38: if(whitespace) \
39: skip=true; /*skipping subsequent*/ \
40: else \
41: whitespace=true; \
42: break; \
43: default: \
44: whitespace=false; \
45: break; \
46: } \
47: if(!skip) { \
48: if ((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ) { \
49: CORD_ec_flush_buf(x); \
50: } \
51: *((x)[0].ec_bufptr)++ = (c); \
52: } \
53: }
54:
1.140 misha 55: #define escape_fragment(action) \
1.119 paf 56: for(; fragment_length--; CORD_next(info->pos)) { \
57: char c=CORD_pos_fetch(info->pos); \
1.117 paf 58: action \
1.1 paf 59: }
1.167 moko 60:
1.117 paf 61: #define encode(need_encode_func, prefix, otherwise) \
62: if(need_encode_func(c)) { \
1.119 paf 63: CORD_ec_append(info->result, prefix); \
1.167 moko 64: to_hex(c); \
65: } else \
66: CORD_ec_append(info->result, otherwise);
67:
68: #define to_hex(c) \
69: { \
1.165 moko 70: CORD_ec_append(info->result, hex_digits[((unsigned char)c) >> 4]); \
71: CORD_ec_append(info->result, hex_digits[((unsigned char)c) & 0x0F]); \
1.167 moko 72: }
73:
1.126 paf 74: #define to_char(c) { CORD_ec_append(info->result, c); whitespace=false; }
1.168 moko 75: #define to_string(s) { CORD_ec_append_cord(info->result, (CORD)(s)); whitespace=false; }
1.167 moko 76: #define _default CORD_ec_append(info->result, c)
1.4 paf 77:
1.182 ! moko 78: /// \u2028 and \u2029 (line/paragraph separators) and the next ones in utf-8 as \u20XX: they end a js string, see bug #1023;
! 79: /// other bytes after 0xE2 are not taken here, so the loop escapes them as usual
! 80: #define escape_u20XX { \
! 81: size_t index=CORD_pos_to_index(info->pos); \
! 82: unsigned char c2; \
! 83: if(info->charsets && info->charsets->source().isUTF8() && fragment_length>=2 \
! 84: && (unsigned char)info->body->fetch(index+1)==0x80 \
! 85: && (c2=(unsigned char)info->body->fetch(index+2))>=0xA8 && c2<=0xAF){ \
! 86: to_string("\\u20"); \
! 87: to_hex(c2-0x80); \
! 88: CORD_next(info->pos); \
! 89: CORD_next(info->pos); \
! 90: fragment_length-=2; \
! 91: } else { \
! 92: _default; \
! 93: } \
! 94: }
! 95:
1.9 paf 96: inline bool need_file_encode(unsigned char c){
1.108 paf 97: // russian letters and space ENABLED
98: // encoding only these...
99: return strchr(
1.164 misha 100: "*?\"<>|"
1.108 paf 101: #ifndef WIN32
1.181 moko 102: ":" // not allowed in windows file names; '\' is a path separator everywhere since #1300
1.31 paf 103: #endif
1.143 misha 104: , c)!=0;
1.9 paf 105: }
1.143 misha 106:
1.5 paf 107: inline bool need_uri_encode(unsigned char c){
1.162 misha 108: return !(pa_isalnum(c) || strchr("_-./*", c));
1.4 paf 109: }
1.143 misha 110:
1.136 paf 111: inline bool need_regex_escape(unsigned char c){
1.142 misha 112: return strchr("\\^$.[]|()?*+{}-", c)!=0;
1.136 paf 113: }
1.1 paf 114:
1.150 misha 115: inline bool need_parser_code_escape(unsigned char c){
116: return strchr("^$;@()[]{}:#\"", c)!=0;
117: }
118:
1.1 paf 119: // String
120:
1.41 paf 121: /*
122: HTTP-header = field-name ":" [ field-value ] CRLF
123:
1.152 misha 124: field-name = token
125: field-value = *( field-content | LWS )
1.41 paf 126:
1.152 misha 127: field-content = <the OCTETs making up the field-value
1.41 paf 128: and consisting of either *TEXT or combinations
129: of token, tspecials, and quoted-string>
130:
131:
1.152 misha 132: token = 1*<any CHAR except CTLs or tspecials>
1.41 paf 133: word = token | quoted-string
1.152 misha 134: quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
135: qdtext = <any TEXT except <">>
136: quoted-pair = "\" CHAR
1.41 paf 137:
1.152 misha 138: OCTET = <any 8-bit sequence of data>
139: CHAR = <any US-ASCII character (octets 0 - 127)>
1.41 paf 140:
141: tspecials = "(" | ")" | "<" | ">" | "@"
1.152 misha 142: | "," | ";" | ":" | "\" | <">
143: | "/" | "[" | "]" | "?" | "="
144: | "{" | "}" | SP | HT
1.41 paf 145:
146: SP = <US-ASCII SP, space (32)>
147: HT = <US-ASCII HT, horizontal-tab (9)>
148:
149: LWS = [CRLF] 1*( SP | HT )
1.152 misha 150: TEXT = <any OCTET except CTLs, but including LWS>
151: CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
1.41 paf 152:
153:
154: if(strchr("()<>@,;:\\\"/[]?={} \t", *ptr))
155: */
1.117 paf 156: inline bool need_quote_http_header(const char* ptr, size_t size) {
1.41 paf 157: for(; size--; ptr++)
1.42 paf 158: if(strchr(";\\\"= \t" /* excluded ()<>@, :/ ? []{} */, *ptr))
1.41 paf 159: return true;
160: return false;
161: }
162:
1.119 paf 163: #ifndef DOXYGEN
164: struct Append_fragment_info {
165: String::Language lang;
166: String::Languages* dest_languages;
167: size_t dest_body_plan_length;
168: };
169: #endif
170: int append_fragment_optimizing(char alang, size_t asize, Append_fragment_info* info) {
171: const String::Language lang=(String::Language)(unsigned char)alang;
172: // main idea here:
173: // tainted piece would get OPTIMIZED bit from 'lang'
174: // clean piece would be marked OPTIMIZED manually
175: // pieces with determined languages [not tainted|clean] would retain theirs langs
176: info->dest_languages->append(info->dest_body_plan_length,
1.120 paf 177: lang==String::L_TAINTED?
1.119 paf 178: info->lang
1.120 paf 179: :lang==String::L_CLEAN?
180: (String::Language)(String::L_CLEAN|String::L_OPTIMIZE_BIT) // ORing with OPTIMIZED flag
1.119 paf 181: :lang,
182: asize);
183: info->dest_body_plan_length+=asize;
184:
185: return 0; // 0=continue
186: }
187: int append_fragment_nonoptimizing(char alang, size_t asize, Append_fragment_info* info) {
188: const String::Language lang=(String::Language)(unsigned char)alang;
189: // The core idea: tainted pieces got marked with context's lang
190: info->dest_languages->append(info->dest_body_plan_length,
1.120 paf 191: lang==String::L_TAINTED?
1.119 paf 192: info->lang
193: :lang,
194: asize);
195: info->dest_body_plan_length+=asize;
196:
197: return 0; // 0=continue
198: }
1.117 paf 199:
1.171 moko 200:
201: /**
202: appends to other String without language change
203: */
204:
205: String& String::append_to(String& dest) const {
206: if(is_empty())
207: return dest;
208:
209: // first: fragment infos
210: dest.langs.appendHelper(dest.body, langs, body);
211:
212: // next: letters
213: dest.body<<body;
214:
215: ASSERT_STRING_INVARIANT(dest);
216: return dest;
217: }
218:
1.91 paf 219: /**
1.117 paf 220: appends to other String,
1.91 paf 221: marking all tainted pieces of it with @a lang.
1.92 paf 222: or marking ALL pieces of it with a @a lang when @a forced to,
223: and propagating OPTIMIZE language bit.
1.117 paf 224: */
1.147 misha 225: String& String::append_to(String& dest, Language ilang, bool forced) const {
1.117 paf 226: if(is_empty())
227: return dest;
1.91 paf 228:
1.119 paf 229: // first: fragment infos
1.117 paf 230:
1.171 moko 231: if(forced) //forcing passed lang?
1.147 misha 232: dest.langs.appendHelper(dest.body, ilang, body);
233: else {
234: if(langs.opt.is_not_just_lang){
235: Append_fragment_info info={ilang, &dest.langs, dest.body.length()};
236: langs.for_each(body, ilang&L_OPTIMIZE_BIT?
237: append_fragment_optimizing
238: :append_fragment_nonoptimizing, &info);
239: } else {
240: Language lang=langs.opt.lang;
241: // see append_fragment_* for explanation
242: if(ilang&L_OPTIMIZE_BIT){
243: dest.langs.appendHelper(dest.body,
244: lang==String::L_TAINTED?
245: ilang
246: :lang==String::L_CLEAN?
247: (String::Language)(String::L_CLEAN|String::L_OPTIMIZE_BIT)
248: :lang,
249: body);
250: } else {
251: dest.langs.appendHelper(dest.body, lang==String::L_TAINTED ? ilang:lang, body);
252: }
253: }
1.91 paf 254: }
255:
1.119 paf 256: // next: letters
257: dest.body<<body;
258:
1.117 paf 259: ASSERT_STRING_INVARIANT(dest);
1.75 paf 260: return dest;
1.51 parser 261: }
262:
1.109 paf 263: /** http://www.ietf.org/rfc/rfc2047.txt
264: RFC
265: (3) As a replacement for a 'word' entity within a 'phrase', for example,
266: one that precedes an address in a From, To, or Cc header. The ABNF
267: definition for 'phrase' from RFC 822 thus becomes:
268:
269: phrase = 1*( encoded-word / word )
270:
271: In this case the set of characters that may be used in a "Q"-encoded
272: 'encoded-word' is restricted to: <upper and lower case ASCII
273: letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_"
274: (underscore, ASCII 95.)>. An 'encoded-word' that appears within a
275: 'phrase' MUST be separated from any adjacent 'word', 'text' or
276: 'special' by 'linear-white-space'.
277: ...
278: (2) The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be
279: represented as "_" (underscore, ASCII 95.). (This character may
280: not pass through some internetwork mail gateways, but its use
281: will greatly enhance readability of "Q" encoded data with mail
282: readers that do not support this encoding.) Note that the "_"
283: always represents hexadecimal 20, even if the SPACE character
284: occupies a different code position in the character set in use.
285:
286: paf: obviously,
287: without "=", or one could not differ "=E0" and "russian letter a"
288: and without "_", or in would mean 0x20
289: */
1.117 paf 290: inline bool mail_header_char_valid_within_Qencoded(char c) {
1.162 misha 291: return (pa_isalnum((unsigned char)c) || strchr("!*+-/", c));
1.109 paf 292: }
1.118 paf 293: inline bool addr_spec_soon(const char *src) {
1.124 paf 294: for(char c; (c=*src); src++)
1.118 paf 295: if(c=='<')
296: return true;
297: else if(!(c==' ' || c=='\t'))
298: return false;
299: return false;
300: }
1.117 paf 301: /**
302: RFC
303: Upper case should be used for hexadecimal digits "A" through "F"
304: The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE)
305: may be represented as "_"
306: */
307: inline bool mail_header_nonspace_char(char c) {
308: return c != 0x20;
309: }
1.125 paf 310:
1.117 paf 311: inline void ec_append(CORD_ec& result, bool& optimize, bool& whitespace, CORD_pos pos, size_t size) {
312: while(size--) {
313: CORD_ec_append(result, CORD_pos_fetch(pos));
314: CORD_next(pos);
315: }
316: }
317: inline void pa_CORD_pos_advance(CORD_pos pos, size_t n) {
318: while(true) {
1.125 paf 319: long avail=CORD_pos_chars_left(pos);
320: if(avail<=0) {
1.117 paf 321: CORD_next(pos);
322: if(!--n)
323: break;
1.125 paf 324: } else if((size_t)avail<n) {
1.117 paf 325: CORD_pos_advance(pos, avail);
326: n-=avail;
327: } else { // avail>=n
328: CORD_pos_advance(pos, n);
329: break;
330: }
331: }
332: }
333:
1.119 paf 334: #ifndef DOXYGEN
335: struct Cstr_to_string_body_block_info {
336: // input
337: String::Language lang;
338: SQL_Connection* connection;
339: const Request_charsets* charsets;
340: const String::Body* body;
341:
342: // output
343: CORD_ec result;
344:
345: // private
346: CORD_pos pos;
347: size_t fragment_begin;
348: bool whitespace;
1.138 misha 349: const char* exception;
1.119 paf 350: };
351: #endif
1.151 misha 352:
353: // @todo: replace info->body->mid with something that uses info->pos
1.148 misha 354: int cstr_to_string_body_block(String::Language to_lang, size_t fragment_length, Cstr_to_string_body_block_info* info) {
1.119 paf 355: bool& whitespace=info->whitespace;
356: size_t fragment_end=info->fragment_begin+fragment_length;
1.148 misha 357: //fprintf(stderr, "%d, %d =%s=\n", to_lang, fragment_length, info->body->cstr());
1.119 paf 358:
1.120 paf 359: bool optimize=(to_lang & String::L_OPTIMIZE_BIT)!=0;
1.119 paf 360: if(!optimize)
361: whitespace=false;
362:
1.120 paf 363: switch(to_lang & ~String::L_OPTIMIZE_BIT) {
364: case String::L_CLEAN:
365: case String::L_TAINTED:
366: case String::L_AS_IS:
1.119 paf 367: // clean piece
368:
369: // tainted piece, but undefined untaint language
370: // for VString.as_double of tainted values
371: // for ^process{body} evaluation
372:
373: // tainted, untaint language: as-is
374: ec_append(info->result, optimize, whitespace, info->pos, fragment_length);
375: break;
1.120 paf 376: case String::L_FILE_SPEC:
1.119 paf 377: // tainted, untaint language: file [name]
1.140 misha 378: {
379: escape_fragment(
1.154 misha 380: encode(need_file_encode, '_', c);
1.140 misha 381: );
382: }
1.119 paf 383: break;
1.120 paf 384: case String::L_URI:
1.169 moko 385: // tainted, untaint language: uri
386: {
387: const char *fragment_str=info->body->mid(info->fragment_begin, fragment_length).cstr();
388: // skip source [we use recoded version]
389: pa_CORD_pos_advance(info->pos, fragment_length);
390: String::C output(fragment_str, fragment_length);
391: if(info->charsets)
392: output=Charset::transcode(output, info->charsets->source(), info->charsets->client());
393:
394: char c;
395: for(const char* src=output.str; (c=*src++); )
396: encode(need_uri_encode, '%', c);
1.155 misha 397: }
398: break;
1.120 paf 399: case String::L_HTTP_HEADER:
1.119 paf 400: // tainted, untaint language: http-field-content-text
1.161 moko 401: escape_fragment(switch(c) {
402: case '\n':
403: case '\r': to_string(" "); break;
404: default: _default; break;
405: });
1.119 paf 406: break;
1.120 paf 407: case String::L_MAIL_HEADER:
1.119 paf 408: // tainted, untaint language: mail-header
409: // http://www.ietf.org/rfc/rfc2047.txt
410: if(info->charsets) {
411: size_t mail_size;
412: const char *mail_ptr=
413: info->body->mid(info->fragment_begin, mail_size=fragment_length).cstr();
414: // skip source [we use recoded version]
415: pa_CORD_pos_advance(info->pos, mail_size);
416:
417: const char* charset_name=info->charsets->mail().NAME().cstr();
418:
419: // Subject: Re: parser3: =?koi8-r?Q?=D3=C5=CD=C9=CE=C1=D2?=
420: bool to_quoted_printable=false;
421:
422: bool email=false;
423: uchar c;
1.173 moko 424: for(const char* src=mail_ptr; c=(uchar)*src; src++) {
1.137 paf 425: if(c=='\r' || c=='\n')
426: c=' ';
1.173 moko 427: if(to_quoted_printable && (c==',' || c == '"' || addr_spec_soon(src))) {
1.119 paf 428: email=c=='<';
429: to_string("?=");
430: to_quoted_printable=false;
431: }
1.130 paf 432: //RFC + An 'encoded-word' MUST NOT appear in any portion of an 'addr-spec'.
1.119 paf 433: if(!email && (
1.173 moko 434: ( !to_quoted_printable && (c & 0x80 || (c == ' ' && src == mail_ptr) ) ) // starting quote-printable-encoding on first 8bit char or leading space (issue #123)
1.166 moko 435: || ( to_quoted_printable && !mail_header_char_valid_within_Qencoded(c) )
1.119 paf 436: )) {
437: if(!to_quoted_printable) {
438: to_string("=?");
439: to_string(charset_name);
440: to_string("?Q?");
441: to_quoted_printable=true;
1.105 paf 442: }
1.119 paf 443: encode(mail_header_nonspace_char, '=', '_');
444: } else
445: to_char(c);
446: if(c=='>')
447: email=false;
448: }
449: if(to_quoted_printable) // close
450: to_string("?=");
451:
452: } else
453: ec_append(info->result, optimize, whitespace, info->pos, fragment_length);
454: break;
1.120 paf 455: case String::L_SQL:
1.119 paf 456: // tainted, untaint language: sql
457: if(info->connection) {
458: const char *fragment_str=info->body->mid(info->fragment_begin, fragment_length).cstr();
459: // skip source [we use recoded version]
460: pa_CORD_pos_advance(info->pos, fragment_length);
461:
462: to_string(info->connection->quote(fragment_str, fragment_length));
1.138 misha 463: } else {
464: info->exception="untaint in SQL language failed - no connection specified";
465: info->fragment_begin=fragment_end;
466: return 1; // stop processing. can't throw exception here
467: }
1.119 paf 468: break;
1.120 paf 469: case String::L_JS:
1.182 ! moko 470: // quotes, < > & as \xXX: safe both inside <script> and in a quoted html attribute
1.140 misha 471: escape_fragment(switch(c) {
472: case '\n': to_string("\\n"); break;
1.182 ! moko 473: case '"': to_string("\\x22"); break;
! 474: case '\'': to_string("\\x27"); break;
! 475: case '<': to_string("\\x3C"); break;
! 476: case '>': to_string("\\x3E"); break;
! 477: case '&': to_string("\\x26"); break;
1.119 paf 478: case '\\': to_string("\\\\"); break;
479: case '\xFF': to_string("\\\xFF"); break;
1.146 misha 480: case '\r': to_string("\\r"); break;
1.182 ! moko 481: case '\xE2': escape_u20XX; break;
1.133 paf 482: default: _default; break;
1.119 paf 483: });
484: break;
1.120 paf 485: case String::L_XML:
1.133 paf 486: // [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
1.140 misha 487: escape_fragment(switch(c) {
488: case '\x20':
489: case '\x9':
490: case '\xA':
1.133 paf 491: case '\xD': // this is usually removed on input
492: _default;
493: break;
1.119 paf 494: case '&': to_string("&"); break;
495: case '>': to_string(">"); break;
496: case '<': to_string("<"); break;
497: case '"': to_string("""); break;
498: case '\'': to_string("'"); break;
1.133 paf 499: default:
500: if(((unsigned char)c)<0x20) {
501: // fixing it, so that libxml would not result
502: // in fatal error parsing text
503: // though it really violates standard.
504: // to indicate there were an error
505: // replace bad char not to it's code,
506: // which we can do,
507: // but rather to '!' to show that input were actually
508: // invalid.
509: // life: shows that MSIE can somehow garble form values
510: // so that they contain these chars.
511: to_char('!');
512: } else {
513: _default;
514: }
515: break;
1.119 paf 516: });
517: break;
1.120 paf 518: case String::L_HTML:
1.140 misha 519: escape_fragment(switch(c) {
1.119 paf 520: case '&': to_string("&"); break;
521: case '>': to_string(">"); break;
522: case '<': to_string("<"); break;
523: case '"': to_string("""); break;
1.178 moko 524: case '\'': to_string("'"); break;
1.133 paf 525: default: _default; break;
1.119 paf 526: });
527: break;
1.136 paf 528: case String::L_REGEX:
529: // tainted, untaint language: regex
1.140 misha 530: escape_fragment(
1.136 paf 531: if(need_regex_escape(c))
532: to_char('\\')
533: _default;
534: );
535: break;
1.156 misha 536: case String::L_JSON:
537: // tainted, untaint language: json <http://json.org/>
538: // escape '"' '\' '/' '\n' '\t' '\r' '\b' '\f' chars and escape chars as \uXXXX if output charset != UTF-8
539: {
1.159 moko 540: if(info->charsets==NULL || info->charsets->client().isUTF8()){
1.156 misha 541: // escaping to \uXXXX is not needed
1.167 moko 542: escape_fragment(switch((unsigned char)c) {
1.156 misha 543: case '\n': to_string("\\n"); break;
544: case '"' : to_string("\\\""); break;
545: case '\\': to_string("\\\\"); break;
546: case '/' : to_string("\\/"); break;
547: case '\t': to_string("\\t"); break;
548: case '\r': to_string("\\r"); break;
549: case '\b': to_string("\\b"); break;
550: case '\f': to_string("\\f"); break;
1.182 ! moko 551: case 0xE2: escape_u20XX; break;
1.165 moko 552: default:
553: if((unsigned char)c < 0x20){
554: to_string("\\u00");
1.167 moko 555: to_hex(c);
1.165 moko 556: } else {
557: _default;
558: }
559: break;
1.156 misha 560: });
561: } else {
562: const char *fragment_str=info->body->mid(info->fragment_begin, fragment_length).cstr();
563: // skip source [we use recoded version]
564: pa_CORD_pos_advance(info->pos, fragment_length);
1.169 moko 565: to_string(Charset::escape_JSON(String::C(fragment_str, fragment_length), info->charsets->source()).str);
1.156 misha 566: }
567: }
568: break;
1.140 misha 569: case String::L_HTTP_COOKIE:
570: // tainted, untaint language: cookie (3.3.0 and higher: %uXXXX in UTF-8)
1.159 moko 571: if(info->charsets) {
1.140 misha 572: const char *fragment_str=info->body->mid(info->fragment_begin, fragment_length).cstr();
573: // skip source [we use recoded version]
574: pa_CORD_pos_advance(info->pos, fragment_length);
1.169 moko 575: to_string(Charset::escape(String::C(fragment_str, fragment_length), info->charsets->source()).str);
1.159 moko 576: } else
577: ec_append(info->result, optimize, whitespace, info->pos, fragment_length);
1.140 misha 578: break;
1.150 misha 579: case String::L_PARSER_CODE:
580: // for auto-untaint in process
581: escape_fragment(
582: if(need_parser_code_escape(c))
583: to_char('^');
584: _default;
585: );
586: break;
1.119 paf 587: default:
1.175 moko 588: SAPI::die("unknown untaint language #%d", static_cast<int>(to_lang)); // should never
1.119 paf 589: break; // never
1.117 paf 590: }
1.55 parser 591:
1.119 paf 592: info->fragment_begin=fragment_end;
593:
594: return 0; // 0=continue
595: }
596:
597:
1.148 misha 598: String::Body String::cstr_to_string_body_taint(Language lang, SQL_Connection* connection, const Request_charsets *charsets) const {
1.134 paf 599: if(is_empty())
600: return String::Body();
1.119 paf 601:
602: Cstr_to_string_body_block_info info;
603: // input
604: info.lang=lang;
605: info.connection=connection;
606: info.charsets=charsets;
607: info.body=&body;
608: // output
609: CORD_ec_init(info.result);
610: // private
611: body.set_pos(info.pos, 0);
612: info.fragment_begin=0;
1.138 misha 613: info.exception=0;
1.119 paf 614: info.whitespace=true;
615:
1.148 misha 616: cstr_to_string_body_block(lang, length(), &info);
617:
618: if(info.exception)
619: throw Exception(0,
620: 0,
621: info.exception);
622:
1.160 moko 623: return String::Body(CORD_ec_to_cord(info.result));
1.148 misha 624: }
625:
626: int cstr_to_string_body_block_untaint(char alang, size_t fragment_length, Cstr_to_string_body_block_info* info){
627: const String::Language lang=(String::Language)(unsigned char)alang;
628: // see append_fragment_* for explanation
629: if(info->lang&String::L_OPTIMIZE_BIT)
630: return cstr_to_string_body_block(
631: lang==String::L_TAINTED?
632: info->lang
633: :lang==String::L_CLEAN?
634: (String::Language)(String::L_CLEAN|String::L_OPTIMIZE_BIT)
635: :lang,
636: fragment_length, info);
637: else
638: return cstr_to_string_body_block(lang==String::L_TAINTED ? info->lang:lang, fragment_length, info);
639: }
640:
641: String::Body String::cstr_to_string_body_untaint(Language lang, SQL_Connection* connection, const Request_charsets *charsets) const {
642: if(is_empty())
643: return String::Body();
644:
645: Cstr_to_string_body_block_info info;
646: // input
647: info.lang=lang;
648: info.connection=connection;
649: info.charsets=charsets;
650: info.body=&body;
651: // output
652: CORD_ec_init(info.result);
653: // private
654: body.set_pos(info.pos, 0);
655: info.fragment_begin=0;
656: info.exception=0;
657: info.whitespace=true;
658:
659: langs.for_each(body, cstr_to_string_body_block_untaint, &info);
1.151 misha 660:
661: if(info.exception)
662: throw Exception(0,
663: 0,
664: info.exception);
665:
1.160 moko 666: return String::Body(CORD_ec_to_cord(info.result));
1.151 misha 667: }
668:
1.155 misha 669: const char* String::untaint_and_transcode_cstr(Language lang, const Request_charsets *charsets) const {
1.151 misha 670: if(charsets && &charsets->source() != &charsets->client()){
1.155 misha 671: // Note: L_URI is allready transcoded during untaint, but transcode does not affect %XX
672: return Charset::transcode(cstr_to_string_body_untaint(lang, 0, charsets), charsets->source(), charsets->client()).cstr();
673: } else
1.151 misha 674: return cstr_to_string_body_untaint(lang, 0, charsets).cstr();
1.1 paf 675: }
E-mail: