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

Generated by: LCOV version 1.13