Annotation of parser3/src/main/pa_common.C, revision 1.196
1.15 paf 1: /** @file
1.16 paf 2: Parser: commonly functions.
3:
1.174 paf 4: Copyright(c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com)
1.101 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.111 paf 6: */
1.16 paf 7:
1.196 ! paf 8: static const char * const IDENT_COMMON_C="$Date: 2004/10/06 10:55:10 $";
1.1 paf 9:
10: #include "pa_common.h"
1.4 paf 11: #include "pa_exception.h"
1.154 paf 12: #include "pa_hash.h"
1.14 paf 13: #include "pa_globals.h"
1.154 paf 14: #include "pa_request_charsets.h"
15: #include "pa_charsets.h"
16:
17: #define PA_HTTP
18:
19: #ifdef PA_HTTP
1.126 paf 20: #include "pa_vstring.h"
1.154 paf 21: #include "pa_vint.h"
1.155 paf 22: #include "pa_vhash.h"
23: #include "pa_vtable.h"
1.195 paf 24: #include "pa_socks.h"
1.154 paf 25:
26: #ifdef CYGWIN
27: #define _GNU_H_WINDOWS32_SOCKETS
28: // for PASCAL
29: #include <windows.h>
30: // SOCKET
31: typedef u_int SOCKET;
32: int PASCAL closesocket(SOCKET);
33: #else
34: # if defined(WIN32)
35: # include <windows.h>
36: # else
37: # define closesocket close
38: # endif
39: #endif
1.1 paf 40:
1.126 paf 41: #else
1.154 paf 42:
43: # if defined(WIN32)
44: # include <windows.h>
45: # endif
46:
1.98 paf 47: #endif
48:
1.93 paf 49: // some maybe-undefined constants
50:
1.82 paf 51: #ifndef _O_TEXT
52: # define _O_TEXT 0
53: #endif
54: #ifndef _O_BINARY
55: # define _O_BINARY 0
1.47 paf 56: #endif
1.80 paf 57:
1.138 paf 58: #ifdef HAVE_FTRUNCATE
59: # define PA_O_TRUNC 0
60: #else
61: # ifdef _O_TRUNC
62: # define PA_O_TRUNC _O_TRUNC
63: # else
64: # error you must have either ftruncate function or _O_TRUNC bit declared
65: # endif
1.154 paf 66: #endif
1.176 paf 67:
68: # ifndef INADDR_NONE
69: # define INADDR_NONE ((ulong) -1)
70: # endif
1.154 paf 71:
72: // defines for globals
73:
74: #define FILE_STATUS_NAME "status"
75:
76: // globals
77:
78: const String file_status_name(FILE_STATUS_NAME);
79:
1.178 paf 80: // defines
1.154 paf 81:
82: #define HTTP_METHOD_NAME "method"
1.180 paf 83: #define HTTP_FORM_NAME "form"
84: #define HTTP_BODY_NAME "body"
1.154 paf 85: #define HTTP_TIMEOUT_NAME "timeout"
86: #define HTTP_HEADERS_NAME "headers"
87: #define HTTP_ANY_STATUS_NAME "any-status"
88: #define HTTP_CHARSET_NAME "charset"
1.155 paf 89: #define HTTP_TABLES_NAME "tables"
1.178 paf 90: #define HTTP_USER "user"
91: #define HTTP_PASSWORD "password"
1.154 paf 92:
93: // defines
94:
1.127 paf 95: #define DEFAULT_USER_AGENT "parser3"
96:
1.154 paf 97: // functions
1.127 paf 98:
1.154 paf 99: void fix_line_breaks(char *str, size_t& length) {
1.87 paf 100: //_asm int 3;
1.154 paf 101: const char* const eob=str+length;
102: char* dest=str;
1.72 parser 103: // fix DOS: \r\n -> \n
104: // fix Macintosh: \r -> \n
1.154 paf 105: char* bol=str;
1.137 paf 106: while(char* eol=(char*)memchr(bol, '\r', eob -bol)) {
1.72 parser 107: size_t len=eol-bol;
108: if(dest!=bol)
1.126 paf 109: memcpy(dest, bol, len);
1.72 parser 110: dest+=len;
1.126 paf 111: *dest++='\n';
1.72 parser 112:
1.126 paf 113: if(&eol[1]<eob && eol[1]=='\n') { // \r, \n = DOS
1.72 parser 114: bol=eol+2;
1.154 paf 115: length--;
1.126 paf 116: } else // \r, not \n = Macintosh
1.72 parser 117: bol=eol+1;
118: }
1.154 paf 119: // last piece without \r
1.72 parser 120: if(dest!=bol)
1.126 paf 121: memcpy(dest, bol, eob-bol);
1.154 paf 122: str[length]=0; // terminating
1.72 parser 123: }
1.18 paf 124:
1.154 paf 125: char* file_read_text(Request_charsets& charsets,
126: const String& file_spec,
127: bool fail_on_read_problem,
128: HashStringValue* params/*, HashStringValue* * out_fields*/) {
129: File_read_result file=
130: file_read(charsets, file_spec, true, params, fail_on_read_problem);
131: return file.success?file.str:0;
1.126 paf 132: }
133:
1.178 paf 134: //http request stuff
1.154 paf 135: #ifdef PA_HTTP
136:
137: #undef CRLF
138: #define CRLF "\r\n"
1.126 paf 139:
140: static bool set_addr(struct sockaddr_in *addr, const char* host, const short port){
141: memset(addr, 0, sizeof(*addr));
142: addr->sin_family=AF_INET;
143: addr->sin_port=htons(port);
144: if(host) {
1.175 paf 145: ulong packed_ip=inet_addr(host);
1.184 paf 146: if(packed_ip!=INADDR_NONE)
1.185 paf 147: memcpy(&addr->sin_addr, &packed_ip, sizeof(packed_ip));
1.184 paf 148: else {
149: struct hostent *hostIP=gethostbyname(host);
150: if(hostIP)
151: memcpy(&addr->sin_addr, hostIP->h_addr, hostIP->h_length);
152: else
153: return false;
154: }
1.126 paf 155: } else
156: addr->sin_addr.s_addr=INADDR_ANY;
157: return true;
158: }
159:
1.171 paf 160: static int http_read_response(char*& response, size_t& response_size, int sock, bool fail_on_status_ne_200){
161: response=(char*)pa_malloc_atomic(1/*terminator*/); // setting memory block type
162: response[(response_size=0)]=0;
1.154 paf 163: int result=0;
1.171 paf 164: char* EOLat=0;
1.126 paf 165: while(true) {
1.171 paf 166: char buf[MAX_STRING*10];
167: ssize_t received_size=recv(sock, buf, sizeof(buf), 0);
1.196 ! paf 168: if(received_size<=0) {
! 169: if(int no=pa_socks_errno())
! 170: throw Exception("http.connect",
! 171: 0,
! 172: "error receiving response: %s (%d)", pa_socks_strerr(no), no);
1.126 paf 173: break;
1.196 ! paf 174: }
1.171 paf 175: response=(char*)pa_realloc(response, response_size+received_size+1/*terminator*/);
176: memcpy(response+response_size, buf, received_size);
177: response_size+=received_size;
178: response[response_size]=0;
179:
1.192 paf 180: if(!result && (EOLat=strstr(response, "\n"))) { // checking status in first response
1.171 paf 181: const String status_line(pa_strdup(response, EOLat-response));
1.154 paf 182: ArrayString astatus;
183: size_t pos_after=0;
184: status_line.split(astatus, pos_after, " ");
1.182 paf 185: const String& status_code=*astatus.get(astatus.count()>1?1:0);
1.154 paf 186: result=status_code.as_int();
1.142 paf 187:
1.154 paf 188: if(fail_on_status_ne_200 && result!=200)
1.142 paf 189: throw Exception("http.status",
1.154 paf 190: &status_code,
1.142 paf 191: "invalid HTTP response status");
192: }
193: }
1.154 paf 194: if(result)
195: return result;
1.142 paf 196: else
197: throw Exception("http.response",
198: 0,
1.173 paf 199: "bad response from host - no status found (size=%u)", response_size);
1.126 paf 200: }
201:
202: /* ********************** request *************************** */
203:
204: #if defined(SIGALRM) && defined(HAVE_SIGSETJMP) && defined(HAVE_SIGLONGJMP)
1.145 paf 205: # define PA_USE_ALARM
1.126 paf 206: #endif
207:
1.145 paf 208: #ifdef PA_USE_ALARM
1.126 paf 209: static sigjmp_buf timeout_env;
210: static void timeout_handler(int sig){
211: siglongjmp(timeout_env, 1);
212: }
213: #endif
214:
1.171 paf 215: static int http_request(char*& response, size_t& response_size,
1.193 paf 216: const char* host, short port,
1.152 paf 217: const char* request,
1.196 ! paf 218: int timeout_secs,
1.152 paf 219: bool fail_on_status_ne_200) {
1.126 paf 220: if(!host)
221: throw Exception("http.host",
1.154 paf 222: 0,
1.126 paf 223: "zero hostname"); //never
224:
1.145 paf 225: #ifdef PA_USE_ALARM
1.146 paf 226: signal(SIGALRM, timeout_handler);
1.126 paf 227: #endif
228: int sock=-1;
1.145 paf 229: #ifdef PA_USE_ALARM
230: if(sigsetjmp(timeout_env, 1)) {
231: // stupid gcc [2.95.4] generated bad code
232: // which failed to handle sigsetjmp+throw: crashed inside of pre-throw code.
233: // rewritten simplier [though duplicating closesocket code]
234: if(sock>=0)
235: closesocket(sock);
236: throw Exception("http.timeout",
237: origin_string,
238: "timeout occured while retrieving document");
1.146 paf 239: return 0; // never
1.145 paf 240: } else {
1.196 ! paf 241: alarm(timeout_secs);
1.145 paf 242: #endif
1.146 paf 243: try {
244: int result;
1.126 paf 245: struct sockaddr_in dest;
1.154 paf 246:
247: if(!set_addr(&dest, host, port))
1.126 paf 248: throw Exception("http.host",
1.154 paf 249: 0,
1.127 paf 250: "can not resolve hostname \"%s\"", host);
1.126 paf 251:
1.195 paf 252: if((sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP/*0*/))<0) {
253: int no=pa_socks_errno();
1.126 paf 254: throw Exception("http.connect",
1.154 paf 255: 0,
1.195 paf 256: "can not make socket: %s (%d)", pa_socks_strerr(no), no);
257: }
1.196 ! paf 258:
! 259: #ifdef SO_DONTLINGER
! 260: int dont_linger = 0;
! 261: setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (const char *)&dont_linger, sizeof(dont_linger));
! 262: #else
! 263: // To enable SO_DONTLINGER (that is, disable SO_LINGER)
! 264: // l_onoff should be set to zero and setsockopt should be called
! 265: linger dont_linger={0,0};
! 266: setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char *)&dont_linger, sizeof(dont_linger));
! 267: #endif
! 268:
! 269: #ifndef PA_USE_ALARM
! 270: int timeout_ms=timeout_secs*1000;
! 271: setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
! 272: setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
! 273: #endif
! 274:
1.195 paf 275: if(connect(sock, (struct sockaddr *)&dest, sizeof(dest))) {
276: int no=pa_socks_errno();
1.126 paf 277: throw Exception("http.connect",
1.154 paf 278: 0,
1.195 paf 279: "can not connect to host \"%s\": %s (%d)", host, pa_socks_strerr(no), no);
280: }
1.126 paf 281: size_t request_size=strlen(request);
1.195 paf 282: if(send(sock, request, request_size, 0)!=(ssize_t)request_size) {
283: int no=pa_socks_errno();
1.126 paf 284: throw Exception("http.connect",
1.154 paf 285: 0,
1.195 paf 286: "error sending request: %s (%d)", pa_socks_strerr(no), no);
287: }
1.126 paf 288:
1.171 paf 289: result=http_read_response(response, response_size, sock, fail_on_status_ne_200);
1.142 paf 290: closesocket(sock);
1.145 paf 291: #ifdef PA_USE_ALARM
1.142 paf 292: alarm(0);
1.126 paf 293: #endif
1.147 paf 294: return result;
1.146 paf 295: } catch(...) {
1.145 paf 296: #ifdef PA_USE_ALARM
1.146 paf 297: alarm(0);
1.126 paf 298: #endif
1.146 paf 299: if(sock>=0)
300: closesocket(sock);
1.154 paf 301: rethrow;
1.146 paf 302: }
1.148 paf 303: #ifdef PA_USE_ALARM
1.126 paf 304: }
1.148 paf 305: #endif
1.126 paf 306: }
307:
1.127 paf 308: #ifndef DOXYGEN
309: struct Http_pass_header_info {
1.154 paf 310: Request_charsets* charsets;
1.127 paf 311: String* request;
312: bool user_agent_specified;
313: };
314: #endif
1.154 paf 315: static void http_pass_header(HashStringValue::key_type key,
316: HashStringValue::value_type value,
317: Http_pass_header_info *info) {
318: *info->request <<key<<": "
319: << attributed_meaning_to_string(*value, String::L_HTTP_HEADER, false)
320: << CRLF;
1.135 paf 321:
1.154 paf 322: if(String(key, String::L_TAINTED).change_case(info->charsets->source(), String::CC_UPPER)=="USER-AGENT")
323: info->user_agent_specified=true;
1.126 paf 324: }
1.154 paf 325:
326:
327: static Charset* detect_charset(Charset& source_charset, const String& content_type_value) {
1.156 paf 328: const String::Body CONTENT_TYPE_VALUE=
1.154 paf 329: content_type_value.change_case(source_charset, String::CC_UPPER);
330: // content-type: xxx/xxx; source_charset=WE-NEED-THIS
331: // content-type: xxx/xxx; source_charset="WE-NEED-THIS"
332: // content-type: xxx/xxx; source_charset="WE-NEED-THIS";
333: size_t before_charseteq_pos=CONTENT_TYPE_VALUE.pos("CHARSET=");
334: if(before_charseteq_pos!=STRING_NOT_FOUND) {
335: size_t charset_begin=before_charseteq_pos+8/*CHARSET="*/;
336: size_t open_quote_pos=CONTENT_TYPE_VALUE.pos('"', charset_begin);
337: bool quoted=open_quote_pos==charset_begin;
338: if(quoted)
339: charset_begin++; // skip opening '"'
340: size_t charset_end=CONTENT_TYPE_VALUE.length();
341: if(quoted) {
342: size_t close_quote_pos=CONTENT_TYPE_VALUE.pos('"', charset_begin);
343: if(close_quote_pos!=STRING_NOT_FOUND)
344: charset_end=close_quote_pos;
345: } else {
346: size_t delim_pos=CONTENT_TYPE_VALUE.pos(';', charset_begin);
347: if(delim_pos!=STRING_NOT_FOUND)
348: charset_end=delim_pos;
349: }
1.156 paf 350: const String::Body CHARSET_NAME_BODY=
1.154 paf 351: CONTENT_TYPE_VALUE.mid(charset_begin, charset_end);
352:
353: return &charsets.get(CHARSET_NAME_BODY);
354: }
355:
356: return 0;
357: }
358:
1.178 paf 359: static const String* basic_authorization_field(const char* user, const char* pass) {
360: if(!user&& !pass)
361: return 0;
362:
363: String combined;
364: if(user)
365: combined<<user;
366: combined<<":";
367: if(pass)
368: combined<<pass;
369:
370: String* result=new String("Basic "); *result<<pa_base64(combined.cstr(), combined.length());
371: return result;
372: }
1.154 paf 373:
1.181 paf 374: static void form_string_value2string(
375: HashStringValue::key_type key,
376: const String& value,
377: String& result)
378: {
379: result << String(key, String::L_TAINTED) << "=";
380: result.append(value, String::L_URI, true);
381: result<< "&";
382: }
383: #ifndef DOXYGEN
384: struct Form_table_value2string_info {
385: HashStringValue::key_type key;
386: String& result;
387:
388: Form_table_value2string_info(HashStringValue::key_type akey, String& aresult):
389: key(akey), result(aresult) {}
390: };
391: #endif
392: static void form_table_value2string(Table::element_type row, Form_table_value2string_info* info) {
393: form_string_value2string(info->key, *row->get(0), info->result);
394: }
1.180 paf 395: static void form_value2string(
396: HashStringValue::key_type key,
397: HashStringValue::value_type value,
398: String* result)
399: {
1.181 paf 400: if(const String* svalue=value->get_string())
401: form_string_value2string(key, *svalue, *result);
402: else if(Table* tvalue=value->get_table()) {
403: Form_table_value2string_info info(key, *result);
404: tvalue->for_each(form_table_value2string, &info);
405: } else
406: throw Exception(0,
407: new String(key, String::L_TAINTED),
408: "is %s, "HTTP_FORM_NAME" option value must either string or table", value->type());
1.180 paf 409: }
410: static const char* form2string(HashStringValue& form) {
411: String string;
412: form.for_each(form_value2string, &string);
1.181 paf 413: return string.cstr(String::L_UNSPECIFIED);
1.180 paf 414: }
1.154 paf 415: #ifndef DOXYGEN
416: struct File_read_http_result {
417: char *str; size_t length;
418: HashStringValue* headers;
419: };
420: #endif
1.194 paf 421: static void find_headers_end(char* p,
422: char*& headers_end_at,
423: char*& raw_body)
424: {
425: raw_body=p;
426: // \n\n
427: // \r\n\r\n
428: while((p=strchr(p, '\n'))) {
429: headers_end_at=++p; // \n>.<
430: if(*p=='\r') // \r\n>\r?<\n
431: p++;
432: if(*p=='\n') { // \r\n\r>\n?<
433: raw_body=p+1;
434: return;
435: }
436: }
437: headers_end_at=0;
438: }
439:
1.155 paf 440: /// @todo build .cookies field. use ^file.tables.SET-COOKIES.menu{ for now
1.154 paf 441: static File_read_http_result file_read_http(Request_charsets& charsets,
442: const String& file_spec,
1.169 paf 443: bool as_text,
444: HashStringValue *options=0) {
1.154 paf 445: File_read_http_result result;
1.126 paf 446: char host[MAX_STRING];
1.129 paf 447: const char* uri;
1.193 paf 448: short port;
1.180 paf 449: const char* method="GET"; bool method_is_get;
450: HashStringValue* form=0;
451: const char* body_cstr=0;
1.196 ! paf 452: int timeout_secs=2;
1.142 paf 453: bool fail_on_status_ne_200=true;
1.154 paf 454: Value* vheaders=0;
455: Charset *asked_remote_charset=0;
1.178 paf 456: const char* user_cstr=0;
457: const char* password_cstr=0;
1.126 paf 458:
1.127 paf 459: if(options) {
460: int valid_options=0;
1.177 paf 461: if(Value* vmethod=options->get(HTTP_METHOD_NAME)) {
1.127 paf 462: valid_options++;
1.154 paf 463: method=vmethod->as_string().cstr();
1.181 paf 464: }
465: if(Value* vform=options->get(HTTP_FORM_NAME)) {
1.180 paf 466: valid_options++;
467: form=vform->get_hash();
1.181 paf 468: }
469: if(Value* vbody=options->get(HTTP_BODY_NAME)) {
1.180 paf 470: valid_options++;
471: body_cstr=vbody->as_string().cstr(String::L_UNSPECIFIED);
1.181 paf 472: }
473: if(Value* vtimeout=options->get(HTTP_TIMEOUT_NAME)) {
1.127 paf 474: valid_options++;
1.196 ! paf 475: timeout_secs=vtimeout->as_int();
1.181 paf 476: }
477: if((vheaders=options->get(HTTP_HEADERS_NAME))) {
1.127 paf 478: valid_options++;
1.181 paf 479: }
480: if(Value* vany_status=options->get(HTTP_ANY_STATUS_NAME)) {
1.142 paf 481: valid_options++;
482: fail_on_status_ne_200=!vany_status->as_bool();
1.181 paf 483: }
484: if(Value* vcharset_name=options->get(HTTP_CHARSET_NAME)) {
1.154 paf 485: valid_options++;
486: asked_remote_charset=&::charsets.get(vcharset_name->as_string().
487: change_case(charsets.source(), String::CC_UPPER));
1.181 paf 488: }
489: if(Value* vuser=options->get(HTTP_USER)) {
1.178 paf 490: valid_options++;
491: user_cstr=vuser->as_string().cstr();
1.181 paf 492: }
493: if(Value* vpassword=options->get(HTTP_PASSWORD)) {
1.178 paf 494: valid_options++;
495: password_cstr=vpassword->as_string().cstr();
496: }
1.142 paf 497:
1.154 paf 498: if(valid_options!=options->count())
1.127 paf 499: throw Exception("parser.runtime",
500: 0,
501: "invalid option passed");
1.154 paf 502: }
503: if(!asked_remote_charset) // defaulting to $request:charset
504: asked_remote_charset=&charsets.source();
505:
1.180 paf 506: method_is_get=strcmp(method, "GET")==0;
507: if(method_is_get && body_cstr)
508: throw Exception("parser.runtime",
509: 0,
510: "you can not use $."HTTP_BODY_NAME" option with method GET");
511:
1.154 paf 512: //preparing request
513: String& connect_string=*new String;
514: // not in ^sql{... L_SQL ...} spirit, but closer to ^file::load one
515: connect_string.append(file_spec, String::L_URI); // tainted pieces -> URI pieces
516:
1.180 paf 517: String request_head_and_body;
1.154 paf 518: {
519: // influence URLencoding of tainted pieces to String::L_URI lang
520: Temp_client_charset temp(charsets, *asked_remote_charset);
521:
522: const char* connect_string_cstr=connect_string.cstr(String::L_UNSPECIFIED);
1.126 paf 523:
1.154 paf 524: const char* current=connect_string_cstr;
525: if(strncmp(current, "http://", 7)!=0)
526: throw Exception(0,
527: &connect_string,
528: "does not start with http://"); //never
529: current+=7;
530:
531: strncpy(host, current, sizeof(host)-1); host[sizeof(host)-1]=0;
532: char* host_uri=lsplit(host, '/');
533: uri=host_uri?current+(host_uri-1-host):"/";
534: char* port_cstr=lsplit(host, ':');
535: char* error_pos=0;
1.193 paf 536: port=port_cstr?(short)strtol(port_cstr, &error_pos, 0):80;
1.154 paf 537:
1.180 paf 538: if(strchr(uri, '?') && form)
539: throw Exception("parser.runtime",
540: 0,
541: "use either uri with ?params or $."HTTP_FORM_NAME" option");
542:
543: //making request head
544: String head;
545: head << method;
546: head << " " << uri;
547: if(form)
548: if(method_is_get)
1.181 paf 549: head << "?" << form2string(*form);
1.180 paf 550: head <<" HTTP/1.0" CRLF
1.154 paf 551: "host: "<< host << CRLF;
1.181 paf 552: if(form && !method_is_get) {
553: head << "content-type: application/x-www-form-urlencoded" CRLF;
554: body_cstr = form2string(*form);
555: }
1.178 paf 556:
1.179 paf 557: // http://www.ietf.org/rfc/rfc2617.txt
1.178 paf 558: if(const String* authorization_field_value=basic_authorization_field(user_cstr, password_cstr))
1.180 paf 559: head<<"authorization: "<<*authorization_field_value<<CRLF;
1.178 paf 560:
1.154 paf 561: bool user_agent_specified=false;
562: if(vheaders && !vheaders->is_string()) { // allow empty
563: if(HashStringValue *headers=vheaders->get_hash()) {
1.180 paf 564: Http_pass_header_info info={&charsets, &head, false};
1.154 paf 565: headers->for_each(http_pass_header, &info);
566: user_agent_specified=info.user_agent_specified;
567: } else
568: throw Exception("parser.runtime",
569: &connect_string,
570: "headers param must be hash");
571: };
572: if(!user_agent_specified) // defaulting
1.180 paf 573: head << "user-agent: " DEFAULT_USER_AGENT CRLF;
574:
575: if(body_cstr) {
576: // recode those pieces which are not in String::L_URI lang
577: // [those violating HTTP standard, but widly used]
578: body_cstr=Charset::transcode(
579: String::C(body_cstr, strlen(body_cstr)),
580: charsets.source(),
581: *asked_remote_charset);
1.181 paf 582:
583: head << "content-length: " << format(strlen(body_cstr), "%u") << CRLF;
1.180 paf 584: }
1.154 paf 585:
1.181 paf 586: const char* head_cstr=head.cstr(String::L_UNSPECIFIED);
587:
588: // recode those pieces which are not in String::L_URI lang
589: // [those violating HTTP standard, but widly used]
590: head_cstr=Charset::transcode(
591: String::C(head_cstr, strlen(head_cstr)),
592: charsets.source(),
593: *asked_remote_charset);
1.180 paf 594:
1.181 paf 595: // head + end of header
596: request_head_and_body << head_cstr << CRLF;
1.180 paf 597: // body
598: if(body_cstr)
599: request_head_and_body << body_cstr;
1.154 paf 600: }
1.126 paf 601:
602: //sending request
1.171 paf 603: char* response;
604: size_t response_size;
605: int status_code=http_request(response, response_size,
1.180 paf 606: host, port, request_head_and_body.cstr(),
1.196 ! paf 607: timeout_secs, fail_on_status_ne_200);
1.126 paf 608:
609: //processing results
1.171 paf 610: char* raw_body; size_t raw_body_size;
1.194 paf 611: char* headers_end_at;
612: find_headers_end(response,
613: headers_end_at,
614: raw_body);
1.191 paf 615: raw_body_size=response_size-(raw_body-response);
1.171 paf 616:
1.154 paf 617: result.headers=new HashStringValue;
1.155 paf 618: VHash* vtables=new VHash;
1.177 paf 619: result.headers->put(HTTP_TABLES_NAME, vtables);
1.194 paf 620: Charset* real_remote_charset=0; // undetected, yet
621:
622: if(headers_end_at) {
623: *headers_end_at=0;
624: const String header_block(String::C(response, headers_end_at-response), true);
625:
626: ArrayString aheaders;
627: HashStringValue& tables=vtables->hash();
1.155 paf 628:
1.194 paf 629: size_t pos_after=0;
630: header_block.split(aheaders, pos_after, "\n");
631:
632: //processing headers
633: size_t aheaders_count=aheaders.count();
634: for(size_t i=1; i<aheaders_count; i++) {
635: const String& line=*aheaders.get(i);
636: size_t pos=line.pos(':');
637: if(pos==STRING_NOT_FOUND || pos<1)
638: throw Exception("http.response",
639: &connect_string,
640: "bad response from host - bad header \"%s\"", line.cstr());
641: const String::Body HEADER_NAME=
642: line.mid(0, pos).change_case(charsets.source(), String::CC_UPPER);
643: const String& header_value=line.mid(pos+1, line.length()).trim(String::TRIM_BOTH, " \t\r");
644: if(as_text && HEADER_NAME=="CONTENT-TYPE")
645: real_remote_charset=detect_charset(charsets.source(), header_value);
646:
647: // tables
648: {
649: Value *valready=(Value *)tables.get(HEADER_NAME);
650: bool existed=valready!=0;
651: Table *table;
652: if(existed) {
653: // second+ appearence
654: table=valready->get_table();
655: } else {
656: // first appearence
657: Table::columns_type columns =new ArrayString(1);
658: *columns+=new String("value");
659: table=new Table(columns);
660: }
661: // this string becomes next row
662: ArrayString& row=*new ArrayString(1);
663: row+=&header_value;
664: *table+=&row;
665: // not existed before? add it
666: if(!existed)
667: tables.put(HEADER_NAME, new VTable(table));
1.155 paf 668: }
1.194 paf 669:
670: result.headers->put(HEADER_NAME, new VString(header_value));
1.155 paf 671: }
1.126 paf 672: }
673:
674: // output response
1.171 paf 675: String::C real_body=String::C(raw_body, raw_body_size);
1.192 paf 676: if(as_text && raw_body_size) { // must be checked because transcode returns CONST string in case length==0, which contradicts hacking few lines below
677: // defaulting to used-asked charset [it's never empty!]
678: if(!real_remote_charset)
679: real_remote_charset=asked_remote_charset;
1.169 paf 680: real_body=Charset::transcode(real_body, *real_remote_charset, charsets.source());
1.192 paf 681: }
1.154 paf 682:
683: result.str=const_cast<char *>(real_body.str); // hacking a little
684: result.length=real_body.length;
685: result.headers->put(file_status_name, new VInt(status_code));
686: return result;
1.34 paf 687: }
1.123 paf 688:
1.154 paf 689: #endif
690:
1.123 paf 691: #ifndef DOXYGEN
692: struct File_read_action_info {
1.154 paf 693: char **data; size_t *data_size;
1.188 paf 694: char* buf; size_t offset; size_t count;
1.126 paf 695: };
1.123 paf 696: #endif
1.154 paf 697: static void file_read_action(
698: struct stat& finfo,
699: int f,
1.166 paf 700: const String& file_spec, const char* /*fname*/, bool as_text,
1.154 paf 701: void *context) {
1.126 paf 702: File_read_action_info& info=*static_cast<File_read_action_info *>(context);
1.188 paf 703: size_t to_read_size=info.count;
704: if(!to_read_size)
705: to_read_size=(size_t)finfo.st_size;
706: assert( !(info.buf && as_text) );
707: if(to_read_size) {
708: if(info.offset)
709: lseek(f, info.offset, SEEK_SET);
710: *info.data=info.buf
711: ? info.buf
712: : new(PointerFreeGC) char[to_read_size+(as_text?1:0)];
1.126 paf 713: *info.data_size=(size_t)read(f, *info.data, to_read_size);
1.123 paf 714:
715: if(ssize_t(*info.data_size)<0 || *info.data_size>to_read_size)
1.126 paf 716: throw Exception(0,
1.123 paf 717: &file_spec,
1.173 paf 718: "read failed: actually read %u bytes count not in [0..%u] valid range",
1.126 paf 719: *info.data_size, to_read_size);
1.123 paf 720: } else { // empty file
721: if(as_text) {
1.154 paf 722: *info.data=new(PointerFreeGC) char[1];
1.123 paf 723: *(char*)(*info.data)=0;
724: } else
725: *info.data=0;
726: *info.data_size=0;
727: return;
728: }
1.126 paf 729: }
1.154 paf 730: File_read_result file_read(Request_charsets& charsets, const String& file_spec,
731: bool as_text, HashStringValue *params,
1.188 paf 732: bool fail_on_read_problem,
733: char* buf, size_t offset, size_t count) {
1.167 paf 734: File_read_result result={false, 0, 0, 0};
1.154 paf 735: #ifdef PA_HTTP
736: if(file_spec.starts_with("http://")) {
1.126 paf 737: // fail on read problem
1.169 paf 738: File_read_http_result http=file_read_http(charsets, file_spec, as_text, params);
1.154 paf 739: result.success=true;
740: result.str=http.str;
741: result.length=http.length;
742: result.headers=http.headers;
1.126 paf 743: } else {
1.154 paf 744: #endif
1.161 paf 745: if(params && params->count())
746: throw Exception("parser.runtime",
747: 0,
748: "invalid option passed");
749:
1.188 paf 750: File_read_action_info info={&result.str, &result.length,
751: buf, offset, count};
1.154 paf 752: result.success=file_read_action_under_lock(file_spec,
1.126 paf 753: "read", file_read_action, &info,
754: as_text, fail_on_read_problem);
1.154 paf 755: #ifdef PA_HTTP
1.126 paf 756: }
1.154 paf 757: #endif
1.123 paf 758:
1.154 paf 759: if(result.success && as_text) {
1.131 paf 760: // UTF-8 signature: EF BB BF
1.154 paf 761: if(result.length>=3) {
762: char *in=(char *)result.str;
1.159 paf 763: if(strncmp(in, "\xEF\xBB\xBF", 3)==0) {
1.154 paf 764: result.str=in+3; result.length-=3;// skip prefix
1.131 paf 765: }
766: }
767:
1.154 paf 768: fix_line_breaks((char *)(result.str), result.length);
1.123 paf 769: }
1.126 paf 770:
771: return result;
1.123 paf 772: }
773:
1.154 paf 774: #ifdef PA_SAFE_MODE
775: void check_safe_mode(struct stat finfo, const String& file_spec, const char* fname) {
776: if(finfo.st_uid/*foreign?*/!=geteuid()
777: && finfo.st_gid/*foreign?*/!=getegid())
778: throw Exception("parser.runtime",
779: &file_spec,
780: "parser is in safe mode: "
781: "reading files of foreign group and user disabled "
782: "[recompile parser with --disable-safe-mode configure option], "
783: "actual filename '%s', "
784: "fuid(%d)!=euid(%d) or fgid(%d)!=egid(%d)",
785: fname,
786: finfo.st_uid, geteuid(),
787: finfo.st_gid, getegid());
788: }
789: #endif
1.149 paf 790:
1.154 paf 791: bool file_read_action_under_lock(const String& file_spec,
1.126 paf 792: const char* action_name, File_read_action action, void *context,
793: bool as_text,
1.123 paf 794: bool fail_on_read_problem) {
1.154 paf 795: const char* fname=file_spec.cstr(String::L_FILE_SPEC);
1.33 paf 796: int f;
797:
798: // first open, next stat:
1.45 paf 799: // directory update of NTFS hard links performed on open.
1.33 paf 800: // ex:
801: // a.html:^test[] and b.html hardlink to a.html
802: // user inserts ! before ^test in a.html
1.126 paf 803: // directory entry of b.html in NTFS not updated at once,
1.35 paf 804: // they delay update till open, so we would receive "!^test[" string
805: // if would do stat, next open.
1.123 paf 806: // later: it seems, even this does not help sometimes
1.98 paf 807: if((f=open(fname, O_RDONLY|(as_text?_O_TEXT:_O_BINARY)))>=0) {
1.123 paf 808: try {
1.162 paf 809: if(pa_lock_shared_blocking(f)!=0)
1.126 paf 810: throw Exception("file.lock",
1.123 paf 811: &file_spec,
812: "shared lock failed: %s (%d), actual filename '%s'",
1.154 paf 813: strerror(errno), errno, fname);
1.123 paf 814:
1.124 paf 815: struct stat finfo;
816: if(stat(fname, &finfo)!=0)
817: throw Exception("file.missing", // hardly possible: we just opened it OK
818: &file_spec,
819: "stat failed: %s (%d), actual filename '%s'",
1.154 paf 820: strerror(errno), errno, fname);
1.124 paf 821:
1.140 paf 822: #ifdef PA_SAFE_MODE
1.149 paf 823: check_safe_mode(finfo, file_spec, fname);
1.105 paf 824: #endif
1.32 paf 825:
1.154 paf 826: action(finfo, f, file_spec, fname, as_text, context);
1.123 paf 827: } catch(...) {
1.162 paf 828: pa_unlock(f);close(f);
1.123 paf 829: if(fail_on_read_problem)
1.154 paf 830: rethrow;
1.123 paf 831: return false;
832: }
1.87 paf 833:
1.162 paf 834: pa_unlock(f);close(f);
1.72 parser 835: return true;
1.118 paf 836: } else {
837: if(fail_on_read_problem)
1.126 paf 838: throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0,
1.118 paf 839: &file_spec,
1.123 paf 840: "%s failed: %s (%d), actual filename '%s'",
1.154 paf 841: action_name, strerror(errno), errno, fname);
1.118 paf 842: return false;
843: }
1.8 paf 844: }
845:
1.63 parser 846: static void create_dir_for_file(const String& file_spec) {
847: size_t pos_after=1;
1.154 paf 848: size_t pos_before;
849: while((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND) {
850: mkdir(file_spec.mid(0, pos_before).cstr(String::L_FILE_SPEC), 0775);
1.63 parser 851: pos_after=pos_before+1;
852: }
853: }
854:
1.98 paf 855: bool file_write_action_under_lock(
1.28 paf 856: const String& file_spec,
1.126 paf 857: const char* action_name, File_write_action action, void *context,
858: bool as_text,
859: bool do_append,
860: bool do_block,
1.110 paf 861: bool fail_on_lock_problem) {
1.154 paf 862: const char* fname=file_spec.cstr(String::L_FILE_SPEC);
1.28 paf 863: int f;
1.80 paf 864: if(access(fname, W_OK)!=0) // no
1.126 paf 865: create_dir_for_file(file_spec);
1.50 paf 866:
1.80 paf 867: if((f=open(fname,
868: O_CREAT|O_RDWR
869: |(as_text?_O_TEXT:_O_BINARY)
1.138 paf 870: |(do_append?O_APPEND:PA_O_TRUNC), 0664))>=0) {
1.162 paf 871: if((do_block?pa_lock_exclusive_blocking(f):pa_lock_exclusive_nonblocking(f))!=0) {
1.126 paf 872: Exception e("file.lock",
1.110 paf 873: &file_spec,
874: "shared lock failed: %s (%d), actual filename '%s'",
1.154 paf 875: strerror(errno), errno, fname);
1.126 paf 876: close(f);
1.110 paf 877: if(fail_on_lock_problem)
878: throw e;
1.98 paf 879: return false;
880: }
1.96 paf 881:
1.158 paf 882: try {
1.126 paf 883: action(f, context);
1.158 paf 884: } catch(...) {
1.138 paf 885: #ifdef HAVE_FTRUNCATE
1.104 paf 886: if(!do_append)
1.125 paf 887: ftruncate(f, lseek(f, 0, SEEK_CUR)); // one can not use O_TRUNC, read lower
1.138 paf 888: #endif
1.162 paf 889: pa_unlock(f);close(f);
1.154 paf 890: rethrow;
1.158 paf 891: }
1.80 paf 892:
1.138 paf 893: #ifdef HAVE_FTRUNCATE
1.104 paf 894: if(!do_append)
1.125 paf 895: ftruncate(f, lseek(f, 0, SEEK_CUR)); // O_TRUNC truncates even exclusevely write-locked file [thanks to Igor Milyakov <virtan@rotabanner.com> for discovering]
1.138 paf 896: #endif
1.162 paf 897: pa_unlock(f);close(f);
1.98 paf 898: return true;
1.80 paf 899: } else
1.126 paf 900: throw Exception(errno==EACCES?"file.access":0,
1.80 paf 901: &file_spec,
1.96 paf 902: "%s failed: %s (%d), actual filename '%s'",
1.154 paf 903: action_name, strerror(errno), errno, fname);
1.96 paf 904: // here should be nothing, see rethrow above
905: }
906:
907: #ifndef DOXYGEN
908: struct File_write_action_info {
1.154 paf 909: const char* str; size_t length;
1.126 paf 910: };
1.96 paf 911: #endif
912: static void file_write_action(int f, void *context) {
1.126 paf 913: File_write_action_info& info=*static_cast<File_write_action_info *>(context);
1.154 paf 914: if(info.length) {
915: int written=write(f, info.str, info.length);
1.116 paf 916: if(written<0)
1.126 paf 917: throw Exception(0,
918: 0,
919: "write failed: %s (%d)", strerror(errno), errno);
1.113 paf 920: }
1.96 paf 921: }
922: void file_write(
923: const String& file_spec,
1.154 paf 924: const char* data, size_t size,
1.126 paf 925: bool as_text,
1.96 paf 926: bool do_append) {
1.126 paf 927: File_write_action_info info={data, size};
1.98 paf 928: file_write_action_under_lock(
1.154 paf 929: file_spec,
930: "write", file_write_action, &info,
931: as_text,
932: do_append);
1.30 paf 933: }
934:
1.63 parser 935: // throws nothing! [this is required in file_move & file_delete]
1.50 paf 936: static void rmdir(const String& file_spec, size_t pos_after) {
1.154 paf 937: size_t pos_before;
938: if((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND)
1.126 paf 939: rmdir(file_spec, pos_before+1);
1.50 paf 940:
1.154 paf 941: rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::L_FILE_SPEC));
1.50 paf 942: }
1.164 paf 943: bool file_delete(const String& file_spec, bool fail_on_problem) {
1.154 paf 944: const char* fname=file_spec.cstr(String::L_FILE_SPEC);
1.54 parser 945: if(unlink(fname)!=0)
1.164 paf 946: if(fail_on_problem)
1.126 paf 947: throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0,
1.93 paf 948: &file_spec,
949: "unlink failed: %s (%d), actual filename '%s'",
1.154 paf 950: strerror(errno), errno, fname);
1.93 paf 951: else
952: return false;
1.50 paf 953:
1.126 paf 954: rmdir(file_spec, 1);
1.93 paf 955: return true;
1.60 parser 956: }
1.95 paf 957: void file_move(const String& old_spec, const String& new_spec) {
1.154 paf 958: const char* old_spec_cstr=old_spec.cstr(String::L_FILE_SPEC);
959: const char* new_spec_cstr=new_spec.cstr(String::L_FILE_SPEC);
1.63 parser 960:
1.126 paf 961: create_dir_for_file(new_spec);
1.63 parser 962:
1.60 parser 963: if(rename(old_spec_cstr, new_spec_cstr)!=0)
1.126 paf 964: throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0,
1.60 parser 965: &old_spec,
966: "rename failed: %s (%d), actual filename '%s' to '%s'",
1.154 paf 967: strerror(errno), errno, old_spec_cstr, new_spec_cstr);
1.63 parser 968:
1.126 paf 969: rmdir(old_spec, 1);
1.31 paf 970: }
971:
1.51 paf 972:
1.126 paf 973: bool entry_exists(const char* fname, struct stat *afinfo) {
1.118 paf 974: struct stat lfinfo;
975: bool result=stat(fname, &lfinfo)==0;
976: if(afinfo)
977: *afinfo=lfinfo;
978: return result;
1.119 paf 979: }
980:
981: bool entry_exists(const String& file_spec) {
1.154 paf 982: const char* fname=file_spec.cstr(String::L_FILE_SPEC);
1.126 paf 983: return entry_exists(fname, 0);
1.118 paf 984: }
985:
1.51 paf 986: static bool entry_readable(const String& file_spec, bool need_dir) {
1.154 paf 987: char* fname=file_spec.cstrm(String::L_FILE_SPEC);
1.120 paf 988: if(need_dir) {
1.126 paf 989: size_t size=strlen(fname);
1.120 paf 990: while(size) {
1.126 paf 991: char c=fname[size-1];
1.120 paf 992: if(c=='/' || c=='\\')
993: fname[--size]=0;
994: else
995: break;
996: }
997: }
1.51 paf 998: struct stat finfo;
1.118 paf 999: if(access(fname, R_OK)==0 && entry_exists(fname, &finfo)) {
1.109 paf 1000: bool is_dir=(finfo.st_mode&S_IFDIR) != 0;
1.51 paf 1001: return is_dir==need_dir;
1002: }
1003: return false;
1004: }
1.31 paf 1005: bool file_readable(const String& file_spec) {
1.126 paf 1006: return entry_readable(file_spec, false);
1.51 paf 1007: }
1008: bool dir_readable(const String& file_spec) {
1.126 paf 1009: return entry_readable(file_spec, true);
1.65 parser 1010: }
1.154 paf 1011: const String* file_readable(const String& path, const String& name) {
1012: String& result=*new String(path);
1013: result << "/";
1014: result << name;
1015: return file_readable(result)?&result:0;
1.43 paf 1016: }
1017: bool file_executable(const String& file_spec) {
1.154 paf 1018: return access(file_spec.cstr(String::L_FILE_SPEC), X_OK)==0;
1.44 paf 1019: }
1020:
1.64 parser 1021: bool file_stat(const String& file_spec,
1.58 parser 1022: size_t& rsize,
1.126 paf 1023: time_t& ratime,
1024: time_t& rmtime,
1025: time_t& rctime,
1.64 parser 1026: bool fail_on_read_problem) {
1.154 paf 1027: const char* fname=file_spec.cstr(String::L_FILE_SPEC);
1028: struct stat finfo;
1.44 paf 1029: if(stat(fname, &finfo)!=0)
1.64 parser 1030: if(fail_on_read_problem)
1.126 paf 1031: throw Exception("file.missing",
1.67 parser 1032: &file_spec,
1033: "getting file size failed: %s (%d), real filename '%s'",
1.154 paf 1034: strerror(errno), errno, fname);
1.64 parser 1035: else
1036: return false;
1.58 parser 1037: rsize=finfo.st_size;
1038: ratime=finfo.st_atime;
1039: rmtime=finfo.st_mtime;
1040: rctime=finfo.st_ctime;
1.64 parser 1041: return true;
1.18 paf 1042: }
1043:
1.126 paf 1044: char* getrow(char* *row_ref, char delim) {
1045: char* result=*row_ref;
1.8 paf 1046: if(result) {
1.126 paf 1047: *row_ref=strchr(result, delim);
1.8 paf 1048: if(*row_ref)
1049: *((*row_ref)++)=0;
1050: else if(!*result)
1051: return 0;
1052: }
1053: return result;
1054: }
1055:
1.126 paf 1056: char* lsplit(char* string, char delim) {
1.23 paf 1057: if(string) {
1.126 paf 1058: char* v=strchr(string, delim);
1.8 paf 1059: if(v) {
1060: *v=0;
1061: return v+1;
1062: }
1063: }
1064: return 0;
1065: }
1066:
1.126 paf 1067: char* lsplit(char* *string_ref, char delim) {
1068: char* result=*string_ref;
1069: char* next=lsplit(*string_ref, delim);
1.8 paf 1070: *string_ref=next;
1071: return result;
1.9 paf 1072: }
1073:
1.126 paf 1074: char* rsplit(char* string, char delim) {
1.18 paf 1075: if(string) {
1.126 paf 1076: char* v=strrchr(string, delim);
1.18 paf 1077: if(v) {
1.9 paf 1078: *v=0;
1079: return v+1;
1080: }
1081: }
1082: return NULL;
1.10 paf 1083: }
1084:
1.37 paf 1085: /// @todo less stupid type detection
1.154 paf 1086: const char* format(double value, char* fmt) {
1.126 paf 1087: char local_buf[MAX_NUMBER];
1.108 paf 1088: size_t size;
1089:
1.10 paf 1090: if(fmt)
1091: if(strpbrk(fmt, "diouxX"))
1092: if(strpbrk(fmt, "ouxX"))
1.126 paf 1093: size=snprintf(local_buf, sizeof(local_buf), fmt, (uint)value);
1.10 paf 1094: else
1.126 paf 1095: size=snprintf(local_buf, sizeof(local_buf), fmt, (int)value);
1.10 paf 1096: else
1.126 paf 1097: size=snprintf(local_buf, sizeof(local_buf), fmt, value);
1.10 paf 1098: else
1.126 paf 1099: size=snprintf(local_buf, sizeof(local_buf), "%d", (int)value);
1.10 paf 1100:
1.154 paf 1101: return pa_strdup(local_buf, size);
1.12 paf 1102: }
1103:
1.36 paf 1104: size_t stdout_write(const void *buf, size_t size) {
1.12 paf 1105: #ifdef WIN32
1.187 paf 1106: size_t to_write = size;
1.12 paf 1107: do{
1.154 paf 1108: int chunk_written=fwrite(buf, 1, min((size_t)8*0x400, size), stdout);
1.12 paf 1109: if(chunk_written<=0)
1110: break;
1111: size-=chunk_written;
1.36 paf 1112: buf=((const char*)buf)+chunk_written;
1.126 paf 1113: } while(size>0);
1.12 paf 1114:
1.187 paf 1115: return to_write-size;
1.12 paf 1116: #else
1.126 paf 1117: return fwrite(buf, 1, size, stdout);
1.12 paf 1118: #endif
1.2 paf 1119: }
1.14 paf 1120:
1.154 paf 1121: char* unescape_chars(const char* cp, int len) {
1122: char* s=new(PointerFreeGC) char[len + 1];
1.14 paf 1123: enum EscapeState {
1.33 paf 1124: EscapeRest,
1125: EscapeFirst,
1.14 paf 1126: EscapeSecond
1127: } escapeState=EscapeRest;
1.193 paf 1128: uchar escapedValue=0;
1.14 paf 1129: int srcPos=0;
1130: int dstPos=0;
1131: while(srcPos < len) {
1.193 paf 1132: uchar ch=(uchar)cp[srcPos];
1.14 paf 1133: switch(escapeState) {
1134: case EscapeRest:
1135: if(ch=='%') {
1136: escapeState=EscapeFirst;
1137: } else if(ch=='+') {
1.126 paf 1138: s[dstPos++]=' ';
1.14 paf 1139: } else {
1140: s[dstPos++]=ch;
1141: }
1142: break;
1143: case EscapeFirst:
1.193 paf 1144: escapedValue=(uchar)(hex_value[ch] << 4);
1.14 paf 1145: escapeState=EscapeSecond;
1146: break;
1147: case EscapeSecond:
1.126 paf 1148: escapedValue +=hex_value[ch];
1.14 paf 1149: s[dstPos++]=escapedValue;
1150: escapeState=EscapeRest;
1151: break;
1152: }
1.126 paf 1153: srcPos++;
1.14 paf 1154: }
1155: s[dstPos]=0;
1156: return s;
1.24 paf 1157: }
1158:
1159: #ifdef WIN32
1.126 paf 1160: void back_slashes_to_slashes(char* s) {
1.24 paf 1161: if(s)
1162: for(; *s; s++)
1163: if(*s=='\\')
1.126 paf 1164: *s='/';
1.24 paf 1165: }
1.42 paf 1166: /*
1.126 paf 1167: void slashes_to_back_slashes(char* s) {
1.42 paf 1168: if(s)
1169: for(; *s; s++)
1170: if(*s=='/')
1.126 paf 1171: *s='\\';
1.42 paf 1172: }
1173: */
1.24 paf 1174: #endif
1.41 paf 1175:
1.126 paf 1176: bool StrEqNc(const char* s1, const char* s2, bool strict) {
1.41 paf 1177: while(true) {
1178: if(!(*s1)) {
1179: if(!(*s2))
1180: return true;
1181: else
1182: return !strict;
1183: } else if(!(*s2))
1184: return !strict;
1.189 paf 1185: if(isalpha((unsigned char)*s1)) {
1.190 paf 1186: if(tolower((unsigned char)*s1) !=tolower((unsigned char)*s2))
1.41 paf 1187: return false;
1188: } else if((*s1) !=(*s2))
1189: return false;
1.126 paf 1190: s1++;
1191: s2++;
1.41 paf 1192: }
1.57 parser 1193: }
1194:
1.84 paf 1195: static bool isLeap(int year) {
1.57 parser 1196: return !(
1197: (year % 4) || ((year % 400) && !(year % 100))
1.126 paf 1198: );
1.57 parser 1199: }
1200:
1201: int getMonthDays(int year, int month) {
1202: int monthDays[]={
1.126 paf 1203: 31,
1204: isLeap(year) ? 29 : 28,
1205: 31,
1206: 30,
1207: 31,
1208: 30,
1209: 31,
1210: 31,
1211: 30,
1212: 31,
1213: 30,
1.57 parser 1214: 31
1.126 paf 1215: };
1216: return monthDays[month];
1.41 paf 1217: }
1.69 parser 1218:
1.126 paf 1219: void remove_crlf(char* start, char* end) {
1220: for(char* p=start; p<end; p++)
1.69 parser 1221: switch(*p) {
1.126 paf 1222: case '\n': *p='|'; break;
1223: case '\r': *p=' '; break;
1.69 parser 1224: }
1.91 paf 1225: }
1226:
1227:
1228: /// must be last in this file
1229: #undef vsnprintf
1.126 paf 1230: int __vsnprintf(char* b, size_t s, const char* f, va_list l) {
1.91 paf 1231: if(!s)
1232: return 0;
1233:
1234: int r;
1235: // note: on win32& maybe somewhere else
1236: // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing
1237: --s;
1.172 paf 1238:
1239: // clients do not check for negative 's', feature: ignore such prints
1240: if((ssize_t)s<0)
1241: return 0;
1242:
1.91 paf 1243: #if _MSC_VER
1244: /*
1245: win32:
1246: mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm
1247:
1.154 paf 1248: if the number of bytes to write exceeds buffer, then count bytes are written and Ö1 is returned
1.91 paf 1249: */
1.126 paf 1250: r=_vsnprintf(b, s, f, l);
1.91 paf 1251: if(r<0)
1252: r=s;
1253: #else
1.126 paf 1254: r=vsnprintf(b, s, f, l);
1.91 paf 1255: /*
1256: solaris:
1257: man vsnprintf
1258:
1259: The snprintf() function returns the number of characters
1260: formatted, that is, the number of characters that would have
1261: been written to the buffer if it were large enough. If the
1262: value of n is 0 on a call to snprintf(), an unspecified
1263: value less than 1 is returned.
1264: */
1265:
1266: if(r<0)
1267: r=0;
1.167 paf 1268: else if((size_t)r>s)
1.91 paf 1269: r=s;
1270: #endif
1271: b[r]=0;
1272: return r;
1273: }
1274:
1.126 paf 1275: int __snprintf(char* b, size_t s, const char* f, ...) {
1.91 paf 1276: va_list l;
1.126 paf 1277: va_start(l, f);
1278: int r=__vsnprintf(b, s, f, l);
1279: va_end(l);
1.91 paf 1280: return r;
1.178 paf 1281: }
1282:
1283: /* mime64 functions are from libgmime[http://spruce.sourceforge.net/gmime/] lib */
1284: /*
1285: * Authors: Michael Zucchi <notzed@helixcode.com>
1286: * Jeffrey Stedfast <fejj@helixcode.com>
1287: *
1288: * Copyright 2000 Helix Code, Inc. (www.helixcode.com)
1289: *
1290: * This program is free software; you can redistribute it and/or modify
1291: * it under the terms of the GNU General Public License as published by
1292: * the Free Software Foundation; either version 2 of the License, or
1293: * (at your option) any later version.
1294: *
1295: * This program is distributed in the hope that it will be useful,
1296: * but WITHOUT ANY WARRANTY; without even the implied warranty of
1297: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1298: * GNU General Public License for more details.
1299: *
1300: * You should have received a copy of the GNU General Public License
1301: * along with this program; if not, write to the Free Software
1302: * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
1303: *
1304: */
1305: static char *base64_alphabet =
1306: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1307:
1308: /**
1309: * g_mime_utils_base64_encode_step:
1310: * @in: input stream
1311: * @inlen: length of the input
1312: * @out: output string
1313: * @state: holds the number of bits that are stored in @save
1314: * @save: leftover bits that have not yet been encoded
1315: *
1316: * Base64 encodes a chunk of data. Performs an 'encode step', only
1317: * encodes blocks of 3 characters to the output at a time, saves
1318: * left-over state in state and save (initialise to 0 on first
1319: * invocation).
1320: *
1321: * Returns the number of bytes encoded.
1322: **/
1323: static size_t
1324: g_mime_utils_base64_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
1325: {
1.186 paf 1326: register const unsigned char *inptr;
1.178 paf 1327: register unsigned char *outptr;
1328:
1329: if (inlen <= 0)
1330: return 0;
1331:
1332: inptr = in;
1333: outptr = out;
1334:
1335: if (inlen + ((unsigned char *)save)[0] > 2) {
1336: const unsigned char *inend = in + inlen - 2;
1337: register int c1 = 0, c2 = 0, c3 = 0;
1338: register int already;
1339:
1340: already = *state;
1341:
1342: switch (((char *)save)[0]) {
1343: case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
1344: case 2: c1 = ((unsigned char *)save)[1];
1345: c2 = ((unsigned char *)save)[2]; goto skip2;
1346: }
1347:
1348: /* yes, we jump into the loop, no i'm not going to change it, its beautiful! */
1349: while (inptr < inend) {
1350: c1 = *inptr++;
1351: skip1:
1352: c2 = *inptr++;
1353: skip2:
1354: c3 = *inptr++;
1355: *outptr++ = base64_alphabet [c1 >> 2];
1356: *outptr++ = base64_alphabet [(c2 >> 4) | ((c1 & 0x3) << 4)];
1357: *outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];
1358: *outptr++ = base64_alphabet [c3 & 0x3f];
1359: /* this is a bit ugly ... */
1360: if ((++already) >= 19) {
1361: *outptr++ = '\n';
1362: already = 0;
1363: }
1364: }
1365:
1366: ((unsigned char *)save)[0] = 0;
1367: inlen = 2 - (inptr - inend);
1368: *state = already;
1369: }
1370:
1371: //d(printf ("state = %d, inlen = %d\n", (int)((char *)save)[0], inlen));
1372:
1373: if (inlen > 0) {
1374: register char *saveout;
1375:
1376: /* points to the slot for the next char to save */
1377: saveout = & (((char *)save)[1]) + ((char *)save)[0];
1378:
1379: /* inlen can only be 0 1 or 2 */
1380: switch (inlen) {
1381: case 2: *saveout++ = *inptr++;
1382: case 1: *saveout++ = *inptr++;
1383: }
1384: ((char *)save)[0] += inlen;
1385: }
1386:
1387: /*d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",
1388: (int)((char *)save)[0],
1389: (int)((char *)save)[1],
1390: (int)((char *)save)[2]));*/
1391:
1392: return (outptr - out);
1393: }
1394:
1395: /**
1396: * g_mime_utils_base64_encode_close:
1397: * @in: input stream
1398: * @inlen: length of the input
1399: * @out: output string
1400: * @state: holds the number of bits that are stored in @save
1401: * @save: leftover bits that have not yet been encoded
1402: *
1403: * Base64 encodes the input stream to the output stream. Call this
1404: * when finished encoding data with g_mime_utils_base64_encode_step to
1405: * flush off the last little bit.
1406: *
1407: * Returns the number of bytes encoded.
1408: **/
1409: static size_t
1410: g_mime_utils_base64_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
1411: {
1412: unsigned char *outptr = out;
1413: int c1, c2;
1414:
1415: if (inlen > 0)
1416: outptr += g_mime_utils_base64_encode_step (in, inlen, outptr, state, save);
1417:
1418: c1 = ((unsigned char *)save)[1];
1419: c2 = ((unsigned char *)save)[2];
1420:
1421: switch (((unsigned char *)save)[0]) {
1422: case 2:
1423: outptr[2] = base64_alphabet [(c2 & 0x0f) << 2];
1424: goto skip;
1425: case 1:
1426: outptr[2] = '=';
1427: skip:
1428: outptr[0] = base64_alphabet [c1 >> 2];
1429: outptr[1] = base64_alphabet [c2 >> 4 | ((c1 & 0x3) << 4)];
1430: outptr[3] = '=';
1431: outptr += 4;
1432: break;
1433: }
1434:
1435: *outptr++ = 0;
1436:
1437: *save = 0;
1438: *state = 0;
1439:
1440: return (outptr - out);
1441: }
1442:
1443: char* pa_base64(const char *in, size_t len)
1444: {
1445: /* wont go to more than 2x size (overly conservative) */
1446: char* result=new(PointerFreeGC) char[len * 2 + 6];
1447: int state=0;
1448: int save=0;
1.183 paf 1449: #ifndef NDEBUG
1450: size_t filled=
1451: #endif
1452: g_mime_utils_base64_encode_close ((const unsigned char*)in, len,
1.178 paf 1453: (unsigned char*)result, &state, &save);
1454: assert(filled <= len * 2 + 6);
1455:
1456: return result;
1.98 paf 1457: }
E-mail: