iOS web browser with a focus on security and privacy
1/*
2 * Endless
3 * Copyright (c) 2015 joshua stein <jcs@jcs.org>
4 *
5 * See LICENSE file for redistribution terms.
6 */
7
8#import "AppDelegate.h"
9#import "HostSettings.h"
10
11@implementation HostSettings
12
13static NSMutableDictionary *_hosts;
14
15+ (NSDictionary *)defaults
16{
17 return @{
18 HOST_SETTINGS_KEY_TLS: HOST_SETTINGS_TLS_AUTO,
19 HOST_SETTINGS_KEY_CSP: HOST_SETTINGS_CSP_OPEN,
20 HOST_SETTINGS_KEY_BLOCK_LOCAL_NETS: HOST_SETTINGS_VALUE_YES,
21 HOST_SETTINGS_KEY_ALLOW_MIXED_MODE: HOST_SETTINGS_VALUE_NO,
22 HOST_SETTINGS_KEY_WHITELIST_COOKIES: HOST_SETTINGS_VALUE_NO,
23 };
24}
25
26+ (NSString *)hostSettingsPath
27{
28 NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
29 return [path stringByAppendingPathComponent:@"host_settings.plist"];
30}
31
32+ (NSMutableDictionary *)hosts
33{
34 if (!_hosts) {
35 NSFileManager *fileManager = [NSFileManager defaultManager];
36 if ([fileManager fileExistsAtPath:[self hostSettingsPath]]) {
37 NSDictionary *td = [NSMutableDictionary dictionaryWithContentsOfFile:[self hostSettingsPath]];
38
39 _hosts = [[NSMutableDictionary alloc] initWithCapacity:[td count]];
40
41 for (NSString *k in [td allKeys])
42 [_hosts setObject:[[HostSettings alloc] initForHost:k withDict:[td objectForKey:k]] forKey:k];
43 }
44 else
45 _hosts = [[NSMutableDictionary alloc] initWithCapacity:20];
46
47 /* ensure default host exists */
48 if (![_hosts objectForKey:HOST_SETTINGS_DEFAULT]) {
49 HostSettings *hs = [[HostSettings alloc] initForHost:HOST_SETTINGS_DEFAULT withDict:nil];
50 [hs save];
51 [HostSettings persist];
52 }
53 }
54
55 return _hosts;
56}
57
58+ (void)persist
59{
60 if ([(AppDelegate *)[[UIApplication sharedApplication] delegate] areTesting])
61 abort();
62
63 NSMutableDictionary *td = [[NSMutableDictionary alloc] initWithCapacity:[[self hosts] count]];
64 for (NSString *k in [[self hosts] allKeys])
65 [td setObject:[[[self hosts] objectForKey:k] dict] forKey:k];
66
67 [td writeToFile:[self hostSettingsPath] atomically:YES];
68}
69
70+ (HostSettings *)forHost:(NSString *)host
71{
72 return [[self hosts] objectForKey:host];
73}
74
75+ (HostSettings *)settingsOrDefaultsForHost:(NSString *)host
76{
77 HostSettings *hs = [self forHost:host];
78 if (!hs) {
79 /* for a host of x.y.z.example.com, try y.z.example.com, z.example.com, example.com, etc. */
80 NSArray *hostp = [host componentsSeparatedByString:@"."];
81 for (int i = 1; i < [hostp count]; i++) {
82 NSString *wc = [[hostp subarrayWithRange:NSMakeRange(i, [hostp count] - i)] componentsJoinedByString:@"."];
83
84 if ((hs = [HostSettings forHost:wc])) {
85#ifdef TRACE_HOST_SETTINGS
86 NSLog(@"[HostSettings] found entry for component %@ in %@", wc, host);
87#endif
88 break;
89 }
90 }
91 }
92
93 if (!hs)
94 hs = [self defaultHostSettings];
95
96 return hs;
97}
98
99+ (BOOL)removeSettingsForHost:(NSString *)host
100{
101 HostSettings *h = [self forHost:host];
102 if (h && ![h isDefault]) {
103 [[self hosts] removeObjectForKey:host];
104 return YES;
105 }
106
107 return NO;
108}
109
110+ (HostSettings *)defaultHostSettings
111{
112 return [self forHost:HOST_SETTINGS_DEFAULT];
113}
114
115#if DEBUG
116/* just for testing */
117+ (void)overrideHosts:(NSMutableDictionary *)hosts
118{
119 _hosts = hosts;
120}
121#endif
122
123+ (NSArray *)sortedHosts
124{
125 NSMutableArray *sorted = [[NSMutableArray alloc] initWithArray:[[self hosts] allKeys]];
126 [sorted removeObject:HOST_SETTINGS_DEFAULT];
127 [sorted sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
128 [sorted insertObject:HOST_SETTINGS_DEFAULT atIndex:0];
129
130 return [[NSArray alloc] initWithArray:sorted];
131}
132
133- (HostSettings *)initForHost:(NSString *)host withDict:(NSDictionary *)dict
134{
135 self = [super init];
136
137 host = [host lowercaseString];
138
139 if (dict)
140 _dict = [[NSMutableDictionary alloc] initWithDictionary:dict];
141 else
142 _dict = [[NSMutableDictionary alloc] initWithCapacity:10];
143
144 [_dict setObject:host forKey:HOST_SETTINGS_KEY_HOST];
145
146 return self;
147}
148
149- (void)save
150{
151 [[HostSettings hosts] setObject:self forKey:[[self dict] objectForKey:HOST_SETTINGS_KEY_HOST]];
152}
153
154- (BOOL)isDefault
155{
156 return ([[[self dict] objectForKey:HOST_SETTINGS_KEY_HOST] isEqualToString:HOST_SETTINGS_DEFAULT]);
157}
158
159- (NSString *)setting:(NSString *)setting
160{
161 NSString *val = [[self dict] objectForKey:setting];
162 if (val != NULL && ![val isKindOfClass:[NSString class]]) {
163 NSLog(@"[HostSettings] setting %@ for %@ was %@, not NSString", setting, [self hostname], [val class]);
164 val = nil;
165 }
166
167 if (val != nil && ![val isEqualToString:@""] && ![val isEqualToString:HOST_SETTINGS_DEFAULT])
168 return val;
169
170 if (val == nil && [self isDefault])
171 /* default host entries must have a value for every setting */
172 return [[HostSettings defaults] objectForKey:setting];
173
174 return nil;
175}
176
177- (NSString *)settingOrDefault:(NSString *)setting
178{
179 NSString *val = [self setting:setting];
180 if (val == nil)
181 /* try default host settings */
182 val = [[HostSettings defaultHostSettings] setting:setting];
183
184 return val;
185}
186
187- (BOOL)boolSettingOrDefault:(NSString *)setting
188{
189 NSString *val = [self settingOrDefault:setting];
190 if (val != nil && [val isEqualToString:HOST_SETTINGS_VALUE_YES])
191 return YES;
192 else
193 return NO;
194}
195
196- (void)setSetting:(NSString *)setting toValue:(NSString *)value
197{
198 if (value == nil || [value isEqualToString:HOST_SETTINGS_DEFAULT]) {
199 [[self dict] removeObjectForKey:setting];
200 return;
201 }
202
203 if ([setting isEqualToString:HOST_SETTINGS_KEY_TLS]) {
204 if (!([value isEqualToString:HOST_SETTINGS_TLS_12] || [value isEqualToString:HOST_SETTINGS_TLS_AUTO]))
205 return;
206 }
207
208 [[self dict] setObject:value forKey:setting];
209}
210
211- (NSString *)hostname
212{
213 if ([self isDefault])
214 return HOST_SETTINGS_HOST_DEFAULT_LABEL;
215 else
216 return [[self dict] objectForKey:HOST_SETTINGS_KEY_HOST];
217}
218
219- (void)setHostname:(NSString *)hostname
220{
221 if ([self isDefault] || !hostname || [hostname isEqualToString:@""])
222 return;
223
224 hostname = [hostname lowercaseString];
225
226 [[HostSettings hosts] removeObjectForKey:[[self dict] objectForKey:HOST_SETTINGS_KEY_HOST]];
227 [[self dict] setObject:hostname forKey:HOST_SETTINGS_KEY_HOST];
228 [[HostSettings hosts] setObject:self forKey:hostname];
229}
230
231@end