File.swift 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // File.swift
  2. // This file is part of KeePassKit.
  3. //
  4. // Copyright © 2019 Maxime Epain. All rights reserved.
  5. //
  6. // KeePassKit is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // KeePassKit is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU General Public License
  17. // along with KeePassKit. If not, see <https://www.gnu.org/licenses/>.
  18. import Binary
  19. import Foundation
  20. public let FileSignature: UInt32 = 0x9AA2_D903
  21. public let BetaFileFormat: UInt32 = 0xB54B_FB66
  22. public let FileFormat: UInt32 = 0xB54B_FB67
  23. public struct Version {
  24. let major: UInt16
  25. let minor: UInt16
  26. }
  27. public class File {
  28. public let version: Version
  29. public let database: Database
  30. public required init(from input: Input) throws {
  31. version = Version(major: 0, minor: 0)
  32. database = try Database0(from: input)
  33. }
  34. public required init(from input: Input, compositeKey: CompositeKey) throws {
  35. version = try input.read()
  36. if version.major > 3 {
  37. database = try Database4(from: input, compositeKey: compositeKey)
  38. } else {
  39. database = try Database3(from: input, compositeKey: compositeKey)
  40. }
  41. }
  42. public convenience init(xml file: URL) throws {
  43. let bytes = try Bytes(contentsOf: file)
  44. let stream = Input(bytes: bytes)
  45. try self.init(from: stream)
  46. }
  47. public convenience init(from file: URL, compositeKey: CompositeKey) throws {
  48. let bytes = try Bytes(contentsOf: file)
  49. let stream = Input(bytes: bytes)
  50. guard try stream.read() == FileSignature else { throw KDBXError.invalidFileFormat }
  51. let format: UInt32 = try stream.read()
  52. guard
  53. format == BetaFileFormat ||
  54. format == FileFormat
  55. else { throw KDBXError.invalidFileFormat }
  56. try self.init(from: stream, compositeKey: compositeKey)
  57. }
  58. public func write(to output: Output, compositeKey: CompositeKey) throws {
  59. try output.write(FileSignature)
  60. try output.write(FileFormat)
  61. try output.write(version)
  62. try database.write(to: output, compositeKey: compositeKey)
  63. }
  64. }
  65. extension Version: Streamable {
  66. public init(from input: Input) throws {
  67. minor = try input.read()
  68. major = try input.read()
  69. }
  70. public func write(to output: Output) throws {
  71. try output.write(minor)
  72. try output.write(major)
  73. }
  74. }