VirtualBox

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

Last change on this file since 1217 was 1128, checked in by vboxsync, 18 years ago

Main/CFGLDR: Fixed: Don't delete DOM objects with |delete| but always use |release()| instead.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.7 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 poldtext->release();
1668 }
1669 else
1670 {
1671 pdomnode->appendChild (val);
1672 }
1673 }
1674
1675 catch (...)
1676 {
1677 rc = VERR_CFG_NO_VALUE;
1678 val->release();
1679 }
1680 }
1681 }
1682 else
1683 {
1684 PRTUTF16 pwszName = NULL;
1685
1686 rc = RTStrToUtf16 (pszName, &pwszName);
1687
1688 if (VBOX_SUCCESS(rc))
1689 {
1690 try
1691 {
1692 static_cast<DOMElement *>(pdomnode)->setAttribute (pwszName, pwszValue);
1693 }
1694
1695 catch (...)
1696 {
1697 rc = VERR_CFG_NO_VALUE;
1698 }
1699 }
1700 }
1701
1702 return rc;
1703}
1704
1705int CfgNode::QueryUInt32 (const char *pszName, uint32_t *pulValue)
1706{
1707 int rc = VINF_SUCCESS;
1708
1709 PRTUTF16 pwszValue = NULL;
1710
1711 rc = queryValueString (pszName, &pwszValue);
1712
1713 if (VBOX_SUCCESS(rc))
1714 {
1715 uint32_t value = 0;
1716
1717 rc = cfgldrhlp_strtouint32 (pwszValue, &value);
1718
1719 if (VBOX_SUCCESS(rc))
1720 {
1721 *pulValue = value;
1722 }
1723 }
1724
1725 return rc;
1726}
1727int CfgNode::SetUInt32 (const char *pszName, uint32_t ulValue)
1728{
1729 int rc = VINF_SUCCESS;
1730
1731 char szValue[64];
1732
1733 rc = cfgldrhlp_uint32tostr (ulValue, szValue);
1734
1735 if (VBOX_SUCCESS (rc))
1736 {
1737 PRTUTF16 pwszValue = NULL;
1738
1739 rc = RTStrToUtf16 (szValue, &pwszValue);
1740
1741 if (VBOX_SUCCESS (rc))
1742 {
1743 rc = setValueString (pszName, pwszValue);
1744
1745 RTUtf16Free (pwszValue);
1746 }
1747 }
1748
1749 return rc;
1750}
1751int CfgNode::QueryUInt64 (const char *pszName, uint64_t *pullValue)
1752{
1753 int rc = VINF_SUCCESS;
1754
1755 PRTUTF16 pwszValue = NULL;
1756
1757 rc = queryValueString (pszName, &pwszValue);
1758
1759 if (VBOX_SUCCESS(rc))
1760 {
1761 uint64_t value = 0;
1762
1763 rc = cfgldrhlp_strtouint64 (pwszValue, &value);
1764
1765 if (VBOX_SUCCESS(rc))
1766 {
1767 *pullValue = value;
1768 }
1769 }
1770
1771 return rc;
1772}
1773int CfgNode::SetUInt64 (const char *pszName, uint64_t ullValue)
1774{
1775 int rc = VINF_SUCCESS;
1776
1777 char szValue[64];
1778
1779 rc = cfgldrhlp_uint64tostr (ullValue, szValue);
1780
1781 if (VBOX_SUCCESS (rc))
1782 {
1783 PRTUTF16 pwszValue = NULL;
1784
1785 rc = RTStrToUtf16 (szValue, &pwszValue);
1786
1787 if (VBOX_SUCCESS (rc))
1788 {
1789 rc = setValueString (pszName, pwszValue);
1790
1791 RTUtf16Free (pwszValue);
1792 }
1793 }
1794
1795 return rc;
1796}
1797
1798int CfgNode::QueryInt32 (const char *pszName, int32_t *pint32Value)
1799{
1800 PRTUTF16 pwszValue = NULL;
1801
1802 int rc = queryValueString (pszName, &pwszValue);
1803
1804 if (VBOX_SUCCESS(rc))
1805 {
1806 rc = cfgldrhlp_ustr_to_integer<int32_t, uint32_t> (pwszValue, pint32Value);
1807 }
1808
1809 return rc;
1810}
1811
1812int CfgNode::SetInt32 (const char *pszName, int32_t int32Value)
1813{
1814 PRTUTF16 pwszValue = NULL;
1815
1816 int rc = cfgldrhlp_integer_to_ustr<int32_t, uint32_t> (int32Value, &pwszValue);
1817
1818 if (VBOX_SUCCESS(rc))
1819 {
1820 rc = setValueString (pszName, pwszValue);
1821
1822 cfgldrhlp_release_ustr (pwszValue);
1823 }
1824
1825 return rc;
1826}
1827
1828int CfgNode::QueryUInt16 (const char *pszName, uint16_t *pu16Value)
1829{
1830 PRTUTF16 pwszValue = NULL;
1831
1832 int rc = queryValueString (pszName, &pwszValue);
1833
1834 if (VBOX_SUCCESS(rc))
1835 {
1836 rc = cfgldrhlp_ustr_to_uinteger<uint16_t> (pwszValue, pu16Value);
1837 }
1838
1839 return rc;
1840}
1841
1842int CfgNode::SetUInt16 (const char *pszName, uint16_t u16Value)
1843{
1844 PRTUTF16 pwszValue = NULL;
1845
1846 int rc = cfgldrhlp_uinteger_to_ustr<uint16_t> (u16Value, &pwszValue);
1847
1848 if (VBOX_SUCCESS(rc))
1849 {
1850 rc = setValueString (pszName, pwszValue);
1851
1852 cfgldrhlp_release_ustr (pwszValue);
1853 }
1854
1855 return rc;
1856}
1857
1858int CfgNode::QueryBin (const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue)
1859{
1860 int rc = VINF_SUCCESS;
1861
1862 PRTUTF16 pwszValue = NULL;
1863
1864 rc = queryValueString (pszName, &pwszValue);
1865
1866 if (VBOX_SUCCESS(rc))
1867 {
1868 if ( (XMLString::stringLen (pwszValue) / 2) > cbValue)
1869 {
1870 rc = VERR_BUFFER_OVERFLOW;
1871 }
1872 else if (!pvValue)
1873 {
1874 rc = VERR_INVALID_POINTER;
1875 }
1876 else
1877 {
1878 rc = cfgldrhlp_strtobin (pwszValue, pvValue, cbValue, pcbValue);
1879 }
1880 }
1881
1882 return rc;
1883}
1884int CfgNode::SetBin (const char *pszName, const void *pvValue, unsigned cbValue)
1885{
1886 int rc = VINF_SUCCESS;
1887
1888 char *pszValue = NULL;
1889
1890 rc = cfgldrhlp_bintostr (pvValue, cbValue, &pszValue);
1891
1892 if (VBOX_SUCCESS (rc))
1893 {
1894 PRTUTF16 pwszValue = NULL;
1895
1896 rc = RTStrToUtf16 (pszValue, &pwszValue);
1897
1898 if (VBOX_SUCCESS (rc))
1899 {
1900 rc = setValueString (pszName, pwszValue);
1901
1902 RTUtf16Free (pwszValue);
1903 }
1904
1905 cfgldrhlp_releasestr (pszValue);
1906 }
1907
1908 return rc;
1909}
1910int CfgNode::QueryString (const char *pszName, void **pValue, unsigned cbValue, unsigned *pcbValue, bool returnUtf16)
1911{
1912 int rc = VINF_SUCCESS;
1913
1914 PRTUTF16 pwszValue = NULL;
1915
1916 // start with 0 bytes return value
1917 if (pcbValue)
1918 *pcbValue = 0;
1919
1920 rc = queryValueString (pszName, &pwszValue);
1921
1922 if (VBOX_SUCCESS(rc))
1923 {
1924 if (returnUtf16)
1925 {
1926 // make a copy
1927 *pValue = (void*)SysAllocString((const OLECHAR*)pwszValue);
1928 } else
1929 {
1930 char *psz = NULL;
1931
1932 rc = RTUtf16ToUtf8 (pwszValue, &psz);
1933
1934 if (VBOX_SUCCESS(rc))
1935 {
1936 unsigned l = strlen (psz) + 1; // take trailing nul to account
1937
1938 *pcbValue = l;
1939
1940 if (l > cbValue)
1941 {
1942 rc = VERR_BUFFER_OVERFLOW;
1943 }
1944 else if (!*pValue)
1945 {
1946 rc = VERR_INVALID_POINTER;
1947 }
1948 else
1949 {
1950 memcpy (*pValue, psz, l);
1951 }
1952
1953 RTStrFree (psz);
1954 }
1955 }
1956 }
1957 return rc;
1958}
1959
1960int CfgNode::SetString (const char *pszName, const char *pszValue, unsigned cbValue, bool isUtf16)
1961{
1962 int rc = VINF_SUCCESS;
1963
1964 PRTUTF16 pwszValue = NULL;
1965
1966 if (isUtf16)
1967 pwszValue = (PRTUTF16)pszValue;
1968 else
1969 rc = RTStrToUtf16 (pszValue, &pwszValue);
1970
1971 if (VBOX_SUCCESS (rc))
1972 {
1973 rc = setValueString (pszName, pwszValue);
1974
1975 if (!isUtf16)
1976 RTUtf16Free (pwszValue);
1977 }
1978
1979 return rc;
1980}
1981
1982int CfgNode::QueryBool (const char *pszName, bool *pfValue)
1983{
1984 int rc = VINF_SUCCESS;
1985
1986 PRTUTF16 pwszValue = NULL;
1987 rc = queryValueString (pszName, &pwszValue);
1988 if (VBOX_SUCCESS (rc))
1989 {
1990 char *pszValue = NULL;
1991 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
1992 if (VBOX_SUCCESS (rc))
1993 {
1994 if ( !stricmp (pszValue, "true")
1995 || !stricmp (pszValue, "yes")
1996 || !stricmp (pszValue, "on"))
1997 *pfValue = true;
1998 else if ( !stricmp (pszValue, "false")
1999 || !stricmp (pszValue, "no")
2000 || !stricmp (pszValue, "off"))
2001 *pfValue = false;
2002 else
2003 rc = VERR_CFG_INVALID_FORMAT;
2004 RTStrFree (pszValue);
2005 }
2006 }
2007
2008 return rc;
2009}
2010
2011int CfgNode::SetBool (const char *pszName, bool fValue)
2012{
2013 return SetString (pszName, fValue ? "true" : "false", fValue ? 4 : 5, false);
2014}
2015
2016int CfgNode::DeleteAttribute (const char *pszName)
2017{
2018 int rc = VINF_SUCCESS;
2019
2020 PRTUTF16 pwszName = NULL;
2021
2022 rc = RTStrToUtf16 (pszName, &pwszName);
2023
2024 if (VBOX_SUCCESS (rc))
2025 {
2026 try
2027 {
2028 (static_cast<DOMElement *>(pdomnode))->removeAttribute (pwszName);
2029 }
2030
2031 catch (...)
2032 {
2033 rc = VERR_CFG_NO_VALUE;
2034 }
2035
2036 RTUtf16Free (pwszName);
2037 }
2038
2039 return rc;
2040}
2041
2042/* Configuration loader public entry points.
2043 */
2044
2045CFGLDRR3DECL(int) CFGLDRCreate(CFGHANDLE *phcfg)
2046{
2047 if (!phcfg)
2048 {
2049 return VERR_INVALID_POINTER;
2050 }
2051
2052 CfgLoader *pcfgldr = new CfgLoader ();
2053
2054 if (!pcfgldr)
2055 {
2056 return VERR_NO_MEMORY;
2057 }
2058
2059 int rc = pcfgldr->Create();
2060
2061 if (VBOX_SUCCESS(rc))
2062 {
2063 *phcfg = pcfgldr;
2064 }
2065 else
2066 {
2067 delete pcfgldr;
2068 }
2069
2070 return rc;
2071}
2072
2073CFGLDRR3DECL(int) CFGLDRLoad (CFGHANDLE *phcfg,
2074 const char *pszFileName, RTFILE hFileHandle,
2075 const char *pszExternalSchemaLocation, bool bDoNamespaces,
2076 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
2077 char **ppszErrorMessage)
2078{
2079 if (!phcfg || !pszFileName)
2080 return VERR_INVALID_POINTER;
2081
2082 CfgLoader *pcfgldr = new CfgLoader();
2083 if (!pcfgldr)
2084 return VERR_NO_MEMORY;
2085
2086 int rc = pcfgldr->Load (pszFileName, hFileHandle,
2087 pszExternalSchemaLocation, bDoNamespaces,
2088 pfnEntityResolver, ppszErrorMessage);
2089
2090 if (VBOX_SUCCESS(rc))
2091 *phcfg = pcfgldr;
2092 else
2093 delete pcfgldr;
2094
2095 return rc;
2096}
2097
2098CFGLDRR3DECL(int) CFGLDRFree(CFGHANDLE hcfg)
2099{
2100 if (!hcfg)
2101 {
2102 return VERR_INVALID_HANDLE;
2103 }
2104
2105 CfgLoader::FreeConfiguration (hcfg);
2106
2107 return VINF_SUCCESS;
2108}
2109
2110CFGLDRR3DECL(int) CFGLDRSave(CFGHANDLE hcfg,
2111 char **ppszErrorMessage)
2112{
2113 if (!hcfg)
2114 {
2115 return VERR_INVALID_HANDLE;
2116 }
2117 return hcfg->Save (NULL, NIL_RTFILE, ppszErrorMessage);
2118}
2119
2120CFGLDRR3DECL(int) CFGLDRSaveAs(CFGHANDLE hcfg,
2121 const char *pszFilename, RTFILE hFileHandle,
2122 char **ppszErrorMessage)
2123{
2124 if (!hcfg)
2125 {
2126 return VERR_INVALID_HANDLE;
2127 }
2128 if (!pszFilename)
2129 {
2130 return VERR_INVALID_POINTER;
2131 }
2132 return hcfg->Save (pszFilename, hFileHandle, ppszErrorMessage);
2133}
2134
2135CFGLDRR3DECL(int) CFGLDRTransform (CFGHANDLE hcfg,
2136 const char *pszTemlateLocation,
2137 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
2138 char **ppszErrorMessage)
2139{
2140#ifdef VBOX_XML_XSLT
2141 if (!hcfg)
2142 {
2143 return VERR_INVALID_HANDLE;
2144 }
2145 if (!pszTemlateLocation)
2146 {
2147 return VERR_INVALID_POINTER;
2148 }
2149 return hcfg->Transform (pszTemlateLocation, pfnEntityResolver, ppszErrorMessage);
2150#else
2151 return VERR_NOT_SUPPORTED;
2152#endif
2153}
2154
2155CFGLDRR3DECL(int) CFGLDRGetNode(CFGHANDLE hcfg, const char *pszName, unsigned uIndex, CFGNODE *phnode)
2156{
2157 if (!hcfg)
2158 {
2159 return VERR_INVALID_HANDLE;
2160 }
2161 if (!phnode)
2162 {
2163 return VERR_INVALID_POINTER;
2164 }
2165 return hcfg->GetNode (pszName, uIndex, phnode);
2166}
2167
2168CFGLDRR3DECL(int) CFGLDRGetChildNode(CFGNODE hparent, const char *pszName, unsigned uIndex, CFGNODE *phnode)
2169{
2170 if (!hparent)
2171 {
2172 return VERR_INVALID_HANDLE;
2173 }
2174 if (!phnode)
2175 {
2176 return VERR_INVALID_POINTER;
2177 }
2178 return hparent->GetChild (pszName, uIndex, phnode);
2179}
2180
2181CFGLDRR3DECL(int) CFGLDRCreateNode(CFGHANDLE hcfg, const char *pszName, CFGNODE *phnode)
2182{
2183 if (!hcfg)
2184 {
2185 return VERR_INVALID_HANDLE;
2186 }
2187 if (!phnode || !pszName)
2188 {
2189 return VERR_INVALID_POINTER;
2190 }
2191 return hcfg->CreateNode (pszName, phnode);
2192}
2193
2194CFGLDRR3DECL(int) CFGLDRCreateChildNode(CFGNODE hparent, const char *pszName, CFGNODE *phnode)
2195{
2196 if (!hparent)
2197 {
2198 return VERR_INVALID_HANDLE;
2199 }
2200 if (!phnode || !pszName)
2201 {
2202 return VERR_INVALID_POINTER;
2203 }
2204 return hparent->CreateChildNode (pszName, phnode);
2205}
2206
2207CFGLDRR3DECL(int) CFGLDRAppendChildNode(CFGNODE hparent, const char *pszName, CFGNODE *phnode)
2208{
2209 if (!hparent)
2210 {
2211 return VERR_INVALID_HANDLE;
2212 }
2213 if (!phnode || !pszName)
2214 {
2215 return VERR_INVALID_POINTER;
2216 }
2217 return hparent->AppendChildNode (pszName, phnode);
2218}
2219
2220CFGLDRR3DECL(int) CFGLDRReleaseNode(CFGNODE hnode)
2221{
2222 if (!hnode)
2223 {
2224 return VERR_INVALID_HANDLE;
2225 }
2226 return CfgNode::ReleaseNode (hnode);
2227}
2228
2229CFGLDRR3DECL(int) CFGLDRDeleteNode(CFGNODE hnode)
2230{
2231 if (!hnode)
2232 {
2233 return VERR_INVALID_HANDLE;
2234 }
2235 return CfgNode::DeleteNode (hnode);
2236}
2237
2238CFGLDRR3DECL(int) CFGLDRCountChildren(CFGNODE hnode, const char *pszChildName, unsigned *pCount)
2239{
2240 if (!hnode)
2241 {
2242 return VERR_INVALID_HANDLE;
2243 }
2244 if (!pCount)
2245 {
2246 return VERR_INVALID_POINTER;
2247 }
2248 return hnode->CountChildren (pszChildName, pCount);
2249}
2250
2251CFGLDRR3DECL(int) CFGLDRQueryUInt32(CFGNODE hnode, const char *pszName, uint32_t *pulValue)
2252{
2253 if (!hnode)
2254 {
2255 return VERR_INVALID_HANDLE;
2256 }
2257 if (!pulValue)
2258 {
2259 return VERR_INVALID_POINTER;
2260 }
2261 return hnode->QueryUInt32 (pszName, pulValue);
2262}
2263
2264CFGLDRR3DECL(int) CFGLDRSetUInt32(CFGNODE hnode, const char *pszName, uint32_t ulValue)
2265{
2266 if (!hnode)
2267 {
2268 return VERR_INVALID_HANDLE;
2269 }
2270 return hnode->SetUInt32 (pszName, ulValue);
2271}
2272
2273CFGLDRR3DECL(int) CFGLDRQueryUInt64(CFGNODE hnode, const char *pszName, uint64_t *pullValue)
2274{
2275 if (!hnode)
2276 {
2277 return VERR_INVALID_HANDLE;
2278 }
2279 if (!pullValue)
2280 {
2281 return VERR_INVALID_POINTER;
2282 }
2283 return hnode->QueryUInt64 (pszName, pullValue);
2284}
2285
2286CFGLDRR3DECL(int) CFGLDRSetUInt64(CFGNODE hnode, const char *pszName, uint64_t ullValue)
2287{
2288 if (!hnode)
2289 {
2290 return VERR_INVALID_HANDLE;
2291 }
2292 return hnode->SetUInt64 (pszName, ullValue);
2293}
2294
2295CFGLDRR3DECL(int) CFGLDRQueryInt32(CFGNODE hnode, const char *pszName, int32_t *pint32Value)
2296{
2297 if (!hnode)
2298 {
2299 return VERR_INVALID_HANDLE;
2300 }
2301 return hnode->QueryInt32 (pszName, pint32Value);
2302}
2303
2304CFGLDRR3DECL(int) CFGLDRSetInt32(CFGNODE hnode, const char *pszName, int32_t int32Value)
2305{
2306 if (!hnode)
2307 {
2308 return VERR_INVALID_HANDLE;
2309 }
2310 return hnode->SetInt32 (pszName, int32Value);
2311}
2312
2313
2314CFGLDRR3DECL(int) CFGLDRQueryUInt16(CFGNODE hnode, const char *pszName, uint16_t *pu16Value)
2315{
2316 if (!hnode)
2317 {
2318 return VERR_INVALID_HANDLE;
2319 }
2320 if (!pu16Value)
2321 {
2322 return VERR_INVALID_POINTER;
2323 }
2324 return hnode->QueryUInt16 (pszName, pu16Value);
2325}
2326
2327CFGLDRR3DECL(int) CFGLDRSetUInt16(CFGNODE hnode, const char *pszName, uint16_t u16Value)
2328{
2329 if (!hnode)
2330 {
2331 return VERR_INVALID_HANDLE;
2332 }
2333 return hnode->SetUInt16 (pszName, u16Value);
2334}
2335
2336CFGLDRR3DECL(int) CFGLDRQueryBin(CFGNODE hnode, const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue)
2337{
2338 if (!hnode)
2339 {
2340 return VERR_INVALID_HANDLE;
2341 }
2342 if (!pcbValue)
2343 {
2344 return VERR_INVALID_POINTER;
2345 }
2346 return hnode->QueryBin (pszName, pvValue, cbValue, pcbValue);
2347}
2348
2349CFGLDRR3DECL(int) CFGLDRSetBin(CFGNODE hnode, const char *pszName, void *pvValue, unsigned cbValue)
2350{
2351 if (!hnode)
2352 {
2353 return VERR_INVALID_HANDLE;
2354 }
2355 if (!pvValue)
2356 {
2357 return VERR_INVALID_POINTER;
2358 }
2359 return hnode->SetBin (pszName, pvValue, cbValue);
2360}
2361
2362CFGLDRR3DECL(int) CFGLDRQueryString(CFGNODE hnode, const char *pszName, char *pszValue, unsigned cbValue, unsigned *pcbValue)
2363{
2364 if (!hnode)
2365 {
2366 return VERR_INVALID_HANDLE;
2367 }
2368 if (!pcbValue)
2369 {
2370 return VERR_INVALID_POINTER;
2371 }
2372 return hnode->QueryString (pszName, (void**)&pszValue, cbValue, pcbValue, false);
2373}
2374
2375CFGLDRR3DECL(int) CFGLDRQueryBSTR(CFGNODE hnode, const char *pszName, BSTR *ppwszValue)
2376{
2377 if (!hnode)
2378 {
2379 return VERR_INVALID_HANDLE;
2380 }
2381 if (!ppwszValue)
2382 {
2383 return VERR_INVALID_POINTER;
2384 }
2385 return hnode->QueryString(pszName, (void**)ppwszValue, 0, NULL, true);
2386}
2387
2388CFGLDRR3DECL(int) CFGLDRQueryUUID(CFGNODE hnode, const char *pszName, PRTUUID pUUID)
2389{
2390 if (!hnode)
2391 {
2392 return VERR_INVALID_HANDLE;
2393 }
2394 if (!pUUID)
2395 {
2396 return VERR_INVALID_POINTER;
2397 }
2398
2399 // we need it as UTF8
2400 unsigned size;
2401 int rc;
2402 rc = CFGLDRQueryString(hnode, pszName, NULL, 0, &size);
2403 if (rc == VERR_BUFFER_OVERFLOW)
2404 {
2405 char *uuidUtf8 = new char[size];
2406 rc = CFGLDRQueryString(hnode, pszName, uuidUtf8, size, &size);
2407 if (VBOX_SUCCESS(rc))
2408 {
2409 // remove the curly brackets
2410 uuidUtf8[strlen(uuidUtf8) - 1] = '\0';
2411 rc = RTUuidFromStr(pUUID, &uuidUtf8[1]);
2412 }
2413 delete[] uuidUtf8;
2414 }
2415 return rc;
2416}
2417
2418CFGLDRR3DECL(int) CFGLDRSetUUID(CFGNODE hnode, const char *pszName, PCRTUUID pUuid)
2419{
2420 if (!hnode)
2421 {
2422 return VERR_INVALID_HANDLE;
2423 }
2424 if (!pUuid)
2425 {
2426 return VERR_INVALID_HANDLE;
2427 }
2428
2429 // UUID + curly brackets
2430 char strUuid[RTUUID_STR_LENGTH + 2 * sizeof(char)];
2431 strUuid[0] = '{';
2432 RTUuidToStr((const PRTUUID)pUuid, &strUuid[1], RTUUID_STR_LENGTH);
2433 strcat(strUuid, "}");
2434 return hnode->SetString (pszName, strUuid, strlen (strUuid), false);
2435}
2436
2437CFGLDRR3DECL(int) CFGLDRSetString(CFGNODE hnode, const char *pszName, const char *pszValue)
2438{
2439 if (!hnode)
2440 {
2441 return VERR_INVALID_HANDLE;
2442 }
2443 if (!pszValue)
2444 {
2445 return VERR_INVALID_POINTER;
2446 }
2447 return hnode->SetString (pszName, pszValue, strlen (pszValue), false);
2448}
2449
2450CFGLDRR3DECL(int) CFGLDRSetBSTR(CFGNODE hnode, const char *pszName, const BSTR bstrValue)
2451{
2452 if (!hnode)
2453 {
2454 return VERR_INVALID_HANDLE;
2455 }
2456 if (!bstrValue)
2457 {
2458 return VERR_INVALID_POINTER;
2459 }
2460 return hnode->SetString (pszName, (char*)bstrValue, RTUtf16Len((PCRTUTF16)bstrValue), true);
2461}
2462
2463CFGLDRR3DECL(int) CFGLDRQueryBool(CFGNODE hnode, const char *pszName, bool *pfValue)
2464{
2465 if (!hnode)
2466 {
2467 return VERR_INVALID_HANDLE;
2468 }
2469 if (!pfValue)
2470 {
2471 return VERR_INVALID_POINTER;
2472 }
2473
2474 return hnode->QueryBool (pszName, pfValue);
2475}
2476
2477CFGLDRR3DECL(int) CFGLDRSetBool(CFGNODE hnode, const char *pszName, bool fValue)
2478{
2479 if (!hnode)
2480 {
2481 return VERR_INVALID_HANDLE;
2482 }
2483
2484 return hnode->SetBool (pszName, fValue);
2485}
2486
2487CFGLDRR3DECL(int) CFGLDRQueryDateTime(CFGNODE hnode, const char *pszName, int64_t *pi64Value)
2488{
2489 if (!hnode)
2490 {
2491 return VERR_INVALID_HANDLE;
2492 }
2493 if (!pi64Value)
2494 {
2495 return VERR_INVALID_POINTER;
2496 }
2497
2498 // query as UTF8 string
2499 unsigned size = 0;
2500 int rc = CFGLDRQueryString(hnode, pszName, NULL, 0, &size);
2501 if (rc != VERR_BUFFER_OVERFLOW)
2502 return rc;
2503
2504 char *pszValue = new char[size];
2505 char *pszBuf = new char[size];
2506 rc = CFGLDRQueryString(hnode, pszName, pszValue, size, &size);
2507 if (VBOX_SUCCESS(rc)) do
2508 {
2509 // Parse xsd:dateTime. The format is:
2510 // '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
2511 // where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z'
2512 uint32_t yyyy = 0;
2513 uint16_t mm = 0, dd = 0, hh = 0, mi = 0, ss = 0;
2514 if (sscanf(pszValue, "%d-%hu-%huT%hu:%hu:%hu%s",
2515 &yyyy, &mm, &dd, &hh, &mi, &ss, pszBuf) != 7)
2516 {
2517 rc = VERR_PARSE_ERROR;
2518 break;
2519 }
2520
2521 // currently, we accept only the UTC timezone ('Z'),
2522 // ignoring fractional seconds, if present
2523 if (pszBuf[0] == 'Z' ||
2524 (pszBuf[0] == '.' && pszBuf[strlen(pszBuf)-1] == 'Z'))
2525 {
2526 // start with an error
2527 rc = VERR_PARSE_ERROR;
2528
2529#if 0
2530 RTTIME time = { yyyy, mm, 0, 0, dd, hh, mm, ss, 0,
2531 RTTIME_FLAGS_TYPE_UTC };
2532 if (RTTimeNormalize(&time))
2533 {
2534 RTTIMESPEC timeSpec;
2535 if (RTTimeImplode(&time, &timeSpec))
2536 {
2537 *pi64Value = RTTimeSpecGetMilli(&timeSpec);
2538 rc = VINF_SUCCESS;
2539 }
2540 }
2541#else
2542 /// @todo (dmik) until RTTimeImplode and friends are done
2543 int isdst = 0;
2544 {
2545 time_t dummyt = time(NULL);
2546 isdst = localtime(&dummyt)->tm_isdst;
2547 }
2548 tm time;
2549 time.tm_hour = hh; /* hour (0 - 23) */
2550 time.tm_isdst = isdst; /* daylight saving time enabled/disabled */
2551 time.tm_mday = dd; /* day of month (1 - 31) */
2552 time.tm_min = mi; /* minutes (0 - 59) */
2553 time.tm_mon = mm - 1; /* month (0 - 11 : 0 = January) */
2554 time.tm_sec = ss; /* seconds (0 - 59) */
2555 time.tm_wday = 0; /* Day of week (0 - 6 : 0 = Sunday) */
2556 time.tm_yday = 0; /* Day of year (0 - 365) */
2557 time.tm_year = yyyy - 1900; /* Year less 1900 */
2558 time_t t = mktime(&time);
2559 // mktime expects local time, but we supply it UTC,
2560 // do a trick to get the right time value
2561 tm *dummytm = gmtime(&t);
2562 dummytm->tm_isdst = isdst;
2563 time_t delta = t - mktime(dummytm);
2564 *pi64Value = t + delta;
2565 *pi64Value *= 1000;
2566 rc = VINF_SUCCESS;
2567#endif
2568 }
2569 else
2570 rc = VERR_PARSE_ERROR;
2571 }
2572 while (0);
2573
2574 delete[] pszBuf;
2575 delete[] pszValue;
2576
2577 return rc;
2578}
2579
2580CFGLDRR3DECL(int) CFGLDRSetDateTime(CFGNODE hnode, const char *pszName, int64_t i64Value)
2581{
2582 if (!hnode)
2583 {
2584 return VERR_INVALID_HANDLE;
2585 }
2586
2587#if 0
2588 RTTIMESPEC timeSpec;
2589 RTTimeSpecSetMilli(&timeSpec, i64Value);
2590 RTTIME time;
2591 RTTimeExplode(&time, &timeSpec);
2592#else
2593 /// @todo (dmik) until RTTimeImplode and friends are done
2594 time_t t = (time_t)(i64Value / 1000);
2595 tm *ptm = gmtime(&t);
2596 if (!ptm)
2597 return VERR_PARSE_ERROR;
2598 tm time = *ptm;
2599 time.tm_year += 1900;
2600 time.tm_mon += 1;
2601#endif
2602
2603 // Store xsd:dateTime. The format is:
2604 // '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
2605 // where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z'
2606 char aszBuf [256];
2607 RTStrPrintf(aszBuf, sizeof(aszBuf),
2608 "%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
2609#if 0
2610 time.i32Year, (uint16_t) time.u8Month, (uint16_t) time.u8MonthDay,
2611 (uint16_t) time.u8Hour, (uint16_t) time.u8Minute, (uint16_t) time.u8Second);
2612#else
2613 /// @todo (dmik) until RTTimeImplode and friends are done
2614 time.tm_year, time.tm_mon, time.tm_mday,
2615 time.tm_hour, time.tm_min, time.tm_sec);
2616#endif
2617
2618 return hnode->SetString (pszName, aszBuf, strlen (aszBuf), false);
2619}
2620
2621CFGLDRR3DECL(int) CFGLDRDeleteAttribute(CFGNODE hnode, const char *pszName)
2622{
2623 if (!hnode)
2624 {
2625 return VERR_INVALID_HANDLE;
2626 }
2627 if (!pszName)
2628 {
2629 return VERR_INVALID_POINTER;
2630 }
2631 return hnode->DeleteAttribute (pszName);
2632}
2633
2634CFGLDRR3DECL(int) CFGLDRInitialize (void)
2635{
2636 int rc = VINF_SUCCESS;
2637
2638 if (!initXML ())
2639 {
2640 rc = VERR_NOT_SUPPORTED;
2641 }
2642
2643 return rc;
2644}
2645
2646CFGLDRR3DECL(void) CFGLDRShutdown (void)
2647{
2648 /// @todo delete CfgLoaders
2649 terminateXML ();
2650}
2651
2652#ifdef STANDALONE_TEST
2653
2654#include <iprt/runtime.h>
2655
2656int main(int argc, char **argv)
2657{
2658 Log(("Configuration loader standalone test\n"));
2659
2660 CFGHANDLE hcfg = 0;
2661 CFGNODE hnode = 0;
2662 unsigned count = 0;
2663 unsigned i;
2664 char *cfgFilename = "vboxcfg.xml";
2665 char *cfgFilenameSaved = "testas.xml";
2666
2667 /*
2668 * Initialize the VBox runtime without loading
2669 * the kernel support driver
2670 */
2671 int rc = RTR3Init(false);
2672 if (VBOX_FAILURE(rc))
2673 {
2674 Log(("RTInit failed %d\n", rc));
2675 goto l_exit_0;
2676 }
2677
2678 rc = CFGLDRInitialize();
2679 if (VBOX_FAILURE(rc))
2680 {
2681 Log(("Initialize failed %d\n", rc));
2682 goto l_exit_0;
2683 }
2684
2685 rc = CFGLDRLoad(&hcfg, cfgFilename, "vboxcfg.xsd");
2686 if (VBOX_FAILURE(rc))
2687 {
2688 Log(("Load failed %d\n", rc));
2689 goto l_exit_0;
2690 }
2691
2692 printf("Configuration loaded from %s\n", cfgFilename);
2693
2694 rc = CFGLDRCreateNode(hcfg, "Configuration/Something/DeviceManager/DeviceList", &hnode);
2695 if (VBOX_FAILURE(rc))
2696 {
2697 Log(("CreateNode failed %d\n", rc));
2698 goto l_exit_1;
2699 }
2700 rc = CFGLDRSetString(hnode, "GUID", "testtestte");
2701 rc = CFGLDRReleaseNode(hnode);
2702
2703 rc = CFGLDRGetNode(hcfg, "Configuration/Managers/DeviceManager/DeviceList", 0, &hnode);
2704 if (VBOX_FAILURE(rc))
2705 {
2706 Log(("GetNode failed %d\n", rc));
2707 goto l_exit_1;
2708 }
2709
2710 rc = CFGLDRCountChildren(hnode, "Device", &count);
2711 if (VBOX_FAILURE(rc))
2712 {
2713 Log(("CountChildren failed %d\n", rc));
2714 goto l_exit_2;
2715 }
2716
2717 Log(("Number of child nodes %d\n", count));
2718
2719 for (i = 0; i < count; i++)
2720 {
2721 CFGNODE hchild = 0;
2722 unsigned cbValue;
2723 char szValue[256];
2724
2725 rc = CFGLDRGetChildNode(hnode, "Device", i, &hchild);
2726 if (VBOX_FAILURE(rc))
2727 {
2728 Log(("GetChildNode failed %d\n", rc));
2729 goto l_exit_2;
2730 }
2731
2732 unsigned dummy;
2733 rc = CFGLDRCountChildren(hchild, NULL, &dummy);
2734 Log(("Number of child nodes of Device %d\n", dummy));
2735
2736 int32_t value;
2737
2738 rc = CFGLDRQueryInt32(hchild, "int", &value);
2739 Log(("Child node %d (rc = %d): int = %d\n", i, rc, value));
2740
2741 rc = CFGLDRQueryString(hchild, NULL, szValue, sizeof (szValue), &cbValue);
2742 if (VBOX_FAILURE(rc))
2743 {
2744 Log(("QueryString failed %d\n", rc));
2745 }
2746 Log(("Child node %d: (length = %d) = '%s'\n", i, cbValue, szValue));
2747
2748 rc = CFGLDRSetString(hchild, "GUID", "testtesttest");
2749 if (VBOX_FAILURE(rc))
2750 {
2751 Log(("SetString failed %d\n", rc));
2752 }
2753
2754 rc = CFGLDRDeleteAttribute(hchild, "int");
2755 Log(("Attribute delete %d (rc = %d)\n", i, rc));
2756
2757 CFGLDRSetBin(hchild, "Bin", (void *)CFGLDRSetBin, 100);
2758 CFGLDRSetInt32(hchild, "int32", 1973);
2759// CFGLDRSetUInt64(hchild, "uint64", 0x1973);
2760
2761 CFGNODE hnew = 0;
2762 CFGLDRCreateChildNode(hchild, "testnode", &hnew);
2763 rc = CFGLDRSetString(hchild, NULL, "her");
2764 if (VBOX_FAILURE(rc))
2765 {
2766 Log(("_SetString failed %d\n", rc));
2767 }
2768 rc = CFGLDRSetString(hnew, NULL, "neher");
2769 if (VBOX_FAILURE(rc))
2770 {
2771 Log(("+SetString failed %d\n", rc));
2772 }
2773 CFGLDRReleaseNode(hchild);
2774 }
2775
2776 rc = CFGLDRSaveAs(hcfg, cfgFilenameSaved);
2777 if (VBOX_FAILURE(rc))
2778 {
2779 Log(("SaveAs failed %d\n", rc));
2780 goto l_exit_2;
2781 }
2782
2783 Log(("Configuration saved as %s\n", cfgFilenameSaved));
2784
2785l_exit_2:
2786
2787 rc = CFGLDRReleaseNode(hnode);
2788 if (VBOX_FAILURE(rc))
2789 {
2790 Log(("ReleaseNode failed %d\n", rc));
2791 }
2792
2793l_exit_1:
2794
2795 rc = CFGLDRFree(hcfg);
2796 if (VBOX_FAILURE(rc))
2797 {
2798 Log(("Load failed %d\n", rc));
2799 }
2800
2801l_exit_0:
2802
2803 CFGLDRShutdown();
2804
2805 Log(("Test completed."));
2806 return rc;
2807}
2808#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