--- parser3/src/main/pa_xml_io.C 2003/11/27 15:10:01 1.2 +++ parser3/src/main/pa_xml_io.C 2004/04/08 08:46:16 1.18 @@ -1,7 +1,7 @@ /** @file Parser: plugins to xml library, controlling i/o; implementation - Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ @@ -9,88 +9,59 @@ #ifdef XML -static const char * const IDENT="$Date: 2003/11/27 15:10:01 $"; +static const char * const IDENT="$Date: 2004/04/08 08:46:16 $"; #include "libxslt/extensions.h" +#include "pa_threads.h" #include "pa_globals.h" #include "pa_request.h" -/** - * xmlFileMatchWithLocalhostEqDocumentRoot: - * filename: the URI for matching - * - * check if the URI matches an HTTP one - * - * Returns 1 if matches, 0 otherwise - */ -static int -xmlFileMatchLocalhost(const char* filename) { - if (!strncmp(filename, "http://localhost", 16)) - return(1); - return(0); -} +static Hash xml_dependencies; +static void add_dependency(const String::Body url) { + pa_thread_t thread_id=pa_get_thread_id(); + HashStringBool* urls; + { + SYNCHRONIZED; -/** - * xmlFileOpenHttpLocalhost : - * filename: the URI for matching - * - * 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 - */ -static void * -xmlFileOpenLocalhost (const char* filename) { - //_asm int 3; - FILE *fd; - const char* document_root=pa_thread_request().request_info.document_root; - if(!document_root) - document_root="."; - - char path[MAX_STRING]; - path[0]=0; - strcat(path, document_root); - strcat(path, &filename[16]); - -#ifdef WIN32 - fd = fopen(path, "rb"); -#else - fd = fopen(path, "r"); -#endif /* WIN32 */ - return((void *) fd); + // 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); } -/** - * xmlFileRead: - * @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 -pa_xmlFileRead (void * context, char * buffer, int len) { - return(fread(&buffer[0], 1, len, (FILE *) context)); +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); + } } -/** - * xmlFileClose: - * @context: the I/O context - * - * Close an I/O channel - */ -static int -pa_xmlFileClose (void * context) { - return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 ); +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 struct MemoryStream { @@ -104,45 +75,156 @@ struct MemoryStream { return 0; 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; return to_read; } }; #endif +static void * +xmlFileOpen_ReadIntoStream (const char* do_not_store_filename, bool adjust_path_to_root_from_document_root=false) { +#ifdef PA_SAFE_MODE +//copied from libxml/catalog.c +# define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog" + // disable attempts to consult default catalog [usually, that file belongs to other user/group] + if(strcmp(do_not_store_filename, XML_XML_DEFAULT_CATALOG)==0) + return 0; +#endif + + 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, "file://")) + do_not_store_filename+=7/*strlen("file://")*/; + 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_read_text(r.charsets, *new String(can_store_filename)); + } 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"; + } + 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 +} + + +/** + * xmlFileMatchWithLocalhostEqDocumentRoot: + * filename: the URI for matching + * + * check if the URI matches an HTTP one + * + * Returns 1 if matches, 0 otherwise + */ +static int +xmlFileMatchLocalhost(const char* filename) { + if (!strncmp(filename, "http://localhost", 16)) + return(1); + return(0); +} + +/** + * xmlFileOpenLocalhost: + * filename: the URI for matching + * + * http://localhost/abc -> $ENV{DOCUMENT_ROOT}/abc | ./abc + * + * Returns an I/O context or NULL in case of error + */ +static void * +xmlFileOpenLocalhost (const char* filename) { + return xmlFileOpen_ReadIntoStream(filename, true/*adjust path to root from document_root*/); +} + static int xmlFileMatchMethod(const char* filename) { - if (!strncmp(filename, "parser://", 7 /*strlen("parser://"), and check xmlFileOpenMethod*/)) + if (!strncmp(filename, "parser://", 9 /*strlen("parser://"), and check xmlFileOpenMethod*/)) return(1); return(0); } -/// parser://method/param/here -> ^/abc | ./abc +/// parser://method/param/here -> ^MAIN:method[/params/here] static void * xmlFileOpenMethod (const char* afilename) { - //_asm int 3; - Request& r=pa_thread_request(); - - char* s=pa_strdup(afilename+7 /*strlen("parser://")*/); - const char* method_cstr=lsplit(&s, '/'); - const String* method=new String(method_cstr); - { - Temp_lang temp_lang(r, String::L_XML); // default language: XML - // @todo: s=params - if(const String* body_string=r.execute_virtual_method(r.main_class, *method)) { - MemoryStream *stream=new(UseGC) MemoryStream; - stream->m_buf=body_string->cstr(String::L_UNSPECIFIED); - stream->m_size=strlen(stream->m_buf); - return (void*)stream; + const char* buf; + try { + Request& r=pa_thread_request(); + + char* s=pa_strdup(afilename+9 /*strlen("parser://")*/); + const char* method_cstr=lsplit(&s, '/'); + const String* method=new String(method_cstr); + String::Body param_body("/"); + if(s) + param_body.append_know_length(s, strlen(s)); + + VString* vparam=new VString(*new String(param_body, String::L_TAINTED)); + { + Temp_lang temp_lang(r, String::L_XML); // default language: XML + Request::Execute_nonvirtual_method_result body= + r.execute_nonvirtual_method(r.main_class, *method, vparam, true); + if(body.string) { + buf=body.string->cstr(String::L_UNSPECIFIED); + } 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"; } - - throw Exception(0, - method, - "not found (referred from parser://name/)"); + MemoryStream* stream=new(UseGC) MemoryStream; + stream->m_buf=buf; + stream->m_size=strlen(buf); + return (void *)stream; } +/** + * 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 pa_xmlFileReadMethod (void * context, //< MemoryStream actually char * buffer, int len) @@ -152,12 +234,6 @@ pa_xmlFileReadMethod (void * context, // return stream.read(buffer, len); } -/** - * xmlFileClose: - * @context: the I/O context - * - * Close an I/O channel - */ static int pa_xmlFileCloseMethod (void * /*context*/) { return 0; @@ -166,12 +242,18 @@ pa_xmlFileCloseMethod (void * /*context* void pa_xml_io_init() { + // file open monitorer [for xslt cacher] + // safe mode checker, always fail match, but checks non-"://" there + xmlRegisterInputCallbacks( + xmlFileMatchMonitor, xmlFileOpenMonitor, + pa_xmlFileReadMethod, pa_xmlFileCloseMethod); + // http://localhost/abc -> $ENV{DOCUMENT_ROOT}/abc | ./abc xmlRegisterInputCallbacks( xmlFileMatchLocalhost, xmlFileOpenLocalhost, - pa_xmlFileRead, pa_xmlFileClose); + pa_xmlFileReadMethod, pa_xmlFileCloseMethod); - // parser://method/param/here -> ^/abc | ./abc + // parser://method/param/here -> ^MAIN:method[/params/here] xmlRegisterInputCallbacks( xmlFileMatchMethod, xmlFileOpenMethod, pa_xmlFileReadMethod, pa_xmlFileCloseMethod);