1 /*
2   libxslt.c: this module implements the main part of the glue of the
3  *           libxslt library and the Python interpreter. It provides the
4  *           entry points where an automatically generated stub is either
5  *           unpractical or would not match cleanly the Python model.
6  *
7  * If compiled with MERGED_MODULES, the entry point will be used to
8  * initialize both the libxml2 and the libxslt wrappers
9  *
10  * See Copyright for the status of this software.
11  *
12  * daniel@veillard.com
13  */
14 #include <Python.h>
15 /* #include "config.h" */
16 #include <libxml/xmlmemory.h>
17 #include <libxml/tree.h>
18 #include <libxml/xpath.h>
19 #include "libexslt/exslt.h"
20 #include "libxslt_wrap.h"
21 #include "libxslt-py.h"
22 
23 #include <stdio.h>
24 #include <stddef.h>
25 
26 #ifdef _MSC_VER
27 
28 /* snprintf emulation taken from http://stackoverflow.com/a/8712996/1956010 */
29 #if _MSC_VER < 1900
30 
31 #include <stdarg.h>
32 
33 #define vsnprintf c99_vsnprintf
34 
c99_vsnprintf(char * outBuf,size_t size,const char * format,va_list ap)35 __inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
36 {
37     int count = -1;
38 
39     if (size != 0)
40         count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
41     if (count == -1)
42         count = _vscprintf(format, ap);
43 
44     return count;
45 }
46 
47 #endif /* _MSC_VER < 1900 */
48 
49 #elif defined(XSLT_NEED_TRIO)
50 #include "trio.h"
51 #define vsnprintf trio_vsnprintf
52 #endif
53 
54 /* #define DEBUG */
55 /* #define DEBUG_XPATH */
56 /* #define DEBUG_ERROR */
57 /* #define DEBUG_MEMORY */
58 /* #define DEBUG_EXTENSIONS */
59 /* #define DEBUG_EXTENSIONS */
60 
61 void initlibxsltmod(void);
62 
63 /************************************************************************
64  *									*
65  *			Per type specific glue				*
66  *									*
67  ************************************************************************/
68 
69 PyObject *
libxslt_xsltStylesheetPtrWrap(xsltStylesheetPtr style)70 libxslt_xsltStylesheetPtrWrap(xsltStylesheetPtr style) {
71     PyObject *ret;
72 
73 #ifdef DEBUG
74     printf("libxslt_xsltStylesheetPtrWrap: style = %p\n", style);
75 #endif
76     if (style == NULL) {
77 	Py_INCREF(Py_None);
78 	return(Py_None);
79     }
80     ret = PyCObject_FromVoidPtrAndDesc((void *) style,
81 	                               (char *)"xsltStylesheetPtr", NULL);
82 
83     return(ret);
84 }
85 
86 PyObject *
libxslt_xsltTransformContextPtrWrap(xsltTransformContextPtr ctxt)87 libxslt_xsltTransformContextPtrWrap(xsltTransformContextPtr ctxt) {
88     PyObject *ret;
89 
90 #ifdef DEBUG
91     printf("libxslt_xsltTransformContextPtrWrap: ctxt = %p\n", ctxt);
92 #endif
93     if (ctxt == NULL) {
94 	Py_INCREF(Py_None);
95 	return(Py_None);
96     }
97     ret = PyCObject_FromVoidPtrAndDesc((void *) ctxt,
98 	                               (char *)"xsltTransformContextPtr", NULL);
99     return(ret);
100 }
101 
102 PyObject *
libxslt_xsltElemPreCompPtrWrap(xsltElemPreCompPtr ctxt)103 libxslt_xsltElemPreCompPtrWrap(xsltElemPreCompPtr ctxt) {
104     PyObject *ret;
105 
106 #ifdef DEBUG
107     printf("libxslt_xsltElemPreCompPtrWrap: ctxt = %p\n", ctxt);
108 #endif
109     if (ctxt == NULL) {
110 	Py_INCREF(Py_None);
111 	return(Py_None);
112     }
113     ret = PyCObject_FromVoidPtrAndDesc((void *) ctxt,
114 	                               (char *)"xsltElemPreCompPtr", NULL);
115     return(ret);
116 }
117 
118 PyObject *
libxslt_xsltGetTransformContextHashCode(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)119 libxslt_xsltGetTransformContextHashCode(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
120     PyObject *py_tctxt;
121     PyObject *ret;
122     long hash_code;
123     xsltTransformContextPtr tctxt;
124 
125     if (!PyArg_ParseTuple(args, (char *)"O:getTransformContextHashCode",
126                           &py_tctxt))
127         return NULL;
128 
129     tctxt =  (xsltTransformContextPtr) PytransformCtxt_Get(py_tctxt);
130     hash_code = (ptrdiff_t) tctxt;
131 
132     ret = PyInt_FromLong(hash_code);
133     return ret;
134 }
135 
136 PyObject *
libxslt_xsltCompareTransformContextsEqual(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)137 libxslt_xsltCompareTransformContextsEqual(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
138 
139     PyObject *py_tctxt1, *py_tctxt2;
140     xsltTransformContextPtr tctxt1, tctxt2;
141 
142     if (!PyArg_ParseTuple(args, (char *)"OO:compareTransformContextsEqual",
143                           &py_tctxt1, &py_tctxt2))
144         return NULL;
145 
146     tctxt1 = (xsltTransformContextPtr) PytransformCtxt_Get(py_tctxt1);
147     tctxt2 = (xsltTransformContextPtr) PytransformCtxt_Get(py_tctxt2);
148 
149     if ( tctxt1 == tctxt2 )
150         return Py_BuildValue((char *)"i", 1);
151     else
152         return Py_BuildValue((char *)"i", 0);
153 }
154 
155 PyObject *
libxslt_xsltGetStylesheetHashCode(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)156 libxslt_xsltGetStylesheetHashCode(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
157     PyObject *py_style;
158     PyObject *ret;
159     long hash_code;
160     xsltStylesheetPtr style;
161 
162     if (!PyArg_ParseTuple(args, (char *)"O:getStylesheetHashCode",
163                           &py_style))
164         return NULL;
165 
166     style =  (xsltStylesheetPtr) Pystylesheet_Get(py_style);
167     hash_code = (ptrdiff_t) style;
168 
169     ret = PyInt_FromLong(hash_code);
170     return ret;
171 }
172 
173 
174 PyObject *
libxslt_xsltCompareStylesheetsEqual(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)175 libxslt_xsltCompareStylesheetsEqual(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
176 
177     PyObject *py_style1, *py_style2;
178     xsltStylesheetPtr style1, style2;
179 
180     if (!PyArg_ParseTuple(args, (char *)"OO:compareStylesheetsEqual",
181                           &py_style1, &py_style2))
182         return NULL;
183 
184     style1 = (xsltStylesheetPtr) Pystylesheet_Get(py_style1);
185     style2 = (xsltStylesheetPtr) Pystylesheet_Get(py_style2);
186 
187     if ( style1 == style2 )
188         return Py_BuildValue((char *)"i", 1);
189     else
190         return Py_BuildValue((char *)"i", 0);
191 }
192 
193 /************************************************************************
194  *									*
195  *			Extending the API				*
196  *									*
197  ************************************************************************/
198 
199 static xmlHashTablePtr libxslt_extModuleFunctions = NULL;
200 static xmlHashTablePtr libxslt_extModuleElements = NULL;
201 static xmlHashTablePtr libxslt_extModuleElementPreComp = NULL;
202 
203 static void
deallocateCallback(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)204 deallocateCallback(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
205     PyObject *function = (PyObject *) payload;
206 
207 #ifdef DEBUG_EXTENSIONS
208     printf("deallocateCallback(%s) called\n", name);
209 #endif
210 
211     Py_XDECREF(function);
212 }
213 
214 static void
deallocateClasse(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)215 deallocateClasse(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
216     PyObject *class = (PyObject *) payload;
217 
218 #ifdef DEBUG_EXTENSIONS
219     printf("deallocateClasse(%s) called\n", name);
220 #endif
221 
222     Py_XDECREF(class);
223 }
224 
225 
226 /**
227  * libxslt_xsltElementPreCompCallback
228  * @style:  the stylesheet
229  * @inst:  the instruction in the stylesheet
230  *
231  * Callback for preprocessing of a custom element
232  */
233 static xsltElemPreCompPtr
libxslt_xsltElementPreCompCallback(xsltStylesheetPtr style,xmlNodePtr inst,xsltTransformFunction function)234 libxslt_xsltElementPreCompCallback(xsltStylesheetPtr style, xmlNodePtr inst,
235               xsltTransformFunction function) {
236     xsltElemPreCompPtr ret;
237     const xmlChar *name;
238     PyObject *args;
239     PyObject *result;
240     PyObject *pyobj_element_f;
241     PyObject *pyobj_precomp_f;
242 
243    const xmlChar *ns_uri;
244 
245 
246 #ifdef DEBUG_EXTENSIONS
247     printf("libxslt_xsltElementPreCompCallback called\n");
248 #endif
249 
250     if (style == NULL) {
251 	xsltTransformError(NULL, NULL, inst,
252 	     "libxslt_xsltElementPreCompCallback: no transformation context\n");
253 	    return (NULL);
254     }
255 
256     if (inst == NULL) {
257 	xsltTransformError(NULL, style, inst,
258 	     "libxslt_xsltElementPreCompCallback: no instruction\n");
259 	if (style != NULL) style->errors++;
260 	return (NULL);
261     }
262 
263     if (style == NULL)
264 	return (NULL);
265 
266     if (inst != NULL && inst->ns != NULL) {
267 	name = inst->name;
268 	ns_uri = inst->ns->href;
269     } else {
270 	xsltTransformError(NULL, style, inst,
271 		"libxslt_xsltElementPreCompCallback: internal error bad parameter\n");
272 		printf("libxslt_xsltElementPreCompCallback: internal error bad parameter\n");
273 	if (style != NULL) style->errors++;
274 	return (NULL);
275     }
276 
277     /*
278      * Find the functions, they should be there it was there at lookup
279      */
280     pyobj_precomp_f = xmlHashLookup2(libxslt_extModuleElementPreComp,
281 	                              name, ns_uri);
282     if (pyobj_precomp_f == NULL) {
283 	xsltTransformError(NULL, style, inst,
284 		"libxslt_xsltElementPreCompCallback: internal error, could not find precompile python function!\n");
285 	if (style != NULL) style->errors++;
286 	return (NULL);
287     }
288 
289     pyobj_element_f = xmlHashLookup2(libxslt_extModuleElements,
290 	                              name, ns_uri);
291     if (pyobj_element_f == NULL) {
292 	xsltTransformError(NULL, style, inst,
293 		"libxslt_xsltElementPreCompCallback: internal error, could not find element python function!\n");
294 	if (style != NULL) style->errors++;
295 	return (NULL);
296     }
297 
298     args = Py_BuildValue((char *)"(OOO)",
299 	    libxslt_xsltStylesheetPtrWrap(style),
300 	    libxml_xmlNodePtrWrap(inst),
301 	    pyobj_element_f);
302 
303     Py_INCREF(pyobj_precomp_f); /* Protect refcount against reentrant manipulation of callback hash */
304     result = PyEval_CallObject(pyobj_precomp_f, args);
305     Py_DECREF(pyobj_precomp_f);
306     Py_DECREF(args);
307 
308     /* FIXME allow callbacks to return meaningful information to modify compile process */
309     /* If error, do we need to check the result and throw exception? */
310 
311     Py_XDECREF(result);
312 
313     ret = xsltNewElemPreComp (style, inst, function);
314     return (ret);
315 }
316 
317 
318 static void
libxslt_xsltElementTransformCallback(xsltTransformContextPtr ctxt,xmlNodePtr node,xmlNodePtr inst,xsltElemPreCompPtr comp)319 libxslt_xsltElementTransformCallback(xsltTransformContextPtr ctxt,
320 				    xmlNodePtr node,
321 				    xmlNodePtr inst,
322 				    xsltElemPreCompPtr comp)
323 {
324     PyObject *args, *result;
325     PyObject *func = NULL;
326     const xmlChar *name;
327     const xmlChar *ns_uri;
328 
329     if (ctxt == NULL)
330 	return;
331 
332     if (inst != NULL && inst->name != NULL && inst->ns != NULL && inst->ns->href != NULL) {
333 	name = inst->name;
334 	ns_uri = inst->ns->href;
335     } else {
336 	printf("libxslt_xsltElementTransformCallback: internal error bad parameter\n");
337 	return;
338     }
339 
340 #ifdef DEBUG_EXTENSIONS
341     printf("libxslt_xsltElementTransformCallback called name %s URI %s\n", name, ns_uri);
342 #endif
343 
344     /*
345      * Find the function, it should be there it was there at lookup
346      */
347     func = xmlHashLookup2(libxslt_extModuleElements,
348 	                              name, ns_uri);
349     if (func == NULL) {
350 	printf("libxslt_xsltElementTransformCallback: internal error %s not found !\n",
351 	       name);
352 	return;
353     }
354 
355     args = Py_BuildValue((char *)"OOOO",
356 	libxslt_xsltTransformContextPtrWrap(ctxt),
357 	libxml_xmlNodePtrWrap(node),
358 	libxml_xmlNodePtrWrap(inst),
359 	libxslt_xsltElemPreCompPtrWrap(comp));
360 
361     Py_INCREF(func); /* Protect refcount against reentrant manipulation of callback hash */
362     result = PyEval_CallObject(func, args);
363     Py_DECREF(func);
364     Py_DECREF(args);
365 
366     /* FIXME Check result of callobject and set exception if fail */
367 
368     Py_XDECREF(result);
369 }
370 
371 PyObject *
libxslt_xsltRegisterExtModuleElement(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)372 libxslt_xsltRegisterExtModuleElement(PyObject *self ATTRIBUTE_UNUSED,
373 	                              PyObject *args) {
374     PyObject *py_retval;
375     int ret = 0;
376     xmlChar *name;
377     xmlChar *ns_uri;
378     PyObject *pyobj_element_f;
379     PyObject *pyobj_precomp_f;
380 
381     if (!PyArg_ParseTuple(args, (char *)"szOO:registerExtModuleElement",
382 		          &name, &ns_uri, &pyobj_precomp_f, &pyobj_element_f))
383         return(NULL);
384 
385 #ifdef DEBUG_EXTENSIONS
386     printf("libxslt_xsltRegisterExtModuleElement called: %s %s\n",
387 	   name, ns_uri);
388 #endif
389 
390     if ((name == NULL) || (pyobj_element_f == NULL) || (pyobj_precomp_f == NULL)) {
391 	py_retval = libxml_intWrap(-1);
392 	return(py_retval);
393     }
394 
395 #ifdef DEBUG_EXTENSIONS
396     printf("libxslt_xsltRegisterExtModuleElement(%s, %s) called\n",
397 	   name, ns_uri);
398 #endif
399 
400     if (libxslt_extModuleElements == NULL)
401 	libxslt_extModuleElements = xmlHashCreate(10);
402 
403     if (libxslt_extModuleElementPreComp == NULL)
404 	libxslt_extModuleElementPreComp = xmlHashCreate(10);
405 
406     if (libxslt_extModuleElements == NULL || libxslt_extModuleElementPreComp == NULL) {
407 	py_retval = libxml_intWrap(-1);
408 	return(py_retval);
409     }
410 
411     ret = xmlHashAddEntry2(libxslt_extModuleElements, name, ns_uri, pyobj_element_f);
412     if (ret != 0) {
413 	py_retval = libxml_intWrap(-1);
414 	return(py_retval);
415     }
416     Py_XINCREF(pyobj_element_f);
417 
418     ret = xmlHashAddEntry2(libxslt_extModuleElementPreComp, name, ns_uri, pyobj_precomp_f);
419     if (ret != 0) {
420 	xmlHashRemoveEntry2(libxslt_extModuleElements, name, ns_uri, deallocateCallback);
421 	py_retval = libxml_intWrap(-1);
422 	return(py_retval);
423     }
424     Py_XINCREF(pyobj_precomp_f);
425 
426     ret = xsltRegisterExtModuleElement(name, ns_uri,
427 					libxslt_xsltElementPreCompCallback,
428 					libxslt_xsltElementTransformCallback);
429     py_retval = libxml_intWrap((int) ret);
430     return(py_retval);
431 }
432 static void
libxslt_xmlXPathFuncCallback(xmlXPathParserContextPtr ctxt,int nargs)433 libxslt_xmlXPathFuncCallback(xmlXPathParserContextPtr ctxt, int nargs) {
434     PyObject *list, *cur, *result;
435     xmlXPathObjectPtr obj;
436     xmlXPathContextPtr rctxt;
437     PyObject *current_function = NULL;
438     const xmlChar *name;
439     const xmlChar *ns_uri;
440     int i;
441 
442     if (ctxt == NULL)
443 	return;
444     rctxt = ctxt->context;
445     if (rctxt == NULL)
446 	return;
447     name = rctxt->function;
448     ns_uri = rctxt->functionURI;
449 #ifdef DEBUG_XPATH
450     printf("libxslt_xmlXPathFuncCallback called name %s URI %s\n", name, ns_uri);
451 #endif
452 
453     /*
454      * Find the function, it should be there it was there at lookup
455      */
456     current_function = xmlHashLookup2(libxslt_extModuleFunctions,
457 	                              name, ns_uri);
458     if (current_function == NULL) {
459 	printf("libxslt_xmlXPathFuncCallback: internal error %s not found !\n",
460 	       name);
461 	return;
462     }
463 
464     list = PyTuple_New(nargs + 1);
465     PyTuple_SetItem(list, 0, libxml_xmlXPathParserContextPtrWrap(ctxt));
466     for (i = nargs - 1;i >= 0;i--) {
467 	obj = valuePop(ctxt);
468 	cur = libxml_xmlXPathObjectPtrWrap(obj);
469 	PyTuple_SetItem(list, i + 1, cur);
470     }
471 
472     Py_INCREF(current_function);
473     result = PyEval_CallObject(current_function, list);
474     Py_DECREF(current_function);
475     Py_DECREF(list);
476 
477     /* Check for null in case of exception */
478     if (result != NULL) {
479 	obj = libxml_xmlXPathObjectPtrConvert(result);
480 	valuePush(ctxt, obj);
481     }
482 }
483 
484 PyObject *
libxslt_xsltRegisterExtModuleFunction(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)485 libxslt_xsltRegisterExtModuleFunction(PyObject *self ATTRIBUTE_UNUSED,
486 	                              PyObject *args) {
487     PyObject *py_retval;
488     int ret = 0;
489     xmlChar *name;
490     xmlChar *ns_uri;
491     PyObject *pyobj_f;
492 
493     if (!PyArg_ParseTuple(args, (char *)"szO:registerExtModuleFunction",
494 		          &name, &ns_uri, &pyobj_f))
495         return(NULL);
496 
497     if ((name == NULL) || (pyobj_f == NULL)) {
498 	py_retval = libxml_intWrap(-1);
499 	return(py_retval);
500     }
501 
502 #ifdef DEBUG_XPATH
503     printf("libxslt_xsltRegisterExtModuleFunction(%s, %s) called\n",
504 	   name, ns_uri);
505 #endif
506 
507     if (libxslt_extModuleFunctions == NULL)
508 	libxslt_extModuleFunctions = xmlHashCreate(10);
509     if (libxslt_extModuleFunctions == NULL) {
510 	py_retval = libxml_intWrap(-1);
511 	return(py_retval);
512     }
513     ret = xmlHashAddEntry2(libxslt_extModuleFunctions, name, ns_uri, pyobj_f);
514     if (ret != 0) {
515 	py_retval = libxml_intWrap(-1);
516 	return(py_retval);
517     }
518     Py_XINCREF(pyobj_f);
519 
520     ret = xsltRegisterExtModuleFunction(name, ns_uri,
521 	                                     libxslt_xmlXPathFuncCallback);
522     py_retval = libxml_intWrap((int) ret);
523     return(py_retval);
524 }
525 
526 
527 /************************************************************************
528  *									*
529  *			Document loading front-ends			*
530  *									*
531  ************************************************************************/
532 
533 static PyObject *pythonDocLoaderObject = NULL;
534 
535 static xmlDocPtr
pythonDocLoaderFuncWrapper(const xmlChar * URI,xmlDictPtr dict,int options,void * ctxt ATTRIBUTE_UNUSED,xsltLoadType type ATTRIBUTE_UNUSED)536 pythonDocLoaderFuncWrapper(const xmlChar * URI, xmlDictPtr dict, int options,
537                            void *ctxt ATTRIBUTE_UNUSED,
538                            xsltLoadType type ATTRIBUTE_UNUSED)
539 {
540     xmlParserCtxtPtr pctxt;
541     xmlDocPtr doc=NULL;
542 
543     pctxt = xmlNewParserCtxt();
544     if (pctxt == NULL)
545         return(NULL);
546     if ((dict != NULL) && (pctxt->dict != NULL)) {
547         xmlDictFree(pctxt->dict);
548 	pctxt->dict = NULL;
549     }
550     if (dict != NULL) {
551 	pctxt->dict = dict;
552 	xmlDictReference(pctxt->dict);
553 #ifdef WITH_XSLT_DEBUG
554 	xsltGenericDebug(xsltGenericDebugContext,
555                      "Reusing dictionary for document\n");
556 #endif
557     }
558     xmlCtxtUseOptions(pctxt, options);
559 
560     /*
561      * Now pass to python the URI, the xsltParserContext and the context
562      * (either a transformContext or a stylesheet) and get back an xmlDocPtr
563      */
564     if (pythonDocLoaderObject != NULL) {
565         PyObject *ctxtobj, *pctxtobj, *result;
566         pctxtobj = libxml_xmlParserCtxtPtrWrap(pctxt);
567 
568         if (type == XSLT_LOAD_DOCUMENT) {
569           ctxtobj = libxslt_xsltTransformContextPtrWrap(ctxt);
570           result = PyObject_CallFunction(pythonDocLoaderObject,
571                                          (char *) "(sOOi)", URI, pctxtobj, ctxtobj, 0);
572         }
573         else {
574           ctxtobj = libxslt_xsltStylesheetPtrWrap(ctxt);
575           result = PyObject_CallFunction(pythonDocLoaderObject,
576                                          (char *) "(sOOi)", URI, pctxtobj, ctxtobj, 1);
577         }
578 
579 	Py_XDECREF(pctxtobj);
580 
581         if (result != NULL) {
582             /*
583 	     * The return value should be the document
584              * Should we test it somehow before getting the C object from it?
585 	     */
586             PyObject *py_doc = PyObject_GetAttrString(result, (char *) "_o");
587             doc = (xmlDocPtr) PyxmlNode_Get(py_doc);
588             /* do we have to DECCREF the result?? */
589         }
590     }
591 
592     if (! pctxt->wellFormed) {
593         if (doc != NULL) {
594             xmlFreeDoc(doc);
595 	    doc = NULL;
596         }
597         if (pctxt->myDoc != NULL) {
598             xmlFreeDoc(pctxt->myDoc);
599             pctxt->myDoc = NULL;
600         }
601     }
602     /*
603      * xmlFreeParserCtxt(pctxt);
604      * libc complains about double free-ing with this line
605      */
606 
607     return(doc);
608 }
609 
610 
611 PyObject *
libxslt_xsltSetLoaderFunc(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)612 libxslt_xsltSetLoaderFunc(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
613     PyObject *py_retval;
614     PyObject *loader;
615 
616     if (!PyArg_ParseTuple(args, (char *)"O:libxslt_xsltSetLoaderFunc",
617 		&loader))
618 	return(NULL);
619 
620     pythonDocLoaderObject = loader;
621     xsltSetLoaderFunc(pythonDocLoaderFuncWrapper);
622 
623     py_retval = PyInt_FromLong(0);
624     return(py_retval);
625 }
626 
627 PyObject *
libxslt_xsltGetLoaderFunc(void)628 libxslt_xsltGetLoaderFunc(void) {
629     PyObject *py_retval;
630 
631     py_retval = pythonDocLoaderObject;
632     return(py_retval);
633 }
634 
635 
636 /************************************************************************
637  *									*
638  *			Some customized front-ends			*
639  *									*
640  ************************************************************************/
641 
642 PyObject *
libxslt_xsltNewTransformContext(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)643 libxslt_xsltNewTransformContext(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
644     PyObject *py_retval;
645     PyObject *pyobj_style;
646     PyObject *pyobj_doc;
647     xsltStylesheetPtr style;
648     xmlDocPtr doc;
649     xsltTransformContextPtr c_retval;
650 
651     if (!PyArg_ParseTuple(args, (char *) "OO:xsltNewTransformContext",
652 		          &pyobj_style, &pyobj_doc))
653         return(NULL);
654 
655     style = (xsltStylesheetPtr) Pystylesheet_Get(pyobj_style);
656     doc = (xmlDocPtr) PyxmlNode_Get(pyobj_doc);
657 
658     c_retval = xsltNewTransformContext(style, doc);
659     py_retval = libxslt_xsltTransformContextPtrWrap((xsltTransformContextPtr) c_retval);
660     return (py_retval);
661 }
662 
663 PyObject *
libxslt_xsltFreeTransformContext(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)664 libxslt_xsltFreeTransformContext(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
665     PyObject *py_tctxt;
666     xsltTransformContextPtr tctxt;
667 
668     if (!PyArg_ParseTuple(args, (char *) "O:xsltFreeTransformContext", &py_tctxt))
669         return(NULL);
670 
671     tctxt = (xsltTransformContextPtr) PytransformCtxt_Get(py_tctxt);
672     xsltFreeTransformContext(tctxt);
673 
674     /* Return None */
675     Py_INCREF(Py_None);
676     return(Py_None);
677 }
678 
679 PyObject *
libxslt_xsltApplyStylesheetUser(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)680 libxslt_xsltApplyStylesheetUser(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
681     PyObject *py_retval;
682     xmlDocPtr c_retval;
683     xsltStylesheetPtr style;
684     PyObject *pyobj_style;
685     xmlDocPtr doc;
686     xsltTransformContextPtr transformCtxt;
687     PyObject *pyobj_doc;
688     PyObject *pyobj_params;
689     PyObject *pyobj_transformCtxt;
690     const char **params = NULL;
691     int len = 0, i, j;
692     ssize_t ppos = 0;
693     PyObject *name;
694     PyObject *value;
695 
696     if (!PyArg_ParseTuple(args, (char *) "OOOO:xsltApplyStylesheetUser",
697 		          &pyobj_style, &pyobj_doc, &pyobj_params, &pyobj_transformCtxt))
698         return(NULL);
699 
700     if (pyobj_params != Py_None) {
701 	if (PyDict_Check(pyobj_params)) {
702 	    len = PyDict_Size(pyobj_params);
703 	    if (len > 0) {
704 		params = (const char **) xmlMalloc((len + 1) * 2 *
705 			                           sizeof(char *));
706 		if (params == NULL) {
707 		    printf("libxslt_xsltApplyStylesheet: out of memory\n");
708 		    Py_INCREF(Py_None);
709 		    return(Py_None);
710 		}
711 		j = 0;
712 		while (PyDict_Next(pyobj_params, &ppos, &name, &value)) {
713 		    const char *tmp;
714 		    int size;
715 
716 		    tmp = PyString_AS_STRING(name);
717 		    size = PyString_GET_SIZE(name);
718 		    params[j * 2] = (char *) xmlCharStrndup(tmp, size);
719 		    if (PyString_Check(value)) {
720 			tmp = PyString_AS_STRING(value);
721 			size = PyString_GET_SIZE(value);
722 			params[(j * 2) + 1] = (char *)
723 			    xmlCharStrndup(tmp, size);
724 		    } else {
725 			params[(j * 2) + 1] = NULL;
726 		    }
727 		    j = j + 1;
728 		}
729 		params[j * 2] = NULL;
730 		params[(j * 2) + 1] = NULL;
731 	    }
732 	} else {
733 	    printf("libxslt_xsltApplyStylesheet: parameters not a dict\n");
734 	    Py_INCREF(Py_None);
735 	    return(Py_None);
736 	}
737     }
738     style = (xsltStylesheetPtr) Pystylesheet_Get(pyobj_style);
739     doc = (xmlDocPtr) PyxmlNode_Get(pyobj_doc);
740     transformCtxt = (xsltTransformContextPtr) PytransformCtxt_Get(pyobj_transformCtxt);
741 
742     c_retval = xsltApplyStylesheetUser(style, doc, params, NULL, NULL, transformCtxt);
743     py_retval = libxml_xmlDocPtrWrap((xmlDocPtr) c_retval);
744     if (params != NULL) {
745 	if (len > 0) {
746 	    for (i = 0;i < 2 * len;i++) {
747 		if (params[i] != NULL)
748 		    xmlFree((char *)params[i]);
749 	    }
750 	    xmlFree(params);
751 	}
752     }
753     return(py_retval);
754 }
755 
756 PyObject *
libxslt_xsltApplyStylesheet(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)757 libxslt_xsltApplyStylesheet(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
758     PyObject *py_retval;
759     xmlDocPtr c_retval;
760     xsltStylesheetPtr style;
761     PyObject *pyobj_style;
762     xmlDocPtr doc;
763     PyObject *pyobj_doc;
764     PyObject *pyobj_params;
765     const char **params = NULL;
766     int len = 0, i, j, params_size;
767     ssize_t ppos = 0;
768     PyObject *name;
769     PyObject *value;
770 
771     if (!PyArg_ParseTuple(args, (char *) "OOO:xsltApplyStylesheet",
772 		          &pyobj_style, &pyobj_doc, &pyobj_params))
773         return(NULL);
774 
775     if (pyobj_params != Py_None) {
776 	if (PyDict_Check(pyobj_params)) {
777 	    len = PyDict_Size(pyobj_params);
778 	    if (len > 0) {
779 	        params_size = (len + 1) * 2 * sizeof(char *);
780 		params = (const char **) xmlMalloc(params_size);
781 		if (params == NULL) {
782 		    printf("libxslt_xsltApplyStylesheet: out of memory\n");
783 		    Py_INCREF(Py_None);
784 		    return(Py_None);
785 		}
786 		memset(params, 0, params_size);
787 		j = 0;
788 		while (PyDict_Next(pyobj_params, &ppos, &name, &value)) {
789 		    const char *tmp;
790 		    int size;
791 
792 		    tmp = PyString_AS_STRING(name);
793 		    size = PyString_GET_SIZE(name);
794 		    params[j * 2] = (char *) xmlCharStrndup(tmp, size);
795 		    if (PyString_Check(value)) {
796 			tmp = PyString_AS_STRING(value);
797 			size = PyString_GET_SIZE(value);
798 			params[(j * 2) + 1] = (char *)
799 			    xmlCharStrndup(tmp, size);
800 		    } else {
801 			params[(j * 2) + 1] = NULL;
802 		    }
803 		    j = j + 1;
804 		}
805 		params[j * 2] = NULL;
806 		params[(j * 2) + 1] = NULL;
807 	    }
808 	} else {
809 	    printf("libxslt_xsltApplyStylesheet: parameters not a dict\n");
810 	    Py_INCREF(Py_None);
811 	    return(Py_None);
812 	}
813     }
814     style = (xsltStylesheetPtr) Pystylesheet_Get(pyobj_style);
815     doc = (xmlDocPtr) PyxmlNode_Get(pyobj_doc);
816 
817     c_retval = xsltApplyStylesheet(style, doc, params);
818     py_retval = libxml_xmlDocPtrWrap((xmlDocPtr) c_retval);
819     if (params != NULL) {
820 	if (len > 0) {
821 	    for (i = 0;i < 2 * len;i++) {
822 		if (params[i] != NULL)
823 		    xmlFree((char *)params[i]);
824 	    }
825 	    xmlFree(params);
826 	}
827     }
828     return(py_retval);
829 }
830 
831 PyObject *
libxslt_xsltSaveResultToString(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)832 libxslt_xsltSaveResultToString(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
833     PyObject *py_retval;        /* our final return value, a python string   */
834     xmlChar  *buffer;
835     int       size    = 0;
836     int       emitted = 0;
837     xmlDocPtr result;
838     PyObject *pyobj_result;
839     xsltStylesheetPtr style;
840     PyObject *pyobj_style;
841 
842     if (!PyArg_ParseTuple(args, (char *)"OO:xsltSaveResultToString", &pyobj_style, &pyobj_result))
843       goto FAIL;
844     result = (xmlDocPtr) PyxmlNode_Get(pyobj_result);
845     style  = (xsltStylesheetPtr) Pystylesheet_Get(pyobj_style);
846 
847 
848     /* FIXME: We should probably add more restrictive error checking
849      * and raise an error instead of "just" returning NULL.
850      * FIXME: Documentation and code for xsltSaveResultToString diff
851      * -> emmitted will never be positive non-null.
852      */
853     emitted = xsltSaveResultToString(&buffer, &size, result, style);
854     if(!buffer || emitted < 0)
855       goto FAIL;
856     /* We haven't tested the aberrant case of a transformation that
857      * renders to an empty string. For now we try to play it safe.
858      */
859     if(size)
860       {
861       buffer[size] = '\0';
862       py_retval = PyString_FromString((char *) buffer);
863       xmlFree(buffer);
864       }
865     else
866       py_retval = PyString_FromString("");
867     return(py_retval);
868  FAIL:
869     return(0);
870 }
871 
872 
873 /************************************************************************
874  *									*
875  *			Error message callback				*
876  *									*
877  ************************************************************************/
878 
879 static PyObject *libxslt_xsltPythonErrorFuncHandler = NULL;
880 static PyObject *libxslt_xsltPythonErrorFuncCtxt = NULL;
881 
882 static void LIBXSLT_ATTR_FORMAT(2,3)
libxslt_xsltErrorFuncHandler(void * ctx ATTRIBUTE_UNUSED,const char * msg,...)883 libxslt_xsltErrorFuncHandler(void *ctx ATTRIBUTE_UNUSED, const char *msg,
884                            ...)
885 {
886     int size;
887     int chars;
888     char *larger;
889     va_list ap;
890     char *str;
891     PyObject *list;
892     PyObject *message;
893     PyObject *result;
894 
895 #ifdef DEBUG_ERROR
896     printf("libxslt_xsltErrorFuncHandler(%p, %s, ...) called\n", ctx, msg);
897 #endif
898 
899 
900     if (libxslt_xsltPythonErrorFuncHandler == NULL) {
901         va_start(ap, msg);
902         vfprintf(stderr, msg, ap);
903         va_end(ap);
904     } else {
905         str = (char *) xmlMalloc(150);
906         if (str == NULL)
907             return;
908 
909         size = 150;
910 
911         while (1) {
912             va_start(ap, msg);
913             chars = vsnprintf(str, size, msg, ap);
914             va_end(ap);
915             if ((chars > -1) && (chars < size))
916                 break;
917             if (chars > -1)
918                 size += chars + 1;
919             else
920                 size += 100;
921             if ((larger = (char *) xmlRealloc(str, size)) == NULL) {
922                 xmlFree(str);
923                 return;
924             }
925             str = larger;
926         }
927 
928         list = PyTuple_New(2);
929         PyTuple_SetItem(list, 0, libxslt_xsltPythonErrorFuncCtxt);
930         Py_XINCREF(libxslt_xsltPythonErrorFuncCtxt);
931         message = libxml_charPtrWrap(str);
932         PyTuple_SetItem(list, 1, message);
933         result = PyEval_CallObject(libxslt_xsltPythonErrorFuncHandler, list);
934         Py_XDECREF(list);
935         Py_XDECREF(result);
936     }
937 }
938 
939 static void
libxslt_xsltErrorInitialize(void)940 libxslt_xsltErrorInitialize(void)
941 {
942 #ifdef DEBUG_ERROR
943     printf("libxslt_xsltErrorInitialize() called\n");
944 #endif
945     xmlSetGenericErrorFunc(NULL, libxslt_xsltErrorFuncHandler);
946     xsltSetGenericErrorFunc(NULL, libxslt_xsltErrorFuncHandler);
947 }
948 
949 PyObject *
libxslt_xsltRegisterErrorHandler(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)950 libxslt_xsltRegisterErrorHandler(PyObject * self ATTRIBUTE_UNUSED,
951                                PyObject * args)
952 {
953     PyObject *py_retval;
954     PyObject *pyobj_f;
955     PyObject *pyobj_ctx;
956 
957     if (!PyArg_ParseTuple
958         (args, (char *) "OO:xmlRegisterErrorHandler", &pyobj_f,
959          &pyobj_ctx))
960         return (NULL);
961 
962 #ifdef DEBUG_ERROR
963     printf("libxml_registerXPathFunction(%p, %p) called\n", pyobj_ctx,
964            pyobj_f);
965 #endif
966 
967     if (libxslt_xsltPythonErrorFuncHandler != NULL) {
968         Py_XDECREF(libxslt_xsltPythonErrorFuncHandler);
969     }
970     if (libxslt_xsltPythonErrorFuncCtxt != NULL) {
971         Py_XDECREF(libxslt_xsltPythonErrorFuncCtxt);
972     }
973 
974     Py_XINCREF(pyobj_ctx);
975     Py_XINCREF(pyobj_f);
976 
977     /* TODO: check f is a function ! */
978     libxslt_xsltPythonErrorFuncHandler = pyobj_f;
979     libxslt_xsltPythonErrorFuncCtxt = pyobj_ctx;
980 
981     py_retval = libxml_intWrap(1);
982     return (py_retval);
983 }
984 
985 /************************************************************************
986  *									*
987  *			Extension classes				*
988  *									*
989  ************************************************************************/
990 
991 static xmlHashTablePtr libxslt_extModuleClasses = NULL;
992 
993 static void *
libxslt_xsltPythonExtModuleStyleInit(xsltStylesheetPtr style,const xmlChar * URI)994 libxslt_xsltPythonExtModuleStyleInit(xsltStylesheetPtr style,
995 	                            const xmlChar * URI) {
996     PyObject *result = NULL;
997     PyObject *class = NULL;
998 
999 #ifdef DEBUG_EXTENSIONS
1000     printf("libxslt_xsltPythonExtModuleStyleInit(%p, %s) called\n",
1001 	   style, URI);
1002 #endif
1003 
1004     if ((style == NULL) || (URI == NULL))
1005 	return(NULL);
1006 
1007     /*
1008      * Find the function, it should be there it was there at lookup
1009      */
1010     class = xmlHashLookup(libxslt_extModuleClasses, URI);
1011     if (class == NULL) {
1012 	fprintf(stderr, "libxslt_xsltPythonExtModuleStyleInit: internal error %s not found !\n", URI);
1013 	return(NULL);
1014     }
1015 
1016     if (PyObject_HasAttrString(class, (char *) "_styleInit")) {
1017 	result = PyObject_CallMethod(class, (char *) "_styleInit",
1018 		     (char *) "Os", libxslt_xsltStylesheetPtrWrap(style), URI);
1019     }
1020     return((void *)result);
1021 }
1022 static void
libxslt_xsltPythonExtModuleStyleShutdown(xsltStylesheetPtr style,const xmlChar * URI,void * data)1023 libxslt_xsltPythonExtModuleStyleShutdown(xsltStylesheetPtr style,
1024 	                                const xmlChar * URI, void *data) {
1025     PyObject *class = NULL;
1026     PyObject *result;
1027 
1028 #ifdef DEBUG_EXTENSIONS
1029     printf("libxslt_xsltPythonExtModuleStyleShutdown(%p, %s, %p) called\n",
1030 	   style, URI, data);
1031 #endif
1032 
1033     if ((style == NULL) || (URI == NULL))
1034 	return;
1035 
1036     /*
1037      * Find the function, it should be there it was there at lookup
1038      */
1039     class = xmlHashLookup(libxslt_extModuleClasses, URI);
1040     if (class == NULL) {
1041 	fprintf(stderr, "libxslt_xsltPythonExtModuleStyleShutdown: internal error %s not found !\n", URI);
1042 	return;
1043     }
1044 
1045     if (PyObject_HasAttrString(class, (char *) "_styleShutdown")) {
1046 	result = PyObject_CallMethod(class, (char *) "_styleShutdown",
1047 		     (char *) "OsO", libxslt_xsltStylesheetPtrWrap(style),
1048 		     URI, (PyObject *) data);
1049 	Py_XDECREF(result);
1050 	Py_XDECREF((PyObject *)data);
1051     }
1052 }
1053 
1054 static void *
libxslt_xsltPythonExtModuleCtxtInit(xsltTransformContextPtr ctxt,const xmlChar * URI)1055 libxslt_xsltPythonExtModuleCtxtInit(xsltTransformContextPtr ctxt,
1056 	                            const xmlChar * URI) {
1057     PyObject *result = NULL;
1058     PyObject *class = NULL;
1059 
1060 #ifdef DEBUG_EXTENSIONS
1061     printf("libxslt_xsltPythonExtModuleCtxtInit(%p, %s) called\n",
1062 	   ctxt, URI);
1063 #endif
1064 
1065     if ((ctxt == NULL) || (URI == NULL))
1066 	return(NULL);
1067 
1068     /*
1069      * Find the function, it should be there it was there at lookup
1070      */
1071     class = xmlHashLookup(libxslt_extModuleClasses, URI);
1072     if (class == NULL) {
1073 	fprintf(stderr, "libxslt_xsltPythonExtModuleCtxtInit: internal error %s not found !\n", URI);
1074 	return(NULL);
1075     }
1076 
1077     if (PyObject_HasAttrString(class, (char *) "_ctxtInit")) {
1078 	result = PyObject_CallMethod(class, (char *) "_ctxtInit",
1079 		     (char *) "Os", libxslt_xsltTransformContextPtrWrap(ctxt),
1080 		     URI);
1081     }
1082     return((void *)result);
1083 }
1084 static void
libxslt_xsltPythonExtModuleCtxtShutdown(xsltTransformContextPtr ctxt,const xmlChar * URI,void * data)1085 libxslt_xsltPythonExtModuleCtxtShutdown(xsltTransformContextPtr ctxt,
1086 	                                const xmlChar * URI, void *data) {
1087     PyObject *class = NULL;
1088     PyObject *result;
1089 
1090 #ifdef DEBUG_EXTENSIONS
1091     printf("libxslt_xsltPythonExtModuleCtxtShutdown(%p, %s, %p) called\n",
1092 	   ctxt, URI, data);
1093 #endif
1094 
1095     if ((ctxt == NULL) || (URI == NULL))
1096 	return;
1097 
1098     /*
1099      * Find the function, it should be there it was there at lookup
1100      */
1101     class = xmlHashLookup(libxslt_extModuleClasses, URI);
1102     if (class == NULL) {
1103 	fprintf(stderr, "libxslt_xsltPythonExtModuleCtxtShutdown: internal error %s not found !\n", URI);
1104 	return;
1105     }
1106 
1107     if (PyObject_HasAttrString(class, (char *) "_ctxtShutdown")) {
1108 	result = PyObject_CallMethod(class, (char *) "_ctxtShutdown",
1109 		     (char *) "OsO", libxslt_xsltTransformContextPtrWrap(ctxt),
1110 		     URI, (PyObject *) data);
1111 	Py_XDECREF(result);
1112 	Py_XDECREF((PyObject *)data);
1113     }
1114 }
1115 
1116 PyObject *
libxslt_xsltRegisterExtensionClass(PyObject * self ATTRIBUTE_UNUSED,PyObject * args)1117 libxslt_xsltRegisterExtensionClass(PyObject *self ATTRIBUTE_UNUSED,
1118 	                           PyObject *args) {
1119     PyObject *py_retval;
1120     int ret = 0;
1121     xmlChar *ns_uri;
1122     PyObject *pyobj_c;
1123 
1124     if (!PyArg_ParseTuple(args, (char *)"zO:registerExtensionClass",
1125 		          &ns_uri, &pyobj_c))
1126         return(NULL);
1127 
1128     if ((ns_uri == NULL) || (pyobj_c == NULL)) {
1129 	py_retval = libxml_intWrap(-1);
1130 	return(py_retval);
1131     }
1132 
1133 #ifdef DEBUG_EXTENSIONS
1134     printf("libxslt_xsltRegisterExtensionClass(%s) called\n", ns_uri);
1135 #endif
1136 
1137     if (libxslt_extModuleClasses == NULL)
1138 	libxslt_extModuleClasses = xmlHashCreate(10);
1139     if (libxslt_extModuleClasses == NULL) {
1140 	py_retval = libxml_intWrap(-1);
1141 	return(py_retval);
1142     }
1143     ret = xmlHashAddEntry(libxslt_extModuleClasses, ns_uri, pyobj_c);
1144     if (ret != 0) {
1145 	py_retval = libxml_intWrap(-1);
1146 	return(py_retval);
1147     }
1148     Py_XINCREF(pyobj_c);
1149 
1150     ret = xsltRegisterExtModuleFull(ns_uri,
1151        libxslt_xsltPythonExtModuleCtxtInit,
1152        libxslt_xsltPythonExtModuleCtxtShutdown,
1153        libxslt_xsltPythonExtModuleStyleInit,
1154        libxslt_xsltPythonExtModuleStyleShutdown);
1155     py_retval = libxml_intWrap((int) ret);
1156     if (ret < 0) {
1157 	Py_XDECREF(pyobj_c);
1158     }
1159     return(py_retval);
1160 }
1161 
1162 /************************************************************************
1163  *									*
1164  *			Integrated cleanup				*
1165  *									*
1166  ************************************************************************/
1167 
1168 PyObject *
libxslt_xsltPythonCleanup(PyObject * self ATTRIBUTE_UNUSED,PyObject * args ATTRIBUTE_UNUSED)1169 libxslt_xsltPythonCleanup(PyObject *self ATTRIBUTE_UNUSED,
1170 	                  PyObject *args ATTRIBUTE_UNUSED) {
1171 
1172     if (libxslt_extModuleFunctions != NULL) {
1173 	xmlHashFree(libxslt_extModuleFunctions, deallocateCallback);
1174     }
1175     if (libxslt_extModuleElements != NULL) {
1176 	xmlHashFree(libxslt_extModuleElements, deallocateCallback);
1177     }
1178     if (libxslt_extModuleElementPreComp != NULL) {
1179 	xmlHashFree(libxslt_extModuleElementPreComp, deallocateCallback);
1180     }
1181     if (libxslt_extModuleClasses != NULL) {
1182 	xmlHashFree(libxslt_extModuleClasses, deallocateClasse);
1183     }
1184     xsltCleanupGlobals();
1185     Py_INCREF(Py_None);
1186     return(Py_None);
1187 }
1188 
1189 /************************************************************************
1190  *									*
1191  *			The registration stuff				*
1192  *									*
1193  ************************************************************************/
1194 static PyMethodDef libxsltMethods[] = {
1195 #include "libxslt-export.c"
1196     { NULL, NULL, 0, NULL }
1197 };
1198 
1199 #ifdef MERGED_MODULES
1200 extern void initlibxml2mod(void);
1201 #endif
1202 
initlibxsltmod(void)1203 void initlibxsltmod(void) {
1204     static int initialized = 0;
1205 
1206 #ifdef MERGED_MODULES
1207     initlibxml2mod();
1208 #endif
1209 
1210     if (initialized != 0)
1211 	return;
1212     Py_InitModule((char *)"libxsltmod", libxsltMethods);
1213     initialized = 1;
1214     /*
1215      * Specific XSLT initializations
1216      */
1217     libxslt_xsltErrorInitialize();
1218     xmlInitMemory();
1219     xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
1220     xmlDefaultSAXHandler.cdataBlock = NULL;
1221     /*
1222      * Register the EXSLT extensions and the test module
1223      */
1224     exsltRegisterAll();
1225 }
1226 
1227 
1228