]> git.saurik.com Git - cydia.git/blobdiff - MobileCydia.mm
Remove obsolete commented-out source check stuff.
[cydia.git] / MobileCydia.mm
index 83f51e3f6ddc9b8050e90c1d67f76ad1145db427..e0539fa02988cdf99d4e73e3b9d3d02b14ec9b62 100644 (file)
@@ -1,40 +1,22 @@
 /* Cydia - iPhone UIKit Front-End for Debian APT
- * Copyright (C) 2008-2011  Jay Freeman (saurik)
+ * Copyright (C) 2008-2013  Jay Freeman (saurik)
 */
 
-/* Modified BSD License {{{ */
+/* GNU General Public License, Version 3 {{{ */
 /*
- *        Redistribution and use in source and binary
- * forms, with or without modification, are permitted
- * provided that the following conditions are met:
+ * Cydia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
  *
- * 1. Redistributions of source code must retain the
- *    above copyright notice, this list of conditions
- *    and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the
- *    above copyright notice, this list of conditions
- *    and the following disclaimer in the documentation
- *    and/or other materials provided with the
- *    distribution.
- * 3. The name of the author may not be used to endorse
- *    or promote products derived from this software
- *    without specific prior written permission.
+ * Cydia is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
  *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
+ * You should have received a copy of the GNU General Public License
+ * along with Cydia.  If not, see <http://www.gnu.org/licenses/>.
+**/
 /* }}} */
 
 // XXX: wtf/FastMalloc.h... wtf?
 #include <QuartzCore/CALayer.h>
 
 #include <WebCore/WebCoreThread.h>
+#include <WebKit/DOMHTMLIFrameElement.h>
 
 #include <algorithm>
 #include <iomanip>
+#include <set>
 #include <sstream>
 #include <string>
 
@@ -129,6 +113,7 @@ extern "C" {
 #include "CyteKit/IndirectDelegate.h"
 #include "CyteKit/PerlCompatibleRegEx.hpp"
 #include "CyteKit/TableViewCell.h"
+#include "CyteKit/TabBarController.h"
 #include "CyteKit/WebScriptObject-Cyte.h"
 #include "CyteKit/WebViewController.h"
 #include "CyteKit/WebViewTableViewCell.h"
@@ -253,14 +238,23 @@ union SplitHash {
 };
 // }}}
 
-static bool ShowPromoted_;
-
 static NSString *Colon_;
 NSString *Elision_;
 static NSString *Error_;
 static NSString *Warning_;
 
-static bool AprilFools_;
+static NSString *Cache_;
+
+static void (*$SBSSetInterceptsMenuButtonForever)(bool);
+
+static CFStringRef (*$MGCopyAnswer)(CFStringRef);
+
+static NSString *UniqueIdentifier(UIDevice *device = nil) {
+    if (kCFCoreFoundationVersionNumber < 800) // iOS 7.x
+        return [device ?: [UIDevice currentDevice] uniqueIdentifier];
+    else
+        return [(id)$MGCopyAnswer(CFSTR("UniqueDeviceID")) autorelease];
+}
 
 static bool IsReachable(const char *name) {
     SCNetworkReachabilityFlags flags; {
@@ -693,6 +687,7 @@ static bool Queuing_;
 static CYColor Blue_;
 static CYColor Blueish_;
 static CYColor Black_;
+static CYColor Folder_;
 static CYColor Off_;
 static CYColor White_;
 static CYColor Gray_;
@@ -711,6 +706,7 @@ static BOOL Ignored_;
 static _H<UIFont> Font12_;
 static _H<UIFont> Font12Bold_;
 static _H<UIFont> Font14_;
+static _H<UIFont> Font18_;
 static _H<UIFont> Font18Bold_;
 static _H<UIFont> Font22Bold_;
 
@@ -720,7 +716,7 @@ static NSString *SerialNumber_ = nil;
 static NSString *ChipID_ = nil;
 static NSString *BBSNum_ = nil;
 static _H<NSString> Token_;
-static NSString *UniqueID_ = nil;
+static _H<NSString> UniqueID_;
 static _H<NSString> UserAgent_;
 static _H<NSString> Product_;
 static _H<NSString> Safari_;
@@ -732,7 +728,6 @@ static CGColorSpaceRef space_;
 static NSDictionary *SectionMap_;
 static NSMutableDictionary *Metadata_;
 static _transient NSMutableDictionary *Settings_;
-static _transient NSString *Role_;
 static _transient NSMutableDictionary *Packages_;
 static _transient NSMutableDictionary *Values_;
 static _transient NSMutableDictionary *Sections_;
@@ -869,6 +864,16 @@ static NSString *CYHex(NSData *data, bool reverse = false) {
 
 @class CYPackageController;
 
+@protocol SourceDelegate
+- (void) setFetch:(NSNumber *)fetch;
+@end
+
+@protocol FetchDelegate
+- (bool) isSourceCancelled;
+- (void) startSourceFetch:(NSString *)uri;
+- (void) stopSourceFetch:(NSString *)uri;
+@end
+
 @protocol CydiaDelegate
 - (void) returnToCydia;
 - (void) saveState;
@@ -880,6 +885,7 @@ static NSString *CYHex(NSData *data, bool reverse = false) {
 - (void) removePackage:(Package *)package;
 - (void) beginUpdate;
 - (BOOL) updating;
+- (bool) requestUpdate;
 - (void) distUpgrade;
 - (void) loadData;
 - (void) updateData;
@@ -887,7 +893,6 @@ static NSString *CYHex(NSData *data, bool reverse = false) {
 - (void) syncData;
 - (void) addSource:(NSDictionary *)source;
 - (void) addTrivialSource:(NSString *)href;
-- (void) showSettings;
 - (UIProgressHUD *) addProgressHUD;
 - (void) removeProgressHUD:(UIProgressHUD *)hud;
 - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item;
@@ -895,29 +900,19 @@ static NSString *CYHex(NSData *data, bool reverse = false) {
 @end
 /* }}} */
 
-/* Status Delegation {{{ */
-class Status :
+/* CancelStatus {{{ */
+class CancelStatus :
     public pkgAcquireStatus
 {
   private:
-    _transient NSObject<ProgressDelegate> *delegate_;
     bool cancelled_;
 
   public:
-    Status() :
-        delegate_(nil),
+    CancelStatus() :
         cancelled_(false)
     {
     }
 
-    void setDelegate(NSObject<ProgressDelegate> *delegate) {
-        delegate_ = delegate;
-    }
-
-    NSObject<ProgressDelegate> *getDelegate() const {
-        return delegate_;
-    }
-
     virtual bool MediaChange(std::string media, std::string drive) {
         return false;
     }
@@ -926,6 +921,39 @@ class Status :
         Done(item);
     }
 
+    virtual bool Pulse_(pkgAcquire *Owner) = 0;
+
+    virtual bool Pulse(pkgAcquire *Owner) {
+        if (pkgAcquireStatus::Pulse(Owner) && Pulse_(Owner))
+            return true;
+        else {
+            cancelled_ = true;
+            return false;
+        }
+    }
+
+    _finline bool WasCancelled() const {
+        return cancelled_;
+    }
+};
+/* }}} */
+/* DelegateStatus {{{ */
+class CydiaStatus :
+    public CancelStatus
+{
+  private:
+    _transient NSObject<ProgressDelegate> *delegate_;
+
+  public:
+    CydiaStatus() :
+        delegate_(nil)
+    {
+    }
+
+    void setDelegate(NSObject<ProgressDelegate> *delegate) {
+        delegate_ = delegate;
+    }
+
     virtual void Fetch(pkgAcquire::ItemDesc &item) {
         NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
         CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithFormat:UCLocalize("DOWNLOADING_"), name] ofType:kCydiaProgressEventTypeStatus forItem:item]);
@@ -953,9 +981,7 @@ class Status :
         [delegate_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES];
     }
 
-    virtual bool Pulse(pkgAcquire *Owner) {
-        bool value = pkgAcquireStatus::Pulse(Owner);
-
+    virtual bool Pulse_(pkgAcquire *Owner) {
         double percent(
             double(CurrentBytes + CurrentItems) /
             double(TotalBytes + TotalItems)
@@ -969,16 +995,7 @@ class Status :
             [NSNumber numberWithDouble:CurrentCPS], @"Speed",
         nil] waitUntilDone:YES];
 
-        if (value && ![delegate_ isProgressCancelled])
-            return true;
-        else {
-            cancelled_ = true;
-            return false;
-        }
-    }
-
-    _finline bool WasCancelled() const {
-        return cancelled_;
+        return ![delegate_ isProgressCancelled];
     }
 
     virtual void Start() {
@@ -1019,7 +1036,7 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
     _transient NSObject<DatabaseDelegate> *delegate_;
     _transient NSObject<ProgressDelegate> *progress_;
 
-    Status status_;
+    CydiaStatus status_;
 
     int cydiafd_;
     int statusfd_;
@@ -1056,7 +1073,7 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
 - (bool) upgrade;
 - (void) update;
 
-- (void) updateWithStatus:(Status &)status;
+- (void) updateWithStatus:(CancelStatus &)status;
 
 - (void) setDelegate:(NSObject<DatabaseDelegate> *)delegate;
 
@@ -1064,11 +1081,61 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
 - (NSObject<ProgressDelegate> *) progressDelegate;
 
 - (Source *) getSource:(pkgCache::PkgFileIterator)file;
+- (void) setFetch:(bool)fetch forURI:(const char *)uri;
+- (void) resetFetch;
 
 - (NSString *) mappedSectionForPointer:(const char *)pointer;
 
 @end
 /* }}} */
+/* SourceStatus {{{ */
+class SourceStatus :
+    public CancelStatus
+{
+  private:
+    _transient NSObject<FetchDelegate> *delegate_;
+    _transient Database *database_;
+
+  public:
+    SourceStatus(NSObject<FetchDelegate> *delegate, Database *database) :
+        delegate_(delegate),
+        database_(database)
+    {
+    }
+
+    void Set(bool fetch, pkgAcquire::ItemDesc &desc) {
+        desc.Owner->ID = 0;
+        [database_ setFetch:fetch forURI:desc.Owner->DescURI().c_str()];
+    }
+
+    virtual void Fetch(pkgAcquire::ItemDesc &desc) {
+        Set(true, desc);
+    }
+
+    virtual void Done(pkgAcquire::ItemDesc &desc) {
+        Set(false, desc);
+    }
+
+    virtual void Fail(pkgAcquire::ItemDesc &desc) {
+        Set(false, desc);
+    }
+
+    virtual bool Pulse_(pkgAcquire *Owner) {
+        for (pkgAcquire::ItemCIterator item = Owner->ItemsBegin(); item != Owner->ItemsEnd(); ++item)
+            if ((*item)->ID != 0);
+            else if ((*item)->Status == pkgAcquire::Item::StatIdle) {
+                (*item)->ID = 1;
+                [database_ setFetch:true forURI:(*item)->DescURI().c_str()];
+            } else (*item)->ID = 0;
+        return ![delegate_ isSourceCancelled];
+    }
+
+    virtual void Stop() {
+        pkgAcquireStatus::Stop();
+        [database_ resetFetch];
+    }
+};
+/* }}} */
 /* ProgressEvent Implementation {{{ */
 @implementation CydiaProgressEvent
 
@@ -1333,6 +1400,10 @@ static void PackageImport(const void *key, const void *value, void *context) {
 
     _H<NSMutableDictionary> record_;
     BOOL trusted_;
+
+    std::set<std::string> fetches_;
+    std::set<std::string> files_;
+    _transient NSObject<SourceDelegate> *delegate_;
 }
 
 - (Source *) initWithMetaIndex:(metaIndex *)index forDatabase:(Database *)database inPool:(apr_pool_t *)pool;
@@ -1362,30 +1433,13 @@ static void PackageImport(const void *key, const void *value, void *context) {
 - (NSString *) defaultIcon;
 - (NSURL *) iconURL;
 
+- (void) setFetch:(bool)fetch forURI:(const char *)uri;
+- (void) resetFetch;
+
 @end
 
 @implementation Source
 
-- (void) _clear {
-    uri_.clear();
-    distribution_.clear();
-    type_.clear();
-
-    base_.clear();
-
-    description_.clear();
-    label_.clear();
-    origin_.clear();
-    depiction_.clear();
-    support_.clear();
-    version_.clear();
-    defaultIcon_.clear();
-
-    record_ = nil;
-    host_ = nil;
-    authority_ = nil;
-}
-
 + (NSString *) webScriptNameForSelector:(SEL)selector {
     if (false);
     else if (selector == @selector(addSection:))
@@ -1436,8 +1490,6 @@ static void PackageImport(const void *key, const void *value, void *context) {
 }
 
 - (void) setMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
-    [self _clear];
-
     trusted_ = index->IsTrusted();
 
     uri_.set(pool, index->GetURI());
@@ -1446,7 +1498,19 @@ static void PackageImport(const void *key, const void *value, void *context) {
 
     debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
     if (dindex != NULL) {
-        base_.set(pool, dindex->MetaIndexURI(""));
+        std::string file(dindex->MetaIndexURI(""));
+        files_.insert(file + "Release.gpg");
+        files_.insert(file + "Release");
+        base_.set(pool, file);
+
+        std::vector<IndexTarget *> *targets(dindex->ComputeIndexTargets());
+        for (std::vector<IndexTarget *>::const_iterator target(targets->begin()); target != targets->end(); ++target) {
+            std::string file((*target)->URI);
+            files_.insert(file);
+            files_.insert(file + ".gz");
+            files_.insert(file + ".bz2");
+            files_.insert(file + "Index");
+        } delete targets;
 
         FileFd fd;
         if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly))
@@ -1682,6 +1746,31 @@ static void PackageImport(const void *key, const void *value, void *context) {
     return defaultIcon_;
 }
 
+- (void) setDelegate:(NSObject<SourceDelegate> *)delegate {
+    delegate_ = delegate;
+}
+
+- (bool) fetch {
+    return !fetches_.empty();
+}
+
+- (void) setFetch:(bool)fetch forURI:(const char *)uri {
+    if (!fetch) {
+        if (fetches_.erase(uri) == 0)
+            return;
+    } else if (files_.find(uri) == files_.end())
+        return;
+    else if (!fetches_.insert(uri).second)
+        return;
+
+    [delegate_ performSelectorOnMainThread:@selector(setFetch:) withObject:[NSNumber numberWithBool:[self fetch]] waitUntilDone:NO];
+}
+
+- (void) resetFetch {
+    fetches_.clear();
+    [delegate_ performSelectorOnMainThread:@selector(setFetch:) withObject:[NSNumber numberWithBool:NO] waitUntilDone:NO];
+}
+
 @end
 /* }}} */
 /* CydiaOperation Class {{{ */
@@ -1850,11 +1939,8 @@ struct ParsedPackage {
 
     CYString depiction_;
     CYString homepage_;
-
-    CYString sponsor_;
     CYString author_;
 
-    CYString bugs_;
     CYString support_;
 };
 
@@ -1956,7 +2042,6 @@ struct ParsedPackage {
 - (uint32_t) rank;
 - (BOOL) matches:(NSArray *)query;
 
-- (bool) hasSupportingRole;
 - (BOOL) hasTag:(NSString *)tag;
 - (NSString *) primaryPurpose;
 - (NSArray *) purposes;
@@ -1974,7 +2059,7 @@ struct ParsedPackage {
 - (bool) isUnfilteredAndSearchedForBy:(NSArray *)query;
 - (bool) isUnfilteredAndSelectedForBy:(NSString *)search;
 - (bool) isInstalledAndUnfiltered:(NSNumber *)number;
-- (bool) isVisibleInSection:(NSString *)section;
+- (bool) isVisibleInSection:(NSString *)section source:(Source *)source;
 - (bool) isVisibleInSource:(Source *)source;
 
 @end
@@ -2124,6 +2209,8 @@ struct PackageNameOrdering :
         return @"clear";
     else if (selector == @selector(getField:))
         return @"getField";
+    else if (selector == @selector(getRecord))
+        return @"getRecord";
     else if (selector == @selector(hasTag:))
         return @"hasTag";
     else if (selector == @selector(install))
@@ -2165,7 +2252,6 @@ struct PackageNameOrdering :
         @"simpleSection",
         @"size",
         @"source",
-        @"sponsor",
         @"state",
         @"support",
         @"tags",
@@ -2209,6 +2295,19 @@ struct PackageNameOrdering :
     return [NSString stringWithString:[(NSString *) CYStringCreate(start, end - start) autorelease]];
 } }
 
+- (NSString *) getRecord {
+@synchronized (database_) {
+    if ([database_ era] != era_ || file_.end())
+        return nil;
+
+    pkgRecords::Parser &parser([database_ records]->Lookup(file_));
+
+    const char *start, *end;
+    parser.GetRec(start, end);
+
+    return [NSString stringWithString:[(NSString *) CYStringCreate(start, end - start) autorelease]];
+} }
+
 - (void) parse {
     if (parsed_ != NULL)
         return;
@@ -2226,6 +2325,7 @@ struct PackageNameOrdering :
             parser = &[database_ records]->Lookup(file_);
         _end
 
+        CYString bugs;
         CYString website;
 
         _profile(Package$parse$Find)
@@ -2238,9 +2338,8 @@ struct PackageNameOrdering :
                 {"depiction", &parsed->depiction_},
                 {"homepage", &parsed->homepage_},
                 {"website", &website},
-                {"bugs", &parsed->bugs_},
+                {"bugs", &bugs},
                 {"support", &parsed->support_},
-                {"sponsor", &parsed->sponsor_},
                 {"author", &parsed->author_},
                 {"md5sum", &parsed->md5sum_},
             };
@@ -2274,6 +2373,8 @@ struct PackageNameOrdering :
                 parsed->homepage_ = website;
             if (parsed->homepage_ == parsed->depiction_)
                 parsed->homepage_.clear();
+            if (parsed->support_.empty())
+                parsed->support_ = bugs;
         _end
     _end
 } }
@@ -2319,9 +2420,14 @@ struct PackageNameOrdering :
             pkgCache::TagIterator tag(iterator.TagList());
             if (!tag.end()) {
                 tags_ = [NSMutableArray arrayWithCapacity:8];
-                do {
+
+                goto tag; for (; !tag.end(); ++tag) tag: {
                     const char *name(tag.Name());
-                    [tags_ addObject:[(NSString *)CYStringCreate(name) autorelease]];
+                    NSString *string((NSString *) CYStringCreate(name));
+                    if (string == nil)
+                        continue;
+
+                    [tags_ addObject:[string autorelease]];
 
                     if (role_ == 0 && strncmp(name, "role::", 6) == 0 /*&& strcmp(name, "role::leaper") != 0*/) {
                         if (strcmp(name + 6, "enduser") == 0)
@@ -2342,9 +2448,7 @@ struct PackageNameOrdering :
                         else if (strcmp(name + 7, "obsolete") == 0)
                             obsolete_ = true;
                     }
-
-                    ++tag;
-                } while (!tag.end());
+                }
             }
         _end
 
@@ -2613,8 +2717,8 @@ struct PackageNameOrdering :
             return false;
     _end
 
-    _profile(Package$unfiltered$hasSupportingRole)
-        if (_unlikely(![self hasSupportingRole]))
+    _profile(Package$unfiltered$role)
+        if (_unlikely(role_ > 3))
             return false;
     _end
 
@@ -2733,16 +2837,12 @@ struct PackageNameOrdering :
     return parsed_ != NULL && !parsed_->depiction_.empty() ? parsed_->depiction_ : [[self source] depictionForPackage:id_];
 }
 
-- (MIMEAddress *) sponsor {
-    return parsed_ == NULL || parsed_->sponsor_.empty() ? nil : [MIMEAddress addressWithString:parsed_->sponsor_];
-}
-
 - (MIMEAddress *) author {
     return parsed_ == NULL || parsed_->author_.empty() ? nil : [MIMEAddress addressWithString:parsed_->author_];
 }
 
 - (NSString *) support {
-    return parsed_ != NULL && !parsed_->bugs_.empty() ? parsed_->bugs_ : [[self source] supportForPackage:id_];
+    return parsed_ != NULL && !parsed_->support_.empty() ? parsed_->support_ : [[self source] supportForPackage:id_];
 }
 
 - (NSArray *) files {
@@ -2878,7 +2978,8 @@ struct PackageNameOrdering :
 
                 NSString *bundle([file stringByDeletingLastPathComponent]);
                 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
-                if (icon == nil || [icon length] == 0)
+                // XXX: maybe this should check if this is really a string, not just for length
+                if (icon == nil || ![icon respondsToSelector:@selector(length)] || [icon length] == 0)
                     icon = @"icon.png";
                 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
 
@@ -2953,24 +3054,6 @@ struct PackageNameOrdering :
     return rank_ != 0;
 }
 
-- (bool) hasSupportingRole {
-    if (role_ == 0)
-        return true;
-    if (role_ == 1)
-        return true;
-    if ([Role_ isEqualToString:@"User"])
-        return false;
-    if (role_ == 2)
-        return true;
-    if ([Role_ isEqualToString:@"Hacker"])
-        return false;
-    if (role_ == 3)
-        return true;
-    if ([Role_ isEqualToString:@"Developer"])
-        return false;
-    _assert(false);
-}
-
 - (NSArray *) tags {
     return tags_;
 }
@@ -3093,13 +3176,16 @@ struct PackageNameOrdering :
     return ![self uninstalled] && (![number boolValue] && role_ != 7 || [self unfiltered]);
 }
 
-- (bool) isVisibleInSection:(NSString *)name {
+- (bool) isVisibleInSection:(NSString *)name source:(Source *)source {
     NSString *section([self section]);
 
     return (
         name == nil ||
         section == nil && [name length] == 0 ||
         [name isEqualToString:section]
+    ) && (
+        source == nil ||
+        [self source] == source
     ) && [self visible];
 }
 
@@ -3841,7 +3927,7 @@ class CydiaLogCleaner :
     [self updateWithStatus:status_];
 }
 
-- (void) updateWithStatus:(Status &)status {
+- (void) updateWithStatus:(CancelStatus &)status {
     NSString *title(UCLocalize("REFRESHING_DATA"));
 
     pkgSourceList list;
@@ -3885,6 +3971,16 @@ class CydiaLogCleaner :
     return i == sourceMap_.end() ? nil : i->second;
 }
 
+- (void) setFetch:(bool)fetch forURI:(const char *)uri {
+    for (Source *source in (id) sourceList_)
+        [source setFetch:fetch forURI:uri];
+}
+
+- (void) resetFetch {
+    for (Source *source in (id) sourceList_)
+        [source resetFetch];
+}
+
 - (NSString *) mappedSectionForPointer:(const char *)section {
     _H<NSString> *mapped;
 
@@ -4050,7 +4146,7 @@ static _H<NSMutableSet> Diversions_;
 }
 
 - (NSString *) device {
-    return [[UIDevice currentDevice] uniqueIdentifier];
+    return UniqueIdentifier();
 }
 
 - (NSString *) firmware {
@@ -4096,7 +4192,7 @@ static _H<NSMutableSet> Diversions_;
 }
 
 - (NSString *) role {
-    return (id) Role_ ?: [NSNull null];
+    return (id) [NSNull null];
 }
 
 - (NSString *) model {
@@ -4130,7 +4226,9 @@ static _H<NSMutableSet> Diversions_;
     else if (selector == @selector(stringWithFormat:arguments:))
         return @"format";
     else if (selector == @selector(getAllSources))
-        return @"getAllSourcs";
+        return @"getAllSources";
+    else if (selector == @selector(getApplicationInfo:value:))
+        return @"getApplicationInfoValue";
     else if (selector == @selector(getKernelNumber:))
         return @"getKernelNumber";
     else if (selector == @selector(getKernelString:))
@@ -4161,6 +4259,8 @@ static _H<NSMutableSet> Diversions_;
         return @"popViewController";
     else if (selector == @selector(refreshSources))
         return @"refreshSources";
+    else if (selector == @selector(registerFrame:))
+        return @"registerFrame";
     else if (selector == @selector(removeButton))
         return @"removeButton";
     else if (selector == @selector(saveConfig))
@@ -4169,8 +4269,6 @@ static _H<NSMutableSet> Diversions_;
         return @"setMetadataValue";
     else if (selector == @selector(setSessionValue::))
         return @"setSessionValue";
-    else if (selector == @selector(setShowPromoted:))
-        return @"setShowPromoted";
     else if (selector == @selector(substitutePackageNames:))
         return @"substitutePackageNames";
     else if (selector == @selector(scrollToBottom:))
@@ -4237,6 +4335,16 @@ static _H<NSMutableSet> Diversions_;
     [CydiaWebViewController performSelectorOnMainThread:@selector(addDiversion:) withObject:[[[Diversion alloc] initWithFrom:from to:to] autorelease] waitUntilDone:NO];
 }
 
+- (NSDictionary *) getApplicationInfo:(NSString *)display value:(NSString *)key {
+    char path[1024];
+    if (SBBundlePathForDisplayIdentifier(SBSSpringBoardServerPort(), [display UTF8String], path) != 0)
+        return (id) [NSNull null];
+    NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:[[NSString stringWithUTF8String:path] stringByAppendingString:@"/Info.plist"]]);
+    if (info == nil)
+        return (id) [NSNull null];
+    return [info objectForKey:key];
+}
+
 - (NSNumber *) getKernelNumber:(NSString *)name {
     const char *string([name UTF8String]);
 
@@ -4286,13 +4394,9 @@ static _H<NSMutableSet> Diversions_;
     return [Values_ allKeys];
 } }
 
-- (void) _setShowPromoted:(NSNumber *)value {
-    [Metadata_ setObject:value forKey:@"ShowPromoted"];
-    Changed_ = true;
-}
-
-- (void) setShowPromoted:(NSNumber *)value {
-    [self performSelectorOnMainThread:@selector(_setShowPromoted:) withObject:value waitUntilDone:NO];
+- (void) registerFrame:(DOMHTMLIFrameElement *)iframe {
+    WebFrame *frame([iframe contentFrame]);
+    [indirect_ registerFrame:frame];
 }
 
 - (id) getMetadataValue:(NSString *)key {
@@ -4436,27 +4540,25 @@ static _H<NSMutableSet> Diversions_;
         /* XXX: this should probably not use du */
         execl("/usr/libexec/cydia/du", "du", "-s", [path UTF8String], NULL);
         exit(1);
-        _assert(false);
-    }
-
-    _assert(close(fds[1]) != -1);
+    } else {
+        _assert(close(fds[1]) != -1);
 
-    if (FILE *du = fdopen(fds[0], "r")) {
-        char line[1024];
-        while (fgets(line, sizeof(line), du) != NULL) {
-            size_t length(strlen(line));
-            while (length != 0 && line[length - 1] == '\n')
-                line[--length] = '\0';
-            if (char *tab = strchr(line, '\t')) {
-                *tab = '\0';
-                value = [NSNumber numberWithUnsignedLong:strtoul(line, NULL, 0)];
+        if (FILE *du = fdopen(fds[0], "r")) {
+            char line[1024];
+            while (fgets(line, sizeof(line), du) != NULL) {
+                size_t length(strlen(line));
+                while (length != 0 && line[length - 1] == '\n')
+                    line[--length] = '\0';
+                if (char *tab = strchr(line, '\t')) {
+                    *tab = '\0';
+                    value = [NSNumber numberWithUnsignedLong:strtoul(line, NULL, 0)];
+                }
             }
-        }
-
-        fclose(du);
-    } else _assert(close(fds[0]));
 
-    ReapZombie(pid);
+            fclose(du);
+        } else
+            _assert(close(fds[0]) != -1);
+    } ReapZombie(pid);
 
     return value;
 }
@@ -5120,6 +5222,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     } return self;
 }
 
+- (id) delegate {
+    return delegate_;
+}
+
 - (void) setDelegate:(id)delegate {
     delegate_ = delegate;
 }
@@ -5267,22 +5373,31 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) reloadSpringBoard {
+    if (kCFCoreFoundationVersionNumber > 700) { // XXX: iOS 6.x
+        system("/bin/launchctl stop com.apple.backboardd");
+        sleep(15);
+        system("/usr/bin/killall backboardd SpringBoard sbreload");
+        return;
+    }
+
     pid_t pid(ExecFork());
     if (pid == 0) {
+        if (setsid() == -1)
+            perror("setsid");
+
         pid_t pid(ExecFork());
         if (pid == 0) {
             execl("/usr/bin/sbreload", "sbreload", NULL);
             perror("sbreload");
+
             exit(0);
-        }
+        } ReapZombie(pid);
 
         exit(0);
-    }
-
-    ReapZombie(pid);
+    } ReapZombie(pid);
 
     sleep(15);
-    system("/usr/bin/killall SpringBoard");
+    system("/usr/bin/killall backboardd SpringBoard sbreload");
 }
 
 - (void) close {
@@ -5523,7 +5638,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (NSString *) accessibilityLabel {
-    return [NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), (id) name_, (id) description_];
+    return name_;
 }
 
 - (void) setPackage:(Package *)package asSummary:(bool)summary {
@@ -5548,14 +5663,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         if (NSString *name = [package name])
             name_ = [NSString stringWithString:name];
 
-        NSString *description(nil);
-
-        if (description == nil && IsWildcat_)
-            description = [package longDescription];
-        if (description == nil)
-            description = [package shortDescription];
-
-        if (description != nil)
+        if (NSString *description = [package shortDescription])
             description_ = [NSString stringWithString:description];
 
         commercial_ = [package isCommercial];
@@ -5590,14 +5698,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         if (NSString *mode = [package mode]) {
             if ([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]) {
                 color = RemovingColor_;
-                //placard = @"removing";
+                placard = @"removing";
             } else {
                 color = InstallingColor_;
-                //placard = @"installing";
+                placard = @"installing";
             }
-
-            // XXX: the removing/installing placards are not @2x
-            placard = nil;
         } else {
             color = [UIColor whiteColor];
 
@@ -5630,8 +5735,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             rect.size.height /= 2;
         }
 
-        rect.origin.x = 18 - rect.size.width / 2;
-        rect.origin.y = 18 - rect.size.height / 2;
+        rect.origin.x = 19 - rect.size.width / 2;
+        rect.origin.y = 19 - rect.size.height / 2;
 
         [icon_ drawInRect:rect];
     }
@@ -5643,13 +5748,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         rect.size.width /= 4;
         rect.size.height /= 4;
 
-        rect.origin.x = 23 - rect.size.width / 2;
-        rect.origin.y = 23 - rect.size.height / 2;
+        rect.origin.x = 25 - rect.size.width / 2;
+        rect.origin.y = 25 - rect.size.height / 2;
 
         [badge_ drawInRect:rect];
     }
 
-    if (highlighted)
+    if (highlighted && kCFCoreFoundationVersionNumber < 800)
         UISetColor(White_);
 
     if (!highlighted)
@@ -5657,7 +5762,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [name_ drawAtPoint:CGPointMake(36, 8) forWidth:(width - (placard_ == nil ? 68 : 94)) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
 
     if (placard_ != nil)
-        [placard_ drawAtPoint:CGPointMake(width - 52, 9)];
+        [placard_ drawAtPoint:CGPointMake(width - 52, 11)];
 }
 
 - (void) drawNormalContentRect:(CGRect)rect {
@@ -5692,7 +5797,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [badge_ drawInRect:rect];
     }
 
-    if (highlighted)
+    if (highlighted && kCFCoreFoundationVersionNumber < 800)
         UISetColor(White_);
 
     if (!highlighted)
@@ -5739,6 +5844,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
     if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) {
         icon_ = [UIImage applicationImageNamed:@"folder.png"];
+        // XXX: this initial frame is wrong, but is fixed later
         switch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(218, 9, 60, 25)] autorelease];
         [switch_ addTarget:self action:@selector(onSwitch:) forEvents:UIControlEventValueChanged];
 
@@ -5803,7 +5909,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super setFrame:frame];
 
     CGRect rect([switch_ frame]);
-    [switch_ setFrame:CGRectMake(frame.size.width - 102, 9, rect.size.width, rect.size.height)];
+    [switch_ setFrame:CGRectMake(frame.size.width - rect.size.width - 9, 9, rect.size.width, rect.size.height)];
 }
 
 - (NSString *) accessibilityLabel {
@@ -5813,24 +5919,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) drawContentRect:(CGRect)rect {
     bool highlighted(highlighted_ && !editing_);
 
-    [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
+    [icon_ drawInRect:CGRectMake(7, 7, 32, 32)];
 
-    if (highlighted)
+    if (highlighted && kCFCoreFoundationVersionNumber < 800)
         UISetColor(White_);
 
     float width(rect.size.width);
     if (editing_)
-        width -= 87;
+        width -= 9 + [switch_ frame].size.width;
 
     if (!highlighted)
         UISetColor(Black_);
-    [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(width - 70) withFont:Font22Bold_ lineBreakMode:UILineBreakModeTailTruncation];
+    [name_ drawAtPoint:CGPointMake(48, 12) forWidth:(width - 58) withFont:Font18_ lineBreakMode:UILineBreakModeTailTruncation];
 
     CGSize size = [count_ sizeWithFont:Font14_];
 
-    UISetColor(White_);
+    UISetColor(Folder_);
     if (count_ != nil)
-        [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
+        [count_ drawAtPoint:CGPointMake(10 + (30 - size.width) / 2, 18) withFont:Font12Bold_];
 }
 
 @end
@@ -6042,20 +6148,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
-// We don't want to allow non-commercial packages to do custom things to the install button,
-// so it must call customButtonClicked with a custom commercial_ == 1 fallthrough.
-- (void) customButtonClicked {
-    if (commercial_)
-        [super customButtonClicked];
-    else
-        [self _customButtonClicked];
-}
-
 - (void) reloadButtonClicked {
-    // Don't reload a commerical package by tapping the loading button,
-    // but if it's not an Install button, we should forward it on.
-    if (![package_ uninstalled])
-        [self _customButtonClicked];
+    if (commercial_ && function_ == nil && [package_ uninstalled])
+        return;
+    [self customButtonClicked];
 }
 
 - (void) applyLoadingTitle {
@@ -6209,8 +6305,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     CGRect kbframe = CGRectMake(round(center.x - bounds.size.width / 2.0), round(center.y - bounds.size.height / 2.0), bounds.size.width, bounds.size.height);
     UIViewController *base = self;
-    while ([base parentViewController] != nil)
-        base = [base parentViewController];
+    while ([base parentOrPresentingViewController] != nil)
+        base = [base parentOrPresentingViewController];
     CGRect viewframe = [[base view] convertRect:[list_ frame] fromView:[list_ superview]];
     CGRect intersection = CGRectIntersection(viewframe, kbframe);
 
@@ -6534,10 +6630,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     SEL filter_;
     IMP imp_;
     _H<NSObject> object_;
+    _H<NSObject> stuff_;
 }
 
 - (void) setObject:(id)object;
+- (void) setStuff:(id)object;
+- (void) setObject:(id)object andStuff:(id)stuff;
+
 - (void) setObject:(id)object forFilter:(SEL)filter;
+- (void) setObject:(id)object andStuff:(id)stuff forFilter:(SEL)filter;
 
 - (SEL) filter;
 - (void) setFilter:(SEL)filter;
@@ -6568,10 +6669,28 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     object_ = object;
 } }
 
+- (void) setStuff:(id)stuff {
+@synchronized (self) {
+    stuff_ = stuff;
+} }
+
+- (void) setObject:(id)object andStuff:(id)stuff {
+@synchronized (self) {
+    object_ = object;
+    stuff_ = stuff;
+} }
+
 - (void) setObject:(id)object forFilter:(SEL)filter {
 @synchronized (self) {
     [self setFilter:filter];
-    [self setObject:object];
+    object_ = object;
+} }
+
+- (void) setObject:(id)object andStuff:(id)stuff forFilter:(SEL)filter {
+@synchronized (self) {
+    [self setFilter:filter];
+    object_ = object;
+    stuff_ = stuff;
 } }
 
 - (NSMutableArray *) _reloadPackages {
@@ -6584,16 +6703,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     IMP imp;
     SEL filter;
     _H<NSObject> object;
+    _H<NSObject> stuff;
 
     @synchronized (self) {
         imp = imp_;
         filter = filter_;
         object = object_;
+        stuff = stuff_;
     }
 
     _profile(PackageTable$reloadData$Filter)
         for (Package *package in packages)
-            if ([package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp))(package, filter, object))
+            if ([package valid] && (*reinterpret_cast<bool (*)(id, SEL, id, id)>(imp))(package, filter, object, stuff))
                 [filtered addObject:package];
     _end
 
@@ -6603,7 +6724,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
     if ((self = [super initWithDatabase:database title:title]) != nil) {
         [self setFilter:filter];
-        [self setObject:object];
+        object_ = object;
+    } return self;
+}
+
+- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object with:(id)stuff {
+    if ((self = [super initWithDatabase:database title:title]) != nil) {
+        [self setFilter:filter];
+        object_ = object;
+        stuff_ = stuff;
     } return self;
 }
 
@@ -6659,7 +6788,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [alert setCancelButtonIndex:0];
 
     [alert setMessage:
-        @"Copyright \u00a9 2008-2011\n"
+        @"Copyright \u00a9 2008-2013\n"
         "SaurikIT, LLC\n"
         "\n"
         "Jay Freeman (saurik)\n"
@@ -6681,335 +6810,79 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 @end
 /* }}} */
-/* Manage Controller {{{ */
-@interface ManageController : CydiaWebViewController {
-}
 
-- (void) queueStatusDidChange;
+/* Cydia Navigation Controller Interface {{{ */
+@interface UINavigationController (Cydia)
+
+- (NSArray *) navigationURLCollection;
+- (void) unloadData;
 
 @end
+/* }}} */
 
-@implementation ManageController
+/* Cydia Tab Bar Controller {{{ */
+@interface CydiaTabBarController : CyteTabBarController <
+    UITabBarControllerDelegate,
+    FetchDelegate
+> {
+    _transient Database *database_;
 
-- (id) init {
-    if ((self = [super init]) != nil) {
-        [self setURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]];
-    } return self;
-}
+    _H<UIActivityIndicatorView> indicator_;
 
-- (NSURL *) navigationURL {
-    return [NSURL URLWithString:@"cydia://manage"];
+    bool updating_;
+    // XXX: ok, "updatedelegate_"?...
+    _transient NSObject<CydiaDelegate> *updatedelegate_;
 }
 
-- (UIBarButtonItem *) leftButton {
-    return [[[UIBarButtonItem alloc]
-        initWithTitle:UCLocalize("SETTINGS")
-        style:UIBarButtonItemStylePlain
-        target:self
-        action:@selector(settingsButtonClicked)
-    ] autorelease];
-}
+- (NSArray *) navigationURLCollection;
+- (void) beginUpdate;
+- (BOOL) updating;
 
-- (void) settingsButtonClicked {
-    [delegate_ showSettings];
-}
+@end
 
-- (void) queueButtonClicked {
-    [delegate_ queue];
-}
+@implementation CydiaTabBarController
 
-- (UIBarButtonItem *) rightButton {
-    return Queuing_ ? [[[UIBarButtonItem alloc]
-        initWithTitle:UCLocalize("QUEUE")
-        style:UIBarButtonItemStyleDone
-        target:self
-        action:@selector(queueButtonClicked)
-    ] autorelease] : nil;
-}
+- (NSArray *) navigationURLCollection {
+    NSMutableArray *items([NSMutableArray array]);
 
-- (void) queueStatusDidChange {
-    [self applyRightButton];
-}
+    // XXX: Should this deal with transient view controllers?
+    for (id navigation in [self viewControllers]) {
+        NSArray *stack = [navigation performSelector:@selector(navigationURLCollection)];
+        if (stack != nil)
+            [items addObject:stack];
+    }
 
-- (bool) isLoading {
-    return !Queuing_ && [super isLoading];
+    return items;
 }
 
-@end
-/* }}} */
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
+        database_ = database;
+        [self setDelegate:self];
+
+        indicator_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteTiny] autorelease];
+        [indicator_ setOrigin:CGPointMake(kCFCoreFoundationVersionNumber >= 800 ? 2 : 4, 2)];
 
-/* Refresh Bar {{{ */
-@interface RefreshBar : UINavigationBar {
-    _H<UIProgressIndicator> indicator_;
-    _H<UITextLabel> prompt_;
-    _H<UIProgressBar> progress_;
-    _H<UINavigationButton> cancel_;
+        [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+    } return self;
 }
 
-@end
+- (void) setUpdate:(NSDate *)date {
+    [self beginUpdate];
+}
 
-@implementation RefreshBar
+- (void) beginUpdate {
+    UIViewController *controller([[self viewControllers] objectAtIndex:1]);
+    UITabBarItem *item([controller tabBarItem]);
 
-- (void) positionViews {
-    CGRect frame = [cancel_ frame];
-    frame.size = [cancel_ sizeThatFits:frame.size];
-    frame.origin.x = [self frame].size.width - frame.size.width - 5;
-    frame.origin.y = ([self frame].size.height - frame.size.height) / 2;
-    [cancel_ setFrame:frame];
+    [item setBadgeValue:@""];
+    UIView *badge(MSHookIvar<UIView *>([item view], "_badge"));
 
-    CGSize prgsize = {75, 100};
-    CGRect prgrect = {{
-        [self frame].size.width - prgsize.width - 10,
-        ([self frame].size.height - prgsize.height) / 2
-    } , prgsize};
-    [progress_ setFrame:prgrect];
+    [indicator_ startAnimating];
+    [badge addSubview:indicator_];
 
-    CGSize indsize([UIProgressIndicator defaultSizeForStyle:[indicator_ activityIndicatorViewStyle]]);
-    unsigned indoffset = ([self frame].size.height - indsize.height) / 2;
-    CGRect indrect = {{indoffset, indoffset}, indsize};
-    [indicator_ setFrame:indrect];
-
-    CGSize prmsize = {215, indsize.height + 4};
-    CGRect prmrect = {{
-        indoffset * 2 + indsize.width,
-        unsigned([self frame].size.height - prmsize.height) / 2 - 1
-    }, prmsize};
-    [prompt_ setFrame:prmrect];
-}
-
-- (void) setFrame:(CGRect)frame {
-    [super setFrame:frame];
-    [self positionViews];
-}
-
-- (id) initWithFrame:(CGRect)frame delegate:(id)delegate {
-    if ((self = [super initWithFrame:frame]) != nil) {
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
-
-        [self setBarStyle:UIBarStyleBlack];
-
-        UIBarStyle barstyle([self _barStyle:NO]);
-        bool ugly(barstyle == UIBarStyleDefault);
-
-        UIProgressIndicatorStyle style = ugly ?
-            UIProgressIndicatorStyleMediumBrown :
-            UIProgressIndicatorStyleMediumWhite;
-
-        indicator_ = [[[UIProgressIndicator alloc] initWithFrame:CGRectZero] autorelease];
-        [(UIProgressIndicator *) indicator_ setStyle:style];
-        [indicator_ startAnimation];
-        [self addSubview:indicator_];
-
-        prompt_ = [[[UITextLabel alloc] initWithFrame:CGRectZero] autorelease];
-        [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
-        [prompt_ setBackgroundColor:[UIColor clearColor]];
-        [prompt_ setFont:[UIFont systemFontOfSize:15]];
-        [self addSubview:prompt_];
-
-        progress_ = [[[UIProgressBar alloc] initWithFrame:CGRectZero] autorelease];
-        [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin];
-        [(UIProgressBar *) progress_ setStyle:0];
-        [self addSubview:progress_];
-
-        cancel_ = [[[UINavigationButton alloc] initWithTitle:UCLocalize("CANCEL") style:UINavigationButtonStyleHighlighted] autorelease];
-        [cancel_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
-        [cancel_ addTarget:delegate action:@selector(cancelPressed) forControlEvents:UIControlEventTouchUpInside];
-        [cancel_ setBarStyle:barstyle];
-
-        [self positionViews];
-    } return self;
-}
-
-- (void) setCancellable:(bool)cancellable {
-    if (cancellable)
-        [self addSubview:cancel_];
-    else
-        [cancel_ removeFromSuperview];
-}
-
-- (void) start {
-    [prompt_ setText:UCLocalize("UPDATING_DATABASE")];
-    [progress_ setProgress:0];
-}
-
-- (void) stop {
-    [self setCancellable:NO];
-}
-
-- (void) setPrompt:(NSString *)prompt {
-    [prompt_ setText:prompt];
-}
-
-- (void) setProgress:(float)progress {
-    [progress_ setProgress:progress];
-}
-
-@end
-/* }}} */
-
-/* Cydia Navigation Controller Interface {{{ */
-@interface UINavigationController (Cydia)
-
-- (NSArray *) navigationURLCollection;
-- (void) unloadData;
-
-@end
-/* }}} */
-
-/* Cydia Tab Bar Controller {{{ */
-@interface CYTabBarController : UITabBarController <
-    UITabBarControllerDelegate,
-    ProgressDelegate
-> {
-    _transient Database *database_;
-    _H<RefreshBar, 1> refreshbar_;
-
-    bool dropped_;
-    bool updating_;
-    // XXX: ok, "updatedelegate_"?...
-    _transient NSObject<CydiaDelegate> *updatedelegate_;
-
-    _H<UIViewController> remembered_;
-    _transient UIViewController *transient_;
-}
-
-- (NSArray *) navigationURLCollection;
-- (void) dropBar:(BOOL)animated;
-- (void) beginUpdate;
-- (void) raiseBar:(BOOL)animated;
-- (BOOL) updating;
-- (void) unloadData;
-
-@end
-
-@implementation CYTabBarController
-
-- (void) didReceiveMemoryWarning {
-    [super didReceiveMemoryWarning];
-
-    // presenting a UINavigationController on 2.x does not update its transitionView
-    // it thereby will not allow its topViewController to be unloaded by memory pressure
-    if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
-        UIViewController *selected([self selectedViewController]);
-        for (UINavigationController *controller in [self viewControllers])
-            if (controller != selected)
-                if (UIViewController *top = [controller topViewController])
-                    [top unloadView];
-    }
-}
-
-- (void) setUnselectedViewController:(UIViewController *)transient {
-    if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
-        if (transient != nil) {
-            [[[self viewControllers] objectAtIndex:0] pushViewController:transient animated:YES];
-            [self setSelectedIndex:0];
-        } return;
-    }
-
-    NSMutableArray *controllers = [[[self viewControllers] mutableCopy] autorelease];
-    if (transient != nil) {
-        UINavigationController *navigation([[[UINavigationController alloc] init] autorelease]);
-        [navigation setViewControllers:[NSArray arrayWithObject:transient]];
-        transient = navigation;
-
-        if (transient_ == nil)
-            remembered_ = [controllers objectAtIndex:0];
-        transient_ = transient;
-        [transient_ setTabBarItem:[remembered_ tabBarItem]];
-        [controllers replaceObjectAtIndex:0 withObject:transient_];
-        [self setSelectedIndex:0];
-        [self setViewControllers:controllers];
-        [self concealTabBarSelection];
-    } else if (remembered_ != nil) {
-        [remembered_ setTabBarItem:[transient_ tabBarItem]];
-        transient_ = transient;
-        [controllers replaceObjectAtIndex:0 withObject:remembered_];
-        remembered_ = nil;
-        [self setViewControllers:controllers];
-        [self revealTabBarSelection];
-    }
-}
-
-- (UIViewController *) unselectedViewController {
-    return transient_;
-}
-
-- (void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
-    if ([self unselectedViewController])
-        [self setUnselectedViewController:nil];
-
-    // presenting a UINavigationController on 2.x does not update its transitionView
-    // if this view was unloaded, the tranitionView may currently be presenting nothing
-    if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
-        UINavigationController *navigation((UINavigationController *) viewController);
-        [navigation pushViewController:[[[UIViewController alloc] init] autorelease] animated:NO];
-        [navigation popViewControllerAnimated:NO];
-    }
-}
-
-- (NSArray *) navigationURLCollection {
-    NSMutableArray *items([NSMutableArray array]);
-
-    // XXX: Should this deal with transient view controllers?
-    for (id navigation in [self viewControllers]) {
-        NSArray *stack = [navigation performSelector:@selector(navigationURLCollection)];
-        if (stack != nil)
-            [items addObject:stack];
-    }
-
-    return items;
-}
-
-- (void) dismissModalViewControllerAnimated:(BOOL)animated {
-    if ([self modalViewController] == nil && [self unselectedViewController] != nil)
-        [self setUnselectedViewController:nil];
-    else
-        [super dismissModalViewControllerAnimated:YES];
-}
-
-- (void) unloadData {
-    [super unloadData];
-
-    for (UINavigationController *controller in [self viewControllers])
-        [controller unloadData];
-
-    if (UIViewController *selected = [self selectedViewController])
-        [selected reloadData];
-
-    if (UIViewController *unselected = [self unselectedViewController]) {
-        [unselected unloadData];
-        [unselected reloadData];
-    }
-}
-
-- (void) dealloc {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-
-    [super dealloc];
-}
-
-- (id) initWithDatabase:(Database *)database {
-    if ((self = [super init]) != nil) {
-        database_ = database;
-        [self setDelegate:self];
-
-        [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
-        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
-
-        refreshbar_ = [[[RefreshBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, [UINavigationBar defaultSize].height) delegate:self] autorelease];
-    } return self;
-}
-
-- (void) setUpdate:(NSDate *)date {
-    [self beginUpdate];
-}
-
-- (void) beginUpdate {
-    [(RefreshBar *) refreshbar_ start];
-    [self dropBar:YES];
-
-    [updatedelegate_ retainNetworkActivityIndicator];
-    updating_ = true;
+    [updatedelegate_ retainNetworkActivityIndicator];
+    updating_ = true;
 
     [NSThread
         detachNewThreadSelector:@selector(performUpdate)
@@ -7021,8 +6894,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 - (void) performUpdate {
     NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
 
-    Status status;
-    status.setDelegate(self);
+    SourceStatus status(self, database_);
     [database_ updateWithStatus:status];
 
     [self
@@ -7038,8 +6910,11 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     updating_ = false;
     [updatedelegate_ releaseNetworkActivityIndicator];
 
-    [self raiseBar:YES];
-    [refreshbar_ stop];
+    UIViewController *controller([[self viewControllers] objectAtIndex:1]);
+    [[controller tabBarItem] setBadgeValue:nil];
+
+    [indicator_ removeFromSuperview];
+    [indicator_ stopAnimating];
 
     [updatedelegate_ performSelector:selector withObject:nil afterDelay:0];
 }
@@ -7062,25 +6937,14 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     return updating_;
 }
 
-- (void) addProgressEvent:(CydiaProgressEvent *)event {
-    [refreshbar_ setPrompt:[event compoundMessage]];
-}
-
-- (bool) isProgressCancelled {
+- (bool) isSourceCancelled {
     return !updating_;
 }
 
-- (void) setProgressCancellable:(NSNumber *)cancellable {
-    [refreshbar_ setCancellable:(updating_ && [cancellable boolValue])];
+- (void) startSourceFetch:(NSString *)uri {
 }
 
-- (void) setProgressPercent:(NSNumber *)percent {
-    [refreshbar_ setProgress:[percent floatValue]];
-}
-
-- (void) setProgressStatus:(NSDictionary *)status {
-    if (status != nil)
-        [self setProgressPercent:[status objectForKey:@"Percent"]];
+- (void) stopSourceFetch:(NSString *)uri {
 }
 
 - (void) setUpdateDelegate:(id)delegate {
@@ -7088,84 +6952,12 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (UIView *) transitionView {
-    if ([self respondsToSelector:@selector(_transitionView)])
-        return [self _transitionView];
-    else
+    if (![self respondsToSelector:@selector(_transitionView)])
         return MSHookIvar<id>(self, "_viewControllerTransitionView");
-}
-
-- (void) dropBar:(BOOL)animated {
-    if (dropped_)
-        return;
-    dropped_ = true;
-
-    UIView *transition([self transitionView]);
-    [[self view] addSubview:refreshbar_];
-
-    CGRect barframe([refreshbar_ frame]);
-
-    if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_0) // XXX: _UIApplicationLinkedOnOrAfter(4)
-        barframe.origin.y = CYStatusBarHeight();
+    else if (kCFCoreFoundationVersionNumber < 800)
+        return [self _transitionView];
     else
-        barframe.origin.y = 0;
-
-    [refreshbar_ setFrame:barframe];
-
-    if (animated)
-        [UIView beginAnimations:nil context:NULL];
-
-    CGRect viewframe = [transition frame];
-    viewframe.origin.y += barframe.size.height;
-    viewframe.size.height -= barframe.size.height;
-    [transition setFrame:viewframe];
-
-    if (animated)
-        [UIView commitAnimations];
-
-    // Ensure bar has the proper width for our view, it might have changed
-    barframe.size.width = viewframe.size.width;
-    [refreshbar_ setFrame:barframe];
-}
-
-- (void) raiseBar:(BOOL)animated {
-    if (!dropped_)
-        return;
-    dropped_ = false;
-
-    UIView *transition([self transitionView]);
-    [refreshbar_ removeFromSuperview];
-
-    CGRect barframe([refreshbar_ frame]);
-
-    if (animated)
-        [UIView beginAnimations:nil context:NULL];
-
-    CGRect viewframe = [transition frame];
-    viewframe.origin.y -= barframe.size.height;
-    viewframe.size.height += barframe.size.height;
-    [transition setFrame:viewframe];
-
-    if (animated)
-        [UIView commitAnimations];
-}
-
-- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
-    bool dropped(dropped_);
-
-    if (dropped)
-        [self raiseBar:NO];
-
-    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
-
-    if (dropped)
-        [self dropBar:NO];
-}
-
-- (void) statusBarFrameChanged:(NSNotification *)notification {
-    if (dropped_) {
-        [self raiseBar:NO];
-        [self dropBar:NO];
-    }
+        return [[[self _transitionView] superview] superview];
 }
 
 @end
@@ -7316,146 +7108,50 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 /* Section Controller {{{ */
 @interface SectionController : FilteredPackageListController {
-    _H<IndirectDelegate, 1> indirect_;
-    _H<CydiaObject> cydia_;
+    _H<NSString> key_;
     _H<NSString> section_;
-    std::vector< _H<CyteWebViewTableViewCell, 1> > promoted_;
 }
 
-- (id) initWithDatabase:(Database *)database section:(NSString *)section;
+- (id) initWithDatabase:(Database *)database source:(Source *)source section:(NSString *)section;
 
 @end
 
 @implementation SectionController
 
 - (NSURL *) referrerURL {
-    NSString *name = section_;
-    if (name == nil)
-        name = @"all";
-
-    return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/sections/%@", UI_, [name stringByAddingPercentEscapesIncludingReserved]]];
+    NSString *name(section_);
+    name = name ?: @"*";
+    NSString *key(key_);
+    key = key ?: @"*";
+    return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/sections/%@/%@", UI_, [key stringByAddingPercentEscapesIncludingReserved], [name stringByAddingPercentEscapesIncludingReserved]]];
 }
 
 - (NSURL *) navigationURL {
-    NSString *name = section_;
-    if (name == nil)
-        name = @"all";
-
-    return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sections/%@", [name stringByAddingPercentEscapesIncludingReserved]]];
+    NSString *name(section_);
+    name = name ?: @"*";
+    NSString *key(key_);
+    key = key ?: @"*";
+    return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sections/%@/%@", [key stringByAddingPercentEscapesIncludingReserved], [name stringByAddingPercentEscapesIncludingReserved]]];
 }
 
-- (id) initWithDatabase:(Database *)database section:(NSString *)name {
+- (id) initWithDatabase:(Database *)database source:(Source *)source section:(NSString *)section {
     NSString *title;
-    if (name == nil)
+    if (section == nil)
         title = UCLocalize("ALL_PACKAGES");
-    else if (![name isEqual:@""])
-        title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"];
+    else if (![section isEqual:@""])
+        title = [[NSBundle mainBundle] localizedStringForKey:Simplify(section) value:nil table:@"Sections"];
     else
         title = UCLocalize("NO_SECTION");
 
-    if ((self = [super initWithDatabase:database title:title filter:@selector(isVisibleInSection:) with:name]) != nil) {
-        indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
-        cydia_ = [[[CydiaObject alloc] initWithDelegate:indirect_] autorelease];
-        section_ = name;
+    if ((self = [super initWithDatabase:database title:title filter:@selector(isVisibleInSection:source:) with:section with:source]) != nil) {
+        key_ = [source key];
+        section_ = section;
     } return self;
 }
 
-- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
-    return [super numberOfSectionsInTableView:list] + 1;
-}
-
-- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
-    return section == 0 ? nil : [super tableView:list titleForHeaderInSection:(section - 1)];
-}
-
-- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
-    return section == 0 ? promoted_.size() : [super tableView:list numberOfRowsInSection:(section - 1)];
-}
-
-+ (NSIndexPath *) adjustedIndexPath:(NSIndexPath *)path {
-    return [NSIndexPath indexPathForRow:[path row] inSection:([path section] - 1)];
-}
-
-- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
-    if ([path section] != 0)
-        return [super tableView:table cellForRowAtIndexPath:[SectionController adjustedIndexPath:path]];
-
-    return promoted_[[path row]];
-}
-
-- (void) tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)path {
-    if ([path section] != 0)
-        return [super tableView:table didSelectRowAtIndexPath:[SectionController adjustedIndexPath:path]];
-}
-
-- (NSInteger) tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
-    NSInteger section([super tableView:tableView sectionForSectionIndexTitle:title atIndex:index]);
-    return section == 0 ? 0 : section + 1;
-}
-
-- (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
-    NSURL *url([request URL]);
-    if (url == nil)
-        return;
-
-    if ([frame isEqualToString:@"_open"])
-        [delegate_ openURL:url];
-    else {
-        WebFrame *frame(nil);
-        if (NSDictionary *WebActionElement = [action objectForKey:@"WebActionElementKey"])
-            frame = [WebActionElement objectForKey:@"WebElementFrame"];
-        if (frame == nil)
-            frame = [view mainFrame];
-
-        WebDataSource *source([frame provisionalDataSource] ?: [frame dataSource]);
-
-        CyteViewController *controller([delegate_ pageForURL:url forExternal:NO withReferrer:([request valueForHTTPHeaderField:@"Referer"] ?: [[[source request] URL] absoluteString])] ?: [[[CydiaWebViewController alloc] initWithRequest:request] autorelease]);
-        [controller setDelegate:delegate_];
-        [[self navigationController] pushViewController:controller animated:YES];
-    }
-
-    [listener ignore];
-}
-
-- (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
-    return [CydiaWebViewController requestWithHeaders:request];
-}
-
-- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    [CydiaWebViewController didClearWindowObject:window forFrame:frame withCydia:cydia_];
-}
-
-- (void) loadView {
-    [super loadView];
-
-    // XXX: this code is horrible. I mean, wtf Jay?
-    if (ShowPromoted_ && [[Metadata_ objectForKey:@"ShowPromoted"] boolValue]) {
-        promoted_.resize(1);
-
-        for (unsigned i(0); i != promoted_.size(); ++i) {
-            CyteWebViewTableViewCell *promoted([CyteWebViewTableViewCell cellWithRequest:[NSURLRequest
-                requestWithURL:[Diversion divertURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/sectionhead/%u/%@",
-                    UI_, i, section_ == nil ? @"" : [section_ stringByAddingPercentEscapesIncludingReserved]]
-                ]]
-
-                cachePolicy:NSURLRequestUseProtocolCachePolicy
-                timeoutInterval:120
-            ]]);
-
-            [promoted setDelegate:self];
-            promoted_[i] = promoted;
-        }
-    }
-}
-
-- (void) setDelegate:(id)delegate {
-    [super setDelegate:delegate];
-    [cydia_ setDelegate:delegate];
-}
-
-- (void) releaseSubviews {
-    promoted_.clear();
-    [super releaseSubviews];
+- (void) reloadData {
+    [super setStuff:[database_ sourceWithKey:key_]];
+    [super reloadData];
 }
 
 @end
@@ -7466,12 +7162,13 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     UITableViewDelegate
 > {
     _transient Database *database_;
+    _H<NSString> key_;
     _H<NSMutableArray> sections_;
     _H<NSMutableArray> filtered_;
     _H<UITableView, 2> list_;
 }
 
-- (id) initWithDatabase:(Database *)database;
+- (id) initWithDatabase:(Database *)database source:(Source *)source;
 - (void) editButtonClicked;
 
 @end
@@ -7479,7 +7176,13 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 @implementation SectionsController
 
 - (NSURL *) navigationURL {
-    return [NSURL URLWithString:@"cydia://sections"];
+    return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sources/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]];
+}
+
+- (Source *) source {
+    if (key_ == nil)
+        return nil;
+    return [database_ sourceWithKey:key_];
 }
 
 - (void) updateNavigationItem {
@@ -7520,7 +7223,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     Section *section = nil;
     int index = [indexPath row];
     if (![self isEditing]) {
-        index -= 1; 
+        index -= 1;
         if (index >= 0)
             section = [filtered_ objectAtIndex:index];
     } else {
@@ -7560,6 +7263,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
     SectionController *controller = [[[SectionController alloc]
         initWithDatabase:database_
+        source:[self source]
         section:[section name]
     ] autorelease];
     [controller setDelegate:delegate_];
@@ -7570,7 +7274,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 - (void) loadView {
     list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
     [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
-    [list_ setRowHeight:45.0f];
+    [list_ setRowHeight:46];
     [(UITableView *) list_ setDataSource:self];
     [list_ setDelegate:self];
     [self setView:list_];
@@ -7591,9 +7295,10 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [super releaseSubviews];
 }
 
-- (id) initWithDatabase:(Database *)database {
+- (id) initWithDatabase:(Database *)database source:(Source *)source {
     if ((self = [super init]) != nil) {
         database_ = database;
+        key_ = [source key];
     } return self;
 }
 
@@ -7607,8 +7312,13 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
     NSMutableDictionary *sections([NSMutableDictionary dictionaryWithCapacity:32]);
 
+    Source *source([self source]);
+
     _trace();
     for (Package *package in packages) {
+        if (source != nil && [package source] != source)
+            continue;
+
         NSString *name([package section]);
         NSString *key(name == nil ? @"" : name);
 
@@ -7663,7 +7373,6 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 /* Changes Controller {{{ */
 @interface ChangesController : CyteViewController <
-    CyteWebViewDelegate,
     UITableViewDataSource,
     UITableViewDelegate
 > {
@@ -7672,10 +7381,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     _H<NSMutableArray> packages_;
     _H<NSMutableArray> sections_;
     _H<UITableView, 2> list_;
-    _H<CyteWebView, 1> dickbar_;
     unsigned upgrades_;
-    _H<IndirectDelegate, 1> indirect_;
-    _H<CydiaObject> cydia_;
 }
 
 - (id) initWithDatabase:(Database *)database;
@@ -7748,22 +7454,30 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         [alert dismissWithClickedButtonIndex:-1 animated:YES];
 }
 
+- (void) setLeftBarButtonItem {
+    if ([delegate_ updating])
+        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("CANCEL")
+            style:UIBarButtonItemStyleDone
+            target:self
+            action:@selector(cancelButtonClicked)
+        ] autorelease] animated:YES];
+    else
+        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("REFRESH")
+            style:UIBarButtonItemStylePlain
+            target:self
+            action:@selector(refreshButtonClicked)
+        ] autorelease] animated:YES];
+}
+
 - (void) refreshButtonClicked {
-    if (IsReachable("cydia.saurik.com")) {
-        [delegate_ beginUpdate];
-        [[self navigationItem] setLeftBarButtonItem:nil animated:YES];
-    } else {
-        UIAlertView *alert = [[[UIAlertView alloc]
-            initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("REFRESH")]
-            message:@"Host Unreachable" // XXX: Localize
-            delegate:self
-            cancelButtonTitle:UCLocalize("OK")
-            otherButtonTitles:nil
-        ] autorelease];
+    if ([delegate_ requestUpdate])
+        [self setLeftBarButtonItem];
+}
 
-        [alert setContext:@"norefresh"];
-        [alert show];
-    }
+- (void) cancelButtonClicked {
+    [delegate_ cancelUpdate];
 }
 
 - (void) upgradeButtonClicked {
@@ -7782,89 +7496,12 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [(UITableView *) list_ setDataSource:self];
     [list_ setDelegate:self];
     [view addSubview:list_];
-
-    if (AprilFools_ && kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
-        CGRect dickframe([view bounds]);
-        dickframe.size.height = 44;
-
-        dickbar_ = [[[CyteWebView alloc] initWithFrame:dickframe] autorelease];
-        [dickbar_ setDelegate:self];
-        [view addSubview:dickbar_];
-
-        [dickbar_ setBackgroundColor:[UIColor clearColor]];
-        [dickbar_ setScalesPageToFit:YES];
-
-        UIWebDocumentView *document([dickbar_ _documentView]);
-        [document setBackgroundColor:[UIColor clearColor]];
-        [document setDrawsBackground:NO];
-
-        WebView *webview([document webView]);
-        [webview setShouldUpdateWhileOffscreen:NO];
-
-        UIScrollView *scroller([dickbar_ scrollView]);
-        [scroller setScrollingEnabled:NO];
-        [scroller setFixedBackgroundPattern:YES];
-        [scroller setBackgroundColor:[UIColor clearColor]];
-
-        WebPreferences *preferences([webview preferences]);
-        [preferences setCacheModel:WebCacheModelDocumentBrowser];
-        [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
-        [preferences setOfflineWebApplicationCacheEnabled:YES];
-
-        [dickbar_ loadRequest:[NSURLRequest
-            requestWithURL:[Diversion divertURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/dickbar/", UI_]]]
-            cachePolicy:NSURLRequestUseProtocolCachePolicy
-            timeoutInterval:120
-        ]];
-
-        UIEdgeInsets inset = {44, 0, 0, 0};
-        [list_ setContentInset:inset];
-
-        [dickbar_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
-    }
-}
-
-- (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
-    NSURL *url([request URL]);
-    if (url == nil)
-        return;
-
-    if ([frame isEqualToString:@"_open"])
-        [delegate_ openURL:url];
-    else {
-        WebFrame *frame(nil);
-        if (NSDictionary *WebActionElement = [action objectForKey:@"WebActionElementKey"])
-            frame = [WebActionElement objectForKey:@"WebElementFrame"];
-        if (frame == nil)
-            frame = [view mainFrame];
-
-        WebDataSource *source([frame provisionalDataSource] ?: [frame dataSource]);
-
-        CyteViewController *controller([delegate_ pageForURL:url forExternal:NO withReferrer:([request valueForHTTPHeaderField:@"Referer"] ?: [[[source request] URL] absoluteString])] ?: [[[CydiaWebViewController alloc] initWithRequest:request] autorelease]);
-        [controller setDelegate:delegate_];
-        [[self navigationController] pushViewController:controller animated:YES];
-    }
-
-    [listener ignore];
-}
-
-- (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
-    return [CydiaWebViewController requestWithHeaders:request];
-}
-
-- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    [CydiaWebViewController didClearWindowObject:window forFrame:frame withCydia:cydia_];
-}
-
-- (void) setDelegate:(id)delegate {
-    [super setDelegate:delegate];
-    [cydia_ setDelegate:delegate];
 }
 
 - (void) viewDidLoad {
     [super viewDidLoad];
 
-    [[self navigationItem] setTitle:(AprilFools_ ? @"Timeline" : UCLocalize("CHANGES"))];
+    [[self navigationItem] setTitle:UCLocalize("CHANGES")];
 }
 
 - (void) releaseSubviews {
@@ -7872,15 +7509,12 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
     packages_ = nil;
     sections_ = nil;
-    dickbar_ = nil;
 
     [super releaseSubviews];
 }
 
 - (id) initWithDatabase:(Database *)database {
     if ((self = [super init]) != nil) {
-        indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
-        cydia_ = [[[CydiaObject alloc] initWithDelegate:indirect_] autorelease];
         database_ = database;
     } return self;
 }
@@ -7908,6 +7542,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 } }
 
 - (void) _reloadData {
+    [self setLeftBarButtonItem];
+
     NSMutableArray *packages;
 
   reload:
@@ -7997,13 +7633,6 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         action:@selector(upgradeButtonClicked)
     ] autorelease]) animated:YES];
 
-    [[self navigationItem] setLeftBarButtonItem:([delegate_ updating] ? nil : [[[UIBarButtonItem alloc]
-        initWithTitle:UCLocalize("REFRESH")
-        style:UIBarButtonItemStylePlain
-        target:self
-        action:@selector(refreshButtonClicked)
-    ] autorelease]) animated:YES];
-
     PrintTimes();
 } }
 
@@ -8234,10 +7863,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         pclose(dpkg);
 
         exit(0);
-        _assert(false);
-    }
-
-    ReapZombie(pid);
+    } ReapZombie(pid);
 }
 
 - (void) onIgnored:(id)control {
@@ -8333,12 +7959,9 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 /* Installed Controller {{{ */
 @interface InstalledController : FilteredPackageListController {
-    BOOL expert_;
 }
 
 - (id) initWithDatabase:(Database *)database;
-
-- (void) updateRoleButton;
 - (void) queueStatusDidChange;
 
 @end
@@ -8355,7 +7978,13 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 - (id) initWithDatabase:(Database *)database {
     if ((self = [super initWithDatabase:database title:UCLocalize("INSTALLED") filter:@selector(isInstalledAndUnfiltered:) with:[NSNumber numberWithBool:YES]]) != nil) {
-        [self updateRoleButton];
+        UISegmentedControl *segmented([[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:UCLocalize("SIMPLE"), UCLocalize("EXPERT"), nil]] autorelease]);
+        [segmented setSelectedSegmentIndex:0];
+        [segmented setSegmentedControlStyle:UISegmentedControlStyleBar];
+        [[self navigationItem] setTitleView:segmented];
+
+        [segmented addTarget:self action:@selector(modeChanged:) forEvents:UIControlEventValueChanged];
+
         [self queueStatusDidChange];
     } return self;
 }
@@ -8368,37 +7997,23 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 - (void) queueStatusDidChange {
 #if !AlwaysReload
-    if (IsWildcat_) {
-        if (Queuing_) {
-            [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
-                initWithTitle:UCLocalize("QUEUE")
-                style:UIBarButtonItemStyleDone
-                target:self
-                action:@selector(queueButtonClicked)
-            ] autorelease]];
-        } else {
-            [[self navigationItem] setLeftBarButtonItem:nil];
-        }
-    }
-#endif
-}
-
-- (void) updateRoleButton {
-    if (Role_ != nil && ![Role_ isEqualToString:@"Developer"])
-        [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
-            initWithTitle:(expert_ ? UCLocalize("EXPERT") : UCLocalize("SIMPLE"))
-            style:(expert_ ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain)
+    if (Queuing_) {
+        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("QUEUE")
+            style:UIBarButtonItemStyleDone
             target:self
-            action:@selector(roleButtonClicked)
+            action:@selector(queueButtonClicked)
         ] autorelease]];
+    } else {
+        [[self navigationItem] setLeftBarButtonItem:nil];
+    }
+#endif
 }
 
-- (void) roleButtonClicked {
-    [self setObject:[NSNumber numberWithBool:expert_]];
+- (void) modeChanged:(UISegmentedControl *)segmented {
+    bool simple([segmented selectedSegmentIndex] == 0);
+    [self setObject:[NSNumber numberWithBool:simple]];
     [self reloadData];
-    expert_ = !expert_;
-
-    [self updateRoleButton];
 }
 
 @end
@@ -8406,15 +8021,19 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 /* Source Cell {{{ */
 @interface SourceCell : CyteTableViewCell <
-    CyteTableViewCellDelegate
+    CyteTableViewCellDelegate,
+    SourceDelegate
 > {
+    _H<Source, 1> source_;
     _H<NSURL> url_;
     _H<UIImage> icon_;
     _H<NSString> origin_;
     _H<NSString> label_;
+    _H<UIActivityIndicatorView> indicator_;
 }
 
 - (void) setSource:(Source *)source;
+- (void) setFetch:(NSNumber *)fetch;
 
 @end
 
@@ -8447,6 +8066,11 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (void) setSource:(Source *)source {
+    source_ = source;
+    [source_ setDelegate:self];
+
+    [self setFetch:[NSNumber numberWithBool:[source_ fetch]]];
+
     icon_ = [UIImage applicationImageNamed:@"unknown.png"];
 
     origin_ = [source name];
@@ -8458,6 +8082,16 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [NSThread detachNewThreadSelector:@selector(_setSource:) toTarget:self withObject:url_];
 }
 
+- (void) setAllSource {
+    source_ = nil;
+    [indicator_ stopAnimating];
+
+    icon_ = [UIImage applicationImageNamed:@"folder.png"];
+    origin_ = UCLocalize("ALL_SOURCES");
+    label_ = UCLocalize("ALL_SOURCES_EX");
+    [content_ setNeedsDisplay];
+}
+
 - (SourceCell *) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
     if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) {
         UIView *content([self contentView]);
@@ -8471,12 +8105,31 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         [content_ setDelegate:self];
         [content_ setOpaque:YES];
 
+        indicator_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGraySmall] autorelease];
+        [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin];// | UIViewAutoresizingFlexibleBottomMargin];
+        [content addSubview:indicator_];
+
         [[content_ layer] setContentsGravity:kCAGravityTopLeft];
     } return self;
 }
 
+- (void) layoutSubviews {
+    [super layoutSubviews];
+
+    UIView *content([self contentView]);
+    CGRect bounds([content bounds]);
+
+    CGRect frame([indicator_ frame]);
+    frame.origin.x = bounds.size.width - frame.size.width;
+    frame.origin.y = (bounds.size.height - frame.size.height) / 2;
+
+    if (kCFCoreFoundationVersionNumber < 800)
+        frame.origin.x -= 8;
+    [indicator_ setFrame:frame];
+}
+
 - (NSString *) accessibilityLabel {
-    return label_;
+    return origin_;
 }
 
 - (void) drawContentRect:(CGRect)rect {
@@ -8490,63 +8143,31 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         while (rect.size.width > 32 || rect.size.height > 32) {
             rect.size.width /= 2;
             rect.size.height /= 2;
-        }
-
-        rect.origin.x = 25 - rect.size.width / 2;
-        rect.origin.y = 25 - rect.size.height / 2;
-
-        [icon_ drawInRect:rect];
-    }
-
-    if (highlighted)
-        UISetColor(White_);
-
-    if (!highlighted)
-        UISetColor(Black_);
-    [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - 65) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
-
-    if (!highlighted)
-        UISetColor(Gray_);
-    [label_ drawAtPoint:CGPointMake(48, 29) forWidth:(width - 65) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation];
-}
-
-@end
-/* }}} */
-/* Source Controller {{{ */
-@interface SourceController : FilteredPackageListController {
-    _transient Source *source_;
-    _H<NSString> key_;
-}
-
-- (id) initWithDatabase:(Database *)database source:(Source *)source;
+        }
 
-@end
+        rect.origin.x = 26 - rect.size.width / 2;
+        rect.origin.y = 26 - rect.size.height / 2;
 
-@implementation SourceController
+        [icon_ drawInRect:rect];
+    }
 
-- (NSURL *) referrerURL {
-    return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/sources/%@", UI_, [key_ stringByAddingPercentEscapesIncludingReserved]]];
-}
+    if (highlighted && kCFCoreFoundationVersionNumber < 800)
+        UISetColor(White_);
 
-- (NSURL *) navigationURL {
-    return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sources/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]];
-}
+    if (!highlighted)
+        UISetColor(Black_);
+    [origin_ drawAtPoint:CGPointMake(52, 8) forWidth:(width - 61) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
 
-- (id) initWithDatabase:(Database *)database source:(Source *)source {
-    if ((self = [super initWithDatabase:database title:[source label] filter:@selector(isVisibleInSource:) with:source]) != nil) {
-        source_ = source;
-        key_ = [source key];
-    } return self;
+    if (!highlighted)
+        UISetColor(Gray_);
+    [label_ drawAtPoint:CGPointMake(52, 29) forWidth:(width - 61) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation];
 }
 
-- (void) reloadData {
-    source_ = [database_ sourceWithKey:key_];
-    key_ = [source_ key];
-    [self setObject:source_];
-
-    [[self navigationItem] setTitle:[source_ label]];
-
-    [super reloadData];
+- (void) setFetch:(NSNumber *)fetch {
+    if ([fetch boolValue])
+        [indicator_ startAnimating];
+    else
+        [indicator_ stopAnimating];
 }
 
 @end
@@ -8567,11 +8188,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     _H<UIProgressHUD> hud_;
     _H<NSError> error_;
 
-    //NSURLConnection *installer_;
-    NSURLConnection *trivial_;
     NSURLConnection *trivial_bz2_;
     NSURLConnection *trivial_gz_;
-    //NSURLConnection *automatic_;
 
     BOOL cydia_;
 }
@@ -8592,11 +8210,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (void) dealloc {
-    //[self _releaseConnection:installer_];
-    [self _releaseConnection:trivial_];
     [self _releaseConnection:trivial_gz_];
     [self _releaseConnection:trivial_bz2_];
-    //[self _releaseConnection:automatic_];
 
     [super dealloc];
 }
@@ -8611,62 +8226,77 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
-    return 1;
+    return 2;
 }
 
 - (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+    if (section == 1)
+        return UCLocalize("INDIVIDUAL_SOURCES");
     return nil;
 }
 
 - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
-    return [sources_ count];
+    switch (section) {
+        case 0: return 1;
+        case 1: return [sources_ count];
+        default: return 0;
+    }
 }
 
 - (Source *) sourceAtIndexPath:(NSIndexPath *)indexPath {
 @synchronized (database_) {
     if ([database_ era] != era_)
         return nil;
-
+    if ([indexPath section] != 1)
+        return nil;
     NSUInteger index([indexPath row]);
-    return index < [sources_ count] ? [sources_ objectAtIndex:index] : nil;
+    if (index >= [sources_ count])
+        return nil;
+    return [sources_ objectAtIndex:index];
 } }
 
 - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     static NSString *cellIdentifier = @"SourceCell";
 
     SourceCell *cell = (SourceCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
-    if(cell == nil) cell = [[[SourceCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellIdentifier] autorelease];
-    [cell setSource:[self sourceAtIndexPath:indexPath]];
+    if (cell == nil) cell = [[[SourceCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellIdentifier] autorelease];
     [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
 
+    Source *source([self sourceAtIndexPath:indexPath]);
+    if (source == nil)
+        [cell setAllSource];
+    else
+        [cell setSource:source];
+
     return cell;
 }
 
 - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
-    Source *source = [self sourceAtIndexPath:indexPath];
-    if (source == nil) return;
-
-    SourceController *controller = [[[SourceController alloc]
+    SectionsController *controller([[[SectionsController alloc]
         initWithDatabase:database_
-        source:source
-    ] autorelease];
+        source:[self sourceAtIndexPath:indexPath]
+    ] autorelease]);
 
     [controller setDelegate:delegate_];
-
     [[self navigationController] pushViewController:controller animated:YES];
 }
 
 - (BOOL) tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
+    if ([indexPath section] != 1)
+        return false;
     Source *source = [self sourceAtIndexPath:indexPath];
     return [source record] != nil;
 }
 
 - (void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
+    _assert([indexPath section] == 1);
     if (editingStyle ==  UITableViewCellEditingStyleDelete) {
         Source *source = [self sourceAtIndexPath:indexPath];
         if (source == nil) return;
 
         [Sources_ removeObjectForKey:[source key]];
+        Changed_ = true;
+
         [delegate_ _saveConfig];
         [delegate_ reloadDataWithInvocation:nil];
     }
@@ -8701,9 +8331,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     // XXX: the memory management in this method is horribly awkward
 
     NSURLConnection **field = NULL;
-    if (connection == trivial_)
-        field = &trivial_;
-    else if (connection == trivial_bz2_)
+    if (connection == trivial_bz2_)
         field = &trivial_bz2_;
     else if (connection == trivial_gz_)
         field = &trivial_gz_;
@@ -8712,7 +8340,6 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     *field = nil;
 
     if (
-        trivial_ == nil &&
         trivial_bz2_ == nil &&
         trivial_gz_ == nil
     ) {
@@ -8827,17 +8454,13 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
             case 1: {
                 NSString *href = [[alert textField] text];
 
-                //installer_ = [[self _requestHRef:href method:@"GET"] retain];
-
                 if (![href hasSuffix:@"/"])
                     href_ = [href stringByAppendingString:@"/"];
                 else
                     href_ = href;
 
-                trivial_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages"] method:@"HEAD"] retain];
                 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
                 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
-                //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
 
                 cydia_ = false;
 
@@ -8874,6 +8497,39 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     }
 }
 
+- (void) updateButtonsForEditingStatusAnimated:(BOOL)animated {
+    BOOL editing([list_ isEditing]);
+
+    if (editing)
+        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("ADD")
+            style:UIBarButtonItemStylePlain
+            target:self
+            action:@selector(addButtonClicked)
+        ] autorelease] animated:animated];
+    else if ([delegate_ updating])
+        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("CANCEL")
+            style:UIBarButtonItemStyleDone
+            target:self
+            action:@selector(cancelButtonClicked)
+        ] autorelease] animated:animated];
+    else
+        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("REFRESH")
+            style:UIBarButtonItemStylePlain
+            target:self
+            action:@selector(refreshButtonClicked)
+        ] autorelease] animated:animated];
+
+    [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
+        initWithTitle:(editing ? UCLocalize("DONE") : UCLocalize("EDIT"))
+        style:(editing ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain)
+        target:self
+        action:@selector(editButtonClicked)
+    ] autorelease] animated:animated];
+}
+
 - (void) loadView {
     list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain] autorelease];
     [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
@@ -8913,6 +8569,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 - (void) reloadData {
     [super reloadData];
+    [self updateButtonsForEditingStatusAnimated:YES];
 
 @synchronized (database_) {
     era_ = [database_ era];
@@ -8964,34 +8621,13 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [self showAddSourcePrompt];
 }
 
-- (void) updateButtonsForEditingStatusAnimated:(BOOL)animated { 
-    BOOL editing([list_ isEditing]);
-
-    [[self navigationItem] setLeftBarButtonItem:(editing ? [[[UIBarButtonItem alloc]
-        initWithTitle:UCLocalize("ADD")
-        style:UIBarButtonItemStylePlain
-        target:self
-        action:@selector(addButtonClicked)
-    ] autorelease] : [[self navigationItem] backBarButtonItem]) animated:animated];
-
-    [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
-        initWithTitle:(editing ? UCLocalize("DONE") : UCLocalize("EDIT"))
-        style:(editing ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain)
-        target:self
-        action:@selector(editButtonClicked)
-    ] autorelease] animated:animated];
-
-    if (IsWildcat_ && !editing)
-        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
-            initWithTitle:UCLocalize("SETTINGS")
-            style:UIBarButtonItemStylePlain
-            target:self
-            action:@selector(settingsButtonClicked)
-        ] autorelease]];
+- (void) refreshButtonClicked {
+    if ([delegate_ requestUpdate])
+        [self updateButtonsForEditingStatusAnimated:YES];
 }
 
-- (void) settingsButtonClicked {
-    [delegate_ showSettings];
+- (void) cancelButtonClicked {
+    [delegate_ cancelUpdate];
 }
 
 - (void) editButtonClicked {
@@ -9002,196 +8638,6 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 @end
 /* }}} */
 
-/* Settings Controller {{{ */
-@interface SettingsController : CyteViewController <
-    UITableViewDataSource,
-    UITableViewDelegate
-> {
-    _transient Database *database_;
-    // XXX: ok, "roledelegate_"?...
-    _transient id roledelegate_;
-    _H<UITableView, 2> table_;
-    _H<UISegmentedControl> segment_;
-    _H<UIView> container_;
-}
-
-- (void) showDoneButton;
-- (void) resizeSegmentedControl;
-
-@end
-
-@implementation SettingsController
-
-- (void) loadView {
-    table_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStyleGrouped] autorelease];
-    [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
-    [table_ setDelegate:self];
-    [(UITableView *) table_ setDataSource:self];
-    [self setView:table_];
-
-    NSArray *items = [NSArray arrayWithObjects:
-        UCLocalize("USER"),
-        UCLocalize("HACKER"),
-        UCLocalize("DEVELOPER"),
-    nil];
-    segment_ = [[[UISegmentedControl alloc] initWithItems:items] autorelease];
-    [segment_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin)];
-    container_ = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, 44.0f)] autorelease];
-    [container_ addSubview:segment_];
-}
-
-- (void) viewDidLoad {
-    [super viewDidLoad];
-
-    [[self navigationItem] setTitle:UCLocalize("WHO_ARE_YOU")];
-
-    int index = -1;
-    if ([Role_ isEqualToString:@"User"]) index = 0;
-    if ([Role_ isEqualToString:@"Hacker"]) index = 1;
-    if ([Role_ isEqualToString:@"Developer"]) index = 2;
-    if (index != -1) {
-        [segment_ setSelectedSegmentIndex:index];
-        [self showDoneButton];
-    }
-
-    [segment_ addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
-    [self resizeSegmentedControl];
-}
-
-- (void) releaseSubviews {
-    table_ = nil;
-    segment_ = nil;
-    container_ = nil;
-
-    [super releaseSubviews];
-}
-
-- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
-    if ((self = [super init]) != nil) {
-        database_ = database;
-        roledelegate_ = delegate;
-    } return self;
-}
-
-- (void) resizeSegmentedControl {
-    CGFloat width = [[self view] frame].size.width;
-    [segment_ setFrame:CGRectMake(width / 32.0f, 0, width - (width / 32.0f * 2.0f), 44.0f)];
-}
-
-- (void) viewWillAppear:(BOOL)animated {
-    [super viewWillAppear:animated];
-
-    [self resizeSegmentedControl];
-}
-
-- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
-    [self resizeSegmentedControl];
-}
-
-- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
-    [self resizeSegmentedControl];
-}
-
-- (void) save {
-    NSString *role(nil);
-
-    switch ([segment_ selectedSegmentIndex]) {
-        case 0: role = @"User"; break;
-        case 1: role = @"Hacker"; break;
-        case 2: role = @"Developer"; break;
-
-        _nodefault
-    }
-
-    if (![role isEqualToString:Role_]) {
-        bool rolling(Role_ == nil);
-        Role_ = role;
-
-        Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-            Role_, @"Role",
-        nil];
-
-        [Metadata_ setObject:Settings_ forKey:@"Settings"];
-        Changed_ = true;
-
-        if (rolling)
-            [roledelegate_ loadData];
-        else
-            [roledelegate_ updateData];
-    }
-}
-
-- (void) segmentChanged:(UISegmentedControl *)control {
-    [self showDoneButton];
-}
-
-- (void) saveAndClose {
-    [self save];
-
-    [[self navigationItem] setRightBarButtonItem:nil];
-    [[self navigationController] dismissModalViewControllerAnimated:YES];
-}
-
-- (void) doneButtonClicked {
-    UIActivityIndicatorView *spinner = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20.0f, 20.0f)] autorelease];
-    [spinner startAnimating];
-    UIBarButtonItem *spinItem = [[[UIBarButtonItem alloc] initWithCustomView:spinner] autorelease];
-    [[self navigationItem] setRightBarButtonItem:spinItem];
-
-    [self performSelector:@selector(saveAndClose) withObject:nil afterDelay:0];
-}
-
-- (void) showDoneButton {
-    [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
-        initWithTitle:UCLocalize("DONE")
-        style:UIBarButtonItemStyleDone
-        target:self
-        action:@selector(doneButtonClicked)
-    ] autorelease] animated:([[self navigationItem] rightBarButtonItem] == nil)];
-}
-
-- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
-    // XXX: For not having a single cell in the table, this sure is a lot of sections.
-    return 6;
-}
-
-- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
-    return 0; // :(
-}
-
-- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
-    return nil; // This method is required by the protocol.
-}
-
-- (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
-    if (section == 1)
-        return UCLocalize("ROLE_EX");
-    if (section == 4)
-        return [NSString stringWithFormat:
-            @"%@: %@\n%@: %@\n%@: %@",
-            UCLocalize("USER"), UCLocalize("USER_EX"),
-            UCLocalize("HACKER"), UCLocalize("HACKER_EX"),
-            UCLocalize("DEVELOPER"), UCLocalize("DEVELOPER_EX")
-        ];
-    else return nil;
-}
-
-- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
-    return section == 3 ? 44.0f : 0;
-}
-
-- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
-    return section == 3 ? container_ : nil;
-}
-
-- (void) reloadData {
-    [super reloadData];
-
-    [table_ reloadData];
-}
-
-@end
-/* }}} */
 /* Stash Controller {{{ */
 @interface StashController : CyteViewController {
     _H<UIActivityIndicatorView> spinner_;
@@ -9316,7 +8762,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     UITabBarControllerDelegate
 > {
     _H<UIWindow> window_;
-    _H<CYTabBarController> tabbar_;
+    _H<CydiaTabBarController> tabbar_;
     _H<CydiaLoadingViewController> emulated_;
 
     _H<NSMutableArray> essential_;
@@ -9340,10 +8786,52 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 @implementation Cydia
 
+- (void) lockSuspend {
+    if (locked_++ == 0) {
+        if ($SBSSetInterceptsMenuButtonForever != NULL)
+            (*$SBSSetInterceptsMenuButtonForever)(true);
+
+        [self setIdleTimerDisabled:YES];
+    }
+}
+
+- (void) unlockSuspend {
+    if (--locked_ == 0) {
+        [self setIdleTimerDisabled:NO];
+
+        if ($SBSSetInterceptsMenuButtonForever != NULL)
+            (*$SBSSetInterceptsMenuButtonForever)(false);
+    }
+}
+
 - (void) beginUpdate {
     [tabbar_ beginUpdate];
 }
 
+- (void) cancelUpdate {
+    [tabbar_ cancelUpdate];
+}
+
+- (bool) requestUpdate {
+    if (IsReachable("cydia.saurik.com")) {
+        [self beginUpdate];
+        return true;
+    } else {
+        UIAlertView *alert = [[[UIAlertView alloc]
+            initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("REFRESH")]
+            message:@"Host Unreachable" // XXX: Localize
+            delegate:self
+            cancelButtonTitle:UCLocalize("OK")
+            otherButtonTitles:nil
+        ] autorelease];
+
+        [alert setContext:@"norefresh"];
+        [alert show];
+
+        return false;
+    }
+}
+
 - (BOOL) updating {
     return [tabbar_ updating];
 }
@@ -9356,7 +8844,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
             initWithTitle:(count == 1 ? UCLocalize("HALFINSTALLED_PACKAGE") : [NSString stringWithFormat:UCLocalize("HALFINSTALLED_PACKAGES"), count])
             message:UCLocalize("HALFINSTALLED_PACKAGE_EX")
             delegate:self
-            cancelButtonTitle:UCLocalize("FORCIBLY_CLEAR")
+            cancelButtonTitle:[NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("FORCIBLY_CLEAR"), UCLocalize("UNSAFE")]
             otherButtonTitles:
                 UCLocalize("TEMPORARY_IGNORE"),
             nil
@@ -9452,7 +8940,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     //  - We already refreshed recently.
     //  - We already auto-refreshed this launch.
     //  - Auto-refresh is disabled.
-    if (recently || loaded_ || ManualRefresh) {
+    //  - Cydia's server is not reachable
+    if (recently || loaded_ || ManualRefresh || !IsReachable("cydia.saurik.com")) {
         // If we are cancelling, we need to make sure it knows it's already loaded.
         loaded_ = true;
 
@@ -9461,9 +8950,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         // We are going to load, so remember that.
         loaded_ = true;
 
-        // If we can reach the server, auto-refresh!
-        if (IsReachable("cydia.saurik.com"))
-            [tabbar_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO];
+        [tabbar_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO];
     }
 
     [pool release];
@@ -9476,7 +8963,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 - (void) reloadDataWithInvocation:(NSInvocation *)invocation {
 @synchronized (self) {
     UIProgressHUD *hud(loaded_ ? [self addProgressHUD] : nil);
-    [hud setText:UCLocalize("RELOADING_DATA")];
+    if (hud != nil)
+        [hud setText:UCLocalize("RELOADING_DATA")];
 
     [database_ yieldToSelector:@selector(reloadDataWithInvocation:) withObject:invocation];
 
@@ -9536,6 +9024,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         return;
 
     [window_ addSubview:[tabbar_ view]];
+    if ([window_ respondsToSelector:@selector(setRootViewController:)])
+        [window_ setRootViewController:tabbar_];
     [[emulated_ view] removeFromSuperview];
     emulated_ = nil;
     [window_ setUserInteractionEnabled:YES];
@@ -9710,13 +9200,9 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 - (void) confirmWithNavigationController:(UINavigationController *)navigation {
     Queuing_ = false;
-    ++locked_;
+    [self lockSuspend];
     [self detachNewProgressSelector:@selector(perform_) toTarget:self forController:navigation title:@"RUNNING"];
-    --locked_;
-}
-
-- (void) showSettings {
-    [self presentModalViewController:[[[SettingsController alloc] initWithDatabase:database_ delegate:self] autorelease] force:NO];
+    [self unlockSuspend];
 }
 
 - (void) retainNetworkActivityIndicator {
@@ -9877,12 +9363,12 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
     [hud showInView:[target view]];
 
-    ++locked_;
+    [self lockSuspend];
     return hud;
 }
 
 - (void) removeProgressHUD:(UIProgressHUD *)hud {
-    --locked_;
+    [self unlockSuspend];
     [hud hide];
     [hud removeFromSuperview];
     [window_ setUserInteractionEnabled:YES];
@@ -9918,14 +9404,6 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         NSString *destination = [[url absoluteString] substringFromIndex:([scheme length] + [@"://" length] + [base length] + [@"/" length])];
         controller = [[[CydiaWebViewController alloc] initWithURL:[NSURL URLWithString:destination]] autorelease];
     } else if (!external && [components count] == 1) {
-        if ([base isEqualToString:@"manage"]) {
-            controller = [[[ManageController alloc] init] autorelease];
-        }
-
-        if ([base isEqualToString:@"storage"]) {
-            controller = [[[CydiaWebViewController alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/storage/", UI_]]] autorelease];
-        }
-
         if ([base isEqualToString:@"sources"]) {
             controller = [[[SourcesController alloc] initWithDatabase:database_] autorelease];
         }
@@ -9935,7 +9413,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         }
 
         if ([base isEqualToString:@"sections"]) {
-            controller = [[[SectionsController alloc] initWithDatabase:database_] autorelease];
+            controller = [[[SectionsController alloc] initWithDatabase:database_ source:nil] autorelease];
         }
 
         if ([base isEqualToString:@"search"]) {
@@ -9950,20 +9428,20 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
             controller = [[[InstalledController alloc] initWithDatabase:database_] autorelease];
         }
     } else if ([components count] == 2) {
-        NSString *argument = [components objectAtIndex:1];
+        NSString *argument = [[components objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 
         if ([base isEqualToString:@"package"]) {
             controller = [self pageForPackage:argument withReferrer:referrer];
         }
 
         if (!external && [base isEqualToString:@"search"]) {
-            controller = [[[SearchController alloc] initWithDatabase:database_ query:[argument stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] autorelease];
+            controller = [[[SearchController alloc] initWithDatabase:database_ query:argument] autorelease];
         }
 
         if (!external && [base isEqualToString:@"sections"]) {
-            if ([argument isEqualToString:@"all"])
+            if ([argument isEqualToString:@"all"] || [argument isEqualToString:@"*"])
                 argument = nil;
-            controller = [[[SectionController alloc] initWithDatabase:database_ section:[argument stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] autorelease];
+            controller = [[[SectionController alloc] initWithDatabase:database_ source:nil section:argument] autorelease];
         }
 
         if (!external && [base isEqualToString:@"sources"]) {
@@ -9971,8 +9449,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
                 controller = [[[SourcesController alloc] initWithDatabase:database_] autorelease];
                 [(SourcesController *)controller showAddSourcePrompt];
             } else {
-                Source *source = [database_ sourceWithKey:[argument stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
-                controller = [[[SourceController alloc] initWithDatabase:database_ source:source] autorelease];
+                Source *source([database_ sourceWithKey:argument]);
+                controller = [[[SectionsController alloc] initWithDatabase:database_ source:source] autorelease];
             }
         }
 
@@ -9981,8 +9459,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
             return nil;
         }
     } else if (!external && [components count] == 3) {
-        NSString *arg1 = [components objectAtIndex:1];
-        NSString *arg2 = [components objectAtIndex:2];
+        NSString *arg1 = [[components objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+        NSString *arg2 = [[components objectAtIndex:2] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 
         if ([base isEqualToString:@"package"]) {
             if ([arg2 isEqualToString:@"settings"]) {
@@ -9994,6 +9472,12 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
                 }
             }
         }
+
+        if ([base isEqualToString:@"sections"]) {
+            Source *source([arg1 isEqualToString:@"*"] ? nil : [database_ sourceWithKey:arg1]);
+            NSString *section([arg2 isEqualToString:@"*"] ? nil : arg2);
+            controller = [[[SectionController alloc] initWithDatabase:database_ source:source section:section] autorelease];
+        }
     }
 
     [controller setDelegate:self];
@@ -10068,7 +9552,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (void) addStashController {
-    ++locked_;
+    [self lockSuspend];
     stash_ = [[[StashController alloc] init] autorelease];
     [window_ addSubview:[stash_ view]];
 }
@@ -10076,12 +9560,10 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 - (void) removeStashController {
     [[stash_ view] removeFromSuperview];
     stash_ = nil;
-    --locked_;
+    [self unlockSuspend];
 }
 
 - (void) stash {
-    [self setIdleTimerDisabled:YES];
-
     [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
     UpdateExternalStatus(1);
     [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"];
@@ -10093,27 +9575,31 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     if (pid == 0) {
         execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
         perror("launchctl stop");
-        exit(0);
-    }
 
-    ReapZombie(pid);
+        exit(0);
+    } ReapZombie(pid);
 }
 
 - (void) setupViewControllers {
-    tabbar_ = [[[CYTabBarController alloc] initWithDatabase:database_] autorelease];
-
-    NSMutableArray *items([NSMutableArray arrayWithObjects:
-        [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:0] autorelease],
-        [[[UITabBarItem alloc] initWithTitle:UCLocalize("SECTIONS") image:[UIImage applicationImageNamed:@"install.png"] tag:0] autorelease],
-        [[[UITabBarItem alloc] initWithTitle:(AprilFools_ ? @"Timeline" : UCLocalize("CHANGES")) image:[UIImage applicationImageNamed:@"changes.png"] tag:0] autorelease],
-        [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:0] autorelease],
-    nil]);
-
-    if (IsWildcat_) {
-        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"source.png"] tag:0] autorelease] atIndex:3];
-        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:0] autorelease] atIndex:3];
+    tabbar_ = [[[CydiaTabBarController alloc] initWithDatabase:database_] autorelease];
+
+    NSMutableArray *items;
+    if (kCFCoreFoundationVersionNumber < 800) {
+        items = [NSMutableArray arrayWithObjects:
+            [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:0] autorelease],
+            [[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"install.png"] tag:0] autorelease],
+            [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes.png"] tag:0] autorelease],
+            [[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:0] autorelease],
+            [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:0] autorelease],
+        nil];
     } else {
-        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("MANAGE") image:[UIImage applicationImageNamed:@"manage.png"] tag:0] autorelease] atIndex:3];
+        items = [NSMutableArray arrayWithObjects:
+            [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home7.png"] selectedImage:[UIImage applicationImageNamed:@"home7s.png"]] autorelease],
+            [[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"install7.png"] selectedImage:[UIImage applicationImageNamed:@"install7s.png"]] autorelease],
+            [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes7.png"] selectedImage:[UIImage applicationImageNamed:@"changes7s.png"]] autorelease],
+            [[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage7.png"] selectedImage:[UIImage applicationImageNamed:@"manage7s.png"]] autorelease],
+            [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search7.png"] selectedImage:[UIImage applicationImageNamed:@"search7s.png"]] autorelease],
+        nil];
     }
 
     NSMutableArray *controllers([NSMutableArray array]);
@@ -10161,7 +9647,7 @@ _trace();
     [NSURLCache setSharedURLCache:[[[CYURLCache alloc]
         initWithMemoryCapacity:524288
         diskCapacity:10485760
-        diskPath:[NSString stringWithFormat:@"%@/Library/Caches/com.saurik.Cydia/SDURLCache", @"/var/root"]
+        diskPath:[NSString stringWithFormat:@"%@/SDURLCache", Cache_]
     ] autorelease]];
 
     [CydiaWebViewController _initialize];
@@ -10174,6 +9660,7 @@ _trace();
     Font12_ = [UIFont systemFontOfSize:12];
     Font12Bold_ = [UIFont boldSystemFontOfSize:12];
     Font14_ = [UIFont systemFontOfSize:14];
+    Font18_ = [UIFont systemFontOfSize:18];
     Font18Bold_ = [UIFont boldSystemFontOfSize:18];
     Font22Bold_ = [UIFont boldSystemFontOfSize:22];
 
@@ -10219,7 +9706,6 @@ _trace();
     //Stash_("/usr/bin");
     Stash_("/usr/include");
     Stash_("/usr/lib/pam");
-    Stash_("/usr/libexec");
     Stash_("/usr/share");
     //Stash_("/var/lib");
 
@@ -10231,6 +9717,8 @@ _trace();
 
     emulated_ = [[[CydiaLoadingViewController alloc] init] autorelease];
     [window_ addSubview:[emulated_ view]];
+    if ([window_ respondsToSelector:@selector(setRootViewController:)])
+        [window_ setRootViewController:emulated_];
 
     [self performSelector:@selector(loadData) withObject:nil afterDelay:0];
 _trace();
@@ -10239,29 +9727,18 @@ _trace();
 - (NSArray *) defaultStartPages {
     NSMutableArray *standard = [NSMutableArray array];
     [standard addObject:[NSArray arrayWithObject:@"cydia://home"]];
-    [standard addObject:[NSArray arrayWithObject:@"cydia://sections"]];
+    [standard addObject:[NSArray arrayWithObject:@"cydia://sources"]];
     [standard addObject:[NSArray arrayWithObject:@"cydia://changes"]];
-    if (!IsWildcat_) {
-        [standard addObject:[NSArray arrayWithObject:@"cydia://manage"]];
-    } else {
-        [standard addObject:[NSArray arrayWithObject:@"cydia://installed"]];
-        [standard addObject:[NSArray arrayWithObject:@"cydia://sources"]];
-    }
+    [standard addObject:[NSArray arrayWithObject:@"cydia://installed"]];
     [standard addObject:[NSArray arrayWithObject:@"cydia://search"]];
     return standard;
 }
 
 - (void) loadData {
 _trace();
-    if (Role_ == nil) {
-        [window_ setUserInteractionEnabled:YES];
-        [self showSettings];
-        return;
-    } else {
-        if ([emulated_ modalViewController] != nil)
-            [emulated_ dismissModalViewControllerAnimated:YES];
-        [window_ setUserInteractionEnabled:NO];
-    }
+    if ([emulated_ modalViewController] != nil)
+        [emulated_ dismissModalViewControllerAnimated:YES];
+    [window_ setUserInteractionEnabled:NO];
 
     [self reloadDataWithInvocation:nil];
     [self refreshIfPossible];
@@ -10374,14 +9851,6 @@ id Dealloc_(id self, SEL selector) {
     return object;
 }*/
 
-Class $WebDefaultUIKitDelegate;
-
-MSHook(void, UIWebDocumentView$_setUIKitDelegate$, UIWebDocumentView *self, SEL _cmd, id delegate) {
-    if (delegate == nil && $WebDefaultUIKitDelegate != nil)
-        delegate = [$WebDefaultUIKitDelegate sharedUIKitDelegate];
-    return _UIWebDocumentView$_setUIKitDelegate$(self, _cmd, delegate);
-}
-
 static NSSet *MobilizedFiles_;
 
 static NSURL *MobilizeURL(NSURL *url) {
@@ -10466,6 +9935,14 @@ static CGSize $WAKWindow$screenSize(WAKWindow *self, SEL _cmd) {
     return size;
 }
 
+Class $NSUserDefaults;
+
+MSHook(id, NSUserDefaults$objectForKey$, NSUserDefaults *self, SEL _cmd, NSString *key) {
+    if ([key respondsToSelector:@selector(isEqualToString:)] && [key isEqualToString:@"WebKitLocalStorageDatabasePathPreferenceKey"])
+        return [NSString stringWithFormat:@"%@/LocalStorage", Cache_];
+    return _NSUserDefaults$objectForKey$(self, _cmd, key);
+}
+
 int main(int argc, char *argv[]) {
     NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
 
@@ -10473,12 +9950,6 @@ int main(int argc, char *argv[]) {
 
     UpdateExternalStatus(0);
 
-    if (Class $UIDevice = objc_getClass("UIDevice")) {
-        UIDevice *device([$UIDevice currentDevice]);
-        IsWildcat_ = [device respondsToSelector:@selector(isWildcat)] && [device isWildcat];
-    } else
-        IsWildcat_ = false;
-
     UIScreen *screen([UIScreen mainScreen]);
     if ([screen respondsToSelector:@selector(scale)])
         ScreenScale_ = [screen scale];
@@ -10486,18 +9957,14 @@ int main(int argc, char *argv[]) {
         ScreenScale_ = 1;
 
     UIDevice *device([UIDevice currentDevice]);
-    if (![device respondsToSelector:@selector(userInterfaceIdiom)])
-        Idiom_ = @"iphone";
-    else {
+    if ([device respondsToSelector:@selector(userInterfaceIdiom)]) {
         UIUserInterfaceIdiom idiom([device userInterfaceIdiom]);
-        if (idiom == UIUserInterfaceIdiomPhone)
-            Idiom_ = @"iphone";
-        else if (idiom == UIUserInterfaceIdiomPad)
-            Idiom_ = @"ipad";
-        else
-            NSLog(@"unknown UIUserInterfaceIdiom!");
+        if (idiom == UIUserInterfaceIdiomPad)
+            IsWildcat_ = true;
     }
 
+    Idiom_ = IsWildcat_ ? @"ipad" : @"iphone";
+
     Pcre pattern("^([0-9]+\\.[0-9]+)");
 
     if (pattern([device systemVersion]))
@@ -10551,19 +10018,19 @@ int main(int argc, char *argv[]) {
         method_setImplementation(CFXPreferencesPropertyListSource$createPlistFromDisk, reinterpret_cast<IMP>(&$CFXPreferencesPropertyListSource$createPlistFromDisk));
     }
 
-    $WebDefaultUIKitDelegate = objc_getClass("WebDefaultUIKitDelegate");
-    Method UIWebDocumentView$_setUIKitDelegate$(class_getInstanceMethod([WebView class], @selector(_setUIKitDelegate:)));
-    if (UIWebDocumentView$_setUIKitDelegate$ != NULL) {
-        _UIWebDocumentView$_setUIKitDelegate$ = reinterpret_cast<void (*)(UIWebDocumentView *, SEL, id)>(method_getImplementation(UIWebDocumentView$_setUIKitDelegate$));
-        method_setImplementation(UIWebDocumentView$_setUIKitDelegate$, reinterpret_cast<IMP>(&$UIWebDocumentView$_setUIKitDelegate$));
-    }
-
     $NSURLConnection = objc_getClass("NSURLConnection");
     Method NSURLConnection$init$(class_getInstanceMethod($NSURLConnection, @selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)));
     if (NSURLConnection$init$ != NULL) {
         _NSURLConnection$init$ = reinterpret_cast<id (*)(NSURLConnection *, SEL, NSURLRequest *, id, BOOL, int64_t, BOOL, NSDictionary *)>(method_getImplementation(NSURLConnection$init$));
         method_setImplementation(NSURLConnection$init$, reinterpret_cast<IMP>(&$NSURLConnection$init$));
     }
+
+    $NSUserDefaults = objc_getClass("NSUserDefaults");
+    Method NSUserDefaults$objectForKey$(class_getInstanceMethod($NSUserDefaults, @selector(objectForKey:)));
+    if (NSUserDefaults$objectForKey$ != NULL) {
+        _NSUserDefaults$objectForKey$ = reinterpret_cast<id (*)(NSUserDefaults *, SEL, NSString *)>(method_getImplementation(NSUserDefaults$objectForKey$));
+        method_setImplementation(NSUserDefaults$objectForKey$, reinterpret_cast<IMP>(&$NSUserDefaults$objectForKey$));
+    }
     /* }}} */
     /* Set Locale {{{ */
     Locale_ = CFLocaleCopyCurrent();
@@ -10626,6 +10093,11 @@ int main(int argc, char *argv[]) {
     setuid(0);
     setgid(0);
 
+    if (access("/var/mobile/Library/Keyboard/UserDictionary.sqlite", F_OK) == 0)
+        system("mkdir -p /var/root/Library/Keyboard; cp -af /var/mobile/Library/Keyboard/UserDictionary.sqlite /var/root/Library/Keyboard/");
+
+    Cache_ = [[NSString stringWithFormat:@"%@/Library/Caches/com.saurik.Cydia", @"/var/root"] retain];
+
     /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
     alloc_ = alloc->method_imp;
     alloc->method_imp = (IMP) &Alloc_;*/
@@ -10634,6 +10106,9 @@ int main(int argc, char *argv[]) {
     dealloc_ = dealloc->method_imp;
     dealloc->method_imp = (IMP) &Dealloc_;*/
 
+    void *gestalt(dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_GLOBAL | RTLD_LAZY));
+    $MGCopyAnswer = reinterpret_cast<CFStringRef (*)(CFStringRef)>(dlsym(gestalt, "MGCopyAnswer"));
+
     /* System Information {{{ */
     size_t size;
 
@@ -10665,7 +10140,7 @@ int main(int argc, char *argv[]) {
     ChipID_ = [CYHex((NSData *) CYIOGetValue("IODeviceTree:/chosen", @"unique-chip-id"), true) uppercaseString];
     BBSNum_ = CYHex((NSData *) CYIOGetValue("IOService:/AppleARMPE/baseband", @"snum"), false);
 
-    UniqueID_ = [device uniqueIdentifier];
+    UniqueID_ = UniqueIdentifier(device);
 
     if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
         Product_ = [info objectForKey:@"SafariProductVersion"];
@@ -10705,9 +10180,6 @@ int main(int argc, char *argv[]) {
         Version_ = [Metadata_ objectForKey:@"Version"];
     }
 
-    if (Settings_ != nil)
-        Role_ = [Settings_ objectForKey:@"Role"];
-
     if (Values_ == nil) {
         Values_ = [[[NSMutableDictionary alloc] initWithCapacity:4] autorelease];
         [Metadata_ setObject:Values_ forKey:@"Values"];
@@ -10780,7 +10252,7 @@ int main(int argc, char *argv[]) {
 
     int version([[NSString stringWithContentsOfFile:@"/var/lib/cydia/firmware.ver"] intValue]);
 
-    if (access("/User", F_OK) != 0 || version != 5) {
+    if (access("/User", F_OK) != 0 || version != 6) {
         _trace();
         system("/usr/libexec/cydia/firmware.sh");
         _trace();
@@ -10818,6 +10290,7 @@ int main(int argc, char *argv[]) {
     Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
     Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
     Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
+    Folder_.Set(space_, 0x8e/255.f, 0x8e/255.f, 0x93/255.f, 1.0);
     Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
     White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
     Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
@@ -10829,18 +10302,16 @@ int main(int argc, char *argv[]) {
     RemovingColor_ = [UIColor colorWithRed:1.00f green:0.88f blue:0.88f alpha:1.00f];
     /* }}}*/
     /* UIKit Configuration {{{ */
-    void (*$GSFontSetUseLegacyFontMetrics)(BOOL)(reinterpret_cast<void (*)(BOOL)>(dlsym(RTLD_DEFAULT, "GSFontSetUseLegacyFontMetrics")));
-    if ($GSFontSetUseLegacyFontMetrics != NULL)
-        $GSFontSetUseLegacyFontMetrics(YES);
-
     // XXX: I have a feeling this was important
     //UIKeyboardDisableAutomaticAppearance();
     /* }}} */
 
-    BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemHasCapability"));
+    $SBSSetInterceptsMenuButtonForever = reinterpret_cast<void (*)(bool)>(dlsym(RTLD_DEFAULT, "SBSSetInterceptsMenuButtonForever"));
+
+    const char *symbol(kCFCoreFoundationVersionNumber >= 800 ? "MGGetBoolAnswer" : "GSSystemHasCapability");
+    BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, symbol));
     bool fast = GSSystemHasCapability != NULL && GSSystemHasCapability(CFSTR("armv7"));
 
-    ShowPromoted_ = fast;
     PulseInterval_ = fast ? 50000 : 500000;
 
     Colon_ = UCLocalize("COLON_DELIMITED");
@@ -10848,13 +10319,6 @@ int main(int argc, char *argv[]) {
     Error_ = UCLocalize("ERROR");
     Warning_ = UCLocalize("WARNING");
 
-#if !ForRelease
-    AprilFools_ = true;
-#else
-    CFGregorianDate date(CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeGetCurrent(), CFTimeZoneCopySystem()));
-    AprilFools_ = date.month == 4 && date.day == 1;
-#endif
-
     _trace();
     int value(UIApplicationMain(argc, argv, @"Cydia", @"Cydia"));