├── .gitignore ├── LICENSE ├── LSStatusBarItem.h ├── Layout ├── DEBIAN │ └── control └── System │ └── Library │ └── Frameworks │ └── UIKit.framework │ ├── Black_ProxySwitcher.png │ ├── Black_ProxySwitcher@2x.png │ ├── Black_ProxySwitcher@3x.png │ ├── Black_ProxySwitcherUnselected.png │ ├── Black_ProxySwitcherUnselected@2x.png │ ├── Black_ProxySwitcherUnselected@3x.png │ ├── LockScreen_ProxySwitcher.png │ ├── LockScreen_ProxySwitcher@2x.png │ ├── LockScreen_ProxySwitcher@3x.png │ ├── LockScreen_ProxySwitcherUnselected.png │ ├── LockScreen_ProxySwitcherUnselected@2x.png │ └── LockScreen_ProxySwitcherUnselected@3x.png ├── MBWiFiProxyInfo.h ├── MBWiFiProxyInfo.m ├── Makefile ├── ProxySwitcher.plist ├── README.md ├── Resources ├── icon.png ├── icon@2x.png └── icon@3x.png ├── Tweak.xm ├── control ├── layout └── DEBIAN │ ├── postinst │ └── prerm ├── proxyswitcherd ├── Layout │ └── Library │ │ └── LaunchDaemons │ │ └── com.mbo42.proxyswitcherd.plist ├── MBWiFiProxyHandler.h ├── MBWiFiProxyHandler.m ├── Makefile ├── SCNetworkHeader.h ├── control └── main.mm ├── proxyswitcherprefs ├── MBRootListController.h ├── MBRootListController.m ├── Makefile ├── Resources │ ├── EmailIcon.png │ ├── EmailIcon@2x.png │ ├── EmailIcon@3x.png │ ├── FooterIcon.png │ ├── FooterIcon@2x.png │ ├── FooterIcon@3x.png │ ├── GithubIcon.png │ ├── GithubIcon@2x.png │ ├── GithubIcon@3x.png │ ├── Info.plist │ ├── Root.plist │ ├── icon.png │ ├── icon@2x.png │ └── icon@3x.png └── entry.plist └── proxyswitcheruikit ├── Makefile ├── Tweak.xm └── proxyswitcheruikit.plist /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Packages 3 | .theos 4 | theos/ 5 | obj/ 6 | *.deb 7 | _/ 8 | *~.nib 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mikael (https://github.com/mikaelbo) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LSStatusBarItem.h: -------------------------------------------------------------------------------- 1 | 2 | typedef NS_ENUM(NSInteger, StatusBarAlignment) 3 | { 4 | StatusBarAlignmentLeft = 1, 5 | StatusBarAlignmentRight = 2, 6 | StatusBarAlignmentCenter = 4 7 | }; 8 | 9 | 10 | // only LSStatusBarItem (API) methods are considered public. 11 | 12 | @interface LSStatusBarItem : NSObject 13 | { 14 | @private 15 | NSString* _identifier; 16 | NSMutableDictionary* _properties; 17 | NSMutableSet* _delegates; 18 | BOOL _manualUpdate; 19 | } 20 | 21 | @end 22 | 23 | 24 | // Supported API 25 | 26 | @interface LSStatusBarItem (API) 27 | 28 | - (id) initWithIdentifier: (NSString*) identifier alignment: (StatusBarAlignment) alignment; 29 | 30 | // bitmasks (e.g. left or right) are not supported yet 31 | @property (nonatomic, readonly) StatusBarAlignment alignment; 32 | 33 | @property (nonatomic, getter=isVisible) BOOL visible; 34 | 35 | // useful only with left/right alignment - will throw error for center alignment 36 | @property (nonatomic, assign) NSString* imageName; 37 | 38 | // useful only with center alignment - will throw error otherwise 39 | // will not be visible on the lockscreen 40 | @property (nonatomic, assign) NSString* titleString; 41 | 42 | // useful if you want to override the UIStatusBarCustomItemView drawing. Your class must exist in EVERY UIKit process. 43 | @property (nonatomic, assign) NSString* customViewClass; 44 | 45 | // set to NO and manually call update if you need to make multiple changes 46 | @property (nonatomic, getter=isManualUpdate) BOOL manualUpdate; 47 | 48 | // manually call if manualUpdate = YES 49 | - (void) update; 50 | 51 | @end 52 | 53 | 54 | 55 | 56 | @interface LSStatusBarItem (Unimplemented) 57 | 58 | 59 | // leave alone unless you want to limit which apps your icon shows up in 60 | @property (nonatomic, assign) NSString* exclusiveToApp; 61 | 62 | // convenience methods? 63 | //@property (nonatomic, getter=isSpringBoardOnly) BOOL springBoardOnly; 64 | //@property (getter=isCurrentAppOnly) BOOL currentAppOnly; 65 | 66 | // delegate must respond to @selector(statusBarAction:); only valid from inside of SpringBoard 67 | - (void) addTouchDelegate: (id) delegate; 68 | - (void) removeTouchDelegate: (id) delegate; 69 | 70 | 71 | @end 72 | 73 | 74 | @interface LSStatusBarItem (Private) 75 | 76 | + (void) _updateProperties: (NSMutableDictionary*) properties forIdentifier: (NSString*) identifier; 77 | - (void) _setProperties: (NSDictionary*) dict; 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: com.mbo42.proxyswitcher 2 | Name: ProxySwitcher 3 | Depends: mobilesubstrate, PreferenceLoader,firmware(>= 8), firmware (<< 9.1) | org.thebigboss.libstatus9, firmware (>= 9.0) | libstatusbar 4 | Version: 1.0 5 | Architecture: iphoneos-arm 6 | Description: Easily toggle between using a proxy on WiFi. 7 | Maintainer: mbo42 8 | Author: mbo42 9 | Section: Tweaks 10 | Icon: file:///Library/Application Support/ProxySwitcher/ProxySwitcherBundle.bundle/icon.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcher.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcher@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcher@2x.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcher@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcher@3x.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcherUnselected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcherUnselected.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcherUnselected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcherUnselected@2x.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcherUnselected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/Black_ProxySwitcherUnselected@3x.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcher.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcher@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcher@2x.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcher@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcher@3x.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcherUnselected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcherUnselected.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcherUnselected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcherUnselected@2x.png -------------------------------------------------------------------------------- /Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcherUnselected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Layout/System/Library/Frameworks/UIKit.framework/LockScreen_ProxySwitcherUnselected@3x.png -------------------------------------------------------------------------------- /MBWiFiProxyInfo.h: -------------------------------------------------------------------------------- 1 | @interface MBWiFiProxyInfo : NSObject 2 | 3 | @property (nonatomic, copy, readonly) NSString *server; 4 | @property (nonatomic, strong, readonly) NSNumber *port; 5 | @property (nonatomic, copy, readonly) NSString *username; 6 | @property (nonatomic, copy, readonly) NSString *password; 7 | 8 | + (instancetype)infoWithServer:(NSString *)server 9 | port:(NSNumber *)port 10 | username:(NSString *)username 11 | password:(NSString *)password; 12 | 13 | + (instancetype)infoFromDictionary:(NSDictionary *)dictionary; 14 | 15 | - (instancetype)initWithServer:(NSString *)server 16 | port:(NSNumber *)port 17 | username:(NSString *)username 18 | password:(NSString *)password; 19 | 20 | - (instancetype)initFromDictionary:(NSDictionary *)dictionary; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /MBWiFiProxyInfo.m: -------------------------------------------------------------------------------- 1 | #import "MBWiFiProxyInfo.h" 2 | 3 | @interface NSDictionary (Getters) 4 | 5 | - (nullable NSString *)stringForKeySafely:(nullable KeyType)key; 6 | - (nullable NSNumber *)numberForKeySafely:(nullable KeyType)key; 7 | 8 | @end 9 | 10 | @implementation NSDictionary (Getters) 11 | 12 | - (NSString *)stringForKeySafely:(id)key { 13 | id string = [self objectForKey:key]; 14 | if ([string isKindOfClass:[NSString class]]) { return string; } 15 | if ([string isKindOfClass:[NSNumber class]]) { return [NSString stringWithFormat:@"%@", string]; } 16 | return nil; 17 | } 18 | 19 | - (NSNumber *)numberForKeySafely:(id)key { 20 | id number = [self objectForKey:key]; 21 | if ([number isKindOfClass:[NSNumber class]]) { return number; } 22 | if ([number isKindOfClass:[NSString class]]) { return [self numberFromString:(NSString *)number]; } 23 | return nil; 24 | } 25 | 26 | - (NSNumber *)numberFromString:(NSString *)string { 27 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 28 | [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; 29 | return [formatter numberFromString:string]; 30 | } 31 | 32 | @end 33 | 34 | @implementation MBWiFiProxyInfo 35 | 36 | + (instancetype)infoWithServer:(NSString *)server port:(NSNumber *)port username:(nullable NSString *)username password:(nullable NSString *)password { 37 | return [[self alloc] initWithServer:server port:port username:username password:password]; 38 | } 39 | 40 | + (instancetype)infoFromDictionary:(NSDictionary *)dictionary { 41 | return [[self alloc] initFromDictionary:dictionary]; 42 | } 43 | 44 | - (instancetype)initWithServer:(NSString *)server port:(NSNumber *)port username:(nullable NSString *)username password:(nullable NSString *)password { 45 | if (self = [super init]) { 46 | _server = server; 47 | _port = port; 48 | _username = username; 49 | _password = password; 50 | } 51 | return self; 52 | } 53 | 54 | - (instancetype)initFromDictionary:(NSDictionary *)dictionary { 55 | if (self = [super init]) { 56 | _server = [dictionary stringForKeySafely:@"server"]; 57 | _port = [dictionary numberForKeySafely:@"port"]; 58 | _username = [dictionary stringForKeySafely:@"username"]; 59 | _password = [dictionary stringForKeySafely:@"password"]; 60 | } 61 | return self; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | ADDITIONAL_OBJCFLAGS = -fobjc-arc 4 | TWEAK_NAME = ProxySwitcher 5 | ProxySwitcher_FILES = $(wildcard *.m *.mm *.x *.xm) 6 | 7 | BUNDLE_NAME = ProxySwitcherBundle 8 | ProxySwitcherBundle_INSTALL_PATH = /Library/Application Support/ProxySwitcher 9 | 10 | include $(THEOS_MAKE_PATH)/tweak.mk 11 | 12 | after-install:: 13 | install.exec "killall -9 SpringBoard;" 14 | 15 | SUBPROJECTS += proxyswitcherprefs 16 | SUBPROJECTS += proxyswitcherd 17 | SUBPROJECTS += proxyswitcheruikit 18 | include $(THEOS_MAKE_PATH)/aggregate.mk 19 | include $(THEOS)/makefiles/bundle.mk 20 | -------------------------------------------------------------------------------- /ProxySwitcher.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.springboard" ); }; } 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProxySwitcher 2 | ProxySwitcher is meant to give an easy way to toggle proxy on / off. Since the proxy settings are on a per-WiFi basis, ProxySwitcher listens to changes to the WiFi network, and makes sure those settings are persisted through switching of active WiFi network. 3 | 4 | Easily toggle between using proxy and non-proxy by tapping the added icon on the right side of the StatusBar. The icon is gray when it's set to not use proxy, and black/white when proxy is active. 5 | 6 | The icon can be toggled to only show when WiFi is active (default), or to always be shown. 7 | 8 | Available from iOS 9.1+ 9 | 10 |

11 | 12 | 13 | 14 |

15 | 16 | ## Future wishlist: 17 | - Saving proxy username and password credentials to the KeyChain (`SecKeychainAddInternetPassword`?) 18 | - Multiple proxy configurations 19 | - Overlay for toggling proxy. Something like this: 20 | 21 | 22 | 23 | ## Installation 24 | 25 | ### 1. Theos 26 | Make sure you have [Theos](https://github.com/theos/theos) installed (guide found [here](http://iphonedevwiki.net/index.php/Theos/Setup)), with the `$THEOS` and `$THEOS_DEVICE_IP` variables configured. 27 | 28 | ### 2. Private headers 29 | Theos needs to point to an iOS SDK including private headers (required for using Preferences). The newer versions of the iOS SDK does not include those anymore. A few ways to solve that has been suggested in this [thread](https://github.com/theos/theos/issues/146). I ended up putting a separate iOS SDK including private headers in `$THEOS/sdks`. 30 | 31 | ### 3. fauxsu 32 | The daemon must be owned by root:wheel, and to make sure that happens during the build, you can use a tool called [fauxsu](https://github.com/DHowett/fauxsu). A compiled version can be downloaded from [here](http://nix.howett.net/~dhowett/fauxsu.tar). 33 | 34 | Extract `fauxsu` and `libfauxsu.dylib` to `$THEOS/bin/`. Run the following commands to make sure `fauxsu` and `libfauxsu.dylib` has the correct permissions: 35 | 36 | ``` 37 | sudo chmod +x $THEOS/bin/fauxsu 38 | sudo chmod +x $THEOS/bin/libfauxsu.dylib 39 | sudo chown root:wheel $THEOS/bin/fauxsu 40 | sudo chown root:wheel $THEOS/bin/libfauxsu.dylib 41 | ``` 42 | 43 | `make install` to build it locally. 44 | 45 | Check ownership by running: 46 | 47 | ``` 48 | dpkg-deb -c ./packages/com.mbo42.proxyswitcher_X+debug_iphoneos-arm.deb 49 | ``` 50 | ``` 51 | drwxr-xr-x root/wheel 0 2017-05-23 16:35 ./ 52 | drwxr-xr-x root/wheel 0 2017-05-22 17:20 ./Library/ 53 | drwxr-xr-x root/wheel 0 2017-05-22 21:08 ./Library/LaunchDaemons/ 54 | -rw-r--r-- root/wheel 504 2017-05-22 21:08 ./Library/LaunchDaemons/ 55 | com.mbo42.proxyswitcherd.plist 56 | drwxr-xr-x root/wheel 0 2017-05-23 16:35 ./usr/ 57 | drwxr-xr-x root/wheel 0 2017-05-23 16:35 ./usr/bin/ 58 | -rwxr-xr-x root/wheel 139680 2017-05-23 16:35 ./usr/bin/ProxySwitcherd 59 | ... 60 | ``` 61 | 62 | If the daemon still isn't owned by root:wheel, it might be because System Integrity Protection is turned on. You can check this by running `csrutil status` in the console. 63 | 64 | When everything is set up and working properly, you can run `make package install` from the root directory to deploy to the device. 65 | 66 | ## Credits 67 | 68 | * [Danny Liu](https://github.com/DYun) for his project [iOSProxyManager](https://github.com/DYun/iOSProxyManager) 69 | * [Uroboro](https://github.com/uroboro) for his help on the #iphonedev IRC and his [iOS Daemon example project](https://github.com/uroboro/iOS-daemon/tree/Objective-C) 70 | * [iOSre](http://bbs.iosre.com/)'s guide on [running a daemon as root](http://bbs.iosre.com/) 71 | 72 | ## Licence 73 | 74 | This project is licensed under the [MIT Licence](https://github.com/mikaelbo/ProxySwitcher/blob/master/LICENSE). Feel free to use the code however you see fit in your own projects. However, redistribution of this tweak **as is** to Cydia is prohibited. -------------------------------------------------------------------------------- /Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Resources/icon.png -------------------------------------------------------------------------------- /Resources/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Resources/icon@2x.png -------------------------------------------------------------------------------- /Resources/icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/Resources/icon@3x.png -------------------------------------------------------------------------------- /Tweak.xm: -------------------------------------------------------------------------------- 1 | #include 2 | #import "LSStatusBarItem.h" 3 | #import "MBWiFiProxyInfo.h" 4 | 5 | static BOOL enabled = NO; 6 | static BOOL alwaysShow = YES; 7 | static NSUInteger type = 0; 8 | static MBWiFiProxyInfo *proxyInfo; 9 | static LSStatusBarItem *statusBarItem; 10 | 11 | @interface SBWiFiManager : NSObject 12 | + (instancetype)sharedInstance; 13 | - (void)_powerStateDidChange; 14 | - (NSString *)currentNetworkName; 15 | - (BOOL)wiFiEnabled; 16 | @end 17 | 18 | @interface RadiosPreferences : NSObject 19 | @property (nonatomic) BOOL airplaneMode; 20 | @end 21 | 22 | static BOOL canShowStatusBarIcon() { 23 | return alwaysShow ? YES : [[%c(SBWiFiManager) sharedInstance] wiFiEnabled] && [[%c(SBWiFiManager) sharedInstance] currentNetworkName]; 24 | } 25 | 26 | static void updateStatusBarImage() { 27 | statusBarItem.imageName = type > 0 ? @"ProxySwitcher" : @"ProxySwitcherUnselected"; 28 | } 29 | 30 | static void setStatusBarVisible(BOOL visible) { 31 | statusBarItem.visible = enabled && canShowStatusBarIcon() ? visible : NO; 32 | } 33 | 34 | static void networkChanged() { 35 | if (!enabled) { return; } 36 | setStatusBarVisible(![[[%c(RadiosPreferences) alloc] init] airplaneMode]); 37 | if ([[%c(SBWiFiManager) sharedInstance] wiFiEnabled] && [[%c(SBWiFiManager) sharedInstance] currentNetworkName]) { 38 | if (type == 1) { 39 | notify_post("com.mbo42.proxyswitcherd.enable"); 40 | } else { 41 | notify_post("com.mbo42.proxyswitcherd.disable"); 42 | } 43 | } 44 | } 45 | 46 | static void loadPreferences() { 47 | CFArrayRef keyList = CFPreferencesCopyKeyList(CFSTR("com.mbo42.proxyswitcher"), kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 48 | NSDictionary *preferences; 49 | if (keyList) { 50 | preferences = (__bridge NSDictionary *)CFPreferencesCopyMultiple(keyList, 51 | CFSTR("com.mbo42.proxyswitcher"), 52 | kCFPreferencesCurrentUser, 53 | kCFPreferencesAnyHost); 54 | if (!preferences) { 55 | preferences = [NSDictionary dictionary]; 56 | } 57 | CFRelease(keyList); 58 | } 59 | enabled = [preferences objectForKey:@"enabled"] ? [[preferences objectForKey:@"enabled"] boolValue] : YES; 60 | alwaysShow = [preferences objectForKey:@"alwaysShow"] ? [[preferences objectForKey:@"alwaysShow"] boolValue] : YES; 61 | proxyInfo = [MBWiFiProxyInfo infoFromDictionary:preferences]; 62 | type = [preferences objectForKey:@"type"] ? [[preferences objectForKey:@"type"] integerValue] : 0; 63 | notify_post("com.mbo42.proxyswitcherd.refreshPreferences"); 64 | setStatusBarVisible(![[[%c(RadiosPreferences) alloc] init] airplaneMode]); 65 | updateStatusBarImage(); 66 | networkChanged(); 67 | } 68 | 69 | static void saveNewType() { 70 | CFPreferencesSetAppValue(CFSTR("type"), 71 | CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &type), 72 | CFSTR("com.mbo42.proxyswitcher")); 73 | networkChanged(); 74 | updateStatusBarImage(); 75 | } 76 | 77 | static void toggleProxy() { 78 | type = !type ? 1 : 0; 79 | saveNewType(); 80 | } 81 | 82 | 83 | %hook SpringBoard 84 | 85 | - (void)applicationDidFinishLaunching:(id)arg1 { 86 | %orig; 87 | statusBarItem = [[%c(LSStatusBarItem) alloc] initWithIdentifier:@"com.mbo42.proxyswitcher" alignment:StatusBarAlignmentRight]; 88 | statusBarItem.imageName = @"ProxySwitcher"; 89 | loadPreferences(); 90 | } 91 | 92 | %end 93 | 94 | 95 | %hook RadiosPreferences 96 | 97 | - (void)setAirplaneMode:(BOOL)airplaneMode { 98 | %orig; 99 | setStatusBarVisible(!airplaneMode); 100 | } 101 | 102 | %end 103 | 104 | 105 | %hook _UIAlertControllerView 106 | 107 | - (void)setAlertController:(id)controller { 108 | %orig; 109 | if ([controller isKindOfClass:[UIAlertController class]]) { 110 | UIAlertController *alertVC = (UIAlertController *)controller; 111 | NSString *message = alertVC.message; 112 | if (!proxyInfo.server.length || !proxyInfo.port || !proxyInfo.username.length || !proxyInfo.password.length) { 113 | return; 114 | } 115 | if (message && 116 | [message rangeOfString:proxyInfo.server].location != NSNotFound && 117 | [message rangeOfString:[proxyInfo.port stringValue]].location != NSNotFound) { 118 | if (alertVC.textFields.count > 1) { 119 | UITextField *usernameField = alertVC.textFields[0]; 120 | UITextField *passwordField = alertVC.textFields[1]; 121 | usernameField.text = proxyInfo.username; 122 | passwordField.text = proxyInfo.password; 123 | } 124 | } 125 | } 126 | } 127 | 128 | %end 129 | 130 | 131 | %ctor { 132 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), 133 | NULL, 134 | (CFNotificationCallback)networkChanged, 135 | CFSTR("com.apple.system.config.network_change"), 136 | NULL, 137 | CFNotificationSuspensionBehaviorDeliverImmediately); 138 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), 139 | NULL, 140 | (CFNotificationCallback)loadPreferences, 141 | CFSTR("com.mbo42.proxyswitcher/settingschanged"), 142 | NULL, 143 | CFNotificationSuspensionBehaviorCoalesce); 144 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), 145 | NULL, 146 | (CFNotificationCallback)toggleProxy, 147 | CFSTR("com.mbo42.proxyswitcheruikit/didTapOnStatusBar"), 148 | NULL, 149 | CFNotificationSuspensionBehaviorCoalesce); 150 | } 151 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.mbo42.proxyswitcher 2 | Name: ProxySwitcher 3 | Depends: mobilesubstrate, PreferenceLoader, org.thebigboss.libstatus9, firmware(>=9.1) 4 | Version: 1.0.2 5 | Architecture: iphoneos-arm 6 | Description: Easily toggle between using a proxy on WiFi. 7 | Maintainer: mbo42 8 | Author: mbo42 9 | Section: Tweaks 10 | Icon: file:///Library/Application Support/ProxySwitcher/ProxySwitcherBundle.bundle/icon.png -------------------------------------------------------------------------------- /layout/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Loading ProxySwitcher daemon..." 3 | launchctl load /Library/LaunchDaemons/com.mbo42.proxyswitcherd.plist 4 | exit 0 -------------------------------------------------------------------------------- /layout/DEBIAN/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Unloading ProxySwitcher daemon..." 3 | launchctl unload /Library/LaunchDaemons/com.mbo42.proxyswitcherd.plist 4 | exit 0 -------------------------------------------------------------------------------- /proxyswitcherd/Layout/Library/LaunchDaemons/com.mbo42.proxyswitcherd.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KeepAlive 6 | 7 | Label 8 | com.mbo42.proxyswitcherd 9 | Program 10 | /usr/bin/ProxySwitcherd 11 | RunAtLoad 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /proxyswitcherd/MBWiFiProxyHandler.h: -------------------------------------------------------------------------------- 1 | @interface MBWiFiProxyHandler : NSObject 2 | 3 | @property (nonatomic, strong, readonly) NSDictionary *preferences; 4 | 5 | + (instancetype)sharedInstance; 6 | 7 | - (void)enableProxy; 8 | - (void)disableProxy; 9 | - (void)refreshPreferences; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /proxyswitcherd/MBWiFiProxyHandler.m: -------------------------------------------------------------------------------- 1 | #import "MBWiFiProxyHandler.h" 2 | #import "SCNetworkHeader.h" 3 | 4 | @interface NSDictionary (Getters) 5 | 6 | - (nullable NSString *)stringForKeySafely:(nullable KeyType)key; 7 | - (nullable NSNumber *)numberForKeySafely:(nullable KeyType)key; 8 | 9 | @end 10 | 11 | @implementation NSDictionary (Getters) 12 | 13 | - (NSString *)stringForKeySafely:(id)key { 14 | id string = [self objectForKey:key]; 15 | if ([string isKindOfClass:[NSString class]]) { return string; } 16 | if ([string isKindOfClass:[NSNumber class]]) { return [NSString stringWithFormat:@"%@", string]; } 17 | return nil; 18 | } 19 | 20 | - (NSNumber *)numberForKeySafely:(id)key { 21 | id number = [self objectForKey:key]; 22 | if ([number isKindOfClass:[NSNumber class]]) { return number; } 23 | if ([number isKindOfClass:[NSString class]]) { return [self numberFromString:(NSString *)number]; } 24 | return nil; 25 | } 26 | 27 | - (NSNumber*)numberFromString:(NSString*)string { 28 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 29 | [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; 30 | return [formatter numberFromString:string]; 31 | } 32 | 33 | @end 34 | 35 | @implementation MBWiFiProxyHandler 36 | 37 | + (instancetype)sharedInstance { 38 | static dispatch_once_t onceToken; 39 | static MBWiFiProxyHandler *handler; 40 | dispatch_once(&onceToken, ^{ 41 | handler = [[MBWiFiProxyHandler alloc] init]; 42 | }); 43 | return handler; 44 | } 45 | 46 | - (void)refreshPreferences { 47 | _preferences = [NSDictionary dictionaryWithContentsOfFile:@"/User/Library/Preferences/com.mbo42.proxyswitcher.plist"]; 48 | } 49 | 50 | - (void)enableProxy { 51 | [self updateProxy:YES]; 52 | } 53 | 54 | - (void)disableProxy { 55 | [self updateProxy:NO]; 56 | } 57 | 58 | - (void)updateProxy:(BOOL)enabled { 59 | SCPreferencesRef prefRef = SCPreferencesCreate(NULL, CFSTR("proxy_switcher"), NULL); 60 | SCPreferencesLock(prefRef, true); 61 | CFStringRef currentSetPath = SCPreferencesGetValue(prefRef, kSCPrefCurrentSet); 62 | 63 | NSDictionary *currentSet = (__bridge NSDictionary *)SCPreferencesPathGetValue(prefRef, currentSetPath); 64 | 65 | if (currentSet) { 66 | NSDictionary *currentSetServices = currentSet[cfs2nss(kSCCompNetwork)][cfs2nss(kSCCompService)]; 67 | NSDictionary *services = (__bridge NSDictionary *)SCPreferencesGetValue(prefRef, kSCPrefNetworkServices); 68 | 69 | NSString *wifiServiceKey = nil; 70 | for (NSString *key in currentSetServices) { 71 | NSDictionary *service = services[key]; 72 | NSString *name = service[cfs2nss(kSCPropUserDefinedName)]; 73 | if (service && [@"Wi-Fi" isEqualToString:name]) { 74 | wifiServiceKey = key; 75 | break; 76 | } 77 | } 78 | 79 | if (wifiServiceKey) { 80 | NSData *data = [NSPropertyListSerialization dataWithPropertyList:services 81 | format:NSPropertyListBinaryFormat_v1_0 82 | options:0 83 | error:nil]; 84 | NSMutableDictionary *nservices = [NSPropertyListSerialization propertyListWithData:data 85 | options:NSPropertyListMutableContainersAndLeaves 86 | format:NULL 87 | error:nil]; 88 | NSMutableDictionary *proxies = nservices[wifiServiceKey][(__bridge NSString *)kSCEntNetProxies]; 89 | BOOL didChange = NO; 90 | if (enabled) { 91 | if (!self.preferences) { [self refreshPreferences]; } 92 | NSString *server = [self.preferences stringForKeySafely:@"server"]; 93 | NSNumber *port = [self.preferences numberForKeySafely:@"port"]; 94 | if (server && port) { 95 | BOOL shouldChange = [self shouldChangeProxyDict:proxies withServer:server port:port]; 96 | HBLogDebug(@"Should change proxy: %d", shouldChange); 97 | if (shouldChange) { 98 | [proxies setObject:@(1) forKey:cfs2nss(kSCPropNetProxiesHTTPEnable)]; 99 | [proxies setObject:server forKey:cfs2nss(kSCPropNetProxiesHTTPProxy)]; 100 | [proxies setObject:port forKey:cfs2nss(kSCPropNetProxiesHTTPPort)]; 101 | [proxies setObject:@(1) forKey:cfs2nss(kSCPropNetProxiesHTTPSEnable)]; 102 | [proxies setObject:server forKey:cfs2nss(kSCPropNetProxiesHTTPSProxy)]; 103 | [proxies setObject:port forKey:cfs2nss(kSCPropNetProxiesHTTPSPort)]; 104 | didChange = YES; 105 | } 106 | //TODO: Save credentials to keychain 107 | } 108 | } else { 109 | if (proxies.count) { 110 | HBLogDebug(@"Remove proxy settings"); 111 | [proxies removeAllObjects]; 112 | didChange = YES; 113 | } 114 | } 115 | if (didChange) { 116 | HBLogDebug(@"Proxy setting did change"); 117 | SCPreferencesSetValue(prefRef, kSCPrefNetworkServices, (__bridge CFPropertyListRef)nservices); 118 | SCPreferencesCommitChanges(prefRef); 119 | SCPreferencesApplyChanges(prefRef); 120 | } 121 | } 122 | } 123 | SCPreferencesUnlock(prefRef); 124 | CFRelease(prefRef); 125 | } 126 | 127 | - (BOOL)shouldChangeProxyDict:(NSDictionary *)proxyDict withServer:(NSString *)server port:(NSNumber *)port { 128 | return 129 | ![[proxyDict objectForKey:cfs2nss(kSCPropNetProxiesHTTPEnable)] isEqualToNumber:@1] || 130 | ![[proxyDict objectForKey:cfs2nss(kSCPropNetProxiesHTTPProxy)] isEqualToString:server] || 131 | ![[proxyDict objectForKey:cfs2nss(kSCPropNetProxiesHTTPPort)] isEqualToNumber:port] || 132 | ![[proxyDict objectForKey:cfs2nss(kSCPropNetProxiesHTTPSEnable)] isEqualToNumber:@1] || 133 | ![[proxyDict objectForKey:cfs2nss(kSCPropNetProxiesHTTPSProxy)] isEqualToString:server] || 134 | ![[proxyDict objectForKey:cfs2nss(kSCPropNetProxiesHTTPSPort)] isEqualToNumber:port]; 135 | } 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /proxyswitcherd/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | TOOL_NAME = ProxySwitcherd 4 | ADDITIONAL_OBJCFLAGS = -fobjc-arc 5 | ProxySwitcherd_FILES = $(wildcard *.m *.mm *.x *.xm) 6 | ProxySwitcherd_FRAMEWORKS = UIKit Foundation SystemConfiguration 7 | 8 | include $(THEOS_MAKE_PATH)/tool.mk 9 | -------------------------------------------------------------------------------- /proxyswitcherd/SCNetworkHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef __SCNetworkHeader 2 | #define __SCNetworkHeader 3 | 4 | #import 5 | 6 | typedef const struct __SCPreferences *SCPreferencesRef; 7 | 8 | extern const CFStringRef kSCPrefNetworkServices; 9 | extern const CFStringRef kSCPrefCurrentSet; 10 | extern const CFStringRef kSCEntNetProxies; 11 | extern const CFStringRef kSCPropNetProxiesHTTPEnable; 12 | extern const CFStringRef kSCPropNetProxiesHTTPProxy; 13 | extern const CFStringRef kSCPropNetProxiesHTTPPort; 14 | extern const CFStringRef kSCPropNetProxiesHTTPSEnable; 15 | extern const CFStringRef kSCPropNetProxiesHTTPSProxy; 16 | extern const CFStringRef kSCPropNetProxiesHTTPSPort; 17 | extern const CFStringRef kSCPrefSets; 18 | extern const CFStringRef kSCPropUserDefinedName; 19 | 20 | extern const CFStringRef kSCCompNetwork; 21 | extern const CFStringRef kSCCompService; 22 | 23 | 24 | extern SCPreferencesRef SCPreferencesCreate ( CFAllocatorRef allocator, CFStringRef name, CFStringRef prefsID ); 25 | 26 | extern CFArrayRef SCPreferencesCopyKeyList ( SCPreferencesRef prefs ); 27 | 28 | extern CFPropertyListRef SCPreferencesGetValue ( SCPreferencesRef prefs, CFStringRef key ); 29 | 30 | extern Boolean SCPreferencesSetValue ( SCPreferencesRef prefs, CFStringRef key, CFPropertyListRef value ); 31 | 32 | extern Boolean SCPreferencesLock ( SCPreferencesRef prefs, Boolean wait ); 33 | 34 | extern Boolean SCPreferencesUnlock ( SCPreferencesRef prefs ); 35 | 36 | extern Boolean SCPreferencesApplyChanges ( SCPreferencesRef prefs ); 37 | 38 | extern Boolean SCPreferencesCommitChanges ( SCPreferencesRef prefs ); 39 | 40 | extern CFDictionaryRef SCPreferencesPathGetValue ( SCPreferencesRef prefs, CFStringRef path ); 41 | 42 | #define cfs2nss(cfs) ((__bridge NSString *)(cfs)) 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /proxyswitcherd/control: -------------------------------------------------------------------------------- 1 | Package: com.mbo42.proxyswitcherd 2 | Name: ProxySwitcherd 3 | Depends: 4 | Version: 1.0 5 | Architecture: iphoneos-arm 6 | Description: The daemon for ProxySwitcher 7 | Maintainer: mbo42 8 | Author: mbo42 9 | Section: System 10 | Tag: role::hacker 11 | -------------------------------------------------------------------------------- /proxyswitcherd/main.mm: -------------------------------------------------------------------------------- 1 | #import "MBWiFiProxyHandler.h" 2 | 3 | static void enable(CFNotificationCenterRef center, 4 | void *observer, 5 | CFStringRef name, 6 | const void *object, 7 | CFDictionaryRef userInfo) { 8 | HBLogDebug(@"ProxySwitcherd: Enable proxy"); 9 | [[MBWiFiProxyHandler sharedInstance] enableProxy]; 10 | } 11 | 12 | static void disable(CFNotificationCenterRef center, 13 | void *observer, 14 | CFStringRef name, 15 | const void *object, 16 | CFDictionaryRef userInfo) { 17 | HBLogDebug(@"ProxySwitcherd: Disable proxy"); 18 | [[MBWiFiProxyHandler sharedInstance] disableProxy]; 19 | } 20 | 21 | static void refreshPreferences(CFNotificationCenterRef center, 22 | void *observer, 23 | CFStringRef name, 24 | const void *object, 25 | CFDictionaryRef userInfo) { 26 | HBLogDebug(@"ProxySwitcherd: Refresh preferences"); 27 | [[MBWiFiProxyHandler sharedInstance] refreshPreferences]; 28 | } 29 | 30 | int main(int argc, char **argv, char **envp) { 31 | HBLogDebug(@"ProxySwitcherd: ProxySwitcherd is launched!"); 32 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), 33 | NULL, 34 | enable, 35 | CFSTR("com.mbo42.proxyswitcherd.enable"), 36 | NULL, 37 | CFNotificationSuspensionBehaviorCoalesce); 38 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), 39 | NULL, 40 | disable, 41 | CFSTR("com.mbo42.proxyswitcherd.disable"), 42 | NULL, 43 | CFNotificationSuspensionBehaviorCoalesce); 44 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), 45 | NULL, 46 | refreshPreferences, 47 | CFSTR("com.mbo42.proxyswitcherd.refreshPreferences"), 48 | NULL, 49 | CFNotificationSuspensionBehaviorCoalesce); 50 | CFRunLoopRun(); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /proxyswitcherprefs/MBRootListController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface MBRootListController : PSListController 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /proxyswitcherprefs/MBRootListController.m: -------------------------------------------------------------------------------- 1 | #import "MBRootListController.h" 2 | #import 3 | #import 4 | #import 5 | 6 | static CFStringRef settingsChangedNotification = CFSTR("com.mbo42.proxyswitcher/settingschanged"); 7 | 8 | @interface MBRootListController() 9 | 10 | @property (nonatomic) BOOL authenticationEnabled; 11 | @property (nonatomic, strong) PSSpecifier *usernameSpecifier; 12 | @property (nonatomic, strong) PSSpecifier *passwordSpecifier; 13 | 14 | @end 15 | 16 | 17 | @implementation MBRootListController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | [self toggleAuthenticationCells:NO]; 22 | [self addFooterView]; 23 | [[NSNotificationCenter defaultCenter] addObserver:self 24 | selector:@selector(MB_applicationWillEnterForeground) 25 | name:UIApplicationWillEnterForegroundNotification 26 | object:nil]; 27 | } 28 | 29 | - (void)addFooterView { 30 | UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 144)]; 31 | NSBundle *bundle = [[NSBundle alloc] initWithPath:@"/Library/PreferenceBundles/ProxySwitcher.bundle"]; 32 | UIImage *image = [UIImage imageWithContentsOfFile:[bundle pathForResource:@"FooterIcon" ofType:@"png"]]; 33 | CGSize size = image.size; 34 | UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((self.view.frame.size.width - size.width) / 2, 35 | 20, 36 | size.width, 37 | size.height)]; 38 | imageView.image = image; 39 | imageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 40 | [footerView addSubview:imageView]; 41 | 42 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 43 | imageView.frame.origin.y + imageView.frame.size.height + 4, 44 | self.view.frame.size.width, 45 | 24)]; 46 | label.text = @"ProxySwitcher"; 47 | 48 | if ([UIFont instancesRespondToSelector:@selector(systemFontOfSize:weight:)]) { 49 | label.font = [UIFont systemFontOfSize:20 weight:UIFontWeightSemibold]; 50 | } else { 51 | label.font = [UIFont boldSystemFontOfSize:20]; 52 | } 53 | 54 | label.textColor = [UIColor colorWithRed:204 / 255.0 green:204 / 255.0 blue:204 / 255.0 alpha:1]; 55 | label.textAlignment = NSTextAlignmentCenter; 56 | label.autoresizingMask = UIViewAutoresizingFlexibleWidth; 57 | 58 | [footerView addSubview:label]; 59 | UILabel *versionLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 60 | label.frame.origin.y + label.frame.size.height, 61 | self.view.frame.size.width, 62 | 18)]; 63 | versionLabel.font = [UIFont systemFontOfSize:14]; 64 | versionLabel.textColor = [UIColor colorWithRed:204 / 255.0 green:204 / 255.0 blue:204 / 255.0 alpha:1]; 65 | versionLabel.text = @"Version 1.0.2"; 66 | versionLabel.textAlignment = NSTextAlignmentCenter; 67 | versionLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; 68 | [footerView addSubview:versionLabel]; 69 | for (UIView *view in self.view.subviews) { 70 | if ([view isKindOfClass:[UITableView class]]) { 71 | UITableView *tableView = (UITableView *)view; 72 | tableView.tableFooterView = footerView; 73 | tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; 74 | } 75 | } 76 | } 77 | 78 | - (NSArray *)specifiers { 79 | if (!_specifiers) { 80 | _specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self]; 81 | self.usernameSpecifier = _specifiers[7]; 82 | self.passwordSpecifier = _specifiers[8]; 83 | [self loadPreferences]; 84 | } 85 | return _specifiers; 86 | } 87 | 88 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 89 | UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; 90 | if ([cell isKindOfClass:NSClassFromString(@"PSSwitchTableCell")] && indexPath.section == 1 && indexPath.row == 2) { 91 | if ([cell respondsToSelector:@selector(control)]) { 92 | UISwitch *theSwitch = [cell performSelector:@selector(control)]; 93 | if ([theSwitch isKindOfClass:[UISwitch class]]) { 94 | [theSwitch addTarget:self action:@selector(MB_authenticationSwitchChanged:) forControlEvents:UIControlEventValueChanged]; 95 | } 96 | } 97 | } 98 | return cell; 99 | } 100 | 101 | - (void)MB_applicationWillEnterForeground { 102 | [self toggleAuthenticationCells:NO]; 103 | } 104 | 105 | - (void)MB_authenticationSwitchChanged:(UISwitch *)theSwitch { 106 | self.authenticationEnabled = theSwitch.isOn; 107 | [self toggleAuthenticationCells:YES]; 108 | } 109 | 110 | - (void)loadPreferences { 111 | CFArrayRef keyList = CFPreferencesCopyKeyList(CFSTR("com.mbo42.proxyswitcher"), kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 112 | NSDictionary *preferences; 113 | if (keyList) { 114 | preferences = (__bridge NSDictionary *)CFPreferencesCopyMultiple(keyList, 115 | CFSTR("com.mbo42.proxyswitcher"), 116 | kCFPreferencesCurrentUser, 117 | kCFPreferencesAnyHost); 118 | if (!preferences) { preferences = [NSDictionary dictionary]; } 119 | CFRelease(keyList); 120 | self.authenticationEnabled = [preferences objectForKey:@"authentication"] ? [[preferences objectForKey:@"authentication"] boolValue] : NO; 121 | } 122 | } 123 | 124 | - (void)toggleAuthenticationCells:(BOOL)animated { 125 | if (self.authenticationEnabled) { 126 | [self insertAuthenticationCellsIfNeeded:animated]; 127 | } else { 128 | [self removeAuthenticationCellsIfNeeded:animated]; 129 | } 130 | } 131 | 132 | - (void)insertAuthenticationCellsIfNeeded:(BOOL)animated { 133 | NSMutableArray *insertionArray = [NSMutableArray array]; 134 | if (![self.specifiers containsObject:self.usernameSpecifier]) { [insertionArray addObject:self.usernameSpecifier]; } 135 | if (![self.specifiers containsObject:self.passwordSpecifier]) { [insertionArray addObject:self.passwordSpecifier]; } 136 | if (insertionArray.count) { 137 | [self insertContiguousSpecifiers:insertionArray atEndOfGroup:1 animated:animated]; 138 | } 139 | } 140 | 141 | - (void)removeAuthenticationCellsIfNeeded:(BOOL)animated { 142 | NSMutableArray *deletionArray = [NSMutableArray array]; 143 | if ([self.specifiers containsObject:self.usernameSpecifier]) { [deletionArray addObject:self.usernameSpecifier]; } 144 | if ([self.specifiers containsObject:self.passwordSpecifier]) { [deletionArray addObject:self.passwordSpecifier]; } 145 | if (deletionArray.count) { 146 | [self removeContiguousSpecifiers:deletionArray animated:animated]; 147 | } 148 | } 149 | 150 | - (void)contactMe { 151 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto:mbo@mbo42.com"]]; 152 | } 153 | 154 | - (void)sendFeedback { 155 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto:mbo@mbo42.com?subject=ProxySwitcher"]]; 156 | } 157 | 158 | - (void)viewSourceCode { 159 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://github.com/mikaelbo/ProxySwitcher"]]; 160 | } 161 | 162 | - (void)dealloc { 163 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 164 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), 165 | settingsChangedNotification, 166 | NULL, 167 | NULL, 168 | true); 169 | } 170 | 171 | @end 172 | 173 | 174 | @interface MBTextFieldCell : PSEditableTableCell 175 | 176 | @end 177 | 178 | @implementation MBTextFieldCell 179 | 180 | - (void)layoutSubviews { 181 | [super layoutSubviews]; 182 | for (UIView *view in self.contentView.subviews) { 183 | if ([view isKindOfClass:[UITextField class]]) { 184 | UITextField *textField = (UITextField *)view; 185 | textField.textAlignment = NSTextAlignmentRight; 186 | textField.returnKeyType = UIReturnKeyNext; 187 | } 188 | } 189 | } 190 | 191 | @end 192 | 193 | 194 | @interface MBPasswordTextFieldCell : PSEditableTableCell 195 | 196 | @end 197 | 198 | @implementation MBPasswordTextFieldCell 199 | 200 | - (void)layoutSubviews { 201 | [super layoutSubviews]; 202 | for (UIView *view in self.contentView.subviews) { 203 | if ([view isKindOfClass:[UITextField class]]) { 204 | UITextField *textField = (UITextField *)view; 205 | textField.textAlignment = NSTextAlignmentRight; 206 | textField.returnKeyType = UIReturnKeyDone; 207 | } 208 | } 209 | } 210 | 211 | - (BOOL)textFieldShouldReturn:(UITextField *)textField { 212 | [textField resignFirstResponder]; 213 | return YES; 214 | } 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /proxyswitcherprefs/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | BUNDLE_NAME = ProxySwitcher 4 | ADDITIONAL_OBJCFLAGS = -fobjc-arc 5 | ProxySwitcher_FILES = MBRootListController.m 6 | ProxySwitcher_INSTALL_PATH = /Library/PreferenceBundles 7 | ProxySwitcher_FRAMEWORKS = UIKit 8 | ProxySwitcher_PRIVATE_FRAMEWORKS = Preferences 9 | 10 | include $(THEOS_MAKE_PATH)/bundle.mk 11 | 12 | internal-stage:: 13 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END) 14 | $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/ProxySwitcher.plist$(ECHO_END) 15 | -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/EmailIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/EmailIcon.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/EmailIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/EmailIcon@2x.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/EmailIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/EmailIcon@3x.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/FooterIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/FooterIcon.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/FooterIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/FooterIcon@2x.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/FooterIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/FooterIcon@3x.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/GithubIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/GithubIcon.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/GithubIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/GithubIcon@2x.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/GithubIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/GithubIcon@3x.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ProxySwitcher 9 | CFBundleIdentifier 10 | com.mbo42.proxyswitcher 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | NSPrincipalClass 22 | MBRootListController 23 | 24 | 25 | -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | PostNotification 9 | com.mbo42.proxyswitcher/settingschanged 10 | cell 11 | PSSwitchCell 12 | default 13 | 14 | defaults 15 | com.mbo42.proxyswitcher 16 | key 17 | enabled 18 | label 19 | Enabled 20 | 21 | 22 | PostNotification 23 | com.mbo42.proxyswitcher/settingschanged 24 | cell 25 | PSSwitchCell 26 | default 27 | 28 | defaults 29 | com.mbo42.proxyswitcher 30 | key 31 | alwaysShow 32 | label 33 | Always show toggle 34 | 35 | 36 | PostNotification 37 | com.mbo42.proxyswitcher/settingschanged 38 | cell 39 | PSLinkListCell 40 | default 41 | 0 42 | defaults 43 | com.mbo42.proxyswitcher 44 | detail 45 | PSListItemsController 46 | key 47 | type 48 | label 49 | Setting 50 | validTitles 51 | 52 | Direct 53 | Proxy 54 | 55 | validValues 56 | 57 | 0 58 | 1 59 | 60 | 61 | 62 | cell 63 | PSGroupCell 64 | label 65 | Proxy 66 | 67 | 68 | cell 69 | PSEditTextCell 70 | keyboard 71 | {"numbers", "phone"} 72 | isURL 73 | 74 | noAutoCorrect 75 | 76 | cellClass 77 | MBTextFieldCell 78 | defaults 79 | com.mbo42.proxyswitcher 80 | key 81 | server 82 | label 83 | Server 84 | 85 | 86 | cell 87 | PSEditTextCell 88 | isNumeric 89 | 90 | noAutoCorrect 91 | 92 | keyboard 93 | {"numbers"} 94 | cellClass 95 | MBTextFieldCell 96 | defaults 97 | com.mbo42.proxyswitcher 98 | key 99 | port 100 | label 101 | Port 102 | 103 | 104 | cell 105 | PSSwitchCell 106 | default 107 | 108 | defaults 109 | com.mbo42.proxyswitcher 110 | key 111 | authentication 112 | label 113 | Authentication 114 | 115 | 116 | cellClass 117 | MBTextFieldCell 118 | keyboard 119 | {"numbers", "phone"} 120 | noAutoCorrect 121 | 122 | cell 123 | PSEditTextCell 124 | defaults 125 | com.mbo42.proxyswitcher 126 | key 127 | username 128 | label 129 | Username 130 | 131 | 132 | cellClass 133 | MBPasswordTextFieldCell 134 | keyboard 135 | {"numbers", "phone"} 136 | noAutoCorrect 137 | 138 | cell 139 | PSSecureEditTextCell 140 | defaults 141 | com.mbo42.proxyswitcher 142 | key 143 | password 144 | label 145 | Password 146 | 147 | 148 | cell 149 | PSGroupCell 150 | label 151 | Support 152 | 153 | 154 | cell 155 | PSButtonCell 156 | label 157 | Contact me 158 | action 159 | contactMe 160 | icon 161 | EmailIcon.png 162 | 163 | 164 | cell 165 | PSButtonCell 166 | label 167 | Send feedback 168 | action 169 | sendFeedback 170 | icon 171 | EmailIcon.png 172 | 173 | 174 | cell 175 | PSButtonCell 176 | label 177 | View source code 178 | action 179 | viewSourceCode 180 | icon 181 | GithubIcon.png 182 | 183 | 184 | title 185 | ProxySwitcher 186 | 187 | 188 | -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/icon.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/icon@2x.png -------------------------------------------------------------------------------- /proxyswitcherprefs/Resources/icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelbo/ProxySwitcher/f6ea2e40721a6b0a81512a26860d5e636ddf85bb/proxyswitcherprefs/Resources/icon@3x.png -------------------------------------------------------------------------------- /proxyswitcherprefs/entry.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entry 6 | 7 | bundle 8 | ProxySwitcher 9 | cell 10 | PSLinkCell 11 | detail 12 | MBRootListController 13 | icon 14 | icon.png 15 | isController 16 | 17 | label 18 | ProxySwitcher 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /proxyswitcheruikit/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | ADDITIONAL_OBJCFLAGS = -fobjc-arc 4 | 5 | TWEAK_NAME = ProxySwitcherUIKit 6 | ProxySwitcherUIKit_FILES = Tweak.xm 7 | 8 | include $(THEOS_MAKE_PATH)/tweak.mk 9 | 10 | 11 | -------------------------------------------------------------------------------- /proxyswitcheruikit/Tweak.xm: -------------------------------------------------------------------------------- 1 | @interface UIStatusBarItem : NSObject 2 | @property (nonatomic, readonly) NSString *indicatorName; 3 | @end 4 | 5 | @interface UIStatusBarItemView : UIView 6 | @property (nonatomic, strong, readonly) UIStatusBarItem *item; 7 | @end 8 | 9 | 10 | %hook UIStatusBarItemView 11 | 12 | - (id)initWithItem:(id)arg1 data:(id)arg2 actions:(int)arg3 style:(id)arg4 { 13 | self = %orig; 14 | if ([self.item.indicatorName isEqualToString:@"ProxySwitcher"] || [self.item.indicatorName isEqualToString:@"ProxySwitcherUnselected"]) { 15 | [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(MB_didTapOnView:)]]; 16 | } 17 | return self; 18 | } 19 | 20 | - (void)setUserInteractionEnabled:(BOOL)enabled { 21 | NSString *indicatorName = self.item.indicatorName; 22 | BOOL hasProxySwitcherItem = [indicatorName isEqualToString:@"ProxySwitcher"] || [indicatorName isEqualToString:@"ProxySwitcherUnselected"]; 23 | hasProxySwitcherItem ? %orig(YES) : %orig; 24 | } 25 | 26 | %new 27 | - (void)MB_didTapOnView:(UITapGestureRecognizer *)recognizer { 28 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), 29 | CFSTR("com.mbo42.proxyswitcheruikit/didTapOnStatusBar"), 30 | NULL, 31 | NULL, 32 | YES); 33 | } 34 | 35 | %end 36 | -------------------------------------------------------------------------------- /proxyswitcheruikit/proxyswitcheruikit.plist: -------------------------------------------------------------------------------- 1 | { 2 | Filter = { 3 | Bundles = ( 4 | "com.apple.UIKit", 5 | "com.apple.springboard", 6 | ); 7 | }; 8 | } --------------------------------------------------------------------------------