VirtualBox

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

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

Replace opening VirtualBox.xml with RTFILE_O_WRITE_THROUGH by a
RTFileFlush after writing the new file contents. Much faster.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 72.8 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 if (hFileHandle != NIL_RTFILE || hOriginalFileHandle != NIL_RTFILE)
1086 (void)RTFileFlush(hFileHandle != NIL_RTFILE ? hFileHandle :
1087 hOriginalFileHandle);
1088
1089 return rc;
1090}
1091
1092#ifdef VBOX_XML_XSLT
1093int CfgLoader::Transform (const char *pszTemlateLocation,
1094 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
1095 char **ppszErrorMessage)
1096{
1097 AssertReturn (strcmp (pszTemlateLocation, "SettingsConverter.xsl") == 0,
1098 VERR_NOT_SUPPORTED);
1099 AssertReturn (pfnEntityResolver == NULL,
1100 VERR_NOT_SUPPORTED);
1101
1102 int rc = VINF_SUCCESS;
1103
1104 if (ppszErrorMessage)
1105 *ppszErrorMessage = NULL;
1106
1107 XalanTransformer::initialize();
1108
1109 XalanTransformer xalan;
1110
1111 // input stream to read from SettingsConverter_xsl
1112 struct SettingsConverterStream : public XSLTInputSource
1113 {
1114 SettingsConverterStream()
1115 {
1116 XMLCh *id = XMLString::transcode ("SettingsConverter.xsl");
1117 setSystemId (id);
1118 setPublicId (id);
1119 XMLString::release (&id);
1120 }
1121
1122 BinInputStream *makeStream () const
1123 {
1124 return new BinMemInputStream (g_abSettingsConverter_xsl,
1125 g_cbSettingsConverter_xsl);
1126 }
1127 };
1128
1129 CfgLdrErrorHandler errorHandler;
1130 xalan.setProblemListener (&errorHandler);
1131
1132 try
1133 {
1134 // target is the DOM tree
1135 DOMDocument *newRoot =
1136 DOMImplementation::getImplementation()->createDocument();
1137 FormatterToXercesDOM formatter (newRoot, 0);
1138
1139 // source is the DOM tree
1140 XercesDOMSupport support;
1141 XercesParserLiaison liaison;
1142 const XercesDOMWrapperParsedSource parsedSource (
1143 Document(), liaison, support,
1144 XalanDOMString (pwszOriginalFilename));
1145
1146 // stylesheet
1147 SettingsConverterStream xsl;
1148
1149 int xrc = xalan.transform (parsedSource, xsl, formatter);
1150
1151 if (xrc)
1152 {
1153 LogFlow(("xalan.transform() = %d (%s)\n", xrc, xalan.getLastError()));
1154
1155 newRoot->release();
1156 rc = VERR_FILE_IO_ERROR;
1157 }
1158 else
1159 {
1160 // release the builder and the old document, if any
1161 if (builder)
1162 {
1163 builder->release();
1164 builder = 0;
1165 root = 0;
1166 }
1167 else if (root)
1168 {
1169 root->release();
1170 root = 0;
1171 }
1172
1173 root = newRoot;
1174
1175 // Xalan 1.9.0 (and 1.10.0) is stupid bacause disregards the
1176 // XSLT specs and ignores the exclude-result-prefixes stylesheet
1177 // attribute, flooding all the elements with stupid xmlns
1178 // specifications. Here we remove them.
1179 XMLCh *xmlnsName = XMLString::transcode ("xmlns");
1180 XMLCh *xmlnsVBox = XMLString::transcode ("http://www.innotek.de/VirtualBox-settings");
1181 DOMNodeIterator *iter =
1182 newRoot->createNodeIterator (newRoot, DOMNodeFilter::SHOW_ELEMENT,
1183 NULL, false);
1184 DOMNode *node = NULL;
1185 while ((node = iter->nextNode()) != NULL)
1186 {
1187 DOMElement *elem = static_cast <DOMElement *> (node);
1188 if (elem->getParentNode() == newRoot)
1189 continue;
1190
1191 const XMLCh *xmlns = elem->getAttribute (xmlnsName);
1192 if (xmlns == NULL)
1193 continue;
1194 if (xmlns[0] == 0 ||
1195 XMLString::compareString (xmlns, xmlnsVBox) == 0)
1196 {
1197 elem->removeAttribute (xmlnsName);
1198 }
1199 }
1200 XMLString::release (&xmlnsVBox);
1201 XMLString::release (&xmlnsName);
1202 }
1203 }
1204 catch (...)
1205 {
1206 rc = VERR_FILE_IO_ERROR;
1207 }
1208
1209 if (VBOX_FAILURE (rc))
1210 {
1211 // this will transfer ownership of the string
1212 if (ppszErrorMessage)
1213 {
1214 if (xalan.getLastError())
1215 *ppszErrorMessage = RTStrDup (xalan.getLastError());
1216 else
1217 *ppszErrorMessage = errorHandler.takeErrorMessage();
1218 }
1219 }
1220
1221 XalanTransformer::terminate();
1222
1223 return rc;
1224}
1225#endif
1226
1227int CfgLoader::Create()
1228{
1229 if (!xmlInitialized)
1230 {
1231 return VERR_NOT_SUPPORTED;
1232 }
1233
1234 int rc = VINF_SUCCESS;
1235
1236 static const XMLCh gLS[] = { chLatin_L, chLatin_S, chNull };
1237 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(gLS);
1238
1239 if (impl)
1240 {
1241 // creating an empty doc is a non-standard extension to DOM specs.
1242 // we're using it since we're bound to Xerces anyway.
1243 root = impl->createDocument();
1244 }
1245 if (!root)
1246 {
1247 rc = VERR_NOT_SUPPORTED;
1248 }
1249
1250 return rc;
1251}
1252
1253int CfgLoader::FreeConfiguration (CfgLoader *pcfg)
1254{
1255 int rc = VINF_SUCCESS;
1256
1257 while (pcfg->pfirstnode)
1258 {
1259 CfgNode::ReleaseNode (pcfg->pfirstnode);
1260 }
1261
1262 delete pcfg;
1263
1264 return rc;
1265}
1266
1267int CfgLoader::CreateNode (const char *pszName, CfgNode **ppnode)
1268{
1269 return getNode (root, pszName, 0, ppnode, CfgNode::fCreateIfNotExists);
1270}
1271
1272int CfgLoader::GetNode (const char *pszName, unsigned uIndex, CfgNode **ppnode)
1273{
1274 return getNode (root, pszName, uIndex, ppnode, CfgNode::fSearch);
1275}
1276
1277int CfgLoader::getNode (DOMNode *prootnode, const char *pszName, unsigned uIndex, CfgNode **ppnode, unsigned flags)
1278{
1279 int rc = VINF_SUCCESS;
1280
1281 CfgNode *pnode = new CfgNode (this);
1282
1283 if (!pnode)
1284 {
1285 rc = VERR_NO_MEMORY;
1286 }
1287 else if (!prootnode)
1288 {
1289 rc = VERR_NOT_SUPPORTED;
1290 }
1291 else
1292 {
1293 rc = pnode->resolve (prootnode, pszName, uIndex, flags);
1294 }
1295
1296 if (VBOX_SUCCESS(rc))
1297 {
1298 pnode->next = pfirstnode;
1299 if (pfirstnode)
1300 {
1301 pfirstnode->prev = pnode;
1302 }
1303 pfirstnode = pnode;
1304
1305 *ppnode = pnode;
1306 }
1307 else
1308 {
1309 if (pnode)
1310 {
1311 delete pnode;
1312 }
1313 }
1314
1315 return rc;
1316}
1317
1318/* CfgNode implementation */
1319CfgNode::CfgNode (CfgLoader *pcfg)
1320 :
1321 pConfiguration (pcfg),
1322 next (NULL),
1323 prev (NULL)
1324{
1325}
1326
1327CfgNode::~CfgNode ()
1328{
1329}
1330
1331int CfgNode::ReleaseNode (CfgNode *pnode)
1332{
1333 int rc = VINF_SUCCESS;
1334
1335 if (pnode->next)
1336 {
1337 pnode->next->prev = pnode->prev;
1338 }
1339
1340 if (pnode->prev)
1341 {
1342 pnode->prev->next = pnode->next;
1343 }
1344 else
1345 {
1346 pnode->pConfiguration->pfirstnode = pnode->next;
1347 }
1348
1349 delete pnode;
1350
1351 return rc;
1352}
1353
1354int CfgNode::DeleteNode (CfgNode *pnode)
1355{
1356 int rc = VINF_SUCCESS;
1357
1358 DOMNode *pparent = pnode->pdomnode->getParentNode ();
1359
1360 pparent->removeChild (pnode->pdomnode);
1361
1362 pnode->pdomnode = 0;
1363
1364 ReleaseNode (pnode);
1365
1366 return rc;
1367}
1368
1369int CfgNode::resolve (DOMNode *root, const char *pszName, unsigned uIndex, unsigned flags)
1370{
1371 static const char *pcszEmptyName = "";
1372
1373 if (!root)
1374 {
1375 return VERR_PATH_NOT_FOUND;
1376 }
1377
1378 if (!pszName)
1379 {
1380 // special case for resolving any child
1381 pszName = pcszEmptyName;
1382 }
1383
1384 if ((flags & (fCreateIfNotExists | fAppend)) && !(*pszName))
1385 {
1386 return VERR_INVALID_PARAMETER;
1387 }
1388
1389 int rc = VINF_SUCCESS;
1390
1391 PRTUTF16 pwszName = NULL;
1392
1393 rc = RTStrToUtf16 (pszName, &pwszName);
1394
1395 if (VBOX_SUCCESS(rc))
1396 {
1397 XMLCh *puszComponent = pwszName;
1398
1399 bool lastComponent = false;
1400
1401 int lastindex = XMLString::indexOf (puszComponent, '/');
1402
1403 if (lastindex == -1)
1404 {
1405 lastindex = XMLString::stringLen (puszComponent);
1406 lastComponent = true;
1407 }
1408
1409 rc = VERR_PATH_NOT_FOUND;
1410
1411 for (;;)
1412 {
1413 DOMNode *child = 0, *first = 0;
1414
1415 if (!lastComponent || !(flags & fAppend))
1416 {
1417 for (child = root->getFirstChild(); child != 0; child=child->getNextSibling())
1418 {
1419 if (child->getNodeType () == DOMNode::ELEMENT_NODE)
1420 {
1421 if (!lastindex || XMLString::compareNString (child->getNodeName (), puszComponent, lastindex) == 0)
1422 {
1423 if (lastComponent)
1424 {
1425 if (uIndex == 0)
1426 {
1427 pdomnode = child;
1428 rc = VINF_SUCCESS;
1429 break;
1430 }
1431 uIndex--;
1432 continue;
1433 }
1434 else
1435 {
1436 if (!first)
1437 {
1438 first = child;
1439 }
1440 else
1441 {
1442 break;
1443 }
1444 }
1445 }
1446 }
1447 }
1448 }
1449
1450 if (first)
1451 {
1452 if (child)
1453 {
1454 // some element in the path (except the last) has
1455 // siblingswith the same name
1456 rc = VERR_INVALID_PARAMETER;
1457 break;
1458 }
1459 root = child = first;
1460 }
1461
1462 if (!child)
1463 {
1464 if (flags & (fCreateIfNotExists | fAppend))
1465 {
1466 // Extract the component name to a temporary buffer
1467 RTUTF16 uszName[256];
1468
1469 memcpy (uszName, puszComponent, lastindex * sizeof(uszName[0]));
1470 uszName[lastindex] = 0;
1471
1472 try
1473 {
1474 DOMElement *elem = pConfiguration->Document()->createElement(uszName);
1475 root = root->appendChild(elem);
1476 }
1477
1478 catch (...)
1479 {
1480 Log(( "Error creating element [%ls]\n", uszName ));
1481 rc = VERR_CFG_NO_VALUE;
1482 break;
1483 }
1484
1485 if (lastComponent)
1486 {
1487 pdomnode = root;
1488 rc = VINF_SUCCESS;
1489 break;
1490 }
1491 }
1492 else
1493 {
1494 break;
1495 }
1496 }
1497
1498 puszComponent += lastindex;
1499 if (*puszComponent)
1500 {
1501 puszComponent++;
1502 }
1503
1504 if (!*puszComponent)
1505 {
1506 break;
1507 }
1508
1509 lastindex = XMLString::indexOf (puszComponent, '/');
1510
1511 if (lastindex == -1)
1512 {
1513 lastindex = XMLString::stringLen (puszComponent);
1514 lastComponent = true;
1515 }
1516 }
1517
1518 RTUtf16Free (pwszName);
1519 }
1520
1521 return rc;
1522}
1523
1524int CfgNode::GetChild (const char *pszName, unsigned uIndex, CfgNode **ppnode)
1525{
1526 return pConfiguration->getNode (pdomnode, pszName, uIndex, ppnode, fSearch);
1527}
1528
1529int CfgNode::CreateChildNode (const char *pszName, CfgNode **ppnode)
1530{
1531 return pConfiguration->getNode (pdomnode, pszName, 0, ppnode, fCreateIfNotExists);
1532}
1533
1534int CfgNode::AppendChildNode (const char *pszName, CfgNode **ppnode)
1535{
1536 return pConfiguration->getNode (pdomnode, pszName, 0, ppnode, fAppend);
1537}
1538
1539int CfgNode::CountChildren (const char *pszChildName, unsigned *pCount)
1540{
1541 int rc = VINF_SUCCESS;
1542
1543 PRTUTF16 pwszChildName = NULL;
1544
1545 if (pszChildName)
1546 {
1547 rc = RTStrToUtf16 (pszChildName, &pwszChildName);
1548 }
1549
1550 if (VBOX_SUCCESS(rc))
1551 {
1552 DOMNode *child;
1553
1554 unsigned count = 0;
1555
1556 for (child = pdomnode->getFirstChild(); child != 0; child=child->getNextSibling())
1557 {
1558 if (child->getNodeType () == DOMNode::ELEMENT_NODE)
1559 {
1560 if (pwszChildName == NULL)
1561 {
1562 count++;
1563 }
1564 else if (XMLString::compareString (child->getNodeName (), pwszChildName) == 0)
1565 {
1566 count++;
1567 }
1568 }
1569 }
1570
1571 if (pwszChildName)
1572 {
1573 RTUtf16Free (pwszChildName);
1574 }
1575
1576 *pCount = count;
1577 }
1578
1579 return rc;
1580}
1581
1582DOMNode *CfgNode::findChildText (void)
1583{
1584 DOMNode *child = NULL;
1585
1586 for (child = pdomnode->getFirstChild(); child != 0; child=child->getNextSibling())
1587 {
1588 if (child->getNodeType () == DOMNode::TEXT_NODE)
1589 {
1590 break;
1591 }
1592 }
1593
1594 return child;
1595}
1596
1597int CfgNode::queryValueString (const char *pszName, PRTUTF16 *ppwszValue)
1598{
1599 int rc = VINF_SUCCESS;
1600
1601 PCRTUTF16 pwszValue = NULL;
1602
1603 if (!pszName)
1604 {
1605 DOMNode *ptext = findChildText ();
1606
1607 if (ptext)
1608 {
1609 pwszValue = ptext->getNodeValue ();
1610 }
1611 }
1612 else
1613 {
1614 PRTUTF16 pwszName = NULL;
1615
1616 rc = RTStrToUtf16 (pszName, &pwszName);
1617
1618 if (VBOX_SUCCESS(rc))
1619 {
1620 DOMAttr *attr = (static_cast<DOMElement *>(pdomnode))->getAttributeNode (pwszName);
1621 if (attr)
1622 {
1623 pwszValue = attr->getValue ();
1624 }
1625
1626 RTUtf16Free (pwszName);
1627 }
1628 }
1629
1630 if (!pwszValue)
1631 {
1632 *ppwszValue = NULL;
1633 rc = VERR_CFG_NO_VALUE;
1634 }
1635 else
1636 {
1637 *ppwszValue = (PRTUTF16)pwszValue;
1638 }
1639
1640 return rc;
1641}
1642
1643int CfgNode::setValueString (const char *pszName, PRTUTF16 pwszValue)
1644{
1645 int rc = VINF_SUCCESS;
1646
1647 if (!pszName)
1648 {
1649 DOMText *val = NULL;
1650
1651 try
1652 {
1653 val = pConfiguration->Document ()->createTextNode(pwszValue);
1654 }
1655
1656 catch (...)
1657 {
1658 rc = VERR_CFG_NO_VALUE;
1659 }
1660
1661 if (VBOX_SUCCESS(rc) && val)
1662 {
1663 try
1664 {
1665 DOMNode *poldtext = findChildText ();
1666
1667 if (poldtext)
1668 {
1669 pdomnode->replaceChild (val, poldtext);
1670 poldtext->release();
1671 }
1672 else
1673 {
1674 pdomnode->appendChild (val);
1675 }
1676 }
1677
1678 catch (...)
1679 {
1680 rc = VERR_CFG_NO_VALUE;
1681 val->release();
1682 }
1683 }
1684 }
1685 else
1686 {
1687 PRTUTF16 pwszName = NULL;
1688
1689 rc = RTStrToUtf16 (pszName, &pwszName);
1690
1691 if (VBOX_SUCCESS(rc))
1692 {
1693 try
1694 {
1695 static_cast<DOMElement *>(pdomnode)->setAttribute (pwszName, pwszValue);
1696 }
1697
1698 catch (...)
1699 {
1700 rc = VERR_CFG_NO_VALUE;
1701 }
1702 }
1703 }
1704
1705 return rc;
1706}
1707
1708int CfgNode::QueryUInt32 (const char *pszName, uint32_t *pulValue)
1709{
1710 int rc = VINF_SUCCESS;
1711
1712 PRTUTF16 pwszValue = NULL;
1713
1714 rc = queryValueString (pszName, &pwszValue);
1715
1716 if (VBOX_SUCCESS(rc))
1717 {
1718 uint32_t value = 0;
1719
1720 rc = cfgldrhlp_strtouint32 (pwszValue, &value);
1721
1722 if (VBOX_SUCCESS(rc))
1723 {
1724 *pulValue = value;
1725 }
1726 }
1727
1728 return rc;
1729}
1730int CfgNode::SetUInt32 (const char *pszName, uint32_t ulValue)
1731{
1732 int rc = VINF_SUCCESS;
1733
1734 char szValue[64];
1735
1736 rc = cfgldrhlp_uint32tostr (ulValue, szValue);
1737
1738 if (VBOX_SUCCESS (rc))
1739 {
1740 PRTUTF16 pwszValue = NULL;
1741
1742 rc = RTStrToUtf16 (szValue, &pwszValue);
1743
1744 if (VBOX_SUCCESS (rc))
1745 {
1746 rc = setValueString (pszName, pwszValue);
1747
1748 RTUtf16Free (pwszValue);
1749 }
1750 }
1751
1752 return rc;
1753}
1754int CfgNode::QueryUInt64 (const char *pszName, uint64_t *pullValue)
1755{
1756 int rc = VINF_SUCCESS;
1757
1758 PRTUTF16 pwszValue = NULL;
1759
1760 rc = queryValueString (pszName, &pwszValue);
1761
1762 if (VBOX_SUCCESS(rc))
1763 {
1764 uint64_t value = 0;
1765
1766 rc = cfgldrhlp_strtouint64 (pwszValue, &value);
1767
1768 if (VBOX_SUCCESS(rc))
1769 {
1770 *pullValue = value;
1771 }
1772 }
1773
1774 return rc;
1775}
1776int CfgNode::SetUInt64 (const char *pszName, uint64_t ullValue)
1777{
1778 int rc = VINF_SUCCESS;
1779
1780 char szValue[64];
1781
1782 rc = cfgldrhlp_uint64tostr (ullValue, szValue);
1783
1784 if (VBOX_SUCCESS (rc))
1785 {
1786 PRTUTF16 pwszValue = NULL;
1787
1788 rc = RTStrToUtf16 (szValue, &pwszValue);
1789
1790 if (VBOX_SUCCESS (rc))
1791 {
1792 rc = setValueString (pszName, pwszValue);
1793
1794 RTUtf16Free (pwszValue);
1795 }
1796 }
1797
1798 return rc;
1799}
1800
1801int CfgNode::QueryInt32 (const char *pszName, int32_t *pint32Value)
1802{
1803 PRTUTF16 pwszValue = NULL;
1804
1805 int rc = queryValueString (pszName, &pwszValue);
1806
1807 if (VBOX_SUCCESS(rc))
1808 {
1809 rc = cfgldrhlp_ustr_to_integer<int32_t, uint32_t> (pwszValue, pint32Value);
1810 }
1811
1812 return rc;
1813}
1814
1815int CfgNode::SetInt32 (const char *pszName, int32_t int32Value)
1816{
1817 PRTUTF16 pwszValue = NULL;
1818
1819 int rc = cfgldrhlp_integer_to_ustr<int32_t, uint32_t> (int32Value, &pwszValue);
1820
1821 if (VBOX_SUCCESS(rc))
1822 {
1823 rc = setValueString (pszName, pwszValue);
1824
1825 cfgldrhlp_release_ustr (pwszValue);
1826 }
1827
1828 return rc;
1829}
1830
1831int CfgNode::QueryInt64 (const char *pszName, int64_t *pint64Value)
1832{
1833 PRTUTF16 pwszValue = NULL;
1834
1835 int rc = queryValueString (pszName, &pwszValue);
1836
1837 if (VBOX_SUCCESS(rc))
1838 {
1839 rc = cfgldrhlp_ustr_to_integer<int64_t, uint64_t> (pwszValue, pint64Value);
1840 }
1841
1842 return rc;
1843}
1844
1845int CfgNode::SetInt64 (const char *pszName, int64_t int64Value)
1846{
1847 PRTUTF16 pwszValue = NULL;
1848
1849 int rc = cfgldrhlp_integer_to_ustr<int64_t, uint64_t> (int64Value, &pwszValue);
1850
1851 if (VBOX_SUCCESS(rc))
1852 {
1853 rc = setValueString (pszName, pwszValue);
1854
1855 cfgldrhlp_release_ustr (pwszValue);
1856 }
1857
1858 return rc;
1859}
1860
1861int CfgNode::QueryUInt16 (const char *pszName, uint16_t *pu16Value)
1862{
1863 PRTUTF16 pwszValue = NULL;
1864
1865 int rc = queryValueString (pszName, &pwszValue);
1866
1867 if (VBOX_SUCCESS(rc))
1868 {
1869 rc = cfgldrhlp_ustr_to_uinteger<uint16_t> (pwszValue, pu16Value);
1870 }
1871
1872 return rc;
1873}
1874
1875int CfgNode::SetUInt16 (const char *pszName, uint16_t u16Value)
1876{
1877 PRTUTF16 pwszValue = NULL;
1878
1879 int rc = cfgldrhlp_uinteger_to_ustr<uint16_t> (u16Value, &pwszValue);
1880
1881 if (VBOX_SUCCESS(rc))
1882 {
1883 rc = setValueString (pszName, pwszValue);
1884
1885 cfgldrhlp_release_ustr (pwszValue);
1886 }
1887
1888 return rc;
1889}
1890
1891int CfgNode::QueryBin (const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue)
1892{
1893 int rc = VINF_SUCCESS;
1894
1895 PRTUTF16 pwszValue = NULL;
1896
1897 rc = queryValueString (pszName, &pwszValue);
1898
1899 if (VBOX_SUCCESS(rc))
1900 {
1901 if ( (XMLString::stringLen (pwszValue) / 2) > cbValue)
1902 {
1903 rc = VERR_BUFFER_OVERFLOW;
1904 }
1905 else if (!pvValue)
1906 {
1907 rc = VERR_INVALID_POINTER;
1908 }
1909 else
1910 {
1911 rc = cfgldrhlp_strtobin (pwszValue, pvValue, cbValue, pcbValue);
1912 }
1913 }
1914
1915 return rc;
1916}
1917int CfgNode::SetBin (const char *pszName, const void *pvValue, unsigned cbValue)
1918{
1919 int rc = VINF_SUCCESS;
1920
1921 char *pszValue = NULL;
1922
1923 rc = cfgldrhlp_bintostr (pvValue, cbValue, &pszValue);
1924
1925 if (VBOX_SUCCESS (rc))
1926 {
1927 PRTUTF16 pwszValue = NULL;
1928
1929 rc = RTStrToUtf16 (pszValue, &pwszValue);
1930
1931 if (VBOX_SUCCESS (rc))
1932 {
1933 rc = setValueString (pszName, pwszValue);
1934
1935 RTUtf16Free (pwszValue);
1936 }
1937
1938 cfgldrhlp_releasestr (pszValue);
1939 }
1940
1941 return rc;
1942}
1943int CfgNode::QueryString (const char *pszName, void **pValue, unsigned cbValue, unsigned *pcbValue, bool returnUtf16)
1944{
1945 int rc = VINF_SUCCESS;
1946
1947 PRTUTF16 pwszValue = NULL;
1948
1949 // start with 0 bytes return value
1950 if (pcbValue)
1951 *pcbValue = 0;
1952
1953 rc = queryValueString (pszName, &pwszValue);
1954
1955 if (VBOX_SUCCESS(rc))
1956 {
1957 if (returnUtf16)
1958 {
1959 // make a copy
1960 *pValue = (void*)SysAllocString((const OLECHAR*)pwszValue);
1961 } else
1962 {
1963 char *psz = NULL;
1964
1965 rc = RTUtf16ToUtf8 (pwszValue, &psz);
1966
1967 if (VBOX_SUCCESS(rc))
1968 {
1969 unsigned l = strlen (psz) + 1; // take trailing nul to account
1970
1971 *pcbValue = l;
1972
1973 if (l > cbValue)
1974 {
1975 rc = VERR_BUFFER_OVERFLOW;
1976 }
1977 else if (!*pValue)
1978 {
1979 rc = VERR_INVALID_POINTER;
1980 }
1981 else
1982 {
1983 memcpy (*pValue, psz, l);
1984 }
1985
1986 RTStrFree (psz);
1987 }
1988 }
1989 }
1990 return rc;
1991}
1992
1993int CfgNode::SetString (const char *pszName, const char *pszValue, unsigned cbValue, bool isUtf16)
1994{
1995 int rc = VINF_SUCCESS;
1996
1997 PRTUTF16 pwszValue = NULL;
1998
1999 if (isUtf16)
2000 pwszValue = (PRTUTF16)pszValue;
2001 else
2002 rc = RTStrToUtf16 (pszValue, &pwszValue);
2003
2004 if (VBOX_SUCCESS (rc))
2005 {
2006 rc = setValueString (pszName, pwszValue);
2007
2008 if (!isUtf16)
2009 RTUtf16Free (pwszValue);
2010 }
2011
2012 return rc;
2013}
2014
2015int CfgNode::QueryBool (const char *pszName, bool *pfValue)
2016{
2017 int rc = VINF_SUCCESS;
2018
2019 PRTUTF16 pwszValue = NULL;
2020 rc = queryValueString (pszName, &pwszValue);
2021 if (VBOX_SUCCESS (rc))
2022 {
2023 char *pszValue = NULL;
2024 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
2025 if (VBOX_SUCCESS (rc))
2026 {
2027 if ( !stricmp (pszValue, "true")
2028 || !stricmp (pszValue, "yes")
2029 || !stricmp (pszValue, "on"))
2030 *pfValue = true;
2031 else if ( !stricmp (pszValue, "false")
2032 || !stricmp (pszValue, "no")
2033 || !stricmp (pszValue, "off"))
2034 *pfValue = false;
2035 else
2036 rc = VERR_CFG_INVALID_FORMAT;
2037 RTStrFree (pszValue);
2038 }
2039 }
2040
2041 return rc;
2042}
2043
2044int CfgNode::SetBool (const char *pszName, bool fValue)
2045{
2046 return SetString (pszName, fValue ? "true" : "false", fValue ? 4 : 5, false);
2047}
2048
2049int CfgNode::DeleteAttribute (const char *pszName)
2050{
2051 int rc = VINF_SUCCESS;
2052
2053 PRTUTF16 pwszName = NULL;
2054
2055 rc = RTStrToUtf16 (pszName, &pwszName);
2056
2057 if (VBOX_SUCCESS (rc))
2058 {
2059 try
2060 {
2061 (static_cast<DOMElement *>(pdomnode))->removeAttribute (pwszName);
2062 }
2063
2064 catch (...)
2065 {
2066 rc = VERR_CFG_NO_VALUE;
2067 }
2068
2069 RTUtf16Free (pwszName);
2070 }
2071
2072 return rc;
2073}
2074
2075/* Configuration loader public entry points.
2076 */
2077
2078CFGLDRR3DECL(int) CFGLDRCreate(CFGHANDLE *phcfg)
2079{
2080 if (!phcfg)
2081 {
2082 return VERR_INVALID_POINTER;
2083 }
2084
2085 CfgLoader *pcfgldr = new CfgLoader ();
2086
2087 if (!pcfgldr)
2088 {
2089 return VERR_NO_MEMORY;
2090 }
2091
2092 int rc = pcfgldr->Create();
2093
2094 if (VBOX_SUCCESS(rc))
2095 {
2096 *phcfg = pcfgldr;
2097 }
2098 else
2099 {
2100 delete pcfgldr;
2101 }
2102
2103 return rc;
2104}
2105
2106CFGLDRR3DECL(int) CFGLDRLoad (CFGHANDLE *phcfg,
2107 const char *pszFileName, RTFILE hFileHandle,
2108 const char *pszExternalSchemaLocation, bool bDoNamespaces,
2109 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
2110 char **ppszErrorMessage)
2111{
2112 if (!phcfg || !pszFileName)
2113 return VERR_INVALID_POINTER;
2114
2115 CfgLoader *pcfgldr = new CfgLoader();
2116 if (!pcfgldr)
2117 return VERR_NO_MEMORY;
2118
2119 int rc = pcfgldr->Load (pszFileName, hFileHandle,
2120 pszExternalSchemaLocation, bDoNamespaces,
2121 pfnEntityResolver, ppszErrorMessage);
2122
2123 if (VBOX_SUCCESS(rc))
2124 *phcfg = pcfgldr;
2125 else
2126 delete pcfgldr;
2127
2128 return rc;
2129}
2130
2131CFGLDRR3DECL(int) CFGLDRFree(CFGHANDLE hcfg)
2132{
2133 if (!hcfg)
2134 {
2135 return VERR_INVALID_HANDLE;
2136 }
2137
2138 CfgLoader::FreeConfiguration (hcfg);
2139
2140 return VINF_SUCCESS;
2141}
2142
2143CFGLDRR3DECL(int) CFGLDRSave(CFGHANDLE hcfg,
2144 char **ppszErrorMessage)
2145{
2146 if (!hcfg)
2147 {
2148 return VERR_INVALID_HANDLE;
2149 }
2150 return hcfg->Save (NULL, NIL_RTFILE, ppszErrorMessage);
2151}
2152
2153CFGLDRR3DECL(int) CFGLDRSaveAs(CFGHANDLE hcfg,
2154 const char *pszFilename, RTFILE hFileHandle,
2155 char **ppszErrorMessage)
2156{
2157 if (!hcfg)
2158 {
2159 return VERR_INVALID_HANDLE;
2160 }
2161 if (!pszFilename)
2162 {
2163 return VERR_INVALID_POINTER;
2164 }
2165 return hcfg->Save (pszFilename, hFileHandle, ppszErrorMessage);
2166}
2167
2168CFGLDRR3DECL(int) CFGLDRTransform (CFGHANDLE hcfg,
2169 const char *pszTemlateLocation,
2170 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
2171 char **ppszErrorMessage)
2172{
2173#ifdef VBOX_XML_XSLT
2174 if (!hcfg)
2175 {
2176 return VERR_INVALID_HANDLE;
2177 }
2178 if (!pszTemlateLocation)
2179 {
2180 return VERR_INVALID_POINTER;
2181 }
2182 return hcfg->Transform (pszTemlateLocation, pfnEntityResolver, ppszErrorMessage);
2183#else
2184 return VERR_NOT_SUPPORTED;
2185#endif
2186}
2187
2188CFGLDRR3DECL(int) CFGLDRGetNode(CFGHANDLE hcfg, const char *pszName, unsigned uIndex, CFGNODE *phnode)
2189{
2190 if (!hcfg)
2191 {
2192 return VERR_INVALID_HANDLE;
2193 }
2194 if (!phnode)
2195 {
2196 return VERR_INVALID_POINTER;
2197 }
2198 return hcfg->GetNode (pszName, uIndex, phnode);
2199}
2200
2201CFGLDRR3DECL(int) CFGLDRGetChildNode(CFGNODE hparent, const char *pszName, unsigned uIndex, CFGNODE *phnode)
2202{
2203 if (!hparent)
2204 {
2205 return VERR_INVALID_HANDLE;
2206 }
2207 if (!phnode)
2208 {
2209 return VERR_INVALID_POINTER;
2210 }
2211 return hparent->GetChild (pszName, uIndex, phnode);
2212}
2213
2214CFGLDRR3DECL(int) CFGLDRCreateNode(CFGHANDLE hcfg, const char *pszName, CFGNODE *phnode)
2215{
2216 if (!hcfg)
2217 {
2218 return VERR_INVALID_HANDLE;
2219 }
2220 if (!phnode || !pszName)
2221 {
2222 return VERR_INVALID_POINTER;
2223 }
2224 return hcfg->CreateNode (pszName, phnode);
2225}
2226
2227CFGLDRR3DECL(int) CFGLDRCreateChildNode(CFGNODE hparent, const char *pszName, CFGNODE *phnode)
2228{
2229 if (!hparent)
2230 {
2231 return VERR_INVALID_HANDLE;
2232 }
2233 if (!phnode || !pszName)
2234 {
2235 return VERR_INVALID_POINTER;
2236 }
2237 return hparent->CreateChildNode (pszName, phnode);
2238}
2239
2240CFGLDRR3DECL(int) CFGLDRAppendChildNode(CFGNODE hparent, const char *pszName, CFGNODE *phnode)
2241{
2242 if (!hparent)
2243 {
2244 return VERR_INVALID_HANDLE;
2245 }
2246 if (!phnode || !pszName)
2247 {
2248 return VERR_INVALID_POINTER;
2249 }
2250 return hparent->AppendChildNode (pszName, phnode);
2251}
2252
2253CFGLDRR3DECL(int) CFGLDRReleaseNode(CFGNODE hnode)
2254{
2255 if (!hnode)
2256 {
2257 return VERR_INVALID_HANDLE;
2258 }
2259 return CfgNode::ReleaseNode (hnode);
2260}
2261
2262CFGLDRR3DECL(int) CFGLDRDeleteNode(CFGNODE hnode)
2263{
2264 if (!hnode)
2265 {
2266 return VERR_INVALID_HANDLE;
2267 }
2268 return CfgNode::DeleteNode (hnode);
2269}
2270
2271CFGLDRR3DECL(int) CFGLDRCountChildren(CFGNODE hnode, const char *pszChildName, unsigned *pCount)
2272{
2273 if (!hnode)
2274 {
2275 return VERR_INVALID_HANDLE;
2276 }
2277 if (!pCount)
2278 {
2279 return VERR_INVALID_POINTER;
2280 }
2281 return hnode->CountChildren (pszChildName, pCount);
2282}
2283
2284CFGLDRR3DECL(int) CFGLDRQueryUInt32(CFGNODE hnode, const char *pszName, uint32_t *pulValue)
2285{
2286 if (!hnode)
2287 {
2288 return VERR_INVALID_HANDLE;
2289 }
2290 if (!pulValue)
2291 {
2292 return VERR_INVALID_POINTER;
2293 }
2294 return hnode->QueryUInt32 (pszName, pulValue);
2295}
2296
2297CFGLDRR3DECL(int) CFGLDRSetUInt32(CFGNODE hnode, const char *pszName, uint32_t ulValue)
2298{
2299 if (!hnode)
2300 {
2301 return VERR_INVALID_HANDLE;
2302 }
2303 return hnode->SetUInt32 (pszName, ulValue);
2304}
2305
2306CFGLDRR3DECL(int) CFGLDRQueryUInt64(CFGNODE hnode, const char *pszName, uint64_t *pullValue)
2307{
2308 if (!hnode)
2309 {
2310 return VERR_INVALID_HANDLE;
2311 }
2312 if (!pullValue)
2313 {
2314 return VERR_INVALID_POINTER;
2315 }
2316 return hnode->QueryUInt64 (pszName, pullValue);
2317}
2318
2319CFGLDRR3DECL(int) CFGLDRSetUInt64(CFGNODE hnode, const char *pszName, uint64_t ullValue)
2320{
2321 if (!hnode)
2322 {
2323 return VERR_INVALID_HANDLE;
2324 }
2325 return hnode->SetUInt64 (pszName, ullValue);
2326}
2327
2328CFGLDRR3DECL(int) CFGLDRQueryInt32(CFGNODE hnode, const char *pszName, int32_t *pint32Value)
2329{
2330 if (!hnode)
2331 {
2332 return VERR_INVALID_HANDLE;
2333 }
2334 return hnode->QueryInt32 (pszName, pint32Value);
2335}
2336
2337CFGLDRR3DECL(int) CFGLDRSetInt32(CFGNODE hnode, const char *pszName, int32_t int32Value)
2338{
2339 if (!hnode)
2340 {
2341 return VERR_INVALID_HANDLE;
2342 }
2343 return hnode->SetInt32 (pszName, int32Value);
2344}
2345
2346CFGLDRR3DECL(int) CFGLDRQueryInt64(CFGNODE hnode, const char *pszName, int64_t *pint64Value)
2347{
2348 if (!hnode)
2349 {
2350 return VERR_INVALID_HANDLE;
2351 }
2352 return hnode->QueryInt64 (pszName, pint64Value);
2353}
2354
2355CFGLDRR3DECL(int) CFGLDRSetInt64(CFGNODE hnode, const char *pszName, int64_t int64Value)
2356{
2357 if (!hnode)
2358 {
2359 return VERR_INVALID_HANDLE;
2360 }
2361 return hnode->SetInt64 (pszName, int64Value);
2362}
2363
2364CFGLDRR3DECL(int) CFGLDRQueryUInt16(CFGNODE hnode, const char *pszName, uint16_t *pu16Value)
2365{
2366 if (!hnode)
2367 {
2368 return VERR_INVALID_HANDLE;
2369 }
2370 if (!pu16Value)
2371 {
2372 return VERR_INVALID_POINTER;
2373 }
2374 return hnode->QueryUInt16 (pszName, pu16Value);
2375}
2376
2377CFGLDRR3DECL(int) CFGLDRSetUInt16(CFGNODE hnode, const char *pszName, uint16_t u16Value)
2378{
2379 if (!hnode)
2380 {
2381 return VERR_INVALID_HANDLE;
2382 }
2383 return hnode->SetUInt16 (pszName, u16Value);
2384}
2385
2386CFGLDRR3DECL(int) CFGLDRQueryBin(CFGNODE hnode, const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue)
2387{
2388 if (!hnode)
2389 {
2390 return VERR_INVALID_HANDLE;
2391 }
2392 if (!pcbValue)
2393 {
2394 return VERR_INVALID_POINTER;
2395 }
2396 return hnode->QueryBin (pszName, pvValue, cbValue, pcbValue);
2397}
2398
2399CFGLDRR3DECL(int) CFGLDRSetBin(CFGNODE hnode, const char *pszName, void *pvValue, unsigned cbValue)
2400{
2401 if (!hnode)
2402 {
2403 return VERR_INVALID_HANDLE;
2404 }
2405 if (!pvValue)
2406 {
2407 return VERR_INVALID_POINTER;
2408 }
2409 return hnode->SetBin (pszName, pvValue, cbValue);
2410}
2411
2412CFGLDRR3DECL(int) CFGLDRQueryString(CFGNODE hnode, const char *pszName, char *pszValue, unsigned cbValue, unsigned *pcbValue)
2413{
2414 if (!hnode)
2415 {
2416 return VERR_INVALID_HANDLE;
2417 }
2418 if (!pcbValue)
2419 {
2420 return VERR_INVALID_POINTER;
2421 }
2422 return hnode->QueryString (pszName, (void**)&pszValue, cbValue, pcbValue, false);
2423}
2424
2425CFGLDRR3DECL(int) CFGLDRQueryBSTR(CFGNODE hnode, const char *pszName, BSTR *ppwszValue)
2426{
2427 if (!hnode)
2428 {
2429 return VERR_INVALID_HANDLE;
2430 }
2431 if (!ppwszValue)
2432 {
2433 return VERR_INVALID_POINTER;
2434 }
2435 return hnode->QueryString(pszName, (void**)ppwszValue, 0, NULL, true);
2436}
2437
2438CFGLDRR3DECL(int) CFGLDRQueryUUID(CFGNODE hnode, const char *pszName, PRTUUID pUUID)
2439{
2440 if (!hnode)
2441 {
2442 return VERR_INVALID_HANDLE;
2443 }
2444 if (!pUUID)
2445 {
2446 return VERR_INVALID_POINTER;
2447 }
2448
2449 // we need it as UTF8
2450 unsigned size;
2451 int rc;
2452 rc = CFGLDRQueryString(hnode, pszName, NULL, 0, &size);
2453 if (rc == VERR_BUFFER_OVERFLOW)
2454 {
2455 char *uuidUtf8 = new char[size];
2456 rc = CFGLDRQueryString(hnode, pszName, uuidUtf8, size, &size);
2457 if (VBOX_SUCCESS(rc))
2458 {
2459 // remove the curly brackets
2460 uuidUtf8[strlen(uuidUtf8) - 1] = '\0';
2461 rc = RTUuidFromStr(pUUID, &uuidUtf8[1]);
2462 }
2463 delete[] uuidUtf8;
2464 }
2465 return rc;
2466}
2467
2468CFGLDRR3DECL(int) CFGLDRSetUUID(CFGNODE hnode, const char *pszName, PCRTUUID pUuid)
2469{
2470 if (!hnode)
2471 {
2472 return VERR_INVALID_HANDLE;
2473 }
2474 if (!pUuid)
2475 {
2476 return VERR_INVALID_HANDLE;
2477 }
2478
2479 // UUID + curly brackets
2480 char strUuid[RTUUID_STR_LENGTH + 2 * sizeof(char)];
2481 strUuid[0] = '{';
2482 RTUuidToStr((const PRTUUID)pUuid, &strUuid[1], RTUUID_STR_LENGTH);
2483 strcat(strUuid, "}");
2484 return hnode->SetString (pszName, strUuid, strlen (strUuid), false);
2485}
2486
2487CFGLDRR3DECL(int) CFGLDRSetString(CFGNODE hnode, const char *pszName, const char *pszValue)
2488{
2489 if (!hnode)
2490 {
2491 return VERR_INVALID_HANDLE;
2492 }
2493 if (!pszValue)
2494 {
2495 return VERR_INVALID_POINTER;
2496 }
2497 return hnode->SetString (pszName, pszValue, strlen (pszValue), false);
2498}
2499
2500CFGLDRR3DECL(int) CFGLDRSetBSTR(CFGNODE hnode, const char *pszName, const BSTR bstrValue)
2501{
2502 if (!hnode)
2503 {
2504 return VERR_INVALID_HANDLE;
2505 }
2506 if (!bstrValue)
2507 {
2508 return VERR_INVALID_POINTER;
2509 }
2510 return hnode->SetString (pszName, (char*)bstrValue, RTUtf16Len((PCRTUTF16)bstrValue), true);
2511}
2512
2513CFGLDRR3DECL(int) CFGLDRQueryBool(CFGNODE hnode, const char *pszName, bool *pfValue)
2514{
2515 if (!hnode)
2516 {
2517 return VERR_INVALID_HANDLE;
2518 }
2519 if (!pfValue)
2520 {
2521 return VERR_INVALID_POINTER;
2522 }
2523
2524 return hnode->QueryBool (pszName, pfValue);
2525}
2526
2527CFGLDRR3DECL(int) CFGLDRSetBool(CFGNODE hnode, const char *pszName, bool fValue)
2528{
2529 if (!hnode)
2530 {
2531 return VERR_INVALID_HANDLE;
2532 }
2533
2534 return hnode->SetBool (pszName, fValue);
2535}
2536
2537CFGLDRR3DECL(int) CFGLDRQueryDateTime(CFGNODE hnode, const char *pszName, int64_t *pi64Value)
2538{
2539 if (!hnode)
2540 {
2541 return VERR_INVALID_HANDLE;
2542 }
2543 if (!pi64Value)
2544 {
2545 return VERR_INVALID_POINTER;
2546 }
2547
2548 // query as UTF8 string
2549 unsigned size = 0;
2550 int rc = CFGLDRQueryString(hnode, pszName, NULL, 0, &size);
2551 if (rc != VERR_BUFFER_OVERFLOW)
2552 return rc;
2553
2554 char *pszValue = new char[size];
2555 char *pszBuf = new char[size];
2556 rc = CFGLDRQueryString(hnode, pszName, pszValue, size, &size);
2557 if (VBOX_SUCCESS(rc)) do
2558 {
2559 // Parse xsd:dateTime. The format is:
2560 // '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
2561 // where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z'
2562 uint32_t yyyy = 0;
2563 uint16_t mm = 0, dd = 0, hh = 0, mi = 0, ss = 0;
2564 if (sscanf(pszValue, "%d-%hu-%huT%hu:%hu:%hu%s",
2565 &yyyy, &mm, &dd, &hh, &mi, &ss, pszBuf) != 7)
2566 {
2567 rc = VERR_PARSE_ERROR;
2568 break;
2569 }
2570
2571 // currently, we accept only the UTC timezone ('Z'),
2572 // ignoring fractional seconds, if present
2573 if (pszBuf[0] == 'Z' ||
2574 (pszBuf[0] == '.' && pszBuf[strlen(pszBuf)-1] == 'Z'))
2575 {
2576 // start with an error
2577 rc = VERR_PARSE_ERROR;
2578
2579#if 0
2580 RTTIME time = { yyyy, mm, 0, 0, dd, hh, mm, ss, 0,
2581 RTTIME_FLAGS_TYPE_UTC };
2582 if (RTTimeNormalize(&time))
2583 {
2584 RTTIMESPEC timeSpec;
2585 if (RTTimeImplode(&time, &timeSpec))
2586 {
2587 *pi64Value = RTTimeSpecGetMilli(&timeSpec);
2588 rc = VINF_SUCCESS;
2589 }
2590 }
2591#else
2592 /// @todo (dmik) until RTTimeImplode and friends are done
2593 int isdst = 0;
2594 {
2595 time_t dummyt = time(NULL);
2596 isdst = localtime(&dummyt)->tm_isdst;
2597 }
2598 tm time;
2599 time.tm_hour = hh; /* hour (0 - 23) */
2600 time.tm_isdst = isdst; /* daylight saving time enabled/disabled */
2601 time.tm_mday = dd; /* day of month (1 - 31) */
2602 time.tm_min = mi; /* minutes (0 - 59) */
2603 time.tm_mon = mm - 1; /* month (0 - 11 : 0 = January) */
2604 time.tm_sec = ss; /* seconds (0 - 59) */
2605 time.tm_wday = 0; /* Day of week (0 - 6 : 0 = Sunday) */
2606 time.tm_yday = 0; /* Day of year (0 - 365) */
2607 time.tm_year = yyyy - 1900; /* Year less 1900 */
2608 time_t t = mktime(&time);
2609 // mktime expects local time, but we supply it UTC,
2610 // do a trick to get the right time value
2611 tm *dummytm = gmtime(&t);
2612 dummytm->tm_isdst = isdst;
2613 time_t delta = t - mktime(dummytm);
2614 *pi64Value = t + delta;
2615 *pi64Value *= 1000;
2616 rc = VINF_SUCCESS;
2617#endif
2618 }
2619 else
2620 rc = VERR_PARSE_ERROR;
2621 }
2622 while (0);
2623
2624 delete[] pszBuf;
2625 delete[] pszValue;
2626
2627 return rc;
2628}
2629
2630CFGLDRR3DECL(int) CFGLDRSetDateTime(CFGNODE hnode, const char *pszName, int64_t i64Value)
2631{
2632 if (!hnode)
2633 {
2634 return VERR_INVALID_HANDLE;
2635 }
2636
2637#if 0
2638 RTTIMESPEC timeSpec;
2639 RTTimeSpecSetMilli(&timeSpec, i64Value);
2640 RTTIME time;
2641 RTTimeExplode(&time, &timeSpec);
2642#else
2643 /// @todo (dmik) until RTTimeImplode and friends are done
2644 time_t t = (time_t)(i64Value / 1000);
2645 tm *ptm = gmtime(&t);
2646 if (!ptm)
2647 return VERR_PARSE_ERROR;
2648 tm time = *ptm;
2649 time.tm_year += 1900;
2650 time.tm_mon += 1;
2651#endif
2652
2653 // Store xsd:dateTime. The format is:
2654 // '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
2655 // where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z'
2656 char aszBuf [256];
2657 RTStrPrintf(aszBuf, sizeof(aszBuf),
2658 "%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
2659#if 0
2660 time.i32Year, (uint16_t) time.u8Month, (uint16_t) time.u8MonthDay,
2661 (uint16_t) time.u8Hour, (uint16_t) time.u8Minute, (uint16_t) time.u8Second);
2662#else
2663 /// @todo (dmik) until RTTimeImplode and friends are done
2664 time.tm_year, time.tm_mon, time.tm_mday,
2665 time.tm_hour, time.tm_min, time.tm_sec);
2666#endif
2667
2668 return hnode->SetString (pszName, aszBuf, strlen (aszBuf), false);
2669}
2670
2671CFGLDRR3DECL(int) CFGLDRDeleteAttribute(CFGNODE hnode, const char *pszName)
2672{
2673 if (!hnode)
2674 {
2675 return VERR_INVALID_HANDLE;
2676 }
2677 if (!pszName)
2678 {
2679 return VERR_INVALID_POINTER;
2680 }
2681 return hnode->DeleteAttribute (pszName);
2682}
2683
2684CFGLDRR3DECL(int) CFGLDRInitialize (void)
2685{
2686 int rc = VINF_SUCCESS;
2687
2688 if (!initXML ())
2689 {
2690 rc = VERR_NOT_SUPPORTED;
2691 }
2692
2693 return rc;
2694}
2695
2696CFGLDRR3DECL(void) CFGLDRShutdown (void)
2697{
2698 /// @todo delete CfgLoaders
2699 terminateXML ();
2700}
2701
2702#ifdef STANDALONE_TEST
2703
2704#include <iprt/runtime.h>
2705
2706int main(int argc, char **argv)
2707{
2708 Log(("Configuration loader standalone test\n"));
2709
2710 CFGHANDLE hcfg = 0;
2711 CFGNODE hnode = 0;
2712 unsigned count = 0;
2713 unsigned i;
2714 char *cfgFilename = "vboxcfg.xml";
2715 char *cfgFilenameSaved = "testas.xml";
2716
2717 /*
2718 * Initialize the VBox runtime without loading
2719 * the kernel support driver
2720 */
2721 int rc = RTR3Init(false);
2722 if (VBOX_FAILURE(rc))
2723 {
2724 Log(("RTInit failed %d\n", rc));
2725 goto l_exit_0;
2726 }
2727
2728 rc = CFGLDRInitialize();
2729 if (VBOX_FAILURE(rc))
2730 {
2731 Log(("Initialize failed %d\n", rc));
2732 goto l_exit_0;
2733 }
2734
2735 rc = CFGLDRLoad(&hcfg, cfgFilename, "vboxcfg.xsd");
2736 if (VBOX_FAILURE(rc))
2737 {
2738 Log(("Load failed %d\n", rc));
2739 goto l_exit_0;
2740 }
2741
2742 printf("Configuration loaded from %s\n", cfgFilename);
2743
2744 rc = CFGLDRCreateNode(hcfg, "Configuration/Something/DeviceManager/DeviceList", &hnode);
2745 if (VBOX_FAILURE(rc))
2746 {
2747 Log(("CreateNode failed %d\n", rc));
2748 goto l_exit_1;
2749 }
2750 rc = CFGLDRSetString(hnode, "GUID", "testtestte");
2751 rc = CFGLDRReleaseNode(hnode);
2752
2753 rc = CFGLDRGetNode(hcfg, "Configuration/Managers/DeviceManager/DeviceList", 0, &hnode);
2754 if (VBOX_FAILURE(rc))
2755 {
2756 Log(("GetNode failed %d\n", rc));
2757 goto l_exit_1;
2758 }
2759
2760 rc = CFGLDRCountChildren(hnode, "Device", &count);
2761 if (VBOX_FAILURE(rc))
2762 {
2763 Log(("CountChildren failed %d\n", rc));
2764 goto l_exit_2;
2765 }
2766
2767 Log(("Number of child nodes %d\n", count));
2768
2769 for (i = 0; i < count; i++)
2770 {
2771 CFGNODE hchild = 0;
2772 unsigned cbValue;
2773 char szValue[256];
2774
2775 rc = CFGLDRGetChildNode(hnode, "Device", i, &hchild);
2776 if (VBOX_FAILURE(rc))
2777 {
2778 Log(("GetChildNode failed %d\n", rc));
2779 goto l_exit_2;
2780 }
2781
2782 unsigned dummy;
2783 rc = CFGLDRCountChildren(hchild, NULL, &dummy);
2784 Log(("Number of child nodes of Device %d\n", dummy));
2785
2786 int32_t value;
2787
2788 rc = CFGLDRQueryInt32(hchild, "int", &value);
2789 Log(("Child node %d (rc = %d): int = %d\n", i, rc, value));
2790
2791 rc = CFGLDRQueryString(hchild, NULL, szValue, sizeof (szValue), &cbValue);
2792 if (VBOX_FAILURE(rc))
2793 {
2794 Log(("QueryString failed %d\n", rc));
2795 }
2796 Log(("Child node %d: (length = %d) = '%s'\n", i, cbValue, szValue));
2797
2798 rc = CFGLDRSetString(hchild, "GUID", "testtesttest");
2799 if (VBOX_FAILURE(rc))
2800 {
2801 Log(("SetString failed %d\n", rc));
2802 }
2803
2804 rc = CFGLDRDeleteAttribute(hchild, "int");
2805 Log(("Attribute delete %d (rc = %d)\n", i, rc));
2806
2807 CFGLDRSetBin(hchild, "Bin", (void *)CFGLDRSetBin, 100);
2808 CFGLDRSetInt32(hchild, "int32", 1973);
2809// CFGLDRSetUInt64(hchild, "uint64", 0x1973);
2810
2811 CFGNODE hnew = 0;
2812 CFGLDRCreateChildNode(hchild, "testnode", &hnew);
2813 rc = CFGLDRSetString(hchild, NULL, "her");
2814 if (VBOX_FAILURE(rc))
2815 {
2816 Log(("_SetString failed %d\n", rc));
2817 }
2818 rc = CFGLDRSetString(hnew, NULL, "neher");
2819 if (VBOX_FAILURE(rc))
2820 {
2821 Log(("+SetString failed %d\n", rc));
2822 }
2823 CFGLDRReleaseNode(hchild);
2824 }
2825
2826 rc = CFGLDRSaveAs(hcfg, cfgFilenameSaved);
2827 if (VBOX_FAILURE(rc))
2828 {
2829 Log(("SaveAs failed %d\n", rc));
2830 goto l_exit_2;
2831 }
2832
2833 Log(("Configuration saved as %s\n", cfgFilenameSaved));
2834
2835l_exit_2:
2836
2837 rc = CFGLDRReleaseNode(hnode);
2838 if (VBOX_FAILURE(rc))
2839 {
2840 Log(("ReleaseNode failed %d\n", rc));
2841 }
2842
2843l_exit_1:
2844
2845 rc = CFGLDRFree(hcfg);
2846 if (VBOX_FAILURE(rc))
2847 {
2848 Log(("Load failed %d\n", rc));
2849 }
2850
2851l_exit_0:
2852
2853 CFGLDRShutdown();
2854
2855 Log(("Test completed."));
2856 return rc;
2857}
2858#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