| 1 | /* Copyright (C) 2008-2009 Peter Speck |
|---|
| 2 | * |
|---|
| 3 | * This program is free software: you can redistribute it and/or modify |
|---|
| 4 | * it under the terms of the GNU General Public License as published by |
|---|
| 5 | * the Free Software Foundation, either version 3 of the License, or |
|---|
| 6 | * (at your option) any later version. |
|---|
| 7 | * |
|---|
| 8 | * This program is distributed in the hope that it will be useful, |
|---|
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | * GNU General Public License for more details. |
|---|
| 12 | * |
|---|
| 13 | * You should have received a copy of the GNU General Public License |
|---|
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 15 | */ |
|---|
| 16 | |
|---|
| 17 | #import "NetworkSettingsUpdater.h" |
|---|
| 18 | #import "GBDebug.h" |
|---|
| 19 | |
|---|
| 20 | /* |
|---|
| 21 | Getting settings: |
|---|
| 22 | http://developer.apple.com/documentation/Networking/Reference/SysConfig/SCNetworkConfiguration/index.html |
|---|
| 23 | |
|---|
| 24 | Committing changes: |
|---|
| 25 | http://developer.apple.com/documentation/Networking/Reference/SysConfig/SCPreferences/CompositePage.html |
|---|
| 26 | */ |
|---|
| 27 | |
|---|
| 28 | @interface NetworkSettingsUpdater() |
|---|
| 29 | |
|---|
| 30 | - (BOOL)updateNetworkService:(SCNetworkServiceRef)serviceR; // returns YES if needs update. |
|---|
| 31 | |
|---|
| 32 | @end |
|---|
| 33 | |
|---|
| 34 | @implementation NetworkSettingsUpdater |
|---|
| 35 | |
|---|
| 36 | BOOL cfstreql(CFStringRef a, CFStringRef b); |
|---|
| 37 | BOOL cfstreql(CFStringRef a, CFStringRef b) |
|---|
| 38 | { |
|---|
| 39 | if ((a != NULL) != (b != NULL)) |
|---|
| 40 | return NO; |
|---|
| 41 | return !a || !CFStringCompare(a, b, 0); |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | - (id)initWithProxyPort:(int)proxyPort |
|---|
| 45 | withWantEnable:(BOOL)wantEnable |
|---|
| 46 | { |
|---|
| 47 | if ((self = [super init])) { |
|---|
| 48 | _proxyPort = proxyPort; |
|---|
| 49 | _wantEnable = wantEnable; |
|---|
| 50 | _actionDescription = [[NSMutableArray arrayWithCapacity:20] retain]; |
|---|
| 51 | } |
|---|
| 52 | return self; |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | - (void)dealloc |
|---|
| 56 | { |
|---|
| 57 | [_actionDescription release]; |
|---|
| 58 | [super dealloc]; |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | - (BOOL)updateNetworkService:(SCNetworkServiceRef)serviceR |
|---|
| 62 | { |
|---|
| 63 | int wantedPort = _wantEnable ? _proxyPort : 0; |
|---|
| 64 | const CFStringRef wantedHost = _wantEnable ? CFSTR("127.0.0.1") : NULL; |
|---|
| 65 | // |
|---|
| 66 | SCNetworkProtocolRef protoR = SCNetworkServiceCopyProtocol(serviceR, kSCNetworkProtocolTypeProxies); |
|---|
| 67 | if (!protoR) |
|---|
| 68 | return NO; // this protocol has not been enabled. |
|---|
| 69 | if (!SCNetworkProtocolGetEnabled(protoR)) |
|---|
| 70 | return NO; |
|---|
| 71 | CFDictionaryRef proxyDictR = SCNetworkProtocolGetConfiguration(protoR); // returns NULL if no configuration. |
|---|
| 72 | //NSLog(@" proxyDict: %@", proxyDictR); |
|---|
| 73 | const void *httpEnableP = proxyDictR ? CFDictionaryGetValue(proxyDictR, kSCPropNetProxiesHTTPEnable) : NULL; |
|---|
| 74 | const void *httpHostP = proxyDictR ? CFDictionaryGetValue(proxyDictR, kSCPropNetProxiesHTTPProxy) : NULL; |
|---|
| 75 | const void *httpPortP = proxyDictR ? CFDictionaryGetValue(proxyDictR, kSCPropNetProxiesHTTPPort) : NULL; |
|---|
| 76 | #define GetCasted(value, type) ((value) && (CFGetTypeID(value) == type##GetTypeID()) \ |
|---|
| 77 | ? ((type##Ref)value) : NULL) |
|---|
| 78 | CFStringRef host = GetCasted(httpHostP, CFString); |
|---|
| 79 | NSNumber* port = (NSNumber*)GetCasted(httpPortP, CFNumber); |
|---|
| 80 | NSNumber* enabled = (NSNumber*)GetCasted(httpEnableP, CFNumber); |
|---|
| 81 | DebugNSLog(@" Settings{%@} enable = %@, host = %@, port = %@", (proxyDictR ? @"has" : @"missing"), enabled, host, port); |
|---|
| 82 | BOOL needsUpdateEnabled = NO; |
|---|
| 83 | BOOL needsUpdateHost = NO; |
|---|
| 84 | BOOL needsUpdatePort = NO; |
|---|
| 85 | CFMutableDictionaryRef newProxyDictR; |
|---|
| 86 | if (proxyDictR) |
|---|
| 87 | newProxyDictR = CFDictionaryCreateMutableCopy(NULL, 0, proxyDictR); |
|---|
| 88 | else |
|---|
| 89 | newProxyDictR = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
|---|
| 90 | // just keep old host/port when we disable proxy. |
|---|
| 91 | if (wantedHost && !cfstreql(wantedHost, host)) { |
|---|
| 92 | DebugNSLog(@" host needs update: %@ -> %@", host, wantedHost); |
|---|
| 93 | needsUpdateHost = YES; |
|---|
| 94 | CFDictionarySetValue(newProxyDictR, kSCPropNetProxiesHTTPProxy, wantedHost); |
|---|
| 95 | } |
|---|
| 96 | if (wantedPort && (port ? [port intValue] : 0) != wantedPort) { |
|---|
| 97 | DebugNSLog(@" port needs update: %@ -> %d", port, wantedPort); |
|---|
| 98 | needsUpdatePort = YES; |
|---|
| 99 | NSNumber* value = [NSNumber numberWithInt:wantedPort]; |
|---|
| 100 | CFDictionarySetValue(newProxyDictR, kSCPropNetProxiesHTTPPort, value); |
|---|
| 101 | } |
|---|
| 102 | if ((enabled && [enabled intValue]) != _wantEnable) { |
|---|
| 103 | DebugNSLog(@" enabled needs update: %@ -> %d", enabled, (int)_wantEnable); |
|---|
| 104 | needsUpdateEnabled = YES; |
|---|
| 105 | NSNumber* value = [NSNumber numberWithInt:(_wantEnable ? 1 : 0)]; |
|---|
| 106 | CFDictionarySetValue(newProxyDictR, kSCPropNetProxiesHTTPEnable, value); |
|---|
| 107 | /* |
|---|
| 108 | if (_wantEnable) { |
|---|
| 109 | NSNumber* value = [NSNumber numberWithInt:1]; |
|---|
| 110 | CFDictionarySetValue(newProxyDictR, kSCPropNetProxiesHTTPEnable, value); |
|---|
| 111 | } else { |
|---|
| 112 | CFDictionaryRemoveValue(newProxyDictR, kSCPropNetProxiesHTTPEnable); |
|---|
| 113 | } |
|---|
| 114 | */ |
|---|
| 115 | } |
|---|
| 116 | DebugNSLog(@" needsUpdate = %d+%d+%d\n", needsUpdateEnabled, needsUpdateHost, needsUpdatePort); |
|---|
| 117 | [_actionDescription addObject:(NSString*)SCNetworkServiceGetName(serviceR)]; |
|---|
| 118 | if (!needsUpdateEnabled && !needsUpdateHost && !needsUpdatePort) { |
|---|
| 119 | [_actionDescription addObject:@" No change."]; |
|---|
| 120 | CFRelease(newProxyDictR); |
|---|
| 121 | return NO; |
|---|
| 122 | } |
|---|
| 123 | if (_wantEnable) { |
|---|
| 124 | if (needsUpdateEnabled && needsUpdateHost && needsUpdatePort) { |
|---|
| 125 | [_actionDescription addObject:@" Enable proxy and set host/port."]; |
|---|
| 126 | } else if (!needsUpdateHost && !needsUpdatePort) { |
|---|
| 127 | [_actionDescription addObject:@" Enable proxy."]; |
|---|
| 128 | } else { |
|---|
| 129 | NSMutableArray* a = [NSMutableArray arrayWithCapacity:3]; |
|---|
| 130 | if (needsUpdateEnabled) |
|---|
| 131 | [a addObject:@"Enable proxy"]; |
|---|
| 132 | if (needsUpdateHost) { |
|---|
| 133 | NSString* s = [NSString stringWithFormat:@"update host ('%@' -> '%@)", |
|---|
| 134 | (host ? (NSString*)host : @""), wantedHost]; |
|---|
| 135 | [a addObject:s]; |
|---|
| 136 | } |
|---|
| 137 | if (needsUpdatePort) { |
|---|
| 138 | NSString* s = [NSString stringWithFormat:@"update port: %@ -> %d", |
|---|
| 139 | (port ? [NSString stringWithFormat:@"%d", [port intValue]] : @"none"), |
|---|
| 140 | wantedPort]; |
|---|
| 141 | [a addObject:s]; |
|---|
| 142 | } |
|---|
| 143 | if (![a count]) |
|---|
| 144 | [a addObject:@"???? no modifications?"]; |
|---|
| 145 | NSString* msg = [NSString stringWithFormat:@" %@", [a componentsJoinedByString:@", "]]; |
|---|
| 146 | [_actionDescription addObject:msg]; |
|---|
| 147 | } |
|---|
| 148 | } else { |
|---|
| 149 | [_actionDescription addObject:@" Disable proxy."]; |
|---|
| 150 | } |
|---|
| 151 | // DebugNSLogNSLog(@" new dict: %@", newProxyDictR); |
|---|
| 152 | BOOL ok = SCNetworkProtocolSetConfiguration(protoR, newProxyDictR); |
|---|
| 153 | CFRelease(newProxyDictR); |
|---|
| 154 | if (!ok) |
|---|
| 155 | DebugNSLog(@"SCNetworkProtocolSetConfiguration failed!"); |
|---|
| 156 | return YES; |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | - (SCPreferencesRef)createSCPreferencesRef:(BOOL)commit |
|---|
| 160 | { |
|---|
| 161 | CFStringRef appName = CFSTR("GlimmerBlocker"); |
|---|
| 162 | return SCPreferencesCreate(NULL, appName, NULL); |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | - (BOOL)updateSettingsWithCommit:(BOOL)commit |
|---|
| 166 | { |
|---|
| 167 | [_actionDescription removeAllObjects]; |
|---|
| 168 | SCPreferencesRef prefsR = [self createSCPreferencesRef:commit]; |
|---|
| 169 | NSAssert(prefsR, @"prefsR"); |
|---|
| 170 | // can only get lock using auth. |
|---|
| 171 | BOOL gotLock = commit ? SCPreferencesLock(prefsR, NO) : NO; |
|---|
| 172 | if (commit && !gotLock) |
|---|
| 173 | NSLog(@"Can't get preferences lock"); |
|---|
| 174 | SCNetworkSetRef networkSetR = SCNetworkSetCopyCurrent(prefsR); |
|---|
| 175 | NSAssert(networkSetR, @"networkSetR"); |
|---|
| 176 | // |
|---|
| 177 | BOOL needsUpdate = NO; |
|---|
| 178 | CFArrayRef networkServicesArrayR = SCNetworkSetCopyServices(networkSetR); |
|---|
| 179 | NSAssert(networkServicesArrayR, @"networkServicesArrayR"); |
|---|
| 180 | for (CFIndex i = 0; i < CFArrayGetCount(networkServicesArrayR); i++) { |
|---|
| 181 | SCNetworkServiceRef serviceR = CFArrayGetValueAtIndex(networkServicesArrayR, i); |
|---|
| 182 | BOOL enabled = SCNetworkServiceGetEnabled(serviceR); |
|---|
| 183 | DebugNSLog(@"Service [%ld], name = %@%@", (long)i, SCNetworkServiceGetName(serviceR), |
|---|
| 184 | enabled ? @"" : @" (disabled)"); |
|---|
| 185 | if (!enabled) |
|---|
| 186 | continue; |
|---|
| 187 | if ([self updateNetworkService:serviceR]) |
|---|
| 188 | needsUpdate = YES; |
|---|
| 189 | } |
|---|
| 190 | // |
|---|
| 191 | CFRelease(networkServicesArrayR); |
|---|
| 192 | CFRelease(networkSetR); |
|---|
| 193 | BOOL ok = !needsUpdate; |
|---|
| 194 | if (needsUpdate && commit) { |
|---|
| 195 | ok = SCPreferencesCommitChanges(prefsR); |
|---|
| 196 | if (!ok) { |
|---|
| 197 | NSLog(@" SCPreferencesCommitChanges failed"); |
|---|
| 198 | } else { |
|---|
| 199 | ok = SCPreferencesApplyChanges(prefsR); |
|---|
| 200 | if (!ok) |
|---|
| 201 | NSLog(@" SCPreferencesApplyChanges failed"); |
|---|
| 202 | } |
|---|
| 203 | // |
|---|
| 204 | if (gotLock) { |
|---|
| 205 | BOOL unlocked = SCPreferencesUnlock(prefsR); |
|---|
| 206 | if (!unlocked) |
|---|
| 207 | NSLog(@"### Got problem releasing prefs lock"); |
|---|
| 208 | } |
|---|
| 209 | } |
|---|
| 210 | CFRelease(prefsR); |
|---|
| 211 | return ok; |
|---|
| 212 | } |
|---|
| 213 | |
|---|
| 214 | @end |
|---|