LCOV - code coverage report
Current view: top level - src/plugins/xmltool - kdbtools.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 122 143 85.3 %
Date: 2022-05-21 16:19:22 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include <errno.h>
      10             : #include <string.h>
      11             : #include <unistd.h>
      12             : 
      13             : #include <libxml/xmlreader.h>
      14             : #include <libxml/xmlschemas.h>
      15             : 
      16             : #include "kdbtools.h"
      17             : #include <kdbinternal.h>
      18             : 
      19             : /*
      20             :  * Processes the current <key> node from reader, converting from XML
      21             :  * to a Key object, and ksAppendKey() it to ks.
      22             :  *
      23             :  * See keyToStream() for an example of a <key> node.
      24             :  *
      25             :  * This function is completely dependent on libxml.
      26             :  *
      27             :  * @param ks where to put the resulting read key
      28             :  * @param context a parent key name, so a full name can be calculated
      29             :  *        if the XML node for the current key only provides a basename
      30             :  * @param reader where to read from
      31             :  */
      32         228 : static int consumeKeyNode (KeySet * ks, const char * context, xmlTextReaderPtr reader)
      33             : {
      34             :         /* printf("%s", KDB_SCHEMA_PATH); */
      35             : 
      36         228 :         xmlChar * keyNodeName = xmlTextReaderName (reader);
      37         228 :         if (!strcmp ((char *) keyNodeName, "key"))
      38             :         {
      39         228 :                 xmlChar * privateContext = 0;
      40         228 :                 int appended = 0;
      41         228 :                 int isbin = 0;
      42         228 :                 int end = 0;
      43             : 
      44         228 :                 Key * newKey = keyNew ("/", KEY_END);
      45             : 
      46             :                 /* a <key> must have one of the following:
      47             :                    - a "name" attribute, used as an absolute name overriding the context
      48             :                    - a "basename" attribute, that will be appended to the current context
      49             :                    - a "parent" plus "basename" attributes, both appended to current context
      50             :                    - only a "parent", appended to current context
      51             :                 */
      52         228 :                 xmlChar * buffer = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
      53         228 :                 if (buffer)
      54             :                 {
      55             :                         /* set absolute name */
      56          26 :                         keySetName (newKey, (char *) buffer);
      57          26 :                         xmlFree (buffer);
      58          26 :                         buffer = 0;
      59             :                 }
      60             :                 else
      61             :                 {
      62             :                         /* logic for relative name calculation */
      63             : 
      64         202 :                         privateContext = xmlTextReaderGetAttribute (reader, (const xmlChar *) "parent");
      65         202 :                         buffer = xmlTextReaderGetAttribute (reader, (const xmlChar *) "basename");
      66             : 
      67         202 :                         if (context) keySetName (newKey, context);
      68         202 :                         if (privateContext) keyAddName (newKey, (char *) privateContext);
      69         202 :                         if (buffer) keyAddName (newKey, (char *) buffer);
      70             : 
      71         202 :                         xmlFree (privateContext);
      72         202 :                         privateContext = 0;
      73         202 :                         xmlFree (buffer);
      74         202 :                         buffer = 0;
      75             :                 }
      76             : 
      77             : 
      78             :                 /* test for a short value attribute, instead of <value> below */
      79         228 :                 buffer = xmlTextReaderGetAttribute (reader, (const xmlChar *) "value");
      80         228 :                 if (buffer)
      81             :                 {
      82         154 :                         keySetRaw (newKey, buffer, elektraStrLen ((char *) buffer));
      83         154 :                         xmlFree (buffer);
      84         154 :                         buffer = 0;
      85             :                 }
      86             : 
      87         228 :                 if (xmlTextReaderIsEmptyElement (reader))
      88             :                 {
      89             :                         /* we have a <key ..../> element */
      90           0 :                         if (newKey && !appended)
      91             :                         {
      92           0 :                                 ksAppendKey (ks, newKey);
      93           0 :                                 appended = 1;
      94           0 :                                 end = 1;
      95             :                         }
      96             :                 }
      97             : 
      98         228 :                 buffer = xmlTextReaderGetAttribute (reader, (const xmlChar *) "type");
      99         228 :                 if (buffer)
     100             :                 {
     101         202 :                         if (!strcmp ((char *) buffer, "binary"))
     102             :                                 isbin = 1;
     103         198 :                         else if (!strcmp ((char *) buffer, "bin"))
     104           2 :                                 isbin = 1;
     105             :                 }
     106         228 :                 xmlFree (buffer);
     107             : 
     108         228 :                 if (isbin) keySetMeta (newKey, "binary", "");
     109             : 
     110             :                 // TODO: should parse arbitrary attributes as metadata
     111             : 
     112             :                 /* Parse everything else */
     113        1286 :                 while (!end)
     114             :                 {
     115        1058 :                         xmlTextReaderRead (reader);
     116        1058 :                         xmlChar * nodeName = xmlTextReaderName (reader);
     117             : 
     118        1058 :                         if (!strcmp ((char *) nodeName, "value"))
     119             :                         {
     120          16 :                                 if (xmlTextReaderIsEmptyElement (reader) || xmlTextReaderNodeType (reader) == 15)
     121             :                                 {
     122           8 :                                         xmlFree (nodeName);
     123           8 :                                         continue;
     124             :                                 }
     125             : 
     126           8 :                                 xmlTextReaderRead (reader);
     127           8 :                                 buffer = xmlTextReaderValue (reader);
     128             : 
     129           8 :                                 if (buffer)
     130             :                                 {
     131             :                                         /* Key's value type was already set above */
     132           8 :                                         if (keyIsBinary (newKey))
     133             :                                         {
     134             :                                                 /* TODO binary values
     135             :                                                 char *unencoded=0;
     136             :                                                 size_t unencodedSize;
     137             : 
     138             :                                                 unencodedSize=elektraStrLen((char *)buffer)/2;
     139             :                                                 unencoded=elektraMalloc(unencodedSize);
     140             :                                                 unencodedSize=kdbbDecode((char *)buffer,unencoded);
     141             :                                                 if (!unencodedSize) return -1;
     142             :                                                         keySetRaw(newKey,unencoded,unencodedSize);
     143             :                                                 elektraFree (unencoded);
     144             :                                                 */
     145             :                                         }
     146             :                                         else
     147           8 :                                                 keySetRaw (newKey, buffer, elektraStrLen ((char *) buffer));
     148             :                                 }
     149           8 :                                 xmlFree (buffer);
     150             :                         }
     151        1042 :                         else if (!strcmp ((char *) nodeName, "comment"))
     152             :                         {
     153         340 :                                 ssize_t commentSize = 0;
     154             : 
     155         340 :                                 if (xmlTextReaderIsEmptyElement (reader) || xmlTextReaderNodeType (reader) == 15)
     156             :                                 {
     157         170 :                                         xmlFree (nodeName);
     158         170 :                                         continue;
     159             :                                 }
     160             : 
     161         170 :                                 xmlTextReaderRead (reader);
     162         170 :                                 buffer = xmlTextReaderValue (reader);
     163             : 
     164         170 :                                 if ((commentSize = keyGetCommentSize (newKey)) > 1)
     165             :                                 {
     166             :                                         /*Multiple line comment*/
     167           2 :                                         char * tmpComment = 0;
     168           2 :                                         tmpComment = elektraMalloc (commentSize + xmlStrlen (buffer) * sizeof (xmlChar) + 1);
     169             : 
     170           2 :                                         if (tmpComment)
     171             :                                         {
     172           2 :                                                 keyGetComment (newKey, tmpComment, commentSize);
     173             : 
     174           2 :                                                 strcat (tmpComment, "\n");
     175           2 :                                                 strcat (tmpComment, (char *) buffer);
     176             : 
     177           2 :                                                 keySetComment (newKey, tmpComment);
     178             : 
     179           2 :                                                 elektraFree (tmpComment);
     180           2 :                                                 tmpComment = 0;
     181             :                                         }
     182             :                                 }
     183             :                                 else
     184         168 :                                         keySetComment (newKey, (char *) buffer);
     185         170 :                                 xmlFree (buffer);
     186             :                         }
     187         702 :                         else if (!strcmp ((char *) nodeName, "key"))
     188             :                         {
     189             :                                 /* Here we found </key> or a sub <key>.
     190             :                                    So include current key in the KeySet. */
     191         398 :                                 if (newKey && !appended)
     192             :                                 {
     193         228 :                                         ksAppendKey (ks, newKey);
     194         228 :                                         appended = 1;
     195             :                                 }
     196             : 
     197         398 :                                 if (xmlTextReaderNodeType (reader) == 15) /* found a </key> */
     198             :                                         end = 1;
     199         170 :                                 else if (newKey)
     200             :                                 {
     201             :                                         /* found a sub <key> */
     202             :                                         /* prepare the context (parent) */
     203         170 :                                         consumeKeyNode (ks, newKey->key, reader);
     204             :                                 }
     205             :                         }
     206             : 
     207         880 :                         xmlFree (nodeName);
     208             :                 }
     209             : 
     210         228 :                 if (privateContext) xmlFree (privateContext);
     211             : 
     212             :                 /* seems like we forgot the key, lets delete it */
     213         228 :                 if (newKey && !appended)
     214             :                 {
     215           0 :                         keyDel (newKey);
     216             :                 }
     217             :         }
     218             : 
     219         228 :         xmlFree (keyNodeName);
     220             : 
     221         228 :         return 0;
     222             : }
     223             : 
     224             : 
     225          29 : static int consumeKeySetNode (KeySet * ks, const char * context, xmlTextReaderPtr reader)
     226             : {
     227          29 :         xmlChar * keySetNodeName = xmlTextReaderName (reader);
     228          29 :         if (!strcmp ((char *) keySetNodeName, "keyset"))
     229             :         {
     230          29 :                 xmlChar fullContext[800] = "";
     231          29 :                 int end = 0;
     232             : 
     233          29 :                 xmlChar * privateContext = xmlTextReaderGetAttribute (reader, (const xmlChar *) "parent");
     234          29 :                 if (context && privateContext)
     235             :                 {
     236             : /*In libxml earlier than 2.9.4 const char * was used as argument, leading to a warning.
     237             : https://git.gnome.org/browse/libxml2/diff/include/libxml/xmlstring.h?id=4472c3a5a5b516aaf59b89be602fbce52756c3e9
     238             : */
     239             : #pragma GCC diagnostic ignored "-Wpointer-sign"
     240           0 :                         xmlStrPrintf (fullContext, sizeof (fullContext), "%s/%s", context, privateContext);
     241             : #pragma GCC diagnostic warning "-Wpointer-sign"
     242             :                 }
     243             : 
     244             :                 /* Parse everything else */
     245         207 :                 while (!end)
     246             :                 {
     247         178 :                         xmlTextReaderRead (reader);
     248         178 :                         xmlChar * nodeName = xmlTextReaderName (reader);
     249             : 
     250         178 :                         if (!strcmp ((char *) nodeName, "key"))
     251             :                         {
     252          58 :                                 if (privateContext)
     253          64 :                                         consumeKeyNode (ks, (char *) (*fullContext ? fullContext : privateContext), reader);
     254             :                                 else
     255          26 :                                         consumeKeyNode (ks, context, reader);
     256             :                         }
     257         120 :                         else if (!strcmp ((char *) nodeName, "keyset"))
     258             :                         {
     259             :                                 /* A <keyset> can have nested <keyset>s */
     260          29 :                                 if (xmlTextReaderNodeType (reader) == 15) /* found a </keyset> */
     261             :                                         end = 1;
     262           0 :                                 else if (privateContext)
     263           0 :                                         consumeKeySetNode (ks, (char *) (*fullContext ? fullContext : privateContext), reader);
     264             :                                 else
     265           0 :                                         consumeKeySetNode (ks, context, reader);
     266             :                         }
     267         178 :                         xmlFree (nodeName);
     268             :                 }
     269          29 :                 if (privateContext) xmlFree (privateContext), privateContext = 0;
     270             :         }
     271          29 :         xmlFree (keySetNodeName);
     272          29 :         return 0;
     273             : }
     274             : 
     275             : 
     276             : /*
     277             :  * This is the workhorse behind ksFromXML() and ksFromXMLfile().
     278             :  * It will process the entire XML document in reader and convert and
     279             :  * save it in ks KeySet. Each node is processed by the processNode() function.
     280             :  *
     281             :  * This function is completely dependent on libxml.
     282             :  */
     283          29 : static int ksFromXMLReader (KeySet * ks, xmlTextReaderPtr reader)
     284             : {
     285          29 :         int ret = xmlTextReaderRead (reader); /* go to first node */
     286          87 :         while (ret == 1)
     287             :         {
     288             :                 /* walk node per node until the end of the stream */
     289          58 :                 xmlChar * nodeName = xmlTextReaderName (reader);
     290             : 
     291          58 :                 if (!strcmp ((char *) nodeName, "key"))
     292           0 :                         consumeKeyNode (ks, 0, reader);
     293          58 :                 else if (!strcmp ((char *) nodeName, "keyset"))
     294          29 :                         consumeKeySetNode (ks, 0, reader);
     295             : 
     296          58 :                 ret = xmlTextReaderRead (reader);
     297             : 
     298          58 :                 xmlFree (nodeName);
     299             :         }
     300             : 
     301          29 :         return ret;
     302             : }
     303             : 
     304             : /*
     305             : static int isValidXML(xmlDocPtr doc,char *schemaPath)
     306             : {
     307             :         xmlSchemaPtr wxschemas = NULL;
     308             :         xmlSchemaValidCtxtPtr ctxt;
     309             :         xmlSchemaParserCtxtPtr ctxt2=NULL;
     310             :         int ret=0;
     311             : 
     312             :         ctxt2 = xmlSchemaNewParserCtxt(schemaPath);
     313             : 
     314             : 
     315             :         if (ctxt2==NULL) {
     316             :                 xmlFreeDoc(doc);
     317             :                 return 1;
     318             :         }
     319             : 
     320             :         xmlSchemaSetParserErrors(ctxt2,
     321             :                 (xmlSchemaValidityErrorFunc) fprintf,
     322             :                 (xmlSchemaValidityWarningFunc) fprintf,
     323             :                 stderr);
     324             :         wxschemas = xmlSchemaParse(ctxt2);
     325             : 
     326             :         if (wxschemas==NULL) {
     327             :                 xmlSchemaFreeParserCtxt(ctxt2);
     328             :                 xmlFreeDoc(doc);
     329             :                 return 1;
     330             :         }
     331             : 
     332             :         ctxt = xmlSchemaNewValidCtxt(wxschemas);
     333             :         xmlSchemaSetValidErrors(ctxt,
     334             :                 (xmlSchemaValidityErrorFunc) fprintf,
     335             :                 (xmlSchemaValidityWarningFunc) fprintf,
     336             :                 stderr);
     337             : 
     338             :         if (ctxt==NULL) {
     339             :                 xmlSchemaFree(wxschemas);
     340             :                 xmlSchemaFreeParserCtxt(ctxt2);
     341             :                 xmlFreeDoc(doc);
     342             :                 return 1;
     343             :         }
     344             : 
     345             :         ret = xmlSchemaValidateDoc(ctxt, doc);
     346             :         xmlSchemaFreeValidCtxt(ctxt);
     347             :         xmlSchemaFree(wxschemas);
     348             :         xmlSchemaFreeParserCtxt(ctxt2);
     349             : 
     350             :         return ret;
     351             : }
     352             : */
     353             : 
     354             : 
     355             : /**
     356             :  * Given an XML @p filename, open it, validate schema, process nodes,
     357             :  * convert and save it in the @p ks KeySet.
     358             :  *
     359             :  * Currently, the XML file can have many root @c @<keyset@> and @c @<key@> nodes.
     360             :  * They will all be reduced to simple keys returned in @p ks.
     361             :  *
     362             :  * To check if the xml file is valid (best before you read from it):
     363             :  * @code
     364             : #include
     365             : char schemaPath[513];
     366             : schemaPath[0]=0;
     367             : ret=kdbGetString(handle, KDB_SCHEMA_PATH_KEY,schemaPath,sizeof(schemaPath));
     368             : 
     369             : if (ret==0) ret = isValidXML(filename,schemaPath);
     370             : else ret = isValidXML(filename,KDB_SCHEMA_PATH);
     371             :  * @endcode
     372             :  *
     373             :  * @retval -1 on error
     374             :  * @retval 0 if file could not be opened
     375             :  * @param ks the keyset
     376             :  * @param filename the file to parse
     377             :  * @ingroup stream
     378             :  */
     379          29 : int ksFromXMLfile (KeySet * ks, const char * filename)
     380             : {
     381          29 :         xmlTextReaderPtr reader = 0;
     382          29 :         xmlDocPtr doc = 0;
     383          29 :         int ret = 0;
     384             : 
     385          29 :         doc = xmlParseFile (filename);
     386          29 :         if (doc == 0)
     387             :         {
     388             :                 // TODO: distinguish between parser errors and
     389             :                 // permission errors?
     390           0 :                 xmlCleanupParser ();
     391           0 :                 return -1;
     392             :         }
     393             : 
     394          29 :         reader = xmlReaderWalker (doc);
     395          29 :         if (reader)
     396             :         {
     397          29 :                 ret = ksFromXMLReader (ks, reader);
     398          29 :                 xmlFreeTextReader (reader);
     399             :         }
     400             :         else
     401             :         {
     402             :                 ret = -1;
     403             :         }
     404             : 
     405          29 :         xmlFreeDoc (doc);
     406             : 
     407          29 :         xmlCleanupParser ();
     408          29 :         return ret;
     409             : }
     410             : 
     411             : 
     412             : /**
     413             :  * Given a file descriptor (that can be @p stdin) for an XML file, validate
     414             :  * schema, process nodes, convert and save it in the @p ks KeySet.
     415             :  *
     416             :  * @param ks keyset
     417             :  * @param fd POSIX file descriptor
     418             :  * @ingroup stream
     419             :  */
     420           0 : int ksFromXML (KeySet * ks, int fd)
     421             : {
     422             :         // a complete XML document is expected
     423           0 :         xmlTextReaderPtr reader = 0;
     424           0 :         int ret;
     425           0 :         reader = xmlReaderForFd (fd, "file:/tmp/imp.xml", 0, 0);
     426           0 :         if (reader)
     427             :         {
     428           0 :                 ret = ksFromXMLReader (ks, reader);
     429             :         }
     430             :         else
     431             :         {
     432           0 :                 printf ("kdb: Unable to open file descriptor %d for XML reading\n", fd);
     433           0 :                 return 1;
     434             :         }
     435           0 :         return ret;
     436             : }

Generated by: LCOV version 1.13