20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
-
-
-
-
-
-
+
+
+
-
-
+
+
-
+
+
-
+
+
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
+
-
-
+
-
+
-
+
-
+
-
-
-
-
-
+
+
-
+
-
-
+
+
-
-
+
-
-
-
+
+
-
+
-
-
+
+
-
-
-
-
+
+
-
-
+
-
-
-
-
-
+
+
-
+
-
-
+
+
+
+
-
-
+
+
|
* POSSIBILITY OF SUCH DAMAGE.
*/
import ObjFW
import ObjFWBridge
class SiteStorage: OFObject {
private typealias Storage =
OFMutableDictionary<OFString, OFDictionary<OFNumber, AnyObject>>
private static let lengthField = OFNumber(uInt8: 0)
private static let legacyField = OFNumber(uInt8: 1)
private static let keyFileField = OFNumber(uInt8: 2)
private static let lengthField = NSNumber(value: 0)
private static let legacyField = NSNumber(value: 1)
private static let keyFileField = NSNumber(value: 2)
private var path: OFString
private var storage: Storage
private var sites: OFArray<OFString>
private var storage: [String: [NSNumber: AnyObject]]
private var sites: [String]
override init() {
let fileManager = OFFileManager.default
let userDataPath = OFSystemInfo.userDataPath!
if !fileManager.directoryExists(atPath: userDataPath) {
fileManager.createDirectory(atPath: userDataPath)
}
let path = userDataPath.appendingPathComponent(
OFString(utf8String: "sites.msgpack"))
var storage: Storage? = nil
var storage: [String: [NSNumber: AnyObject]]? = nil
OFException.try({
storage = OFData(contentsOfFile: path).messagePackValue as? Storage
let decoded = (OFData(contentsOfFile: path).messagePackValue)
as? OFDictionary<OFString, OFDictionary<OFNumber, AnyObject>>
storage =
(decoded?.nsObject as? [String: [NSNumber: AnyObject]]) ?? [:]
}, catch: { (OFException) in
storage = OFMutableDictionary()
storage = [:]
})
self.path = path
self.storage = storage!
self.sites = self.storage.allKeys.sorted
self.sites = self.storage.keys.sorted()
}
func sites(withFilter filter: OFString?) -> OFArray<OFString> {
func sites(withFilter filter: String?) -> [String] {
// FIXME: We need case folding here, but there is no method for it yet.
let filter = filter?.lowercase
return storage.keys.sorted().filter({ (name) in
if let filter = filter {
return name.localizedCaseInsensitiveContains(filter)
return storage.allKeys.sorted.filteredArray({
(name: Any, index: size_t) -> Bool in
if filter == nil {
return true
}
}
let name = name as! OFString
return name.lowercase.contains(filter!)
return true
})
}
func hasSite(_ name: OFString) -> Bool {
func hasSite(_ name: String) -> Bool {
return (storage[name] != nil)
}
func length(forSite name: OFString) -> size_t {
func length(forSite name: String) -> UInt {
guard let site = storage[name] else {
OFInvalidArgumentException().throw()
}
return (site[SiteStorage.lengthField] as! OFNumber).sizeValue
return (site[SiteStorage.lengthField] as! NSNumber).uintValue
}
func isLegacy(site name: OFString) -> Bool {
guard let site = storage[name] else {
OFInvalidArgumentException().throw()
}
func isLegacy(site name: String) -> Bool {
guard let site = storage[name] else { return false }
return (site[SiteStorage.legacyField] as! OFNumber).boolValue
return (site[SiteStorage.legacyField] as! NSNumber).boolValue
}
func keyFile(forSite name: OFString) -> OFString? {
guard let site = storage[name] else {
func keyFile(forSite name: String) -> String? {
guard let site = storage[name] else { return nil }
OFInvalidArgumentException().throw()
}
let keyFile = site[SiteStorage.keyFileField]
if keyFile is OFNull {
guard let keyFile = site[SiteStorage.keyFileField], !(keyFile is NSNull)
else {
return nil
}
return keyFile as? OFString
return keyFile as? String
}
func setSite(_ name: OFString, length: size_t, legacy: Bool,
keyFile: OFString?) {
func setSite(_ name: String, length: UInt, isLegacy: Bool,
keyFile: String?) {
let siteDictionary = OFMutableDictionary<OFNumber, AnyObject>()
siteDictionary.setObject(OFNumber(size: length),
forKey: SiteStorage.lengthField)
var siteDictionary: [NSNumber: AnyObject] = [
SiteStorage.lengthField: NSNumber(value: length),
siteDictionary.setObject(OFNumber(bool: legacy),
forKey: SiteStorage.legacyField)
SiteStorage.legacyField: NSNumber(value: isLegacy),
if keyFile != nil {
siteDictionary.setObject(keyFile!, forKey: SiteStorage.keyFileField)
}
siteDictionary.makeImmutable()
]
siteDictionary[SiteStorage.keyFileField] = keyFile as AnyObject?
storage.setObject(siteDictionary, forKey: name)
storage[name] = siteDictionary
self.update()
}
func removeSite(_ name: OFString) {
self.storage.removeObject(forKey: name)
func removeSite(_ name: String) {
storage[name] = nil
self.update()
}
private func update() {
let ofStorage = (storage as NSDictionary).ofObject
storage.messagePackRepresentation.write(toFile: path)
sites = storage.allKeys.sorted
ofStorage.messagePackRepresentation.write(toFile: path)
sites = storage.keys.sorted()
}
}
|