kdbtools.c

00001 /***************************************************************************
00002             kdbtools.c  -  Elektra High Level Methods
00003                              -------------------
00004     begin                : Sat Jan 22 2005
00005     copyright            : (C) 2005 by Avi Alkalay
00006     email                : avi@unix.sh
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the BSD License (revised).                      *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 
00017 
00018 
00019 /* Subversion stuff
00020 
00021 $Id: kdbtools.c 935 2006-09-13 12:46:39Z aviram $
00022 
00023 */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include <config.h>
00027 #endif
00028 
00029 #include <ctype.h>
00030 #include <string.h>
00031 
00032 #ifdef HAVE_GRP_H
00033 #include <grp.h>
00034 #endif
00035 
00036 #ifdef HAVE_PWD_H
00037 #include <pwd.h>
00038 #endif
00039 
00040 #include <unistd.h>
00041 #include <stdlib.h>
00042 
00043 #ifdef HAVE_SYS_STAT_H
00044 #include <sys/stat.h>
00045 #endif
00046 
00047 #include <libxml/xmlreader.h>
00048 #include <libxml/xmlschemas.h>
00049 
00050 #include "kdbtools.h"
00051 #include "kdbprivate.h"
00052 #include "kdb.h"
00053 
00054 /* #define KDB_SCHEMA_PATH       DATADIR KDB_SCHEMA_REL_PATH */
00055 #define KDB_SCHEMA_PATH_KEY   "system/sw/kdb/current/schemapath"
00056 
00057 #ifdef ELEKTRA_STATIC
00058 #define ksFromXMLfile libelektratools_LTX_ksFromXMLfile
00059 #define ksFromXML libelektratools_LTX_ksFromXML
00060 #endif
00061 
00062 
00079 /*
00080  * Processes the current <key> node from reader, converting from XML
00081  * to a Key object, and ksAppend() it to ks.
00082  *
00083  * See keyToStream() for an example of a <key> node.
00084  *
00085  * This function is completelly dependent on libxml.
00086  * 
00087  * @param ks where to put the resulting reded key
00088  * @param context a prent key name, so a full name can be calculated
00089  *        if the XML node for the current key only provides a basename
00090  * @param reader where to read from
00091  */
00092 int consumeKeyNode(KeySet *ks, const char *context, xmlTextReaderPtr reader) {
00093     xmlChar *nodeName=0;
00094     xmlChar *buffer=0;
00095     xmlChar *privateContext=0;
00096     Key *newKey=0;
00097     int appended=0;
00098 
00099     /* printf("%s", KDB_SCHEMA_PATH); */
00100     
00101     nodeName=xmlTextReaderName(reader);
00102     if (!strcmp((char *)nodeName,"key")) {
00103         uint8_t type=KEY_TYPE_STRING; /* default type */
00104         uint8_t isdir=0;
00105         int end=0;
00106         
00107         newKey=keyNew(0);
00108 
00109         xmlFree(nodeName); nodeName=0;
00110 
00111 
00112         /* a <key> must have one of the following:
00113            - a "name" attribute, used as an absolute name overriding the context
00114            - a "basename" attribute, that will be appended to the current context
00115            - a "parent" plus "basename" attributes, both appended to current context
00116            - only a "parent", appended to current context
00117         */
00118         buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"name");
00119         if (buffer) {
00120             /* set absolute name */
00121             keySetName(newKey,(char *)buffer);
00122             xmlFree(buffer); buffer=0;
00123         } else {
00124             /* logic for relative name calculation */
00125             
00126             privateContext=xmlTextReaderGetAttribute(reader,
00127                 (const xmlChar *)"parent");
00128             buffer=xmlTextReaderGetAttribute(reader,
00129                 (const xmlChar *)"basename");
00130 
00131             if (context) keySetName(newKey,context);
00132             if (privateContext) keyAddBaseName(newKey, (char *)privateContext);
00133             if (buffer) keyAddBaseName(newKey,(char *)buffer);
00134 
00135             xmlFree(privateContext); privateContext=0;
00136             xmlFree(buffer); buffer=0;
00137         }
00138 
00139 
00140         /* test for a short value attribute, instead of <value> bellow */
00141         buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"value");
00142         if (buffer) {
00143             keySetRaw(newKey,buffer,strblen((char *)buffer));
00144             xmlFree(buffer); buffer=0;
00145         }
00146 
00147 
00148 #ifdef HAVE_PWD_H
00149         /* Parse UID */
00150         buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"uid");
00151         if (buffer) {
00152             if (isdigit(*buffer))
00153                 keySetUID(newKey,atoi((char *)buffer));
00154             else {
00155                 struct passwd *pwd;
00156                 pwd=getpwnam((char *)buffer);
00157                 if (pwd) keySetUID(newKey,pwd->pw_uid);
00158                 else fprintf(stderr,"%s: Ignoring invalid user %s.\n",
00159                         newKey->key, buffer);
00160             }
00161             xmlFree(buffer); buffer=0;
00162         }
00163 #endif
00164 
00165 #ifdef HAVE_GRP_H
00166         /* Parse GID */
00167         buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"gid");
00168         if (buffer) {
00169             if (isdigit(*buffer)) {
00170                 keySetGID(newKey,atoi((char *)buffer));
00171             } else {
00172                 struct group *grp;
00173                 grp=getgrnam((char *)buffer);
00174                 if (grp) keySetGID(newKey,grp->gr_gid);
00175                 else fprintf(stderr,"%s: Ignoring invalid group %s.\n",
00176                         newKey->key, buffer);
00177             }
00178             xmlFree(buffer); buffer=0;
00179         }
00180 #endif
00181 
00182         /* Parse permissions */
00183         buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"mode");
00184         if (buffer) keySetAccess(newKey,strtol((char *)buffer,0,0));
00185         xmlFree(buffer); buffer=0;
00186 
00187 
00188 
00189         if (xmlTextReaderIsEmptyElement(reader)) {
00190             /* we have a <key ..../> element */
00191             if (newKey && !appended) {
00192                 ksAppend(ks,newKey);
00193                 appended=1;
00194                 end=1;
00195             }
00196         }
00197 
00198 
00199         buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"type");
00200         if (buffer) {
00201             if (!strcmp((char *)buffer,"string"))
00202                 type=KEY_TYPE_STRING;
00203             else if (!strcmp((char *)buffer,"link"))
00204                 type=KEY_TYPE_LINK;
00205             else if (!strcmp((char *)buffer,"directory"))
00206                 isdir=1; /* backwards compatibility */
00207             else if (!strcmp((char *)buffer,"binary"))
00208                 type=KEY_TYPE_BINARY;
00209             else if (!strcmp((char *)buffer,"undefined"))
00210                 type=KEY_TYPE_UNDEFINED;
00211             else { /* special numerical user-defined value types */
00212                 void *converter=0;
00213 
00214                 type=strtol((char *)buffer,(char **)&converter,10);
00215                 if ((void *)buffer==converter)
00216                     /* in case of error, fallback to default type again */
00217                     type=KEY_TYPE_STRING;
00218             }
00219         }
00220         xmlFree(buffer); buffer=0;
00221 
00222         /* If "isdir" appears, everything different from "0", "false" or "no"
00223         marks it as a dir key */
00224         buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"isdir");
00225         if (buffer) isdir=! (strcmp((char *)buffer,"0") &&
00226                              strcmp((char *)buffer,"false") &&
00227                              strcmp((char *)buffer,"no"));
00228         
00229         xmlFree(buffer); buffer=0;
00230 
00231 
00232         if (isdir) {
00233             mode_t mask=umask(0);
00234             umask(mask);
00235             keySetDir(newKey,mask);
00236         }
00237         
00238         keySetType(newKey,type);
00239 
00240 
00241         /* Parse everything else */
00242         while (!end) {
00243             xmlFree(nodeName); nodeName=0;
00244             xmlTextReaderRead(reader);
00245             nodeName=xmlTextReaderName(reader);
00246 
00247             if (!strcmp((char *)nodeName,"value")) {
00248                 if (xmlTextReaderIsEmptyElement(reader) ||
00249                     xmlTextReaderNodeType(reader)==15) continue;
00250                     
00251                 xmlTextReaderRead(reader);
00252                 buffer=xmlTextReaderValue(reader);
00253                 
00254                 if (buffer) {
00255                     /* Key's value type was already set above */
00256                     if (KEY_TYPE_BINARY <= type && type < KEY_TYPE_STRING) {
00257                         char *unencoded=0;
00258                         size_t unencodedSize;
00259                         
00260                         unencodedSize=strblen((char *)buffer)/2;
00261                         unencoded=malloc(unencodedSize);
00262                         unencodedSize=unencode((char *)buffer,unencoded);
00263                         if (!unencodedSize) return -1;
00264                             keySetRaw(newKey,unencoded,unencodedSize);
00265                         free(unencoded);
00266                     } else keySetRaw(newKey,buffer,strblen((char *)buffer));
00267                 }
00268             } else if (!strcmp((char *)nodeName,"comment")) {
00269                 ssize_t commentSize=0;
00270                 
00271                 if (xmlTextReaderIsEmptyElement(reader) ||
00272                     xmlTextReaderNodeType(reader)==15) continue;
00273                     
00274                 xmlTextReaderRead(reader);
00275                 buffer=xmlTextReaderValue(reader);
00276                 
00277                 if ((commentSize=keyGetCommentSize(newKey)) > 0) {
00278                     char *tmpComment=0;
00279                     tmpComment=malloc(commentSize+
00280                         xmlStrlen(buffer)*sizeof(xmlChar)+1);
00281 
00282                     if (tmpComment) {
00283                         keyGetComment(newKey,tmpComment,commentSize);
00284 
00285                         strcat(tmpComment,"\n");
00286                         strcat(tmpComment,(char *)buffer);
00287 
00288                         keySetComment(newKey,tmpComment);
00289 
00290                         free(tmpComment); tmpComment=0;
00291                     }
00292                 } else keySetComment(newKey,(char *)buffer);
00293             } else if (!strcmp((char *)nodeName,"key")) {
00294                 /* Here we found </key> or a sub <key>.
00295                    So include current key in the KeySet. */
00296                 if (newKey && !appended) {
00297                     ksAppend(ks,newKey);
00298                     appended=1;
00299                 }
00300                 
00301                 if (xmlTextReaderNodeType(reader)==15)
00302                     /* found a </key> */
00303                     end=1;
00304                 else {
00305                     /* found a sub <key> */
00306                     if (! keyIsDir(newKey)) {
00307                         /* Mark current key as a directory key */
00308                         mode_t mask=umask(0);
00309                         umask(mask);
00310                     
00311                         keySetDir(newKey,mask);
00312                     }
00313                     /* prepare the context (parent) */
00314                     consumeKeyNode(ks,newKey->key,reader);
00315                 }
00316             }
00317             xmlFree(buffer); buffer=0;
00318         }
00319 
00320 /*      printf("%s: %o\n",newKey->key,keyGetAccess(newKey)); */
00321         if (privateContext) xmlFree(privateContext);
00322     }
00323 
00324     if (nodeName) xmlFree(nodeName),nodeName=0;
00325     return 0;
00326 }
00327 
00328 
00329 
00330 
00331 int consumeKeySetNode(KeySet *ks, const char *context, xmlTextReaderPtr reader) {
00332     xmlChar *nodeName=0;
00333     xmlChar *privateContext=0;
00334     xmlChar fullContext[800]="";
00335     
00336     nodeName=xmlTextReaderName(reader);
00337     if (!strcmp((char *)nodeName,"keyset")) {
00338         int end=0;
00339 
00340         privateContext=xmlTextReaderGetAttribute(reader,(const xmlChar *)"parent");
00341         if (context && privateContext) {
00342             xmlStrPrintf(fullContext,sizeof(fullContext),
00343                 (const xmlChar *)"%s/%s", context, privateContext);
00344         }
00345 
00346         /* Parse everything else */
00347         while (!end) {
00348             xmlFree(nodeName); nodeName=0;
00349             xmlTextReaderRead(reader);
00350             nodeName=xmlTextReaderName(reader);
00351 
00352             if (!strcmp((char *)nodeName,"key")) {
00353                 if (privateContext) consumeKeyNode(ks,(char *)(*fullContext?fullContext:privateContext),reader);
00354                 else consumeKeyNode(ks,context,reader);
00355             } else if (!strcmp((char *)nodeName,"keyset")) {
00356                 /* A <keyset> can have nested <keyset>s */
00357                 if (xmlTextReaderNodeType(reader)==15)
00358                     /* found a </keyset> */
00359                     end=1;
00360                 else if (privateContext)
00361                     consumeKeySetNode(ks, (char *)(*fullContext?fullContext:privateContext), reader);
00362                 else consumeKeySetNode(ks, context, reader);
00363             }
00364         }
00365         if (privateContext) xmlFree(privateContext),privateContext=0;
00366     }
00367     return 0;
00368 }
00369 
00370 
00371 
00372 /*
00373  * This is the workhorse behind for ksFromXML() and ksFromXMLfile().
00374  * It will process the entire XML document in reader and convert and
00375  * save it in ks KeySet. Each node is processed by the processNode() function.
00376  *
00377  * This function is completelly dependent on libxml.
00378  */
00379 int ksFromXMLReader(KeySet *ks,xmlTextReaderPtr reader) {
00380     int ret;
00381     xmlChar *nodeName=0;
00382 
00383     ret = xmlTextReaderRead(reader); /* go to first node */
00384     while (ret == 1) {
00385         /* walk node per node until the end of the stream */
00386         nodeName=xmlTextReaderName(reader);
00387         
00388         if (!strcmp((char *)nodeName,"key"))
00389             consumeKeyNode(ks, 0, reader);
00390         else if (!strcmp((char *)nodeName,"keyset"))
00391             consumeKeySetNode(ks, 0, reader);
00392         
00393         ret = xmlTextReaderRead(reader);
00394     }
00395     xmlFreeTextReader(reader);
00396     if (ret) fprintf(stderr,"kdb: Failed to parse XML input\n");
00397 
00398     return ret;
00399 }
00400 
00401 
00402 
00403 int isValidXML(xmlDocPtr doc,char *schemaPath) {
00404     xmlSchemaPtr wxschemas = NULL;
00405     xmlSchemaValidCtxtPtr ctxt;
00406     xmlSchemaParserCtxtPtr ctxt2=NULL;
00407     int ret=0;
00408 
00409     ctxt2 = xmlSchemaNewParserCtxt(schemaPath);
00410 
00411 
00412     if (ctxt2==NULL) {
00413         xmlFreeDoc(doc);
00414         return 1;
00415     }
00416     
00417     xmlSchemaSetParserErrors(ctxt2,
00418         (xmlSchemaValidityErrorFunc) fprintf,
00419         (xmlSchemaValidityWarningFunc) fprintf,
00420         stderr);
00421     wxschemas = xmlSchemaParse(ctxt2);
00422     
00423     if (wxschemas==NULL) {
00424         xmlSchemaFreeParserCtxt(ctxt2);
00425         xmlFreeDoc(doc);
00426         return 1;
00427     }
00428     
00429     /* try to validate the doc against the xml schema */
00430     ctxt = xmlSchemaNewValidCtxt(wxschemas);
00431     xmlSchemaSetValidErrors(ctxt,
00432         (xmlSchemaValidityErrorFunc) fprintf,
00433         (xmlSchemaValidityWarningFunc) fprintf,
00434         stderr);
00435     
00436     if (ctxt==NULL) {
00437         xmlSchemaFree(wxschemas);
00438         xmlSchemaFreeParserCtxt(ctxt2);
00439         xmlFreeDoc(doc);
00440         return 1;
00441     }
00442     
00443     ret = xmlSchemaValidateDoc(ctxt, doc);
00444     xmlSchemaFreeValidCtxt(ctxt);
00445     xmlSchemaFree(wxschemas);
00446     xmlSchemaFreeParserCtxt(ctxt2);
00447 
00448     return ret;
00449 }
00450 
00451 
00452 
00462 int ksFromXMLfile(KeySet *ks,const char *filename) {
00463     xmlTextReaderPtr reader;
00464     xmlDocPtr doc;
00465     int ret=0;
00466     char schemaPath[513];
00467 
00468     doc = xmlParseFile(filename);
00469     if (doc==NULL) return 1;
00470     
00471     /* Open the kdb to get the xml schema path */
00472     schemaPath[0]=0;
00473     // ret=kdbGetValue(KDB_SCHEMA_PATH_KEY,schemaPath,sizeof(schemaPath));
00474 
00475 //  if (ret==0) ret = isValidXML(filename,schemaPath);
00476 //  else ret = isValidXML(filename,KDB_SCHEMA_PATH); /* fallback to builtin */
00477 
00478     
00479     /* if the validation was successful */
00480     if (!ret) {
00481         reader=xmlReaderWalker(doc);
00482         if (reader) ret=ksFromXMLReader(ks,reader);
00483         else {
00484             perror("kdb");
00485             return 1;
00486         }
00487     }
00488     xmlFreeDoc(doc);
00489     return ret;
00490 }
00491 
00492 
00493 
00494 
00495 
00496 /* FIXME: not working when fd is stdin */
00502 int ksFromXML(KeySet *ks,int fd) {
00503     /* Start of support for old XML library (no xmlReaderForFd()) */
00504     char filename[]="/var/tmp/kdbeditXXXXXX";
00505     FILE *xmlfile=0;
00506     xmlfile=fdopen(mkstemp(filename),"rw+");
00507     while (! feof(xmlfile)) {
00508         char buffer[1000];
00509         ssize_t readed, writen;
00510 
00511         readed=read(fd,buffer,sizeof(buffer));
00512         if (readed<0) {
00513             perror("kdb");
00514             fclose(xmlfile);
00515             remove(filename);
00516             return 1;
00517         }
00518 
00519         writen=write(fileno(xmlfile),buffer,readed);
00520         if (writen<0) {
00521             perror("kdb");
00522             fclose(xmlfile);
00523             remove(filename);
00524             return 1;
00525         }
00526     }
00527     fclose(xmlfile);
00528     return ksFromXMLfile(ks,filename);
00529     /* end of support */
00530 
00531     /* This code requires a newer version of XML library, not present in all
00532        Linux/BSD/Unix distros. Don't use it yet.
00533     // a complete XML document is expected
00534     xmlTextReaderPtr reader=0;
00535     int ret;
00536     reader=xmlReaderForFd(fd,"file:/tmp/imp.xml",0,0);
00537     if (reader) {
00538         ret=ksFromXMLReader(ks,reader);
00539     } else {
00540         printf("kdb: Unable to open file descriptor %d for XML reading\n", fd);
00541         return 1;
00542     }
00543     return ret;
00544     // end of newer code */
00545 }
00546 
00547 
00548 
00549 
00550 
00551 
00552 

Generated on Mon Dec 17 18:47:55 2007 for Elektra Project by  doxygen 1.5.4