XMLParser.swift
1 // This source file is part of the Swift.org open source project 2 // 3 // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 4 // Licensed under Apache License v2.0 with Runtime Library Exception 5 // 6 // See http://swift.org/LICENSE.txt for license information 7 // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 8 // 9 10 #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) 11 import SwiftFoundation 12 import CFXMLInterface 13 #else 14 import Foundation 15 @_implementationOnly import CFXMLInterface 16 #endif 17 @_implementationOnly import CoreFoundation 18 19 extension XMLParser { 20 public enum ExternalEntityResolvingPolicy : UInt { 21 case never // default 22 case noNetwork 23 case sameOriginOnly //only applies to NSXMLParser instances initialized with -initWithContentsOfURL: 24 case always 25 } 26 } 27 28 extension _CFXMLInterface { 29 var parser: XMLParser { 30 return unsafeBitCast(self, to: XMLParser.self) 31 } 32 } 33 34 extension XMLParser { 35 internal var interface: _CFXMLInterface { 36 return unsafeBitCast(self, to: _CFXMLInterface.self) 37 } 38 } 39 40 private func UTF8STRING(_ bytes: UnsafePointer<UInt8>?) -> String? { 41 guard let bytes = bytes else { 42 return nil 43 } 44 if let (str, _) = String.decodeCString(bytes, as: UTF8.self, 45 repairingInvalidCodeUnits: false) { 46 return str 47 } 48 return nil 49 } 50 51 internal func _NSXMLParserCurrentParser() -> _CFXMLInterface? { 52 if let parser = XMLParser.currentParser() { 53 return parser.interface 54 } else { 55 return nil 56 } 57 } 58 59 internal func _NSXMLParserExternalEntityWithURL(_ interface: _CFXMLInterface, urlStr: UnsafePointer<Int8>, identifier: UnsafePointer<Int8>, context: _CFXMLInterfaceParserContext, originalLoaderFunction: _CFXMLInterfaceExternalEntityLoader) -> _CFXMLInterfaceParserInput? { 60 let parser = interface.parser 61 let policy = parser.externalEntityResolvingPolicy 62 var a: URL? 63 if let allowedEntityURLs = parser.allowedExternalEntityURLs { 64 if let url = URL(string: String(describing: urlStr)) { 65 a = url 66 if let scheme = url.scheme { 67 if scheme == "file" { 68 a = URL(fileURLWithPath: url.path) 69 } 70 } 71 } 72 if let url = a { 73 let allowed = allowedEntityURLs.contains(url) 74 if allowed || policy != .sameOriginOnly { 75 if allowed { 76 return originalLoaderFunction(urlStr, identifier, context) 77 } 78 } 79 } 80 } 81 82 switch policy { 83 case .sameOriginOnly: 84 guard let url = parser._url else { break } 85 86 if a == nil { 87 a = URL(string: String(describing: urlStr)) 88 } 89 90 guard let aUrl = a else { break } 91 92 var matches: Bool 93 if let aHost = aUrl.host, let host = url.host { 94 matches = host == aHost 95 } else { 96 return nil 97 } 98 99 if matches { 100 if let aPort = aUrl.port, let port = url.port { 101 matches = port == aPort 102 } else { 103 return nil 104 } 105 } 106 107 if matches { 108 if let aScheme = aUrl.scheme, let scheme = url.scheme { 109 matches = scheme == aScheme 110 } else { 111 return nil 112 } 113 } 114 115 if !matches { 116 return nil 117 } 118 case .always: 119 break 120 case .never: 121 return nil 122 case .noNetwork: 123 return _CFXMLInterfaceNoNetExternalEntityLoader(urlStr, identifier, context) 124 } 125 126 return originalLoaderFunction(urlStr, identifier, context) 127 } 128 129 internal func _NSXMLParserGetContext(_ ctx: _CFXMLInterface) -> _CFXMLInterfaceParserContext { 130 return ctx.parser._parserContext! 131 } 132 133 internal func _NSXMLParserInternalSubset(_ ctx: _CFXMLInterface, name: UnsafePointer<UInt8>, ExternalID: UnsafePointer<UInt8>, SystemID: UnsafePointer<UInt8>) -> Void { 134 _CFXMLInterfaceSAX2InternalSubset(ctx.parser._parserContext, name, ExternalID, SystemID) 135 } 136 137 internal func _NSXMLParserIsStandalone(_ ctx: _CFXMLInterface) -> Int32 { 138 return _CFXMLInterfaceIsStandalone(ctx.parser._parserContext) 139 } 140 141 internal func _NSXMLParserHasInternalSubset(_ ctx: _CFXMLInterface) -> Int32 { 142 return _CFXMLInterfaceHasInternalSubset(ctx.parser._parserContext) 143 } 144 145 internal func _NSXMLParserHasExternalSubset(_ ctx: _CFXMLInterface) -> Int32 { 146 return _CFXMLInterfaceHasExternalSubset(ctx.parser._parserContext) 147 } 148 149 internal func _NSXMLParserGetEntity(_ ctx: _CFXMLInterface, name: UnsafePointer<UInt8>) -> _CFXMLInterfaceEntity? { 150 let parser = ctx.parser 151 let context = _NSXMLParserGetContext(ctx) 152 var entity = _CFXMLInterfaceGetPredefinedEntity(name) 153 if entity == nil { 154 entity = _CFXMLInterfaceSAX2GetEntity(context, name) 155 } 156 if entity == nil { 157 if let delegate = parser.delegate { 158 let entityName = UTF8STRING(name)! 159 // if the systemID was valid, we would already have the correct entity (since we're loading external dtds) so this callback is a bit of a misnomer 160 let result = delegate.parser(parser, resolveExternalEntityName: entityName, systemID: nil) 161 if _CFXMLInterfaceHasDocument(context) != 0 { 162 if let data = result { 163 // unfortunately we can't add the entity to the doc to avoid further lookup since the delegate can change under us 164 data.withUnsafeBytes { (rawBuffer: UnsafeRawBufferPointer) in 165 let bytes = rawBuffer.baseAddress!.assumingMemoryBound(to: UInt8.self) 166 _NSXMLParserCharacters(ctx, ch: bytes, len: Int32(data.count)) 167 } 168 169 } 170 } 171 } 172 } 173 return entity 174 } 175 176 internal func _NSXMLParserNotationDecl(_ ctx: _CFXMLInterface, name: UnsafePointer<UInt8>, publicId: UnsafePointer<UInt8>, systemId: UnsafePointer<UInt8>) -> Void { 177 let parser = ctx.parser 178 if let delegate = parser.delegate { 179 let notationName = UTF8STRING(name)! 180 let publicIDString = UTF8STRING(publicId) 181 let systemIDString = UTF8STRING(systemId) 182 delegate.parser(parser, foundNotationDeclarationWithName: notationName, publicID: publicIDString, systemID: systemIDString) 183 } 184 } 185 186 internal func _NSXMLParserAttributeDecl(_ ctx: _CFXMLInterface, elem: UnsafePointer<UInt8>, fullname: UnsafePointer<UInt8>, type: Int32, def: Int32, defaultValue: UnsafePointer<UInt8>, tree: _CFXMLInterfaceEnumeration) -> Void { 187 let parser = ctx.parser 188 if let delegate = parser.delegate { 189 let elementString = UTF8STRING(elem)! 190 let nameString = UTF8STRING(fullname)! 191 let typeString = "" // FIXME! 192 let defaultValueString = UTF8STRING(defaultValue) 193 delegate.parser(parser, foundAttributeDeclarationWithName: nameString, forElement: elementString, type: typeString, defaultValue: defaultValueString) 194 } 195 // in a regular sax implementation tree is added to an attribute, which takes ownership of it; in our case we need to make sure to release it 196 _CFXMLInterfaceFreeEnumeration(tree) 197 } 198 199 internal func _NSXMLParserElementDecl(_ ctx: _CFXMLInterface, name: UnsafePointer<UInt8>, type: Int32, content: _CFXMLInterfaceElementContent) -> Void { 200 let parser = ctx.parser 201 if let delegate = parser.delegate { 202 let nameString = UTF8STRING(name)! 203 let modelString = "" // FIXME! 204 delegate.parser(parser, foundElementDeclarationWithName: nameString, model: modelString) 205 } 206 } 207 208 internal func _NSXMLParserUnparsedEntityDecl(_ ctx: _CFXMLInterface, name: UnsafePointer<UInt8>, publicId: UnsafePointer<UInt8>, systemId: UnsafePointer<UInt8>, notationName: UnsafePointer<UInt8>) -> Void { 209 let parser = ctx.parser 210 let context = _NSXMLParserGetContext(ctx) 211 212 // Add entities to the libxml2 doc so they'll resolve properly 213 _CFXMLInterfaceSAX2UnparsedEntityDecl(context, name, publicId, systemId, notationName) 214 if let delegate = parser.delegate { 215 let declName = UTF8STRING(name)! 216 let publicIDString = UTF8STRING(publicId) 217 let systemIDString = UTF8STRING(systemId) 218 let notationNameString = UTF8STRING(notationName) 219 delegate.parser(parser, foundUnparsedEntityDeclarationWithName: declName, publicID: publicIDString, systemID: systemIDString, notationName: notationNameString) 220 } 221 } 222 223 internal func _NSXMLParserStartDocument(_ ctx: _CFXMLInterface) -> Void { 224 let parser = ctx.parser 225 if let delegate = parser.delegate { 226 delegate.parserDidStartDocument(parser) 227 } 228 } 229 230 internal func _NSXMLParserEndDocument(_ ctx: _CFXMLInterface) -> Void { 231 let parser = ctx.parser 232 if let delegate = parser.delegate { 233 delegate.parserDidEndDocument(parser) 234 } 235 } 236 237 238 internal func _NSXMLParserStartElementNs(_ ctx: _CFXMLInterface, localname: UnsafePointer<UInt8>, prefix: UnsafePointer<UInt8>?, URI: UnsafePointer<UInt8>?, nb_namespaces: Int32, namespaces: UnsafeMutablePointer<UnsafePointer<UInt8>?>, nb_attributes: Int32, nb_defaulted: Int32, attributes: UnsafeMutablePointer<UnsafePointer<UInt8>?>) -> Void { 239 let parser = ctx.parser 240 let reportNamespaces = parser.shouldReportNamespacePrefixes 241 242 var nsDict = [String:String]() 243 var attrDict = [String:String]() 244 if nb_attributes + nb_namespaces > 0 { 245 for idx in stride(from: 0, to: Int(nb_namespaces) * 2, by: 2) { 246 var namespaceNameString: String? 247 var asAttrNamespaceNameString: String? 248 if let ns = namespaces[idx] { 249 namespaceNameString = UTF8STRING(ns) 250 asAttrNamespaceNameString = "xmlns:" + namespaceNameString! 251 } else { 252 namespaceNameString = "" 253 asAttrNamespaceNameString = "xmlns" 254 } 255 let namespaceValueString = namespaces[idx + 1] != nil ? UTF8STRING(namespaces[idx + 1]!) : "" 256 if reportNamespaces { 257 if let k = namespaceNameString, let v = namespaceValueString { 258 nsDict[k] = v 259 } 260 } 261 if !parser.shouldProcessNamespaces { 262 if let k = asAttrNamespaceNameString, 263 let v = namespaceValueString { 264 attrDict[k] = v 265 } 266 } 267 } 268 } 269 270 if reportNamespaces { 271 parser._pushNamespaces(nsDict) 272 } 273 274 for idx in stride(from: 0, to: Int(nb_attributes) * 5, by: 5) { 275 if attributes[idx] == nil { 276 continue 277 } 278 var attributeQName: String 279 let attrLocalName = attributes[idx]! 280 let attrLocalNameString = UTF8STRING(attrLocalName)! 281 let attrPrefix = attributes[idx + 1] 282 if let attrPrefixString = UTF8STRING(attrPrefix), !attrPrefixString.isEmpty { 283 attributeQName = attrPrefixString + ":" + attrLocalNameString 284 } else { 285 attributeQName = attrLocalNameString 286 } 287 // idx+2 = URI, which we throw away 288 // idx+3 = value, i+4 = endvalue 289 // By using XML_PARSE_NOENT the attribute value string will already have entities resolved 290 var attributeValue = "" 291 if let value = attributes[idx + 3], let endvalue = attributes[idx + 4] { 292 let numBytesWithoutTerminator = endvalue - value 293 if numBytesWithoutTerminator > 0 { 294 let buffer = UnsafeBufferPointer(start: value, 295 count: numBytesWithoutTerminator) 296 attributeValue = String(decoding: buffer, as: UTF8.self) 297 } 298 attrDict[attributeQName] = attributeValue 299 } 300 } 301 302 var elementName: String = UTF8STRING(localname)! 303 var namespaceURI: String? = nil 304 var qualifiedName: String? = nil 305 if parser.shouldProcessNamespaces { 306 namespaceURI = UTF8STRING(URI) ?? "" 307 if let prefix = UTF8STRING(prefix) { 308 qualifiedName = prefix + ":" + elementName 309 } else { 310 qualifiedName = elementName 311 } 312 } 313 else if let prefix = UTF8STRING(prefix) { 314 elementName = prefix + ":" + elementName 315 } 316 317 parser.delegate?.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qualifiedName, attributes: attrDict) 318 } 319 320 internal func _NSXMLParserEndElementNs(_ ctx: _CFXMLInterface , localname: UnsafePointer<UInt8>, prefix: UnsafePointer<UInt8>?, URI: UnsafePointer<UInt8>?) -> Void { 321 let parser = ctx.parser 322 323 var elementName: String = UTF8STRING(localname)! 324 var namespaceURI: String? = nil 325 var qualifiedName: String? = nil 326 if parser.shouldProcessNamespaces { 327 namespaceURI = UTF8STRING(URI) ?? "" 328 if let prefix = UTF8STRING(prefix) { 329 qualifiedName = prefix + ":" + elementName 330 } else { 331 qualifiedName = elementName 332 } 333 } 334 else if let prefix = UTF8STRING(prefix) { 335 elementName = prefix + ":" + elementName 336 } 337 338 parser.delegate?.parser(parser, didEndElement: elementName, namespaceURI: namespaceURI, qualifiedName: qualifiedName) 339 340 // Pop the last namespaces that were pushed (safe since XML is balanced) 341 if parser.shouldReportNamespacePrefixes { 342 parser._popNamespaces() 343 } 344 } 345 346 internal func _NSXMLParserCharacters(_ ctx: _CFXMLInterface, ch: UnsafePointer<UInt8>, len: Int32) -> Void { 347 let parser = ctx.parser 348 let context = parser._parserContext! 349 if _CFXMLInterfaceInRecursiveState(context) != 0 { 350 _CFXMLInterfaceResetRecursiveState(context) 351 } else { 352 if let delegate = parser.delegate { 353 let str = String(decoding: UnsafeBufferPointer(start: ch, count: Int(len)), as: UTF8.self) 354 delegate.parser(parser, foundCharacters: str) 355 } 356 } 357 } 358 359 internal func _NSXMLParserProcessingInstruction(_ ctx: _CFXMLInterface, target: UnsafePointer<UInt8>, data: UnsafePointer<UInt8>) -> Void { 360 let parser = ctx.parser 361 if let delegate = parser.delegate { 362 let targetString = UTF8STRING(target)! 363 let dataString = UTF8STRING(data) 364 delegate.parser(parser, foundProcessingInstructionWithTarget: targetString, data: dataString) 365 } 366 } 367 368 internal func _NSXMLParserCdataBlock(_ ctx: _CFXMLInterface, value: UnsafePointer<UInt8>, len: Int32) -> Void { 369 let parser = ctx.parser 370 if let delegate = parser.delegate { 371 delegate.parser(parser, foundCDATA: Data(bytes: value, count: Int(len))) 372 } 373 } 374 375 internal func _NSXMLParserComment(_ ctx: _CFXMLInterface, value: UnsafePointer<UInt8>) -> Void { 376 let parser = ctx.parser 377 if let delegate = parser.delegate { 378 let comment = UTF8STRING(value)! 379 delegate.parser(parser, foundComment: comment) 380 } 381 } 382 383 internal func _NSXMLParserExternalSubset(_ ctx: _CFXMLInterface, name: UnsafePointer<UInt8>, ExternalID: UnsafePointer<UInt8>, SystemID: UnsafePointer<UInt8>) -> Void { 384 _CFXMLInterfaceSAX2ExternalSubset(ctx.parser._parserContext, name, ExternalID, SystemID) 385 } 386 387 internal func _structuredErrorFunc(_ interface: _CFXMLInterface, error: _CFXMLInterfaceError) { 388 let cferr = _CFErrorCreateFromXMLInterface(error) 389 let err = _CFErrorSPIForFoundationXMLUseOnly(unsafelyAssumingIsCFError: cferr)._nsObject 390 let parser = interface.parser 391 parser._parserError = err 392 if let delegate = parser.delegate { 393 delegate.parser(parser, parseErrorOccurred: err) 394 } 395 } 396 397 open class XMLParser : NSObject { 398 private var _handler: _CFXMLInterfaceSAXHandler 399 #if !os(WASI) 400 internal var _stream: InputStream? 401 #endif 402 internal var _data: Data? 403 404 internal var _chunkSize = Int(4096 * 32) // a suitably large number for a decent chunk size 405 // This chunk of data stores the head of the stream. We know we have enough information for encoding 406 // when there are atleast 4 bytes in here. 407 internal var _bomChunk: Data? 408 fileprivate var _parserContext: _CFXMLInterfaceParserContext? 409 internal var _delegateAborted = false 410 internal var _url: URL? 411 internal var _namespaces = [[String:String]]() 412 413 // initializes the parser with the specified URL. 414 public convenience init?(contentsOf url: URL) { 415 #if os(WASI) 416 return nil 417 #else 418 setupXMLParsing() 419 if url.isFileURL { 420 if let stream = InputStream(url: url) { 421 self.init(stream: stream) 422 _url = url 423 } else { 424 return nil 425 } 426 } else { 427 do { 428 let data = try Data(contentsOf: url) 429 self.init(data: data) 430 self._url = url 431 } catch { 432 return nil 433 } 434 } 435 #endif 436 } 437 438 // create the parser from data 439 public init(data: Data) { 440 setupXMLParsing() 441 _data = data 442 _handler = _CFXMLInterfaceCreateSAXHandler() 443 _parserContext = nil 444 } 445 446 deinit { 447 _CFXMLInterfaceDestroySAXHandler(_handler) 448 _CFXMLInterfaceDestroyContext(_parserContext) 449 } 450 451 #if !os(WASI) 452 //create a parser that incrementally pulls data from the specified stream and parses it. 453 public init(stream: InputStream) { 454 setupXMLParsing() 455 _stream = stream 456 _handler = _CFXMLInterfaceCreateSAXHandler() 457 _parserContext = nil 458 } 459 #endif 460 461 open weak var delegate: XMLParserDelegate? 462 463 open var shouldProcessNamespaces: Bool = false 464 open var shouldReportNamespacePrefixes: Bool = false 465 466 //defaults to XMLNode.ExternalEntityResolvingPolicy.never 467 open var externalEntityResolvingPolicy: ExternalEntityResolvingPolicy = .never 468 469 open var allowedExternalEntityURLs: Set<URL>? 470 471 #if os(WASI) 472 private static var _currentParser: XMLParser? 473 #endif 474 475 internal static func currentParser() -> XMLParser? { 476 #if os(WASI) 477 return _currentParser 478 #else 479 if let current = Thread.current.threadDictionary["__CurrentNSXMLParser"] { 480 return current as? XMLParser 481 } else { 482 return nil 483 } 484 #endif 485 } 486 487 internal static func setCurrentParser(_ parser: XMLParser?) { 488 #if os(WASI) 489 _currentParser = parser 490 #else 491 if let p = parser { 492 Thread.current.threadDictionary["__CurrentNSXMLParser"] = p 493 } else { 494 Thread.current.threadDictionary.removeObject(forKey: "__CurrentNSXMLParser") 495 } 496 #endif 497 } 498 499 internal func _handleParseResult(_ parseResult: Int32) -> Bool { 500 if parseResult == 0 { 501 return true 502 } else { 503 if _parserError == nil { 504 _parserError = NSError(domain: XMLParser.errorDomain, code: Int(parseResult)) 505 } 506 } 507 return false 508 } 509 510 internal func parseData(_ data: Data, lastChunkOfData: Bool = false) -> Bool { 511 _CFXMLInterfaceSetStructuredErrorFunc(interface, _structuredErrorFunc) 512 defer { _CFXMLInterfaceSetStructuredErrorFunc(interface, nil) } 513 514 let handler: _CFXMLInterfaceSAXHandler? = (delegate != nil ? _handler : nil) 515 let unparsedData: Data 516 // If the parser context is nil, we have not received enough bytes to create the push parser 517 if _parserContext == nil { 518 // Look at the bomChunk and this data 519 let bomChunk: Data = { 520 guard var bomChunk = _bomChunk else { 521 return data 522 } 523 bomChunk.append(data) 524 return bomChunk 525 }() 526 // If we have not received 4 bytes, save the bomChunk for next pass 527 if bomChunk.count < 4 { 528 _bomChunk = bomChunk 529 return true 530 } 531 // Prepare options (substitute entities, recover on errors) 532 var options = _kCFXMLInterfaceRecover | _kCFXMLInterfaceNoEnt 533 if shouldResolveExternalEntities { 534 options |= _kCFXMLInterfaceDTDLoad 535 } 536 if handler == nil { 537 options |= (_kCFXMLInterfaceNoError | _kCFXMLInterfaceNoWarning) 538 } 539 540 // Create the push context with the first 4 bytes 541 bomChunk.withUnsafeBytes { (rawBuffer: UnsafeRawBufferPointer) in 542 let bytes = rawBuffer.baseAddress!.assumingMemoryBound(to: CChar.self) 543 _parserContext = _CFXMLInterfaceCreatePushParserCtxt(handler, interface, bytes, 4, nil) 544 } 545 guard _parserContext != nil else { 546 if _parserError == nil { 547 _parserError = NSError(domain: XMLParser.errorDomain, code: ErrorCode.outOfMemoryError.rawValue) 548 } 549 return false 550 }; 551 _CFXMLInterfaceCtxtUseOptions(_parserContext, options) 552 // Prepare the remaining data for parsing 553 let dataRange = bomChunk.indices 554 let unparsed = Range(uncheckedBounds: (dataRange.startIndex.advanced(by: 4), dataRange.endIndex)) 555 unparsedData = bomChunk.subdata(in: unparsed) 556 } 557 else { 558 unparsedData = data 559 } 560 561 let parseResult = unparsedData.withUnsafeBytes { (rawBuffer: UnsafeRawBufferPointer) -> Int32 in 562 let bytes = rawBuffer.baseAddress!.assumingMemoryBound(to: CChar.self) 563 return _CFXMLInterfaceParseChunk(_parserContext, bytes, Int32(unparsedData.count), lastChunkOfData ? 1 : 0) 564 } 565 566 let result = _handleParseResult(parseResult) 567 return result 568 } 569 570 #if !os(WASI) 571 internal func parseFrom(_ stream : InputStream) -> Bool { 572 var result = true 573 574 guard let buffer = malloc(_chunkSize)?.bindMemory(to: UInt8.self, capacity: _chunkSize) else { return false } 575 defer { free(buffer) } 576 577 stream.open() 578 defer { stream.close() } 579 parseLoop: while result { 580 switch stream.read(buffer, maxLength: _chunkSize) { 581 case let len where len > 0: 582 let data = Data(bytesNoCopy: buffer, count: len, deallocator: .none) 583 result = parseData(data) 584 case 0: 585 result = parseData(Data(), lastChunkOfData: true) 586 break parseLoop 587 default: // See SR-13516, should be `case ..<0:` 588 result = false 589 if _parserError == nil { 590 _parserError = stream.streamError 591 } 592 593 break parseLoop 594 } 595 } 596 597 return result 598 } 599 #else 600 internal func parse(from data: Data) -> Bool { 601 var result = true 602 var chunkStart = 0 603 var chunkEnd = min(_chunkSize, data.count) 604 while result { 605 if chunkStart >= data.count || chunkEnd >= data.count { 606 break 607 } 608 let chunk = data[chunkStart..<chunkEnd] 609 result = parseData(chunk) 610 chunkStart = chunkEnd 611 chunkEnd = min(chunkEnd + _chunkSize, data.count) 612 } 613 return result 614 } 615 #endif 616 617 // called to start the event-driven parse. Returns YES in the event of a successful parse, and NO in case of error. 618 open func parse() -> Bool { 619 #if os(WASI) 620 return _data.map { parse(from: $0) } ?? false 621 #else 622 XMLParser.setCurrentParser(self) 623 defer { XMLParser.setCurrentParser(nil) } 624 625 if _stream != nil { 626 return parseFrom(_stream!) 627 } else if _data != nil { 628 return parseData(_data!, lastChunkOfData: true) 629 } 630 631 return false 632 #endif 633 } 634 635 // called by the delegate to stop the parse. The delegate will get an error message sent to it. 636 open func abortParsing() { 637 if let context = _parserContext { 638 _CFXMLInterfaceStopParser(context) 639 _delegateAborted = true 640 } 641 } 642 643 internal var _parserError: Error? 644 645 // can be called after a parse is over to determine parser state. 646 open var parserError: Error? { 647 return _parserError 648 } 649 650 //Toggles between disabling external entities entirely, and the current setting of the 'externalEntityResolvingPolicy'. 651 //The 'externalEntityResolvingPolicy' property should be used instead of this, unless targeting 10.9/7.0 or earlier 652 open var shouldResolveExternalEntities: Bool = false 653 654 // Once a parse has begun, the delegate may be interested in certain parser state. These methods will only return meaningful information during parsing, or after an error has occurred. 655 open var publicID: String? { 656 return nil 657 } 658 659 open var systemID: String? { 660 return nil 661 } 662 663 open var lineNumber: Int { 664 return Int(_CFXMLInterfaceSAX2GetLineNumber(_parserContext)) 665 } 666 667 open var columnNumber: Int { 668 return Int(_CFXMLInterfaceSAX2GetColumnNumber(_parserContext)) 669 } 670 671 internal func _pushNamespaces(_ ns: [String:String]) { 672 _namespaces.append(ns) 673 if let del = self.delegate { 674 ns.forEach { 675 del.parser(self, didStartMappingPrefix: $0.0, toURI: $0.1) 676 } 677 } 678 } 679 680 internal func _popNamespaces() { 681 let ns = _namespaces.removeLast() 682 if let del = self.delegate { 683 ns.forEach { 684 del.parser(self, didEndMappingPrefix: $0.0) 685 } 686 } 687 } 688 } 689 690 /* 691 692 For the discussion of event methods, assume the following XML: 693 694 <?xml version="1.0" encoding="UTF-8"?> 695 <?xml-stylesheet type='text/css' href='cvslog.css'?> 696 <!DOCTYPE cvslog SYSTEM "cvslog.dtd"> 697 <cvslog xmlns="http://xml.apple.com/cvslog"> 698 <radar:radar xmlns:radar="http://xml.apple.com/radar"> 699 <radar:bugID>2920186</radar:bugID> 700 <radar:title>API/NSXMLParser: there ought to be an NSXMLParser</radar:title> 701 </radar:radar> 702 </cvslog> 703 704 */ 705 706 // The parser's delegate is informed of events through the methods in the NSXMLParserDelegateEventAdditions category. 707 public protocol XMLParserDelegate: AnyObject { 708 709 // Document handling methods 710 func parserDidStartDocument(_ parser: XMLParser) 711 // sent when the parser begins parsing of the document. 712 func parserDidEndDocument(_ parser: XMLParser) 713 // sent when the parser has completed parsing. If this is encountered, the parse was successful. 714 715 // DTD handling methods for various declarations. 716 func parser(_ parser: XMLParser, foundNotationDeclarationWithName name: String, publicID: String?, systemID: String?) 717 718 func parser(_ parser: XMLParser, foundUnparsedEntityDeclarationWithName name: String, publicID: String?, systemID: String?, notationName: String?) 719 720 func parser(_ parser: XMLParser, foundAttributeDeclarationWithName attributeName: String, forElement elementName: String, type: String?, defaultValue: String?) 721 722 func parser(_ parser: XMLParser, foundElementDeclarationWithName elementName: String, model: String) 723 724 func parser(_ parser: XMLParser, foundInternalEntityDeclarationWithName name: String, value: String?) 725 726 func parser(_ parser: XMLParser, foundExternalEntityDeclarationWithName name: String, publicID: String?, systemID: String?) 727 728 func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) 729 // sent when the parser finds an element start tag. 730 // In the case of the cvslog tag, the following is what the delegate receives: 731 // elementName == cvslog, namespaceURI == http://xml.apple.com/cvslog, qualifiedName == cvslog 732 // In the case of the radar tag, the following is what's passed in: 733 // elementName == radar, namespaceURI == http://xml.apple.com/radar, qualifiedName == radar:radar 734 // If namespace processing >isn't< on, the xmlns:radar="http://xml.apple.com/radar" is returned as an attribute pair, the elementName is 'radar:radar' and there is no qualifiedName. 735 736 func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) 737 // sent when an end tag is encountered. The various parameters are supplied as above. 738 739 func parser(_ parser: XMLParser, didStartMappingPrefix prefix: String, toURI namespaceURI: String) 740 // sent when the parser first sees a namespace attribute. 741 // In the case of the cvslog tag, before the didStartElement:, you'd get one of these with prefix == @"" and namespaceURI == @"http://xml.apple.com/cvslog" (i.e. the default namespace) 742 // In the case of the radar:radar tag, before the didStartElement: you'd get one of these with prefix == @"radar" and namespaceURI == @"http://xml.apple.com/radar" 743 744 func parser(_ parser: XMLParser, didEndMappingPrefix prefix: String) 745 // sent when the namespace prefix in question goes out of scope. 746 747 func parser(_ parser: XMLParser, foundCharacters string: String) 748 // This returns the string of the characters encountered thus far. You may not necessarily get the longest character run. The parser reserves the right to hand these to the delegate as potentially many calls in a row to -parser:foundCharacters: 749 750 func parser(_ parser: XMLParser, foundIgnorableWhitespace whitespaceString: String) 751 // The parser reports ignorable whitespace in the same way as characters it's found. 752 753 func parser(_ parser: XMLParser, foundProcessingInstructionWithTarget target: String, data: String?) 754 // The parser reports a processing instruction to you using this method. In the case above, target == @"xml-stylesheet" and data == @"type='text/css' href='cvslog.css'" 755 756 func parser(_ parser: XMLParser, foundComment comment: String) 757 // A comment (Text in a <!-- --> block) is reported to the delegate as a single string 758 759 func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) 760 // this reports a CDATA block to the delegate as an NSData. 761 762 func parser(_ parser: XMLParser, resolveExternalEntityName name: String, systemID: String?) -> Data? 763 // this gives the delegate an opportunity to resolve an external entity itself and reply with the resulting data. 764 765 func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) 766 // ...and this reports a fatal error to the delegate. The parser will stop parsing. 767 768 func parser(_ parser: XMLParser, validationErrorOccurred validationError: Error) 769 } 770 771 public extension XMLParserDelegate { 772 773 func parserDidStartDocument(_ parser: XMLParser) { } 774 func parserDidEndDocument(_ parser: XMLParser) { } 775 776 func parser(_ parser: XMLParser, foundNotationDeclarationWithName name: String, publicID: String?, systemID: String?) { } 777 778 func parser(_ parser: XMLParser, foundUnparsedEntityDeclarationWithName name: String, publicID: String?, systemID: String?, notationName: String?) { } 779 780 func parser(_ parser: XMLParser, foundAttributeDeclarationWithName attributeName: String, forElement elementName: String, type: String?, defaultValue: String?) { } 781 782 func parser(_ parser: XMLParser, foundElementDeclarationWithName elementName: String, model: String) { } 783 784 func parser(_ parser: XMLParser, foundInternalEntityDeclarationWithName name: String, value: String?) { } 785 786 func parser(_ parser: XMLParser, foundExternalEntityDeclarationWithName name: String, publicID: String?, systemID: String?) { } 787 788 func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { } 789 790 func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { } 791 792 func parser(_ parser: XMLParser, didStartMappingPrefix prefix: String, toURI namespaceURI: String) { } 793 794 func parser(_ parser: XMLParser, didEndMappingPrefix prefix: String) { } 795 796 func parser(_ parser: XMLParser, foundCharacters string: String) { } 797 798 func parser(_ parser: XMLParser, foundIgnorableWhitespace whitespaceString: String) { } 799 800 func parser(_ parser: XMLParser, foundProcessingInstructionWithTarget target: String, data: String?) { } 801 802 func parser(_ parser: XMLParser, foundComment comment: String) { } 803 804 func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) { } 805 806 func parser(_ parser: XMLParser, resolveExternalEntityName name: String, systemID: String?) -> Data? { return nil } 807 808 func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { } 809 810 func parser(_ parser: XMLParser, validationErrorOccurred validationError: Error) { } 811 } 812 813 extension XMLParser { 814 // If validation is on, this will report a fatal validation error to the delegate. The parser will stop parsing. 815 public static let errorDomain: String = "NSXMLParserErrorDomain" // for use with NSError. 816 817 // Error reporting 818 public enum ErrorCode : Int { 819 820 821 case internalError 822 823 case outOfMemoryError 824 825 case documentStartError 826 827 case emptyDocumentError 828 829 case prematureDocumentEndError 830 831 case invalidHexCharacterRefError 832 833 case invalidDecimalCharacterRefError 834 835 case invalidCharacterRefError 836 837 case invalidCharacterError 838 839 case characterRefAtEOFError 840 841 case characterRefInPrologError 842 843 case characterRefInEpilogError 844 845 case characterRefInDTDError 846 847 case entityRefAtEOFError 848 849 case entityRefInPrologError 850 851 case entityRefInEpilogError 852 853 case entityRefInDTDError 854 855 case parsedEntityRefAtEOFError 856 857 case parsedEntityRefInPrologError 858 859 case parsedEntityRefInEpilogError 860 861 case parsedEntityRefInInternalSubsetError 862 863 case entityReferenceWithoutNameError 864 865 case entityReferenceMissingSemiError 866 867 case parsedEntityRefNoNameError 868 869 case parsedEntityRefMissingSemiError 870 871 case undeclaredEntityError 872 873 case unparsedEntityError 874 875 case entityIsExternalError 876 877 case entityIsParameterError 878 879 case unknownEncodingError 880 881 case encodingNotSupportedError 882 883 case stringNotStartedError 884 885 case stringNotClosedError 886 887 case namespaceDeclarationError 888 889 case entityNotStartedError 890 891 case entityNotFinishedError 892 893 case lessThanSymbolInAttributeError 894 895 case attributeNotStartedError 896 897 case attributeNotFinishedError 898 899 case attributeHasNoValueError 900 901 case attributeRedefinedError 902 903 case literalNotStartedError 904 905 case literalNotFinishedError 906 907 case commentNotFinishedError 908 909 case processingInstructionNotStartedError 910 911 case processingInstructionNotFinishedError 912 913 case notationNotStartedError 914 915 case notationNotFinishedError 916 917 case attributeListNotStartedError 918 919 case attributeListNotFinishedError 920 921 case mixedContentDeclNotStartedError 922 923 case mixedContentDeclNotFinishedError 924 925 case elementContentDeclNotStartedError 926 927 case elementContentDeclNotFinishedError 928 929 case xmlDeclNotStartedError 930 931 case xmlDeclNotFinishedError 932 933 case conditionalSectionNotStartedError 934 935 case conditionalSectionNotFinishedError 936 937 case externalSubsetNotFinishedError 938 939 case doctypeDeclNotFinishedError 940 941 case misplacedCDATAEndStringError 942 943 case cdataNotFinishedError 944 945 case misplacedXMLDeclarationError 946 947 case spaceRequiredError 948 949 case separatorRequiredError 950 951 case nmtokenRequiredError 952 953 case nameRequiredError 954 955 case pcdataRequiredError 956 957 case uriRequiredError 958 959 case publicIdentifierRequiredError 960 961 case ltRequiredError 962 963 case gtRequiredError 964 965 case ltSlashRequiredError 966 967 case equalExpectedError 968 969 case tagNameMismatchError 970 971 case unfinishedTagError 972 973 case standaloneValueError 974 975 case invalidEncodingNameError 976 977 case commentContainsDoubleHyphenError 978 979 case invalidEncodingError 980 981 case externalStandaloneEntityError 982 983 case invalidConditionalSectionError 984 985 case entityValueRequiredError 986 987 case notWellBalancedError 988 989 case extraContentError 990 991 case invalidCharacterInEntityError 992 993 case parsedEntityRefInInternalError 994 995 case entityRefLoopError 996 997 case entityBoundaryError 998 999 case invalidURIError 1000 1001 case uriFragmentError 1002 1003 case noDTDError 1004 1005 case delegateAbortedParseError 1006 } 1007 } 1008 1009 internal func NSUnimplemented(_ fn: String = #function, file: StaticString = #file, line: UInt = #line) -> Never { 1010 #if os(Android) 1011 NSLog("\(fn) is not yet implemented. \(file):\(line)") 1012 #endif 1013 fatalError("\(fn) is not yet implemented", file: file, line: line) 1014 } 1015 1016 internal func NSUnsupported(_ fn: String = #function, file: StaticString = #file, line: UInt = #line) -> Never { 1017 #if os(Android) 1018 NSLog("\(fn) is not supported on this platform. \(file):\(line)") 1019 #endif 1020 fatalError("\(fn) is not supported on this platform", file: file, line: line) 1021 } 1022 1023 extension NSObject { 1024 func withUnretainedReference<T, R>(_ work: (UnsafePointer<T>) -> R) -> R { 1025 let selfPtr = Unmanaged.passUnretained(self).toOpaque().assumingMemoryBound(to: T.self) 1026 return work(selfPtr) 1027 } 1028 1029 func withOpaqueUnretainedReference<R>(_ work: (UnsafeMutableRawPointer) -> R) -> R { 1030 let selfPtr = Unmanaged.passUnretained(self).toOpaque() 1031 return work(selfPtr) 1032 } 1033 } 1034 1035 func setupXMLParsing() { 1036 _CFSetupXMLInterface() 1037 _CFSetupXMLBridgeIfNeededUsingBlock { 1038 __CFSwiftXMLParserBridge.CFBridge = CF.originalBridge 1039 __CFSwiftXMLParserBridge.currentParser = _NSXMLParserCurrentParser 1040 __CFSwiftXMLParserBridge._xmlExternalEntityWithURL = _NSXMLParserExternalEntityWithURL 1041 __CFSwiftXMLParserBridge.getContext = _NSXMLParserGetContext 1042 __CFSwiftXMLParserBridge.internalSubset = _NSXMLParserInternalSubset 1043 __CFSwiftXMLParserBridge.isStandalone = _NSXMLParserIsStandalone 1044 __CFSwiftXMLParserBridge.hasInternalSubset = _NSXMLParserHasInternalSubset 1045 __CFSwiftXMLParserBridge.hasExternalSubset = _NSXMLParserHasExternalSubset 1046 __CFSwiftXMLParserBridge.getEntity = _NSXMLParserGetEntity 1047 __CFSwiftXMLParserBridge.notationDecl = _NSXMLParserNotationDecl 1048 __CFSwiftXMLParserBridge.attributeDecl = _NSXMLParserAttributeDecl 1049 __CFSwiftXMLParserBridge.elementDecl = _NSXMLParserElementDecl 1050 __CFSwiftXMLParserBridge.unparsedEntityDecl = _NSXMLParserUnparsedEntityDecl 1051 __CFSwiftXMLParserBridge.startDocument = _NSXMLParserStartDocument 1052 __CFSwiftXMLParserBridge.endDocument = _NSXMLParserEndDocument 1053 __CFSwiftXMLParserBridge.startElementNs = _NSXMLParserStartElementNs 1054 __CFSwiftXMLParserBridge.endElementNs = _NSXMLParserEndElementNs 1055 __CFSwiftXMLParserBridge.characters = _NSXMLParserCharacters 1056 __CFSwiftXMLParserBridge.processingInstruction = _NSXMLParserProcessingInstruction 1057 __CFSwiftXMLParserBridge.cdataBlock = _NSXMLParserCdataBlock 1058 __CFSwiftXMLParserBridge.comment = _NSXMLParserComment 1059 __CFSwiftXMLParserBridge.externalSubset = _NSXMLParserExternalSubset 1060 } 1061 }