|
|
| version 1.143.2.21.2.8, 2003/03/21 13:42:31 | version 1.143.2.21.2.19, 2003/04/01 16:16:23 |
|---|---|
| Line 12 static const char* IDENT_COMMON_C="$Date | Line 12 static const char* IDENT_COMMON_C="$Date |
| #include "pa_hash.h" | #include "pa_hash.h" |
| #include "pa_globals.h" | #include "pa_globals.h" |
| //#define PA_HTTP | #define PA_HTTP |
| #ifdef PA_HTTP | #ifdef PA_HTTP |
| #include "pa_vstring.h" | #include "pa_vstring.h" |
| Line 61 int PASCAL closesocket(SOCKET); | Line 61 int PASCAL closesocket(SOCKET); |
| #endif | #endif |
| // locking constants | // locking constants |
| //#define PA_DEBUG_NO_LOCKING | |
| #ifdef PA_DEBUG_NO_LOCKING | |
| #ifdef HAVE_FLOCK | #ifdef HAVE_FLOCK |
| Line 105 static int unlock(int fd) { FLOCK(F_TLOC | Line 108 static int unlock(int fd) { FLOCK(F_TLOC |
| #endif | #endif |
| #endif | #endif |
| #else | |
| static int lock_shared_blocking(int fd) { return 0; } | |
| static int lock_exclusive_blocking(int fd) { return 0; } | |
| static int lock_exclusive_nonblocking(int fd) { return 0; } | |
| static int unlock(int fd) { return 0; } | |
| #endif | |
| // defines for globals | // defines for globals |
| #define FILE_STATUS_NAME "status" | #define FILE_STATUS_NAME "status" |
| Line 187 static bool set_addr(struct sockaddr_in | Line 198 static bool set_addr(struct sockaddr_in |
| return true; | return true; |
| } | } |
| static int http_read_responseString& response, int sock, bool fail_on_status_ne_200){ | static int http_read_response(String& response, int sock, bool fail_on_status_ne_200){ |
| int result=0; | int result=0; |
| ssize_t EOLat=0; | size_t EOLat=0; |
| while(true) { | while(true) { |
| char *buf=new(PointerFreeGC) char[MAX_STRING]; | char *buf=new(PointerFreeGC) char[MAX_STRING]; |
| ssize_t size=recv(sock, buf, MAX_STRING, 0); | ssize_t size=recv(sock, buf, MAX_STRING, 0); |
| if(size<=0) | if(size<=0) |
| break; | break; |
| response.APPEND_TAINTED(buf, size, "remote HTTP server response", 0); | response.append_strdup(buf, size, String::L_TAINTED); |
| if(!result && (EOLat=response.pos("\r\n", 2))>=0) { // checking status in first response | if(!result && (EOLat=response.pos("\r\n", 2))!=STRING_NOT_FOUND) { // checking status in first response |
| const String& status_line=response.mid(0, (size_t)EOLat); | const String& status_line=response.mid(0, (size_t)EOLat); |
| ArrayString astatus; | ArrayString astatus; |
| status_line->split(astatus, 0, " ", 1); | size_t pos_after=0; |
| const String& status_code=astatus.get(1); | status_line.split(astatus, pos_after, " "); |
| result=status_code->as_int(); | const String& status_code=*astatus.get(1); |
| result=status_code.as_int(); | |
| if(fail_on_status_ne_200 && result!=200) | if(fail_on_status_ne_200 && result!=200) |
| throw Exception("http.status", | throw Exception("http.status", |
| status_code, | &status_code, |
| "invalid HTTP response status"); | "invalid HTTP response status"); |
| } | } |
| } | } |
| Line 231 static void timeout_handler(int sig){ | Line 243 static void timeout_handler(int sig){ |
| #endif | #endif |
| static int http_request( | static int http_request( |
| String& response, | String& response, |
| const String* origin_string, | const char* host, int port, |
| const char* host, int port, | const char* request, |
| const char* request, | int timeout, |
| int timeout, | bool fail_on_status_ne_200) { |
| bool fail_on_status_ne_200) { | |
| if(!host) | if(!host) |
| throw Exception("http.host", | throw Exception("http.host", |
| origin_string, | 0, |
| "zero hostname"); //never | "zero hostname"); //never |
| #ifdef WE_CAN_USE_ALARM | #ifdef WE_CAN_USE_ALARM |
| Line 251 static int http_request( | Line 262 static int http_request( |
| #ifdef WE_CAN_USE_ALARM | #ifdef WE_CAN_USE_ALARM |
| if(sigsetjmp(timeout_env, 1)) | if(sigsetjmp(timeout_env, 1)) |
| throw Exception("http.timeout", | throw Exception("http.timeout", |
| origin_string, | 0, |
| "timeout occured while retrieving document"); | "timeout occured while retrieving document"); |
| else { | else { |
| alarm(timeout); | alarm(timeout); |
| Line 260 static int http_request( | Line 271 static int http_request( |
| if(!set_addr(&dest, host, port)) | if(!set_addr(&dest, host, port)) |
| throw Exception("http.host", | throw Exception("http.host", |
| origin_string, | 0, |
| "can not resolve hostname \"%s\"", host); | "can not resolve hostname \"%s\"", host); |
| if((sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP/*0*/))<0) | if((sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP/*0*/))<0) |
| throw Exception("http.connect", | throw Exception("http.connect", |
| origin_string, | 0, |
| "can not make socket: %s (%d)", strerror(errno), errno); | "can not make socket: %s (%d)", strerror(errno), errno); |
| if(connect(sock, (struct sockaddr *)&dest, sizeof(dest))) | if(connect(sock, (struct sockaddr *)&dest, sizeof(dest))) |
| throw Exception("http.connect", | throw Exception("http.connect", |
| origin_string, | 0, |
| "can not connect to host \"%s\": %s (%d)", host, strerror(errno), errno); | "can not connect to host \"%s\": %s (%d)", host, strerror(errno), errno); |
| size_t request_size=strlen(request); | size_t request_size=strlen(request); |
| if(send(sock, request, request_size, 0)!=(ssize_t)request_size) | if(send(sock, request, request_size, 0)!=(ssize_t)request_size) |
| throw Exception("http.connect", | throw Exception("http.connect", |
| origin_string, | 0, |
| "error sending request: %s (%d)", strerror(errno), errno); | "error sending request: %s (%d)", strerror(errno), errno); |
| result=http_read_response(response, sock, fail_on_status_ne_200); | result=http_read_response(response, sock, fail_on_status_ne_200); |
| Line 296 static int http_request( | Line 307 static int http_request( |
| #ifndef DOXYGEN | #ifndef DOXYGEN |
| struct Http_pass_header_info { | struct Http_pass_header_info { |
| ; | |
| Charset* charset; | Charset* charset; |
| String* request; | String* request; |
| bool user_agent_specified; | bool user_agent_specified; |
| }; | }; |
| #endif | #endif |
| static void http_pass_header(HashStringValue::key_type key, HashStringValue::value_type value, | static void http_pass_header(HashStringValue::key_type key, HashStringValue::value_type value, |
| Http_pass_header_info *info) { | Http_pass_header_info *info) { |
| *(info->request)<<*key<<": " | *info->request <<key<<": " |
| << *attributed_meaning_to_string(*info->value, String::L_HTTP_HEADER, false) | << attributed_meaning_to_string(*value, String::L_HTTP_HEADER, false) |
| <<"\n"; | << "\n"; |
| if(*key->change_case(*info->*info->charset, String::CC_UPPER)=="USER-AGENT") | if(String(key, String::L_TAINTED).change_case(*info->charset, String::CC_UPPER)=="USER-AGENT") |
| info->user_agent_specified=true; | info->user_agent_specified=true; |
| } | } |
| #ifndef DOXYGEN | #ifndef DOXYGEN |
| struct File_read_http_result { | struct File_read_http_result { |
| char *data; size_t size; | char *str; size_t length; |
| HashStringValue* headers; | HashStringValue* headers; |
| }; | }; |
| #endif | #endif |
| static File_read_http_result file_read_httpCharset& charset, const String& file_spec, | static File_read_http_result file_read_http(Charset& charset, const String& file_spec, |
| HashStringValue *options=0) { | HashStringValue *options=0) { |
| File_read_http_result result; | File_read_http_result result; |
| char host[MAX_STRING]; | char host[MAX_STRING]; |
| const char* uri; | const char* uri; |
| int port; | int port; |
| const char* method(0); | const char* method=0; |
| int timeout=2; | int timeout=2; |
| bool fail_on_status_ne_200=true; | bool fail_on_status_ne_200=true; |
| Value* vheaders(0); | Value* vheaders=0; |
| const String& connect_string(new String()); | String& connect_string=*new String; |
| // not in ^sql{... L_SQL ...} spirit, but closer to ^file::load one | // not in ^sql{... L_SQL ...} spirit, but closer to ^file::load one |
| connect_string->append(*file_spec, String::L_URI); // tainted pieces -> URI pieces | connect_string.append(file_spec, String::L_URI); // tainted pieces -> URI pieces |
| const char* connect_string_cstr=connect_string->cstr(String::L_UNSPECIFIED); | const char* connect_string_cstr=connect_string.cstr(String::L_UNSPECIFIED); |
| const char* current=connect_string_cstr; | const char* current=connect_string_cstr; |
| if(strncmp(current, "http://", 7)!=0) | if(strncmp(current, "http://", 7)!=0) |
| throw Exception(0, | throw Exception(0, |
| connect_string, | &connect_string, |
| "does not start with http://"); //never | "does not start with http://"); //never |
| current+=7; | current+=7; |
| Line 351 static File_read_http_result file_read_h | Line 361 static File_read_http_result file_read_h |
| int valid_options=0; | int valid_options=0; |
| if(Value* vmethod=options->get(http_method_name)) { | if(Value* vmethod=options->get(http_method_name)) { |
| valid_options++; | valid_options++; |
| method=vmethod->as_string()->cstr(); | method=vmethod->as_string().cstr(); |
| } | } |
| if(Value* vtimeout=options->get(http_timeout_name)) { | if(Value* vtimeout=options->get(http_timeout_name)) { |
| valid_options++; | valid_options++; |
| Line 380 static File_read_http_result file_read_h | Line 390 static File_read_http_result file_read_h |
| request<< " "<< uri <<" HTTP/1.0\nHost: "<< host<<"\n"; | request<< " "<< uri <<" HTTP/1.0\nHost: "<< host<<"\n"; |
| bool user_agent_specified=false; | bool user_agent_specified=false; |
| if(vheaders && !vheaders->is_string()) { // allow empty | if(vheaders && !vheaders->is_string()) { // allow empty |
| if(HashStringValue *headers=vheaders->get_hash(connect_string)) { | if(HashStringValue *headers=vheaders->get_hash()) { |
| Http_pass_header_info info={&&charset, &request}; | Http_pass_header_info info={&charset, &request}; |
| headers->for_each(http_pass_header, &info); | headers->for_each(http_pass_header, &info); |
| user_agent_specified=info.user_agent_specified; | user_agent_specified=info.user_agent_specified; |
| } else | } else |
| throw Exception("parser.runtime", | throw Exception("parser.runtime", |
| connect_string, | &connect_string, |
| "headers param must be hash"); | "headers param must be hash"); |
| }; | }; |
| if(!user_agent_specified) // defaulting | if(!user_agent_specified) // defaulting |
| Line 397 static File_read_http_result file_read_h | Line 407 static File_read_http_result file_read_h |
| String response; | String response; |
| const char* request_cstr=request.cstr(String::L_UNSPECIFIED); | const char* request_cstr=request.cstr(String::L_UNSPECIFIED); |
| int status_code=http_request(response, | int status_code=http_request(response, |
| connect_string, host, port, request_cstr, | host, port, request_cstr, |
| timeout, fail_on_status_ne_200); | timeout, fail_on_status_ne_200); |
| //processing results | //processing results |
| int pos=response.pos("\r\n\r\n", 4); | size_t pos=response.pos("\r\n\r\n", 4); |
| if(pos<1){ | if(pos==STRING_NOT_FOUND || pos<1){ |
| throw Exception("http.response", | throw Exception("http.response", |
| connect_string, | &connect_string, |
| "bad response from host - no headers found"); | "bad response from host - no headers found"); |
| } | } |
| const String& header_block=response.mid(0, pos); | const String& header_block=response.mid(0, pos); |
| const String& body=response.mid(pos+4, response.length()); | const String& body=response.mid(pos+4, response.length()); |
| ArrayString aheaders; | ArrayString aheaders; |
| result.headers=HashStringValue* (new HashStringValue); | result.headers=new HashStringValue; |
| header_block->split(aheaders, 0, "\r\n", 2); | size_t pos_after=0; |
| header_block.split(aheaders, pos_after, "\r\n"); | |
| //processing headers | //processing headers |
| for(int i=1; i<aheaders.count(); i++) { | size_t aheaders_count=aheaders.count(); |
| const String& line=aheaders.get(i); | for(size_t i=1; i<aheaders_count; i++) { |
| pos=line->pos(": ", 2); | const String& line=*aheaders.get(i); |
| if(pos<1) | pos=line.pos(": ", 2); |
| if(pos==STRING_NOT_FOUND || pos<1) | |
| throw Exception("http.response", | throw Exception("http.response", |
| connect_string, | &connect_string, |
| "bad response from host - bad header \"%s\"", line->cstr().get()); | "bad response from host - bad header \"%s\"", line.cstr()); |
| result.headers->put( | result.headers->put( |
| line->mid(0, pos)->change_case(charset, String::CC_UPPER), | line.mid(0, pos).change_case(charset, String::CC_UPPER), |
| Value*(new VString(line->mid(pos+2, line->length())))); | new VString(line.mid(pos+2, line.length()))); |
| } | } |
| // output response | // output response |
| result.str=new(PointerFreeGC) char[1/*as text puts terminating zero later*/+(result.length=body->length())]; | result.str=body.cstrm(); |
| body->store_to(result.str, String::L_AS_IS); | result.length=body.length(); |
| result.headers->put(file_status_name, Value*(new VInt(status_code))); | result.headers->put(file_status_name, new VInt(status_code)); |
| return result; | return result; |
| } | } |
| Line 470 static void file_read_action( | Line 482 static void file_read_action( |
| File_read_result file_read(Charset& charset, const String& file_spec, | File_read_result file_read(Charset& charset, const String& file_spec, |
| bool as_text, HashStringValue *params, | bool as_text, HashStringValue *params, |
| bool fail_on_read_problem) { | bool fail_on_read_problem) { |
| File_read_result result; | File_read_result result={false}; |
| #ifdef PA_HTTP | #ifdef PA_HTTP |
| if(file_spec.starts_with("http://", 7)) { | if(file_spec.starts_with("http://")) { |
| // fail on read problem | // fail on read problem |
| File_read_http_result http=file_read_http(charset, file_spec, params); | File_read_http_result http;//=file_read_http(charset, file_spec, params); |
| result.success=true; | result.success=true; |
| result.str=http.data; | result.str="HELLO";//http.str; |
| result.length=http.size; | result.length=5;//http.length; |
| result.headers=http.headers; | result.headers=new HashStringValue;//http.headers; |
| } else { | } else { |
| #endif | #endif |
| File_read_action_info info={&result.str, &result.length}; | File_read_action_info info={&result.str, &result.length}; |
| Line 569 bool file_read_action_under_lock(const S | Line 581 bool file_read_action_under_lock(const S |
| static void create_dir_for_file(const String& file_spec) { | static void create_dir_for_file(const String& file_spec) { |
| size_t pos_after=1; | size_t pos_after=1; |
| int pos_before; | size_t pos_before; |
| while((pos_before=file_spec.pos("/", pos_after))>=0) { | while((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND) { |
| mkdir(file_spec.mid(0, pos_before).cstr(String::L_FILE_SPEC), 0775); | mkdir(file_spec.mid(0, pos_before).cstr(String::L_FILE_SPEC), 0775); |
| pos_after=pos_before+1; | pos_after=pos_before+1; |
| } | } |
| Line 658 void file_write( | Line 670 void file_write( |
| // throws nothing! [this is required in file_move & file_delete] | // throws nothing! [this is required in file_move & file_delete] |
| static void rmdir(const String& file_spec, size_t pos_after) { | static void rmdir(const String& file_spec, size_t pos_after) { |
| int pos_before; | size_t pos_before; |
| if((pos_before=file_spec.pos("/", pos_after))>=0) | if((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND) |
| rmdir(file_spec, pos_before+1); | rmdir(file_spec, pos_before+1); |
| rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::L_FILE_SPEC)); | rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::L_FILE_SPEC)); |
| Line 732 bool file_readable(const String& file_sp | Line 744 bool file_readable(const String& file_sp |
| bool dir_readable(const String& file_spec) { | bool dir_readable(const String& file_spec) { |
| return entry_readable(file_spec, true); | return entry_readable(file_spec, true); |
| } | } |
| const String* file_readable(String& path, String& name) { | const String* file_readable(const String& path, const String& name) { |
| String& result=*new String(path); | String& result=*new String(path); |
| result << "/"; | result << "/"; |
| result << name; | result << name; |
| Line 749 bool file_stat(const String& file_spec, | Line 761 bool file_stat(const String& file_spec, |
| time_t& rctime, | time_t& rctime, |
| bool fail_on_read_problem) { | bool fail_on_read_problem) { |
| const char* fname=file_spec.cstr(String::L_FILE_SPEC); | const char* fname=file_spec.cstr(String::L_FILE_SPEC); |
| struct stat finfo; | struct stat finfo; |
| if(stat(fname, &finfo)!=0) | if(stat(fname, &finfo)!=0) |
| if(fail_on_read_problem) | if(fail_on_read_problem) |
| throw Exception("file.missing", | throw Exception("file.missing", |