VirtualBox

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

Last change on this file since 34575 was 34570, checked in by vboxsync, 14 years ago

ExtPackUtil.cpp: The underscore=space trick.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.1 KB
Line 
1/* $Id: ExtPackUtil.cpp 34570 2010-12-01 13:33:49Z 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 * Trick: '_' == ' ' - filenames with spaces cause trouble.
249 */
250 const char *pszSrc = RTPathFilename(pszTarball);
251 if (!pszSrc)
252 return NULL;
253
254 size_t off = 0;
255 while (RT_C_IS_ALNUM(pszSrc[off]) || pszSrc[off] == ' ' || pszSrc[off] == '_')
256 off++;
257
258 /*
259 * Check min and max name limits.
260 */
261 if ( off > VBOX_EXTPACK_NAME_MAX_LEN
262 || off < VBOX_EXTPACK_NAME_MIN_LEN)
263 return NULL;
264
265 /*
266 * Replace underscores, duplicate the string and return it.
267 * (Unforuntately, iprt::ministring does not offer simple search & replace.)
268 */
269 char szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1];
270 memcpy(szTmp, pszSrc, off);
271 szTmp[off] = '\0';
272 char *psz = szTmp;
273 while ((psz = strchr(psz, '_')) != NULL)
274 *psz++ = ' ';
275 Assert(VBoxExtPackIsValidName(szTmp));
276
277 iprt::MiniString *pStrRet = new iprt::MiniString(szTmp, off);
278 return pStrRet;
279}
280
281
282/**
283 * Validates the extension pack name.
284 *
285 * @returns true if valid, false if not.
286 * @param pszName The name to validate.
287 * @sa VBoxExtPackExtractNameFromTarballPath
288 */
289bool VBoxExtPackIsValidName(const char *pszName)
290{
291 if (!pszName)
292 return false;
293
294 /*
295 * Check the characters making up the name, only english alphabet
296 * characters, decimal digits and spaces are allowed.
297 */
298 size_t off = 0;
299 while (pszName[off])
300 {
301 if (!RT_C_IS_ALNUM(pszName[off]) && pszName[off] != ' ')
302 return false;
303 off++;
304 }
305
306 /*
307 * Check min and max name limits.
308 */
309 if ( off > VBOX_EXTPACK_NAME_MAX_LEN
310 || off < VBOX_EXTPACK_NAME_MIN_LEN)
311 return false;
312
313 return true;
314}
315
316/**
317 * Validates the extension pack version string.
318 *
319 * @returns true if valid, false if not.
320 * @param pszVersion The version string to validate.
321 */
322bool VBoxExtPackIsValidVersionString(const char *pszVersion)
323{
324 if (!pszVersion || *pszVersion == '\0')
325 return false;
326
327 /* 1.x.y.z... */
328 for (;;)
329 {
330 if (!RT_C_IS_DIGIT(*pszVersion))
331 return false;
332 do
333 pszVersion++;
334 while (RT_C_IS_DIGIT(*pszVersion));
335 if (*pszVersion != '.')
336 break;
337 pszVersion++;
338 }
339
340 /* upper case string + numbers indicating the build type */
341 if (*pszVersion == '-' || *pszVersion == '_')
342 {
343 do
344 pszVersion++;
345 while ( RT_C_IS_DIGIT(*pszVersion)
346 || RT_C_IS_UPPER(*pszVersion)
347 || *pszVersion == '-'
348 || *pszVersion == '_');
349 }
350
351 /* revision or nothing */
352 if (*pszVersion != '\0')
353 {
354 if (*pszVersion != 'r')
355 return false;
356 do
357 pszVersion++;
358 while (RT_C_IS_DIGIT(*pszVersion));
359 }
360
361 return *pszVersion == '\0';
362}
363
364/**
365 * Validates an extension pack module string.
366 *
367 * @returns true if valid, false if not.
368 * @param pszModule The module string to validate.
369 */
370bool VBoxExtPackIsValidModuleString(const char *pszModule)
371{
372 if (!pszModule || *pszModule == '\0')
373 return false;
374
375 /* Restricted charset, no extensions (dots). */
376 while ( RT_C_IS_ALNUM(*pszModule)
377 || *pszModule == '-'
378 || *pszModule == '_')
379 pszModule++;
380
381 return *pszModule == '\0';
382}
383
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