VirtualBox

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

最後變更 在這個檔案從34587是 34579,由 vboxsync 提交於 14 年 前

Completed the extension pack renaming. Some bugfixes.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 15.2 KB
 
1/* $Id: ExtPackUtil.cpp 34579 2010-12-01 15:45:02Z 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.alldomusa.eu.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 mangled 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_MAX_LEN
260 || off < VBOX_EXTPACK_NAME_MIN_LEN)
261 return NULL;
262
263 /*
264 * Return the unmangled name.
265 */
266 return VBoxExtPackUnmangleName(pszSrc, off);
267}
268
269/**
270 * Validates the extension pack name.
271 *
272 * @returns true if valid, false if not.
273 * @param pszName The name to validate.
274 * @sa VBoxExtPackExtractNameFromTarballPath
275 */
276bool VBoxExtPackIsValidName(const char *pszName)
277{
278 if (!pszName)
279 return false;
280
281 /*
282 * Check the characters making up the name, only english alphabet
283 * characters, decimal digits and spaces are allowed.
284 */
285 size_t off = 0;
286 while (pszName[off])
287 {
288 if (!RT_C_IS_ALNUM(pszName[off]) && pszName[off] != ' ')
289 return false;
290 off++;
291 }
292
293 /*
294 * Check min and max name limits.
295 */
296 if ( off > VBOX_EXTPACK_NAME_MAX_LEN
297 || off < VBOX_EXTPACK_NAME_MIN_LEN)
298 return false;
299
300 return true;
301}
302
303/**
304 * Checks if an alledged manged extension pack name.
305 *
306 * @returns true if valid, false if not.
307 * @param pszMangledName The mangled name to validate.
308 * @param cchMax The max number of chars to test.
309 * @sa VBoxExtPackMangleName
310 */
311bool VBoxExtPackIsValidMangledName(const char *pszMangledName, size_t cchMax /*= RTSTR_MAX*/)
312{
313 if (!pszMangledName)
314 return false;
315
316 /*
317 * Check the characters making up the name, only english alphabet
318 * characters, decimal digits and underscores (=space) are allowed.
319 */
320 size_t off = 0;
321 while (off < cchMax && pszMangledName[off])
322 {
323 if (!RT_C_IS_ALNUM(pszMangledName[off]) && pszMangledName[off] != '_')
324 return false;
325 off++;
326 }
327
328 /*
329 * Check min and max name limits.
330 */
331 if ( off > VBOX_EXTPACK_NAME_MAX_LEN
332 || off < VBOX_EXTPACK_NAME_MIN_LEN)
333 return false;
334
335 return true;
336}
337
338/**
339 * Mangle an extension pack name so it can be used by a directory or file name.
340 *
341 * @returns String containing the mangled name on success, the caller must
342 * delete it. NULL on failure.
343 * @param pszName The unmangled name.
344 * @sa VBoxExtPackUnmangleName, VBoxExtPackIsValidMangledName
345 */
346iprt::MiniString *VBoxExtPackMangleName(const char *pszName)
347{
348 AssertReturn(VBoxExtPackIsValidName(pszName), NULL);
349
350 char szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1];
351 size_t off = 0;
352 char ch;
353 while ((ch = pszName[off]) != '\0')
354 {
355 if (ch == ' ')
356 ch = '_';
357 szTmp[off++] = ch;
358 }
359 szTmp[off] = '\0';
360 Assert(VBoxExtPackIsValidMangledName(szTmp));
361
362 return new iprt::MiniString(szTmp, off);
363}
364
365/**
366 * Unmangle an extension pack name (reverses VBoxExtPackMangleName).
367 *
368 * @returns String containing the mangled name on success, the caller must
369 * delete it. NULL on failure.
370 * @param pszMangledName The mangled name.
371 * @param cchMax The max name length. RTSTR_MAX is fine.
372 * @sa VBoxExtPackMangleName, VBoxExtPackIsValidMangledName
373 */
374iprt::MiniString *VBoxExtPackUnmangleName(const char *pszMangledName, size_t cchMax)
375{
376 AssertReturn(VBoxExtPackIsValidMangledName(pszMangledName, cchMax), NULL);
377
378 char szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1];
379 size_t off = 0;
380 char ch;
381 while ( off < cchMax
382 && (ch = pszMangledName[off]) != '\0')
383 {
384 if (ch == '_')
385 ch = ' ';
386 else
387 AssertReturn(RT_C_IS_ALNUM(ch) || ch == ' ', NULL);
388 szTmp[off++] = ch;
389 }
390 szTmp[off] = '\0';
391 AssertReturn(VBoxExtPackIsValidName(szTmp), NULL);
392
393 return new iprt::MiniString(szTmp, off);
394}
395
396/**
397 * Constructs the extension pack directory path.
398 *
399 * A combination of RTPathJoin and VBoxExtPackMangleName.
400 *
401 * @returns IPRT status code like RTPathJoin.
402 * @param pszExtPackDir Where to return the directory path.
403 * @param cbExtPackDir The size of the return buffer.
404 * @param pszParentDir The parent directory (".../Extensions").
405 * @param pszName The extension pack name, unmangled.
406 */
407int VBoxExtPackCalcDir(char *pszExtPackDir, size_t cbExtPackDir, const char *pszParentDir, const char *pszName)
408{
409 AssertReturn(VBoxExtPackIsValidName(pszName), VERR_INTERNAL_ERROR_5);
410
411 iprt::MiniString *pstrMangledName = VBoxExtPackMangleName(pszName);
412 if (!pstrMangledName)
413 return VERR_INTERNAL_ERROR_4;
414
415 int vrc = RTPathJoin(pszExtPackDir, cbExtPackDir, pszParentDir, pstrMangledName->c_str());
416 delete pstrMangledName;
417
418 return vrc;
419}
420
421
422/**
423 * Validates the extension pack version string.
424 *
425 * @returns true if valid, false if not.
426 * @param pszVersion The version string to validate.
427 */
428bool VBoxExtPackIsValidVersionString(const char *pszVersion)
429{
430 if (!pszVersion || *pszVersion == '\0')
431 return false;
432
433 /* 1.x.y.z... */
434 for (;;)
435 {
436 if (!RT_C_IS_DIGIT(*pszVersion))
437 return false;
438 do
439 pszVersion++;
440 while (RT_C_IS_DIGIT(*pszVersion));
441 if (*pszVersion != '.')
442 break;
443 pszVersion++;
444 }
445
446 /* upper case string + numbers indicating the build type */
447 if (*pszVersion == '-' || *pszVersion == '_')
448 {
449 do
450 pszVersion++;
451 while ( RT_C_IS_DIGIT(*pszVersion)
452 || RT_C_IS_UPPER(*pszVersion)
453 || *pszVersion == '-'
454 || *pszVersion == '_');
455 }
456
457 /* revision or nothing */
458 if (*pszVersion != '\0')
459 {
460 if (*pszVersion != 'r')
461 return false;
462 do
463 pszVersion++;
464 while (RT_C_IS_DIGIT(*pszVersion));
465 }
466
467 return *pszVersion == '\0';
468}
469
470/**
471 * Validates an extension pack module string.
472 *
473 * @returns true if valid, false if not.
474 * @param pszModule The module string to validate.
475 */
476bool VBoxExtPackIsValidModuleString(const char *pszModule)
477{
478 if (!pszModule || *pszModule == '\0')
479 return false;
480
481 /* Restricted charset, no extensions (dots). */
482 while ( RT_C_IS_ALNUM(*pszModule)
483 || *pszModule == '-'
484 || *pszModule == '_')
485 pszModule++;
486
487 return *pszModule == '\0';
488}
489
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette