]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Gracefully handle someone /deleting/ the firmware package.
[cydia.git] / Cydia.mm
index 4b3fa9f172015358fcbd330f1a3ccc0c8353b409..7a5a33d130aaf5e2f0dac2a48c909c4ae4c744c7 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -35,6 +35,9 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+// XXX: wtf/FastMalloc.h... wtf?
+#define USE_SYSTEM_MALLOC 1
+
 /* #include Directives {{{ */
 #import "UICaboodle.h"
 
 // XXX: remove
 #import <MessageUI/MailComposeController.h>
 
-#include <WebKit/DOMCSSPrimitiveValue.h>
-#include <WebKit/DOMCSSStyleDeclaration.h>
-#include <WebKit/DOMDocument.h>
-#include <WebKit/DOMHTMLBodyElement.h>
-#include <WebKit/DOMNodeList.h>
-#include <WebKit/DOMRGBColor.h>
-
-#include <WebKit/WebFrame.h>
-#include <WebKit/WebPolicyDelegate.h>
-#include <WebKit/WebScriptObject.h>
-
-#import <WebKit/WebView.h>
-#import <WebKit/WebView-WebPrivate.h>
-
-#import <JavaScriptCore/JavaScriptCore.h>
-
 #include <sstream>
 #include <string>
 
@@ -92,6 +79,8 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/sysctl.h>
+#include <sys/param.h>
+#include <sys/mount.h>
 
 #include <notify.h>
 #include <dlfcn.h>
@@ -109,6 +98,8 @@ extern "C" {
 
 #import "BrowserView.h"
 #import "ResetView.h"
+
+#import "substrate.h"
 /* }}} */
 
 //#define _finline __attribute__((force_inline))
@@ -273,6 +264,7 @@ extern NSString * const kCAFilterNearest;
 
 #define ForRelease 0
 #define ForSaurik (1 && !ForRelease)
+#define IgnoreInstall (0 && !ForRelease)
 #define RecycleWebViews 0
 #define AlwaysReload (1 && !ForRelease)
 
@@ -611,6 +603,7 @@ static const float KeyboardTime_ = 0.3f;
 
 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
+#define NotifyConfig_ "/etc/notify.conf"
 
 static CGColor Blue_;
 static CGColor Blueish_;
@@ -636,20 +629,12 @@ static UIFont *Font18Bold_;
 static UIFont *Font22Bold_;
 
 static const char *Machine_ = NULL;
-static const NSString *UniqueID_ = NULL;
-
-unsigned Major_;
-unsigned Minor_;
-unsigned BugFix_;
+static const NSString *UniqueID_ = nil;
+static const NSString *Build_ = nil;
 
 CFLocaleRef Locale_;
 CGColorSpaceRef space_;
 
-#define FW_LEAST(major, minor, bugfix) \
-    (major < Major_ || major == Major_ && \
-        (minor < Minor_ || minor == Minor_ && \
-            bugfix <= BugFix_))
-
 bool bootstrap_;
 bool reload_;
 
@@ -1744,6 +1729,8 @@ class Progress :
         [warnings addObject:@"illegal package identifier"];
     else for (size_t i(0); i != length; ++i)
         if (
+            /* XXX: technically this is not allowed */
+            (name[i] < 'A' || name[i] > 'Z') &&
             (name[i] < 'a' || name[i] > 'z') &&
             (name[i] < '0' || name[i] > '9') &&
             (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
@@ -1751,17 +1738,25 @@ class Progress :
 
     if (strcmp(name, "cydia") != 0) {
         bool cydia = false;
+        bool _private = false;
         bool stash = false;
 
+        bool repository = [[self section] isEqualToString:@"Repositories"];
+
         if (NSArray *files = [self files])
             for (NSString *file in files)
                 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
                     cydia = true;
+                else if (!_private && [file isEqualToString:@"/private"])
+                    _private = true;
                 else if (!stash && [file isEqualToString:@"/var/stash"])
                     stash = true;
 
-        if (cydia)
+        /* XXX: this is not sensitive enough. only some folders are valid. */
+        if (cydia && !repository)
             [warnings addObject:@"files installed into Cydia.app"];
+        if (_private)
+            [warnings addObject:@"files installed with /private/*"];
         if (stash)
             [warnings addObject:@"files installed to /var/stash"];
     }
@@ -2863,8 +2858,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return @"Cancel";
 }
 
-- (NSString *) _rightButtonTitle {
+- (id) _rightButtonTitle {
+#if AlwaysReload || IgnoreInstall
+    return @"Reload";
+#else
     return issues_ == nil ? @"Confirm" : nil;
+#endif
 }
 
 - (void) _leftButtonClicked {
@@ -2873,6 +2872,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 #if !AlwaysReload
 - (void) _rightButtonClicked {
+#if IgnoreInstall
+    return [super _rightButtonClicked];
+#endif
     if (essential_ != nil)
         [essential_ popupAlertAnimated:YES];
     else {
@@ -2942,6 +2944,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     id delegate_;
     BOOL running_;
     SHA1SumValue springlist_;
+    SHA1SumValue notifyconf_;
     SHA1SumValue sandplate_;
     size_t received_;
     NSTimeInterval last_;
@@ -3161,6 +3164,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             Finish_ = 4;
     }
 
+    if (Finish_ < 4) {
+        FileFd file(NotifyConfig_, FileFd::ReadOnly);
+        MMap mmap(file, MMap::ReadOnly);
+        SHA1Summation sha1;
+        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+        if (!(notifyconf_ == sha1.Result()))
+            Finish_ = 4;
+    }
+
     if (Finish_ < 3) {
         FileFd file(SpringBoard_, FileFd::ReadOnly);
         MMap mmap(file, MMap::ReadOnly);
@@ -3261,6 +3273,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         sandplate_ = sha1.Result();
     }
 
+    {
+        FileFd file(NotifyConfig_, FileFd::ReadOnly);
+        MMap mmap(file, MMap::ReadOnly);
+        SHA1Summation sha1;
+        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+        notifyconf_ = sha1.Result();
+    }
+
     {
         FileFd file(SpringBoard_, FileFd::ReadOnly);
         MMap mmap(file, MMap::ReadOnly);
@@ -3911,7 +3931,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 #endif
 
-- (NSString *) _rightButtonTitle {
+- (id) _rightButtonTitle {
     int count = [buttons_ count];
     return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
 }
@@ -4600,7 +4620,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
 }
 
-- (NSString *) rightButtonTitle {
+- (id) rightButtonTitle {
     return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
 }
 
@@ -4671,7 +4691,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return @"Packages";
 }
 
-- (NSString *) rightButtonTitle {
+- (id) rightButtonTitle {
     return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
 }
 
@@ -4758,7 +4778,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 #if !AlwaysReload
-- (NSString *) _rightButtonTitle {
+- (id) _rightButtonTitle {
     return nil;
 }
 #endif
@@ -4770,14 +4790,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-@interface WebView (Cydia)
-- (void) setScriptDebugDelegate:(id)delegate;
-- (void) _setFormDelegate:(id)delegate;
-- (void) _setUIKitDelegate:(id)delegate;
-- (void) setWebMailDelegate:(id)delegate;
-- (void) _setLayoutInterval:(float)interval;
-@end
-
 /* Indirect Delegate {{{ */
 @interface IndirectDelegate : NSProxy {
     _transient volatile id delegate_;
@@ -4814,747 +4826,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 /* }}} */
-/* Browser Implementation {{{ */
-@implementation BrowserView
-#include "internals.h"
-
-- (void) dealloc {
-    if (challenge_ != nil)
-        [challenge_ release];
-
-    WebView *webview = [webview_ webView];
-    [webview setFrameLoadDelegate:nil];
-    [webview setResourceLoadDelegate:nil];
-    [webview setUIDelegate:nil];
-    [webview setScriptDebugDelegate:nil];
-    [webview setPolicyDelegate:nil];
-
-    [webview setDownloadDelegate:nil];
-
-    [webview _setFormDelegate:nil];
-    [webview _setUIKitDelegate:nil];
-    [webview setWebMailDelegate:nil];
-    [webview setEditingDelegate:nil];
-
-    [webview_ setDelegate:nil];
-    [webview_ setGestureDelegate:nil];
-
-    //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
-
-    [webview close];
-
-#if RecycleWebViews
-    [webview_ removeFromSuperview];
-    [Documents_ addObject:[webview_ autorelease]];
-#else
-    [webview_ release];
-#endif
-
-    [indirect_ setDelegate:nil];
-    [indirect_ release];
-
-    [scroller_ setDelegate:nil];
-
-    if (button_ != nil)
-        [button_ release];
-    if (style_ != nil)
-        [style_ release];
-    if (function_ != nil)
-        [function_ release];
-
-    [scroller_ release];
-    [urls_ release];
-    [indicator_ release];
-    if (confirm_ != nil)
-        [confirm_ release];
-    if (title_ != nil)
-        [title_ release];
-    [super dealloc];
-}
-
-- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
-    [self loadRequest:[NSURLRequest
-        requestWithURL:url
-        cachePolicy:policy
-        timeoutInterval:30.0
-    ]];
-}
-
-- (void) loadURL:(NSURL *)url {
-    [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
-}
-
-- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
-    NSMutableURLRequest *copy = [request mutableCopy];
-
-    if (Machine_ != NULL)
-        [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
-    if (UniqueID_ != nil)
-        [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
-
-    if (Role_ != nil)
-        [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
-
-    return copy;
-}
-
-- (void) loadRequest:(NSURLRequest *)request {
-    pushed_ = true;
-    [webview_ loadRequest:request];
-}
-
-- (void) reloadURL {
-    if ([urls_ count] == 0)
-        return;
-    NSURL *url = [[[urls_ lastObject] retain] autorelease];
-    [urls_ removeLastObject];
-    [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
-}
-
-- (WebView *) webView {
-    return [webview_ webView];
-}
-
-- (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
-    [scroller_ setContentSize:frame.size];
-}
-
-- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
-    [self view:sender didSetFrame:frame];
-}
-
-- (void) pushPage:(RVPage *)page {
-    [self setBackButtonTitle:title_];
-    [page setDelegate:delegate_];
-    [book_ pushPage:page];
-}
-
-- (BOOL) getSpecial:(NSURL *)url {
-    NSString *href([url absoluteString]);
-    NSString *scheme([[url scheme] lowercaseString]);
-
-    RVPage *page = nil;
-
-    if ([href hasPrefix:@"apptapp://package/"])
-        page = [delegate_ pageForPackage:[href substringFromIndex:18]];
-    else if ([scheme isEqualToString:@"cydia"]) {
-        page = [delegate_ pageForURL:url hasTag:NULL];
-        if (page == nil)
-            return false;
-    } else if (![scheme isEqualToString:@"apptapp"])
-        return false;
-
-    if (page != nil)
-        [self pushPage:page];
-    return true;
-}
-
-- (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
-        initWithTitle:nil
-        buttons:[NSArray arrayWithObjects:@"OK", nil]
-        defaultButtonIndex:0
-        delegate:self
-        context:@"alert"
-    ] autorelease];
-
-    [sheet setBodyText:message];
-    [sheet popupAlertAnimated:YES];
-}
-
-- (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
-        initWithTitle:nil
-        buttons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil]
-        defaultButtonIndex:0
-        delegate:self
-        context:@"confirm"
-    ] autorelease];
-
-    [sheet setNumberOfRows:1];
-    [sheet setBodyText:message];
-    [sheet popupAlertAnimated:YES];
-
-    NSRunLoop *loop([NSRunLoop currentRunLoop]);
-    NSDate *future([NSDate distantFuture]);
-
-    while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
-
-    NSNumber *confirm([confirm_ autorelease]);
-    confirm_ = nil;
-    return [confirm boolValue];
-}
-
-/* Web Scripting {{{ */
-+ (NSString *) webScriptNameForSelector:(SEL)selector {
-    if (selector == @selector(getPackageById:))
-        return @"getPackageById";
-    else if (selector == @selector(setButtonTitle:withStyle:toFunction:))
-        return @"setButtonTitle";
-    else if (selector == @selector(supports:))
-        return @"supports";
-    else
-        return nil;
-}
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
-    return [self webScriptNameForSelector:selector] == nil;
-}
-
-- (BOOL) supports:(NSString *)feature {
-    return [feature isEqualToString:@"window.open"];
-}
-
-- (Package *) getPackageById:(NSString *)id {
-    return [[Database sharedInstance] packageWithName:id];
-}
-
-- (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
-    if (button_ != nil)
-        [button_ autorelease];
-    button_ = button == nil ? nil : [button retain];
-
-    if (style_ != nil)
-        [style_ autorelease];
-    style_ = style == nil ? nil : [style retain];
-
-    if (function_ != nil)
-        [function_ autorelease];
-    function_ = function == nil ? nil : [function retain];
-}
-/* }}} */
-
-- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    [window setValue:self forKey:@"cydia"];
-}
-
-- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
-    if (NSURL *url = [request URL]) {
-        if (name != nil && [name isEqualToString:@"_open"])
-            [delegate_ openURL:url];
-
-        NSLog(@"win:%@:%@", url, [action description]);
-        if (![self getSpecial:url]) {
-            NSString *scheme([[url scheme] lowercaseString]);
-            if ([scheme isEqualToString:@"mailto"])
-                [delegate_ openMailToURL:url];
-            else goto use;
-        }
-
-        [listener ignore];
-    } else use:
-        [listener use];
-}
-
-- (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
-    if ([WebView canShowMIMEType:type])
-        [listener use];
-    else {
-        // XXX: handle more mime types!
-        [listener ignore];
-        if (frame == [webView mainFrame])
-            [UIApp openURL:[request URL]];
-    }
-}
-
-- (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
-    NSURL *url([request URL]);
-
-    if (url == nil) use: {
-        [listener use];
-        return;
-    }
-    else NSLog(@"nav:%@:%@", url, [action description]);
-
-    const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
-
-    if (
-        [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
-        [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
-    ) {
-      open:
-        [UIApp openURL:url];
-      ignore:
-        [listener ignore];
-        return;
-    }
-
-    int store(_not(int));
-    if (NSURL *itms = [url itmsURL:&store]) {
-        NSLog(@"itms#%@#%u#%@", url, store, itms);
-        if (
-            store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
-            store == 2 && [capability containsObject:@"com.apple.AppStore"]
-        ) {
-            url = itms;
-            goto open;
-        }
-    }
-
-    NSString *scheme([[url scheme] lowercaseString]);
-
-    if ([scheme isEqualToString:@"tel"]) {
-        // XXX: intelligence
-        goto open;
-    }
-
-    if ([scheme isEqualToString:@"mailto"]) {
-        [delegate_ openMailToURL:url];
-        goto ignore;
-    }
 
-    if ([self getSpecial:url])
-        goto ignore;
-    else if ([WebView _canHandleRequest:request])
-        goto use;
-    else if ([url isSpringboardHandledURL])
-        goto open;
-    else
-        goto use;
-}
-
-- (void) webView:(WebView *)sender setStatusText:(NSString *)text {
-    //lprintf("Status:%s\n", [text UTF8String]);
-}
-
-- (void) _pushPage {
-    if (pushed_)
-        return;
-    pushed_ = true;
-    [book_ pushPage:self];
-}
-
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
-
-    if ([context isEqualToString:@"alert"])
-        [sheet dismiss];
-    else if ([context isEqualToString:@"confirm"]) {
-        switch (button) {
-            case 1:
-                confirm_ = [NSNumber numberWithBool:YES];
-            break;
-
-            case 2:
-                confirm_ = [NSNumber numberWithBool:NO];
-            break;
-        }
-
-        [sheet dismiss];
-    } else if ([context isEqualToString:@"challenge"]) {
-        id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
-
-        switch (button) {
-            case 1: {
-                NSString *username([[sheet textFieldAtIndex:0] text]);
-                NSString *password([[sheet textFieldAtIndex:1] text]);
-
-                NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
-
-                [sender useCredential:credential forAuthenticationChallenge:challenge_];
-            } break;
-
-            case 2:
-                [sender cancelAuthenticationChallenge:challenge_];
-            break;
-
-            default:
-                _assert(false);
-        }
-
-        [challenge_ release];
-        challenge_ = nil;
-
-        [sheet dismiss];
-    }
-}
-
-- (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
-    challenge_ = [challenge retain];
-
-    NSURLProtectionSpace *space([challenge protectionSpace]);
-    NSString *realm([space realm]);
-    if (realm == nil)
-        realm = @"";
-
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
-        initWithTitle:realm
-        buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
-        defaultButtonIndex:0
-        delegate:self
-        context:@"challenge"
-    ] autorelease];
-
-    [sheet setNumberOfRows:1];
-
-    [sheet addTextFieldWithValue:@"" label:@"username"];
-    [sheet addTextFieldWithValue:@"" label:@"password"];
-
-    UITextField *username([sheet textFieldAtIndex:0]); {
-        UITextInputTraits *traits([username textInputTraits]);
-        [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
-        [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
-        [traits setKeyboardType:UIKeyboardTypeASCIICapable];
-        [traits setReturnKeyType:UIReturnKeyNext];
-    }
-
-    UITextField *password([sheet textFieldAtIndex:1]); {
-        UITextInputTraits *traits([password textInputTraits]);
-        [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
-        [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
-        [traits setKeyboardType:UIKeyboardTypeASCIICapable];
-        // XXX: UIReturnKeyDone
-        [traits setReturnKeyType:UIReturnKeyNext];
-        [traits setSecureTextEntry:YES];
-    }
-
-    [sheet popupAlertAnimated:YES];
-}
-
-- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
-    NSURL *url = [request URL];
-    if ([self getSpecial:url])
-        return nil;
-    [self _pushPage];
-    return [self _addHeadersToRequest:request];
-}
-
-- (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
-    [self setBackButtonTitle:title_];
-
-    BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
-    [browser setDelegate:delegate_];
-
-    if (pushed) {
-        [browser loadRequest:[self _addHeadersToRequest:request]];
-        [book_ pushPage:browser];
-    }
-
-    return [browser webView];
-}
-
-- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
-    return [self _createWebViewWithRequest:request pushed:(request != nil)];
-}
-
-- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
-    return [self _createWebViewWithRequest:request pushed:YES];
-}
-
-- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
-
-    title_ = [title retain];
-    [book_ reloadTitleForPage:self];
-}
-
-- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
-
-    reloading_ = false;
-    loading_ = true;
-    [self reloadButtons];
-
-    if (title_ != nil) {
-        [title_ release];
-        title_ = nil;
-    }
-
-    if (button_ != nil) {
-        [button_ release];
-        button_ = nil;
-    }
-
-    if (style_ != nil) {
-        [style_ release];
-        style_ = nil;
-    }
-
-    if (function_ != nil) {
-        [function_ release];
-        function_ = nil;
-    }
-
-    [book_ reloadTitleForPage:self];
-
-    WebView *webview = [webview_ webView];
-    NSString *href = [webview mainFrameURL];
-    [urls_ addObject:[NSURL URLWithString:href]];
-
-    [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
-
-    CGRect webrect = [scroller_ bounds];
-    webrect.size.height = 0;
-    [webview_ setFrame:webrect];
-}
-
-- (void) _finishLoading {
-    if (!reloading_) {
-        loading_ = false;
-        [self reloadButtons];
-    }
-}
-
-- (bool) _loading {
-    return loading_;
-}
-
-- (void) reloadButtons {
-    if ([self _loading])
-        [indicator_ startAnimation];
-    else
-        [indicator_ stopAnimation];
-    [super reloadButtons];
-}
-
-- (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
-    return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
-    return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
-}
-
-- (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
-    return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
-    return [webview_ webView:sender didCommitLoadForFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
-    return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
-    if ([frame parentFrame] == nil) {
-        [self _finishLoading];
-
-        if (DOMDocument *document = [frame DOMDocument])
-            if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
-                for (DOMHTMLBodyElement *body in bodies) {
-                    DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
-
-                    bool colored(false);
-
-                    if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
-                        DOMRGBColor *rgb([color getRGBColorValue]);
-
-                        float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
-                        NSLog(@"alpha:%g", alpha);
-
-                        if (alpha != 0) {
-                            colored = true;
-
-                            [scroller_ setBackgroundColor:[UIColor
-                                colorWithRed:([[rgb red] getFloatValue:DOM_CSS_NUMBER] / 255)
-                                green:([[rgb green] getFloatValue:DOM_CSS_NUMBER] / 255)
-                                blue:([[rgb blue] getFloatValue:DOM_CSS_NUMBER] / 255)
-                                alpha:alpha
-                            ]];
-                        }
-                    }
-
-                    if (!colored)
-                        [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
-                    break;
-                }
-    }
-
-    return [webview_ webView:sender didFinishLoadForFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
-    [self _finishLoading];
-
-    [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
-        [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
-        [[error localizedDescription] stringByAddingPercentEscapes]
-    ]]];
-}
-
-- (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
-#if ForSaurik
-    lprintf("Console:%s\n", [[dictionary description] UTF8String]);
-#endif
-}
-
-- (id) initWithBook:(RVBook *)book {
-    if ((self = [super initWithBook:book]) != nil) {
-        loading_ = false;
-
-        struct CGRect bounds = [self bounds];
-
-        scroller_ = [[UIScroller alloc] initWithFrame:bounds];
-        [self addSubview:scroller_];
-
-        [scroller_ setShowBackgroundShadow:NO];
-        [scroller_ setFixedBackgroundPattern:YES];
-        [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
-
-        [scroller_ setScrollingEnabled:YES];
-        [scroller_ setAdjustForContentSizeChange:YES];
-        [scroller_ setClipsSubviews:YES];
-        [scroller_ setAllowsRubberBanding:YES];
-        [scroller_ setScrollDecelerationFactor:0.99];
-        [scroller_ setDelegate:self];
-
-        CGRect webrect = [scroller_ bounds];
-        webrect.size.height = 0;
-
-        WebView *webview;
-
-#if RecycleWebViews
-        webview_ = [Documents_ lastObject];
-        if (webview_ != nil) {
-            webview_ = [webview_ retain];
-            webview = [webview_ webView];
-            [Documents_ removeLastObject];
-            [webview_ setFrame:webrect];
-        } else {
-#else
-        if (true) {
-#endif
-            webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
-            webview = [webview_ webView];
-
-            // XXX: this is terribly (too?) expensive
-            [webview_ setDrawsBackground:NO];
-
-            [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
-
-            [webview_ setAllowsMessaging:YES];
-
-            [webview_ setTilingEnabled:YES];
-            [webview_ setDrawsGrid:NO];
-            [webview_ setLogsTilingChanges:NO];
-            [webview_ setTileMinificationFilter:kCAFilterNearest];
-            [webview_ setDetectsPhoneNumbers:NO];
-            [webview_ setAutoresizes:YES];
-
-            [webview_ setMinimumScale:0.25f forDocumentTypes:0x10];
-            [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10];
-            [webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
-
-            [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2];
-
-            [webview_ setMinimumScale:1.0f forDocumentTypes:0x8];
-            [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8];
-            [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8];
-
-            [webview_ _setDocumentType:0x4];
-
-            [webview_ setZoomsFocusedFormControl:YES];
-            [webview_ setContentsPosition:7];
-            [webview_ setEnabledGestures:0xa];
-            [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
-            [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
-
-            [webview_ setSmoothsFonts:YES];
-
-            [webview _setUsesLoaderCache:YES];
-            [webview setGroupName:@"Cydia"];
-            [webview _setLayoutInterval:0];
-        }
-
-        [webview_ setDelegate:self];
-        [webview_ setGestureDelegate:self];
-        [scroller_ addSubview:webview_];
-
-        //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
-
-        CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
-        indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
-        [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
-
-        Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
-        NSString *application = package == nil ? @"Cydia" : [NSString
-            stringWithFormat:@"Cydia/%@",
-            [package installed]
-        ]; [webview setApplicationNameForUserAgent:application];
-
-        indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
-
-        [webview setFrameLoadDelegate:self];
-        [webview setResourceLoadDelegate:indirect_];
-        [webview setUIDelegate:self];
-        [webview setScriptDebugDelegate:self];
-        [webview setPolicyDelegate:self];
-
-        urls_ = [[NSMutableArray alloc] initWithCapacity:16];
-
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-    } return self;
-}
-
-- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
-    [webview_ redrawScaledDocument];
-}
-
-- (void) _rightButtonClicked {
-    if (function_ == nil) {
-        reloading_ = true;
-        [self reloadURL];
-    } else {
-        [delegate_ clearFirstResponder];
-        JSObjectRef function([function_ JSObject]);
-        JSGlobalContextRef context([[[webview_ webView] mainFrame] globalContext]);
-        JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
-    }
-}
-
-- (NSString *) _rightButtonTitle {
-    return button_ != nil ? button_ : @"Reload";
-}
-
-- (NSString *) rightButtonTitle {
-    return [self _loading] ? @"" : [self _rightButtonTitle];
-}
-
-- (UINavigationButtonStyle) rightButtonStyle {
-    if (style_ == nil) normal:
-        return UINavigationButtonStyleNormal;
-    else if ([style_ isEqualToString:@"Normal"])
-        return UINavigationButtonStyleNormal;
-    else if ([style_ isEqualToString:@"Back"])
-        return UINavigationButtonStyleBack;
-    else if ([style_ isEqualToString:@"Highlighted"])
-        return UINavigationButtonStyleHighlighted;
-    else if ([style_ isEqualToString:@"Destructive"])
-        return UINavigationButtonStyleDestructive;
-    else goto normal;
-}
-
-- (NSString *) title {
-    return title_ == nil ? @"Loading" : title_;
-}
-
-- (NSString *) backButtonTitle {
-    return @"Browser";
-}
-
-- (void) setPageActive:(BOOL)active {
-    if (!active)
-        [indicator_ removeFromSuperview];
-    else
-        [[book_ navigationBar] addSubview:indicator_];
-}
-
-- (void) resetViewAnimated:(BOOL)animated {
-}
-
-- (void) setPushed:(bool)pushed {
-    pushed_ = pushed;
-}
-
-@end
-/* }}} */
+#include <BrowserView.m>
 
 /* Cydia Book {{{ */
 @interface CYBook : RVBook <
@@ -6114,7 +5387,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return @"Sections";
 }
 
-- (NSString *) rightButtonTitle {
+- (id) rightButtonTitle {
     return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
 }
 
@@ -6322,7 +5595,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return [(CYBook *)book_ updating] ? nil : @"Refresh";
 }
 
-- (NSString *) rightButtonTitle {
+- (id) rightButtonTitle {
     return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
 }
 
@@ -6459,10 +5732,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if ((self = [super initWithBook:book]) != nil) {
         CGRect pageBounds = [book_ pageBounds];
 
-        /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
-        [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
-        [self addSubview:pinstripe];*/
-
         transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
         [self addSubview:transition_];
 
@@ -7308,7 +6577,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
     NSString *context([sheet context]);
 
-    if ([context isEqualToString:@"fixhalf"]) {
+    if ([context isEqualToString:@"missing"])
+        [sheet dismiss];
+    else if ([context isEqualToString:@"fixhalf"]) {
         switch (button) {
             case 1:
                 @synchronized (self) {
@@ -7460,29 +6731,39 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
-    NSString *href = [url absoluteString];
-
     if (tag != NULL)
         tag = 0;
 
-    if ([href isEqualToString:@"cydia://add-source"])
+    NSString *scheme([[url scheme] lowercaseString]);
+    if (![scheme isEqualToString:@"cydia"])
+        return nil;
+    NSString *path([url absoluteString]);
+    if ([path length] < 8)
+        return nil;
+    path = [path substringFromIndex:8];
+    if (![path hasPrefix:@"/"])
+        path = [@"/" stringByAppendingString:path];
+
+    if ([path isEqualToString:@"/add-source"])
         return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
-    else if ([href isEqualToString:@"cydia://sources"])
+    else if ([path isEqualToString:@"/storage"])
+        return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
+    else if ([path isEqualToString:@"/sources"])
         return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
-    else if ([href isEqualToString:@"cydia://packages"])
+    else if ([path isEqualToString:@"/packages"])
         return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
-    else if ([href hasPrefix:@"cydia://url/"])
-        return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
-    else if ([href hasPrefix:@"cydia://launch/"])
-        [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
-    else if ([href hasPrefix:@"cydia://package-settings/"])
-        return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
-    else if ([href hasPrefix:@"cydia://package-signature/"])
-        return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
-    else if ([href hasPrefix:@"cydia://package/"])
-        return [self pageForPackage:[href substringFromIndex:16]];
-    else if ([href hasPrefix:@"cydia://files/"]) {
-        NSString *name = [href substringFromIndex:14];
+    else if ([path hasPrefix:@"/url/"])
+        return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
+    else if ([path hasPrefix:@"/launch/"])
+        [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
+    else if ([path hasPrefix:@"/package-settings/"])
+        return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
+    else if ([path hasPrefix:@"/package-signature/"])
+        return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
+    else if ([path hasPrefix:@"/package/"])
+        return [self pageForPackage:[path substringFromIndex:9]];
+    else if ([path hasPrefix:@"/files/"]) {
+        NSString *name = [path substringFromIndex:7];
 
         if (Package *package = [database_ packageWithName:name]) {
             FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
@@ -7726,11 +7007,19 @@ int main(int argc, char *argv[]) { _pooled
 
     UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
 
+    if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
+        Build_ = [system objectForKey:@"ProductBuildVersion"];
+
     /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
     AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
 
-    if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
-        Indices_ = [[NSMutableDictionary alloc] init];
+    /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
+        Indices_ = [[NSMutableDictionary alloc] init];*/
+
+    Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+        @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
+        @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
+    nil];
 
     if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
         Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
@@ -7764,8 +7053,10 @@ int main(int argc, char *argv[]) { _pooled
     Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
 #endif
 
-    if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
-        dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
+    if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
+        dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
+    /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
+        dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
 
     if (access("/User", F_OK) != 0)
         system("/usr/libexec/cydia/firmware.sh");