VirtualBox

source: vbox/trunk/src/VBox/Main/ExtPackUtil.cpp@ 34373

Last change on this file since 34373 was 34307, checked in by vboxsync, 14 years ago

ExtPack: Correct file headers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.8 KB
Line 
1/* $Id: ExtPackUtil.cpp 34307 2010-11-24 10:56:41Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Extension Pack Utilities and definitions, VBoxC, VBoxSVC, ++.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "include/ExtPackUtil.h"
23
24#include <iprt/ctype.h>
25#include <iprt/dir.h>
26#include <iprt/file.h>
27#include <iprt/param.h>
28#include <iprt/path.h>
29#include <iprt/string.h>
30#include <iprt/cpp/xml.h>
31
32#include <VBox/log.h>
33
34
35/**
36 * Worker for VBoxExtPackLoadDesc that loads the plug-in descriptors.
37 *
38 * @returns Same as VBoxExtPackLoadDesc.
39 * @param pVBoxExtPackElm
40 * @param pcPlugIns Where to return the number of plug-ins in the
41 * array.
42 * @param paPlugIns Where to return the plug-in descriptor array.
43 * (RTMemFree it even on failure)
44 */
45static iprt::MiniString *
46vboxExtPackLoadPlugInDescs(const xml::ElementNode *pVBoxExtPackElm,
47 uint32_t *pcPlugIns, PVBOXEXTPACKPLUGINDESC *paPlugIns)
48{
49 *pcPlugIns = 0;
50 *paPlugIns = NULL;
51
52 /** @todo plug-ins */
53 NOREF(pVBoxExtPackElm);
54
55 return NULL;
56}
57
58/**
59 * Reads the extension pack descriptor.
60 *
61 * @returns NULL on success, pointer to an error message on failure (caller
62 * deletes it).
63 * @param a_pszDir The directory containing the description file.
64 * @param a_pExtPackDesc Where to store the extension pack descriptor.
65 * @param a_pObjInfo Where to store the object info for the file (unix
66 * attribs). Optional.
67 */
68iprt::MiniString *VBoxExtPackLoadDesc(const char *a_pszDir, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
69{
70 /*
71 * Clear the descriptor.
72 */
73 a_pExtPackDesc->strName.setNull();
74 a_pExtPackDesc->strDescription.setNull();
75 a_pExtPackDesc->strVersion.setNull();
76 a_pExtPackDesc->uRevision = 0;
77 a_pExtPackDesc->strMainModule.setNull();
78 a_pExtPackDesc->strVrdeModule.setNull();
79 a_pExtPackDesc->cPlugIns = 0;
80 a_pExtPackDesc->paPlugIns = NULL;
81
82 /*
83 * Validate, open and parse the XML file.
84 */
85 char szFilePath[RTPATH_MAX];
86 int vrc = RTPathJoin(szFilePath, sizeof(szFilePath), a_pszDir, VBOX_EXTPACK_DESCRIPTION_NAME);
87 if (RT_FAILURE(vrc))
88 return new iprt::MiniString("RTPathJoin failed with %Rrc", vrc);
89
90 RTFSOBJINFO ObjInfo;
91 vrc = RTPathQueryInfoEx(szFilePath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
92 if (RT_FAILURE(vrc))
93 return &(new iprt::MiniString())->printf("RTPathQueryInfoEx failed with %Rrc", vrc);
94 if (a_pObjInfo)
95 *a_pObjInfo = ObjInfo;
96 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
97 {
98 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
99 return new iprt::MiniString("The XML file is symlinked, that is not allowed");
100 return &(new iprt::MiniString)->printf("The XML file is not a file (fMode=%#x)", ObjInfo.Attr.fMode);
101 }
102
103 xml::Document Doc;
104 xml::XmlFileParser Parser;
105 try
106 {
107 Parser.read(szFilePath, Doc);
108 }
109 catch (xml::XmlError Err)
110 {
111 return new iprt::MiniString(Err.what());
112 }
113
114 /*
115 * Get the main element and check its version.
116 */
117 const xml::ElementNode *pVBoxExtPackElm = Doc.getRootElement();
118 if ( !pVBoxExtPackElm
119 || strcmp(pVBoxExtPackElm->getName(), "VirtualBoxExtensionPack") != 0)
120 return new iprt::MiniString("No VirtualBoxExtensionPack element");
121
122 iprt::MiniString strFormatVersion;
123 if (!pVBoxExtPackElm->getAttributeValue("version", strFormatVersion))
124 return new iprt::MiniString("Missing format version");
125 if (!strFormatVersion.equals("1.0"))
126 return &(new iprt::MiniString("Unsupported format version: "))->append(strFormatVersion);
127
128 /*
129 * Read and validate mandatory bits.
130 */
131 const xml::ElementNode *pNameElm = pVBoxExtPackElm->findChildElement("Name");
132 if (!pNameElm)
133 return new iprt::MiniString("The 'Name' element is missing");
134 const char *pszName = pNameElm->getValue();
135 if (!VBoxExtPackIsValidName(pszName))
136 return &(new iprt::MiniString("Invalid name: "))->append(pszName);
137
138 const xml::ElementNode *pDescElm = pVBoxExtPackElm->findChildElement("Description");
139 if (!pDescElm)
140 return new iprt::MiniString("The 'Description' element is missing");
141 const char *pszDesc = pDescElm->getValue();
142 if (!pszDesc || *pszDesc == '\0')
143 return new iprt::MiniString("The 'Description' element is empty");
144 if (strpbrk(pszDesc, "\n\r\t\v\b") != NULL)
145 return new iprt::MiniString("The 'Description' must not contain control characters");
146
147 const xml::ElementNode *pVersionElm = pVBoxExtPackElm->findChildElement("Version");
148 if (!pVersionElm)
149 return new iprt::MiniString("The 'Version' element is missing");
150 const char *pszVersion = pVersionElm->getValue();
151 if (!pszVersion || *pszVersion == '\0')
152 return new iprt::MiniString("The 'Version' element is empty");
153 if (!VBoxExtPackIsValidVersionString(pszVersion))
154 return &(new iprt::MiniString("Invalid version string: "))->append(pszVersion);
155
156 uint32_t uRevision;
157 if (!pVersionElm->getAttributeValue("revision", uRevision))
158 uRevision = 0;
159
160 const xml::ElementNode *pMainModuleElm = pVBoxExtPackElm->findChildElement("MainModule");
161 if (!pMainModuleElm)
162 return new iprt::MiniString("The 'MainModule' element is missing");
163 const char *pszMainModule = pMainModuleElm->getValue();
164 if (!pszMainModule || *pszMainModule == '\0')
165 return new iprt::MiniString("The 'MainModule' element is empty");
166 if (!VBoxExtPackIsValidModuleString(pszMainModule))
167 return &(new iprt::MiniString("Invalid main module string: "))->append(pszMainModule);
168
169 /*
170 * The VRDE module, optional.
171 * Accept both none and empty as tokens of no VRDE module.
172 */
173 const char *pszVrdeModule = NULL;
174 const xml::ElementNode *pVrdeModuleElm = pVBoxExtPackElm->findChildElement("VRDEModule");
175 if (pVrdeModuleElm)
176 {
177 pszVrdeModule = pVrdeModuleElm->getValue();
178 if (!pszVrdeModule || *pszVrdeModule == '\0')
179 pszVrdeModule = NULL;
180 else if (!VBoxExtPackIsValidModuleString(pszVrdeModule))
181 return &(new iprt::MiniString("Invalid VRDE module string: "))->append(pszVrdeModule);
182 }
183
184 /*
185 * Parse plug-in descriptions.
186 */
187 uint32_t cPlugIns = 0;
188 PVBOXEXTPACKPLUGINDESC paPlugIns = NULL;
189 iprt::MiniString *pstrRet = vboxExtPackLoadPlugInDescs(pVBoxExtPackElm, &cPlugIns, &paPlugIns);
190 if (pstrRet)
191 {
192 RTMemFree(paPlugIns);
193 return pstrRet;
194 }
195
196 /*
197 * Everything seems fine, fill in the return values and return successfully.
198 */
199 a_pExtPackDesc->strName = pszName;
200 a_pExtPackDesc->strDescription = pszDesc;
201 a_pExtPackDesc->strVersion = pszVersion;
202 a_pExtPackDesc->uRevision = uRevision;
203 a_pExtPackDesc->strMainModule = pszMainModule;
204 a_pExtPackDesc->strVrdeModule = pszVrdeModule;
205 a_pExtPackDesc->cPlugIns = cPlugIns;
206 a_pExtPackDesc->paPlugIns = paPlugIns;
207
208 return NULL;
209}
210
211
212/**
213 * Frees all resources associated with a extension pack descriptor.
214 *
215 * @param a_pExtPackDesc The extension pack descriptor which members
216 * should be freed.
217 */
218void VBoxExtPackFreeDesc(PVBOXEXTPACKDESC a_pExtPackDesc)
219{
220 if (!a_pExtPackDesc)
221 return;
222
223 a_pExtPackDesc->strName.setNull();
224 a_pExtPackDesc->strDescription.setNull();
225 a_pExtPackDesc->strVersion.setNull();
226 a_pExtPackDesc->uRevision = 0;
227 a_pExtPackDesc->strMainModule.setNull();
228 a_pExtPackDesc->strVrdeModule.setNull();
229 a_pExtPackDesc->cPlugIns = 0;
230 RTMemFree(a_pExtPackDesc->paPlugIns);
231 a_pExtPackDesc->paPlugIns = NULL;
232}
233
234
235/**
236 * Extract the extension pack name from the tarball path.
237 *
238 * @returns String containing the name on success, the caller must delete it.
239 * NULL if no valid name was found or if we ran out of memory.
240 * @param pszTarball The path to the tarball.
241 */
242iprt::MiniString *VBoxExtPackExtractNameFromTarballPath(const char *pszTarball)
243{
244 /*
245 * Skip ahead to the filename part and count the number of characters
246 * that matches the criteria for a extension pack name.
247 */
248 const char *pszSrc = RTPathFilename(pszTarball);
249 if (!pszSrc)
250 return NULL;
251
252 size_t off = 0;
253 while (RT_C_IS_ALNUM(pszSrc[off]) || pszSrc[off] == ' ')
254 off++;
255
256 /*
257 * Check min and max name limits.
258 */
259 if ( off > VBOX_EXTPACK_NAME_MIN_LEN
260 || off < VBOX_EXTPACK_NAME_MIN_LEN)
261 return NULL;
262
263 /*
264 * Make a duplicate of the name and return it.
265 */
266 iprt::MiniString *pStrRet = new iprt::MiniString(pszSrc, off);
267 Assert(VBoxExtPackIsValidName(pStrRet->c_str()));
268 return pStrRet;
269}
270
271
272/**
273 * Validates the extension pack name.
274 *
275 * @returns true if valid, false if not.
276 * @param pszName The name to validate.
277 * @sa VBoxExtPackExtractNameFromTarballPath
278 */
279bool VBoxExtPackIsValidName(const char *pszName)
280{
281 if (!pszName)
282 return false;
283
284 /*
285 * Check the characters making up the name, only english alphabet
286 * characters, decimal digits and spaces are allowed.
287 */
288 size_t off = 0;
289 while (pszName[off])
290 {
291 if (!RT_C_IS_ALNUM(pszName[off]) && pszName[off] != ' ')
292 return false;
293 off++;
294 }
295
296 /*
297 * Check min and max name limits.
298 */
299 if ( off > VBOX_EXTPACK_NAME_MAX_LEN
300 || off < VBOX_EXTPACK_NAME_MIN_LEN)
301 return false;
302
303 return true;
304}
305
306/**
307 * Validates the extension pack version string.
308 *
309 * @returns true if valid, false if not.
310 * @param pszVersion The version string to validate.
311 */
312bool VBoxExtPackIsValidVersionString(const char *pszVersion)
313{
314 if (!pszVersion || *pszVersion == '\0')
315 return false;
316
317 /* 1.x.y.z... */
318 for (;;)
319 {
320 if (!RT_C_IS_DIGIT(*pszVersion))
321 return false;
322 do
323 pszVersion++;
324 while (RT_C_IS_DIGIT(*pszVersion));
325 if (*pszVersion != '.')
326 break;
327 pszVersion++;
328 }
329
330 /* upper case string + numbers indicating the build type */
331 if (*pszVersion == '-' || *pszVersion == '_')
332 {
333 do
334 pszVersion++;
335 while ( RT_C_IS_DIGIT(*pszVersion)
336 || RT_C_IS_UPPER(*pszVersion)
337 || *pszVersion == '-'
338 || *pszVersion == '_');
339 }
340
341 /* revision or nothing */
342 if (*pszVersion != '\0')
343 {
344 if (*pszVersion != 'r')
345 return false;
346 do
347 pszVersion++;
348 while (RT_C_IS_DIGIT(*pszVersion));
349 }
350
351 return *pszVersion == '\0';
352}
353
354/**
355 * Validates an extension pack module string.
356 *
357 * @returns true if valid, false if not.
358 * @param pszModule The module string to validate.
359 */
360bool VBoxExtPackIsValidModuleString(const char *pszModule)
361{
362 if (!pszModule || *pszModule == '\0')
363 return false;
364
365 /* Restricted charset, no extensions (dots). */
366 while ( RT_C_IS_ALNUM(*pszModule)
367 || *pszModule == '-'
368 || *pszModule == '_')
369 pszModule++;
370
371 return *pszModule == '\0';
372}
373
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