-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSSYWeblocGuy.m
173 lines (144 loc) · 6.22 KB
/
SSYWeblocGuy.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#import "SSYWeblocGuy.h"
/* Prior to 2016-08-10, this class did not have a proper state machine for
parsing the XML expected in a .webloc file. I just took the last string that
was parsed. This worked because there are only two strings in the file:
<key>URL</key>
<string>http://www.example.com</string>
and the second one was the one I wanted. And so I marked a TODO to fix
this someday.
Now, it is fixed, but I'm no sure if it is better or worse. The goal is to
future-proof in case Apple changes the .webloc format. But after looking at
this state machine, I'm not sure it's really any more future proof. It is
definitely less cheesy. Oh, well, the old code is in git if I want to go back.
*/
typedef enum {
SSYWeblocParserStateZero,
SSYWeblocParserStateInPlist,
SSYWeblocParserStateInDict,
SSYWeblocParserStateParsingAKey,
SSYWeblocParserStateParsingUrlString,
SSYWeblocParserStateGotAllWeNeed
} SSYWeblocParserState ;
@interface SSYWeblocGuy ()
@property (retain) NSMutableString* stringBeingParsed ;
@property (assign) SSYWeblocParserState parserState ;
@end
@implementation SSYWeblocGuy
- (void)dealloc {
[_stringBeingParsed release] ;
[super dealloc] ;
}
- (void) parser:(NSXMLParser*)parser
didStartElement:(NSString*)elementName
namespaceURI:(NSString*)namespaceURI
qualifiedName:(NSString*)qualifiedName
attributes:(NSDictionary*)attributeDict {
if ((_parserState == SSYWeblocParserStateZero) && [elementName isEqualToString:@"plist"]) {
_parserState = SSYWeblocParserStateInPlist ;
}
else if ((_parserState == SSYWeblocParserStateInPlist) && [elementName isEqualToString:@"dict"]) {
_parserState = SSYWeblocParserStateInDict ;
}
else if ((_parserState == SSYWeblocParserStateInDict) && [elementName isEqualToString:@"key"]) {
_parserState = SSYWeblocParserStateParsingAKey ;
}
else if ((_parserState == SSYWeblocParserStateParsingUrlString) && [elementName isEqualToString:@"string"]) {
if ([self.stringBeingParsed isEqualToString:@"URL"]) {
_parserState = SSYWeblocParserStateParsingUrlString ;
[self.stringBeingParsed setString:@""] ;
}
}
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ((_parserState == SSYWeblocParserStateParsingAKey) && [elementName isEqualToString:@"key"]) {
_parserState = SSYWeblocParserStateParsingUrlString ;
}
if ((_parserState == SSYWeblocParserStateParsingUrlString) && [elementName isEqualToString:@"string"]) {
_parserState = SSYWeblocParserStateGotAllWeNeed ;
}
}
- (void) parser:(NSXMLParser*)parser
foundCharacters:(NSString*)string {
if ((_parserState ==SSYWeblocParserStateParsingAKey) || (_parserState == SSYWeblocParserStateParsingUrlString)) {
/* I have not investigated why this method gets invoked with
string = @"\n" in between the real strings. I just filter them out… */
[[self stringBeingParsed] appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]] ;
}
}
- (void) parser:(NSXMLParser*)parser
parseErrorOccurred:(NSError*)error {
NSLog(@"Found error in XML: %@", error) ;
}
- (NSDictionary*)filenameAndUrlFromWeblocFileAtPath:(NSString*)path {
NSString* url = nil ;
if ([path.pathExtension isEqualToString:@"webloc"]) {
NSData* data = [NSData dataWithContentsOfFile:path] ;
/* The .webloc files produced by Safari 9.0 (macOS 10.11) or later
are .plist files. Look for that first. */
NSDictionary* dic = [NSPropertyListSerialization propertyListWithData:data
options:0
format:NULL
error:NULL] ;
url = [dic objectForKey:@"URL"] ;
if (!url) {
/* We are parsing a .webloc file produced by Safari 5 - 8, which
uses XML format. */
if (data.length > 0) {
NSXMLParser* parser = [[NSXMLParser alloc] initWithData:data] ;
[parser setDelegate:self] ;
self.parserState = SSYWeblocParserStateZero ;
NSMutableString* stringBeingParsed = [NSMutableString new] ;
self.stringBeingParsed = stringBeingParsed ;
[stringBeingParsed release] ;
[parser parse] ;
/* Note: -parse is synchronous and will not return until the parsing
is done or aborted. */
[parser release] ;
url = [self.stringBeingParsed copy] ;
[url autorelease] ;
self.stringBeingParsed = nil ;
}
}
}
NSDictionary* answer ;
if (url.length > 0) {
NSString* filename = [[path lastPathComponent] stringByDeletingPathExtension] ;
answer = [NSDictionary dictionaryWithObjectsAndKeys:
filename, @"filename",
url, @"url",
nil] ;
}
else {
answer = nil ;
}
return answer ;
}
+ (NSDictionary*)filenameAndUrlFromWeblocFileAtPath:(NSString*)path {
SSYWeblocGuy* instance = [[SSYWeblocGuy alloc] init] ;
NSDictionary* answer = [instance filenameAndUrlFromWeblocFileAtPath:path] ;
[instance release] ;
return answer ;
}
- (NSArray*)weblocFilenamesAndUrlsInPaths:(NSArray*)paths {
NSMutableArray* filenamesAndURLs = [NSMutableArray array] ;
for (NSString* path in paths) {
NSDictionary* filenameAndUrl = [self filenameAndUrlFromWeblocFileAtPath:path] ;
if (filenameAndUrl) {
[filenamesAndURLs addObject:filenameAndUrl] ;
}
}
if ([filenamesAndURLs count])
return [[[NSArray alloc] initWithArray:filenamesAndURLs] autorelease] ;
else
return nil ;
}
+ (NSArray*)weblocFilenamesAndUrlsInPaths:(NSArray*)paths {
SSYWeblocGuy* instance = [[SSYWeblocGuy alloc] init] ;
NSArray* answer = [instance weblocFilenamesAndUrlsInPaths:paths] ;
[instance release] ;
return answer ;
}
@end