]> git.saurik.com Git - safemode-ios.git/blame - MobileSafety.mm
Update the copyright notices to terminate in 2013.
[safemode-ios.git] / MobileSafety.mm
CommitLineData
df972f42 1/* Cydia Substrate - Powerful Code Insertion Platform
bef4d375 2 * Copyright (C) 2008-2013 Jay Freeman (saurik)
25f84761
JF
3*/
4
df972f42 5/* GNU Lesser General Public License, Version 3 {{{ */
25f84761 6/*
5bb6a76c 7 * Substrate is free software: you can redistribute it and/or modify it under
df972f42
JF
8 * the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
25f84761 11 *
5bb6a76c 12 * Substrate is distributed in the hope that it will be useful, but WITHOUT
df972f42
JF
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
25f84761 16 *
df972f42 17 * You should have received a copy of the GNU Lesser General Public License
5bb6a76c 18 * along with Substrate. If not, see <http://www.gnu.org/licenses/>.
df972f42
JF
19**/
20/* }}} */
25f84761 21
9065bb44
JF
22%apt Package: com.saurik.substrate.safemode
23%apt Author: Jay Freeman (saurik) <saurik@saurik.com>
24
25%apt Name: Substrate Safe Mode
26%apt Description: safe mode safety extension (safe)
27
28%apt Depends: mobilesubstrate (>= 0.9.3367+38)
29
30%fflag 1
31%fflag 2
32
33%bundle com.apple.springboard
34
35%flag -framework Foundation
36%flag -framework UIKit
37
25f84761
JF
38#import <CoreFoundation/CoreFoundation.h>
39#import <Foundation/Foundation.h>
40#import <CoreGraphics/CGGeometry.h>
39f17851
JF
41#import <UIKit/UIKit.h>
42
f6ceb738 43#include "CydiaSubstrate.h"
25f84761 44
d641d519
JF
45MSClassHook(UIStatusBar)
46
47MSClassHook(UIImage)
48MSMetaClassHook(UIImage)
49
f7673201
JF
50MSClassHook(AAAccountManager)
51MSMetaClassHook(AAAccountManager)
52
e9502538 53MSClassHook(BBSectionInfo)
e06a3b1a 54MSClassHook(BKSApplicationLaunchSettings)
e9502538 55
d641d519
JF
56MSClassHook(SBAlertItemsController)
57MSClassHook(SBButtonBar)
f7673201 58MSClassHook(SBIconController)
d641d519
JF
59MSClassHook(SBStatusBar)
60MSClassHook(SBStatusBarDataManager)
61MSClassHook(SBStatusBarTimeView)
62MSClassHook(SBUIController)
2c75d26c 63
39f17851 64Class $SafeModeAlertItem;
39f17851 65
2c75d26c
JF
66@interface SBAlertItem : NSObject {
67}
68- (UIAlertView *) alertSheet;
69- (void) dismiss;
70@end
71
72@interface SBAlertItemsController : NSObject {
73}
74+ (SBAlertItemsController *) sharedInstance;
75- (void) activateAlertItem:(SBAlertItem *)item;
76@end
77
78@interface SBStatusBarTimeView : UIView {
79}
80- (id) textFont;
81@end
82
88d77501
JF
83@interface UIApplication (CydiaSubstrate)
84- (void) applicationOpenURL:(id)url;
85@end
86
87@interface UIAlertView (CydiaSubstrate)
88- (void) setForceHorizontalButtonsLayout:(BOOL)force;
89- (void) setBodyText:(NSString *)body;
90- (void) setNumberOfRows:(NSInteger)rows;
91@end
92
39f17851
JF
93void SafeModeAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
94 switch (button) {
95 case 1:
96 break;
97
98 case 2:
8d6b6bf4 99 if (kCFCoreFoundationVersionNumber >= 700)
d10105f2
JF
100 system("killall backboardd");
101 else
102 // XXX: there are better ways of restarting SpringBoard that would actually save state
103 exit(0);
39f17851
JF
104 break;
105
106 case 3:
88d77501 107 [[UIApplication sharedApplication] applicationOpenURL:[NSURL URLWithString:@"http://cydia.saurik.com/safemode/"]];
39f17851
JF
108 break;
109 }
110
111 [self dismiss];
112}
113
114void SafeModeAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
2c75d26c 115 UIAlertView *sheet([self alertSheet]);
0625b5c3 116
39f17851 117 [sheet setDelegate:self];
a6712361 118 [sheet setBodyText:@"We apologize for the inconvenience, but SpringBoard has just crashed.\n\nMobileSubstrate /did not/ cause this problem: it has protected you from it.\n\nYour device is now running in Safe Mode. All extensions that support this safety system are disabled.\n\nReboot (or restart SpringBoard) to return to the normal mode. To return to this dialog touch the status bar.\n\nTap \"Help\" below for more tips."];
39f17851
JF
119 [sheet addButtonWithTitle:@"OK"];
120 [sheet addButtonWithTitle:@"Restart"];
121 [sheet addButtonWithTitle:@"Help"];
122 [sheet setNumberOfRows:1];
0625b5c3 123
e5a54d69
JF
124 if ([sheet respondsToSelector:@selector(setForceHorizontalButtonsLayout:)])
125 [sheet setForceHorizontalButtonsLayout:YES];
39f17851
JF
126}
127
128void SafeModeAlertItem$performUnlockAction(id self, SEL sel) {
129 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
130}
131
132static void MSAlert() {
133 if ($SafeModeAlertItem == nil)
134 $SafeModeAlertItem = objc_lookUpClass("SafeModeAlertItem");
135 if ($SafeModeAlertItem == nil) {
136 $SafeModeAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "SafeModeAlertItem", 0);
137 if ($SafeModeAlertItem == nil)
138 return;
139
140 class_addMethod($SafeModeAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &SafeModeAlertItem$alertSheet$buttonClicked$, "v@:@i");
141 class_addMethod($SafeModeAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &SafeModeAlertItem$configure$requirePasscodeForActions$, "v@:cc");
142 class_addMethod($SafeModeAlertItem, @selector(performUnlockAction), (IMP) SafeModeAlertItem$performUnlockAction, "v@:");
143 objc_registerClassPair($SafeModeAlertItem);
144 }
145
146 if ($SBAlertItemsController != nil)
a938d168 147 [[$SBAlertItemsController sharedInstance] activateAlertItem:[[[$SafeModeAlertItem alloc] init] autorelease]];
39f17851
JF
148}
149
0625b5c3
JF
150
151// XXX: on iOS 5.0, we really would prefer avoiding
152
d641d519 153MSInstanceMessageHook2(void, SBStatusBar, touchesEnded,withEvent, id, touches, id, event) {
b4f9ed46 154 MSAlert();
d641d519 155 MSOldCall(touches, event);
b4f9ed46
JF
156}
157
d641d519 158MSInstanceMessageHook1(void, SBStatusBar, mouseDown, void *, event) {
39f17851 159 MSAlert();
d641d519 160 MSOldCall(event);
39f17851
JF
161}
162
d641d519 163MSInstanceMessageHook2(void, UIStatusBar, touchesBegan,withEvent, void *, touches, void *, event) {
e5a54d69 164 MSAlert();
d641d519 165 MSOldCall(touches, event);
e5a54d69
JF
166}
167
0625b5c3
JF
168
169// this fairly complex code came from Grant, to solve the "it Safe Mode"-in-bar bug
170
d641d519 171MSInstanceMessageHook0(void, SBStatusBarDataManager, _updateTimeString) {
22a7ee91
JF
172 char *_data(&MSHookIvar<char>(self, "_data"));
173 if (_data == NULL)
174 return;
175
176 Ivar _itemIsEnabled(object_getInstanceVariable(self, "_itemIsEnabled", NULL));
177 if (_itemIsEnabled == NULL)
178 return;
179
180 Ivar _itemIsCloaked(object_getInstanceVariable(self, "_itemIsCloaked", NULL));
181 if (_itemIsCloaked == NULL)
182 return;
183
184 size_t enabledOffset(ivar_getOffset(_itemIsEnabled));
185 size_t cloakedOffset(ivar_getOffset(_itemIsCloaked));
186 if (enabledOffset >= cloakedOffset)
187 return;
188
189 size_t offset(cloakedOffset - enabledOffset);
190 char *timeString(_data + offset);
191 strcpy(timeString, "Exit Safe Mode");
e5a54d69
JF
192}
193
0625b5c3 194
f7673201
JF
195static bool alerted_;
196
197static void AlertIfNeeded() {
198 if (alerted_)
39f17851 199 return;
f7673201 200 alerted_ = true;
39f17851 201 MSAlert();
25f84761
JF
202}
203
0625b5c3
JF
204
205// on iOS 4.3 and above we can use this advertisement, which seems to check every time the user unlocks
206// XXX: verify that this still works on iOS 5.0
207
f7673201
JF
208MSClassMessageHook0(void, AAAccountManager, showMobileMeOfferIfNecessary) {
209 AlertIfNeeded();
210}
211
0625b5c3
JF
212
213// -[SBIconController showInfoAlertIfNeeded] explains how to drag icons around the iPhone home screen
214// it used to be shown to users when they unlocked their screen for the first time, and happened every unlock
215// however, as of iOS 4.3, it got relegated to only appearing once the user installed an app or web clip
216
f7673201
JF
217MSInstanceMessageHook0(void, SBIconController, showInfoAlertIfNeeded) {
218 AlertIfNeeded();
219}
220
0625b5c3
JF
221
222// the icon state, including crazy configurations like Five Icon Dock, is stored in SpringBoard's defaults
223// unfortunately, SpringBoard on iOS 2.0 and 2.1 (maybe 2.2 as well) buffer overrun with more than 4 icons
224// there is a third party package called IconSupport that remedies this, but not everyone is using it yet
225
d641d519 226MSInstanceMessageHook0(int, SBButtonBar, maxIconColumns) {
25f84761
JF
227 static int max;
228 if (max == 0) {
d641d519 229 max = MSOldCall();
25f84761
JF
230 if (NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults])
231 if (NSDictionary *iconState = [defaults objectForKey:@"iconState"])
232 if (NSDictionary *buttonBar = [iconState objectForKey:@"buttonBar"])
233 if (NSArray *iconMatrix = [buttonBar objectForKey:@"iconMatrix"])
234 if ([iconMatrix count] != 0)
235 if (NSArray *row = [iconMatrix objectAtIndex:0]) {
236 int count([row count]);
237 if (max < count)
238 max = count;
239 }
240 } return max;
241}
242
0625b5c3 243
d641d519
JF
244MSInstanceMessageHook0(id, SBUIController, init) {
245 if ((self = MSOldCall()) != nil) {
b4f9ed46
JF
246 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
247 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
248
249 UIView *layer;
250 if (&_contentLayer != NULL)
251 layer = _contentLayer;
252 else if (&_contentView != NULL)
253 layer = _contentView;
254 else
255 layer = nil;
256
257 if (layer != nil)
258 [layer setBackgroundColor:[UIColor darkGrayColor]];
259 } return self;
25f84761
JF
260}
261
1c74ea29 262#define Paper_ "/Library/MobileSubstrate/MobileSafety.png"
dbbe0f32 263
d641d519 264MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
dbbe0f32
JF
265 return [UIImage imageWithContentsOfFile:@Paper_];
266}
267
d641d519 268MSInstanceMessageHook0(void, SBStatusBarTimeView, tile) {
39f17851
JF
269 NSString *&_time(MSHookIvar<NSString *>(self, "_time"));
270 CGRect &_textRect(MSHookIvar<CGRect>(self, "_textRect"));
25f84761 271 if (_time != nil)
39f17851 272 [_time release];
dbbe0f32 273 _time = [@"Exit Safe Mode" retain];
2c75d26c 274 id font([self textFont]);
88d77501 275 CGSize size([_time sizeWithFont:font]);
39f17851 276 CGRect frame([self frame]);
39f17851
JF
277 _textRect.size = size;
278 _textRect.origin.x = (frame.size.width - size.width) / 2;
279 _textRect.origin.y = (frame.size.height - size.height) / 2;
25f84761 280}
e9502538 281
0625b5c3
JF
282
283// notification widgets ("wee apps" or "bulletin board sections") are capable of crashing SpringBoard
284// unfortunately, which ones are in use are stored in SpringBoard's defaults, so we need to turn them off
285
e9502538
JF
286MSInstanceMessageHook0(BOOL, BBSectionInfo, showsInNotificationCenter) {
287 return NO;
288}
e06a3b1a
JF
289
290
291// on iOS 6.0, Apple split parts of SpringBoard into a daemon called backboardd, including app launches
292// in order to allow safe mode to propogate into applications, we need to then tell backboardd here
293// XXX: (all of this should be replaced, however, with per-process launchd-mediated exception handling)
294
295MSInstanceMessageHook1(void, BKSApplicationLaunchSettings, setEnvironment, NSDictionary *, original) {
296 if (original == nil)
297 return MSOldCall(nil);
298
299 NSMutableDictionary *modified([original mutableCopy]);
300 [modified setObject:@"1" forKey:@"_MSSafeMode"];
301 return MSOldCall(modified);
302}
43b2fcfa
JF
303
304MSInitialize {
305 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
306
307 // on iOS 6, backboardd is in charge of brightness, and freaks out when SpringBoard restarts :(
308 // the result is that the device is super dark until we attempt to update the brightness here.
309
310 if (kCFCoreFoundationVersionNumber >= 700) {
311 if (void (*GSEventSetBacklightLevel)(float) = reinterpret_cast<void (*)(float)>(dlsym(RTLD_DEFAULT, "GSEventSetBacklightLevel")))
312 if (NSMutableDictionary *defaults = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Library/Preferences/com.apple.springboard.plist", NSHomeDirectory()]])
313 if (NSNumber *level = [defaults objectForKey:@"SBBacklightLevel2"])
314 GSEventSetBacklightLevel([level floatValue]);
315 }
316
317 [pool release];
318}