|
|
| version 1.12, 2003/12/01 09:35:38 | version 1.26, 2009/09/25 12:58:39 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: plugins to xml library, controlling i/o; implementation | Parser: plugins to xml library, controlling i/o; implementation |
| Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com) | Copyright (c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com) |
| Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| */ | */ |
| Line 13 static const char * const IDENT="$Date$" | Line 13 static const char * const IDENT="$Date$" |
| #include "libxslt/extensions.h" | #include "libxslt/extensions.h" |
| #include "pa_threads.h" | |
| #include "pa_globals.h" | #include "pa_globals.h" |
| #include "pa_request.h" | #include "pa_request.h" |
| static Hash<pa_thread_t, HashStringBool*> xml_dependencies; | |
| static void add_dependency(const String::Body url) { | |
| pa_thread_t thread_id=pa_get_thread_id(); | |
| HashStringBool* urls; | |
| { | |
| SYNCHRONIZED; | |
| // try to get existing for this thread_id | |
| urls=xml_dependencies.get(thread_id); | |
| } | |
| if(urls) // do we need to monitor now? | |
| urls->put(url, true); | |
| } | |
| void pa_xmlStartMonitoringDependencies() { | |
| pa_thread_t thread_id=pa_get_thread_id(); | |
| HashStringBool* urls=new HashStringBool; | |
| { | |
| SYNCHRONIZED; // find+fill blocked | |
| xml_dependencies.put(thread_id, urls); | |
| } | |
| } | |
| void pa_xmlStopMonitoringDependencies() { | |
| pa_thread_t thread_id=pa_get_thread_id(); | |
| { | |
| SYNCHRONIZED; // find+fill blocked | |
| xml_dependencies.put(thread_id, 0); | |
| } | |
| } | |
| HashStringBool* pa_xmlGetDependencies() { | |
| pa_thread_t thread_id=pa_get_thread_id(); | |
| { | |
| SYNCHRONIZED; // find+remove blocked | |
| HashStringBool* result=xml_dependencies.get(thread_id); | |
| xml_dependencies.remove(thread_id); | |
| return result; | |
| } | |
| } | |
| #ifndef DOXYGEN | #ifndef DOXYGEN |
| struct MemoryStream { | struct MemoryStream { |
| const char* m_buf; | const char* m_buf; |
| Line 28 struct MemoryStream { | Line 75 struct MemoryStream { |
| return 0; | return 0; |
| size_t to_read=min(a_size, left); | size_t to_read=min(a_size, left); |
| memcpy(a_buffer, m_buf, to_read); | memcpy(a_buffer, m_buf+m_position, to_read); |
| m_position+=to_read; | m_position+=to_read; |
| return to_read; | return to_read; |
| } | } |
| Line 36 struct MemoryStream { | Line 83 struct MemoryStream { |
| }; | }; |
| #endif | #endif |
| static void * | |
| xmlFileOpen_ReadIntoStream (const char* do_not_store_filename, bool adjust_path_to_root_from_document_root=false) { | |
| #ifdef PA_SAFE_MODE | #ifdef PA_SAFE_MODE |
| static int | //copied from libxml/catalog.c |
| xmlFileMatchSafeMode(const char* file_spec_cstr) { | # define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog" |
| if(strstr(file_spec_cstr, "://")) { | // disable attempts to consult default catalog [usually, that file belongs to other user/group] |
| String* file_spec=new String(file_spec_cstr, true); | if(strcmp(do_not_store_filename, XML_XML_DEFAULT_CATALOG)==0) |
| struct stat finfo; | return 0; |
| if(stat(file_spec_cstr, &finfo)!=0) | #endif |
| throw Exception("file.missing", | |
| file_spec, | |
| "stat failed: %s (%d)", | |
| strerror(errno), errno); | |
| check_safe_mode(finfo, file_spec, file_spec_cstr); | Request& r=pa_thread_request(); |
| char adjust_buf[MAX_STRING]; | |
| if(adjust_path_to_root_from_document_root) { | |
| const char* document_root=r.request_info.document_root; | |
| if(!document_root) | |
| document_root="."; | |
| adjust_buf[0]=0; | |
| strcat(adjust_buf, document_root); | |
| strcat(adjust_buf, &do_not_store_filename[16]); | |
| do_not_store_filename=adjust_buf; | |
| } else | |
| if(!strstr(do_not_store_filename, "http://")) | |
| if(strstr(do_not_store_filename, "file://")){ | |
| do_not_store_filename+=7/*strlen("file://")*/; | |
| #ifdef WIN32 | |
| if( | |
| do_not_store_filename[0]=='/' | |
| && do_not_store_filename[1] | |
| && do_not_store_filename[2]==':' | |
| && do_not_store_filename[3]=='/' | |
| ){ | |
| // skip leading slash for absolute path file:///C:/path/to/file | |
| do_not_store_filename++; | |
| } | |
| #endif | |
| } else if(*do_not_store_filename && do_not_store_filename[1]!=':' && strstr(do_not_store_filename, "://")) { | |
| pa_xmlStopMonitoringDependencies(); | |
| return 0; // plug out [do not handle other prefixes] | |
| } | |
| const char* can_store_filename=pa_strdup(do_not_store_filename); | |
| add_dependency(can_store_filename); | |
| const char *buf; | |
| try { | |
| buf=file_load_text(r, *new String(can_store_filename), | |
| true/*fail_on_read_problem*/, | |
| 0/*params*/, | |
| false/*don't transcode result because it must be fit with @encoding value!*/); | |
| } catch(const Exception& e) { | |
| if(strcmp(e.type(), "file.missing")==0) | |
| return 0; // let the library try that and report an error properly | |
| buf=e.comment(); | |
| } catch(...) { | |
| buf="xmlFileOpen_ReadIntoStream: unknown error"; | |
| } | } |
| return 0; | MemoryStream* stream=new(UseGC) MemoryStream; |
| stream->m_buf=buf; | |
| stream->m_size=strlen(buf); | |
| return (void *)stream; | |
| } | |
| static int | |
| xmlFileMatchMonitor(const char* /*file_spec_cstr*/) { | |
| return 1; // always intercept, causing xmlFileOpenMonitor to be called | |
| } | |
| static void * | |
| xmlFileOpenMonitor(const char* filename) { | |
| return xmlFileOpen_ReadIntoStream(filename); // handles localfile case, else returns 0 | |
| } | } |
| #endif | |
| /** | /** |
| Line 71 xmlFileMatchLocalhost(const char* filena | Line 172 xmlFileMatchLocalhost(const char* filena |
| return(0); | return(0); |
| } | } |
| /** | /** |
| * xmlFileOpenHttpLocalhost : | * xmlFileOpenLocalhost: |
| * filename: the URI for matching | * filename: the URI for matching |
| * | * |
| * http://localhost/abc -> $ENV{DOCUMENT_ROOT}/abc | ./abc | * http://localhost/abc -> $ENV{DOCUMENT_ROOT}/abc | ./abc |
| * | * |
| * input from FILE *, supports compressed input | |
| * if filename is " " then the standard input is used | |
| * | |
| * Returns an I/O context or NULL in case of error | * Returns an I/O context or NULL in case of error |
| */ | */ |
| static void * | static void * |
| xmlFileOpenLocalhost (const char* filename) { | xmlFileOpenLocalhost (const char* filename) { |
| Request& r=pa_thread_request(); | return xmlFileOpen_ReadIntoStream(filename, true/*adjust path to root from document_root*/); |
| const char* document_root=r.request_info.document_root; | |
| if(!document_root) | |
| document_root="."; | |
| char path[MAX_STRING]; | |
| path[0]=0; | |
| strcat(path, document_root); | |
| strcat(path, &filename[16]); | |
| if(char *buf=file_read_text(r.charsets, *new String(path), false)) { | |
| MemoryStream *stream=new(UseGC) MemoryStream; | |
| stream->m_buf=buf | |
| stream->m_size=strlen(buf); | |
| return (void *)stream; | |
| } else | |
| return 0; | |
| } | } |
| static int | static int |
| Line 114 xmlFileMatchMethod(const char* filename) | Line 195 xmlFileMatchMethod(const char* filename) |
| /// parser://method/param/here -> ^MAIN:method[/params/here] | /// parser://method/param/here -> ^MAIN:method[/params/here] |
| static void * | static void * |
| xmlFileOpenMethod (const char* afilename) { | xmlFileOpenMethod (const char* afilename) { |
| Request& r=pa_thread_request(); | const char* buf; |
| try { | |
| char* s=pa_strdup(afilename+9 /*strlen("parser://")*/); | Request& r=pa_thread_request(); |
| const char* method_cstr=lsplit(&s, '/'); | |
| const String* method=new String(method_cstr); | char* s=pa_strdup(afilename+9 /*strlen("parser://")*/); |
| String::Body param_body("/"); | const char* method_cstr=lsplit(&s, '/'); |
| if(s) | const String* method=new String(method_cstr); |
| param_body.append_know_length(s, strlen(s)); | String::Body param_body("/"); |
| if(s) | |
| VString* vparam=new VString(*new String(param_body, String::L_TAINTED)); | param_body.append_know_length(s, strlen(s)); |
| { | |
| Temp_lang temp_lang(r, String::L_XML); // default language: XML | VString* vparam=new VString(*new String(param_body, String::L_TAINTED)); |
| Request::Execute_nonvirtual_method_result body= | { |
| r.execute_nonvirtual_method(r.main_class, *method, vparam, true); | Temp_lang temp_lang(r, String::L_XML); // default language: XML |
| if(body.string) { | Request::Execute_nonvirtual_method_result body= |
| MemoryStream *stream=new(UseGC) MemoryStream; | r.execute_nonvirtual_method(r.main_class, *method, vparam, true); |
| stream->m_buf=body.string->cstr(String::L_UNSPECIFIED); | if(body.string) { |
| stream->m_size=strlen(stream->m_buf); | buf=body.string->untaint_cstr(r.flang); |
| return (void*)stream; | } else |
| throw Exception(0, | |
| new String(afilename), | |
| "'%s' method not found in %s class", | |
| method_cstr, MAIN_CLASS_NAME); | |
| } | } |
| } catch(const Exception& e) { | |
| buf=e.comment(); | |
| } catch(...) { | |
| buf="xmlFileOpenLocalhost: unknown error"; | |
| } | } |
| MemoryStream* stream=new(UseGC) MemoryStream; | |
| throw Exception(0, | stream->m_buf=buf; |
| new String(afilename), | stream->m_size=strlen(buf); |
| "'%s' method not found in %s class", | return (void *)stream; |
| method_cstr, MAIN_CLASS_NAME); | |
| } | } |
| /** | |
| * pa_xmlFileReadMethod: | |
| * @context: the I/O context | |
| * @buffer: where to drop data | |
| * @len: number of bytes to write | |
| * | |
| * Read @len bytes to @buffer from the I/O channel. | |
| * | |
| * Returns the number of bytes written | |
| */ | |
| static int | static int |
| pa_xmlFileReadMethod (void * context, //< MemoryStream actually | pa_xmlFileReadMethod (void * context, //< MemoryStream actually |
| char * buffer, int len) | char * buffer, int len) |
| Line 159 pa_xmlFileCloseMethod (void * /*context* | Line 257 pa_xmlFileCloseMethod (void * /*context* |
| void pa_xml_io_init() { | void pa_xml_io_init() { |
| #ifdef PA_SAFE_MODE | // file open monitorer [for xslt cacher] |
| // safe mode checker, always fail match, but checks non-"://" there | // safe mode checker, always fail match, but checks non-"://" there |
| xmlRegisterInputCallbacks( | xmlRegisterInputCallbacks( |
| xmlFileMatchSafeMode, 0, | xmlFileMatchMonitor, xmlFileOpenMonitor, |
| 0, 0); | pa_xmlFileReadMethod, pa_xmlFileCloseMethod); |
| #endif | |
| // http://localhost/abc -> $ENV{DOCUMENT_ROOT}/abc | ./abc | // http://localhost/abc -> $ENV{DOCUMENT_ROOT}/abc | ./abc |
| xmlRegisterInputCallbacks( | xmlRegisterInputCallbacks( |
| xmlFileMatchLocalhost, xmlFileOpenLocalhost, | xmlFileMatchLocalhost, xmlFileOpenLocalhost, |