1## @file 2# This file is for converting package information data file to xml file. 3# 4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> 5# 6# SPDX-License-Identifier: BSD-2-Clause-Patent 7# 8 9''' 10IniToXml 11''' 12 13import os.path 14import re 15from time import strftime 16from time import localtime 17 18import Logger.Log as Logger 19from Logger.ToolError import UPT_INI_PARSE_ERROR 20from Logger.ToolError import FILE_NOT_FOUND 21from Library.Xml.XmlRoutines import CreateXmlElement 22from Library.DataType import TAB_VALUE_SPLIT 23from Library.DataType import TAB_EQUAL_SPLIT 24from Library.DataType import TAB_SECTION_START 25from Library.DataType import TAB_SECTION_END 26from Logger import StringTable as ST 27from Library.StringUtils import ConvertSpecialChar 28from Library.ParserValidate import IsValidPath 29from Library import GlobalData 30 31## log error: 32# 33# @param error: error 34# @param File: File 35# @param Line: Line 36# 37def IniParseError(Error, File, Line): 38 Logger.Error("UPT", UPT_INI_PARSE_ERROR, File=File, 39 Line=Line, ExtraData=Error) 40 41## __ValidatePath 42# 43# @param Path: Path to be checked 44# 45def __ValidatePath(Path, Root): 46 Path = Path.strip() 47 if os.path.isabs(Path) or not IsValidPath(Path, Root): 48 return False, ST.ERR_FILELIST_LOCATION % (Root, Path) 49 return True, '' 50 51## ValidateMiscFile 52# 53# @param Filename: File to be checked 54# 55def ValidateMiscFile(Filename): 56 Root = GlobalData.gWORKSPACE 57 return __ValidatePath(Filename, Root) 58 59## ValidateToolsFile 60# 61# @param Filename: File to be checked 62# 63def ValidateToolsFile(Filename): 64 Valid, Cause = False, '' 65 if not Valid and 'EDK_TOOLS_PATH' in os.environ: 66 Valid, Cause = __ValidatePath(Filename, os.environ['EDK_TOOLS_PATH']) 67 if not Valid: 68 Valid, Cause = __ValidatePath(Filename, GlobalData.gWORKSPACE) 69 return Valid, Cause 70 71## ParseFileList 72# 73# @param Line: Line 74# @param Map: Map 75# @param CurrentKey: CurrentKey 76# @param PathFunc: Path validate function 77# 78def ParseFileList(Line, Map, CurrentKey, PathFunc): 79 FileList = ["", {}] 80 TokenList = Line.split(TAB_VALUE_SPLIT) 81 if len(TokenList) > 0: 82 Path = TokenList[0].strip().replace('\\', '/') 83 if not Path: 84 return False, ST.ERR_WRONG_FILELIST_FORMAT 85 Valid, Cause = PathFunc(Path) 86 if not Valid: 87 return Valid, Cause 88 FileList[0] = TokenList[0].strip() 89 for Token in TokenList[1:]: 90 Attr = Token.split(TAB_EQUAL_SPLIT) 91 if len(Attr) != 2 or not Attr[0].strip() or not Attr[1].strip(): 92 return False, ST.ERR_WRONG_FILELIST_FORMAT 93 94 Key = Attr[0].strip() 95 Val = Attr[1].strip() 96 if Key not in ['OS', 'Executable']: 97 return False, ST.ERR_UNKNOWN_FILELIST_ATTR % Key 98 99 if Key == 'OS' and Val not in ["Win32", "Win64", "Linux32", 100 "Linux64", "OS/X32", "OS/X64", 101 "GenericWin", "GenericNix"]: 102 return False, ST.ERR_FILELIST_ATTR % 'OS' 103 elif Key == 'Executable' and Val not in ['true', 'false']: 104 return False, ST.ERR_FILELIST_ATTR % 'Executable' 105 FileList[1][Key] = Val 106 107 Map[CurrentKey].append(FileList) 108 return True, '' 109 110## Create header XML file 111# 112# @param DistMap: DistMap 113# @param Root: Root 114# 115def CreateHeaderXml(DistMap, Root): 116 Element1 = CreateXmlElement('Name', DistMap['Name'], 117 [], [['BaseName', DistMap['BaseName']]]) 118 Element2 = CreateXmlElement('GUID', DistMap['GUID'], 119 [], [['Version', DistMap['Version']]]) 120 AttributeList = [['ReadOnly', DistMap['ReadOnly']], 121 ['RePackage', DistMap['RePackage']]] 122 NodeList = [Element1, 123 Element2, 124 ['Vendor', DistMap['Vendor']], 125 ['Date', DistMap['Date']], 126 ['Copyright', DistMap['Copyright']], 127 ['License', DistMap['License']], 128 ['Abstract', DistMap['Abstract']], 129 ['Description', DistMap['Description']], 130 ['Signature', DistMap['Signature']], 131 ['XmlSpecification', DistMap['XmlSpecification']], 132 ] 133 Root.appendChild(CreateXmlElement('DistributionHeader', '', 134 NodeList, AttributeList)) 135 136## Create tools XML file 137# 138# @param Map: Map 139# @param Root: Root 140# @param Tag: Tag 141# 142def CreateToolsXml(Map, Root, Tag): 143 # 144 # Check if all elements in this section are empty 145 # 146 for Key in Map: 147 if len(Map[Key]) > 0: 148 break 149 else: 150 return 151 152 NodeList = [['Name', Map['Name']], 153 ['Copyright', Map['Copyright']], 154 ['License', Map['License']], 155 ['Abstract', Map['Abstract']], 156 ['Description', Map['Description']], 157 ] 158 HeaderNode = CreateXmlElement('Header', '', NodeList, []) 159 NodeList = [HeaderNode] 160 161 for File in Map['FileList']: 162 AttrList = [] 163 for Key in File[1]: 164 AttrList.append([Key, File[1][Key]]) 165 NodeList.append(CreateXmlElement('Filename', File[0], [], AttrList)) 166 Root.appendChild(CreateXmlElement(Tag, '', NodeList, [])) 167 168## ValidateValues 169# 170# @param Key: Key 171# @param Value: Value 172# @param SectionName: SectionName 173# 174def ValidateValues(Key, Value, SectionName): 175 if SectionName == 'DistributionHeader': 176 Valid, Cause = ValidateRegValues(Key, Value) 177 if not Valid: 178 return Valid, Cause 179 Valid = __ValidateDistHeader(Key, Value) 180 if not Valid: 181 return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName) 182 else: 183 Valid = __ValidateOtherHeader(Key, Value) 184 if not Valid: 185 return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName) 186 return True, '' 187 188## ValidateRegValues 189# 190# @param Key: Key 191# @param Value: Value 192# 193def ValidateRegValues(Key, Value): 194 ValidateMap = { 195 'ReadOnly' : 196 ('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)), 197 'RePackage' : 198 ('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)), 199 'GUID' : 200 ('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}' 201 '-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}', 202 ST.ERR_GUID_VALUE % Value), 203 'Version' : ('[0-9]+(\.[0-9]+)?', ST.ERR_VERSION_VALUE % \ 204 (Key, Value)), 205 'XmlSpecification' : ('1\.1', ST.ERR_VERSION_XMLSPEC % Value) 206 } 207 if Key not in ValidateMap: 208 return True, '' 209 Elem = ValidateMap[Key] 210 Match = re.compile(Elem[0]).match(Value) 211 if Match and Match.start() == 0 and Match.end() == len(Value): 212 return True, '' 213 return False, Elem[1] 214 215## __ValidateDistHeaderName 216# 217# @param Name: Name 218# 219def __ValidateDistHeaderName(Name): 220 if len(Name) < 1: 221 return False 222 223 for Char in Name: 224 if ord(Char) < 0x20 or ord(Char) >= 0x7f: 225 return False 226 return True 227 228## __ValidateDistHeaderBaseName 229# 230# @param BaseName: BaseName 231# 232def __ValidateDistHeaderBaseName(BaseName): 233 if not BaseName: 234 return False 235# if CheckLen and len(BaseName) < 2: 236# return False 237 if not BaseName[0].isalnum() and BaseName[0] != '_': 238 return False 239 for Char in BaseName[1:]: 240 if not Char.isalnum() and Char not in '-_': 241 return False 242 return True 243 244## __ValidateDistHeaderAbstract 245# 246# @param Abstract: Abstract 247# 248def __ValidateDistHeaderAbstract(Abstract): 249 return '\t' not in Abstract and len(Abstract.splitlines()) == 1 250 251## __ValidateOtherHeaderAbstract 252# 253# @param Abstract: Abstract 254# 255def __ValidateOtherHeaderAbstract(Abstract): 256 return __ValidateDistHeaderAbstract(Abstract) 257 258## __ValidateDistHeader 259# 260# @param Key: Key 261# @param Value: Value 262# 263def __ValidateDistHeader(Key, Value): 264 ValidateMap = { 265 'Name' : __ValidateDistHeaderName, 266 'BaseName' : __ValidateDistHeaderBaseName, 267 'Abstract' : __ValidateDistHeaderAbstract, 268 'Vendor' : __ValidateDistHeaderAbstract 269 } 270 return not (Value and Key in ValidateMap and not ValidateMap[Key](Value)) 271 272## __ValidateOtherHeader 273# 274# @param Key: Key 275# @param Value: Value 276# 277def __ValidateOtherHeader(Key, Value): 278 ValidateMap = { 279 'Name' : __ValidateDistHeaderName, 280 'Abstract' : __ValidateOtherHeaderAbstract 281 } 282 return not (Value and Key in ValidateMap and not ValidateMap[Key](Value)) 283 284## Convert ini file to xml file 285# 286# @param IniFile 287# 288def IniToXml(IniFile): 289 if not os.path.exists(IniFile): 290 Logger.Error("UPT", FILE_NOT_FOUND, ST.ERR_TEMPLATE_NOTFOUND % IniFile) 291 292 DistMap = {'ReadOnly' : '', 'RePackage' : '', 'Name' : '', 293 'BaseName' : '', 'GUID' : '', 'Version' : '', 'Vendor' : '', 294 'Date' : '', 'Copyright' : '', 'License' : '', 'Abstract' : '', 295 'Description' : '', 'Signature' : '', 'XmlSpecification' : '' 296 } 297 298 ToolsMap = {'Name' : '', 'Copyright' : '', 'License' : '', 299 'Abstract' : '', 'Description' : '', 'FileList' : []} 300 # 301 # Only FileList is a list: [['file1', {}], ['file2', {}], ...] 302 # 303 MiscMap = {'Name' : '', 'Copyright' : '', 'License' : '', 304 'Abstract' : '', 'Description' : '', 'FileList' : []} 305 306 SectionMap = { 307 'DistributionHeader' : DistMap, 308 'ToolsHeader' : ToolsMap, 309 'MiscellaneousFilesHeader' : MiscMap 310 } 311 312 PathValidator = { 313 'ToolsHeader' : ValidateToolsFile, 314 'MiscellaneousFilesHeader' : ValidateMiscFile 315 } 316 317 ParsedSection = [] 318 319 SectionName = '' 320 CurrentKey = '' 321 PreMap = None 322 Map = None 323 FileContent = ConvertSpecialChar(open(IniFile, 'r').readlines()) 324 LastIndex = 0 325 for Index in range(0, len(FileContent)): 326 LastIndex = Index 327 Line = FileContent[Index].strip() 328 if Line == '' or Line.startswith(';'): 329 continue 330 if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END: 331 CurrentKey = '' 332 SectionName = Line[1:-1].strip() 333 if SectionName not in SectionMap: 334 IniParseError(ST.ERR_SECTION_NAME_INVALID % SectionName, 335 IniFile, Index+1) 336 337 if SectionName in ParsedSection: 338 IniParseError(ST.ERR_SECTION_REDEFINE % SectionName, 339 IniFile, Index+1) 340 else: 341 ParsedSection.append(SectionName) 342 343 Map = SectionMap[SectionName] 344 continue 345 if not Map: 346 IniParseError(ST.ERR_SECTION_NAME_NONE, IniFile, Index+1) 347 TokenList = Line.split(TAB_EQUAL_SPLIT, 1) 348 TempKey = TokenList[0].strip() 349 # 350 # Value spanned multiple or same keyword appears more than one time 351 # 352 if len(TokenList) < 2 or TempKey not in Map: 353 if CurrentKey == '': 354 IniParseError(ST.ERR_KEYWORD_INVALID % TempKey, 355 IniFile, Index+1) 356 elif CurrentKey == 'FileList': 357 # 358 # Special for FileList 359 # 360 Valid, Cause = ParseFileList(Line, Map, CurrentKey, 361 PathValidator[SectionName]) 362 if not Valid: 363 IniParseError(Cause, IniFile, Index+1) 364 365 else: 366 # 367 # Multiple lines for one key such as license 368 # Or if string on the left side of '=' is not a keyword 369 # 370 Map[CurrentKey] = ''.join([Map[CurrentKey], '\n', Line]) 371 Valid, Cause = ValidateValues(CurrentKey, 372 Map[CurrentKey], SectionName) 373 if not Valid: 374 IniParseError(Cause, IniFile, Index+1) 375 continue 376 377 if (TokenList[1].strip() == ''): 378 IniParseError(ST.ERR_EMPTY_VALUE, IniFile, Index+1) 379 380 # 381 # A keyword found 382 # 383 CurrentKey = TempKey 384 if Map[CurrentKey]: 385 IniParseError(ST.ERR_KEYWORD_REDEFINE % CurrentKey, 386 IniFile, Index+1) 387 388 if id(Map) != id(PreMap) and Map['Copyright']: 389 PreMap = Map 390 Copyright = Map['Copyright'].lower() 391 Pos = Copyright.find('copyright') 392 if Pos == -1: 393 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index) 394 if not Copyright[Pos + len('copyright'):].lstrip(' ').startswith('('): 395 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index) 396 397 if CurrentKey == 'FileList': 398 Valid, Cause = ParseFileList(TokenList[1], Map, CurrentKey, 399 PathValidator[SectionName]) 400 if not Valid: 401 IniParseError(Cause, IniFile, Index+1) 402 else: 403 Map[CurrentKey] = TokenList[1].strip() 404 Valid, Cause = ValidateValues(CurrentKey, 405 Map[CurrentKey], SectionName) 406 if not Valid: 407 IniParseError(Cause, IniFile, Index+1) 408 409 if id(Map) != id(PreMap) and Map['Copyright'] and 'copyright' not in Map['Copyright'].lower(): 410 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, LastIndex) 411 412 # 413 # Check mandatory keys 414 # 415 CheckMdtKeys(DistMap, IniFile, LastIndex, 416 (('ToolsHeader', ToolsMap), ('MiscellaneousFilesHeader', MiscMap)) 417 ) 418 419 return CreateXml(DistMap, ToolsMap, MiscMap, IniFile) 420 421 422## CheckMdtKeys 423# 424# @param MdtDistKeys: All mandatory keys 425# @param DistMap: Dist content 426# @param IniFile: Ini file 427# @param LastIndex: Last index of Ini file 428# @param Maps: Tools and Misc section name and map. (('section_name', map),*) 429# 430def CheckMdtKeys(DistMap, IniFile, LastIndex, Maps): 431 MdtDistKeys = ['Name', 'GUID', 'Version', 'Vendor', 'Copyright', 'License', 'Abstract', 'XmlSpecification'] 432 for Key in MdtDistKeys: 433 if Key not in DistMap or DistMap[Key] == '': 434 IniParseError(ST.ERR_KEYWORD_MANDATORY % Key, IniFile, LastIndex+1) 435 436 if '.' not in DistMap['Version']: 437 DistMap['Version'] = DistMap['Version'] + '.0' 438 439 DistMap['Date'] = str(strftime("%Y-%m-%dT%H:%M:%S", localtime())) 440 441 # 442 # Check Tools Surface Area according to UPT Spec 443 # <Tools> {0,} 444 # <Header> ... </Header> {0,1} 445 # <Filename> ... </Filename> {1,} 446 # </Tools> 447 # <Header> 448 # <Name> xs:normalizedString </Name> {1} 449 # <Copyright> xs:string </Copyright> {0,1} 450 # <License> xs:string </License> {0,1} 451 # <Abstract> xs:normalizedString </Abstract> {0,1} 452 # <Description> xs:string </Description> {0,1} 453 # </Header> 454 # 455 for Item in Maps: 456 Map = Item[1] 457 NonEmptyKey = 0 458 for Key in Map: 459 if Map[Key]: 460 NonEmptyKey += 1 461 462 if NonEmptyKey > 0 and not Map['FileList']: 463 IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.FileList'), IniFile, LastIndex+1) 464 465 if NonEmptyKey > 0 and not Map['Name']: 466 IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.Name'), IniFile, LastIndex+1) 467 468## CreateXml 469# 470# @param DistMap: Dist Content 471# @param ToolsMap: Tools Content 472# @param MiscMap: Misc Content 473# @param IniFile: Ini File 474# 475def CreateXml(DistMap, ToolsMap, MiscMap, IniFile): 476 Attrs = [['xmlns', 'http://www.uefi.org/2011/1.1'], 477 ['xmlns:xsi', 'http:/www.w3.org/2001/XMLSchema-instance'], 478 ] 479 Root = CreateXmlElement('DistributionPackage', '', [], Attrs) 480 CreateHeaderXml(DistMap, Root) 481 CreateToolsXml(ToolsMap, Root, 'Tools') 482 CreateToolsXml(MiscMap, Root, 'MiscellaneousFiles') 483 484 FileAndExt = IniFile.rsplit('.', 1) 485 if len(FileAndExt) > 1: 486 FileName = FileAndExt[0] + '.xml' 487 else: 488 FileName = IniFile + '.xml' 489 File = open(FileName, 'w') 490 491 try: 492 File.write(Root.toprettyxml(indent = ' ')) 493 finally: 494 File.close() 495 return FileName 496 497