VirtualBox

source: vbox/trunk/src/VBox/Main/xml/cfgldr.cpp@ 4071

Last change on this file since 4071 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 72.7 KB
Line 
1/** @file
2 *
3 * CFGLDR - Configuration Loader
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Define STANDALONE_TEST for making a executable that
19 * will load and parse a XML file
20 */
21// #define STANDALONE_TEST
22
23/** @page pg_cfgldr CFGLDR - The Configuration Loader
24 *
25 * The configuration loader loads and keeps the configuration of VBox
26 * session. It will be used by VBox components to retrieve configuration
27 * values at startup and to change values if necessary.
28 *
29 * When VBox is started, it must call CFGLDRLoad to load global
30 * VBox configuration and then VM configuration.
31 *
32 * Handles then used by VBox components to retrieve configuration
33 * values. Components must query their values at startup.
34 * Configuration values can be changed only by respective
35 * component and the component must call CFGLDRSet*, to change the
36 * value in configuration file.
37 *
38 * Values are accessed only by name. It is important for components to
39 * use CFGLDRQuery* only at startup for performance reason.
40 *
41 * All CFGLDR* functions that take a CFGHANDLE or CFGNODE as their first
42 * argument return the VERR_INVALID_HANDLE status code if the argument is
43 * null handle (i.e. its value is zero).
44 */
45
46#define VBOX_XML_WRITER_FILTER
47#define VBOX_XML_XSLT
48#define _CRT_SECURE_NO_DEPRECATE
49
50#define LOG_GROUP LOG_GROUP_MAIN
51#include <VBox/log.h>
52
53#include <VBox/err.h>
54#include <iprt/string.h>
55#include <iprt/uuid.h>
56#include <iprt/path.h>
57#include <iprt/file.h>
58#include <iprt/time.h>
59
60/// @todo (dmik) until RTTimeImplode and friends are done
61#include <time.h>
62
63#include <xercesc/util/PlatformUtils.hpp>
64
65#include <xercesc/dom/DOM.hpp>
66#include <xercesc/dom/DOMImplementation.hpp>
67#include <xercesc/dom/DOMImplementationLS.hpp>
68#include <xercesc/dom/DOMBuilder.hpp>
69#include <xercesc/dom/DOMWriter.hpp>
70
71#include <xercesc/framework/LocalFileFormatTarget.hpp>
72
73#include <xercesc/util/XMLUni.hpp>
74#include <xercesc/util/XMLUniDefs.hpp>
75#include <xercesc/util/BinInputStream.hpp>
76#include <xercesc/util/BinMemInputStream.hpp>
77
78#ifdef VBOX_XML_WRITER_FILTER
79#include <xercesc/dom/DOMWriterFilter.hpp>
80#endif
81
82#ifdef VBOX_XML_XSLT
83
84#include <xalanc/Include/PlatformDefinitions.hpp>
85#include <xalanc/XalanTransformer/XalanTransformer.hpp>
86#include <xalanc/XalanTransformer/XercesDOMWrapperParsedSource.hpp>
87#include <xalanc/XercesParserLiaison/XercesParserLiaison.hpp>
88#include <xalanc/XercesParserLiaison/XercesDOMSupport.hpp>
89#include <xalanc/XercesParserLiaison/FormatterToXercesDOM.hpp>
90#include <xalanc/XSLT/ProblemListener.hpp>
91
92XALAN_CPP_NAMESPACE_USE
93
94// generated file
95#include "SettingsConverter_xsl.h"
96
97#endif // VBOX_XML_XSLT
98
99// Little hack so we don't have to include the COM stuff
100// Note that those defines need to be made before we include
101// cfgldr.h so that the prototypes can be compiled.
102#ifndef BSTR
103#define OLECHAR wchar_t
104#define BSTR void*
105#if defined(RT_OS_WINDOWS)
106extern "C" BSTR __stdcall SysAllocString(const OLECHAR* sz);
107#else
108extern "C" BSTR SysAllocString(const OLECHAR* sz);
109#endif
110#endif
111
112#include "Logging.h"
113
114#include <VBox/cfgldr.h>
115#include "cfgldrhlp.h"
116
117#include <string.h>
118#include <stdio.h> // for sscanf
119#ifdef STANDALONE_TEST
120# include <stdlib.h>
121# include <iprt/runtime.h>
122#endif
123
124XERCES_CPP_NAMESPACE_USE
125
126class CfgNode
127{
128 private:
129 friend class CfgLoader;
130 CfgLoader *pConfiguration;
131 CfgNode *next;
132 CfgNode *prev;
133
134 DOMNode *pdomnode;
135
136 CfgNode (CfgLoader *pcfg);
137 virtual ~CfgNode ();
138
139 int resolve (DOMNode *root, const char *pszName, unsigned uIndex, unsigned flags);
140
141 int queryValueString (const char *pszName, PRTUTF16 *ppwszValue);
142 int setValueString (const char *pszName, PRTUTF16 pwszValue);
143
144 DOMNode *findChildText (void);
145
146 public:
147
148 enum {
149 fSearch = 0,
150 fCreateIfNotExists = 1,
151 fAppend = 2
152 };
153
154 static int ReleaseNode (CfgNode *pnode);
155 static int DeleteNode (CfgNode *pnode);
156
157 int CreateChildNode (const char *pszName, CfgNode **ppnode);
158 int AppendChildNode (const char *pszName, CfgNode **ppnode);
159
160 int GetChild (const char *pszName, unsigned uIndex, CfgNode **ppnode);
161 int CountChildren (const char *pszChildName, unsigned *pCount);
162
163
164 int QueryUInt32 (const char *pszName, uint32_t *pulValue);
165 int SetUInt32 (const char *pszName, uint32_t ulValue);
166 int QueryUInt64 (const char *pszName, uint64_t *pullValue);
167 int SetUInt64 (const char *pszName, uint64_t ullValue);
168
169 int QueryInt32 (const char *pszName, int32_t *pint32Value);
170 int SetInt32 (const char *pszName, int32_t int32Value);
171 int QueryInt64 (const char *pszName, int64_t *pint64Value);
172 int SetInt64 (const char *pszName, int64_t int64Value);
173
174 int QueryUInt16 (const char *pszName, uint16_t *pu16Value);
175 int SetUInt16 (const char *pszName, uint16_t u16Value);
176
177 int QueryBin (const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue);
178 int SetBin (const char *pszName, const void *pvValue, unsigned cbValue);
179 int QueryString (const char *pszName, void **pValue, unsigned cbValue, unsigned *pcbValue, bool returnUtf16);
180 int SetString (const char *pszName, const char *pszValue, unsigned cbValue, bool isUtf16);
181
182 int QueryBool (const char *pszName, bool *pfValue);
183 int SetBool (const char *pszName, bool fValue);
184
185 int DeleteAttribute (const char *pszName);
186};
187
188class CfgLoader
189{
190 private:
191
192 friend class CfgNode;
193
194 PRTUTF16 pwszOriginalFilename;
195 RTFILE hOriginalFileHandle; /** r=bird: this is supposed to mean 'handle to the orignal file handle'? The name is
196 * overloaded with too many 'handles'... That goes for those hFileHandle parameters too.
197 * hOriginalFile / FileOriginal and hFile / File should do by a long way. */
198 CfgNode *pfirstnode;
199
200 DOMBuilder *builder;
201 DOMNode *root;
202
203 int getNode (DOMNode *prootnode, const char *pszName, unsigned uIndex, CfgNode **ppnode, unsigned flags);
204
205 DOMDocument *Document(void) { return static_cast<DOMDocument *>(root); };
206
207 public:
208
209 CfgLoader ();
210 virtual ~CfgLoader ();
211
212 int Load (const char *pszFileName, RTFILE hFileHandle,
213 const char *pszExternalSchemaLocation, bool bDoNamespaces,
214 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
215 char **ppszErrorMessage);
216 int Save (const char *pszFilename, RTFILE hFileHandle,
217 char **ppszErrorMessage);
218 int Create ();
219#ifdef VBOX_XML_XSLT
220 int Transform (const char *pszTemlateLocation,
221 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
222 char **ppszErrorMessage);
223#endif
224
225 static int FreeConfiguration (CfgLoader *pcfg);
226
227 int CreateNode (const char *pszName, CfgNode **ppnode);
228
229 int GetNode (const char *pszName, unsigned uIndex, CfgNode **ppnode);
230};
231
232#ifdef VBOX_XML_WRITER_FILTER
233class VBoxWriterFilter : public DOMWriterFilter
234{
235 public:
236 VBoxWriterFilter (unsigned long whatToShow = DOMNodeFilter::SHOW_ALL);
237 ~VBoxWriterFilter () {};
238
239 virtual short acceptNode (const DOMNode*) const;
240 virtual unsigned long getWhatToShow () const { return fWhatToShow; };
241 virtual void setWhatToShow (unsigned long toShow) { fWhatToShow = toShow; };
242
243 private:
244 unsigned long fWhatToShow;
245};
246
247VBoxWriterFilter::VBoxWriterFilter(unsigned long whatToShow)
248 :
249 fWhatToShow (whatToShow)
250{
251}
252
253short VBoxWriterFilter::acceptNode(const DOMNode* node) const
254{
255 switch (node->getNodeType())
256 {
257 case DOMNode::TEXT_NODE:
258 {
259 /* Reject empty nodes */
260 const XMLCh *pxmlch = node->getNodeValue ();
261
262 if (pxmlch)
263 {
264 while (*pxmlch != chNull)
265 {
266 if ( *pxmlch == chLF
267 || *pxmlch == chCR
268 || *pxmlch == chSpace
269 || *pxmlch == chHTab
270 )
271 {
272 pxmlch++;
273 continue;
274 }
275
276 break;
277 }
278
279 if (*pxmlch != chNull)
280 {
281 /* Accept the node because it contains non space characters */
282 return DOMNodeFilter::FILTER_ACCEPT;
283 }
284 }
285
286 return DOMNodeFilter::FILTER_REJECT;
287 }
288 }
289
290 return DOMNodeFilter::FILTER_ACCEPT;
291}
292
293#endif
294
295// CfgLdrInputSource
296////////////////////////////////////////////////////////////////////////////////
297
298/**
299 * A wrapper around RTFILE or a char buffer that acts like a DOMInputSource
300 * and therefore can be with DOMBuilder instances.
301 */
302class CfgLdrInputSource : public DOMInputSource
303{
304public:
305
306 CfgLdrInputSource (PCFGLDRENTITY pcEntity, const char *pcszSystemId);
307 virtual ~CfgLdrInputSource() { release(); }
308
309 // Functions introduced in DOM Level 3
310
311 const XMLCh *getEncoding () const { return NULL; }
312 const XMLCh *getPublicId () const { return NULL; }
313 const XMLCh *getSystemId () const { return (const XMLCh *)m_pwszSystemId; }
314 const XMLCh *getBaseURI () const { return (const XMLCh *)m_pwszBaseURI; }
315
316 void setEncoding (const XMLCh *const /* encodingStr */)
317 { AssertMsgFailed (("Not implemented!\n")); }
318 void setPublicId (const XMLCh *const /* publicId */)
319 { AssertMsgFailed (("Not implemented!\n")); }
320 void setSystemId (const XMLCh *const /* systemId */)
321 { AssertMsgFailed (("Not implemented!\n")); }
322 void setBaseURI (const XMLCh *const /* baseURI */)
323 { AssertMsgFailed (("Not implemented!\n")); }
324
325 // Non-standard Extension
326
327 BinInputStream *makeStream() const;
328 void setIssueFatalErrorIfNotFound (const bool /* flag */) {}
329 bool getIssueFatalErrorIfNotFound() const { return true; }
330 void release();
331
332private:
333
334 class FileHandleInputStream : public BinInputStream
335 {
336 public:
337
338 FileHandleInputStream (RTFILE hFileHandle);
339
340 unsigned int curPos() const;
341 unsigned int readBytes (XMLByte *const toFill, const unsigned int maxToRead);
342
343 private:
344
345 RTFILE m_hFileHandle;
346 uint64_t m_cbPos;
347 };
348
349 void init (const char *pcszSystemId);
350
351 CFGLDRENTITY m_entity;
352
353 PRTUTF16 m_pwszSystemId;
354 PRTUTF16 m_pwszBaseURI;
355};
356
357CfgLdrInputSource::CfgLdrInputSource (PCFGLDRENTITY pcEntity,
358 const char *pcszSystemId) :
359 m_pwszSystemId (NULL), m_pwszBaseURI (NULL)
360{
361 Assert (pcEntity && pcEntity->enmType != CFGLDRENTITYTYPE_INVALID);
362 // make a copy of the entity descriptor
363 m_entity = *pcEntity;
364
365 Assert (pcszSystemId);
366 int rc = RTStrToUtf16 (pcszSystemId, &m_pwszSystemId);
367 AssertRC (rc);
368
369 char *pszBaseURI = NULL;
370 pszBaseURI = RTStrDup (pcszSystemId);
371 Assert (pszBaseURI);
372 RTPathStripFilename (pszBaseURI);
373
374 rc = RTStrToUtf16 (pszBaseURI, &m_pwszBaseURI);
375 AssertRC (rc);
376}
377
378void CfgLdrInputSource::release()
379{
380 switch (m_entity.enmType)
381 {
382 case CFGLDRENTITYTYPE_HANDLE:
383 if (m_entity.u.handle.bClose)
384 RTFileClose (m_entity.u.handle.hFile);
385 break;
386 case CFGLDRENTITYTYPE_MEMORY:
387 if (m_entity.u.memory.bFree)
388 RTMemTmpFree (m_entity.u.memory.puchBuf);
389 break;
390 default:
391 break;
392 };
393
394 m_entity.enmType = CFGLDRENTITYTYPE_INVALID;
395
396 if (m_pwszBaseURI)
397 {
398 RTUtf16Free (m_pwszBaseURI);
399 m_pwszBaseURI = NULL;
400 }
401 if (m_pwszSystemId)
402 {
403 RTUtf16Free (m_pwszSystemId);
404 m_pwszSystemId = NULL;
405 }
406}
407
408BinInputStream *CfgLdrInputSource::makeStream() const
409{
410 BinInputStream *stream = NULL;
411
412 switch (m_entity.enmType)
413 {
414 case CFGLDRENTITYTYPE_HANDLE:
415 stream = new FileHandleInputStream (m_entity.u.handle.hFile);
416 break;
417 case CFGLDRENTITYTYPE_MEMORY:
418 // puchBuf is neither copied nor destructed by BinMemInputStream
419 stream = new BinMemInputStream (m_entity.u.memory.puchBuf,
420 m_entity.u.memory.cbBuf,
421 BinMemInputStream::BufOpt_Reference);
422 break;
423 default:
424 AssertMsgFailed (("Invalid resolver entity type!\n"));
425 break;
426 };
427
428 return stream;
429}
430
431CfgLdrInputSource::FileHandleInputStream::FileHandleInputStream (RTFILE hFileHandle) :
432 m_hFileHandle (hFileHandle),
433 m_cbPos (0)
434{
435}
436
437unsigned int CfgLdrInputSource::FileHandleInputStream::curPos() const
438{
439 AssertMsg (!(m_cbPos >> 32), ("m_cbPos exceeds 32 bits (%16Xll)\n", m_cbPos));
440 return (unsigned int)m_cbPos;
441}
442
443unsigned int CfgLdrInputSource::FileHandleInputStream::readBytes (
444 XMLByte *const toFill, const unsigned int maxToRead
445)
446{
447 /// @todo (dmik) trhow the appropriate exceptions if we fail to write
448
449 int rc;
450 NOREF (rc);
451
452 // memorize the current position
453 uint64_t cbOriginalPos = RTFileTell (m_hFileHandle);
454 Assert (cbOriginalPos != ~0ULL);
455 // set the new position
456 rc = RTFileSeek (m_hFileHandle, m_cbPos, RTFILE_SEEK_BEGIN, NULL);
457 AssertRC (rc);
458
459 // read from the file
460 unsigned cbRead = 0;
461 rc = RTFileRead (m_hFileHandle, toFill, maxToRead, &cbRead);
462 AssertRC (rc);
463
464 // adjust the private position
465 m_cbPos += cbRead;
466 // restore the current position
467 rc = RTFileSeek (m_hFileHandle, cbOriginalPos, RTFILE_SEEK_BEGIN, NULL);
468 AssertRC (rc);
469
470 return cbRead;
471}
472
473// CfgLdrFormatTarget
474////////////////////////////////////////////////////////////////////////////////
475
476class CfgLdrFormatTarget : public XMLFormatTarget
477{
478public:
479
480 CfgLdrFormatTarget (PCFGLDRENTITY pcEntity);
481 ~CfgLdrFormatTarget();
482
483 // virtual XMLFormatTarget methods
484 void writeChars (const XMLByte *const toWrite, const unsigned int count,
485 XMLFormatter *const formatter);
486 void flush() {}
487
488private:
489
490 CFGLDRENTITY m_entity;
491};
492
493CfgLdrFormatTarget::CfgLdrFormatTarget (PCFGLDRENTITY pcEntity)
494{
495 Assert (pcEntity && pcEntity->enmType != CFGLDRENTITYTYPE_INVALID);
496 // make a copy of the entity descriptor
497 m_entity = *pcEntity;
498
499 switch (m_entity.enmType)
500 {
501 case CFGLDRENTITYTYPE_HANDLE:
502 int rc;
503 // start writting from the beginning
504 rc = RTFileSeek (m_entity.u.handle.hFile, 0, RTFILE_SEEK_BEGIN, NULL);
505 AssertRC (rc);
506 NOREF (rc);
507 break;
508 case CFGLDRENTITYTYPE_MEMORY:
509 AssertMsgFailed (("Unsupported entity type!\n"));
510 break;
511 default:
512 break;
513 };
514}
515
516CfgLdrFormatTarget::~CfgLdrFormatTarget()
517{
518 switch (m_entity.enmType)
519 {
520 case CFGLDRENTITYTYPE_HANDLE:
521 {
522 int rc;
523 // truncate the file upto the size actually written
524 uint64_t cbPos = RTFileTell (m_entity.u.handle.hFile);
525 Assert (cbPos != ~0ULL);
526 rc = RTFileSetSize (m_entity.u.handle.hFile, cbPos);
527 AssertRC (rc);
528 // reset the position to he beginning
529 rc = RTFileSeek (m_entity.u.handle.hFile, 0, RTFILE_SEEK_BEGIN, NULL);
530 AssertRC (rc);
531 NOREF (rc);
532 if (m_entity.u.handle.bClose)
533 RTFileClose (m_entity.u.handle.hFile);
534 break;
535 }
536 case CFGLDRENTITYTYPE_MEMORY:
537 break;
538 default:
539 break;
540 };
541}
542
543void CfgLdrFormatTarget::writeChars (const XMLByte *const toWrite,
544 const unsigned int count,
545 XMLFormatter *const /* formatter */)
546{
547 /// @todo (dmik) trhow the appropriate exceptions if we fail to write
548
549 switch (m_entity.enmType)
550 {
551 case CFGLDRENTITYTYPE_HANDLE:
552 int rc;
553 rc = RTFileWrite (m_entity.u.handle.hFile, toWrite, count, NULL);
554 AssertRC (rc);
555 NOREF (rc);
556 break;
557 case CFGLDRENTITYTYPE_MEMORY:
558 AssertMsgFailed (("Unsupported entity type!\n"));
559 break;
560 default:
561 AssertMsgFailed (("Invalid entity type!\n"));
562 break;
563 };
564}
565
566// CfgLdrEntityResolver
567////////////////////////////////////////////////////////////////////////////////
568
569/**
570 * A wrapper around FNCFGLDRENTITYRESOLVER callback that acts like a
571 * DOMEntityResolver and therefore can be used with DOMBuilder instances.
572 */
573class CfgLdrEntityResolver : public DOMEntityResolver
574{
575public:
576
577 CfgLdrEntityResolver (PFNCFGLDRENTITYRESOLVER pfnEntityResolver) :
578 m_pfnEntityResolver (pfnEntityResolver) {}
579
580 // Functions introduced in DOM Level 2
581 DOMInputSource *resolveEntity (const XMLCh *const publicId,
582 const XMLCh *const systemId,
583 const XMLCh *const baseURI);
584
585private:
586
587 PFNCFGLDRENTITYRESOLVER m_pfnEntityResolver;
588};
589
590DOMInputSource *CfgLdrEntityResolver::resolveEntity (const XMLCh *const publicId,
591 const XMLCh *const systemId,
592 const XMLCh *const baseURI)
593{
594 if (!m_pfnEntityResolver)
595 return NULL;
596
597 DOMInputSource *source = NULL;
598 int rc = VINF_SUCCESS;
599
600 char *pszPublicId = NULL;
601 char *pszSystemId = NULL;
602 char *pszBaseURI = NULL;
603
604 if (publicId)
605 rc = RTUtf16ToUtf8 (publicId, &pszPublicId);
606 if (VBOX_SUCCESS (rc))
607 {
608 if (systemId)
609 rc = RTUtf16ToUtf8 (systemId, &pszSystemId);
610 if (VBOX_SUCCESS (rc))
611 {
612 if (baseURI)
613 rc = RTUtf16ToUtf8 (baseURI, &pszBaseURI);
614 if (VBOX_SUCCESS (rc))
615 {
616 CFGLDRENTITY entity;
617 rc = m_pfnEntityResolver (pszPublicId, pszSystemId, pszBaseURI,
618 &entity);
619 if (rc == VINF_SUCCESS)
620 source = new CfgLdrInputSource (&entity, pszSystemId);
621 }
622 }
623 }
624
625 if (pszBaseURI)
626 RTStrFree (pszBaseURI);
627 if (pszSystemId)
628 RTStrFree (pszSystemId);
629 if (pszPublicId)
630 RTStrFree (pszPublicId);
631
632 return source;
633}
634
635// CfgLdrErrorHandler
636////////////////////////////////////////////////////////////////////////////////
637
638/**
639 * An error handler that accumulates all error messages in a single UTF-8 string.
640 */
641class CfgLdrErrorHandler : public DOMErrorHandler
642#ifdef VBOX_XML_XSLT
643 , public ProblemListener
644#endif
645{
646public:
647
648 CfgLdrErrorHandler();
649 ~CfgLdrErrorHandler();
650
651 bool hasErrors() { return m_pszBuf != NULL; }
652
653 /** Transfers ownership of the string to the caller and resets the handler */
654 char *takeErrorMessage() {
655 char *pszBuf = m_pszBuf;
656 m_pszBuf = NULL;
657 return pszBuf;
658 }
659
660 // Functions introduced in DOM Level 3
661 bool handleError (const DOMError &domError);
662
663#ifdef VBOX_XML_XSLT
664 // Xalan ProblemListener interface
665 void setPrintWriter (PrintWriter *pw) {}
666 void problem (eProblemSource where, eClassification classification,
667 const XalanNode *sourceNode, const ElemTemplateElement *styleNode,
668 const XalanDOMString &msg, const XalanDOMChar *uri,
669 int lineNo, int charOffset);
670#endif
671
672private:
673
674 char *m_pszBuf;
675};
676
677CfgLdrErrorHandler::CfgLdrErrorHandler() :
678 m_pszBuf (NULL)
679{
680}
681
682CfgLdrErrorHandler::~CfgLdrErrorHandler()
683{
684 if (m_pszBuf)
685 RTMemTmpFree (m_pszBuf);
686}
687
688bool CfgLdrErrorHandler::handleError (const DOMError &domError)
689{
690 const char *pszSeverity = NULL;
691 switch (domError.getSeverity())
692 {
693 case DOMError::DOM_SEVERITY_WARNING: pszSeverity = "WARNING: ";
694 case DOMError::DOM_SEVERITY_ERROR: pszSeverity = "ERROR: ";
695 case DOMError::DOM_SEVERITY_FATAL_ERROR: pszSeverity = "FATAL ERROR: ";
696 }
697
698 char *pszLocation = NULL;
699 const DOMLocator *pLocation = domError.getLocation();
700 if (pLocation)
701 {
702 static const char Location[] = "\nLocation: '%s', line %d, column %d";
703
704 char *pszURI = NULL;
705 if (pLocation->getURI())
706 RTUtf16ToUtf8 (pLocation->getURI(), &pszURI);
707
708 size_t cbLocation = sizeof (Location) +
709 (pszURI ? strlen (pszURI) : 10 /* NULL */) +
710 10 + 10 + 1 /* line & column & \0 */;
711 pszLocation = (char *) RTMemTmpAllocZ (cbLocation);
712 RTStrPrintf (pszLocation, cbLocation, Location,
713 pszURI,
714 pLocation->getLineNumber(), pLocation->getColumnNumber());
715
716 if (pszURI)
717 RTStrFree (pszURI);
718 }
719
720 LogFlow (("CfgLdrErrorHandler::handleError():\n %s%ls%s\n",
721 pszSeverity, domError.getMessage(), pszLocation));
722
723 char *pszMsg = NULL;
724 if (domError.getMessage())
725 RTUtf16ToUtf8 (domError.getMessage(), &pszMsg);
726
727 size_t cbNewBuf = (m_pszBuf ? strlen (m_pszBuf) : 0) +
728 (pszSeverity ? strlen (pszSeverity) : 0) +
729 (pszMsg ? strlen (pszMsg) : 0) +
730 (pszLocation ? strlen (pszLocation) : 0);
731 char *pszNewBuf = (char *) RTMemTmpAllocZ (cbNewBuf + 2 /* \n + \0 */);
732
733 if (m_pszBuf)
734 {
735 strcpy (pszNewBuf, m_pszBuf);
736 strcat (pszNewBuf, "\n");
737 }
738 if (pszSeverity)
739 strcat (pszNewBuf, pszSeverity);
740 if (pszMsg)
741 strcat (pszNewBuf, pszMsg);
742 if (pszLocation)
743 strcat (pszNewBuf, pszLocation);
744
745 if (m_pszBuf)
746 RTMemTmpFree (m_pszBuf);
747 m_pszBuf = pszNewBuf;
748
749 if (pszLocation)
750 RTMemTmpFree (pszLocation);
751 if (pszMsg)
752 RTStrFree (pszMsg);
753
754 // fail on any error when possible
755 return false;
756}
757
758#ifdef VBOX_XML_XSLT
759void CfgLdrErrorHandler::problem (eProblemSource where, eClassification classification,
760 const XalanNode *sourceNode, const ElemTemplateElement *styleNode,
761 const XalanDOMString &msg, const XalanDOMChar *uri,
762 int lineNo, int charOffset)
763{
764 const char *pszClass = NULL;
765 switch (classification)
766 {
767 case eMESSAGE: pszClass = "INFO: ";
768 case eWARNING: pszClass = "WARNING: ";
769 case eERROR: pszClass = "ERROR: ";
770 }
771
772 LogFlow (("CfgLdrErrorHandler::problem():\n %s%ls\n", pszClass, msg.c_str()));
773
774 char *pszMsg = NULL;
775 if (msg.c_str())
776 RTUtf16ToUtf8 (msg.c_str(), &pszMsg);
777
778 size_t cbNewBuf = (m_pszBuf ? strlen (m_pszBuf) : 0) +
779 (pszClass ? strlen (pszClass) : 0) +
780 (pszMsg ? strlen (pszMsg) : 0);
781 char *pszNewBuf = (char *) RTMemTmpAllocZ (cbNewBuf + 2 /* \n + \0 */);
782
783 if (m_pszBuf)
784 {
785 strcpy (pszNewBuf, m_pszBuf);
786 strcat (pszNewBuf, "\n");
787 }
788 if (pszClass)
789 strcat (pszNewBuf, pszClass);
790 if (pszMsg)
791 strcat (pszNewBuf, pszMsg);
792
793 if (m_pszBuf)
794 RTStrFree (m_pszBuf);
795
796 m_pszBuf = RTStrDup (pszNewBuf);
797
798 if (pszNewBuf)
799 RTMemTmpFree (pszNewBuf);
800 if (pszMsg)
801 RTStrFree (pszMsg);
802}
803#endif
804
805//
806////////////////////////////////////////////////////////////////////////////////
807
808static int xmlInitialized = 0;
809
810static int initXML (void)
811{
812 try
813 {
814 XMLPlatformUtils::Initialize();
815 }
816
817 catch(...)
818 {
819 return 0;
820 }
821
822 return (xmlInitialized = 1);
823}
824
825static void terminateXML (void)
826{
827 if (xmlInitialized)
828 {
829 XMLPlatformUtils::Terminate();
830 xmlInitialized = 0;
831 }
832}
833
834/* CfgLoader implementation */
835CfgLoader::CfgLoader ()
836 :
837 pwszOriginalFilename (NULL),
838 hOriginalFileHandle (NIL_RTFILE),
839 pfirstnode (NULL),
840 builder (NULL),
841 root (NULL)
842{
843}
844
845CfgLoader::~CfgLoader ()
846{
847 if (pwszOriginalFilename)
848 {
849 RTUtf16Free (pwszOriginalFilename);
850 }
851
852 if (builder)
853 {
854 /* Configuration was parsed from a file.
855 * Parser owns root and will delete root.
856 */
857 builder->release();
858 }
859 else if (root)
860 {
861 /* This is new, just created configuration.
862 * root is new object created by DOMImplementation::createDocument
863 * We have to delete root.
864 */
865 root->release();
866 }
867}
868
869int CfgLoader::Load (const char *pszFileName, RTFILE hFileHandle,
870 const char *pszExternalSchemaLocation, bool bDoNamespaces,
871 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
872 char **ppszErrorMessage)
873{
874 if (!xmlInitialized)
875 return VERR_NOT_SUPPORTED;
876
877 Assert (!root && !pwszOriginalFilename);
878 if (root || pwszOriginalFilename)
879 return VERR_ALREADY_LOADED;
880
881 static const XMLCh LS[] = { chLatin_L, chLatin_S, chNull };
882 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation (LS);
883 if (!impl)
884 return VERR_NOT_SUPPORTED;
885
886 // note:
887 // member variables allocated here (builder, pwszOriginalFilename) are not
888 // freed in case of error because CFGLDRLoad() that calls this method will
889 // delete this instance (and all members) if we return a failure from here
890
891 builder = static_cast <DOMImplementationLS *> (impl)->
892 createDOMBuilder (DOMImplementationLS::MODE_SYNCHRONOUS, 0);
893 if (!builder)
894 return VERR_NOT_SUPPORTED;
895
896 int rc = VINF_SUCCESS;
897
898 if (ppszErrorMessage)
899 *ppszErrorMessage = NULL;
900
901 // set parser's features
902 Assert (builder->canSetFeature (XMLUni::fgDOMDatatypeNormalization, true));
903 if (builder->canSetFeature (XMLUni::fgDOMDatatypeNormalization, true))
904 builder->setFeature (XMLUni::fgDOMDatatypeNormalization, true);
905 else
906 return VERR_NOT_SUPPORTED;
907 if (bDoNamespaces)
908 {
909 Assert (builder->canSetFeature (XMLUni::fgDOMNamespaces, true));
910 if (builder->canSetFeature (XMLUni::fgDOMNamespaces, true))
911 builder->setFeature (XMLUni::fgDOMNamespaces, true);
912 else
913 return VERR_NOT_SUPPORTED;
914 }
915 if (pszExternalSchemaLocation)
916 {
917 // set validation related features & properties
918 Assert (builder->canSetFeature (XMLUni::fgDOMValidation, true));
919 if (builder->canSetFeature (XMLUni::fgDOMValidation, true))
920 builder->setFeature (XMLUni::fgDOMValidation, true);
921 else
922 return VERR_NOT_SUPPORTED;
923 Assert (builder->canSetFeature (XMLUni::fgXercesSchema, true));
924 if (builder->canSetFeature (XMLUni::fgXercesSchema, true))
925 builder->setFeature (XMLUni::fgXercesSchema, true);
926 else
927 return VERR_NOT_SUPPORTED;
928 Assert (builder->canSetFeature (XMLUni::fgXercesSchemaFullChecking, true));
929 if (builder->canSetFeature (XMLUni::fgXercesSchemaFullChecking, true))
930 builder->setFeature (XMLUni::fgXercesSchemaFullChecking, true);
931 else
932 return VERR_NOT_SUPPORTED;
933
934 PRTUTF16 pwszExternalSchemaLocation = NULL;
935 rc = RTStrToUtf16 (pszExternalSchemaLocation, &pwszExternalSchemaLocation);
936 if (VBOX_FAILURE (rc))
937 return rc;
938
939 if (bDoNamespaces)
940 {
941 // set schema that supports namespaces
942 builder->setProperty (XMLUni::fgXercesSchemaExternalSchemaLocation,
943 pwszExternalSchemaLocation);
944 }
945 else
946 {
947 // set schema that doesn't support namespaces
948 builder->setProperty (XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation,
949 pwszExternalSchemaLocation);
950 }
951
952 RTUtf16Free (pwszExternalSchemaLocation);
953 }
954
955 hOriginalFileHandle = hFileHandle;
956 rc = RTStrToUtf16 (pszFileName, &pwszOriginalFilename);
957 if (VBOX_FAILURE (rc))
958 return rc;
959
960 CfgLdrEntityResolver entityResolver (pfnEntityResolver);
961 builder->setEntityResolver (&entityResolver);
962
963 CfgLdrErrorHandler errorHandler;
964 builder->setErrorHandler (&errorHandler);
965
966 try
967 {
968 if (hFileHandle != NIL_RTFILE)
969 {
970 CFGLDRENTITY entity;
971 entity.enmType = CFGLDRENTITYTYPE_HANDLE;
972 entity.u.handle.hFile = hFileHandle;
973 entity.u.handle.bClose = false;
974 CfgLdrInputSource source (&entity, pszFileName);
975 root = builder->parse (source);
976 }
977 else
978 {
979 root = builder->parseURI (pwszOriginalFilename);
980 }
981 }
982 catch (...)
983 {
984 rc = VERR_OPEN_FAILED;
985 }
986
987 if (errorHandler.hasErrors())
988 {
989 // this will transfer ownership of the string
990 if (ppszErrorMessage)
991 *ppszErrorMessage = errorHandler.takeErrorMessage();
992 rc = VERR_OPEN_FAILED;
993 }
994
995 builder->setErrorHandler (NULL);
996 builder->setEntityResolver (NULL);
997
998 return rc;
999}
1000
1001int CfgLoader::Save (const char *pszFilename, RTFILE hFileHandle,
1002 char **ppszErrorMessage)
1003{
1004 if (!pszFilename && !pwszOriginalFilename &&
1005 hFileHandle == NIL_RTFILE && hOriginalFileHandle == NIL_RTFILE)
1006 {
1007 // no explicit handle/filename specified and the configuration
1008 // was created from scratch
1009 return VERR_INVALID_PARAMETER;
1010 }
1011
1012 static const XMLCh LS[] = { chLatin_L, chLatin_S, chNull };
1013 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation (LS);
1014 if (!impl)
1015 return VERR_NOT_SUPPORTED;
1016
1017 DOMWriter *writer = static_cast <DOMImplementationLS *> (impl)->createDOMWriter();
1018 if (!writer)
1019 return VERR_NOT_SUPPORTED;
1020
1021 int rc = VINF_SUCCESS;
1022
1023 if (ppszErrorMessage)
1024 *ppszErrorMessage = NULL;
1025
1026#ifdef VBOX_XML_WRITER_FILTER
1027 VBoxWriterFilter theFilter (DOMNodeFilter::SHOW_TEXT);
1028 writer->setFilter (&theFilter);
1029#endif
1030
1031 writer->setEncoding (XMLUni::fgUTF8EncodingString);
1032
1033 // set feature if the serializer supports the feature/mode
1034 if (writer->canSetFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true))
1035 writer->setFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true);
1036 if (writer->canSetFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true))
1037 writer->setFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true);
1038
1039 CfgLdrErrorHandler errorHandler;
1040 writer->setErrorHandler (&errorHandler);
1041
1042 try
1043 {
1044 if (hFileHandle != NIL_RTFILE || hOriginalFileHandle != NIL_RTFILE)
1045 {
1046 CFGLDRENTITY entity;
1047 entity.enmType = CFGLDRENTITYTYPE_HANDLE;
1048 entity.u.handle.hFile = hFileHandle != NIL_RTFILE ? hFileHandle :
1049 hOriginalFileHandle;
1050 entity.u.handle.bClose = false;
1051 CfgLdrFormatTarget target (&entity);
1052 writer->writeNode (&target, *root);
1053 }
1054 else
1055 {
1056 PRTUTF16 pwszFilename = NULL;
1057 if (pszFilename)
1058 rc = RTStrToUtf16 (pszFilename, &pwszFilename);
1059 if (VBOX_SUCCESS (rc))
1060 {
1061 LocalFileFormatTarget target (pwszFilename ? pwszFilename :
1062 pwszOriginalFilename);
1063 if (pwszFilename)
1064 RTUtf16Free (pwszFilename);
1065
1066 writer->writeNode (&target, *root);
1067 }
1068 }
1069 }
1070 catch(...)
1071 {
1072 rc = VERR_FILE_IO_ERROR;
1073 }
1074
1075 if (errorHandler.hasErrors())
1076 {
1077 // this will transfer ownership of the string
1078 if (ppszErrorMessage)
1079 *ppszErrorMessage = errorHandler.takeErrorMessage();
1080 rc = VERR_FILE_IO_ERROR;
1081 }
1082
1083 writer->release();
1084
1085 return rc;
1086}
1087
1088#ifdef VBOX_XML_XSLT
1089int CfgLoader::Transform (const char *pszTemlateLocation,
1090 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
1091 char **ppszErrorMessage)
1092{
1093 AssertReturn (strcmp (pszTemlateLocation, "SettingsConverter.xsl") == 0,
1094 VERR_NOT_SUPPORTED);
1095 AssertReturn (pfnEntityResolver == NULL,
1096 VERR_NOT_SUPPORTED);
1097
1098 int rc = VINF_SUCCESS;
1099
1100 if (ppszErrorMessage)
1101 *ppszErrorMessage = NULL;
1102
1103 XalanTransformer::initialize();
1104
1105 XalanTransformer xalan;
1106
1107 // input stream to read from SettingsConverter_xsl
1108 struct SettingsConverterStream : public XSLTInputSource
1109 {
1110 SettingsConverterStream()
1111 {
1112 XMLCh *id = XMLString::transcode ("SettingsConverter.xsl");
1113 setSystemId (id);
1114 setPublicId (id);
1115 XMLString::release (&id);
1116 }
1117
1118 BinInputStream *makeStream () const
1119 {
1120 return new BinMemInputStream (g_abSettingsConverter_xsl,
1121 g_cbSettingsConverter_xsl);
1122 }
1123 };
1124
1125 CfgLdrErrorHandler errorHandler;
1126 xalan.setProblemListener (&errorHandler);
1127
1128 try
1129 {
1130 // target is the DOM tree
1131 DOMDocument *newRoot =
1132 DOMImplementation::getImplementation()->createDocument();
1133 FormatterToXercesDOM formatter (newRoot, 0);
1134
1135 // source is the DOM tree
1136 XercesDOMSupport support;
1137 XercesParserLiaison liaison;
1138 const XercesDOMWrapperParsedSource parsedSource (
1139 Document(), liaison, support,
1140 XalanDOMString (pwszOriginalFilename));
1141
1142 // stylesheet
1143 SettingsConverterStream xsl;
1144
1145 int xrc = xalan.transform (parsedSource, xsl, formatter);
1146
1147 if (xrc)
1148 {
1149 LogFlow(("xalan.transform() = %d (%s)\n", xrc, xalan.getLastError()));
1150
1151 newRoot->release();
1152 rc = VERR_FILE_IO_ERROR;
1153 }
1154 else
1155 {
1156 // release the builder and the old document, if any
1157 if (builder)
1158 {
1159 builder->release();
1160 builder = 0;
1161 root = 0;
1162 }
1163 else if (root)
1164 {
1165 root->release();
1166 root = 0;
1167 }
1168
1169 root = newRoot;
1170
1171 // Xalan 1.9.0 (and 1.10.0) is stupid bacause disregards the
1172 // XSLT specs and ignores the exclude-result-prefixes stylesheet
1173 // attribute, flooding all the elements with stupid xmlns
1174 // specifications. Here we remove them.
1175 XMLCh *xmlnsName = XMLString::transcode ("xmlns");
1176 XMLCh *xmlnsVBox = XMLString::transcode ("http://www.innotek.de/VirtualBox-settings");
1177 DOMNodeIterator *iter =
1178 newRoot->createNodeIterator (newRoot, DOMNodeFilter::SHOW_ELEMENT,
1179 NULL, false);
1180 DOMNode *node = NULL;
1181 while ((node = iter->nextNode()) != NULL)
1182 {
1183 DOMElement *elem = static_cast <DOMElement *> (node);
1184 if (elem->getParentNode() == newRoot)
1185 continue;
1186
1187 const XMLCh *xmlns = elem->getAttribute (xmlnsName);
1188 if (xmlns == NULL)
1189 continue;
1190 if (xmlns[0] == 0 ||
1191 XMLString::compareString (xmlns, xmlnsVBox) == 0)
1192 {
1193 elem->removeAttribute (xmlnsName);
1194 }
1195 }
1196 XMLString::release (&xmlnsVBox);
1197 XMLString::release (&xmlnsName);
1198 }
1199 }
1200 catch (...)
1201 {
1202 rc = VERR_FILE_IO_ERROR;
1203 }
1204
1205 if (VBOX_FAILURE (rc))
1206 {
1207 // this will transfer ownership of the string
1208 if (ppszErrorMessage)
1209 {
1210 if (xalan.getLastError())
1211 *ppszErrorMessage = RTStrDup (xalan.getLastError());
1212 else
1213 *ppszErrorMessage = errorHandler.takeErrorMessage();
1214 }
1215 }
1216
1217 XalanTransformer::terminate();
1218
1219 return rc;
1220}
1221#endif
1222
1223int CfgLoader::Create()
1224{
1225 if (!xmlInitialized)
1226 {
1227 return VERR_NOT_SUPPORTED;
1228 }
1229
1230 int rc = VINF_SUCCESS;
1231
1232 static const XMLCh gLS[] = { chLatin_L, chLatin_S, chNull };
1233 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(gLS);
1234
1235 if (impl)
1236 {
1237 // creating an empty doc is a non-standard extension to DOM specs.
1238 // we're using it since we're bound to Xerces anyway.
1239 root = impl->createDocument();
1240 }
1241 if (!root)
1242 {
1243 rc = VERR_NOT_SUPPORTED;
1244 }
1245
1246 return rc;
1247}
1248
1249int CfgLoader::FreeConfiguration (CfgLoader *pcfg)
1250{
1251 int rc = VINF_SUCCESS;
1252
1253 while (pcfg->pfirstnode)
1254 {
1255 CfgNode::ReleaseNode (pcfg->pfirstnode);
1256 }
1257
1258 delete pcfg;
1259
1260 return rc;
1261}
1262
1263int CfgLoader::CreateNode (const char *pszName, CfgNode **ppnode)
1264{
1265 return getNode (root, pszName, 0, ppnode, CfgNode::fCreateIfNotExists);
1266}
1267
1268int CfgLoader::GetNode (const char *pszName, unsigned uIndex, CfgNode **ppnode)
1269{
1270 return getNode (root, pszName, uIndex, ppnode, CfgNode::fSearch);
1271}
1272
1273int CfgLoader::getNode (DOMNode *prootnode, const char *pszName, unsigned uIndex, CfgNode **ppnode, unsigned flags)
1274{
1275 int rc = VINF_SUCCESS;
1276
1277 CfgNode *pnode = new CfgNode (this);
1278
1279 if (!pnode)
1280 {
1281 rc = VERR_NO_MEMORY;
1282 }
1283 else if (!prootnode)
1284 {
1285 rc = VERR_NOT_SUPPORTED;
1286 }
1287 else
1288 {
1289 rc = pnode->resolve (prootnode, pszName, uIndex, flags);
1290 }
1291
1292 if (VBOX_SUCCESS(rc))
1293 {
1294 pnode->next = pfirstnode;
1295 if (pfirstnode)
1296 {
1297 pfirstnode->prev = pnode;
1298 }
1299 pfirstnode = pnode;
1300
1301 *ppnode = pnode;
1302 }
1303 else
1304 {
1305 if (pnode)
1306 {
1307 delete pnode;
1308 }
1309 }
1310
1311 return rc;
1312}
1313
1314/* CfgNode implementation */
1315CfgNode::CfgNode (CfgLoader *pcfg)
1316 :
1317 pConfiguration (pcfg),
1318 next (NULL),
1319 prev (NULL)
1320{
1321}
1322
1323CfgNode::~CfgNode ()
1324{
1325}
1326
1327int CfgNode::ReleaseNode (CfgNode *pnode)
1328{
1329 int rc = VINF_SUCCESS;
1330
1331 if (pnode->next)
1332 {
1333 pnode->next->prev = pnode->prev;
1334 }
1335
1336 if (pnode->prev)
1337 {
1338 pnode->prev->next = pnode->next;
1339 }
1340 else
1341 {
1342 pnode->pConfiguration->pfirstnode = pnode->next;
1343 }
1344
1345 delete pnode;
1346
1347 return rc;
1348}
1349
1350int CfgNode::DeleteNode (CfgNode *pnode)
1351{
1352 int rc = VINF_SUCCESS;
1353
1354 DOMNode *pparent = pnode->pdomnode->getParentNode ();
1355
1356 pparent->removeChild (pnode->pdomnode);
1357
1358 pnode->pdomnode = 0;
1359
1360 ReleaseNode (pnode);
1361
1362 return rc;
1363}
1364
1365int CfgNode::resolve (DOMNode *root, const char *pszName, unsigned uIndex, unsigned flags)
1366{
1367 static const char *pcszEmptyName = "";
1368
1369 if (!root)
1370 {
1371 return VERR_PATH_NOT_FOUND;
1372 }
1373
1374 if (!pszName)
1375 {
1376 // special case for resolving any child
1377 pszName = pcszEmptyName;
1378 }
1379
1380 if ((flags & (fCreateIfNotExists | fAppend)) && !(*pszName))
1381 {
1382 return VERR_INVALID_PARAMETER;
1383 }
1384
1385 int rc = VINF_SUCCESS;
1386
1387 PRTUTF16 pwszName = NULL;
1388
1389 rc = RTStrToUtf16 (pszName, &pwszName);
1390
1391 if (VBOX_SUCCESS(rc))
1392 {
1393 XMLCh *puszComponent = pwszName;
1394
1395 bool lastComponent = false;
1396
1397 int lastindex = XMLString::indexOf (puszComponent, '/');
1398
1399 if (lastindex == -1)
1400 {
1401 lastindex = XMLString::stringLen (puszComponent);
1402 lastComponent = true;
1403 }
1404
1405 rc = VERR_PATH_NOT_FOUND;
1406
1407 for (;;)
1408 {
1409 DOMNode *child = 0, *first = 0;
1410
1411 if (!lastComponent || !(flags & fAppend))
1412 {
1413 for (child = root->getFirstChild(); child != 0; child=child->getNextSibling())
1414 {
1415 if (child->getNodeType () == DOMNode::ELEMENT_NODE)
1416 {
1417 if (!lastindex || XMLString::compareNString (child->getNodeName (), puszComponent, lastindex) == 0)
1418 {
1419 if (lastComponent)
1420 {
1421 if (uIndex == 0)
1422 {
1423 pdomnode = child;
1424 rc = VINF_SUCCESS;
1425 break;
1426 }
1427 uIndex--;
1428 continue;
1429 }
1430 else
1431 {
1432 if (!first)
1433 {
1434 first = child;
1435 }
1436 else
1437 {
1438 break;
1439 }
1440 }
1441 }
1442 }
1443 }
1444 }
1445
1446 if (first)
1447 {
1448 if (child)
1449 {
1450 // some element in the path (except the last) has
1451 // siblingswith the same name
1452 rc = VERR_INVALID_PARAMETER;
1453 break;
1454 }
1455 root = child = first;
1456 }
1457
1458 if (!child)
1459 {
1460 if (flags & (fCreateIfNotExists | fAppend))
1461 {
1462 // Extract the component name to a temporary buffer
1463 RTUTF16 uszName[256];
1464
1465 memcpy (uszName, puszComponent, lastindex * sizeof(uszName[0]));
1466 uszName[lastindex] = 0;
1467
1468 try
1469 {
1470 DOMElement *elem = pConfiguration->Document()->createElement(uszName);
1471 root = root->appendChild(elem);
1472 }
1473
1474 catch (...)
1475 {
1476 Log(( "Error creating element [%ls]\n", uszName ));
1477 rc = VERR_CFG_NO_VALUE;
1478 break;
1479 }
1480
1481 if (lastComponent)
1482 {
1483 pdomnode = root;
1484 rc = VINF_SUCCESS;
1485 break;
1486 }
1487 }
1488 else
1489 {
1490 break;
1491 }
1492 }
1493
1494 puszComponent += lastindex;
1495 if (*puszComponent)
1496 {
1497 puszComponent++;
1498 }
1499
1500 if (!*puszComponent)
1501 {
1502 break;
1503 }
1504
1505 lastindex = XMLString::indexOf (puszComponent, '/');
1506
1507 if (lastindex == -1)
1508 {
1509 lastindex = XMLString::stringLen (puszComponent);
1510 lastComponent = true;
1511 }
1512 }
1513
1514 RTUtf16Free (pwszName);
1515 }
1516
1517 return rc;
1518}
1519
1520int CfgNode::GetChild (const char *pszName, unsigned uIndex, CfgNode **ppnode)
1521{
1522 return pConfiguration->getNode (pdomnode, pszName, uIndex, ppnode, fSearch);
1523}
1524
1525int CfgNode::CreateChildNode (const char *pszName, CfgNode **ppnode)
1526{
1527 return pConfiguration->getNode (pdomnode, pszName, 0, ppnode, fCreateIfNotExists);
1528}
1529
1530int CfgNode::AppendChildNode (const char *pszName, CfgNode **ppnode)
1531{
1532 return pConfiguration->getNode (pdomnode, pszName, 0, ppnode, fAppend);
1533}
1534
1535int CfgNode::CountChildren (const char *pszChildName, unsigned *pCount)
1536{
1537 int rc = VINF_SUCCESS;
1538
1539 PRTUTF16 pwszChildName = NULL;
1540
1541 if (pszChildName)
1542 {
1543 rc = RTStrToUtf16 (pszChildName, &pwszChildName);
1544 }
1545
1546 if (VBOX_SUCCESS(rc))
1547 {
1548 DOMNode *child;
1549
1550 unsigned count = 0;
1551
1552 for (child = pdomnode->getFirstChild(); child != 0; child=child->getNextSibling())
1553 {
1554 if (child->getNodeType () == DOMNode::ELEMENT_NODE)
1555 {
1556 if (pwszChildName == NULL)
1557 {
1558 count++;
1559 }
1560 else if (XMLString::compareString (child->getNodeName (), pwszChildName) == 0)
1561 {
1562 count++;
1563 }
1564 }
1565 }
1566
1567 if (pwszChildName)
1568 {
1569 RTUtf16Free (pwszChildName);
1570 }
1571
1572 *pCount = count;
1573 }
1574
1575 return rc;
1576}
1577
1578DOMNode *CfgNode::findChildText (void)
1579{
1580 DOMNode *child = NULL;
1581
1582 for (child = pdomnode->getFirstChild(); child != 0; child=child->getNextSibling())
1583 {
1584 if (child->getNodeType () == DOMNode::TEXT_NODE)
1585 {
1586 break;
1587 }
1588 }
1589
1590 return child;
1591}
1592
1593int CfgNode::queryValueString (const char *pszName, PRTUTF16 *ppwszValue)
1594{
1595 int rc = VINF_SUCCESS;
1596
1597 PCRTUTF16 pwszValue = NULL;
1598
1599 if (!pszName)
1600 {
1601 DOMNode *ptext = findChildText ();
1602
1603 if (ptext)
1604 {
1605 pwszValue = ptext->getNodeValue ();
1606 }
1607 }
1608 else
1609 {
1610 PRTUTF16 pwszName = NULL;
1611
1612 rc = RTStrToUtf16 (pszName, &pwszName);
1613
1614 if (VBOX_SUCCESS(rc))
1615 {
1616 DOMAttr *attr = (static_cast<DOMElement *>(pdomnode))->getAttributeNode (pwszName);
1617 if (attr)
1618 {
1619 pwszValue = attr->getValue ();
1620 }
1621
1622 RTUtf16Free (pwszName);
1623 }
1624 }
1625
1626 if (!pwszValue)
1627 {
1628 *ppwszValue = NULL;
1629 rc = VERR_CFG_NO_VALUE;
1630 }
1631 else
1632 {
1633 *ppwszValue = (PRTUTF16)pwszValue;
1634 }
1635
1636 return rc;
1637}
1638
1639int CfgNode::setValueString (const char *pszName, PRTUTF16 pwszValue)
1640{
1641 int rc = VINF_SUCCESS;
1642
1643 if (!pszName)
1644 {
1645 DOMText *val = NULL;
1646
1647 try
1648 {
1649 val = pConfiguration->Document ()->createTextNode(pwszValue);
1650 }
1651
1652 catch (...)
1653 {
1654 rc = VERR_CFG_NO_VALUE;
1655 }
1656
1657 if (VBOX_SUCCESS(rc) && val)
1658 {
1659 try
1660 {
1661 DOMNode *poldtext = findChildText ();
1662
1663 if (poldtext)
1664 {
1665 pdomnode->replaceChild (val, poldtext);
1666 poldtext->release();
1667 }
1668 else
1669 {
1670 pdomnode->appendChild (val);
1671 }
1672 }
1673
1674 catch (...)
1675 {
1676 rc = VERR_CFG_NO_VALUE;
1677 val->release();
1678 }
1679 }
1680 }
1681 else
1682 {
1683 PRTUTF16 pwszName = NULL;
1684
1685 rc = RTStrToUtf16 (pszName, &pwszName);
1686
1687 if (VBOX_SUCCESS(rc))
1688 {
1689 try
1690 {
1691 static_cast<DOMElement *>(pdomnode)->setAttribute (pwszName, pwszValue);
1692 }
1693
1694 catch (...)
1695 {
1696 rc = VERR_CFG_NO_VALUE;
1697 }
1698 }
1699 }
1700
1701 return rc;
1702}
1703
1704int CfgNode::QueryUInt32 (const char *pszName, uint32_t *pulValue)
1705{
1706 int rc = VINF_SUCCESS;
1707
1708 PRTUTF16 pwszValue = NULL;
1709
1710 rc = queryValueString (pszName, &pwszValue);
1711
1712 if (VBOX_SUCCESS(rc))
1713 {
1714 uint32_t value = 0;
1715
1716 rc = cfgldrhlp_strtouint32 (pwszValue, &value);
1717
1718 if (VBOX_SUCCESS(rc))
1719 {
1720 *pulValue = value;
1721 }
1722 }
1723
1724 return rc;
1725}
1726int CfgNode::SetUInt32 (const char *pszName, uint32_t ulValue)
1727{
1728 int rc = VINF_SUCCESS;
1729
1730 char szValue[64];
1731
1732 rc = cfgldrhlp_uint32tostr (ulValue, szValue);
1733
1734 if (VBOX_SUCCESS (rc))
1735 {
1736 PRTUTF16 pwszValue = NULL;
1737
1738 rc = RTStrToUtf16 (szValue, &pwszValue);
1739
1740 if (VBOX_SUCCESS (rc))
1741 {
1742 rc = setValueString (pszName, pwszValue);
1743
1744 RTUtf16Free (pwszValue);
1745 }
1746 }
1747
1748 return rc;
1749}
1750int CfgNode::QueryUInt64 (const char *pszName, uint64_t *pullValue)
1751{
1752 int rc = VINF_SUCCESS;
1753
1754 PRTUTF16 pwszValue = NULL;
1755
1756 rc = queryValueString (pszName, &pwszValue);
1757
1758 if (VBOX_SUCCESS(rc))
1759 {
1760 uint64_t value = 0;
1761
1762 rc = cfgldrhlp_strtouint64 (pwszValue, &value);
1763
1764 if (VBOX_SUCCESS(rc))
1765 {
1766 *pullValue = value;
1767 }
1768 }
1769
1770 return rc;
1771}
1772int CfgNode::SetUInt64 (const char *pszName, uint64_t ullValue)
1773{
1774 int rc = VINF_SUCCESS;
1775
1776 char szValue[64];
1777
1778 rc = cfgldrhlp_uint64tostr (ullValue, szValue);
1779
1780 if (VBOX_SUCCESS (rc))
1781 {
1782 PRTUTF16 pwszValue = NULL;
1783
1784 rc = RTStrToUtf16 (szValue, &pwszValue);
1785
1786 if (VBOX_SUCCESS (rc))
1787 {
1788 rc = setValueString (pszName, pwszValue);
1789
1790 RTUtf16Free (pwszValue);
1791 }
1792 }
1793
1794 return rc;
1795}
1796
1797int CfgNode::QueryInt32 (const char *pszName, int32_t *pint32Value)
1798{
1799 PRTUTF16 pwszValue = NULL;
1800
1801 int rc = queryValueString (pszName, &pwszValue);
1802
1803 if (VBOX_SUCCESS(rc))
1804 {
1805 rc = cfgldrhlp_ustr_to_integer<int32_t, uint32_t> (pwszValue, pint32Value);
1806 }
1807
1808 return rc;
1809}
1810
1811int CfgNode::SetInt32 (const char *pszName, int32_t int32Value)
1812{
1813 PRTUTF16 pwszValue = NULL;
1814
1815 int rc = cfgldrhlp_integer_to_ustr<int32_t, uint32_t> (int32Value, &pwszValue);
1816
1817 if (VBOX_SUCCESS(rc))
1818 {
1819 rc = setValueString (pszName, pwszValue);
1820
1821 cfgldrhlp_release_ustr (pwszValue);
1822 }
1823
1824 return rc;
1825}
1826
1827int CfgNode::QueryInt64 (const char *pszName, int64_t *pint64Value)
1828{
1829 PRTUTF16 pwszValue = NULL;
1830
1831 int rc = queryValueString (pszName, &pwszValue);
1832
1833 if (VBOX_SUCCESS(rc))
1834 {
1835 rc = cfgldrhlp_ustr_to_integer<int64_t, uint64_t> (pwszValue, pint64Value);
1836 }
1837
1838 return rc;
1839}
1840
1841int CfgNode::SetInt64 (const char *pszName, int64_t int64Value)
1842{
1843 PRTUTF16 pwszValue = NULL;
1844
1845 int rc = cfgldrhlp_integer_to_ustr<int64_t, uint64_t> (int64Value, &pwszValue);
1846
1847 if (VBOX_SUCCESS(rc))
1848 {
1849 rc = setValueString (pszName, pwszValue);
1850
1851 cfgldrhlp_release_ustr (pwszValue);
1852 }
1853
1854 return rc;
1855}
1856
1857int CfgNode::QueryUInt16 (const char *pszName, uint16_t *pu16Value)
1858{
1859 PRTUTF16 pwszValue = NULL;
1860
1861 int rc = queryValueString (pszName, &pwszValue);
1862
1863 if (VBOX_SUCCESS(rc))
1864 {
1865 rc = cfgldrhlp_ustr_to_uinteger<uint16_t> (pwszValue, pu16Value);
1866 }
1867
1868 return rc;
1869}
1870
1871int CfgNode::SetUInt16 (const char *pszName, uint16_t u16Value)
1872{
1873 PRTUTF16 pwszValue = NULL;
1874
1875 int rc = cfgldrhlp_uinteger_to_ustr<uint16_t> (u16Value, &pwszValue);
1876
1877 if (VBOX_SUCCESS(rc))
1878 {
1879 rc = setValueString (pszName, pwszValue);
1880
1881 cfgldrhlp_release_ustr (pwszValue);
1882 }
1883
1884 return rc;
1885}
1886
1887int CfgNode::QueryBin (const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue)
1888{
1889 int rc = VINF_SUCCESS;
1890
1891 PRTUTF16 pwszValue = NULL;
1892
1893 rc = queryValueString (pszName, &pwszValue);
1894
1895 if (VBOX_SUCCESS(rc))
1896 {
1897 if ( (XMLString::stringLen (pwszValue) / 2) > cbValue)
1898 {
1899 rc = VERR_BUFFER_OVERFLOW;
1900 }
1901 else if (!pvValue)
1902 {
1903 rc = VERR_INVALID_POINTER;
1904 }
1905 else
1906 {
1907 rc = cfgldrhlp_strtobin (pwszValue, pvValue, cbValue, pcbValue);
1908 }
1909 }
1910
1911 return rc;
1912}
1913int CfgNode::SetBin (const char *pszName, const void *pvValue, unsigned cbValue)
1914{
1915 int rc = VINF_SUCCESS;
1916
1917 char *pszValue = NULL;
1918
1919 rc = cfgldrhlp_bintostr (pvValue, cbValue, &pszValue);
1920
1921 if (VBOX_SUCCESS (rc))
1922 {
1923 PRTUTF16 pwszValue = NULL;
1924
1925 rc = RTStrToUtf16 (pszValue, &pwszValue);
1926
1927 if (VBOX_SUCCESS (rc))
1928 {
1929 rc = setValueString (pszName, pwszValue);
1930
1931 RTUtf16Free (pwszValue);
1932 }
1933
1934 cfgldrhlp_releasestr (pszValue);
1935 }
1936
1937 return rc;
1938}
1939int CfgNode::QueryString (const char *pszName, void **pValue, unsigned cbValue, unsigned *pcbValue, bool returnUtf16)
1940{
1941 int rc = VINF_SUCCESS;
1942
1943 PRTUTF16 pwszValue = NULL;
1944
1945 // start with 0 bytes return value
1946 if (pcbValue)
1947 *pcbValue = 0;
1948
1949 rc = queryValueString (pszName, &pwszValue);
1950
1951 if (VBOX_SUCCESS(rc))
1952 {
1953 if (returnUtf16)
1954 {
1955 // make a copy
1956 *pValue = (void*)SysAllocString((const OLECHAR*)pwszValue);
1957 } else
1958 {
1959 char *psz = NULL;
1960
1961 rc = RTUtf16ToUtf8 (pwszValue, &psz);
1962
1963 if (VBOX_SUCCESS(rc))
1964 {
1965 unsigned l = strlen (psz) + 1; // take trailing nul to account
1966
1967 *pcbValue = l;
1968
1969 if (l > cbValue)
1970 {
1971 rc = VERR_BUFFER_OVERFLOW;
1972 }
1973 else if (!*pValue)
1974 {
1975 rc = VERR_INVALID_POINTER;
1976 }
1977 else
1978 {
1979 memcpy (*pValue, psz, l);
1980 }
1981
1982 RTStrFree (psz);
1983 }
1984 }
1985 }
1986 return rc;
1987}
1988
1989int CfgNode::SetString (const char *pszName, const char *pszValue, unsigned cbValue, bool isUtf16)
1990{
1991 int rc = VINF_SUCCESS;
1992
1993 PRTUTF16 pwszValue = NULL;
1994
1995 if (isUtf16)
1996 pwszValue = (PRTUTF16)pszValue;
1997 else
1998 rc = RTStrToUtf16 (pszValue, &pwszValue);
1999
2000 if (VBOX_SUCCESS (rc))
2001 {
2002 rc = setValueString (pszName, pwszValue);
2003
2004 if (!isUtf16)
2005 RTUtf16Free (pwszValue);
2006 }
2007
2008 return rc;
2009}
2010
2011int CfgNode::QueryBool (const char *pszName, bool *pfValue)
2012{
2013 int rc = VINF_SUCCESS;
2014
2015 PRTUTF16 pwszValue = NULL;
2016 rc = queryValueString (pszName, &pwszValue);
2017 if (VBOX_SUCCESS (rc))
2018 {
2019 char *pszValue = NULL;
2020 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
2021 if (VBOX_SUCCESS (rc))
2022 {
2023 if ( !stricmp (pszValue, "true")
2024 || !stricmp (pszValue, "yes")
2025 || !stricmp (pszValue, "on"))
2026 *pfValue = true;
2027 else if ( !stricmp (pszValue, "false")
2028 || !stricmp (pszValue, "no")
2029 || !stricmp (pszValue, "off"))
2030 *pfValue = false;
2031 else
2032 rc = VERR_CFG_INVALID_FORMAT;
2033 RTStrFree (pszValue);
2034 }
2035 }
2036
2037 return rc;
2038}
2039
2040int CfgNode::SetBool (const char *pszName, bool fValue)
2041{
2042 return SetString (pszName, fValue ? "true" : "false", fValue ? 4 : 5, false);
2043}
2044
2045int CfgNode::DeleteAttribute (const char *pszName)
2046{
2047 int rc = VINF_SUCCESS;
2048
2049 PRTUTF16 pwszName = NULL;
2050
2051 rc = RTStrToUtf16 (pszName, &pwszName);
2052
2053 if (VBOX_SUCCESS (rc))
2054 {
2055 try
2056 {
2057 (static_cast<DOMElement *>(pdomnode))->removeAttribute (pwszName);
2058 }
2059
2060 catch (...)
2061 {
2062 rc = VERR_CFG_NO_VALUE;
2063 }
2064
2065 RTUtf16Free (pwszName);
2066 }
2067
2068 return rc;
2069}
2070
2071/* Configuration loader public entry points.
2072 */
2073
2074CFGLDRR3DECL(int) CFGLDRCreate(CFGHANDLE *phcfg)
2075{
2076 if (!phcfg)
2077 {
2078 return VERR_INVALID_POINTER;
2079 }
2080
2081 CfgLoader *pcfgldr = new CfgLoader ();
2082
2083 if (!pcfgldr)
2084 {
2085 return VERR_NO_MEMORY;
2086 }
2087
2088 int rc = pcfgldr->Create();
2089
2090 if (VBOX_SUCCESS(rc))
2091 {
2092 *phcfg = pcfgldr;
2093 }
2094 else
2095 {
2096 delete pcfgldr;
2097 }
2098
2099 return rc;
2100}
2101
2102CFGLDRR3DECL(int) CFGLDRLoad (CFGHANDLE *phcfg,
2103 const char *pszFileName, RTFILE hFileHandle,
2104 const char *pszExternalSchemaLocation, bool bDoNamespaces,
2105 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
2106 char **ppszErrorMessage)
2107{
2108 if (!phcfg || !pszFileName)
2109 return VERR_INVALID_POINTER;
2110
2111 CfgLoader *pcfgldr = new CfgLoader();
2112 if (!pcfgldr)
2113 return VERR_NO_MEMORY;
2114
2115 int rc = pcfgldr->Load (pszFileName, hFileHandle,
2116 pszExternalSchemaLocation, bDoNamespaces,
2117 pfnEntityResolver, ppszErrorMessage);
2118
2119 if (VBOX_SUCCESS(rc))
2120 *phcfg = pcfgldr;
2121 else
2122 delete pcfgldr;
2123
2124 return rc;
2125}
2126
2127CFGLDRR3DECL(int) CFGLDRFree(CFGHANDLE hcfg)
2128{
2129 if (!hcfg)
2130 {
2131 return VERR_INVALID_HANDLE;
2132 }
2133
2134 CfgLoader::FreeConfiguration (hcfg);
2135
2136 return VINF_SUCCESS;
2137}
2138
2139CFGLDRR3DECL(int) CFGLDRSave(CFGHANDLE hcfg,
2140 char **ppszErrorMessage)
2141{
2142 if (!hcfg)
2143 {
2144 return VERR_INVALID_HANDLE;
2145 }
2146 return hcfg->Save (NULL, NIL_RTFILE, ppszErrorMessage);
2147}
2148
2149CFGLDRR3DECL(int) CFGLDRSaveAs(CFGHANDLE hcfg,
2150 const char *pszFilename, RTFILE hFileHandle,
2151 char **ppszErrorMessage)
2152{
2153 if (!hcfg)
2154 {
2155 return VERR_INVALID_HANDLE;
2156 }
2157 if (!pszFilename)
2158 {
2159 return VERR_INVALID_POINTER;
2160 }
2161 return hcfg->Save (pszFilename, hFileHandle, ppszErrorMessage);
2162}
2163
2164CFGLDRR3DECL(int) CFGLDRTransform (CFGHANDLE hcfg,
2165 const char *pszTemlateLocation,
2166 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
2167 char **ppszErrorMessage)
2168{
2169#ifdef VBOX_XML_XSLT
2170 if (!hcfg)
2171 {
2172 return VERR_INVALID_HANDLE;
2173 }
2174 if (!pszTemlateLocation)
2175 {
2176 return VERR_INVALID_POINTER;
2177 }
2178 return hcfg->Transform (pszTemlateLocation, pfnEntityResolver, ppszErrorMessage);
2179#else
2180 return VERR_NOT_SUPPORTED;
2181#endif
2182}
2183
2184CFGLDRR3DECL(int) CFGLDRGetNode(CFGHANDLE hcfg, const char *pszName, unsigned uIndex, CFGNODE *phnode)
2185{
2186 if (!hcfg)
2187 {
2188 return VERR_INVALID_HANDLE;
2189 }
2190 if (!phnode)
2191 {
2192 return VERR_INVALID_POINTER;
2193 }
2194 return hcfg->GetNode (pszName, uIndex, phnode);
2195}
2196
2197CFGLDRR3DECL(int) CFGLDRGetChildNode(CFGNODE hparent, const char *pszName, unsigned uIndex, CFGNODE *phnode)
2198{
2199 if (!hparent)
2200 {
2201 return VERR_INVALID_HANDLE;
2202 }
2203 if (!phnode)
2204 {
2205 return VERR_INVALID_POINTER;
2206 }
2207 return hparent->GetChild (pszName, uIndex, phnode);
2208}
2209
2210CFGLDRR3DECL(int) CFGLDRCreateNode(CFGHANDLE hcfg, const char *pszName, CFGNODE *phnode)
2211{
2212 if (!hcfg)
2213 {
2214 return VERR_INVALID_HANDLE;
2215 }
2216 if (!phnode || !pszName)
2217 {
2218 return VERR_INVALID_POINTER;
2219 }
2220 return hcfg->CreateNode (pszName, phnode);
2221}
2222
2223CFGLDRR3DECL(int) CFGLDRCreateChildNode(CFGNODE hparent, const char *pszName, CFGNODE *phnode)
2224{
2225 if (!hparent)
2226 {
2227 return VERR_INVALID_HANDLE;
2228 }
2229 if (!phnode || !pszName)
2230 {
2231 return VERR_INVALID_POINTER;
2232 }
2233 return hparent->CreateChildNode (pszName, phnode);
2234}
2235
2236CFGLDRR3DECL(int) CFGLDRAppendChildNode(CFGNODE hparent, const char *pszName, CFGNODE *phnode)
2237{
2238 if (!hparent)
2239 {
2240 return VERR_INVALID_HANDLE;
2241 }
2242 if (!phnode || !pszName)
2243 {
2244 return VERR_INVALID_POINTER;
2245 }
2246 return hparent->AppendChildNode (pszName, phnode);
2247}
2248
2249CFGLDRR3DECL(int) CFGLDRReleaseNode(CFGNODE hnode)
2250{
2251 if (!hnode)
2252 {
2253 return VERR_INVALID_HANDLE;
2254 }
2255 return CfgNode::ReleaseNode (hnode);
2256}
2257
2258CFGLDRR3DECL(int) CFGLDRDeleteNode(CFGNODE hnode)
2259{
2260 if (!hnode)
2261 {
2262 return VERR_INVALID_HANDLE;
2263 }
2264 return CfgNode::DeleteNode (hnode);
2265}
2266
2267CFGLDRR3DECL(int) CFGLDRCountChildren(CFGNODE hnode, const char *pszChildName, unsigned *pCount)
2268{
2269 if (!hnode)
2270 {
2271 return VERR_INVALID_HANDLE;
2272 }
2273 if (!pCount)
2274 {
2275 return VERR_INVALID_POINTER;
2276 }
2277 return hnode->CountChildren (pszChildName, pCount);
2278}
2279
2280CFGLDRR3DECL(int) CFGLDRQueryUInt32(CFGNODE hnode, const char *pszName, uint32_t *pulValue)
2281{
2282 if (!hnode)
2283 {
2284 return VERR_INVALID_HANDLE;
2285 }
2286 if (!pulValue)
2287 {
2288 return VERR_INVALID_POINTER;
2289 }
2290 return hnode->QueryUInt32 (pszName, pulValue);
2291}
2292
2293CFGLDRR3DECL(int) CFGLDRSetUInt32(CFGNODE hnode, const char *pszName, uint32_t ulValue)
2294{
2295 if (!hnode)
2296 {
2297 return VERR_INVALID_HANDLE;
2298 }
2299 return hnode->SetUInt32 (pszName, ulValue);
2300}
2301
2302CFGLDRR3DECL(int) CFGLDRQueryUInt64(CFGNODE hnode, const char *pszName, uint64_t *pullValue)
2303{
2304 if (!hnode)
2305 {
2306 return VERR_INVALID_HANDLE;
2307 }
2308 if (!pullValue)
2309 {
2310 return VERR_INVALID_POINTER;
2311 }
2312 return hnode->QueryUInt64 (pszName, pullValue);
2313}
2314
2315CFGLDRR3DECL(int) CFGLDRSetUInt64(CFGNODE hnode, const char *pszName, uint64_t ullValue)
2316{
2317 if (!hnode)
2318 {
2319 return VERR_INVALID_HANDLE;
2320 }
2321 return hnode->SetUInt64 (pszName, ullValue);
2322}
2323
2324CFGLDRR3DECL(int) CFGLDRQueryInt32(CFGNODE hnode, const char *pszName, int32_t *pint32Value)
2325{
2326 if (!hnode)
2327 {
2328 return VERR_INVALID_HANDLE;
2329 }
2330 return hnode->QueryInt32 (pszName, pint32Value);
2331}
2332
2333CFGLDRR3DECL(int) CFGLDRSetInt32(CFGNODE hnode, const char *pszName, int32_t int32Value)
2334{
2335 if (!hnode)
2336 {
2337 return VERR_INVALID_HANDLE;
2338 }
2339 return hnode->SetInt32 (pszName, int32Value);
2340}
2341
2342CFGLDRR3DECL(int) CFGLDRQueryInt64(CFGNODE hnode, const char *pszName, int64_t *pint64Value)
2343{
2344 if (!hnode)
2345 {
2346 return VERR_INVALID_HANDLE;
2347 }
2348 return hnode->QueryInt64 (pszName, pint64Value);
2349}
2350
2351CFGLDRR3DECL(int) CFGLDRSetInt64(CFGNODE hnode, const char *pszName, int64_t int64Value)
2352{
2353 if (!hnode)
2354 {
2355 return VERR_INVALID_HANDLE;
2356 }
2357 return hnode->SetInt64 (pszName, int64Value);
2358}
2359
2360CFGLDRR3DECL(int) CFGLDRQueryUInt16(CFGNODE hnode, const char *pszName, uint16_t *pu16Value)
2361{
2362 if (!hnode)
2363 {
2364 return VERR_INVALID_HANDLE;
2365 }
2366 if (!pu16Value)
2367 {
2368 return VERR_INVALID_POINTER;
2369 }
2370 return hnode->QueryUInt16 (pszName, pu16Value);
2371}
2372
2373CFGLDRR3DECL(int) CFGLDRSetUInt16(CFGNODE hnode, const char *pszName, uint16_t u16Value)
2374{
2375 if (!hnode)
2376 {
2377 return VERR_INVALID_HANDLE;
2378 }
2379 return hnode->SetUInt16 (pszName, u16Value);
2380}
2381
2382CFGLDRR3DECL(int) CFGLDRQueryBin(CFGNODE hnode, const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue)
2383{
2384 if (!hnode)
2385 {
2386 return VERR_INVALID_HANDLE;
2387 }
2388 if (!pcbValue)
2389 {
2390 return VERR_INVALID_POINTER;
2391 }
2392 return hnode->QueryBin (pszName, pvValue, cbValue, pcbValue);
2393}
2394
2395CFGLDRR3DECL(int) CFGLDRSetBin(CFGNODE hnode, const char *pszName, void *pvValue, unsigned cbValue)
2396{
2397 if (!hnode)
2398 {
2399 return VERR_INVALID_HANDLE;
2400 }
2401 if (!pvValue)
2402 {
2403 return VERR_INVALID_POINTER;
2404 }
2405 return hnode->SetBin (pszName, pvValue, cbValue);
2406}
2407
2408CFGLDRR3DECL(int) CFGLDRQueryString(CFGNODE hnode, const char *pszName, char *pszValue, unsigned cbValue, unsigned *pcbValue)
2409{
2410 if (!hnode)
2411 {
2412 return VERR_INVALID_HANDLE;
2413 }
2414 if (!pcbValue)
2415 {
2416 return VERR_INVALID_POINTER;
2417 }
2418 return hnode->QueryString (pszName, (void**)&pszValue, cbValue, pcbValue, false);
2419}
2420
2421CFGLDRR3DECL(int) CFGLDRQueryBSTR(CFGNODE hnode, const char *pszName, BSTR *ppwszValue)
2422{
2423 if (!hnode)
2424 {
2425 return VERR_INVALID_HANDLE;
2426 }
2427 if (!ppwszValue)
2428 {
2429 return VERR_INVALID_POINTER;
2430 }
2431 return hnode->QueryString(pszName, (void**)ppwszValue, 0, NULL, true);
2432}
2433
2434CFGLDRR3DECL(int) CFGLDRQueryUUID(CFGNODE hnode, const char *pszName, PRTUUID pUUID)
2435{
2436 if (!hnode)
2437 {
2438 return VERR_INVALID_HANDLE;
2439 }
2440 if (!pUUID)
2441 {
2442 return VERR_INVALID_POINTER;
2443 }
2444
2445 // we need it as UTF8
2446 unsigned size;
2447 int rc;
2448 rc = CFGLDRQueryString(hnode, pszName, NULL, 0, &size);
2449 if (rc == VERR_BUFFER_OVERFLOW)
2450 {
2451 char *uuidUtf8 = new char[size];
2452 rc = CFGLDRQueryString(hnode, pszName, uuidUtf8, size, &size);
2453 if (VBOX_SUCCESS(rc))
2454 {
2455 // remove the curly brackets
2456 uuidUtf8[strlen(uuidUtf8) - 1] = '\0';
2457 rc = RTUuidFromStr(pUUID, &uuidUtf8[1]);
2458 }
2459 delete[] uuidUtf8;
2460 }
2461 return rc;
2462}
2463
2464CFGLDRR3DECL(int) CFGLDRSetUUID(CFGNODE hnode, const char *pszName, PCRTUUID pUuid)
2465{
2466 if (!hnode)
2467 {
2468 return VERR_INVALID_HANDLE;
2469 }
2470 if (!pUuid)
2471 {
2472 return VERR_INVALID_HANDLE;
2473 }
2474
2475 // UUID + curly brackets
2476 char strUuid[RTUUID_STR_LENGTH + 2 * sizeof(char)];
2477 strUuid[0] = '{';
2478 RTUuidToStr((const PRTUUID)pUuid, &strUuid[1], RTUUID_STR_LENGTH);
2479 strcat(strUuid, "}");
2480 return hnode->SetString (pszName, strUuid, strlen (strUuid), false);
2481}
2482
2483CFGLDRR3DECL(int) CFGLDRSetString(CFGNODE hnode, const char *pszName, const char *pszValue)
2484{
2485 if (!hnode)
2486 {
2487 return VERR_INVALID_HANDLE;
2488 }
2489 if (!pszValue)
2490 {
2491 return VERR_INVALID_POINTER;
2492 }
2493 return hnode->SetString (pszName, pszValue, strlen (pszValue), false);
2494}
2495
2496CFGLDRR3DECL(int) CFGLDRSetBSTR(CFGNODE hnode, const char *pszName, const BSTR bstrValue)
2497{
2498 if (!hnode)
2499 {
2500 return VERR_INVALID_HANDLE;
2501 }
2502 if (!bstrValue)
2503 {
2504 return VERR_INVALID_POINTER;
2505 }
2506 return hnode->SetString (pszName, (char*)bstrValue, RTUtf16Len((PCRTUTF16)bstrValue), true);
2507}
2508
2509CFGLDRR3DECL(int) CFGLDRQueryBool(CFGNODE hnode, const char *pszName, bool *pfValue)
2510{
2511 if (!hnode)
2512 {
2513 return VERR_INVALID_HANDLE;
2514 }
2515 if (!pfValue)
2516 {
2517 return VERR_INVALID_POINTER;
2518 }
2519
2520 return hnode->QueryBool (pszName, pfValue);
2521}
2522
2523CFGLDRR3DECL(int) CFGLDRSetBool(CFGNODE hnode, const char *pszName, bool fValue)
2524{
2525 if (!hnode)
2526 {
2527 return VERR_INVALID_HANDLE;
2528 }
2529
2530 return hnode->SetBool (pszName, fValue);
2531}
2532
2533CFGLDRR3DECL(int) CFGLDRQueryDateTime(CFGNODE hnode, const char *pszName, int64_t *pi64Value)
2534{
2535 if (!hnode)
2536 {
2537 return VERR_INVALID_HANDLE;
2538 }
2539 if (!pi64Value)
2540 {
2541 return VERR_INVALID_POINTER;
2542 }
2543
2544 // query as UTF8 string
2545 unsigned size = 0;
2546 int rc = CFGLDRQueryString(hnode, pszName, NULL, 0, &size);
2547 if (rc != VERR_BUFFER_OVERFLOW)
2548 return rc;
2549
2550 char *pszValue = new char[size];
2551 char *pszBuf = new char[size];
2552 rc = CFGLDRQueryString(hnode, pszName, pszValue, size, &size);
2553 if (VBOX_SUCCESS(rc)) do
2554 {
2555 // Parse xsd:dateTime. The format is:
2556 // '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
2557 // where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z'
2558 uint32_t yyyy = 0;
2559 uint16_t mm = 0, dd = 0, hh = 0, mi = 0, ss = 0;
2560 if (sscanf(pszValue, "%d-%hu-%huT%hu:%hu:%hu%s",
2561 &yyyy, &mm, &dd, &hh, &mi, &ss, pszBuf) != 7)
2562 {
2563 rc = VERR_PARSE_ERROR;
2564 break;
2565 }
2566
2567 // currently, we accept only the UTC timezone ('Z'),
2568 // ignoring fractional seconds, if present
2569 if (pszBuf[0] == 'Z' ||
2570 (pszBuf[0] == '.' && pszBuf[strlen(pszBuf)-1] == 'Z'))
2571 {
2572 // start with an error
2573 rc = VERR_PARSE_ERROR;
2574
2575#if 0
2576 RTTIME time = { yyyy, mm, 0, 0, dd, hh, mm, ss, 0,
2577 RTTIME_FLAGS_TYPE_UTC };
2578 if (RTTimeNormalize(&time))
2579 {
2580 RTTIMESPEC timeSpec;
2581 if (RTTimeImplode(&time, &timeSpec))
2582 {
2583 *pi64Value = RTTimeSpecGetMilli(&timeSpec);
2584 rc = VINF_SUCCESS;
2585 }
2586 }
2587#else
2588 /// @todo (dmik) until RTTimeImplode and friends are done
2589 int isdst = 0;
2590 {
2591 time_t dummyt = time(NULL);
2592 isdst = localtime(&dummyt)->tm_isdst;
2593 }
2594 tm time;
2595 time.tm_hour = hh; /* hour (0 - 23) */
2596 time.tm_isdst = isdst; /* daylight saving time enabled/disabled */
2597 time.tm_mday = dd; /* day of month (1 - 31) */
2598 time.tm_min = mi; /* minutes (0 - 59) */
2599 time.tm_mon = mm - 1; /* month (0 - 11 : 0 = January) */
2600 time.tm_sec = ss; /* seconds (0 - 59) */
2601 time.tm_wday = 0; /* Day of week (0 - 6 : 0 = Sunday) */
2602 time.tm_yday = 0; /* Day of year (0 - 365) */
2603 time.tm_year = yyyy - 1900; /* Year less 1900 */
2604 time_t t = mktime(&time);
2605 // mktime expects local time, but we supply it UTC,
2606 // do a trick to get the right time value
2607 tm *dummytm = gmtime(&t);
2608 dummytm->tm_isdst = isdst;
2609 time_t delta = t - mktime(dummytm);
2610 *pi64Value = t + delta;
2611 *pi64Value *= 1000;
2612 rc = VINF_SUCCESS;
2613#endif
2614 }
2615 else
2616 rc = VERR_PARSE_ERROR;
2617 }
2618 while (0);
2619
2620 delete[] pszBuf;
2621 delete[] pszValue;
2622
2623 return rc;
2624}
2625
2626CFGLDRR3DECL(int) CFGLDRSetDateTime(CFGNODE hnode, const char *pszName, int64_t i64Value)
2627{
2628 if (!hnode)
2629 {
2630 return VERR_INVALID_HANDLE;
2631 }
2632
2633#if 0
2634 RTTIMESPEC timeSpec;
2635 RTTimeSpecSetMilli(&timeSpec, i64Value);
2636 RTTIME time;
2637 RTTimeExplode(&time, &timeSpec);
2638#else
2639 /// @todo (dmik) until RTTimeImplode and friends are done
2640 time_t t = (time_t)(i64Value / 1000);
2641 tm *ptm = gmtime(&t);
2642 if (!ptm)
2643 return VERR_PARSE_ERROR;
2644 tm time = *ptm;
2645 time.tm_year += 1900;
2646 time.tm_mon += 1;
2647#endif
2648
2649 // Store xsd:dateTime. The format is:
2650 // '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
2651 // where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z'
2652 char aszBuf [256];
2653 RTStrPrintf(aszBuf, sizeof(aszBuf),
2654 "%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
2655#if 0
2656 time.i32Year, (uint16_t) time.u8Month, (uint16_t) time.u8MonthDay,
2657 (uint16_t) time.u8Hour, (uint16_t) time.u8Minute, (uint16_t) time.u8Second);
2658#else
2659 /// @todo (dmik) until RTTimeImplode and friends are done
2660 time.tm_year, time.tm_mon, time.tm_mday,
2661 time.tm_hour, time.tm_min, time.tm_sec);
2662#endif
2663
2664 return hnode->SetString (pszName, aszBuf, strlen (aszBuf), false);
2665}
2666
2667CFGLDRR3DECL(int) CFGLDRDeleteAttribute(CFGNODE hnode, const char *pszName)
2668{
2669 if (!hnode)
2670 {
2671 return VERR_INVALID_HANDLE;
2672 }
2673 if (!pszName)
2674 {
2675 return VERR_INVALID_POINTER;
2676 }
2677 return hnode->DeleteAttribute (pszName);
2678}
2679
2680CFGLDRR3DECL(int) CFGLDRInitialize (void)
2681{
2682 int rc = VINF_SUCCESS;
2683
2684 if (!initXML ())
2685 {
2686 rc = VERR_NOT_SUPPORTED;
2687 }
2688
2689 return rc;
2690}
2691
2692CFGLDRR3DECL(void) CFGLDRShutdown (void)
2693{
2694 /// @todo delete CfgLoaders
2695 terminateXML ();
2696}
2697
2698#ifdef STANDALONE_TEST
2699
2700#include <iprt/runtime.h>
2701
2702int main(int argc, char **argv)
2703{
2704 Log(("Configuration loader standalone test\n"));
2705
2706 CFGHANDLE hcfg = 0;
2707 CFGNODE hnode = 0;
2708 unsigned count = 0;
2709 unsigned i;
2710 char *cfgFilename = "vboxcfg.xml";
2711 char *cfgFilenameSaved = "testas.xml";
2712
2713 /*
2714 * Initialize the VBox runtime without loading
2715 * the kernel support driver
2716 */
2717 int rc = RTR3Init(false);
2718 if (VBOX_FAILURE(rc))
2719 {
2720 Log(("RTInit failed %d\n", rc));
2721 goto l_exit_0;
2722 }
2723
2724 rc = CFGLDRInitialize();
2725 if (VBOX_FAILURE(rc))
2726 {
2727 Log(("Initialize failed %d\n", rc));
2728 goto l_exit_0;
2729 }
2730
2731 rc = CFGLDRLoad(&hcfg, cfgFilename, "vboxcfg.xsd");
2732 if (VBOX_FAILURE(rc))
2733 {
2734 Log(("Load failed %d\n", rc));
2735 goto l_exit_0;
2736 }
2737
2738 printf("Configuration loaded from %s\n", cfgFilename);
2739
2740 rc = CFGLDRCreateNode(hcfg, "Configuration/Something/DeviceManager/DeviceList", &hnode);
2741 if (VBOX_FAILURE(rc))
2742 {
2743 Log(("CreateNode failed %d\n", rc));
2744 goto l_exit_1;
2745 }
2746 rc = CFGLDRSetString(hnode, "GUID", "testtestte");
2747 rc = CFGLDRReleaseNode(hnode);
2748
2749 rc = CFGLDRGetNode(hcfg, "Configuration/Managers/DeviceManager/DeviceList", 0, &hnode);
2750 if (VBOX_FAILURE(rc))
2751 {
2752 Log(("GetNode failed %d\n", rc));
2753 goto l_exit_1;
2754 }
2755
2756 rc = CFGLDRCountChildren(hnode, "Device", &count);
2757 if (VBOX_FAILURE(rc))
2758 {
2759 Log(("CountChildren failed %d\n", rc));
2760 goto l_exit_2;
2761 }
2762
2763 Log(("Number of child nodes %d\n", count));
2764
2765 for (i = 0; i < count; i++)
2766 {
2767 CFGNODE hchild = 0;
2768 unsigned cbValue;
2769 char szValue[256];
2770
2771 rc = CFGLDRGetChildNode(hnode, "Device", i, &hchild);
2772 if (VBOX_FAILURE(rc))
2773 {
2774 Log(("GetChildNode failed %d\n", rc));
2775 goto l_exit_2;
2776 }
2777
2778 unsigned dummy;
2779 rc = CFGLDRCountChildren(hchild, NULL, &dummy);
2780 Log(("Number of child nodes of Device %d\n", dummy));
2781
2782 int32_t value;
2783
2784 rc = CFGLDRQueryInt32(hchild, "int", &value);
2785 Log(("Child node %d (rc = %d): int = %d\n", i, rc, value));
2786
2787 rc = CFGLDRQueryString(hchild, NULL, szValue, sizeof (szValue), &cbValue);
2788 if (VBOX_FAILURE(rc))
2789 {
2790 Log(("QueryString failed %d\n", rc));
2791 }
2792 Log(("Child node %d: (length = %d) = '%s'\n", i, cbValue, szValue));
2793
2794 rc = CFGLDRSetString(hchild, "GUID", "testtesttest");
2795 if (VBOX_FAILURE(rc))
2796 {
2797 Log(("SetString failed %d\n", rc));
2798 }
2799
2800 rc = CFGLDRDeleteAttribute(hchild, "int");
2801 Log(("Attribute delete %d (rc = %d)\n", i, rc));
2802
2803 CFGLDRSetBin(hchild, "Bin", (void *)CFGLDRSetBin, 100);
2804 CFGLDRSetInt32(hchild, "int32", 1973);
2805// CFGLDRSetUInt64(hchild, "uint64", 0x1973);
2806
2807 CFGNODE hnew = 0;
2808 CFGLDRCreateChildNode(hchild, "testnode", &hnew);
2809 rc = CFGLDRSetString(hchild, NULL, "her");
2810 if (VBOX_FAILURE(rc))
2811 {
2812 Log(("_SetString failed %d\n", rc));
2813 }
2814 rc = CFGLDRSetString(hnew, NULL, "neher");
2815 if (VBOX_FAILURE(rc))
2816 {
2817 Log(("+SetString failed %d\n", rc));
2818 }
2819 CFGLDRReleaseNode(hchild);
2820 }
2821
2822 rc = CFGLDRSaveAs(hcfg, cfgFilenameSaved);
2823 if (VBOX_FAILURE(rc))
2824 {
2825 Log(("SaveAs failed %d\n", rc));
2826 goto l_exit_2;
2827 }
2828
2829 Log(("Configuration saved as %s\n", cfgFilenameSaved));
2830
2831l_exit_2:
2832
2833 rc = CFGLDRReleaseNode(hnode);
2834 if (VBOX_FAILURE(rc))
2835 {
2836 Log(("ReleaseNode failed %d\n", rc));
2837 }
2838
2839l_exit_1:
2840
2841 rc = CFGLDRFree(hcfg);
2842 if (VBOX_FAILURE(rc))
2843 {
2844 Log(("Load failed %d\n", rc));
2845 }
2846
2847l_exit_0:
2848
2849 CFGLDRShutdown();
2850
2851 Log(("Test completed."));
2852 return rc;
2853}
2854#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette