VirtualBox

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

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

replace underscore symbols in Main/

  • 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 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/* Define STANDALONE_TEST for making a executable that
23 * will load and parse a XML file
24 */
25// #define STANDALONE_TEST
26
27/** @page pg_cfgldr CFGLDR - The Configuration Loader
28 *
29 * The configuration loader loads and keeps the configuration of VBox
30 * session. It will be used by VBox components to retrieve configuration
31 * values at startup and to change values if necessary.
32 *
33 * When VBox is started, it must call CFGLDRLoad to load global
34 * VBox configuration and then VM configuration.
35 *
36 * Handles then used by VBox components to retrieve configuration
37 * values. Components must query their values at startup.
38 * Configuration values can be changed only by respective
39 * component and the component must call CFGLDRSet*, to change the
40 * value in configuration file.
41 *
42 * Values are accessed only by name. It is important for components to
43 * use CFGLDRQuery* only at startup for performance reason.
44 *
45 * All CFGLDR* functions that take a CFGHANDLE or CFGNODE as their first
46 * argument return the VERR_INVALID_HANDLE status code if the argument is
47 * null handle (i.e. its value is zero).
48 */
49
50#define VBOX_XML_WRITER_FILTER
51#define VBOX_XML_XSLT
52#define _CRT_SECURE_NO_DEPRECATE
53
54#define LOG_GROUP LOG_GROUP_MAIN
55#include <VBox/log.h>
56
57#include <VBox/err.h>
58#include <iprt/string.h>
59#include <iprt/uuid.h>
60#include <iprt/path.h>
61#include <iprt/file.h>
62#include <iprt/time.h>
63
64/// @todo (dmik) until RTTimeImplode and friends are done
65#include <time.h>
66
67#include <xercesc/util/PlatformUtils.hpp>
68
69#include <xercesc/dom/DOM.hpp>
70#include <xercesc/dom/DOMImplementation.hpp>
71#include <xercesc/dom/DOMImplementationLS.hpp>
72#include <xercesc/dom/DOMBuilder.hpp>
73#include <xercesc/dom/DOMWriter.hpp>
74
75#include <xercesc/framework/LocalFileFormatTarget.hpp>
76
77#include <xercesc/util/XMLUni.hpp>
78#include <xercesc/util/XMLUniDefs.hpp>
79#include <xercesc/util/BinInputStream.hpp>
80#include <xercesc/util/BinMemInputStream.hpp>
81
82#ifdef VBOX_XML_WRITER_FILTER
83#include <xercesc/dom/DOMWriterFilter.hpp>
84#endif
85
86#ifdef VBOX_XML_XSLT
87
88#include <xalanc/Include/PlatformDefinitions.hpp>
89#include <xalanc/XalanTransformer/XalanTransformer.hpp>
90#include <xalanc/XalanTransformer/XercesDOMWrapperParsedSource.hpp>
91#include <xalanc/XercesParserLiaison/XercesParserLiaison.hpp>
92#include <xalanc/XercesParserLiaison/XercesDOMSupport.hpp>
93#include <xalanc/XercesParserLiaison/FormatterToXercesDOM.hpp>
94#include <xalanc/XSLT/ProblemListener.hpp>
95
96XALAN_CPP_NAMESPACE_USE
97
98// generated file
99#include "SettingsConverter_xsl.h"
100
101#endif // VBOX_XML_XSLT
102
103// Little hack so we don't have to include the COM stuff
104// Note that those defines need to be made before we include
105// cfgldr.h so that the prototypes can be compiled.
106#ifndef BSTR
107#define OLECHAR wchar_t
108#define BSTR void*
109#if defined(RT_OS_WINDOWS)
110extern "C" BSTR __stdcall SysAllocString(const OLECHAR* sz);
111#else
112extern "C" BSTR SysAllocString(const OLECHAR* sz);
113#endif
114#endif
115
116#include "Logging.h"
117
118#include <VBox/cfgldr.h>
119#include "cfgldrhlp.h"
120
121#include <string.h>
122#include <stdio.h> // for sscanf
123#ifdef STANDALONE_TEST
124# include <stdlib.h>
125# include <iprt/runtime.h>
126#endif
127
128XERCES_CPP_NAMESPACE_USE
129
130class CfgNode
131{
132 private:
133 friend class CfgLoader;
134 CfgLoader *pConfiguration;
135 CfgNode *next;
136 CfgNode *prev;
137
138 DOMNode *pdomnode;
139
140 CfgNode (CfgLoader *pcfg);
141 virtual ~CfgNode ();
142
143 int resolve (DOMNode *root, const char *pszName, unsigned uIndex, unsigned flags);
144
145 int queryValueString (const char *pszName, PRTUTF16 *ppwszValue);
146 int setValueString (const char *pszName, PRTUTF16 pwszValue);
147
148 DOMNode *findChildText (void);
149
150 public:
151
152 enum {
153 fSearch = 0,
154 fCreateIfNotExists = 1,
155 fAppend = 2
156 };
157
158 static int ReleaseNode (CfgNode *pnode);
159 static int DeleteNode (CfgNode *pnode);
160
161 int CreateChildNode (const char *pszName, CfgNode **ppnode);
162 int AppendChildNode (const char *pszName, CfgNode **ppnode);
163
164 int GetChild (const char *pszName, unsigned uIndex, CfgNode **ppnode);
165 int CountChildren (const char *pszChildName, unsigned *pCount);
166
167
168 int QueryUInt32 (const char *pszName, uint32_t *pulValue);
169 int SetUInt32 (const char *pszName, uint32_t ulValue);
170 int QueryUInt64 (const char *pszName, uint64_t *pullValue);
171 int SetUInt64 (const char *pszName, uint64_t ullValue);
172
173 int QueryInt32 (const char *pszName, int32_t *pint32Value);
174 int SetInt32 (const char *pszName, int32_t int32Value);
175 int QueryInt64 (const char *pszName, int64_t *pint64Value);
176 int SetInt64 (const char *pszName, int64_t int64Value);
177
178 int QueryUInt16 (const char *pszName, uint16_t *pu16Value);
179 int SetUInt16 (const char *pszName, uint16_t u16Value);
180
181 int QueryBin (const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue);
182 int SetBin (const char *pszName, const void *pvValue, unsigned cbValue);
183 int QueryString (const char *pszName, void **pValue, unsigned cbValue, unsigned *pcbValue, bool returnUtf16);
184 int SetString (const char *pszName, const char *pszValue, unsigned cbValue, bool isUtf16);
185
186 int QueryBool (const char *pszName, bool *pfValue);
187 int SetBool (const char *pszName, bool fValue);
188
189 int DeleteAttribute (const char *pszName);
190};
191
192class CfgLoader
193{
194 private:
195
196 friend class CfgNode;
197
198 PRTUTF16 pwszOriginalFilename;
199 RTFILE hOriginalFileHandle; /** r=bird: this is supposed to mean 'handle to the orignal file handle'? The name is
200 * overloaded with too many 'handles'... That goes for those hFileHandle parameters too.
201 * hOriginalFile / FileOriginal and hFile / File should do by a long way. */
202 CfgNode *pfirstnode;
203
204 DOMBuilder *builder;
205 DOMNode *root;
206
207 int getNode (DOMNode *prootnode, const char *pszName, unsigned uIndex, CfgNode **ppnode, unsigned flags);
208
209 DOMDocument *Document(void) { return static_cast<DOMDocument *>(root); };
210
211 public:
212
213 CfgLoader ();
214 virtual ~CfgLoader ();
215
216 int Load (const char *pszFileName, RTFILE hFileHandle,
217 const char *pszExternalSchemaLocation, bool bDoNamespaces,
218 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
219 char **ppszErrorMessage);
220 int Save (const char *pszFilename, RTFILE hFileHandle,
221 char **ppszErrorMessage);
222 int Create ();
223#ifdef VBOX_XML_XSLT
224 int Transform (const char *pszTemlateLocation,
225 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
226 char **ppszErrorMessage);
227#endif
228
229 static int FreeConfiguration (CfgLoader *pcfg);
230
231 int CreateNode (const char *pszName, CfgNode **ppnode);
232
233 int GetNode (const char *pszName, unsigned uIndex, CfgNode **ppnode);
234};
235
236#ifdef VBOX_XML_WRITER_FILTER
237class VBoxWriterFilter : public DOMWriterFilter
238{
239 public:
240 VBoxWriterFilter (unsigned long whatToShow = DOMNodeFilter::SHOW_ALL);
241 ~VBoxWriterFilter () {};
242
243 virtual short acceptNode (const DOMNode*) const;
244 virtual unsigned long getWhatToShow () const { return fWhatToShow; };
245 virtual void setWhatToShow (unsigned long toShow) { fWhatToShow = toShow; };
246
247 private:
248 unsigned long fWhatToShow;
249};
250
251VBoxWriterFilter::VBoxWriterFilter(unsigned long whatToShow)
252 :
253 fWhatToShow (whatToShow)
254{
255}
256
257short VBoxWriterFilter::acceptNode(const DOMNode* node) const
258{
259 switch (node->getNodeType())
260 {
261 case DOMNode::TEXT_NODE:
262 {
263 /* Reject empty nodes */
264 const XMLCh *pxmlch = node->getNodeValue ();
265
266 if (pxmlch)
267 {
268 while (*pxmlch != chNull)
269 {
270 if ( *pxmlch == chLF
271 || *pxmlch == chCR
272 || *pxmlch == chSpace
273 || *pxmlch == chHTab
274 )
275 {
276 pxmlch++;
277 continue;
278 }
279
280 break;
281 }
282
283 if (*pxmlch != chNull)
284 {
285 /* Accept the node because it contains non space characters */
286 return DOMNodeFilter::FILTER_ACCEPT;
287 }
288 }
289
290 return DOMNodeFilter::FILTER_REJECT;
291 }
292 }
293
294 return DOMNodeFilter::FILTER_ACCEPT;
295}
296
297#endif
298
299// CfgLdrInputSource
300////////////////////////////////////////////////////////////////////////////////
301
302/**
303 * A wrapper around RTFILE or a char buffer that acts like a DOMInputSource
304 * and therefore can be with DOMBuilder instances.
305 */
306class CfgLdrInputSource : public DOMInputSource
307{
308public:
309
310 CfgLdrInputSource (PCFGLDRENTITY pcEntity, const char *pcszSystemId);
311 virtual ~CfgLdrInputSource() { release(); }
312
313 // Functions introduced in DOM Level 3
314
315 const XMLCh *getEncoding () const { return NULL; }
316 const XMLCh *getPublicId () const { return NULL; }
317 const XMLCh *getSystemId () const { return (const XMLCh *)m_pwszSystemId; }
318 const XMLCh *getBaseURI () const { return (const XMLCh *)m_pwszBaseURI; }
319
320 void setEncoding (const XMLCh *const /* encodingStr */)
321 { AssertMsgFailed (("Not implemented!\n")); }
322 void setPublicId (const XMLCh *const /* publicId */)
323 { AssertMsgFailed (("Not implemented!\n")); }
324 void setSystemId (const XMLCh *const /* systemId */)
325 { AssertMsgFailed (("Not implemented!\n")); }
326 void setBaseURI (const XMLCh *const /* baseURI */)
327 { AssertMsgFailed (("Not implemented!\n")); }
328
329 // Non-standard Extension
330
331 BinInputStream *makeStream() const;
332 void setIssueFatalErrorIfNotFound (const bool /* flag */) {}
333 bool getIssueFatalErrorIfNotFound() const { return true; }
334 void release();
335
336private:
337
338 class FileHandleInputStream : public BinInputStream
339 {
340 public:
341
342 FileHandleInputStream (RTFILE hFileHandle);
343
344 unsigned int curPos() const;
345 unsigned int readBytes (XMLByte *const toFill, const unsigned int maxToRead);
346
347 private:
348
349 RTFILE m_hFileHandle;
350 uint64_t m_cbPos;
351 };
352
353 void init (const char *pcszSystemId);
354
355 CFGLDRENTITY m_entity;
356
357 PRTUTF16 m_pwszSystemId;
358 PRTUTF16 m_pwszBaseURI;
359};
360
361CfgLdrInputSource::CfgLdrInputSource (PCFGLDRENTITY pcEntity,
362 const char *pcszSystemId) :
363 m_pwszSystemId (NULL), m_pwszBaseURI (NULL)
364{
365 Assert (pcEntity && pcEntity->enmType != CFGLDRENTITYTYPE_INVALID);
366 // make a copy of the entity descriptor
367 m_entity = *pcEntity;
368
369 Assert (pcszSystemId);
370 int rc = RTStrToUtf16 (pcszSystemId, &m_pwszSystemId);
371 AssertRC (rc);
372
373 char *pszBaseURI = NULL;
374 pszBaseURI = RTStrDup (pcszSystemId);
375 Assert (pszBaseURI);
376 RTPathStripFilename (pszBaseURI);
377
378 rc = RTStrToUtf16 (pszBaseURI, &m_pwszBaseURI);
379 AssertRC (rc);
380}
381
382void CfgLdrInputSource::release()
383{
384 switch (m_entity.enmType)
385 {
386 case CFGLDRENTITYTYPE_HANDLE:
387 if (m_entity.u.handle.bClose)
388 RTFileClose (m_entity.u.handle.hFile);
389 break;
390 case CFGLDRENTITYTYPE_MEMORY:
391 if (m_entity.u.memory.bFree)
392 RTMemTmpFree (m_entity.u.memory.puchBuf);
393 break;
394 default:
395 break;
396 };
397
398 m_entity.enmType = CFGLDRENTITYTYPE_INVALID;
399
400 if (m_pwszBaseURI)
401 {
402 RTUtf16Free (m_pwszBaseURI);
403 m_pwszBaseURI = NULL;
404 }
405 if (m_pwszSystemId)
406 {
407 RTUtf16Free (m_pwszSystemId);
408 m_pwszSystemId = NULL;
409 }
410}
411
412BinInputStream *CfgLdrInputSource::makeStream() const
413{
414 BinInputStream *stream = NULL;
415
416 switch (m_entity.enmType)
417 {
418 case CFGLDRENTITYTYPE_HANDLE:
419 stream = new FileHandleInputStream (m_entity.u.handle.hFile);
420 break;
421 case CFGLDRENTITYTYPE_MEMORY:
422 // puchBuf is neither copied nor destructed by BinMemInputStream
423 stream = new BinMemInputStream (m_entity.u.memory.puchBuf,
424 m_entity.u.memory.cbBuf,
425 BinMemInputStream::BufOpt_Reference);
426 break;
427 default:
428 AssertMsgFailed (("Invalid resolver entity type!\n"));
429 break;
430 };
431
432 return stream;
433}
434
435CfgLdrInputSource::FileHandleInputStream::FileHandleInputStream (RTFILE hFileHandle) :
436 m_hFileHandle (hFileHandle),
437 m_cbPos (0)
438{
439}
440
441unsigned int CfgLdrInputSource::FileHandleInputStream::curPos() const
442{
443 AssertMsg (!(m_cbPos >> 32), ("m_cbPos exceeds 32 bits (%16Xll)\n", m_cbPos));
444 return (unsigned int)m_cbPos;
445}
446
447unsigned int CfgLdrInputSource::FileHandleInputStream::readBytes (
448 XMLByte *const toFill, const unsigned int maxToRead
449)
450{
451 /// @todo (dmik) trhow the appropriate exceptions if we fail to write
452
453 int rc;
454 NOREF (rc);
455
456 // memorize the current position
457 uint64_t cbOriginalPos = RTFileTell (m_hFileHandle);
458 Assert (cbOriginalPos != ~0ULL);
459 // set the new position
460 rc = RTFileSeek (m_hFileHandle, m_cbPos, RTFILE_SEEK_BEGIN, NULL);
461 AssertRC (rc);
462
463 // read from the file
464 unsigned cbRead = 0;
465 rc = RTFileRead (m_hFileHandle, toFill, maxToRead, &cbRead);
466 AssertRC (rc);
467
468 // adjust the private position
469 m_cbPos += cbRead;
470 // restore the current position
471 rc = RTFileSeek (m_hFileHandle, cbOriginalPos, RTFILE_SEEK_BEGIN, NULL);
472 AssertRC (rc);
473
474 return cbRead;
475}
476
477// CfgLdrFormatTarget
478////////////////////////////////////////////////////////////////////////////////
479
480class CfgLdrFormatTarget : public XMLFormatTarget
481{
482public:
483
484 CfgLdrFormatTarget (PCFGLDRENTITY pcEntity);
485 ~CfgLdrFormatTarget();
486
487 // virtual XMLFormatTarget methods
488 void writeChars (const XMLByte *const toWrite, const unsigned int count,
489 XMLFormatter *const formatter);
490 void flush() {}
491
492private:
493
494 CFGLDRENTITY m_entity;
495};
496
497CfgLdrFormatTarget::CfgLdrFormatTarget (PCFGLDRENTITY pcEntity)
498{
499 Assert (pcEntity && pcEntity->enmType != CFGLDRENTITYTYPE_INVALID);
500 // make a copy of the entity descriptor
501 m_entity = *pcEntity;
502
503 switch (m_entity.enmType)
504 {
505 case CFGLDRENTITYTYPE_HANDLE:
506 int rc;
507 // start writting from the beginning
508 rc = RTFileSeek (m_entity.u.handle.hFile, 0, RTFILE_SEEK_BEGIN, NULL);
509 AssertRC (rc);
510 NOREF (rc);
511 break;
512 case CFGLDRENTITYTYPE_MEMORY:
513 AssertMsgFailed (("Unsupported entity type!\n"));
514 break;
515 default:
516 break;
517 };
518}
519
520CfgLdrFormatTarget::~CfgLdrFormatTarget()
521{
522 switch (m_entity.enmType)
523 {
524 case CFGLDRENTITYTYPE_HANDLE:
525 {
526 int rc;
527 // truncate the file upto the size actually written
528 uint64_t cbPos = RTFileTell (m_entity.u.handle.hFile);
529 Assert (cbPos != ~0ULL);
530 rc = RTFileSetSize (m_entity.u.handle.hFile, cbPos);
531 AssertRC (rc);
532 // reset the position to he beginning
533 rc = RTFileSeek (m_entity.u.handle.hFile, 0, RTFILE_SEEK_BEGIN, NULL);
534 AssertRC (rc);
535 NOREF (rc);
536 if (m_entity.u.handle.bClose)
537 RTFileClose (m_entity.u.handle.hFile);
538 break;
539 }
540 case CFGLDRENTITYTYPE_MEMORY:
541 break;
542 default:
543 break;
544 };
545}
546
547void CfgLdrFormatTarget::writeChars (const XMLByte *const toWrite,
548 const unsigned int count,
549 XMLFormatter *const /* formatter */)
550{
551 /// @todo (dmik) trhow the appropriate exceptions if we fail to write
552
553 switch (m_entity.enmType)
554 {
555 case CFGLDRENTITYTYPE_HANDLE:
556 int rc;
557 rc = RTFileWrite (m_entity.u.handle.hFile, toWrite, count, NULL);
558 AssertRC (rc);
559 NOREF (rc);
560 break;
561 case CFGLDRENTITYTYPE_MEMORY:
562 AssertMsgFailed (("Unsupported entity type!\n"));
563 break;
564 default:
565 AssertMsgFailed (("Invalid entity type!\n"));
566 break;
567 };
568}
569
570// CfgLdrEntityResolver
571////////////////////////////////////////////////////////////////////////////////
572
573/**
574 * A wrapper around FNCFGLDRENTITYRESOLVER callback that acts like a
575 * DOMEntityResolver and therefore can be used with DOMBuilder instances.
576 */
577class CfgLdrEntityResolver : public DOMEntityResolver
578{
579public:
580
581 CfgLdrEntityResolver (PFNCFGLDRENTITYRESOLVER pfnEntityResolver) :
582 m_pfnEntityResolver (pfnEntityResolver) {}
583
584 // Functions introduced in DOM Level 2
585 DOMInputSource *resolveEntity (const XMLCh *const publicId,
586 const XMLCh *const systemId,
587 const XMLCh *const baseURI);
588
589private:
590
591 PFNCFGLDRENTITYRESOLVER m_pfnEntityResolver;
592};
593
594DOMInputSource *CfgLdrEntityResolver::resolveEntity (const XMLCh *const publicId,
595 const XMLCh *const systemId,
596 const XMLCh *const baseURI)
597{
598 if (!m_pfnEntityResolver)
599 return NULL;
600
601 DOMInputSource *source = NULL;
602 int rc = VINF_SUCCESS;
603
604 char *pszPublicId = NULL;
605 char *pszSystemId = NULL;
606 char *pszBaseURI = NULL;
607
608 if (publicId)
609 rc = RTUtf16ToUtf8 (publicId, &pszPublicId);
610 if (VBOX_SUCCESS (rc))
611 {
612 if (systemId)
613 rc = RTUtf16ToUtf8 (systemId, &pszSystemId);
614 if (VBOX_SUCCESS (rc))
615 {
616 if (baseURI)
617 rc = RTUtf16ToUtf8 (baseURI, &pszBaseURI);
618 if (VBOX_SUCCESS (rc))
619 {
620 CFGLDRENTITY entity;
621 rc = m_pfnEntityResolver (pszPublicId, pszSystemId, pszBaseURI,
622 &entity);
623 if (rc == VINF_SUCCESS)
624 source = new CfgLdrInputSource (&entity, pszSystemId);
625 }
626 }
627 }
628
629 if (pszBaseURI)
630 RTStrFree (pszBaseURI);
631 if (pszSystemId)
632 RTStrFree (pszSystemId);
633 if (pszPublicId)
634 RTStrFree (pszPublicId);
635
636 return source;
637}
638
639// CfgLdrErrorHandler
640////////////////////////////////////////////////////////////////////////////////
641
642/**
643 * An error handler that accumulates all error messages in a single UTF-8 string.
644 */
645class CfgLdrErrorHandler : public DOMErrorHandler
646#ifdef VBOX_XML_XSLT
647 , public ProblemListener
648#endif
649{
650public:
651
652 CfgLdrErrorHandler();
653 ~CfgLdrErrorHandler();
654
655 bool hasErrors() { return m_pszBuf != NULL; }
656
657 /** Transfers ownership of the string to the caller and resets the handler */
658 char *takeErrorMessage() {
659 char *pszBuf = m_pszBuf;
660 m_pszBuf = NULL;
661 return pszBuf;
662 }
663
664 // Functions introduced in DOM Level 3
665 bool handleError (const DOMError &domError);
666
667#ifdef VBOX_XML_XSLT
668 // Xalan ProblemListener interface
669 void setPrintWriter (PrintWriter *pw) {}
670 void problem (eProblemSource where, eClassification classification,
671 const XalanNode *sourceNode, const ElemTemplateElement *styleNode,
672 const XalanDOMString &msg, const XalanDOMChar *uri,
673 int lineNo, int charOffset);
674#endif
675
676private:
677
678 char *m_pszBuf;
679};
680
681CfgLdrErrorHandler::CfgLdrErrorHandler() :
682 m_pszBuf (NULL)
683{
684}
685
686CfgLdrErrorHandler::~CfgLdrErrorHandler()
687{
688 if (m_pszBuf)
689 RTMemTmpFree (m_pszBuf);
690}
691
692bool CfgLdrErrorHandler::handleError (const DOMError &domError)
693{
694 const char *pszSeverity = NULL;
695 switch (domError.getSeverity())
696 {
697 case DOMError::DOM_SEVERITY_WARNING: pszSeverity = "WARNING: ";
698 case DOMError::DOM_SEVERITY_ERROR: pszSeverity = "ERROR: ";
699 case DOMError::DOM_SEVERITY_FATAL_ERROR: pszSeverity = "FATAL ERROR: ";
700 }
701
702 char *pszLocation = NULL;
703 const DOMLocator *pLocation = domError.getLocation();
704 if (pLocation)
705 {
706 static const char Location[] = "\nLocation: '%s', line %d, column %d";
707
708 char *pszURI = NULL;
709 if (pLocation->getURI())
710 RTUtf16ToUtf8 (pLocation->getURI(), &pszURI);
711
712 size_t cbLocation = sizeof (Location) +
713 (pszURI ? strlen (pszURI) : 10 /* NULL */) +
714 10 + 10 + 1 /* line & column & \0 */;
715 pszLocation = (char *) RTMemTmpAllocZ (cbLocation);
716 RTStrPrintf (pszLocation, cbLocation, Location,
717 pszURI,
718 pLocation->getLineNumber(), pLocation->getColumnNumber());
719
720 if (pszURI)
721 RTStrFree (pszURI);
722 }
723
724 LogFlow (("CfgLdrErrorHandler::handleError():\n %s%ls%s\n",
725 pszSeverity, domError.getMessage(), pszLocation));
726
727 char *pszMsg = NULL;
728 if (domError.getMessage())
729 RTUtf16ToUtf8 (domError.getMessage(), &pszMsg);
730
731 size_t cbNewBuf = (m_pszBuf ? strlen (m_pszBuf) : 0) +
732 (pszSeverity ? strlen (pszSeverity) : 0) +
733 (pszMsg ? strlen (pszMsg) : 0) +
734 (pszLocation ? strlen (pszLocation) : 0);
735 char *pszNewBuf = (char *) RTMemTmpAllocZ (cbNewBuf + 2 /* \n + \0 */);
736
737 if (m_pszBuf)
738 {
739 strcpy (pszNewBuf, m_pszBuf);
740 strcat (pszNewBuf, "\n");
741 }
742 if (pszSeverity)
743 strcat (pszNewBuf, pszSeverity);
744 if (pszMsg)
745 strcat (pszNewBuf, pszMsg);
746 if (pszLocation)
747 strcat (pszNewBuf, pszLocation);
748
749 if (m_pszBuf)
750 RTMemTmpFree (m_pszBuf);
751 m_pszBuf = pszNewBuf;
752
753 if (pszLocation)
754 RTMemTmpFree (pszLocation);
755 if (pszMsg)
756 RTStrFree (pszMsg);
757
758 // fail on any error when possible
759 return false;
760}
761
762#ifdef VBOX_XML_XSLT
763void CfgLdrErrorHandler::problem (eProblemSource where, eClassification classification,
764 const XalanNode *sourceNode, const ElemTemplateElement *styleNode,
765 const XalanDOMString &msg, const XalanDOMChar *uri,
766 int lineNo, int charOffset)
767{
768 const char *pszClass = NULL;
769 switch (classification)
770 {
771 case eMESSAGE: pszClass = "INFO: ";
772 case eWARNING: pszClass = "WARNING: ";
773 case eERROR: pszClass = "ERROR: ";
774 }
775
776 LogFlow (("CfgLdrErrorHandler::problem():\n %s%ls\n", pszClass, msg.c_str()));
777
778 char *pszMsg = NULL;
779 if (msg.c_str())
780 RTUtf16ToUtf8 (msg.c_str(), &pszMsg);
781
782 size_t cbNewBuf = (m_pszBuf ? strlen (m_pszBuf) : 0) +
783 (pszClass ? strlen (pszClass) : 0) +
784 (pszMsg ? strlen (pszMsg) : 0);
785 char *pszNewBuf = (char *) RTMemTmpAllocZ (cbNewBuf + 2 /* \n + \0 */);
786
787 if (m_pszBuf)
788 {
789 strcpy (pszNewBuf, m_pszBuf);
790 strcat (pszNewBuf, "\n");
791 }
792 if (pszClass)
793 strcat (pszNewBuf, pszClass);
794 if (pszMsg)
795 strcat (pszNewBuf, pszMsg);
796
797 if (m_pszBuf)
798 RTStrFree (m_pszBuf);
799
800 m_pszBuf = RTStrDup (pszNewBuf);
801
802 if (pszNewBuf)
803 RTMemTmpFree (pszNewBuf);
804 if (pszMsg)
805 RTStrFree (pszMsg);
806}
807#endif
808
809//
810////////////////////////////////////////////////////////////////////////////////
811
812static int xmlInitialized = 0;
813
814static int initXML (void)
815{
816 try
817 {
818 XMLPlatformUtils::Initialize();
819 }
820
821 catch(...)
822 {
823 return 0;
824 }
825
826 return (xmlInitialized = 1);
827}
828
829static void terminateXML (void)
830{
831 if (xmlInitialized)
832 {
833 XMLPlatformUtils::Terminate();
834 xmlInitialized = 0;
835 }
836}
837
838/* CfgLoader implementation */
839CfgLoader::CfgLoader ()
840 :
841 pwszOriginalFilename (NULL),
842 hOriginalFileHandle (NIL_RTFILE),
843 pfirstnode (NULL),
844 builder (NULL),
845 root (NULL)
846{
847}
848
849CfgLoader::~CfgLoader ()
850{
851 if (pwszOriginalFilename)
852 {
853 RTUtf16Free (pwszOriginalFilename);
854 }
855
856 if (builder)
857 {
858 /* Configuration was parsed from a file.
859 * Parser owns root and will delete root.
860 */
861 builder->release();
862 }
863 else if (root)
864 {
865 /* This is new, just created configuration.
866 * root is new object created by DOMImplementation::createDocument
867 * We have to delete root.
868 */
869 root->release();
870 }
871}
872
873int CfgLoader::Load (const char *pszFileName, RTFILE hFileHandle,
874 const char *pszExternalSchemaLocation, bool bDoNamespaces,
875 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
876 char **ppszErrorMessage)
877{
878 if (!xmlInitialized)
879 return VERR_NOT_SUPPORTED;
880
881 Assert (!root && !pwszOriginalFilename);
882 if (root || pwszOriginalFilename)
883 return VERR_ALREADY_LOADED;
884
885 static const XMLCh LS[] = { chLatin_L, chLatin_S, chNull };
886 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation (LS);
887 if (!impl)
888 return VERR_NOT_SUPPORTED;
889
890 // note:
891 // member variables allocated here (builder, pwszOriginalFilename) are not
892 // freed in case of error because CFGLDRLoad() that calls this method will
893 // delete this instance (and all members) if we return a failure from here
894
895 builder = static_cast <DOMImplementationLS *> (impl)->
896 createDOMBuilder (DOMImplementationLS::MODE_SYNCHRONOUS, 0);
897 if (!builder)
898 return VERR_NOT_SUPPORTED;
899
900 int rc = VINF_SUCCESS;
901
902 if (ppszErrorMessage)
903 *ppszErrorMessage = NULL;
904
905 // set parser's features
906 Assert (builder->canSetFeature (XMLUni::fgDOMDatatypeNormalization, true));
907 if (builder->canSetFeature (XMLUni::fgDOMDatatypeNormalization, true))
908 builder->setFeature (XMLUni::fgDOMDatatypeNormalization, true);
909 else
910 return VERR_NOT_SUPPORTED;
911 if (bDoNamespaces)
912 {
913 Assert (builder->canSetFeature (XMLUni::fgDOMNamespaces, true));
914 if (builder->canSetFeature (XMLUni::fgDOMNamespaces, true))
915 builder->setFeature (XMLUni::fgDOMNamespaces, true);
916 else
917 return VERR_NOT_SUPPORTED;
918 }
919 if (pszExternalSchemaLocation)
920 {
921 // set validation related features & properties
922 Assert (builder->canSetFeature (XMLUni::fgDOMValidation, true));
923 if (builder->canSetFeature (XMLUni::fgDOMValidation, true))
924 builder->setFeature (XMLUni::fgDOMValidation, true);
925 else
926 return VERR_NOT_SUPPORTED;
927 Assert (builder->canSetFeature (XMLUni::fgXercesSchema, true));
928 if (builder->canSetFeature (XMLUni::fgXercesSchema, true))
929 builder->setFeature (XMLUni::fgXercesSchema, true);
930 else
931 return VERR_NOT_SUPPORTED;
932 Assert (builder->canSetFeature (XMLUni::fgXercesSchemaFullChecking, true));
933 if (builder->canSetFeature (XMLUni::fgXercesSchemaFullChecking, true))
934 builder->setFeature (XMLUni::fgXercesSchemaFullChecking, true);
935 else
936 return VERR_NOT_SUPPORTED;
937
938 PRTUTF16 pwszExternalSchemaLocation = NULL;
939 rc = RTStrToUtf16 (pszExternalSchemaLocation, &pwszExternalSchemaLocation);
940 if (VBOX_FAILURE (rc))
941 return rc;
942
943 if (bDoNamespaces)
944 {
945 // set schema that supports namespaces
946 builder->setProperty (XMLUni::fgXercesSchemaExternalSchemaLocation,
947 pwszExternalSchemaLocation);
948 }
949 else
950 {
951 // set schema that doesn't support namespaces
952 builder->setProperty (XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation,
953 pwszExternalSchemaLocation);
954 }
955
956 RTUtf16Free (pwszExternalSchemaLocation);
957 }
958
959 hOriginalFileHandle = hFileHandle;
960 rc = RTStrToUtf16 (pszFileName, &pwszOriginalFilename);
961 if (VBOX_FAILURE (rc))
962 return rc;
963
964 CfgLdrEntityResolver entityResolver (pfnEntityResolver);
965 builder->setEntityResolver (&entityResolver);
966
967 CfgLdrErrorHandler errorHandler;
968 builder->setErrorHandler (&errorHandler);
969
970 try
971 {
972 if (hFileHandle != NIL_RTFILE)
973 {
974 CFGLDRENTITY entity;
975 entity.enmType = CFGLDRENTITYTYPE_HANDLE;
976 entity.u.handle.hFile = hFileHandle;
977 entity.u.handle.bClose = false;
978 CfgLdrInputSource source (&entity, pszFileName);
979 root = builder->parse (source);
980 }
981 else
982 {
983 root = builder->parseURI (pwszOriginalFilename);
984 }
985 }
986 catch (...)
987 {
988 rc = VERR_OPEN_FAILED;
989 }
990
991 if (errorHandler.hasErrors())
992 {
993 // this will transfer ownership of the string
994 if (ppszErrorMessage)
995 *ppszErrorMessage = errorHandler.takeErrorMessage();
996 rc = VERR_OPEN_FAILED;
997 }
998
999 builder->setErrorHandler (NULL);
1000 builder->setEntityResolver (NULL);
1001
1002 return rc;
1003}
1004
1005int CfgLoader::Save (const char *pszFilename, RTFILE hFileHandle,
1006 char **ppszErrorMessage)
1007{
1008 if (!pszFilename && !pwszOriginalFilename &&
1009 hFileHandle == NIL_RTFILE && hOriginalFileHandle == NIL_RTFILE)
1010 {
1011 // no explicit handle/filename specified and the configuration
1012 // was created from scratch
1013 return VERR_INVALID_PARAMETER;
1014 }
1015
1016 static const XMLCh LS[] = { chLatin_L, chLatin_S, chNull };
1017 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation (LS);
1018 if (!impl)
1019 return VERR_NOT_SUPPORTED;
1020
1021 DOMWriter *writer = static_cast <DOMImplementationLS *> (impl)->createDOMWriter();
1022 if (!writer)
1023 return VERR_NOT_SUPPORTED;
1024
1025 int rc = VINF_SUCCESS;
1026
1027 if (ppszErrorMessage)
1028 *ppszErrorMessage = NULL;
1029
1030#ifdef VBOX_XML_WRITER_FILTER
1031 VBoxWriterFilter theFilter (DOMNodeFilter::SHOW_TEXT);
1032 writer->setFilter (&theFilter);
1033#endif
1034
1035 writer->setEncoding (XMLUni::fgUTF8EncodingString);
1036
1037 // set feature if the serializer supports the feature/mode
1038 if (writer->canSetFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true))
1039 writer->setFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true);
1040 if (writer->canSetFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true))
1041 writer->setFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true);
1042
1043 CfgLdrErrorHandler errorHandler;
1044 writer->setErrorHandler (&errorHandler);
1045
1046 try
1047 {
1048 if (hFileHandle != NIL_RTFILE || hOriginalFileHandle != NIL_RTFILE)
1049 {
1050 CFGLDRENTITY entity;
1051 entity.enmType = CFGLDRENTITYTYPE_HANDLE;
1052 entity.u.handle.hFile = hFileHandle != NIL_RTFILE ? hFileHandle :
1053 hOriginalFileHandle;
1054 entity.u.handle.bClose = false;
1055 CfgLdrFormatTarget target (&entity);
1056 writer->writeNode (&target, *root);
1057 }
1058 else
1059 {
1060 PRTUTF16 pwszFilename = NULL;
1061 if (pszFilename)
1062 rc = RTStrToUtf16 (pszFilename, &pwszFilename);
1063 if (VBOX_SUCCESS (rc))
1064 {
1065 LocalFileFormatTarget target (pwszFilename ? pwszFilename :
1066 pwszOriginalFilename);
1067 if (pwszFilename)
1068 RTUtf16Free (pwszFilename);
1069
1070 writer->writeNode (&target, *root);
1071 }
1072 }
1073 }
1074 catch(...)
1075 {
1076 rc = VERR_FILE_IO_ERROR;
1077 }
1078
1079 if (errorHandler.hasErrors())
1080 {
1081 // this will transfer ownership of the string
1082 if (ppszErrorMessage)
1083 *ppszErrorMessage = errorHandler.takeErrorMessage();
1084 rc = VERR_FILE_IO_ERROR;
1085 }
1086
1087 writer->release();
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