|
|
| version 1.10, 2010/12/29 12:17:58 | version 1.26, 2013/04/21 21:24:53 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b curl parser class. | Parser: @b curl parser class. |
| Copyright(c) 2001-2009 ArtLebedev Group(http://www.artlebedev.com) | Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com) |
| */ | */ |
| #include "pa_config_includes.h" | #include "pa_config_includes.h" |
| #ifdef HAVE_CURL | |
| static const char * const IDENT_INET_C="$Date$"; | |
| #include "pa_vmethod_frame.h" | #include "pa_vmethod_frame.h" |
| #include "pa_request.h" | #include "pa_request.h" |
| #include "pa_vfile.h" | #include "pa_vfile.h" |
| Line 17 static const char * const IDENT_INET_C=" | Line 13 static const char * const IDENT_INET_C=" |
| #include "pa_vstring.h" | #include "pa_vstring.h" |
| #include "pa_vtable.h" | #include "pa_vtable.h" |
| #include "pa_common.h" | #include "pa_common.h" |
| #include "pa_http.h" | |
| #include "ltdl.h" | #include "ltdl.h" |
| volatile const char * IDENT_CURL_C="$Id$"; | |
| class MCurl: public Methoded { | class MCurl: public Methoded { |
| public: | public: |
| MCurl(); | MCurl(); |
| Line 28 public: | Line 27 public: |
| DECLARE_CLASS_VAR(curl, new MCurl, 0); | DECLARE_CLASS_VAR(curl, new MCurl, 0); |
| // from file.C | |
| extern bool is_text_mode(const String& mode); | |
| #include "curl.h" | #include "curl.h" |
| typedef CURL *(*t_curl_easy_init)(); t_curl_easy_init f_curl_easy_init; | typedef CURL *(*t_curl_easy_init)(); t_curl_easy_init f_curl_easy_init; |
| Line 47 typedef void (*t_curl_formfree)(struct c | Line 43 typedef void (*t_curl_formfree)(struct c |
| #define GLINK(name) f_##name=(t_##name)lt_dlsym(handle, #name); | #define GLINK(name) f_##name=(t_##name)lt_dlsym(handle, #name); |
| #define DLINK(name) GLINK(name) if(!f_##name) return "function " #name " was not found"; | #define DLINK(name) GLINK(name) if(!f_##name) return "function " #name " was not found"; |
| const char *dlink(const char *dlopen_file_spec) { | static const char *dlink(const char *dlopen_file_spec) { |
| if(lt_dlinit()) | if(lt_dlinit()) |
| return lt_dlerror(); | return lt_dlerror(); |
| Line 75 const char *dlink(const char *dlopen_fil | Line 71 const char *dlink(const char *dlopen_fil |
| class ParserOptions { | class ParserOptions { |
| public: | public: |
| const char *filename; | const String *filename; |
| const String *content_type; | const String *content_type; |
| bool is_text; | bool is_text; |
| Charset *charset, *response_charset; | Charset *charset, *response_charset; |
| struct curl_httppost *f_post; | struct curl_httppost *f_post; |
| FILE *f_stderr; | |
| ParserOptions() : filename(0), content_type(0), is_text(true), charset(0), response_charset(0), f_post(0){} | ParserOptions() : filename(0), content_type(0), is_text(true), charset(0), response_charset(0), f_post(0), f_stderr(0){} |
| ~ParserOptions() { | ~ParserOptions() { |
| f_curl_formfree(f_post); | f_curl_formfree(f_post); |
| if(f_stderr) | |
| fclose(f_stderr); | |
| } | } |
| }; | }; |
| Line 188 public: | Line 187 public: |
| CURL_FORM, | CURL_FORM, |
| CURL_HEADERS, | CURL_HEADERS, |
| CURL_FILE, | CURL_FILE, |
| CURL_STDERR, | |
| PARSER_LIBRARY, | PARSER_LIBRARY, |
| PARSER_NAME, | PARSER_NAME, |
| PARSER_CONTENT_TYPE, | PARSER_CONTENT_TYPE, |
| Line 211 public: | Line 211 public: |
| CURL_OPT(CURL_INT, LOCALPORT); | CURL_OPT(CURL_INT, LOCALPORT); |
| CURL_OPT(CURL_INT, PORT); | CURL_OPT(CURL_INT, PORT); |
| CURL_OPT(CURL_INT, VERBOSE); | |
| CURL_OPT(CURL_STDERR, STDERR); | |
| CURL_OPT(CURL_INT, MAXFILESIZE); | |
| CURL_OPT(CURL_INT, HTTPAUTH); | CURL_OPT(CURL_INT, HTTPAUTH); |
| CURL_OPT(CURL_STRING, USERPWD); | CURL_OPT(CURL_STRING, USERPWD); |
| #ifdef CURLOPT_USERNAME | |
| CURL_OPT(CURL_STRING, USERNAME); | CURL_OPT(CURL_STRING, USERNAME); |
| CURL_OPT(CURL_STRING, PASSWORD); | CURL_OPT(CURL_STRING, PASSWORD); |
| #endif | |
| CURL_OPT(CURL_URLENCODE, USERAGENT); | CURL_OPT(CURL_URLENCODE, USERAGENT); |
| CURL_OPT(CURL_URLENCODE, REFERER); | CURL_OPT(CURL_URLENCODE, REFERER); |
| CURL_OPT(CURL_INT, AUTOREFERER); | CURL_OPT(CURL_INT, AUTOREFERER); |
| CURL_OPT(CURL_STRING, ENCODING); // gzip or deflate | CURL_OPT(CURL_STRING, ENCODING); // gzip or deflate |
| CURL_OPT(CURL_STRING, ACCEPT_ENCODING); // gzip or deflate | |
| CURL_OPT(CURL_INT, FOLLOWLOCATION); | CURL_OPT(CURL_INT, FOLLOWLOCATION); |
| CURL_OPT(CURL_INT, UNRESTRICTED_AUTH); | CURL_OPT(CURL_INT, UNRESTRICTED_AUTH); |
| Line 276 public: | Line 281 public: |
| CURL_OPT(CURL_STRING, SSLENGINE); | CURL_OPT(CURL_STRING, SSLENGINE); |
| CURL_OPT(CURL_STRING, SSLENGINE_DEFAULT); | CURL_OPT(CURL_STRING, SSLENGINE_DEFAULT); |
| #ifdef CURLOPT_ISSUERCERT | |
| CURL_OPT(CURL_FILE, ISSUERCERT); | CURL_OPT(CURL_FILE, ISSUERCERT); |
| #endif | |
| #ifdef CURLOPT_CRLFILE | |
| CURL_OPT(CURL_FILE, CRLFILE); | CURL_OPT(CURL_FILE, CRLFILE); |
| #endif | |
| CURL_OPT(CURL_STRING, CAINFO); | CURL_OPT(CURL_STRING, CAINFO); |
| CURL_OPT(CURL_STRING, CAPATH); | CURL_OPT(CURL_FILE, CAPATH); |
| CURL_OPT(CURL_INT, SSL_VERIFYPEER); | CURL_OPT(CURL_INT, SSL_VERIFYPEER); |
| CURL_OPT(CURL_INT, SSL_VERIFYHOST); | CURL_OPT(CURL_INT, SSL_VERIFYHOST); |
| CURL_OPT(CURL_STRING, SSL_CIPHER_LIST); | CURL_OPT(CURL_STRING, SSL_CIPHER_LIST); |
| Line 360 static void curl_form(HashStringValue *v | Line 360 static void curl_form(HashStringValue *v |
| } | } |
| } | } |
| static const char *curl_check_file(const String &file_spec){ | |
| const char *file_spec_cstr=file_spec.taint_cstr(String::L_FILE_SPEC); | |
| struct stat finfo; | |
| if(stat(file_spec_cstr, &finfo)==0) | |
| check_safe_mode(finfo, file_spec, file_spec_cstr); | |
| return file_spec_cstr; | |
| } | |
| static void curl_setopt(HashStringValue::key_type key, HashStringValue::value_type value, Request& r) { | static void curl_setopt(HashStringValue::key_type key, HashStringValue::value_type value, Request& r) { |
| CurlOption *opt=curl_options->get(key); | CurlOption *opt=curl_options->get(key); |
| Line 417 static void curl_setopt(HashStringValue: | Line 425 static void curl_setopt(HashStringValue: |
| f_curl_formfree(options().f_post); | f_curl_formfree(options().f_post); |
| options().f_post = 0; | options().f_post = 0; |
| } else { | } else { |
| throw Exception("curl", 0, "%s must be a hash", key.cstr()); | throw Exception("curl", 0, "failed to set option '%s': value must be a hash", key.cstr()); |
| } | } |
| res=f_curl_easy_setopt(curl(), CURLOPT_HTTPPOST, foptions->f_post); | res=f_curl_easy_setopt(curl(), CURLOPT_HTTPPOST, foptions->f_post); |
| break; | break; |
| Line 430 static void curl_setopt(HashStringValue: | Line 438 static void curl_setopt(HashStringValue: |
| } | } |
| case CurlOption::CURL_FILE:{ | case CurlOption::CURL_FILE:{ |
| // file-spec curl option | // file-spec curl option |
| const char *value_str=r.absolute(v.as_string()).taint_cstr(String::L_FILE_SPEC); | const char *file_spec_cstr=curl_check_file(r.absolute(v.as_string())); |
| res=f_curl_easy_setopt(curl(), opt->id, value_str); | res=f_curl_easy_setopt(curl(), opt->id, file_spec_cstr); |
| break; | |
| } | |
| case CurlOption::CURL_STDERR:{ | |
| // verbose output redirection from stderr to file curl option | |
| const char *file_spec_cstr=curl_check_file(r.absolute(v.as_string())); | |
| FILE *f_stderr=options().f_stderr=fopen(file_spec_cstr, "wt"); | |
| if (f_stderr){ | |
| res=f_curl_easy_setopt(curl(), opt->id, f_stderr); | |
| } else { | |
| throw Exception("curl", 0, "failed to set option '%s': unable to open file '%s'", key.cstr(), file_spec_cstr); | |
| } | |
| break; | break; |
| } | } |
| case CurlOption::PARSER_LIBRARY:{ | case CurlOption::PARSER_LIBRARY:{ |
| Line 439 static void curl_setopt(HashStringValue: | Line 458 static void curl_setopt(HashStringValue: |
| if(fcurl==0){ | if(fcurl==0){ |
| curl_library=v.as_string().taint_cstr(String::L_FILE_SPEC); | curl_library=v.as_string().taint_cstr(String::L_FILE_SPEC); |
| } else | } else |
| throw Exception("curl", 0, "failed to set option '%s': %s", key.cstr(), "already loaded"); | throw Exception("curl", 0, "failed to set option '%s': already loaded", key.cstr()); |
| break; | break; |
| } | } |
| case CurlOption::PARSER_NAME:{ | case CurlOption::PARSER_NAME:{ |
| // 'name' parser option | // 'name' parser option |
| options().filename=v.as_string().taint_cstr(String::L_FILE_SPEC); | options().filename=&v.as_string(); |
| break; | break; |
| } | } |
| case CurlOption::PARSER_CONTENT_TYPE:{ | case CurlOption::PARSER_CONTENT_TYPE:{ |
| Line 454 static void curl_setopt(HashStringValue: | Line 473 static void curl_setopt(HashStringValue: |
| } | } |
| case CurlOption::PARSER_MODE:{ | case CurlOption::PARSER_MODE:{ |
| // 'mode' parser option | // 'mode' parser option |
| options().is_text=is_text_mode(v.as_string()); | options().is_text=VFile::is_text_mode(v.as_string()); |
| break; | break; |
| } | } |
| case CurlOption::PARSER_CHARSET:{ | case CurlOption::PARSER_CHARSET:{ |
| Line 463 static void curl_setopt(HashStringValue: | Line 482 static void curl_setopt(HashStringValue: |
| break; | break; |
| } | } |
| case CurlOption::PARSER_RESPONSE_CHARSET:{ | case CurlOption::PARSER_RESPONSE_CHARSET:{ |
| // 'charset' parser option | // 'response-charset' parser option |
| options().response_charset=&::charsets.get(v.as_string().change_case(r.charsets.source(), String::CC_UPPER)); | options().response_charset=&::charsets.get(v.as_string().change_case(r.charsets.source(), String::CC_UPPER)); |
| break; | break; |
| } | } |
| Line 477 static void _curl_options(Request& r, Me | Line 496 static void _curl_options(Request& r, Me |
| if(curl_options==0) | if(curl_options==0) |
| curl_options=new CurlOptionHash(); | curl_options=new CurlOptionHash(); |
| if(HashStringValue* options=params.as_no_junction(0, OPTIONS_MUST_NOT_BE_CODE).get_hash()){ | if(HashStringValue* options=params.as_hash(0)) |
| options->for_each<Request&>(curl_setopt, r); | options->for_each<Request&>(curl_setopt, r); |
| } else | |
| throw Exception("curl", 0, OPTIONS_MUST_BE_HASH); | |
| } | } |
| Line 509 static int curl_writer(char *data, size_ | Line 526 static int curl_writer(char *data, size_ |
| return size; | return size; |
| } | } |
| static int curl_header(char *data, size_t size, size_t nmemb, HASH_STRING<char *> *result){ | class Curl_response { |
| public: | |
| HASH_STRING<char*> headers; | |
| Array<char*> cookies; | |
| }; | |
| static int curl_header(char *data, size_t size, size_t nmemb, Curl_response *result){ | |
| if(result == 0) | if(result == 0) |
| return 0; | return 0; |
| Line 519 static int curl_header(char *data, size_ | Line 542 static int curl_header(char *data, size_ |
| char *value=lsplit(line,':'); | char *value=lsplit(line,':'); |
| if(value && *line){ | if(value && *line){ |
| // we need only headers, not the response code | // we need only headers, not the response code |
| result->put(str_upper(line), value); | const char* HEADER_NAME=str_upper(line); |
| result->headers.put(HEADER_NAME, value); | |
| if(strcmp(HEADER_NAME, "SET-COOKIE")==0) | |
| result->cookies+=value; | |
| } | } |
| } | } |
| return size; | return size; |
| Line 541 static void _curl_load_action(Request& r | Line 567 static void _curl_load_action(Request& r |
| CURL_SETOPT(CURLOPT_WRITEDATA, &body, "curl write buffer"); | CURL_SETOPT(CURLOPT_WRITEDATA, &body, "curl write buffer"); |
| // we need a container for headers as VFile fields can be put only after VFile.set | // we need a container for headers as VFile fields can be put only after VFile.set |
| HASH_STRING<char *> headers; | Curl_response response; |
| CURL_SETOPT(CURLOPT_HEADERFUNCTION, curl_header, "curl header function"); | CURL_SETOPT(CURLOPT_HEADERFUNCTION, curl_header, "curl header function"); |
| CURL_SETOPT(CURLOPT_WRITEHEADER, &headers, "curl header buffer"); | CURL_SETOPT(CURLOPT_WRITEHEADER, &response, "curl header buffer"); |
| if((res=f_curl_easy_perform(curl())) != CURLE_OK){ | if((res=f_curl_easy_perform(curl())) != CURLE_OK){ |
| char *ex_type = 0; | const char *ex_type = 0; |
| switch(res){ | switch(res){ |
| case CURLE_OPERATION_TIMEDOUT: | case CURLE_OPERATION_TIMEDOUT: |
| ex_type = "curl.timeout"; break; | ex_type = "curl.timeout"; break; |
| Line 569 static void _curl_load_action(Request& r | Line 595 static void _curl_load_action(Request& r |
| // assure trailing zero | // assure trailing zero |
| body.buf[body.length]=0; | body.buf[body.length]=0; |
| Value* vcontent_type= | |
| options().content_type ? new VString(*options().content_type) : | |
| options().filename ? new VString(r.mime_type_of(options().filename)) : 0; | |
| VFile& result=*new VFile; | VFile& result=*new VFile; |
| String::Body ct_header = headers.get(HTTP_CONTENT_TYPE_UPPER); | String::Body ct_header = response.headers.get(HTTP_CONTENT_TYPE_UPPER); |
| Charset *asked_charset = options().response_charset; | Charset *asked_charset = options().response_charset; |
| if (asked_charset == 0){ | if (asked_charset == 0){ |
| Charset *remote_charset = ct_header.is_empty() ? 0 : detect_charset(ct_header.trim(String::TRIM_BOTH, " \t\n\r").cstr()); | Charset *remote_charset = ct_header.is_empty() ? 0 : detect_charset(ct_header.trim(String::TRIM_BOTH, " \t\n\r").cstr()); |
| Line 588 static void _curl_load_action(Request& r | Line 610 static void _curl_load_action(Request& r |
| body.length=c.length; | body.length=c.length; |
| } | } |
| result.set(true /*tainted*/, body.buf, body.length, options().filename, vcontent_type); | result.set(true/*tainted*/, options().is_text, body.buf, body.length, options().filename |
| result.set_mode(options().is_text); | , options().content_type ? new VString(*options().content_type) : 0, &r); |
| long http_status = 0; | long http_status = 0; |
| if(f_curl_easy_getinfo(curl(), CURLINFO_RESPONSE_CODE, &http_status) == CURLE_OK){ | if(f_curl_easy_getinfo(curl(), CURLINFO_RESPONSE_CODE, &http_status) == CURLE_OK){ |
| result.fields().put("status", new VInt(http_status)); | result.fields().put("status", new VInt(http_status)); |
| } | } |
| for(HASH_STRING<char *>::Iterator i(headers); i; i.next() ){ | for(HASH_STRING<char *>::Iterator i(response.headers); i; i.next() ){ |
| String::Body key=i.key(); | String::Body HEADER_NAME=i.key(); |
| String::Body value=i.value(); | String::Body value=i.value(); |
| if(asked_charset){ | if(asked_charset){ |
| key=Charset::transcode(key, *asked_charset, r.charsets.source()); | HEADER_NAME=Charset::transcode(HEADER_NAME, *asked_charset, r.charsets.source()); |
| value=Charset::transcode(value, *asked_charset, r.charsets.source()); | value=Charset::transcode(value, *asked_charset, r.charsets.source()); |
| } | } |
| result.fields().put(key, new VString(*new String(value.trim(String::TRIM_BOTH, " \t\n\r"), String::L_TAINTED))); | result.fields().put(HEADER_NAME, new VString(*new String(value.trim(String::TRIM_BOTH, " \t\n\r"), String::L_TAINTED))); |
| } | } |
| // filling $.cookies | |
| Table* tcookies=0; | |
| for(Array_iterator<char*> i(response.cookies); i.has_next(); ){ | |
| if(!tcookies){ | |
| Table::columns_type columns=new ArrayString(1); | |
| *columns+=new String("value"); | |
| tcookies=new Table(columns); | |
| } | |
| String::Body value=i.next(); | |
| if(asked_charset) | |
| value=Charset::transcode(value, *asked_charset, r.charsets.source()); | |
| ArrayString& row=*new ArrayString(1); | |
| row+=new String(value.trim(String::TRIM_BOTH, " \t\n\r"), String::L_TAINTED); | |
| *tcookies+=&row; | |
| } | |
| if(tcookies) | |
| result.fields().put(HTTP_COOKIES_NAME, new VTable(parse_cookies(r, tcookies))); | |
| r.write_no_lang(result); | r.write_no_lang(result); |
| } | } |
| Line 620 MCurl::MCurl(): Methoded("curl") { | Line 661 MCurl::MCurl(): Methoded("curl") { |
| add_native_method("options", Method::CT_STATIC, _curl_options, 1, 1); | add_native_method("options", Method::CT_STATIC, _curl_options, 1, 1); |
| add_native_method("load", Method::CT_STATIC, _curl_load, 0, 1); | add_native_method("load", Method::CT_STATIC, _curl_load, 0, 1); |
| } | } |
| #else // HAVE_CURL | |
| #include "classes.h" | |
| // global variable | |
| DECLARE_CLASS_VAR(curl, 0, 0); // fictive | |
| #endif // HAVE_CURL |