VirtualBox

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

Last change on this file since 787 was 123, checked in by vboxsync, 18 years ago

64-bit (I wonder why the old code was so complicated, hope it's not to work around some peculiar problem...)

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