]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Ghost packages suck.
[cydia.git] / Cydia.mm
index a8474b1fee38b020ae672ede35bf6aaf1aa7ff28..8702e29a2773fe24d67728b8862a8cbb639cc632 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -1,7 +1,9 @@
 /* #include Directives {{{ */
 #include <Foundation/NSURL.h>
 #include <UIKit/UIKit.h>
-#import <GraphicsServices/GraphicsServices.h>
+#include <GraphicsServices/GraphicsServices.h>
+
+#include <objc/objc.h>
 
 #include <sstream>
 #include <ext/stdio_filebuf.h>
 #include <apt-pkg/sptr.h>
 
 #include <sys/sysctl.h>
+#include <notify.h>
 
 extern "C" {
 #include <mach-o/nlist.h>
 }
 
-#include <objc/objc-class.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 #include <errno.h>
 #include <pcre.h>
@@ -39,6 +43,8 @@ extern "C" {
         exit(-1); \
     } \
 while (false)
+
+#define _not(type) ((type) ~ (type) 0)
 /* }}} */
 /* Miscellaneous Messages {{{ */
 @interface WebView
@@ -53,6 +59,12 @@ while (false)
 @end
 /* }}} */
 
+#ifdef SRK_ASPEN
+#define UITable UITableView
+#endif
+
+OBJC_EXPORT const char *class_getName(Class cls);
+
 /* Reset View (UIView) {{{ */
 @interface UIView (CYResetView)
 - (void) resetViewAnimated:(BOOL)animated;
@@ -61,7 +73,7 @@ while (false)
 @implementation UIView (CYResetView)
 
 - (void) resetViewAnimated:(BOOL)animated {
-    fprintf(stderr, "%s\n", self->isa->name);
+    fprintf(stderr, "%s\n", class_getName(self->isa));
     _assert(false);
 }
 
@@ -171,9 +183,20 @@ class GSFont {
 /* }}} */
 
 static const int PulseInterval_ = 50000;
+
+const char *Firmware_ = NULL;
 const char *Machine_ = NULL;
 const char *SerialNumber_ = NULL;
 
+unsigned Major_;
+unsigned Minor_;
+unsigned BugFix_;
+
+#define FW_LEAST(major, minor, bugfix) \
+    (major < Major_ || major == Major_ && \
+        (minor < Minor_ || minor == Minor_ && \
+            bugfix <= BugFix_))
+
 bool bootstrap_ = false;
 
 static NSMutableDictionary *Metadata_;
@@ -448,6 +471,7 @@ inline float interpolate(float begin, float end, float fraction) {
 /* }}} */
 
 @class Package;
+@class Source;
 
 /* Database Interface {{{ */
 @interface Database : NSObject {
@@ -457,6 +481,10 @@ inline float interpolate(float begin, float end, float fraction) {
     pkgAcquire *fetcher_;
     FileFd *lock_;
     SPtr<pkgPackageManager> manager_;
+    pkgSourceList *list_;
+
+    NSMutableDictionary *sources_;
+    NSMutableArray *packages_;
 
     id delegate_;
     Status status_;
@@ -476,6 +504,7 @@ inline float interpolate(float begin, float end, float fraction) {
 - (pkgRecords *) records;
 - (pkgProblemResolver *) resolver;
 - (pkgAcquire &) fetcher;
+- (NSArray *) packages;
 - (void) reloadData;
 
 - (void) prepare;
@@ -484,6 +513,7 @@ inline float interpolate(float begin, float end, float fraction) {
 - (void) upgrade;
 
 - (void) setDelegate:(id)delegate;
+- (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
 @end
 /* }}} */
 
@@ -501,6 +531,7 @@ inline float interpolate(float begin, float end, float fraction) {
 - (void) dealloc;
 
 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item;
+- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
 
 - (id) initWithFrame:(CGRect)frame;
 - (void) setDelegate:(id)delegate;
@@ -537,6 +568,10 @@ inline float interpolate(float begin, float end, float fraction) {
     }
 }
 
+- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
+    [sheet dismiss];
+}
+
 - (id) initWithFrame:(CGRect)frame {
     if ((self = [super initWithFrame:frame]) != nil) {
         views_ = [[NSMutableArray arrayWithCapacity:4] retain];
@@ -579,6 +614,16 @@ inline float interpolate(float begin, float end, float fraction) {
 }
 
 - (void) configurePushed {
+    UIAlertSheet *sheet = [[[UIAlertSheet alloc]
+        initWithTitle:@"Sources Unimplemented"
+        buttons:[NSArray arrayWithObjects:@"Okay", nil]
+        defaultButtonIndex:0
+        delegate:self
+        context:self
+    ] autorelease];
+
+    [sheet setBodyText:@"This feature will be implemented soon. In the mean time, you may add sources by adding .list files to '/etc/apt/sources.list.d' or modifying '/etc/apt/sources.list'."];
+    [sheet popupAlertAnimated:YES];
 }
 
 - (void) reloadPushed {
@@ -682,7 +727,6 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 @end
 
 @implementation ConfirmationView
-#include "internals.h"
 
 - (void) dealloc {
     [transition_ release];
@@ -827,23 +871,31 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
         NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
 
-        bool essential(false);
+        bool install(false);
+        bool upgrade(false);
+        bool remove(false);
 
         pkgCacheFile &cache([database_ cache]);
         for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
             NSString *name([NSString stringWithCString:iterator.Name()]);
-            if (cache[iterator].NewInstall())
+            bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
+
+            if (cache[iterator].NewInstall()) {
+                if (essential)
+                    install = true;
                 [installing addObject:name];
-            else if (cache[iterator].Upgrade())
+            } else if (cache[iterator].Upgrade()) {
+                if (essential)
+                    upgrade = true;
                 [upgrading addObject:name];
-            else if (cache[iterator].Delete()) {
+            } else if (cache[iterator].Delete()) {
+                if (essential)
+                    remove = true;
                 [removing addObject:name];
-                if ((iterator->Flags & pkgCache::Flag::Essential) != 0)
-                    essential = true;
             }
         }
 
-        if (!essential)
+        if (!remove)
             essential_ = nil;
         else {
             essential_ = [[UIAlertSheet alloc]
@@ -885,6 +937,138 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 @end
 /* }}} */
 
+/* Source Class {{{ */
+@interface Source : NSObject {
+    NSString *description_;
+    NSString *label_;
+    NSString *origin_;
+
+    NSString *uri_;
+    NSString *distribution_;
+    NSString *type_;
+    NSString *version_;
+
+    NSString *defaultIcon_;
+
+    BOOL trusted_;
+}
+
+- (void) dealloc;
+
+- (Source *) initWithMetaIndex:(metaIndex *)index;
+
+- (BOOL) trusted;
+
+- (NSString *) uri;
+- (NSString *) distribution;
+- (NSString *) type;
+
+- (NSString *) description;
+- (NSString *) label;
+- (NSString *) origin;
+- (NSString *) version;
+
+- (NSString *) defaultIcon;
+@end
+
+@implementation Source
+
+- (void) dealloc {
+    [uri_ release];
+    [distribution_ release];
+    [type_ release];
+
+    if (description_ != nil)
+        [description_ release];
+    if (label_ != nil)
+        [label_ release];
+    if (origin_ != nil)
+        [origin_ release];
+    if (version_ != nil)
+        [version_ release];
+
+    [super dealloc];
+}
+
+- (Source *) initWithMetaIndex:(metaIndex *)index {
+    if ((self = [super init]) != nil) {
+        trusted_ = index->IsTrusted();
+
+        uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
+        distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
+        type_ = [[NSString stringWithCString:index->GetType()] retain];
+
+        description_ = nil;
+        label_ = nil;
+        origin_ = nil;
+
+        debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
+        if (dindex != NULL) {
+            std::ifstream release(dindex->MetaIndexFile("Release").c_str());
+            std::string line;
+            while (std::getline(release, line)) {
+                std::string::size_type colon(line.find(':'));
+                if (colon == std::string::npos)
+                    continue;
+
+                std::string name(line.substr(0, colon));
+                std::string value(line.substr(colon + 1));
+                while (!value.empty() && value[0] == ' ')
+                    value = value.substr(1);
+
+                if (name == "Default-Icon")
+                    defaultIcon_ = [[NSString stringWithCString:value.c_str()] retain];
+                else if (name == "Description")
+                    description_ = [[NSString stringWithCString:value.c_str()] retain];
+                else if (name == "Label")
+                    label_ = [[NSString stringWithCString:value.c_str()] retain];
+                else if (name == "Origin")
+                    origin_ = [[NSString stringWithCString:value.c_str()] retain];
+                else if (name == "Version")
+                    version_ = [[NSString stringWithCString:value.c_str()] retain];
+            }
+        }
+    } return self;
+}
+
+- (BOOL) trusted {
+    return trusted_;
+}
+
+- (NSString *) uri {
+    return uri_;
+}
+
+- (NSString *) distribution {
+    return distribution_;
+}
+
+- (NSString *) type {
+    return type_;
+}
+
+- (NSString *) description {
+    return description_;
+}
+
+- (NSString *) label {
+    return label_;
+}
+
+- (NSString *) origin {
+    return origin_;
+}
+
+- (NSString *) version {
+    return version_;
+}
+
+- (NSString *) defaultIcon {
+    return defaultIcon_;
+}
+
+@end
+/* }}} */
 /* Package Class {{{ */
 NSString *Scour(const char *field, const char *begin, const char *end) {
     size_t i(0), l(strlen(field));
@@ -919,6 +1103,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     Database *database_;
     pkgCache::VerIterator version_;
     pkgCache::VerFileIterator file_;
+    Source *source_;
 
     NSString *latest_;
     NSString *installed_;
@@ -927,7 +1112,6 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     NSString *name_;
     NSString *tagline_;
     NSString *icon_;
-    NSString *bundle_;
     NSString *website_;
 }
 
@@ -947,14 +1131,17 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 - (NSString *) latest;
 - (NSString *) installed;
 - (BOOL) upgradable;
+- (BOOL) essential;
+- (BOOL) broken;
 
 - (NSString *) id;
 - (NSString *) name;
 - (NSString *) tagline;
 - (NSString *) icon;
-- (NSString *) bundle;
 - (NSString *) website;
 
+- (Source *) source;
+
 - (BOOL) matches:(NSString *)text;
 
 - (NSComparisonResult) compareByName:(Package *)package;
@@ -978,8 +1165,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     [tagline_ release];
     if (icon_ != nil)
         [icon_ release];
-    if (bundle_ != nil)
-        [bundle_ release];
+    if (website_ != nil)
+        [website_ release];
+
+    if (source_ != nil)
+        [source_ release];
+
     [super dealloc];
 }
 
@@ -1007,15 +1198,14 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         icon_ = Scour("Icon", begin, end);
         if (icon_ != nil)
             icon_ = [icon_ retain];
-        bundle_ = Scour("Bundle", begin, end);
-        if (bundle_ != nil)
-            bundle_ = [bundle_ retain];
         website_ = Scour("Website", begin, end);
         if (website_ != nil)
             website_ = [website_ retain];
 
+        source_ = [[database_ getSource:file_.File()] retain];
+
         NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
-        if (metadata == nil) {
+        if (metadata == nil || [metadata count] == 0) {
             metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                 now_, @"FirstSeen",
             nil];
@@ -1038,7 +1228,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (NSString *) section {
-    return [[NSString stringWithCString:iterator_.Section()] stringByReplacingCharacter:'_' withCharacter:' '];
+    const char *section = iterator_.Section();
+    return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
 }
 
 - (Address *) maintainer {
@@ -1069,7 +1260,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (NSString *) index {
-    return [[[self name] substringToIndex:1] uppercaseString];
+    NSString *index = [[[self name] substringToIndex:1] uppercaseString];
+    return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
 }
 
 - (NSDate *) seen {
@@ -1085,8 +1277,18 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (BOOL) upgradable {
-    NSString *installed = [self installed];
-    return installed != nil && [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
+    if (NSString *installed = [self installed])
+        return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
+    else
+        return [self essential];
+}
+
+- (BOOL) essential {
+    return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
+}
+
+- (BOOL) broken {
+    return (*[database_ cache])[iterator_].InstBroken();
 }
 
 - (NSString *) id {
@@ -1105,20 +1307,24 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return icon_;
 }
 
-- (NSString *) bundle {
-    return bundle_;
-}
-
 - (NSString *) website {
     return website_;
 }
 
+- (Source *) source {
+    return source_;
+}
+
 - (BOOL) matches:(NSString *)text {
     if (text == nil)
         return NO;
 
     NSRange range;
 
+    range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
+    if (range.location != NSNotFound)
+        return YES;
+
     range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
     if (range.location != NSNotFound)
         return YES;
@@ -1131,13 +1337,36 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (NSComparisonResult) compareByName:(Package *)package {
-    return [[self name] caseInsensitiveCompare:[package name]];
+    NSString *lhs = [self name];
+    NSString *rhs = [package name];
+
+    if ([lhs length] != 0 && [rhs length] != 0) {
+        unichar lhc = [lhs characterAtIndex:0];
+        unichar rhc = [rhs characterAtIndex:0];
+
+        if (isalpha(lhc) && !isalpha(rhc))
+            return NSOrderedAscending;
+        else if (!isalpha(lhc) && isalpha(rhc))
+            return NSOrderedDescending;
+    }
+
+    return [lhs caseInsensitiveCompare:rhs];
 }
 
 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
-    NSComparisonResult result = [[self section] caseInsensitiveCompare:[package section]];
-    if (result != NSOrderedSame)
-        return result;
+    NSString *lhs = [self section];
+    NSString *rhs = [package section];
+
+    if (lhs == NULL && rhs != NULL)
+        return NSOrderedAscending;
+    else if (lhs != NULL && rhs == NULL)
+        return NSOrderedDescending;
+    else if (lhs != NULL && rhs != NULL) {
+        NSComparisonResult result = [lhs compare:rhs];
+        if (result != NSOrderedSame)
+            return result;
+    }
+
     return [self compareByName:package];
 }
 
@@ -1278,14 +1507,14 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
-    return 2;
+    return 3;
 }
 
 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
     switch (group) {
         case 0: return nil;
-        case 1: return @"Details";
-        case 2: return @"Source";
+        case 1: return @"Package Details";
+        case 2: return @"Source Information";
 
         default: _assert(false);
     }
@@ -1302,7 +1531,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     switch (group) {
         case 0: return [package_ website] == nil ? 2 : 3;
         case 1: return 5;
-        case 2: return 0;
+        case 2: return 3;
 
         default: _assert(false);
     }
@@ -1344,10 +1573,11 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
                 [cell setValue:(installed == nil ? @"n/a" : installed)];
             } break;
 
-            case 2:
+            case 2: {
                 [cell setTitle:@"Section"];
-                [cell setValue:[package_ section]];
-            break;
+                NSString *section([package_ section]);
+                [cell setValue:(section == nil ? @"n/a" : section)];
+            } break;
 
             case 3:
                 [cell setTitle:@"Expanded Size"];
@@ -1365,6 +1595,21 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         } break;
 
         case 2: switch (row) {
+            case 0:
+                [cell setTitle:[[package_ source] label]];
+                [cell setValue:[[package_ source] version]];
+            break;
+
+            case 1:
+                [cell setValue:[[package_ source] description]];
+            break;
+
+            case 2:
+                [cell setTitle:@"Origin"];
+                [cell setValue:[[package_ source] origin]];
+            break;
+
+            default: _assert(false);
         } break;
 
         default: _assert(false);
@@ -1378,14 +1623,16 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (void) tableRowSelected:(NSNotification *)notification {
-    switch ([table_ selectedRow]) {
-        case 8:
-            [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
-                [[package_ maintainer] email],
-                [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
-            ]]];
-        break;
-    }
+    int row = [table_ selectedRow];
+    NSString *website = [package_ website];
+
+    if (row == (website == nil ? 8 : 9))
+        [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
+            [[package_ maintainer] email],
+            [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
+        ]]];
+    else if (website != nil && row == 3)
+        [delegate_ openURL:[NSURL URLWithString:website]];
 }
 
 - (Package *) package {
@@ -1438,9 +1685,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 /* }}} */
 /* Package Cell {{{ */
 @interface PackageCell : UITableCell {
+    UIImageView *icon_;
     UITextLabel *name_;
-    UITextLabel *version_;
     UITextLabel *description_;
+    UITextLabel *source_;
+    UITextLabel *version_;
+    UIImageView *trusted_;
     SEL versioner_;
 }
 
@@ -1459,9 +1709,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 @implementation PackageCell
 
 - (void) dealloc {
+    [icon_ release];
     [name_ release];
-    [version_ release];
     [description_ release];
+    [source_ release];
+    [version_ release];
+    [trusted_ release];
     [super dealloc];
 }
 
@@ -1477,21 +1730,33 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
         CGColor clear(space, 0, 0, 0, 0);
 
-        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 250, 25)];
+        icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
+
+        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 12, 240, 25)];
         [name_ setBackgroundColor:clear];
         [name_ setFont:bold];
 
-        version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(286, 7, 70, 25)];
+        description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
+        [description_ setBackgroundColor:clear];
+        [description_ setFont:small];
+
+        source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 72, 225, 20)];
+        [source_ setBackgroundColor:clear];
+        [source_ setFont:large];
+
+        version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(286, 69, 70, 25)];
         [version_ setBackgroundColor:clear];
         [version_ setFont:large];
 
-        description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
-        [description_ setBackgroundColor:clear];
-        [description_ setFont:small];
+        //trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(278, 7, 16, 16)];
+        trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
+        [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
 
+        [self addSubview:icon_];
         [self addSubview:name_];
-        [self addSubview:version_];
         [self addSubview:description_];
+        [self addSubview:source_];
+        [self addSubview:version_];
 
         CGColorSpaceRelease(space);
 
@@ -1502,9 +1767,43 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (void) setPackage:(Package *)package {
+    Source *source = [package source];
+
+    UIImage *image = nil;
+    if (NSString *icon = [package icon])
+        image = [UIImage imageAtPath:[icon substringFromIndex:6]];
+    if (image == nil) if (NSString *icon = [source defaultIcon])
+        image = [UIImage imageAtPath:[icon substringFromIndex:6]];
+    if (image == nil)
+        image = [UIImage applicationImageNamed:@"unknown.png"];
+
+    [icon_ setImage:image];
+    [icon_ zoomToScale:0.5f];
+    [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
+
     [name_ setText:[package name]];
     [version_ setText:[package latest]];
     [description_ setText:[package tagline]];
+
+    NSString *label;
+    bool trusted;
+
+    if (source != nil) {
+        label = [source label];
+        trusted = [source trusted];
+    } else if ([[package id] isEqualToString:@"firmware"]) {
+        label = @"Apple";
+        trusted = false;
+    } else {
+        label = @"Unknown/Local";
+        trusted = false;
+    }
+
+    [source_ setText:[NSString stringWithFormat:@"from %@", label]];
+
+    [trusted_ removeFromSuperview];
+    if (trusted)
+        [self addSubview:trusted_];
 }
 
 - (void) _setSelected:(float)fraction {
@@ -1529,8 +1828,9 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     1.0);
 
     [name_ setColor:black];
-    [version_ setColor:blue];
     [description_ setColor:gray];
+    [source_ setColor:black];
+    [version_ setColor:blue];
 
     CGColorSpaceRelease(space);
 }
@@ -1630,6 +1930,9 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         fetcher_ = NULL;
         lock_ = NULL;
 
+        sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
+        packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
+
         int fds[2];
 
         _assert(pipe(fds) != -1);
@@ -1669,19 +1972,52 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return *fetcher_;
 }
 
+- (NSArray *) packages {
+    return packages_;
+}
+
 - (void) reloadData {
     _error->Discard();
+    delete list_;
     manager_ = NULL;
     delete lock_;
     delete fetcher_;
     delete resolver_;
     delete records_;
     cache_.Close();
-    _assert(cache_.Open(progress_, true));
+
+    if (!cache_.Open(progress_, true)) {
+        fprintf(stderr, "repairing corrupted database...\n");
+        _error->Discard();
+        [self update];
+        _assert(cache_.Open(progress_, true));
+    }
+
+    now_ = [NSDate date];
+
     records_ = new pkgRecords(cache_);
     resolver_ = new pkgProblemResolver(cache_);
     fetcher_ = new pkgAcquire(&status_);
     lock_ = NULL;
+
+    list_ = new pkgSourceList();
+    _assert(list_->ReadMainList());
+
+    [sources_ removeAllObjects];
+    for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
+        std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
+        for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
+            [sources_
+                setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
+                forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
+            ];
+    }
+
+    [packages_ removeAllObjects];
+    for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
+        if (Package *package = [Package packageWithIterator:iterator database:self])
+            if ([package source] != nil || [package installed] != nil)
+                [packages_ addObject:package];
 }
 
 - (void) prepare {
@@ -1760,6 +2096,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     progress_.setDelegate(delegate);
 }
 
+- (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
+    pkgIndexFile *index(NULL);
+    list_->FindIndex(file, index);
+    return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
+}
+
 @end
 /* }}} */
 
@@ -2128,7 +2470,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (float) table:(UITable *)table heightForRow:(int)row {
-    return 64;
+    return 100;
 }
 
 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
@@ -2246,7 +2588,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         CGColor clear(space, 0, 0, 0, 0);
         CGColor white(space, 1, 1, 1, 1);
 
-        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(47, 9, 250, 25)];
+        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
         [name_ setBackgroundColor:clear];
         [name_ setFont:bold];
 
@@ -2277,7 +2619,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         [name_ setText:@"All Packages"];
         [count_ setText:nil];
     } else {
-        [name_ setText:[section name]];
+        NSString *name = [section name];
+        [name_ setText:(name == nil ? @"(No Section)" : name)];
         [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
     }
 }
@@ -2492,10 +2835,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         Package *package = [packages_ objectAtIndex:offset];
         NSString *name = [package section];
 
-        if (section == nil || ![[section name] isEqual:name]) {
+        if (section == nil || name != nil && ![[section name] isEqual:name]) {
             section = [[[Section alloc] initWithName:name row:offset] autorelease];
 
-            if ([name isEqualToString:section_])
+            if (name == nil || [name isEqualToString:section_])
                 nsection = section;
             [sections addObject:section];
         }
@@ -2520,12 +2863,15 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     else if (package_ != nil)
         ++views;
 
-    if (nsection != nil)
+    if (table_ != nil && section_ == nil) 
+        [table_ setPackages:packages_];
+    else if (nsection != nil)
         [table_ setPackages:[nsection packages]];
     else if (section_ != nil)
         ++views;
 
-    [self popViews:views];
+    if (views != 0)
+        [self popViews:views];
     [self setPrompt];
 }
 
@@ -2601,7 +2947,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (float) table:(UITable *)table heightForRow:(int)row {
-    return 64;
+    return 100;
 }
 
 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
@@ -2711,11 +3057,19 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         else {
             NSDate *seen = [package seen];
 
-            CFLocaleRef locale = CFLocaleCopyCurrent();
-            CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
-            CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
-
-            NSString *name = (NSString *) formatted;
+            NSString *name;
+            CFStringRef formatted = NULL;
+
+            if (seen == nil)
+                name = @"n/a ?";
+            else {
+                CFLocaleRef locale = CFLocaleCopyCurrent();
+                CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
+                formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
+                name = (NSString *) formatted;
+                CFRelease(formatter);
+                CFRelease(locale);
+            }
 
             if (section == nil || ![[section name] isEqual:name]) {
                 section = [[[Section alloc] initWithName:name row:offset] autorelease];
@@ -2724,9 +3078,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
             [section addPackage:package];
 
-            CFRelease(formatter);
-            CFRelease(formatted);
-            CFRelease(locale);
+            if (formatted != NULL)
+                CFRelease(formatted);
         }
     }
 
@@ -3140,7 +3493,6 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 @end
 
 @implementation Cydia
-#include "internals.h"
 
 - (void) loadNews {
     NSMutableURLRequest *request = [NSMutableURLRequest
@@ -3149,6 +3501,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         timeoutInterval:30.0
     ];
 
+    [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
     [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
     [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
 
@@ -3165,12 +3518,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         [Metadata_ setObject:Packages_ forKey:@"Packages"];
     }
 
-    now_ = [NSDate date];
-
-    NSMutableArray *packages = [NSMutableArray arrayWithCapacity:count];
-    for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator)
-        if (Package *package = [Package packageWithIterator:iterator database:database_])
-            [packages addObject:package];
+    NSArray *packages = [database_ packages];
 
     [install_ setPackages:packages];
     [changes_ setPackages:packages];
@@ -3180,11 +3528,13 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     if (size_t count = [changes_ count]) {
         NSString *badge([[NSNumber numberWithInt:count] stringValue]);
         [buttonbar_ setBadgeValue:badge forButton:3];
-        [buttonbar_ setBadgeAnimated:YES forButton:3];
+        if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
+            [buttonbar_ setBadgeAnimated:YES forButton:3];
         [self setApplicationBadge:badge];
     } else {
         [buttonbar_ setBadgeValue:nil forButton:3];
-        [buttonbar_ setBadgeAnimated:NO forButton:3];
+        if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
+            [buttonbar_ setBadgeAnimated:NO forButton:3];
         [self removeApplicationBadge];
     }
 
@@ -3205,7 +3555,32 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
 - (void) perform {
     [database_ prepare];
-    confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
+
+    if ([database_ cache]->BrokenCount() == 0)
+        confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
+    else {
+        NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
+        NSArray *packages = [database_ packages];
+
+        for (size_t i(0); i != [packages count]; ++i) {
+            Package *package = [packages objectAtIndex:i];
+            if ([package broken])
+                [broken addObject:[package name]];
+        }
+
+        UIAlertSheet *sheet = [[[UIAlertSheet alloc]
+            initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
+            buttons:[NSArray arrayWithObjects:@"Okay", nil]
+            defaultButtonIndex:0
+            delegate:self
+            context:self
+        ] autorelease];
+
+        [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
+        [sheet popupAlertAnimated:YES];
+
+        [self reloadData:NO];
+    }
 }
 
 - (void) upgrade {
@@ -3350,7 +3725,11 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
 - (void) applicationWillSuspend {
     if (restart_)
-        system("launchctl stop com.apple.SpringBoard");
+        if (FW_LEAST(1,1,3))
+            notify_post("com.apple.language.changed");
+        else
+            system("launchctl stop com.apple.SpringBoard");
+
     [super applicationWillSuspend];
 }
 
@@ -3543,7 +3922,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     [self reloadData:NO];
 
     Package *package([database_ packageWithName:@"cydia"]);
-    NSString *application = package == nil ? @"Cydia" : [NSString stringWithFormat:@"Cydia/%@", [package installed]];
+    NSString *application = package == nil ? @"Cydia" : [NSString
+        stringWithFormat:@"Cydia/%@",
+        [package installed]
+    ];
 
     WebView *webview = [webview_ webView];
     [webview setApplicationNameForUserAgent:application];
@@ -3634,6 +4016,17 @@ int main(int argc, char *argv[]) {
 
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
+    if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
+        if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
+            Firmware_ = strdup([prover cString]);
+            NSArray *versions = [prover componentsSeparatedByString:@"."];
+            int count = [versions count];
+            Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
+            Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
+            BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
+        }
+    }
+
     size_t size;
     sysctlbyname("hw.machine", NULL, &size, NULL, 0);
     char *machine = new char[size];
@@ -3650,14 +4043,19 @@ int main(int argc, char *argv[]) {
             IOObjectRelease(service);
         }
 
-    AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
-    AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");
+    /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
+    AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
 
     if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
         Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
     else
         Packages_ = [Metadata_ objectForKey:@"Packages"];
 
+    setenv("CYDIA", "", _not(int));
+    if (access("/User", F_OK) != 0)
+        system("/usr/libexec/cydia/firmware.sh");
+    system("dpkg --configure -a");
+
     UIApplicationMain(argc, argv, [Cydia class]);
     [pool release];
     return 0;