]> git.saurik.com Git - cydia.git/blobdiff - MobileCydia.mm
The default permission from mktemp is only 600 :/.
[cydia.git] / MobileCydia.mm
index 612cd23e3cb83f144c6a07ea03722e98f94d3b07..addc9418f6ba0383348cf30df22c877e14870f6e 100644 (file)
@@ -1,5 +1,5 @@
 /* Cydia - iPhone UIKit Front-End for Debian APT
 /* Cydia - iPhone UIKit Front-End for Debian APT
- * Copyright (C) 2008-2014  Jay Freeman (saurik)
+ * Copyright (C) 2008-2015  Jay Freeman (saurik)
 */
 
 /* GNU General Public License, Version 3 {{{ */
 */
 
 /* GNU General Public License, Version 3 {{{ */
 #include <WebKit/DOMHTMLIFrameElement.h>
 
 #include <algorithm>
 #include <WebKit/DOMHTMLIFrameElement.h>
 
 #include <algorithm>
+#include <fstream>
 #include <iomanip>
 #include <set>
 #include <sstream>
 #include <string>
 
 #include <iomanip>
 #include <set>
 #include <sstream>
 #include <string>
 
-#include <ext/stdio_filebuf.h>
+#include "fdstream.hpp"
 
 #undef ABS
 
 
 #undef ABS
 
+#include "apt.h"
 #include <apt-pkg/acquire.h>
 #include <apt-pkg/acquire-item.h>
 #include <apt-pkg/algorithms.h>
 #include <apt-pkg/acquire.h>
 #include <apt-pkg/acquire-item.h>
 #include <apt-pkg/algorithms.h>
@@ -90,6 +92,7 @@
 #include <sys/mount.h>
 #include <sys/reboot.h>
 
 #include <sys/mount.h>
 #include <sys/reboot.h>
 
+#include <dirent.h>
 #include <fcntl.h>
 #include <notify.h>
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <notify.h>
 #include <dlfcn.h>
@@ -237,34 +240,15 @@ union SplitHash {
 };
 // }}}
 
 };
 // }}}
 
-static void setreugid(uid_t uid, gid_t gid) {
-    _assert(setreuid(uid, uid) != -1);
-    _assert(setregid(gid, gid) != -1);
-}
-
-static void setreguid(gid_t gid, uid_t uid) {
-    _assert(setregid(gid, gid) != -1);
-    _assert(setreuid(uid, uid) != -1);
-}
-
-struct Root {
-    Root() {
-        _trace();
-        setreugid(0, 0);
-        _assert(pthread_setugid_np(0, 0) != -1);
-        setreguid(501, 501);
-    }
-
-    ~Root() {
-        _trace();
-        setreugid(0, 0);
-        _assert(pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE) != -1);
-        setreguid(501, 501);
-    }
-};
-
-#define _root(code) \
-    ({ Root _root; code; })
+@implementation NSDictionary (Cydia)
+- (id) invokeUndefinedMethodFromWebScript:(NSString *)name withArguments:(NSArray *)arguments {
+    if (false);
+    else if ([name isEqualToString:@"get"])
+        return [self objectForKey:[arguments objectAtIndex:0]];
+    else if ([name isEqualToString:@"keys"])
+        return [self allKeys];
+    return nil;
+} @end
 
 static NSString *Colon_;
 NSString *Elision_;
 
 static NSString *Colon_;
 NSString *Elision_;
@@ -276,6 +260,7 @@ static NSString *Cache_;
     [NSString stringWithFormat:@"%@/%s", Cache_, file]
 
 static void (*$SBSSetInterceptsMenuButtonForever)(bool);
     [NSString stringWithFormat:@"%@/%s", Cache_, file]
 
 static void (*$SBSSetInterceptsMenuButtonForever)(bool);
+static NSData *(*$SBSCopyIconImagePNGDataForDisplayIdentifier)(NSString *);
 
 static CFStringRef (*$MGCopyAnswer)(CFStringRef);
 
 
 static CFStringRef (*$MGCopyAnswer)(CFStringRef);
 
@@ -319,13 +304,8 @@ static _finline NSString *CydiaURL(NSString *path) {
     return [[NSString stringWithUTF8String:page] stringByAppendingString:path];
 }
 
     return [[NSString stringWithUTF8String:page] stringByAppendingString:path];
 }
 
-static void ReapZombie(pid_t pid) {
-    int status;
-  wait:
-    if (waitpid(pid, &status, 0) == -1)
-        if (errno == EINTR)
-            goto wait;
-        else _assert(false);
+static NSString *ShellEscape(NSString *value) {
+    return [NSString stringWithFormat:@"'%@'", [value stringByReplacingOccurrencesOfString:@"'" withString:@"'\\''"]];
 }
 
 static _finline void UpdateExternalStatus(uint64_t newStatus) {
 }
 
 static _finline void UpdateExternalStatus(uint64_t newStatus) {
@@ -522,6 +502,10 @@ static _finline CFStringRef CYStringCreate(const char *data, size_t size) {
         CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(data), size, kCFStringEncodingISOLatin1, NO, kCFAllocatorNull);
 }
 
         CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(data), size, kCFStringEncodingISOLatin1, NO, kCFAllocatorNull);
 }
 
+static _finline CFStringRef CYStringCreate(const std::string &data) {
+    return CYStringCreate(data.data(), data.size());
+}
+
 static _finline CFStringRef CYStringCreate(const char *data) {
     return CYStringCreate(data, strlen(data));
 }
 static _finline CFStringRef CYStringCreate(const char *data) {
     return CYStringCreate(data, strlen(data));
 }
@@ -707,7 +691,6 @@ static const NSString *UI_;
 
 static int Finish_;
 static bool RestartSubstrate_;
 
 static int Finish_;
 static bool RestartSubstrate_;
-static bool UpgradeCydia_;
 static NSArray *Finishes_;
 
 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
 static NSArray *Finishes_;
 
 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
@@ -934,6 +917,25 @@ static NSString *CYHex(NSData *data, bool reverse = false) {
     return [NSString stringWithUTF8String:string];
 }
 
     return [NSString stringWithUTF8String:string];
 }
 
+static NSString *VerifySource(NSString *href) {
+    static RegEx href_r("(http(s?)://|file:///)[^# ]*");
+    if (!href_r(href)) {
+        [[[[UIAlertView alloc]
+            initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("INVALID_URL")]
+            message:UCLocalize("INVALID_URL_EX")
+            delegate:nil
+            cancelButtonTitle:UCLocalize("OK")
+            otherButtonTitles:nil
+        ] autorelease] show];
+
+        return nil;
+    }
+
+    if (![href hasSuffix:@"/"])
+        href = [href stringByAppendingString:@"/"];
+    return href;
+}
+
 @class Cydia;
 
 /* Delegate Prototypes {{{ */
 @class Cydia;
 
 /* Delegate Prototypes {{{ */
@@ -977,7 +979,7 @@ static NSString *CYHex(NSData *data, bool reverse = false) {
 - (void) _saveConfig;
 - (void) syncData;
 - (void) addSource:(NSDictionary *)source;
 - (void) _saveConfig;
 - (void) syncData;
 - (void) addSource:(NSDictionary *)source;
-- (void) addTrivialSource:(NSString *)href;
+- (BOOL) addTrivialSource:(NSString *)href;
 - (UIProgressHUD *) addProgressHUD;
 - (void) removeProgressHUD:(UIProgressHUD *)hud;
 - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item;
 - (UIProgressHUD *) addProgressHUD;
 - (void) removeProgressHUD:(UIProgressHUD *)hud;
 - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item;
@@ -1133,6 +1135,7 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
 
 + (Database *) sharedInstance;
 - (unsigned) era;
 
 + (Database *) sharedInstance;
 - (unsigned) era;
+- (bool) hasPackages;
 
 - (void) _readCydia:(NSNumber *)fd;
 - (void) _readStatus:(NSNumber *)fd;
 
 - (void) _readCydia:(NSNumber *)fd;
 - (void) _readStatus:(NSNumber *)fd;
@@ -1418,14 +1421,14 @@ struct PackageValue :
 
     char version_[8];
     char name_[];
 
     char version_[8];
     char name_[];
-};
+} _packed;
 
 struct MetaValue :
     Cytore::Block
 {
     uint32_t active_;
     Cytore::Offset<PackageValue> packages_[1 << 16];
 
 struct MetaValue :
     Cytore::Block
 {
     uint32_t active_;
     Cytore::Offset<PackageValue> packages_[1 << 16];
-};
+} _packed;
 
 static Cytore::File<MetaValue> MetaFile_;
 // }}}
 
 static Cytore::File<MetaValue> MetaFile_;
 // }}}
@@ -2171,7 +2174,6 @@ struct ParsedPackage {
 - (NSString *) installed;
 - (BOOL) uninstalled;
 
 - (NSString *) installed;
 - (BOOL) uninstalled;
 
-- (BOOL) valid;
 - (BOOL) upgradableAndEssential:(BOOL)essential;
 - (BOOL) essential;
 - (BOOL) broken;
 - (BOOL) upgradableAndEssential:(BOOL)essential;
 - (BOOL) essential;
 - (BOOL) broken;
@@ -2518,15 +2520,7 @@ struct PackageNameOrdering :
         _end
 
         _profile(Package$parse$Tagline)
         _end
 
         _profile(Package$parse$Tagline)
-            const char *start, *end;
-            if (parser->ShortDesc(start, end)) {
-                const char *stop(reinterpret_cast<const char *>(memchr(start, '\n', end - start)));
-                if (stop == NULL)
-                    stop = end;
-                while (stop != start && stop[-1] == '\r')
-                    --stop;
-                parsed->tagline_.set(pool_, start, stop - start);
-            }
+            parsed->tagline_.set(pool_, parser->ShortDesc());
         _end
 
         _profile(Package$parse$Retain)
         _end
 
         _profile(Package$parse$Retain)
@@ -2555,20 +2549,15 @@ struct PackageNameOrdering :
 
         version_ = version;
 
 
         version_ = version;
 
-        pkgCache::PkgIterator iterator(version.ParentPkg());
+        pkgCache::PkgIterator iterator(version_.ParentPkg());
         iterator_ = iterator;
 
         _profile(Package$initWithVersion$Version)
         iterator_ = iterator;
 
         _profile(Package$initWithVersion$Version)
-            if (!version_.end())
-                file_ = version_.FileList();
-            else {
-                pkgCache &cache([database_ cache]);
-                file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
-            }
+            file_ = version_.FileList();
         _end
 
         _profile(Package$initWithVersion$Cache)
         _end
 
         _profile(Package$initWithVersion$Cache)
-            name_.set(NULL, iterator.Display());
+            name_.set(NULL, version_.Display());
 
             latest_.set(NULL, StripVersion_(version_.VerStr()));
 
 
             latest_.set(NULL, StripVersion_(version_.VerStr()));
 
@@ -2632,7 +2621,11 @@ struct PackageNameOrdering :
         } while (false); _end
 
         _profile(Package$initWithVersion$Tags)
         } while (false); _end
 
         _profile(Package$initWithVersion$Tags)
+#ifdef __arm64__
+            pkgCache::TagIterator tag(version_.TagList());
+#else
             pkgCache::TagIterator tag(iterator.TagList());
             pkgCache::TagIterator tag(iterator.TagList());
+#endif
             if (!tag.end()) {
                 tags_ = [NSMutableArray arrayWithCapacity:8];
 
             if (!tag.end()) {
                 tags_ = [NSMutableArray arrayWithCapacity:8];
 
@@ -2755,6 +2748,21 @@ struct PackageNameOrdering :
     return iterator_;
 }
 
     return iterator_;
 }
 
+- (NSArray *) downgrades {
+    NSMutableArray *versions([NSMutableArray arrayWithCapacity:4]);
+
+    for (auto version(iterator_.VersionList()); !version.end(); ++version) {
+        if (version == version_)
+            continue;
+        Package *package([[[Package allocWithZone:NULL] initWithVersion:version withZone:NULL inPool:NULL database:database_] autorelease]);
+        if ([package source] == nil)
+            continue;
+        [versions addObject:package];
+    }
+
+    return versions;
+}
+
 - (NSString *) section {
     if (section$_ == nil) {
         if (section_ == NULL)
 - (NSString *) section {
     if (section$_ == nil) {
         if (section_ == NULL)
@@ -2774,7 +2782,10 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) longSection {
 }
 
 - (NSString *) longSection {
-    return LocalizeSection([self section]);
+    if (NSString *section = [self section])
+        return LocalizeSection(section);
+    else
+        return nil;
 }
 
 - (NSString *) shortSection {
 }
 
 - (NSString *) shortSection {
@@ -2844,23 +2855,12 @@ struct PackageNameOrdering :
 
 @synchronized (database_) {
     pkgRecords::Parser &parser([database_ records]->Lookup(file_));
 
 @synchronized (database_) {
     pkgRecords::Parser &parser([database_ records]->Lookup(file_));
-
-    const char *start, *end;
-    if (!parser.ShortDesc(start, end))
+    std::string value(parser.ShortDesc());
+    if (value.empty())
         return nil;
         return nil;
-
-    if (end - start > 200)
-        end = start + 200;
-
-    /*
-    if (const char *stop = reinterpret_cast<const char *>(memchr(start, '\n', end - start)))
-        end = stop;
-
-    while (end != start && end[-1] == '\r')
-        --end;
-    */
-
-    return [(id) CYStringCreate(start, end - start) autorelease];
+    if (value.size() > 200)
+        value.resize(200);
+    return [(id) CYStringCreate(value) autorelease];
 } }
 
 - (unichar) index {
 } }
 
 - (unichar) index {
@@ -2912,17 +2912,13 @@ struct PackageNameOrdering :
     return installed_.empty();
 }
 
     return installed_.empty();
 }
 
-- (BOOL) valid {
-    return !version_.end();
-}
-
 - (BOOL) upgradableAndEssential:(BOOL)essential {
     _profile(Package$upgradableAndEssential)
         pkgCache::VerIterator current(iterator_.CurrentVer());
         if (current.end())
             return essential && essential_;
         else
 - (BOOL) upgradableAndEssential:(BOOL)essential {
     _profile(Package$upgradableAndEssential)
         pkgCache::VerIterator current(iterator_.CurrentVer());
         if (current.end())
             return essential && essential_;
         else
-            return !version_.end() && version_ != current;
+            return version_ != current;
     _end
 }
 
     _end
 }
 
@@ -3205,8 +3201,10 @@ struct PackageNameOrdering :
         for (NSString *file in files)
             if (application_r(file)) {
                 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
         for (NSString *file in files)
             if (application_r(file)) {
                 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
+                if (info == nil)
+                    continue;
                 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
                 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
-                if ([id isEqualToString:me])
+                if (id == nil || [id isEqualToString:me])
                     continue;
 
                 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
                     continue;
 
                 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
@@ -3350,6 +3348,9 @@ struct PackageNameOrdering :
 
 - (void) clear {
 @synchronized (database_) {
 
 - (void) clear {
 @synchronized (database_) {
+    if ([database_ era] != era_ || file_.end())
+        return;
+
     pkgProblemResolver *resolver = [database_ resolver];
     resolver->Clear(iterator_);
 
     pkgProblemResolver *resolver = [database_ resolver];
     resolver->Clear(iterator_);
 
@@ -3360,11 +3361,15 @@ struct PackageNameOrdering :
 
 - (void) install {
 @synchronized (database_) {
 
 - (void) install {
 @synchronized (database_) {
+    if ([database_ era] != era_ || file_.end())
+        return;
+
     pkgProblemResolver *resolver = [database_ resolver];
     resolver->Clear(iterator_);
     resolver->Protect(iterator_);
 
     pkgCacheFile &cache([database_ cache]);
     pkgProblemResolver *resolver = [database_ resolver];
     resolver->Clear(iterator_);
     resolver->Protect(iterator_);
 
     pkgCacheFile &cache([database_ cache]);
+    cache->SetCandidateVersion(version_);
     cache->SetReInstall(iterator_, false);
     cache->MarkInstall(iterator_, false);
 
     cache->SetReInstall(iterator_, false);
     cache->MarkInstall(iterator_, false);
 
@@ -3375,6 +3380,9 @@ struct PackageNameOrdering :
 
 - (void) remove {
 @synchronized (database_) {
 
 - (void) remove {
 @synchronized (database_) {
+    if ([database_ era] != era_ || file_.end())
+        return;
+
     pkgProblemResolver *resolver = [database_ resolver];
     resolver->Clear(iterator_);
     resolver->Remove(iterator_);
     pkgProblemResolver *resolver = [database_ resolver];
     resolver->Clear(iterator_);
     resolver->Remove(iterator_);
@@ -3516,6 +3524,10 @@ class CydiaLogCleaner :
     CFArrayRemoveAllValues(packages_);
 }
 
     CFArrayRemoveAllValues(packages_);
 }
 
+- (bool) hasPackages {
+    return CFArrayGetCount(packages_) != 0;
+}
+
 - (void) dealloc {
     // XXX: actually implement this thing
     _assert(false);
 - (void) dealloc {
     // XXX: actually implement this thing
     _assert(false);
@@ -3525,8 +3537,7 @@ class CydiaLogCleaner :
 }
 
 - (void) _readCydia:(NSNumber *)fd {
 }
 
 - (void) _readCydia:(NSNumber *)fd {
-    __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
-    std::istream is(&ib);
+    boost::fdistream is([fd intValue]);
     std::string line;
 
     static RegEx finish_r("finish:([^:]*)");
     std::string line;
 
     static RegEx finish_r("finish:([^:]*)");
@@ -3552,8 +3563,7 @@ class CydiaLogCleaner :
 }
 
 - (void) _readStatus:(NSNumber *)fd {
 }
 
 - (void) _readStatus:(NSNumber *)fd {
-    __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
-    std::istream is(&ib);
+    boost::fdistream is([fd intValue]);
     std::string line;
 
     static RegEx conffile_r("status: [^ ]* : conffile-prompt : (.*?) *");
     std::string line;
 
     static RegEx conffile_r("status: [^ ]* : conffile-prompt : (.*?) *");
@@ -3609,8 +3619,7 @@ class CydiaLogCleaner :
 }
 
 - (void) _readOutput:(NSNumber *)fd {
 }
 
 - (void) _readOutput:(NSNumber *)fd {
-    __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
-    std::istream is(&ib);
+    boost::fdistream is([fd intValue]);
     std::string line;
 
     while (std::getline(is, line)) {
     std::string line;
 
     while (std::getline(is, line)) {
@@ -3637,7 +3646,11 @@ class CydiaLogCleaner :
 @synchronized (self) {
     if (static_cast<pkgDepCache *>(cache_) == NULL)
         return nil;
 @synchronized (self) {
     if (static_cast<pkgDepCache *>(cache_) == NULL)
         return nil;
-    pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
+    pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]
+#ifdef __arm64__
+        , "any"
+#endif
+    ));
     return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:NULL database:self];
 } }
 
     return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:NULL database:self];
 } }
 
@@ -3775,9 +3788,26 @@ class CydiaLogCleaner :
 - (bool) popErrorWithTitle:(NSString *)title forReadList:(pkgSourceList &)list {
     if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
         return true;
 - (bool) popErrorWithTitle:(NSString *)title forReadList:(pkgSourceList &)list {
     if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
         return true;
-    if ([self popErrorWithTitle:title forOperation:list.Read(SOURCES_LIST)])
-        return true;
     return false;
     return false;
+
+    list.Reset();
+
+    bool error(false);
+
+    if (access("/etc/apt/sources.list", F_OK) == 0)
+        error |= [self popErrorWithTitle:title forOperation:list.ReadAppend("/etc/apt/sources.list")];
+
+    std::string base("/etc/apt/sources.list.d");
+    if (DIR *sources = opendir(base.c_str())) {
+        while (dirent *source = readdir(sources))
+            if (source->d_name[0] != '.' && source->d_namlen > 5 && strcmp(source->d_name + source->d_namlen - 5, ".list") == 0 && strcmp(source->d_name, "cydia.list") != 0)
+                error |= [self popErrorWithTitle:title forOperation:list.ReadAppend((base + "/" + source->d_name).c_str())];
+        closedir(sources);
+    }
+
+    error |= [self popErrorWithTitle:title forOperation:list.ReadAppend(SOURCES_LIST)];
+
+    return error;
 }
 
 - (void) reloadDataWithInvocation:(NSInvocation *)invocation {
 }
 
 - (void) reloadDataWithInvocation:(NSInvocation *)invocation {
@@ -3835,17 +3865,16 @@ class CydiaLogCleaner :
     }
     _end
 
     }
     _end
 
-    delock_ = GetStatusDate();
-
     _trace();
     OpProgress progress;
     bool opened;
   open:
     _trace();
     OpProgress progress;
     bool opened;
   open:
+    delock_ = GetStatusDate();
     _profile(reloadDataWithInvocation$pkgCacheFile)
         opened = cache_.Open(progress, false);
     _end
     if (!opened) {
     _profile(reloadDataWithInvocation$pkgCacheFile)
         opened = cache_.Open(progress, false);
     _end
     if (!opened) {
-        // XXX: what if there are errors, but Open() == true? this should be merged with popError:
+        // XXX: this block should probably be merged with popError: in some way
         while (!_error->empty()) {
             std::string error;
             bool warning(!_error->PopMessage(error));
         while (!_error->empty()) {
             std::string error;
             bool warning(!_error->PopMessage(error));
@@ -3873,7 +3902,8 @@ class CydiaLogCleaner :
         }
 
         return;
         }
 
         return;
-    }
+    } else if ([self popErrorWithTitle:title forOperation:true])
+        return;
     _trace();
 
     unlink("/tmp/cydia.chk");
     _trace();
 
     unlink("/tmp/cydia.chk");
@@ -4108,6 +4138,17 @@ class CydiaLogCleaner :
     delock_ = nil;
 
     pkgPackageManager::OrderResult result(manager_->DoInstall(statusfd_));
     delock_ = nil;
 
     pkgPackageManager::OrderResult result(manager_->DoInstall(statusfd_));
+
+    NSString *oextended(@"/var/lib/apt/extended_states");
+    NSString *nextended(Cache("extended_states"));
+
+    struct stat info;
+    if (stat([nextended UTF8String], &info) != -1 && (info.st_mode & S_IFMT) == S_IFREG)
+        system([[NSString stringWithFormat:@"/usr/libexec/cydia/cydo /bin/cp --remove-destination %@ %@", ShellEscape(nextended), ShellEscape(oextended)] UTF8String]);
+
+    unlink([nextended UTF8String]);
+    symlink([oextended UTF8String], [nextended UTF8String]);
+
     if ([self popErrorWithTitle:title])
         return;
 
     if ([self popErrorWithTitle:title])
         return;
 
@@ -4329,8 +4370,10 @@ static _H<NSMutableSet> Diversions_;
 
 + (NSArray *) _attributeKeys {
     return [NSArray arrayWithObjects:
 
 + (NSArray *) _attributeKeys {
     return [NSArray arrayWithObjects:
+        @"bittage",
         @"bbsnum",
         @"build",
         @"bbsnum",
         @"build",
+        @"cells",
         @"coreFoundationVersionNumber",
         @"device",
         @"ecid",
         @"coreFoundationVersionNumber",
         @"device",
         @"ecid",
@@ -4359,6 +4402,17 @@ static _H<NSMutableSet> Diversions_;
     return Cydia_;
 }
 
     return Cydia_;
 }
 
+- (unsigned) bittage {
+#if 0
+#elif defined(__arm64__)
+    return 64;
+#elif defined(__arm__)
+    return 32;
+#else
+    return 0;
+#endif
+}
+
 - (NSString *) build {
     return System_;
 }
 - (NSString *) build {
     return System_;
 }
@@ -4383,6 +4437,29 @@ static _H<NSMutableSet> Diversions_;
     return (id) Idiom_ ?: [NSNull null];
 }
 
     return (id) Idiom_ ?: [NSNull null];
 }
 
+- (NSArray *) cells {
+    auto *$_CTServerConnectionCreate(reinterpret_cast<id (*)(void *, void *, void *)>(dlsym(RTLD_DEFAULT, "_CTServerConnectionCreate")));
+    if ($_CTServerConnectionCreate == NULL)
+        return nil;
+
+    struct CTResult { int flag; int error; };
+    auto *$_CTServerConnectionCellMonitorCopyCellInfo(reinterpret_cast<CTResult (*)(CFTypeRef, void *, CFArrayRef *)>(dlsym(RTLD_DEFAULT, "_CTServerConnectionCellMonitorCopyCellInfo")));
+    if ($_CTServerConnectionCellMonitorCopyCellInfo == NULL)
+        return nil;
+
+    _H<const void> connection($_CTServerConnectionCreate(NULL, NULL, NULL), true);
+    if (connection == nil)
+        return nil;
+
+    int count(0);
+    CFArrayRef cells(NULL);
+    auto result($_CTServerConnectionCellMonitorCopyCellInfo(connection, &count, &cells));
+    if (result.flag != 0)
+        return nil;
+
+    return [(NSArray *) cells autorelease];
+}
+
 - (NSString *) mcc {
     if (CFStringRef (*$CTSIMSupportCopyMobileSubscriberCountryCode)(CFAllocatorRef) = reinterpret_cast<CFStringRef (*)(CFAllocatorRef)>(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode")))
         return [(NSString *) (*$CTSIMSupportCopyMobileSubscriberCountryCode)(kCFAllocatorDefault) autorelease];
 - (NSString *) mcc {
     if (CFStringRef (*$CTSIMSupportCopyMobileSubscriberCountryCode)(CFAllocatorRef) = reinterpret_cast<CFStringRef (*)(CFAllocatorRef)>(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode")))
         return [(NSString *) (*$CTSIMSupportCopyMobileSubscriberCountryCode)(kCFAllocatorDefault) autorelease];
@@ -4445,6 +4522,10 @@ static _H<NSMutableSet> Diversions_;
         return @"getAllSources";
     else if (selector == @selector(getApplicationInfo:value:))
         return @"getApplicationInfoValue";
         return @"getAllSources";
     else if (selector == @selector(getApplicationInfo:value:))
         return @"getApplicationInfoValue";
+    else if (selector == @selector(getDisplayIdentifiers))
+        return @"getDisplayIdentifiers";
+    else if (selector == @selector(getLocalizedNameForDisplayIdentifier:))
+        return @"getLocalizedNameForDisplayIdentifier";
     else if (selector == @selector(getKernelNumber:))
         return @"getKernelNumber";
     else if (selector == @selector(getKernelString:))
     else if (selector == @selector(getKernelNumber:))
         return @"getKernelNumber";
     else if (selector == @selector(getKernelString:))
@@ -4561,6 +4642,14 @@ static _H<NSMutableSet> Diversions_;
     return [info objectForKey:key];
 }
 
     return [info objectForKey:key];
 }
 
+- (NSArray *) getDisplayIdentifiers {
+    return SBSCopyApplicationDisplayIdentifiers(false, false);
+}
+
+- (NSString *) getLocalizedNameForDisplayIdentifier:(NSString *)identifier {
+    return [SBSCopyLocalizedApplicationNameForDisplayIdentifier(identifier) autorelease] ?: (id) [NSNull null];
+}
+
 - (NSNumber *) getKernelNumber:(NSString *)name {
     const char *string([name UTF8String]);
 
 - (NSNumber *) getKernelNumber:(NSString *)name {
     const char *string([name UTF8String]);
 
@@ -4679,8 +4768,12 @@ static _H<NSMutableSet> Diversions_;
     nil] waitUntilDone:NO];
 }
 
     nil] waitUntilDone:NO];
 }
 
-- (void) addTrivialSource:(NSString *)href {
+- (BOOL) addTrivialSource:(NSString *)href {
+    href = VerifySource(href);
+    if (href == nil)
+        return NO;
     [delegate_ performSelectorOnMainThread:@selector(addTrivialSource:) withObject:href waitUntilDone:NO];
     [delegate_ performSelectorOnMainThread:@selector(addTrivialSource:) withObject:href waitUntilDone:NO];
+    return YES;
 }
 
 - (void) refreshSources {
 }
 
 - (void) refreshSources {
@@ -4735,13 +4828,25 @@ static _H<NSMutableSet> Diversions_;
     nil];
 }
 
     nil];
 }
 
-ssize_t DiskUsage(const char *path);
-
 - (NSNumber *) du:(NSString *)path {
 - (NSNumber *) du:(NSString *)path {
-    ssize_t usage(DiskUsage([path UTF8String]));
-    if (usage != -1)
-        usage /= 1024;
-    return [NSNumber numberWithUnsignedLong:usage];
+    NSNumber *value(nil);
+
+    FILE *du(popen([[NSString stringWithFormat:@"/usr/libexec/cydia/cydo /usr/libexec/cydia/du -ks %@", ShellEscape(path)] UTF8String], "r"));
+    if (du != NULL) {
+        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)];
+            }
+        }
+        pclose(du);
+    }
+
+    return value;
 }
 
 - (void) close {
 }
 
 - (void) close {
@@ -4757,10 +4862,16 @@ ssize_t DiskUsage(const char *path);
 }
 
 - (NSString *) substitutePackageNames:(NSString *)message {
 }
 
 - (NSString *) substitutePackageNames:(NSString *)message {
+    auto database([Database sharedInstance]);
+
+    // XXX: this check is less racy than you'd expect, but this entire concept is a little awkward
+    if ([database hasPackages])
+        return message;
+
     NSMutableArray *words([[[message componentsSeparatedByString:@" "] mutableCopy] autorelease]);
     for (size_t i(0), e([words count]); i != e; ++i) {
         NSString *word([words objectAtIndex:i]);
     NSMutableArray *words([[[message componentsSeparatedByString:@" "] mutableCopy] autorelease]);
     for (size_t i(0), e([words count]); i != e; ++i) {
         NSString *word([words objectAtIndex:i]);
-        if (Package *package = [[Database sharedInstance] packageWithName:word])
+        if (Package *package = [database packageWithName:word])
             [words replaceObjectAtIndex:i withObject:[package name]];
     }
 
             [words replaceObjectAtIndex:i withObject:[package name]];
     }
 
@@ -5135,8 +5246,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
         issues_ = [NSMutableArray arrayWithCapacity:4];
 
 
         issues_ = [NSMutableArray arrayWithCapacity:4];
 
-        UpgradeCydia_ = false;
-
         for (Package *package in packages) {
             pkgCache::PkgIterator iterator([package iterator]);
             NSString *name([package id]);
         for (Package *package in packages) {
             pkgCache::PkgIterator iterator([package iterator]);
             NSString *name([package id]);
@@ -5248,9 +5357,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 [removes addObject:name];
             }
 
                 [removes addObject:name];
             }
 
-            if ([name isEqualToString:@"cydia"])
-                UpgradeCydia_ = true;
-
             substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
             substrate_ |= DepSubstrate(iterator.CurrentVer());
         }
             substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
             substrate_ |= DepSubstrate(iterator.CurrentVer());
         }
@@ -5540,34 +5646,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super viewWillAppear:animated];
 }
 
     [super viewWillAppear:animated];
 }
 
-- (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/libexec/cydia/cydo", "cydo", "/usr/bin/sbreload", NULL);
-            perror("sbreload");
-
-            exit(0);
-        } ReapZombie(pid);
-
-        exit(0);
-    } ReapZombie(pid);
-
-    sleep(15);
-    system("/usr/bin/killall backboardd SpringBoard sbreload");
-}
-
 - (void) close {
     UpdateExternalStatus(0);
 
 - (void) close {
     UpdateExternalStatus(0);
 
@@ -5598,7 +5676,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         reload: {
             UIProgressHUD *hud([delegate_ addProgressHUD]);
             [hud setText:UCLocalize("LOADING")];
         reload: {
             UIProgressHUD *hud([delegate_ addProgressHUD]);
             [hud setText:UCLocalize("LOADING")];
-            [self performSelector:@selector(reloadSpringBoard) withObject:nil afterDelay:0.5];
+            [delegate_ performSelector:@selector(reloadSpringBoard) withObject:nil afterDelay:0.5];
             return;
         }
 
             return;
         }
 
@@ -6212,7 +6290,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 NSString *directory = [stack lastObject];
                 [stack addObject:[file stringByAppendingString:@"/"]];
                 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
                 NSString *directory = [stack lastObject];
                 [stack addObject:[file stringByAppendingString:@"/"]];
                 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
-                    ([stack count] - 2) * 3, "",
+                    int(([stack count] - 2) * 3), "",
                     [file substringFromIndex:[directory length]]
                 ]];
             }
                     [file substringFromIndex:[directory length]]
                 ]];
             }
@@ -6239,7 +6317,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _H<NSString> name_;
     bool commercial_;
     std::vector<std::pair<_H<NSString>, _H<NSString>>> buttons_;
     _H<NSString> name_;
     bool commercial_;
     std::vector<std::pair<_H<NSString>, _H<NSString>>> buttons_;
+    _H<UIActionSheet> sheet_;
     _H<UIBarButtonItem> button_;
     _H<UIBarButtonItem> button_;
+    _H<NSArray> versions_;
 }
 
 - (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer;
 }
 
 - (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer;
@@ -6252,22 +6332,44 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", (id) name_]];
 }
 
     return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", (id) name_]];
 }
 
+- (void) _clickButtonWithPackage:(Package *)package {
+    [delegate_ installPackage:package];
+}
+
 - (void) _clickButtonWithName:(NSString *)name {
     if ([name isEqualToString:@"CLEAR"])
 - (void) _clickButtonWithName:(NSString *)name {
     if ([name isEqualToString:@"CLEAR"])
-        [delegate_ clearPackage:package_];
-    else if ([name isEqualToString:@"INSTALL"])
-        [delegate_ installPackage:package_];
-    else if ([name isEqualToString:@"REINSTALL"])
-        [delegate_ installPackage:package_];
+        return [delegate_ clearPackage:package_];
     else if ([name isEqualToString:@"REMOVE"])
     else if ([name isEqualToString:@"REMOVE"])
-        [delegate_ removePackage:package_];
-    else if ([name isEqualToString:@"UPGRADE"])
-        [delegate_ installPackage:package_];
+        return [delegate_ removePackage:package_];
+    else if ([name isEqualToString:@"DOWNGRADE"]) {
+        sheet_ = [[[UIActionSheet alloc]
+            initWithTitle:nil
+            delegate:self
+            cancelButtonTitle:nil
+            destructiveButtonTitle:nil
+            otherButtonTitles:nil
+        ] autorelease];
+
+        for (Package *version in (id) versions_)
+            [sheet_ addButtonWithTitle:[version latest]];
+        [sheet_ setContext:@"version"];
+
+        [delegate_ showActionSheet:sheet_ fromItem:[[self navigationItem] rightBarButtonItem]];
+        return;
+    }
+
+    else if ([name isEqualToString:@"INSTALL"]);
+    else if ([name isEqualToString:@"REINSTALL"]);
+    else if ([name isEqualToString:@"UPGRADE"]);
     else _assert(false);
     else _assert(false);
+
+    [delegate_ installPackage:package_];
 }
 
 - (void) actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)button {
     NSString *context([sheet context]);
 }
 
 - (void) actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)button {
     NSString *context([sheet context]);
+    if (sheet_ == sheet)
+        sheet_ = nil;
 
     if ([context isEqualToString:@"modify"]) {
         if (button != [sheet cancelButtonIndex]) {
 
     if ([context isEqualToString:@"modify"]) {
         if (button != [sheet cancelButtonIndex]) {
@@ -6277,6 +6379,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 [self _clickButtonWithName:buttons_[button].first];
         }
 
                 [self _clickButtonWithName:buttons_[button].first];
         }
 
+        [sheet dismissWithClickedButtonIndex:button animated:YES];
+    } else if ([context isEqualToString:@"version"]) {
+        if (button != [sheet cancelButtonIndex]) {
+            Package *version([versions_ objectAtIndex:button]);
+            if (IsWildcat_)
+                [self performSelector:@selector(_clickButtonWithPackage:) withObject:version afterDelay:0];
+            else
+                [self _clickButtonWithPackage:version];
+        }
+
         [sheet dismissWithClickedButtonIndex:button animated:YES];
     }
 }
         [sheet dismissWithClickedButtonIndex:button animated:YES];
     }
 }
@@ -6298,7 +6410,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         for (const auto &button : buttons_)
             [buttons addObject:button.second];
 
         for (const auto &button : buttons_)
             [buttons addObject:button.second];
 
-        UIActionSheet *sheet = [[[UIActionSheet alloc]
+        sheet_ = [[[UIActionSheet alloc]
             initWithTitle:nil
             delegate:self
             cancelButtonTitle:nil
             initWithTitle:nil
             delegate:self
             cancelButtonTitle:nil
@@ -6306,14 +6418,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             otherButtonTitles:nil
         ] autorelease];
 
             otherButtonTitles:nil
         ] autorelease];
 
-        for (NSString *button in buttons) [sheet addButtonWithTitle:button];
-        if (!IsWildcat_) {
-           [sheet addButtonWithTitle:UCLocalize("CANCEL")];
-           [sheet setCancelButtonIndex:[sheet numberOfButtons] - 1];
-        }
-        [sheet setContext:@"modify"];
+        for (NSString *button in buttons)
+            [sheet_ addButtonWithTitle:button];
+        [sheet_ setContext:@"modify"];
 
 
-        [delegate_ showActionSheet:sheet fromItem:[[self navigationItem] rightBarButtonItem]];
+        [delegate_ showActionSheet:sheet_ fromItem:[[self navigationItem] rightBarButtonItem]];
     }
 }
 
     }
 }
 
@@ -6347,7 +6456,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) reloadData {
     [super reloadData];
 
 - (void) reloadData {
     [super reloadData];
 
+    [sheet_ dismissWithClickedButtonIndex:[sheet_ cancelButtonIndex] animated:YES];
+    sheet_ = nil;
+
     package_ = [database_ packageWithName:name_];
     package_ = [database_ packageWithName:name_];
+    versions_ = [package_ downgrades];
 
     buttons_.clear();
 
 
     buttons_.clear();
 
@@ -6367,6 +6480,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             buttons_.push_back(std::make_pair(@"REINSTALL", UCLocalize("REINSTALL")));
         if (![package_ uninstalled])
             buttons_.push_back(std::make_pair(@"REMOVE", UCLocalize("REMOVE")));
             buttons_.push_back(std::make_pair(@"REINSTALL", UCLocalize("REINSTALL")));
         if (![package_ uninstalled])
             buttons_.push_back(std::make_pair(@"REMOVE", UCLocalize("REMOVE")));
+        if ([versions_ count] != 0)
+            buttons_.push_back(std::make_pair(@"DOWNGRADE", UCLocalize("DOWNGRADE")));
     }
 
     NSString *title;
     }
 
     NSString *title;
@@ -6821,7 +6936,7 @@ typedef Function<void, NSMutableArray *> PackageSorter;
 
     _profile(PackageTable$reloadData$Filter)
         for (Package *package in packages)
 
     _profile(PackageTable$reloadData$Filter)
         for (Package *package in packages)
-            if ([package valid] && filter(package))
+            if (filter(package))
                 [filtered addObject:package];
     _end
 
                 [filtered addObject:package];
     _end
 
@@ -6888,7 +7003,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [alert setCancelButtonIndex:0];
 
     [alert setMessage:
     [alert setCancelButtonIndex:0];
 
     [alert setMessage:
-        @"Copyright \u00a9 2008-2014\n"
+        @"Copyright \u00a9 2008-2015\n"
         "SaurikIT, LLC\n"
         "\n"
         "Jay Freeman (saurik)\n"
         "SaurikIT, LLC\n"
         "\n"
         "Jay Freeman (saurik)\n"
@@ -7163,7 +7278,28 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
     Database *database([Database sharedInstance]);
 
 
     Database *database([Database sharedInstance]);
 
-    if ([command isEqualToString:@"package-icon"]) {
+    if (false);
+    else if ([command isEqualToString:@"application-icon"]) {
+        if (path == nil)
+            goto fail;
+        path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+
+        UIImage *icon(nil);
+
+        if (icon == nil && $SBSCopyIconImagePNGDataForDisplayIdentifier != NULL) {
+            NSData *data([$SBSCopyIconImagePNGDataForDisplayIdentifier(path) autorelease]);
+            icon = [UIImage imageWithData:data];
+        }
+
+        if (icon == nil)
+            if (NSString *file = SBSCopyIconImagePathForDisplayIdentifier(path))
+                icon = [UIImage imageAtPath:file];
+
+        if (icon == nil)
+            icon = [UIImage imageNamed:@"unknown.png"];
+
+        [self _returnPNGWithImage:icon forRequest:request];
+    } else if ([command isEqualToString:@"package-icon"]) {
         if (path == nil)
             goto fail;
         path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
         if (path == nil)
             goto fail;
         path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
@@ -7444,7 +7580,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         [section addToCount];
 
         _profile(SectionsView$reloadData$Filter)
         [section addToCount];
 
         _profile(SectionsView$reloadData$Filter)
-            if (![package valid] || ![package visible])
+            if (![package visible])
                 continue;
         _end
 
                 continue;
         _end
 
@@ -7896,20 +8032,15 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     const char *package([name_ UTF8String]);
     bool on([ignoredSwitch_ isOn]);
 
     const char *package([name_ UTF8String]);
     bool on([ignoredSwitch_ isOn]);
 
-    pid_t pid(ExecFork());
-    if (pid == 0) {
-        FILE *dpkg(popen("/usr/libexec/cydo --set-selections", "w"));
-        fwrite(package, strlen(package), 1, dpkg);
-
-        if (on)
-            fwrite(" hold\n", 6, 1, dpkg);
-        else
-            fwrite(" install\n", 9, 1, dpkg);
+    FILE *dpkg(popen("/usr/libexec/cydia/cydo --set-selections", "w"));
+    fwrite(package, strlen(package), 1, dpkg);
 
 
-        pclose(dpkg);
+    if (on)
+        fwrite(" hold\n", 6, 1, dpkg);
+    else
+        fwrite(" install\n", 9, 1, dpkg);
 
 
-        exit(0);
-    } ReapZombie(pid);
+    pclose(dpkg);
 }
 
 - (void) onIgnored:(id)control {
 }
 
 - (void) onIgnored:(id)control {
@@ -8412,8 +8543,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
         [Sources_ removeObjectForKey:[source key]];
 
 
         [Sources_ removeObjectForKey:[source key]];
 
-        [delegate_ _saveConfig];
-        [delegate_ reloadDataWithInvocation:nil];
+        [delegate_ syncData];
     }
 }
 
     }
 }
 
@@ -8572,27 +8702,10 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         switch (button) {
             case 1: {
                 NSString *href = [[alert textField] text];
         switch (button) {
             case 1: {
                 NSString *href = [[alert textField] text];
-
-                static RegEx href_r("(http(s?)://|file:///)[^# ]*");
-                if (!href_r(href)) {
-                    UIAlertView *alert = [[[UIAlertView alloc]
-                        initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("INVALID_URL")]
-                        message:UCLocalize("INVALID_URL_EX")
-                        delegate:self
-                        cancelButtonTitle:UCLocalize("OK")
-                        otherButtonTitles:nil
-                    ] autorelease];
-
-                    [alert setContext:@"badurl"];
-                    [alert show];
-
+                href = VerifySource(href);
+                if (href == nil)
                     break;
                     break;
-                }
-
-                if (![href hasSuffix:@"/"])
-                    href_ = [href stringByAppendingString:@"/"];
-                else
-                    href_ = href;
+                href_ = href;
 
                 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:@"Packages.bz2"] method:@"HEAD"] retain];
                 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
@@ -9014,6 +9127,15 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [self _loaded];
 }
 
     [self _loaded];
 }
 
+- (void) reloadSpringBoard {
+    if (kCFCoreFoundationVersionNumber >= 700) // XXX: iOS 6.x
+        system("/usr/libexec/cydia/cydo /bin/launchctl stop com.apple.backboardd");
+    else
+        system("/usr/libexec/cydia/cydo /bin/launchctl stop com.apple.SpringBoard");
+    sleep(15);
+    system("/usr/bin/killall backboardd SpringBoard");
+}
+
 - (void) _saveConfig {
     SaveConfig(database_);
 }
 - (void) _saveConfig {
     SaveConfig(database_);
 }
@@ -9223,8 +9345,10 @@ _end
     CydiaAddSource(href, distribution, sections);
 }
 
     CydiaAddSource(href, distribution, sections);
 }
 
-- (void) addTrivialSource:(NSString *)href {
+// XXX: this method should not return anything
+- (BOOL) addTrivialSource:(NSString *)href {
     CydiaAddSource(href, @"./");
     CydiaAddSource(href, @"./");
+    return YES;
 }
 
 - (void) resolve {
 }
 
 - (void) resolve {
@@ -9306,14 +9430,7 @@ _end
 
 - (void) _uicache {
     _trace();
 
 - (void) _uicache {
     _trace();
-
-    if (UpgradeCydia_ && Finish_ > 0) {
-        setreugid(0, 0);
-        system("su -c /usr/bin/uicache mobile");
-    } else {
-        system("/usr/bin/uicache");
-    }
-
+    system("/usr/bin/uicache");
     _trace();
 }
 
     _trace();
 }
 
@@ -9386,15 +9503,14 @@ _end
             @synchronized (self) {
                 for (Package *broken in (id) broken_) {
                     [broken remove];
             @synchronized (self) {
                 for (Package *broken in (id) broken_) {
                     [broken remove];
-                    NSString *id = [broken id];
-
+                    NSString *id(ShellEscape([broken id]));
                     system([[NSString stringWithFormat:@"/usr/libexec/cydia/cydo /bin/rm -f"
                         " /var/lib/dpkg/info/%@.prerm"
                         " /var/lib/dpkg/info/%@.postrm"
                         " /var/lib/dpkg/info/%@.preinst"
                         " /var/lib/dpkg/info/%@.postinst"
                         " /var/lib/dpkg/info/%@.extrainst_"
                     system([[NSString stringWithFormat:@"/usr/libexec/cydia/cydo /bin/rm -f"
                         " /var/lib/dpkg/info/%@.prerm"
                         " /var/lib/dpkg/info/%@.postrm"
                         " /var/lib/dpkg/info/%@.preinst"
                         " /var/lib/dpkg/info/%@.postinst"
                         " /var/lib/dpkg/info/%@.extrainst_"
-                    , id, id, id, id, id] UTF8String]);
+                    "", id, id, id, id, id] UTF8String]);
                 }
 
                 [self resolve];
                 }
 
                 [self resolve];
@@ -9597,7 +9713,7 @@ _end
             controller = [[[SectionController alloc] initWithDatabase:database_ source:nil section:argument] autorelease];
         }
 
             controller = [[[SectionController alloc] initWithDatabase:database_ source:nil section:argument] autorelease];
         }
 
-        if (!external && [base isEqualToString:@"sources"]) {
+        if ([base isEqualToString:@"sources"]) {
             if ([argument isEqualToString:@"add"]) {
                 controller = [[[SourcesController alloc] initWithDatabase:database_] autorelease];
                 [(SourcesController *)controller showAddSourcePrompt];
             if ([argument isEqualToString:@"add"]) {
                 controller = [[[SourcesController alloc] initWithDatabase:database_] autorelease];
                 [(SourcesController *)controller showAddSourcePrompt];
@@ -9753,14 +9869,7 @@ _end
     UpdateExternalStatus(0);
 
     [self removeStashController];
     UpdateExternalStatus(0);
 
     [self removeStashController];
-
-    pid_t pid(ExecFork());
-    if (pid == 0) {
-        execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
-        perror("launchctl stop");
-
-        exit(0);
-    } ReapZombie(pid);
+    [self reloadSpringBoard];
 }
 
 - (void) setupViewControllers {
 }
 
 - (void) setupViewControllers {
@@ -9859,6 +9968,9 @@ _trace();
     [window_ makeKey:self];
     [window_ setHidden:NO];
 
     [window_ makeKey:self];
     [window_ setHidden:NO];
 
+    if (access("/.cydia_no_stash", F_OK) == 0);
+    else {
+
     if (false) stash: {
         [self addStashController];
         // XXX: this would be much cleaner as a yieldToSelector:
     if (false) stash: {
         [self addStashController];
         // XXX: this would be much cleaner as a yieldToSelector:
@@ -9892,6 +10004,8 @@ _trace();
     Stash_("/usr/share");
     //Stash_("/var/lib");
 
     Stash_("/usr/share");
     //Stash_("/var/lib");
 
+    }
+
     database_ = [Database sharedInstance];
     [database_ setDelegate:self];
 
     database_ = [Database sharedInstance];
     [database_ setDelegate:self];
 
@@ -10005,6 +10119,11 @@ _trace();
 }
 
 - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item {
 }
 
 - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item {
+    if (!IsWildcat_) {
+       [sheet addButtonWithTitle:UCLocalize("CANCEL")];
+       [sheet setCancelButtonIndex:[sheet numberOfButtons] - 1];
+    }
+
     if (item != nil && IsWildcat_) {
         [sheet showFromBarButtonItem:item animated:YES];
     } else {
     if (item != nil && IsWildcat_) {
         [sheet showFromBarButtonItem:item animated:YES];
     } else {
@@ -10098,8 +10217,31 @@ MSHook(id, NSUserDefaults$objectForKey$, NSUserDefaults *self, SEL _cmd, NSStrin
     return _NSUserDefaults$objectForKey$(self, _cmd, key);
 }
 
     return _NSUserDefaults$objectForKey$(self, _cmd, key);
 }
 
+static NSMutableDictionary *AutoreleaseDeepMutableCopyOfDictionary(CFTypeRef type) {
+    if (type == NULL)
+        return nil;
+    if (CFGetTypeID(type) != CFDictionaryGetTypeID())
+        return nil;
+    CFTypeRef copy(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, type, kCFPropertyListMutableContainers));
+    CFRelease(type);
+    return [(NSMutableDictionary *) copy autorelease];
+}
+
+int main_store(int, char *argv[]);
+
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-    setreugid(501, 501);
+#ifdef __arm64__
+    const char *argv0(argv[0]);
+    if (const char *slash = strrchr(argv0, '/'))
+        argv0 = slash + 1;
+    if (false);
+    else if (!strcmp(argv0, "store"))
+        return main_store(argc, argv);
+#endif
+
+    int fd(open("/tmp/cydia.log", O_WRONLY | O_APPEND | O_CREAT, 0644));
+    dup2(fd, 2);
+    close(fd);
 
     NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
 
 
     NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
 
@@ -10173,29 +10315,28 @@ int main(int argc, char *argv[]) {
     Locale_ = CFLocaleCopyCurrent();
     Languages_ = [NSLocale preferredLanguages];
 
     Locale_ = CFLocaleCopyCurrent();
     Languages_ = [NSLocale preferredLanguages];
 
-    //CFStringRef locale(CFLocaleGetIdentifier(Locale_));
-    //NSLog(@"%@", [Languages_ description]);
+    std::string languages;
+    const char *translation(NULL);
 
 
-    const char *lang;
+    // XXX: this isn't really a language, but this is compatible with older Cydia builds
     if (Locale_ != NULL)
     if (Locale_ != NULL)
-        lang = [(NSString *) CFLocaleGetIdentifier(Locale_) UTF8String];
-    else if (Languages_ != nil && [Languages_ count] != 0)
-        lang = [[Languages_ objectAtIndex:0] UTF8String];
-    else
-        // XXX: consider just setting to C and then falling through?
-        lang = NULL;
-
-    if (lang != NULL) {
-        RegEx pattern("([a-z][a-z])(?:-[A-Za-z]*)?(_[A-Z][A-Z])?");
-        lang = !pattern(lang) ? NULL : [pattern->*@"%1$@%2$@" UTF8String];
-    }
+        if (const char *language = [(NSString *) CFLocaleGetIdentifier(Locale_) UTF8String]) {
+            RegEx pattern("([a-z][a-z])(?:-[A-Za-z]*)?(_[A-Z][A-Z])?");
+            if (pattern(language)) {
+                translation = strdup([pattern->*@"%1$@%2$@" UTF8String]);
+                languages += translation;
+                languages += ",";
+            }
+        }
 
 
-    NSLog(@"Setting Language: %s", lang);
+    if (Languages_ != nil)
+        for (NSString *language : Languages_) {
+            languages += [language UTF8String];
+            languages += ",";
+        }
 
 
-    if (lang != NULL) {
-        setenv("LANG", lang, true);
-        std::setlocale(LC_ALL, lang);
-    }
+    languages += "en";
+    NSLog(@"Setting Language: [%s] %s", translation, languages.c_str());
     /* }}} */
     /* Index Collation {{{ */
     if (Class $UILocalizedIndexedCollation = objc_getClass("UILocalizedIndexedCollation")) { @try {
     /* }}} */
     /* Index Collation {{{ */
     if (Class $UILocalizedIndexedCollation = objc_getClass("UILocalizedIndexedCollation")) { @try {
@@ -10276,6 +10417,7 @@ int main(int argc, char *argv[]) {
     Advanced_ = YES;
 
     Cache_ = [[NSString stringWithFormat:@"%@/Library/Caches/com.saurik.Cydia", @"/var/mobile"] retain];
     Advanced_ = YES;
 
     Cache_ = [[NSString stringWithFormat:@"%@/Library/Caches/com.saurik.Cydia", @"/var/mobile"] retain];
+    mkdir([Cache_ UTF8String], 0755);
 
     /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
     alloc_ = alloc->method_imp;
 
     /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
     alloc_ = alloc->method_imp;
@@ -10350,10 +10492,9 @@ int main(int argc, char *argv[]) {
     MetaFile_.Open("/var/mobile/Library/Cydia/metadata.cb0");
     _trace();
 
     MetaFile_.Open("/var/mobile/Library/Cydia/metadata.cb0");
     _trace();
 
-    // XXX: port this to NSUserDefaults when you aren't in such a rush
-    Values_ = [[[(NSDictionary *) CFPreferencesCopyAppValue(CFSTR("CydiaValues"), CFSTR("com.saurik.Cydia")) autorelease] mutableCopy] autorelease];
-    Sections_ = [[[(NSDictionary *) CFPreferencesCopyAppValue(CFSTR("CydiaSections"), CFSTR("com.saurik.Cydia")) autorelease] mutableCopy] autorelease];
-    Sources_ = [[[(NSDictionary *) CFPreferencesCopyAppValue(CFSTR("CydiaSources"), CFSTR("com.saurik.Cydia")) autorelease] mutableCopy] autorelease];
+    Values_ = AutoreleaseDeepMutableCopyOfDictionary(CFPreferencesCopyAppValue(CFSTR("CydiaValues"), CFSTR("com.saurik.Cydia")));
+    Sections_ = AutoreleaseDeepMutableCopyOfDictionary(CFPreferencesCopyAppValue(CFSTR("CydiaSections"), CFSTR("com.saurik.Cydia")));
+    Sources_ = AutoreleaseDeepMutableCopyOfDictionary(CFPreferencesCopyAppValue(CFSTR("CydiaSources"), CFSTR("com.saurik.Cydia")));
     Version_ = [(NSNumber *) CFPreferencesCopyAppValue(CFSTR("CydiaVersion"), CFSTR("com.saurik.Cydia")) autorelease];
 
     _trace();
     Version_ = [(NSNumber *) CFPreferencesCopyAppValue(CFSTR("CydiaVersion"), CFSTR("com.saurik.Cydia")) autorelease];
 
     _trace();
@@ -10404,7 +10545,7 @@ int main(int argc, char *argv[]) {
 
     _H<NSMutableArray> broken([NSMutableArray array]);
     for (NSString *key in (id) Sources_)
 
     _H<NSMutableArray> broken([NSMutableArray array]);
     for (NSString *key in (id) Sources_)
-        if ([key rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"# "]].location != NSNotFound)
+        if ([key rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"# "]].location != NSNotFound || ![([[Sources_ objectForKey:key] objectForKey:@"URI"] ?: @"/") hasSuffix:@"/"])
             [broken addObject:key];
     if ([broken count] != 0)
         for (NSString *key in (id) broken)
             [broken addObject:key];
     if ([broken count] != 0)
         for (NSString *key in (id) broken)
@@ -10417,21 +10558,6 @@ int main(int argc, char *argv[]) {
 
     Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
 
 
     Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
 
-#define MobileSubstrate_(name) \
-    if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", F_OK) == 0) { \
-        void *handle(dlopen("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", RTLD_LAZY | RTLD_GLOBAL)); \
-        if (handle == NULL) \
-            NSLog(@"%s", dlerror()); \
-    }
-
-    MobileSubstrate_(Activator)
-    MobileSubstrate_(libstatusbar)
-    MobileSubstrate_(SimulatedKeyEvents)
-    MobileSubstrate_(WinterBoard)
-
-    /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
-        dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
-
     if (kCFCoreFoundationVersionNumber > 1000)
         system("/usr/libexec/cydia/cydo /usr/libexec/cydia/setnsfpn /var/lib");
 
     if (kCFCoreFoundationVersionNumber > 1000)
         system("/usr/libexec/cydia/cydo /usr/libexec/cydia/setnsfpn /var/lib");
 
@@ -10450,23 +10576,34 @@ int main(int argc, char *argv[]) {
             _assert(errno == ENOENT);
     }
 
             _assert(errno == ENOENT);
     }
 
+    system("/usr/libexec/cydia/cydo /bin/ln -sf /var/mobile/Library/Caches/com.saurik.Cydia/sources.list /etc/apt/sources.list.d/cydia.list");
+
     /* APT Initialization {{{ */
     _assert(pkgInitConfig(*_config));
     _assert(pkgInitSystem(*_config, _system));
 
     /* APT Initialization {{{ */
     _assert(pkgInitConfig(*_config));
     _assert(pkgInitSystem(*_config, _system));
 
-    if (lang != NULL)
-        _config->Set("APT::Acquire::Translation", lang);
+    _config->Set("Acquire::AllowInsecureRepositories", true);
+    _config->Set("Acquire::Check-Valid-Until", false);
+    _config->Set("Dir::Bin::Methods::store", "/Applications/Cydia.app/store");
+
+    _config->Set("pkgCacheGen::ForceEssential", "");
+
+    if (translation != NULL)
+        _config->Set("APT::Acquire::Translation", translation);
+    _config->Set("Acquire::Languages", languages);
 
     // XXX: this timeout might be important :(
     //_config->Set("Acquire::http::Timeout", 15);
 
     _config->Set("Acquire::http::MaxParallel", usermem >= 384 * 1024 * 1024 ? 16 : 3);
 
 
     // XXX: this timeout might be important :(
     //_config->Set("Acquire::http::Timeout", 15);
 
     _config->Set("Acquire::http::MaxParallel", usermem >= 384 * 1024 * 1024 ? 16 : 3);
 
-    mkdir([Cache_ UTF8String], 0755);
     mkdir([Cache("archives") UTF8String], 0755);
     mkdir([Cache("archives/partial") UTF8String], 0755);
     _config->Set("Dir::Cache", [Cache_ UTF8String]);
 
     mkdir([Cache("archives") UTF8String], 0755);
     mkdir([Cache("archives/partial") UTF8String], 0755);
     _config->Set("Dir::Cache", [Cache_ UTF8String]);
 
+    symlink("/var/lib/apt/extended_states", [Cache("extended_states") UTF8String]);
+    _config->Set("Dir::State", [Cache_ UTF8String]);
+
     mkdir([Cache("lists") UTF8String], 0755);
     mkdir([Cache("lists/partial") UTF8String], 0755);
     mkdir([Cache("periodic") UTF8String], 0755);
     mkdir([Cache("lists") UTF8String], 0755);
     mkdir([Cache("lists/partial") UTF8String], 0755);
     mkdir([Cache("periodic") UTF8String], 0755);
@@ -10474,7 +10611,7 @@ int main(int argc, char *argv[]) {
 
     std::string logs("/var/mobile/Library/Logs/Cydia");
     mkdir(logs.c_str(), 0755);
 
     std::string logs("/var/mobile/Library/Logs/Cydia");
     mkdir(logs.c_str(), 0755);
-    _config->Set("Dir::Log::Terminal", logs + "/apt.log");
+    _config->Set("Dir::Log", logs);
 
     _config->Set("Dir::Bin::dpkg", "/usr/libexec/cydia/cydo");
     /* }}} */
 
     _config->Set("Dir::Bin::dpkg", "/usr/libexec/cydia/cydo");
     /* }}} */
@@ -10501,6 +10638,7 @@ int main(int argc, char *argv[]) {
     /* }}} */
 
     $SBSSetInterceptsMenuButtonForever = reinterpret_cast<void (*)(bool)>(dlsym(RTLD_DEFAULT, "SBSSetInterceptsMenuButtonForever"));
     /* }}} */
 
     $SBSSetInterceptsMenuButtonForever = reinterpret_cast<void (*)(bool)>(dlsym(RTLD_DEFAULT, "SBSSetInterceptsMenuButtonForever"));
+    $SBSCopyIconImagePNGDataForDisplayIdentifier = reinterpret_cast<NSData *(*)(NSString *)>(dlsym(RTLD_DEFAULT, "SBSCopyIconImagePNGDataForDisplayIdentifier"));
 
     const char *symbol(kCFCoreFoundationVersionNumber >= 800 ? "MGGetBoolAnswer" : "GSSystemHasCapability");
     BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, symbol));
 
     const char *symbol(kCFCoreFoundationVersionNumber >= 800 ? "MGGetBoolAnswer" : "GSSystemHasCapability");
     BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, symbol));