]> git.saurik.com Git - safemode-ios.git/blob - MobileSafety.mm
3510a7fc75ba2d58caa547211d65d2b6b8d09bc2
[safemode-ios.git] / MobileSafety.mm
1 /* Cydia Substrate - Powerful Code Insertion Platform
2 * Copyright (C) 2008-2011 Jay Freeman (saurik)
3 */
4
5 /* GNU Lesser General Public License, Version 3 {{{ */
6 /*
7 * Substrate is free software: you can redistribute it and/or modify it under
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.
11 *
12 * Substrate is distributed in the hope that it will be useful, but WITHOUT
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.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with Substrate. If not, see <http://www.gnu.org/licenses/>.
19 **/
20 /* }}} */
21
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
38 #import <CoreFoundation/CoreFoundation.h>
39 #import <Foundation/Foundation.h>
40 #import <CoreGraphics/CGGeometry.h>
41 #import <UIKit/UIKit.h>
42
43 #include "CydiaSubstrate.h"
44
45 MSClassHook(UIStatusBar)
46
47 MSClassHook(UIImage)
48 MSMetaClassHook(UIImage)
49
50 MSClassHook(AAAccountManager)
51 MSMetaClassHook(AAAccountManager)
52
53 MSClassHook(BBSectionInfo)
54
55 MSClassHook(SBAlertItemsController)
56 MSClassHook(SBButtonBar)
57 MSClassHook(SBIconController)
58 MSClassHook(SBStatusBar)
59 MSClassHook(SBStatusBarDataManager)
60 MSClassHook(SBStatusBarTimeView)
61 MSClassHook(SBUIController)
62
63 Class $SafeModeAlertItem;
64
65 @interface SBAlertItem : NSObject {
66 }
67 - (UIAlertView *) alertSheet;
68 - (void) dismiss;
69 @end
70
71 @interface SBAlertItemsController : NSObject {
72 }
73 + (SBAlertItemsController *) sharedInstance;
74 - (void) activateAlertItem:(SBAlertItem *)item;
75 @end
76
77 @interface SBStatusBarTimeView : UIView {
78 }
79 - (id) textFont;
80 @end
81
82 @interface UIApplication (CydiaSubstrate)
83 - (void) applicationOpenURL:(id)url;
84 @end
85
86 @interface UIAlertView (CydiaSubstrate)
87 - (void) setForceHorizontalButtonsLayout:(BOOL)force;
88 - (void) setBodyText:(NSString *)body;
89 - (void) setNumberOfRows:(NSInteger)rows;
90 @end
91
92 void SafeModeAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
93 switch (button) {
94 case 1:
95 break;
96
97 case 2:
98 // XXX: there are better ways of restarting SpringBoard that would actually save state
99 exit(0);
100 break;
101
102 case 3:
103 [[UIApplication sharedApplication] applicationOpenURL:[NSURL URLWithString:@"http://cydia.saurik.com/safemode/"]];
104 break;
105 }
106
107 [self dismiss];
108 }
109
110 void SafeModeAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
111 UIAlertView *sheet([self alertSheet]);
112
113 [sheet setDelegate:self];
114 [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."];
115 [sheet addButtonWithTitle:@"OK"];
116 [sheet addButtonWithTitle:@"Restart"];
117 [sheet addButtonWithTitle:@"Help"];
118 [sheet setNumberOfRows:1];
119
120 if ([sheet respondsToSelector:@selector(setForceHorizontalButtonsLayout:)])
121 [sheet setForceHorizontalButtonsLayout:YES];
122 }
123
124 void SafeModeAlertItem$performUnlockAction(id self, SEL sel) {
125 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
126 }
127
128 static void MSAlert() {
129 if ($SafeModeAlertItem == nil)
130 $SafeModeAlertItem = objc_lookUpClass("SafeModeAlertItem");
131 if ($SafeModeAlertItem == nil) {
132 $SafeModeAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "SafeModeAlertItem", 0);
133 if ($SafeModeAlertItem == nil)
134 return;
135
136 class_addMethod($SafeModeAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &SafeModeAlertItem$alertSheet$buttonClicked$, "v@:@i");
137 class_addMethod($SafeModeAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &SafeModeAlertItem$configure$requirePasscodeForActions$, "v@:cc");
138 class_addMethod($SafeModeAlertItem, @selector(performUnlockAction), (IMP) SafeModeAlertItem$performUnlockAction, "v@:");
139 objc_registerClassPair($SafeModeAlertItem);
140 }
141
142 if ($SBAlertItemsController != nil)
143 [[$SBAlertItemsController sharedInstance] activateAlertItem:[[[$SafeModeAlertItem alloc] init] autorelease]];
144 }
145
146
147 // XXX: on iOS 5.0, we really would prefer avoiding
148
149 MSInstanceMessageHook2(void, SBStatusBar, touchesEnded,withEvent, id, touches, id, event) {
150 MSAlert();
151 MSOldCall(touches, event);
152 }
153
154 MSInstanceMessageHook1(void, SBStatusBar, mouseDown, void *, event) {
155 MSAlert();
156 MSOldCall(event);
157 }
158
159 MSInstanceMessageHook2(void, UIStatusBar, touchesBegan,withEvent, void *, touches, void *, event) {
160 MSAlert();
161 MSOldCall(touches, event);
162 }
163
164
165 // this fairly complex code came from Grant, to solve the "it Safe Mode"-in-bar bug
166
167 MSInstanceMessageHook0(void, SBStatusBarDataManager, _updateTimeString) {
168 char *_data(&MSHookIvar<char>(self, "_data"));
169 if (_data == NULL)
170 return;
171
172 Ivar _itemIsEnabled(object_getInstanceVariable(self, "_itemIsEnabled", NULL));
173 if (_itemIsEnabled == NULL)
174 return;
175
176 Ivar _itemIsCloaked(object_getInstanceVariable(self, "_itemIsCloaked", NULL));
177 if (_itemIsCloaked == NULL)
178 return;
179
180 size_t enabledOffset(ivar_getOffset(_itemIsEnabled));
181 size_t cloakedOffset(ivar_getOffset(_itemIsCloaked));
182 if (enabledOffset >= cloakedOffset)
183 return;
184
185 size_t offset(cloakedOffset - enabledOffset);
186 char *timeString(_data + offset);
187 strcpy(timeString, "Exit Safe Mode");
188 }
189
190
191 static bool alerted_;
192
193 static void AlertIfNeeded() {
194 if (alerted_)
195 return;
196 alerted_ = true;
197 MSAlert();
198 }
199
200
201 // on iOS 4.3 and above we can use this advertisement, which seems to check every time the user unlocks
202 // XXX: verify that this still works on iOS 5.0
203
204 MSClassMessageHook0(void, AAAccountManager, showMobileMeOfferIfNecessary) {
205 AlertIfNeeded();
206 }
207
208
209 // -[SBIconController showInfoAlertIfNeeded] explains how to drag icons around the iPhone home screen
210 // it used to be shown to users when they unlocked their screen for the first time, and happened every unlock
211 // however, as of iOS 4.3, it got relegated to only appearing once the user installed an app or web clip
212
213 MSInstanceMessageHook0(void, SBIconController, showInfoAlertIfNeeded) {
214 AlertIfNeeded();
215 }
216
217
218 // the icon state, including crazy configurations like Five Icon Dock, is stored in SpringBoard's defaults
219 // unfortunately, SpringBoard on iOS 2.0 and 2.1 (maybe 2.2 as well) buffer overrun with more than 4 icons
220 // there is a third party package called IconSupport that remedies this, but not everyone is using it yet
221
222 MSInstanceMessageHook0(int, SBButtonBar, maxIconColumns) {
223 static int max;
224 if (max == 0) {
225 max = MSOldCall();
226 if (NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults])
227 if (NSDictionary *iconState = [defaults objectForKey:@"iconState"])
228 if (NSDictionary *buttonBar = [iconState objectForKey:@"buttonBar"])
229 if (NSArray *iconMatrix = [buttonBar objectForKey:@"iconMatrix"])
230 if ([iconMatrix count] != 0)
231 if (NSArray *row = [iconMatrix objectAtIndex:0]) {
232 int count([row count]);
233 if (max < count)
234 max = count;
235 }
236 } return max;
237 }
238
239
240 MSInstanceMessageHook0(id, SBUIController, init) {
241 if ((self = MSOldCall()) != nil) {
242 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
243 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
244
245 UIView *layer;
246 if (&_contentLayer != NULL)
247 layer = _contentLayer;
248 else if (&_contentView != NULL)
249 layer = _contentView;
250 else
251 layer = nil;
252
253 if (layer != nil)
254 [layer setBackgroundColor:[UIColor darkGrayColor]];
255 } return self;
256 }
257
258 #define Paper_ "/Library/MobileSubstrate/MobileSafety.png"
259
260 MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
261 return [UIImage imageWithContentsOfFile:@Paper_];
262 }
263
264 MSInstanceMessageHook0(void, SBStatusBarTimeView, tile) {
265 NSString *&_time(MSHookIvar<NSString *>(self, "_time"));
266 CGRect &_textRect(MSHookIvar<CGRect>(self, "_textRect"));
267 if (_time != nil)
268 [_time release];
269 _time = [@"Exit Safe Mode" retain];
270 id font([self textFont]);
271 CGSize size([_time sizeWithFont:font]);
272 CGRect frame([self frame]);
273 _textRect.size = size;
274 _textRect.origin.x = (frame.size.width - size.width) / 2;
275 _textRect.origin.y = (frame.size.height - size.height) / 2;
276 }
277
278
279 // notification widgets ("wee apps" or "bulletin board sections") are capable of crashing SpringBoard
280 // unfortunately, which ones are in use are stored in SpringBoard's defaults, so we need to turn them off
281
282 MSInstanceMessageHook0(BOOL, BBSectionInfo, showsInNotificationCenter) {
283 return NO;
284 }