1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15 
16 #include "XMLObject.hxx"
17 #include "XMLDocument.hxx"
18 #include "XMLElement.hxx"
19 #include "XMLXPath.hxx"
20 #include "XMLValidation.hxx"
21 #include "XMLValidationRelaxNG.hxx"
22 #include "VariableScope.hxx"
23 
24 extern "C"
25 {
26 #include "expandPathVariable.h"
27 #include "sci_malloc.h"
28 #include "localization.h"
29 }
30 
31 #include <iostream>
32 
33 #define BUFFER_SIZE 1024
34 
35 namespace org_modules_xml
36 {
37 
38 std::string XMLDocument::errorBuffer;
39 std::string XMLDocument::errorXPathBuffer;
40 std::list < XMLDocument *> XMLDocument::openDocs;
41 
XMLDocument(const char * path,bool validate,std::string * error,const char * encoding,const bool html)42 XMLDocument::XMLDocument(const char *path, bool validate, std::string * error, const char * encoding, const bool html): XMLObject()
43 {
44     char *expandedPath = expandPathVariable(const_cast<char *>(path));
45     if (expandedPath)
46     {
47         if (html)
48         {
49             document = readHTMLDocument(const_cast<const char *>(expandedPath), encoding, error);
50         }
51         else
52         {
53             document = readDocument(const_cast<const char *>(expandedPath), encoding, validate, error);
54         }
55 
56         FREE(expandedPath);
57         if (document)
58         {
59             openDocs.push_back(this);
60             scope->registerPointers(document, this);
61         }
62     }
63     else
64     {
65         document = 0;
66         *error = std::string(gettext("Invalid file name: ")) + std::string(path);
67     }
68 
69     id = scope->getVariableId(*this);
70     scilabType = XMLDOCUMENT;
71 }
72 
XMLDocument(const std::string & xmlCode,bool validate,std::string * error,const char * encoding,const bool html)73 XMLDocument::XMLDocument(const std::string & xmlCode, bool validate, std::string * error, const char * encoding, const bool html): XMLObject()
74 {
75     if (html)
76     {
77         document = readHTMLDocument(xmlCode, encoding, error);
78     }
79     else
80     {
81         document = readDocument(xmlCode, encoding, validate, error);
82     }
83 
84     if (document)
85     {
86         openDocs.push_back(this);
87     }
88     scope->registerPointers(document, this);
89     id = scope->getVariableId(*this);
90     scilabType = XMLDOCUMENT;
91 }
92 
XMLDocument(char * uri,char * version)93 XMLDocument::XMLDocument(char *uri, char *version): XMLObject()
94 {
95     char *newUri = 0;
96     char *expandedPath = 0;
97 
98     if (!version)
99     {
100         version = const_cast < char *>("1.0");
101     }
102     document = xmlNewDoc((xmlChar *) version);
103     openDocs.push_back(this);
104     scope->registerPointers(document, this);
105     id = scope->getVariableId(*this);
106     scilabType = XMLDOCUMENT;
107 
108     expandedPath = expandPathVariable(const_cast < char *>(uri));
109 
110     if (expandedPath)
111     {
112         newUri = (char *)xmlMalloc(sizeof(char) * (strlen(expandedPath) + 1));
113         memcpy(newUri, expandedPath, sizeof(char) * (strlen(expandedPath) + 1));
114         document->URL = (xmlChar *) newUri;
115         FREE(expandedPath);
116     }
117 }
118 
~XMLDocument()119 XMLDocument::~XMLDocument()
120 {
121     scope->unregisterPointer(document);
122     scope->removeId(id);
123     if (document)
124     {
125         openDocs.remove(this);
126         if (openDocs.size() == 0 && XMLValidation::getOpenValidationFiles().size() == 0)
127         {
128             resetScope();
129         }
130         xmlFreeDoc(document);
131     }
132 
133 #ifdef SCILAB_DEBUG_XML
134     for (std::set<XMLObject *>::const_iterator i = XMLObject::pointers.begin(), e = XMLObject::pointers.end(); i != e; ++i)
135     {
136         XMLObject * p = *i;
137         if (p != this)
138         {
139             std::cout << "Stay = " << (void*)p << ":" << typeid(*p).name() << std::endl;
140         }
141     }
142 #endif
143 }
144 
getRealXMLPointer() const145 void *XMLDocument::getRealXMLPointer() const
146 {
147     return static_cast < void *>(document);
148 }
149 
makeXPathQuery(const char * query,char ** namespaces,int length,const XMLElement * e,std::string * error)150 const XMLXPath *XMLDocument::makeXPathQuery(const char *query, char **namespaces, int length, const XMLElement * e, std::string * error)
151 {
152     errorXPathBuffer.clear();
153 
154     xmlXPathContext *ctxt = xmlXPathNewContext(document);
155 
156     if (!ctxt)
157     {
158         errorXPathBuffer.append(gettext("Cannot create a parser context"));
159         *error = errorXPathBuffer;
160         return 0;
161     }
162 
163     if (e)
164     {
165         ctxt->node = (xmlNode *) e->getRealXMLPointer();
166     }
167 
168     if (namespaces)
169     {
170         for (int i = 0; i < length; i++)
171         {
172             xmlXPathRegisterNs(ctxt, (const xmlChar *)namespaces[i], (const xmlChar *)namespaces[i + length]);
173         }
174     }
175 
176     xmlSetStructuredErrorFunc(ctxt, XMLDocument::errorXPathFunction);
177     xmlXPathCompExpr *expr = xmlXPathCtxtCompile(ctxt, (const xmlChar *)query);
178 
179     if (!expr)
180     {
181         xmlSetStructuredErrorFunc(ctxt, 0);
182         xmlXPathFreeContext(ctxt);
183         *error = errorXPathBuffer;
184         return 0;
185     }
186 
187     xmlXPathObject *xpath = xmlXPathCompiledEval(expr, ctxt);
188 
189     xmlSetStructuredErrorFunc(ctxt, 0);
190     xmlXPathFreeContext(ctxt);
191     xmlXPathFreeCompExpr(expr);
192     if (!xpath)
193     {
194         *error = errorXPathBuffer;
195         return 0;
196     }
197 
198     return new XMLXPath(*this, xpath);
199 }
200 
getXMLObjectParent() const201 const XMLObject *XMLDocument::getXMLObjectParent() const
202 {
203     return 0;
204 }
205 
toString() const206 const std::string XMLDocument::toString() const
207 {
208     std::ostringstream oss;
209 
210     oss << "XML Document" << std::endl
211         << "url: " << getDocumentURL() << std::endl
212         << "root: " << "XML Element";
213 
214     return oss.str();
215 }
216 
dump(bool indent) const217 const std::string XMLDocument::dump(bool indent) const
218 {
219     xmlChar *buffer = 0;
220     int size = 0;
221     xmlDocDumpFormatMemory(document, &buffer, &size, indent ? 1 : 0);
222     std::string str((const char *)buffer);
223     xmlFree(buffer);
224 
225     return str;
226 }
227 
dumpHTML(bool indent) const228 const std::string XMLDocument::dumpHTML(bool indent) const
229 {
230     xmlBuffer * buffer = xmlBufferCreate();
231     int ret;
232     int options = XML_SAVE_AS_HTML;
233     if (indent)
234     {
235         options |= XML_SAVE_FORMAT;
236     }
237 
238     xmlThrDefIndentTreeOutput(1);
239     xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buffer, 0, options);
240     ret = xmlSaveDoc(ctxt, document);
241     xmlSaveFlush(ctxt);
242     xmlSaveClose(ctxt);
243 
244     std::string str((const char *)xmlBufferDetach(buffer));
245     xmlBufferFree(buffer);
246 
247     return str;
248 }
249 
getRoot() const250 const XMLElement *XMLDocument::getRoot() const
251 {
252     xmlNode *root = xmlDocGetRootElement(document);
253     if (!root)
254     {
255         return 0;
256     }
257 
258     XMLObject *obj = scope->getXMLObjectFromLibXMLPtr(root);
259 
260     if (obj)
261     {
262         return static_cast < XMLElement * >(obj);
263     }
264 
265     return new XMLElement(*this, root);
266 }
267 
setRoot(const XMLElement & elem) const268 void XMLDocument::setRoot(const XMLElement & elem) const
269 {
270     xmlNode *root = xmlDocGetRootElement(document);
271     if (root != elem.getRealNode())
272     {
273         xmlNode *cpy = xmlCopyNodeList(elem.getRealNode());
274         xmlUnlinkNode(cpy);
275         xmlDocSetRootElement(document, cpy);
276     }
277 }
278 
setRoot(const std::string & xmlCode,std::string * error) const279 void XMLDocument::setRoot(const std::string & xmlCode, std::string * error) const
280 {
281     XMLDocument doc = XMLDocument(xmlCode, false, error);
282 
283     if (error->empty())
284     {
285         setRoot(*doc.getRoot());
286     }
287 }
288 
getDocumentURL() const289 const char *XMLDocument::getDocumentURL() const
290 {
291     if (document->URL)
292     {
293         return (const char *)document->URL;
294     }
295     else
296     {
297         return "Undefined";
298     }
299 }
300 
setDocumentURL(const std::string & url) const301 void XMLDocument::setDocumentURL(const std::string & url) const
302 {
303     char *expandedPath = 0;
304     char *newURL = 0;
305     expandedPath = expandPathVariable(const_cast < char *>(url.c_str()));
306 
307     if (expandedPath)
308     {
309         xmlFree((void *)document->URL);
310         newURL = (char *)xmlMalloc(sizeof(char) * (strlen(expandedPath) + 1));
311         memcpy(newURL, expandedPath, sizeof(char) * (strlen(expandedPath) + 1));
312         document->URL = (xmlChar *) newURL;
313         FREE(expandedPath);
314     }
315 }
316 
getOpenDocuments()317 const std::list < XMLDocument * >&XMLDocument::getOpenDocuments()
318 {
319     return openDocs;
320 }
321 
closeAllDocuments()322 void XMLDocument::closeAllDocuments()
323 {
324     int size = (int)openDocs.size();
325     XMLDocument **arr = new XMLDocument *[size];
326     int j = 0;
327 
328     for (std::list < XMLDocument * >::iterator i = openDocs.begin(); i != openDocs.end(); i++, j++)
329     {
330         arr[j] = *i;
331     }
332     for (j = 0; j < size; j++)
333     {
334         delete arr[j];
335     }
336     delete[]arr;
337 }
338 
readDocument(const char * filename,const char * encoding,bool validate,std::string * error)339 xmlDoc *XMLDocument::readDocument(const char *filename, const char * encoding, bool validate, std::string * error)
340 {
341     xmlParserCtxt *ctxt = initContext(error, validate);
342     xmlDoc *doc = 0;
343     int options = XML_PARSE_NSCLEAN | XML_PARSE_NOBLANKS;
344 
345     if (validate)
346     {
347         options |= XML_PARSE_DTDVALID;
348     }
349 
350     if (!ctxt)
351     {
352         xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
353         return 0;
354     }
355 
356     doc = xmlCtxtReadFile(ctxt, filename, encoding, options);
357     if (!doc || !ctxt->valid)
358     {
359         *error = errorBuffer;
360     }
361 
362     xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
363     xmlFreeParserCtxt(ctxt);
364 
365     return doc;
366 }
367 
readHTMLDocument(const char * filename,const char * encoding,std::string * error)368 xmlDoc *XMLDocument::readHTMLDocument(const char *filename, const char * encoding, std::string * error)
369 {
370     htmlParserCtxt *ctxt = initHTMLContext(error);
371     htmlDocPtr doc = 0;
372     int options = HTML_PARSE_NOWARNING | HTML_PARSE_NOBLANKS | HTML_PARSE_COMPACT;
373 
374     if (!ctxt)
375     {
376         xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
377         return 0;
378     }
379 
380     doc = htmlCtxtReadFile(ctxt, filename, encoding, options);
381     if (!doc || !ctxt->valid)
382     {
383         *error = errorBuffer;
384     }
385 
386     xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
387     htmlFreeParserCtxt(ctxt);
388 
389     return (xmlDoc *)doc;
390 }
391 
readDocument(const std::string & xmlCode,const char * encoding,bool validate,std::string * error)392 xmlDoc *XMLDocument::readDocument(const std::string & xmlCode, const char * encoding, bool validate, std::string * error)
393 {
394     xmlParserCtxt *ctxt = initContext(error, validate);
395     xmlDoc *doc = 0;
396     int options = XML_PARSE_NSCLEAN | XML_PARSE_NOBLANKS;
397 
398     if (validate)
399     {
400         options |= XML_PARSE_DTDVALID;
401     }
402 
403     if (!ctxt)
404     {
405         xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
406         return 0;
407     }
408 
409     doc = xmlCtxtReadDoc(ctxt, (const xmlChar *)xmlCode.c_str(), 0, encoding, options);
410     if (!doc || !ctxt->valid)
411     {
412         *error = errorBuffer;
413     }
414 
415     xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
416     xmlFreeParserCtxt(ctxt);
417 
418     return doc;
419 }
420 
readHTMLDocument(const std::string & htmlCode,const char * encoding,std::string * error)421 xmlDoc *XMLDocument::readHTMLDocument(const std::string & htmlCode, const char * encoding, std::string * error)
422 {
423     htmlParserCtxt *ctxt = initHTMLContext(error);
424     htmlDocPtr doc = 0;
425     int options = HTML_PARSE_NOWARNING | HTML_PARSE_NOBLANKS | HTML_PARSE_COMPACT;
426 
427     if (!ctxt)
428     {
429         xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
430         return 0;
431     }
432 
433     doc = htmlCtxtReadDoc(ctxt, (const xmlChar *)htmlCode.c_str(), 0, encoding, options);
434     if (!doc || !ctxt->valid)
435     {
436         *error = errorBuffer;
437     }
438 
439     xmlSetGenericErrorFunc(0, errorFunctionWithoutOutput);
440     htmlFreeParserCtxt(ctxt);
441 
442     return (xmlDoc *)doc;
443 }
444 
saveToFile(const std::string & filename,const bool indent) const445 bool XMLDocument::saveToFile(const std::string & filename, const bool indent) const
446 {
447     xmlThrDefIndentTreeOutput(1);
448     return xmlSaveFormatFile(filename.c_str(), document, indent) != -1;
449 }
450 
saveToHTMLFile(const std::string & filename,const bool indent) const451 bool XMLDocument::saveToHTMLFile(const std::string & filename, const bool indent) const
452 {
453     int ret;
454     int options = XML_SAVE_AS_HTML;
455     if (indent)
456     {
457         options |= XML_SAVE_FORMAT;
458     }
459 
460     xmlThrDefIndentTreeOutput(1);
461     xmlSaveCtxtPtr ctxt = xmlSaveToFilename(filename.c_str(), 0, options);
462     ret = xmlSaveDoc(ctxt, document);
463     xmlSaveFlush(ctxt);
464     xmlSaveClose(ctxt);
465 
466     return ret != -1;
467 }
468 
initContext(std::string * error,bool validate)469 xmlParserCtxt *XMLDocument::initContext(std::string * error, bool validate)
470 {
471     xmlParserCtxt *ctxt;
472 
473     errorXPathBuffer.clear();
474 
475     ctxt = xmlNewParserCtxt();
476     if (!ctxt)
477     {
478         errorBuffer.append(gettext("Cannot create a parser context"));
479         *error = errorBuffer;
480         return 0;
481     }
482 
483     if (validate)
484     {
485         ctxt->vctxt.error = (xmlValidityErrorFunc) errorFunction;
486     }
487 
488     xmlSetGenericErrorFunc(ctxt, errorFunction);
489 
490     return ctxt;
491 }
492 
initHTMLContext(std::string * error)493 htmlParserCtxt *XMLDocument::initHTMLContext(std::string * error)
494 {
495     htmlParserCtxt *ctxt;
496 
497     errorXPathBuffer.clear();
498 
499     ctxt = htmlNewParserCtxt();
500     if (!ctxt)
501     {
502         errorBuffer.append(gettext("Cannot create a parser context"));
503         *error = errorBuffer;
504         return 0;
505     }
506 
507     xmlSetGenericErrorFunc((xmlParserCtxt *)ctxt, errorFunction);
508 
509     return ctxt;
510 }
511 
errorFunction(void * ctx,const char * msg,...)512 void XMLDocument::errorFunction(void *ctx, const char *msg, ...)
513 {
514     char str[BUFFER_SIZE];
515     va_list args;
516 
517     va_start(args, msg);
518     vsnprintf(str, BUFFER_SIZE, msg, args);
519     va_end(args);
520     errorBuffer.append(str);
521 }
522 
errorXPathFunction(void * ctx,xmlError * error)523 void XMLDocument::errorXPathFunction(void *ctx, xmlError * error)
524 {
525     errorXPathBuffer.append(error->message);
526 }
527 }
528