--- parser3/src/main/pa_xml_io.C 2003/11/28 09:36:04 1.6 +++ parser3/src/main/pa_xml_io.C 2025/05/26 00:52:15 1.46 @@ -1,109 +1,58 @@ /** @file Parser: plugins to xml library, controlling i/o; implementation - Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com) - Author: Alexandr Petrosian (http://paf.design.ru) + Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com) + Authors: Konstantin Morshnev , Alexandr Petrosian */ #include "pa_xml_io.h" #ifdef XML -static const char * const IDENT="$Date: 2003/11/28 09:36:04 $"; +volatile const char * IDENT_PA_XML_IO_C="$Id: pa_xml_io.C,v 1.46 2025/05/26 00:52:15 moko Exp $" IDENT_PA_XML_IO_H; #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); -} - +THREAD_LOCAL HashStringBool* xml_dependencies = NULL; // every TLS should be referenced elsewhere, or GC will collect it -/** - * 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) { - 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); +static void add_dependency(const char *url) { + if(xml_dependencies) // do we need to monitor now? + xml_dependencies->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)); +HashStringBool* pa_xmlStartMonitoringDependencies() { + return xml_dependencies=new HashStringBool; // to keep another reference on TLS } -/** - * 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() { + xml_dependencies=NULL; } -////////////////////////// +HashStringBool* pa_xmlGetDependencies() { + HashStringBool* result=xml_dependencies; + xml_dependencies=NULL; + return result; +} #ifndef DOXYGEN -struct MemoryStream { +struct MemoryStream : public PA_Allocated { const char* m_buf; size_t m_size; size_t m_position; + MemoryStream(const char *a_buf) : m_buf(a_buf), m_size(strlen(m_buf)), m_position(0) {} + int read(char* a_buffer, size_t a_size) { size_t left=m_size-m_position; if(!left) 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; } @@ -111,76 +60,125 @@ struct MemoryStream { }; #endif -static int -xmlFileMatchMethod(const char* filename) { - if (!strncmp(filename, "parser://", 9 /*strlen("parser://"), and check xmlFileOpenMethod*/)) - return(1); - return(0); +static int xmlFileMatchMonitor(const char* /*file_spec_cstr*/) { + return 1; // always intercept, causing xmlFileOpenMonitor to be called } -/// parser://method/param/here -> ^MAIN:method[/params/here] -static void * -xmlFileOpenMethod (const char* afilename) { - Request& r=pa_thread_request(); +/** + * xmlFileOpenMonitor: + * afilename: 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 *xmlFileOpenMonitor(const char* afilename) { +#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(afilename, XML_XML_DEFAULT_CATALOG)==0) + return 0; +#endif - 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) { - MemoryStream *stream=new(UseGC) MemoryStream; - stream->m_buf=body.string->cstr(String::L_UNSPECIFIED); - stream->m_size=strlen(stream->m_buf); - return (void*)stream; + Request& r=pa_thread_request(); + if(!strncmp(afilename, "http://localhost", 16)) { + const char* document_root=r.request_info.document_root; + if(!document_root) + document_root="."; + afilename=pa_strcat(document_root, &afilename[16]); + } else { + if(!strstr(afilename, "http://")) { + if(strstr(afilename, "file://")) { + afilename+=7 /*strlen("file://")*/; +#ifdef WIN32 + if(afilename[0]=='/' && afilename[1] && afilename[2]==':' && afilename[3]=='/') { + // skip leading slash for absolute path file:///C:/path/to/file + afilename++; + } +#endif + } else if(afilename[0] && afilename[1]!=':' && strstr(afilename, "://")) { + pa_xmlStopMonitoringDependencies(); + return 0; // plug out [do not handle other prefixes] + } } + afilename=pa_strdup(afilename); } - throw Exception(0, - new String(afilename), - "'%s' method not found in %s class", - method_cstr, MAIN_CLASS_NAME); + add_dependency(afilename); + + const char *buf; + try { + buf=file_load_text(r, *new String(afilename), + 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 (void *)new MemoryStream(buf); } -static int -pa_xmlFileReadMethod (void * context, //< MemoryStream actually - char * buffer, int len) -{ - MemoryStream& stream=*static_cast(context); +static int xmlFileMatchMethod(const char* filename) { + return !strncmp(filename, "parser://", 9 /*strlen("parser://"), and check xmlFileOpenMethod*/); +} - return stream.read(buffer, len); +/// parser://method/param/here -> ^MAIN:method[/params/here] +static void *xmlFileOpenMethod (const char* afilename) { + 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)); + + const String *body=r.execute_method(r.main_class, *method, new VString(*new String(param_body, String::L_TAINTED))); + if(!body) + throw Exception(0, new String(afilename), "'%s' method not found in %s class", method_cstr, MAIN_CLASS_NAME); + buf=body->untaint_cstr(String::L_XML); + } catch(const Exception& e) { + buf=e.comment(); + } catch(...) { + buf="xmlFileOpenLocalhost: unknown error"; + } + return (void *)new MemoryStream(buf); } /** - * xmlFileClose: + * 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. * - * Close an I/O channel + * Returns the number of bytes written */ -static int -pa_xmlFileCloseMethod (void * /*context*/) { - return 0; +static int pa_xmlFileReadMethod (void* context, char* buffer, int len){ + MemoryStream& stream=*static_cast(context); + return stream.read(buffer, len); } - +static int pa_xmlFileCloseMethod (void* /*context*/) { + return 0; +} void pa_xml_io_init() { - // http://localhost/abc -> $ENV{DOCUMENT_ROOT}/abc | ./abc - xmlRegisterInputCallbacks( - xmlFileMatchLocalhost, xmlFileOpenLocalhost, - pa_xmlFileRead, pa_xmlFileClose); - - // parser://method/param/here -> ^MAIN:method[/params/here] - xmlRegisterInputCallbacks( - xmlFileMatchMethod, xmlFileOpenMethod, - pa_xmlFileReadMethod, pa_xmlFileCloseMethod); + // file open monitorer [for xslt cacher] + // safe mode checker, always fail match, but checks non-"://" there + xmlRegisterInputCallbacks(xmlFileMatchMonitor, xmlFileOpenMonitor, pa_xmlFileReadMethod, pa_xmlFileCloseMethod); + + // parser://method/param/here -> ^MAIN:method[/params/here] - should be last to be called first + xmlRegisterInputCallbacks(xmlFileMatchMethod, xmlFileOpenMethod, pa_xmlFileReadMethod, pa_xmlFileCloseMethod); } #endif