/ Sources / FoundationXML / XMLParser.swift
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  }