Annotation of parser3/src/main/pa_xml_io.C, revision 1.48
1.1 paf 1: /** @file
2: Parser: plugins to xml library, controlling i/o; implementation
3:
1.48 ! moko 4: Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com)
1.44 moko 5: Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
1.1 paf 6: */
7:
8: #include "pa_xml_io.h"
9:
10: #ifdef XML
11:
1.48 ! moko 12: volatile const char * IDENT_PA_XML_IO_C="$Id: pa_xml_io.C,v 1.47 2025/05/26 01:56:54 moko Exp $" IDENT_PA_XML_IO_H;
1.1 paf 13:
14: #include "libxslt/extensions.h"
15:
1.17 paf 16: #include "pa_threads.h"
1.1 paf 17: #include "pa_globals.h"
18: #include "pa_request.h"
19:
1.41 moko 20: THREAD_LOCAL HashStringBool* xml_dependencies = NULL; // every TLS should be referenced elsewhere, or GC will collect it
1.17 paf 21:
1.46 moko 22: static void add_dependency(const char *url) {
1.27 moko 23: if(xml_dependencies) // do we need to monitor now?
24: xml_dependencies->put(url, true);
1.17 paf 25: }
26:
1.41 moko 27: HashStringBool* pa_xmlStartMonitoringDependencies() {
28: return xml_dependencies=new HashStringBool; // to keep another reference on TLS
1.17 paf 29: }
30:
31: void pa_xmlStopMonitoringDependencies() {
1.27 moko 32: xml_dependencies=NULL;
1.17 paf 33: }
34:
35: HashStringBool* pa_xmlGetDependencies() {
1.27 moko 36: HashStringBool* result=xml_dependencies;
37: xml_dependencies=NULL;
38: return result;
1.17 paf 39: }
40:
1.8 paf 41: #ifndef DOXYGEN
1.34 moko 42: struct MemoryStream : public PA_Allocated {
1.8 paf 43: const char* m_buf;
44: size_t m_size;
45: size_t m_position;
46:
1.36 moko 47: MemoryStream(const char *a_buf) : m_buf(a_buf), m_size(strlen(m_buf)), m_position(0) {}
48:
1.8 paf 49: int read(char* a_buffer, size_t a_size) {
50: size_t left=m_size-m_position;
51: if(!left)
52: return 0;
53:
54: size_t to_read=min(a_size, left);
1.14 paf 55: memcpy(a_buffer, m_buf+m_position, to_read);
1.8 paf 56: m_position+=to_read;
57: return to_read;
58: }
59:
60: };
61: #endif
62:
1.38 moko 63: static int xmlFileMatchMonitor(const char* /*file_spec_cstr*/) {
64: return 1; // always intercept, causing xmlFileOpenMonitor to be called
65: }
66:
67: /**
1.39 moko 68: * xmlFileOpenMonitor:
69: * afilename: the URI for matching
1.38 moko 70: *
71: * http://localhost/abc -> $ENV{DOCUMENT_ROOT}/abc | ./abc
72: *
73: * Returns an I/O context or NULL in case of error
74: */
75: static void *xmlFileOpenMonitor(const char* afilename) {
1.18 paf 76: #ifdef PA_SAFE_MODE
77: //copied from libxml/catalog.c
78: # define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
79: // disable attempts to consult default catalog [usually, that file belongs to other user/group]
1.37 moko 80: if(strcmp(afilename, XML_XML_DEFAULT_CATALOG)==0)
1.18 paf 81: return 0;
82: #endif
83:
1.15 paf 84: Request& r=pa_thread_request();
1.38 moko 85: if(!strncmp(afilename, "http://localhost", 16)) {
1.15 paf 86: const char* document_root=r.request_info.document_root;
87: if(!document_root)
88: document_root=".";
1.43 moko 89: afilename=pa_strcat(document_root, &afilename[16]);
90: } else {
1.37 moko 91: if(!strstr(afilename, "http://")) {
92: if(strstr(afilename, "file://")) {
93: afilename+=7 /*strlen("file://")*/;
1.26 misha 94: #ifdef WIN32
1.37 moko 95: if(afilename[0]=='/' && afilename[1] && afilename[2]==':' && afilename[3]=='/') {
1.26 misha 96: // skip leading slash for absolute path file:///C:/path/to/file
1.37 moko 97: afilename++;
1.26 misha 98: }
99: #endif
1.37 moko 100: } else if(afilename[0] && afilename[1]!=':' && strstr(afilename, "://")) {
1.20 misha 101: pa_xmlStopMonitoringDependencies();
102: return 0; // plug out [do not handle other prefixes]
103: }
1.30 moko 104: }
1.43 moko 105: afilename=pa_strdup(afilename);
106: }
1.17 paf 107:
1.43 moko 108: add_dependency(afilename);
1.15 paf 109:
110: const char *buf;
111: try {
1.43 moko 112: buf=file_load_text(r, *new String(afilename),
1.35 moko 113: true /*fail_on_read_problem*/,
114: 0 /*params*/,
115: false /*don't transcode result because it must be fit with @encoding value!*/);
1.15 paf 116: } catch(const Exception& e) {
1.17 paf 117: if(strcmp(e.type(), "file.missing")==0)
118: return 0; // let the library try that and report an error properly
119:
1.15 paf 120: buf=e.comment();
121: } catch(...) {
1.17 paf 122: buf="xmlFileOpen_ReadIntoStream: unknown error";
1.15 paf 123: }
1.36 moko 124: return (void *)new MemoryStream(buf);
1.15 paf 125: }
1.9 paf 126:
1.35 moko 127: static int xmlFileMatchMethod(const char* filename) {
128: return !strncmp(filename, "parser://", 9 /*strlen("parser://"), and check xmlFileOpenMethod*/);
1.2 paf 129: }
130:
1.5 paf 131: /// parser://method/param/here -> ^MAIN:method[/params/here]
1.35 moko 132: static void *xmlFileOpenMethod (const char* afilename) {
1.15 paf 133: const char* buf;
134: try {
135: Request& r=pa_thread_request();
136:
137: char* s=pa_strdup(afilename+9 /*strlen("parser://")*/);
138: const char* method_cstr=lsplit(&s, '/');
139: const String* method=new String(method_cstr);
1.40 moko 140: String::Body param_body("/");
1.15 paf 141: if(s)
142: param_body.append_know_length(s, strlen(s));
143:
1.47 moko 144: const String *body=r.execute_method(r.main_class, *method, new VString(param_body));
1.40 moko 145: if(!body)
1.32 moko 146: throw Exception(0, new String(afilename), "'%s' method not found in %s class", method_cstr, MAIN_CLASS_NAME);
1.40 moko 147: buf=body->untaint_cstr(String::L_XML);
1.15 paf 148: } catch(const Exception& e) {
149: buf=e.comment();
150: } catch(...) {
151: buf="xmlFileOpenLocalhost: unknown error";
1.2 paf 152: }
1.36 moko 153: return (void *)new MemoryStream(buf);
1.2 paf 154: }
155:
1.15 paf 156: /**
157: * pa_xmlFileReadMethod:
158: * @context: the I/O context
159: * @buffer: where to drop data
160: * @len: number of bytes to write
161: *
162: * Read @len bytes to @buffer from the I/O channel.
163: *
164: * Returns the number of bytes written
165: */
1.35 moko 166: static int pa_xmlFileReadMethod (void* context, char* buffer, int len){
1.2 paf 167: MemoryStream& stream=*static_cast<MemoryStream*>(context);
168: return stream.read(buffer, len);
169: }
170:
1.35 moko 171: static int pa_xmlFileCloseMethod (void* /*context*/) {
1.2 paf 172: return 0;
173: }
174:
1.1 paf 175: void pa_xml_io_init() {
1.17 paf 176: // file open monitorer [for xslt cacher]
1.9 paf 177: // safe mode checker, always fail match, but checks non-"://" there
1.35 moko 178: xmlRegisterInputCallbacks(xmlFileMatchMonitor, xmlFileOpenMonitor, pa_xmlFileReadMethod, pa_xmlFileCloseMethod);
1.17 paf 179:
1.37 moko 180: // parser://method/param/here -> ^MAIN:method[/params/here] - should be last to be called first
1.35 moko 181: xmlRegisterInputCallbacks(xmlFileMatchMethod, xmlFileOpenMethod, pa_xmlFileReadMethod, pa_xmlFileCloseMethod);
1.1 paf 182: }
183:
184: #endif
E-mail: