]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Replaced Sources icon.
[cydia.git] / Cydia.mm
index fd83416b2928ed7a5a3af79fc44c93c08efa1a5f..a8ef907db817e78a40d130dd5accc814a57a2050 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -1,4 +1,5 @@
 /* #include Directives {{{ */
+#include <Foundation/NSURL.h>
 #include <UIKit/UIKit.h>
 #import <GraphicsServices/GraphicsServices.h>
 
 #include <apt-pkg/algorithms.h>
 #include <apt-pkg/cachefile.h>
 #include <apt-pkg/configuration.h>
+#include <apt-pkg/debmetaindex.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/init.h>
 #include <apt-pkg/pkgrecords.h>
 #include <apt-pkg/sourcelist.h>
 #include <apt-pkg/sptr.h>
 
+#include <sys/sysctl.h>
+
 #include <errno.h>
 #include <pcre.h>
 #include <string.h>
 while (false)
 /* }}} */
 
+@interface WebView
+- (void) setApplicationNameForUserAgent:(NSString *)applicationName;
+@end
+
+static const int PulseInterval_ = 50000;
+const char *Machine_ = NULL;
+const char *SerialNumber_ = NULL;
+
+@interface NSString (CydiaBypass)
+- (NSString *) stringByAddingPercentEscapes;
+@end
+
 @protocol ProgressDelegate
 - (void) setError:(NSString *)error;
 - (void) setTitle:(NSString *)title;
@@ -38,6 +54,18 @@ while (false)
 - (void) addOutput:(NSString *)output;
 @end
 
+NSString *SizeString(double size) {
+    unsigned power = 0;
+    while (size > 1024) {
+        size /= 1024;
+        ++power;
+    }
+
+    static const char *powers_[] = {"B", "kB", "MB", "GB"};
+
+    return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
+}
+
 /* Status Delegation {{{ */
 class Status :
     public pkgAcquireStatus
@@ -60,19 +88,23 @@ class Status :
     }
 
     virtual void IMSHit(pkgAcquire::ItemDesc &item) {
-        [delegate_ performSelectorOnMainThread:@selector(setStatusIMSHit) withObject:nil waitUntilDone:YES];
     }
 
     virtual void Fetch(pkgAcquire::ItemDesc &item) {
-        [delegate_ setTitle:[NSString stringWithCString:item.Description.c_str()]];
+        [delegate_ setTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
     }
 
     virtual void Done(pkgAcquire::ItemDesc &item) {
-        [delegate_ performSelectorOnMainThread:@selector(setStatusDone) withObject:nil waitUntilDone:YES];
     }
 
     virtual void Fail(pkgAcquire::ItemDesc &item) {
-        [delegate_ performSelectorOnMainThread:@selector(setStatusFail) withObject:nil waitUntilDone:YES];
+        if (
+            item.Owner->Status == pkgAcquire::Item::StatIdle ||
+            item.Owner->Status == pkgAcquire::Item::StatDone
+        )
+            return;
+
+        [delegate_ setError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
     }
 
     virtual bool Pulse(pkgAcquire *Owner) {
@@ -155,7 +187,8 @@ extern NSString *kUIButtonBarButtonType;
 
 - (void) dealloc {
     [name_ release];
-    [email_ release];
+    if (email_ != nil)
+        [email_ release];
     [super dealloc];
 }
 
@@ -190,10 +223,13 @@ extern NSString *kUIButtonBarButtonType;
         size_t size = [string length];
         const char *data = [string UTF8String];
 
-        _assert(pcre_exec(code, study, data, size, 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
-
-        name_ = [[NSString stringWithCString:(data + matches[2]) length:(matches[3] - matches[2])] retain];
-        email_ = [[NSString stringWithCString:(data + matches[4]) length:(matches[5] - matches[4])] retain];
+        if (pcre_exec(code, study, data, size, 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0) {
+            name_ = [[NSString stringWithCString:(data + matches[2]) length:(matches[3] - matches[2])] retain];
+            email_ = [[NSString stringWithCString:(data + matches[4]) length:(matches[5] - matches[4])] retain];
+        } else {
+            name_ = [[NSString stringWithCString:data length:size] retain];
+            email_ = nil;
+        }
     } return self;
 }
 
@@ -248,10 +284,15 @@ inline float interpolate(float begin, float end, float fraction) {
 }
 /* }}} */
 
+@class Package;
+
 @interface Database : NSObject {
     pkgCacheFile cache_;
     pkgRecords *records_;
     pkgProblemResolver *resolver_;
+    pkgAcquire *fetcher_;
+    FileFd *lock_;
+    SPtr<pkgPackageManager> manager_;
 
     id delegate_;
     Status status_;
@@ -259,12 +300,19 @@ inline float interpolate(float begin, float end, float fraction) {
     int statusfd_;
 }
 
+- (void) _readStatus:(NSNumber *)fd;
+- (void) _readOutput:(NSNumber *)fd;
+
+- (Package *) packageWithName:(NSString *)name;
+
 - (Database *) init;
 - (pkgCacheFile &) cache;
 - (pkgRecords *) records;
 - (pkgProblemResolver *) resolver;
+- (pkgAcquire &) fetcher;
 - (void) reloadData;
 
+- (void) prepare;
 - (void) perform;
 - (void) update;
 - (void) upgrade;
@@ -272,6 +320,317 @@ inline float interpolate(float begin, float end, float fraction) {
 - (void) setDelegate:(id)delegate;
 @end
 
+/* Reset View {{{ */
+@interface ResetView : UIView {
+    UINavigationBar *navbar_;
+    bool resetting_;
+}
+
+- (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item;
+
+- (void) dealloc;
+- (void) resetView;
+- (void) _resetView;
+- (NSString *) leftTitle;
+- (NSString *) rightTitle;
+@end
+
+@implementation ResetView
+
+- (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
+    if ([[navbar_ navigationItems] count] == 1)
+        [self _resetView];
+}
+
+- (void) dealloc {
+    [navbar_ release];
+    [super dealloc];
+}
+
+- (void) resetView {
+    resetting_ = true;
+    if ([[navbar_ navigationItems] count] == 1)
+        [self _resetView];
+    else while ([[navbar_ navigationItems] count] != 1)
+        [navbar_ popNavigationItem];
+    resetting_ = false;
+}
+
+- (void) _resetView {
+    [navbar_ showButtonsWithLeftTitle:[self leftTitle] rightTitle:[self rightTitle]];
+}
+
+- (NSString *) leftTitle {
+    return nil;
+}
+
+- (NSString *) rightTitle {
+    return nil;
+}
+
+@end
+/* }}} */
+/* Confirmation View {{{ */
+void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
+    if ([packages count] == 0)
+        return;
+
+    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
+    float clear[] = {0, 0, 0, 0};
+    float blue[] = {0, 0, 0.4, 1};
+
+    UITextView *text([[[UITextView alloc] initWithFrame: CGRectMake(110, 3, 200, 60)] autorelease]);
+    [text setEditable:NO];
+    [text setTextSize:16];
+    [text setBackgroundColor:CGColorCreate(space, clear)];
+    [text setTextColor:CGColorCreate(space, blue)];
+    [text setText:([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "])];
+    [text setEnabled:NO];
+
+    CGRect frame([text frame]);
+    CGSize size([text contentSize]);
+    frame.size.height = size.height;
+    [text setFrame:frame];
+
+    [fields setObject:text forKey:key];
+}
+
+@protocol ConfirmationViewDelegate
+- (void) cancel;
+- (void) confirm;
+@end
+
+@interface ConfirmationView : UIView {
+    Database *database_;
+    id delegate_;
+    UITransitionView *transition_;
+    UIView *overlay_;
+    UINavigationBar *navbar_;
+    UIPreferencesTable *table_;
+    NSMutableDictionary *fields_;
+    UIAlertSheet *essential_;
+}
+
+- (void) dealloc;
+- (void) cancel;
+
+- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
+- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
+- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
+
+- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
+- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
+- (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed;
+- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
+- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
+
+- (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
+
+@end
+
+@implementation ConfirmationView
+
+- (void) dealloc {
+    [transition_ release];
+    [overlay_ release];
+    [navbar_ release];
+    [table_ release];
+    [fields_ release];
+    if (essential_ != nil)
+        [essential_ release];
+    [super dealloc];
+}
+
+- (void) cancel {
+    [transition_ transition:7 toView:nil];
+    [delegate_ cancel];
+}
+
+- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
+    if (from != nil && to == nil)
+        [self removeFromSuperview];
+}
+
+- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
+    switch (button) {
+        case 0:
+            if (essential_ != nil)
+                [essential_ popupAlertAnimated:YES];
+            else
+                [delegate_ confirm];
+        break;
+
+        case 1:
+            [self cancel];
+        break;
+    }
+}
+
+- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
+    [essential_ dismiss];
+    [self cancel];
+}
+
+- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
+    return 2;
+}
+
+- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
+    switch (group) {
+        case 0: return @"Statistics";
+        case 1: return @"Modifications";
+
+        default: _assert(false);
+    }
+}
+
+- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
+    switch (group) {
+        case 0: return 3;
+        case 1: return [fields_ count];
+
+        default: _assert(false);
+    }
+}
+
+- (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
+    if (group != 1 || row == -1)
+        return proposed;
+    else {
+        _assert(size_t(row) < [fields_ count]);
+        return [[[fields_ allValues] objectAtIndex:row] contentSize].height;
+    }
+}
+
+- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
+    UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
+    [cell setShowSelection:NO];
+
+    switch (group) {
+        case 0: switch (row) {
+            case 0: {
+                [cell setTitle:@"Downloading"];
+                [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
+            } break;
+
+            case 1: {
+                [cell setTitle:@"Resuming At"];
+                [cell setValue:SizeString([database_ fetcher].PartialPresent())];
+            } break;
+
+            case 2: {
+                double size([database_ cache]->UsrSize());
+
+                if (size < 0) {
+                    [cell setTitle:@"Disk Freeing"];
+                    [cell setValue:SizeString(-size)];
+                } else {
+                    [cell setTitle:@"Disk Using"];
+                    [cell setValue:SizeString(size)];
+                }
+            } break;
+
+            default: _assert(false);
+        } break;
+
+        case 1:
+            _assert(size_t(row) < [fields_ count]);
+            [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
+            [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
+        break;
+
+        default: _assert(false);
+    }
+
+    return cell;
+}
+
+- (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
+    if ((self = [super initWithFrame:[view bounds]]) != nil) {
+        database_ = database;
+        delegate_ = delegate;
+
+        transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
+        [self addSubview:transition_];
+
+        overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
+
+        CGSize navsize = [UINavigationBar defaultSize];
+        CGRect navrect = {{0, 0}, navsize};
+        CGRect bounds = [overlay_ bounds];
+
+        navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
+        [navbar_ setBarStyle:1];
+        [navbar_ setDelegate:self];
+
+        UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
+        [navbar_ pushNavigationItem:navitem];
+        [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
+
+        fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
+
+        NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
+        NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
+        NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
+
+        bool essential(false);
+
+        pkgCacheFile &cache([database_ cache]);
+        for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
+            NSString *name([NSString stringWithCString:iterator.Name()]);
+            if (cache[iterator].NewInstall())
+                [installing addObject:name];
+            else if (cache[iterator].Upgrade())
+                [upgrading addObject:name];
+            else if (cache[iterator].Delete()) {
+                [removing addObject:name];
+                if ((iterator->Flags & pkgCache::Flag::Essential) != 0)
+                    essential = true;
+            }
+        }
+
+        if (!essential)
+            essential_ = nil;
+        else {
+            essential_ = [[UIAlertSheet alloc]
+                initWithTitle:@"Unable to Comply"
+                buttons:[NSArray arrayWithObjects:@"Okay", nil]
+                defaultButtonIndex:0
+                delegate:self
+                context:self
+            ];
+
+            [essential_ setBodyText:@"One or more of the packages you are about to remove are marked 'Essential' and cannot be removed by Cydia. Please use apt-get."];
+        }
+
+        AddTextView(fields_, installing, @"Installing");
+        AddTextView(fields_, upgrading, @"Upgrading");
+        AddTextView(fields_, removing, @"Removing");
+
+        table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
+            0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
+        )];
+
+        [table_ setReusesTableCells:YES];
+        [table_ setDataSource:self];
+        [table_ reloadData];
+
+        [overlay_ addSubview:navbar_];
+        [overlay_ addSubview:table_];
+
+        [view addSubview:self];
+
+        [transition_ setDelegate:self];
+
+        UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
+        [transition_ transition:0 toView:blank];
+        [transition_ transition:3 toView:overlay_];
+    } return self;
+}
+
+@end
+/* }}} */
+
 /* Package Class {{{ */
 @interface Package : NSObject {
     pkgCache::PkgIterator iterator_;
@@ -286,8 +645,8 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (NSString *) name;
 - (NSString *) section;
-- (BOOL) installed;
-- (NSString *) version;
+- (NSString *) latest;
+- (NSString *) installed;
 - (Address *) maintainer;
 - (size_t) size;
 - (NSString *) tagline;
@@ -324,19 +683,19 @@ inline float interpolate(float begin, float end, float fraction) {
 }
 
 - (NSString *) name {
-    return [NSString stringWithCString:iterator_.Name()];
+    return [[NSString stringWithCString:iterator_.Name()] lowercaseString];
 }
 
 - (NSString *) section {
     return [NSString stringWithCString:iterator_.Section()];
 }
 
-- (BOOL) installed {
-    return iterator_->CurrentState != pkgCache::State::NotInstalled;
+- (NSString *) latest {
+    return [NSString stringWithCString:version_.VerStr()];
 }
 
-- (NSString *) version {
-    return [NSString stringWithCString:version_.VerStr()];
+- (NSString *) installed {
+    return iterator_.CurrentVer().end() ? nil : [NSString stringWithCString:iterator_.CurrentVer().VerStr()];
 }
 
 - (Address *) maintainer {
@@ -425,30 +784,28 @@ inline float interpolate(float begin, float end, float fraction) {
 @end
 /* }}} */
 
-/* Confirmation View {{{ */
-@interface ConfirmationView : UIView {
-}
-
-@end
-
-@implementation ConfirmationView
-@end
-/* }}} */
 /* Package View {{{ */
-@interface PackageView : UIPreferencesTable {
+@interface PackageView : UIView {
+    UIPreferencesTable *table_;
     Package *package_;
     Database *database_;
     NSMutableArray *cells_;
+    id delegate_;
 }
 
 - (void) dealloc;
 
 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
+- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
 
-- (PackageView *) initWithFrame:(struct CGRect)frame database:(Database *)database;
+- (BOOL) canSelectRow:(int)row;
+- (void) tableRowSelected:(NSNotification *)notification;
+
+- (id) initWithFrame:(struct CGRect)frame database:(Database *)database;
 - (void) setPackage:(Package *)package;
+- (void) setDelegate:(id)delegate;
 @end
 
 @implementation PackageView
@@ -456,6 +813,7 @@ inline float interpolate(float begin, float end, float fraction) {
 - (void) dealloc {
     if (package_ != nil)
         [package_ release];
+    [table_ release];
     [database_ release];
     [cells_ release];
     [super dealloc];
@@ -467,13 +825,8 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
     switch (group) {
-        case 0:
-            return @"Specifics";
-        break;
-
-        case 1:
-            return @"Description";
-        break;
+        case 0: return @"Specifics";
+        case 1: return @"Description";
 
         default: _assert(false);
     }
@@ -481,13 +834,8 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
     switch (group) {
-        case 0:
-            return 5;
-        break;
-
-        case 1:
-            return 1;
-        break;
+        case 0: return 6;
+        case 1: return 1;
 
         default: _assert(false);
     }
@@ -504,33 +852,33 @@ inline float interpolate(float begin, float end, float fraction) {
                 [cell setValue:[package_ name]];
             break;
 
-            case 1:
+            case 1: {
                 cell = [cells_ objectAtIndex:1];
-                [cell setTitle:@"Version"];
-                [cell setValue:[package_ version]];
-            break;
+                [cell setTitle:@"Installed"];
+                NSString *installed([package_ installed]);
+                [cell setValue:(installed == nil ? @"n/a" : installed)];
+            } break;
 
             case 2:
                 cell = [cells_ objectAtIndex:2];
-                [cell setTitle:@"Section"];
-                [cell setValue:[package_ section]];
+                [cell setTitle:@"Latest"];
+                [cell setValue:[package_ latest]];
             break;
 
-            case 3: {
-                double size = [package_ size];
-                unsigned power = 0;
-                while (size > 1024) {
-                    size /= 1024;
-                    ++power;
-                }
-
+            case 3:
                 cell = [cells_ objectAtIndex:3];
-                [cell setTitle:@"Size"];
-                [cell setValue:[NSString stringWithFormat:@"%.1f%c", size, "bkMG"[power]]];
-            break;
+                [cell setTitle:@"Section"];
+                [cell setValue:[package_ section]];
+            break;
 
             case 4:
                 cell = [cells_ objectAtIndex:4];
+                [cell setTitle:@"Size"];
+                [cell setValue:SizeString([package_ size])];
+            break;
+
+            case 5:
+                cell = [cells_ objectAtIndex:5];
                 [cell setTitle:@"Maintainer"];
                 [cell setValue:[[package_ maintainer] name]];
                 [cell setShowDisclosure:YES];
@@ -542,13 +890,13 @@ inline float interpolate(float begin, float end, float fraction) {
 
         case 1: switch (row) {
             case 0:
-                cell = [cells_ objectAtIndex:5];
+                cell = [cells_ objectAtIndex:6];
                 [cell setTitle:nil];
                 [cell setValue:[package_ tagline]];
             break;
 
             case 1:
-                cell = [cells_ objectAtIndex:6];
+                cell = [cells_ objectAtIndex:7];
                 [cell setTitle:@"Description"];
                 [cell setValue:[package_ description]];
             break;
@@ -560,15 +908,34 @@ inline float interpolate(float begin, float end, float fraction) {
     return cell;
 }
 
-- (PackageView *) initWithFrame:(struct CGRect)frame database:(Database *)database {
+- (BOOL) canSelectRow:(int)row {
+    return YES;
+}
+
+- (void) tableRowSelected:(NSNotification *)notification {
+    switch ([table_ selectedRow]) {
+        case 5:
+            [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
+                [[package_ maintainer] email],
+                [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
+            ]]];
+        break;
+    }
+}
+
+- (id) initWithFrame:(struct CGRect)frame database:(Database *)database {
     if ((self = [super initWithFrame:frame]) != nil) {
         database_ = [database retain];
-        [self setDataSource:self];
+
+        table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
+        [self addSubview:table_];
+
+        [table_ setDataSource:self];
+        [table_ setDelegate:self];
 
         cells_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
-        for (unsigned i = 0; i != 6; ++i) {
-            struct CGRect frame = [self frameOfPreferencesCellAtRow:0 inGroup:0];
+        for (unsigned i = 0; i != 8; ++i) {
             UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
             [cell setShowSelection:NO];
             [cells_ addObject:cell];
@@ -578,21 +945,31 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (void) setPackage:(Package *)package {
     package_ = [package retain];
-    [self reloadData];
+    [table_ reloadData];
+}
+
+- (void) setDelegate:(id)delegate {
+    delegate_ = delegate;
 }
 
 @end
 /* }}} */
 /* Package Cell {{{ */
+@protocol PackageCellDelegate
+- (NSString *) versionWithPackage:(Package *)package;
+@end
+
 @interface PackageCell : UITableCell {
     UITextLabel *name_;
     UIRightTextLabel *version_;
     UITextLabel *description_;
+    id delegate_;
 }
 
 - (void) dealloc;
 
-- (PackageCell *) initWithPackage:(Package *)package;
+- (PackageCell *) initWithDelegate:(id)delegate;
+- (void) setPackage:(Package *)package;
 
 - (void) _setSelected:(float)fraction;
 - (void) setSelected:(BOOL)selected;
@@ -610,8 +987,10 @@ inline float interpolate(float begin, float end, float fraction) {
     [super dealloc];
 }
 
-- (PackageCell *) initWithPackage:(Package *)package {
+- (PackageCell *) initWithDelegate:(id)delegate {
     if ((self = [super init]) != nil) {
+        delegate_ = delegate;
+
         GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
         GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
         GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
@@ -620,70 +999,472 @@ inline float interpolate(float begin, float end, float fraction) {
         float clear[] = {0, 0, 0, 0};
 
         name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 250, 25)];
-        [name_ setText:[package name]];
         [name_ setBackgroundColor:CGColorCreate(space, clear)];
         [name_ setFont:bold];
 
-        version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 7, 70, 25)];
-        [version_ setText:[package version]];
-        [version_ setBackgroundColor:CGColorCreate(space, clear)];
-        [version_ setFont:large];
+        version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 7, 70, 25)];
+        [version_ setBackgroundColor:CGColorCreate(space, clear)];
+        [version_ setFont:large];
+
+        description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
+        [description_ setBackgroundColor:CGColorCreate(space, clear)];
+        [description_ setFont:small];
+
+        [self addSubview:name_];
+        [self addSubview:version_];
+        [self addSubview:description_];
+
+        CFRelease(small);
+        CFRelease(large);
+        CFRelease(bold);
+    } return self;
+}
+
+- (void) setPackage:(Package *)package {
+    [name_ setText:[package name]];
+    [version_ setText:[delegate_ versionWithPackage:package]];
+    [description_ setText:[package tagline]];
+}
+
+- (void) _setSelected:(float)fraction {
+    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
+
+    float black[] = {
+        interpolate(0.0, 1.0, fraction),
+        interpolate(0.0, 1.0, fraction),
+        interpolate(0.0, 1.0, fraction),
+    1.0};
+
+    float blue[] = {
+        interpolate(0.2, 1.0, fraction),
+        interpolate(0.2, 1.0, fraction),
+        interpolate(1.0, 1.0, fraction),
+    1.0};
+
+    float gray[] = {
+        interpolate(0.4, 1.0, fraction),
+        interpolate(0.4, 1.0, fraction),
+        interpolate(0.4, 1.0, fraction),
+    1.0};
+
+    [name_ setColor:CGColorCreate(space, black)];
+    [version_ setColor:CGColorCreate(space, blue)];
+    [description_ setColor:CGColorCreate(space, gray)];
+}
+
+- (void) setSelected:(BOOL)selected {
+    [self _setSelected:(selected ? 1.0 : 0.0)];
+    [super setSelected:selected];
+}
+
+- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
+    if (!fade)
+        [self _setSelected:(selected ? 1.0 : 0.0)];
+    [super setSelected:selected withFade:fade];
+}
+
+- (void) _setSelectionFadeFraction:(float)fraction {
+    [self _setSelected:fraction];
+    [super _setSelectionFadeFraction:fraction];
+}
+
+@end
+/* }}} */
+
+/* Source {{{ */
+@interface Source : NSObject {
+    NSString *description_;
+    NSString *label_;
+    NSString *origin_;
+
+    NSString *uri_;
+    NSString *distribution_;
+    NSString *type_;
+
+    BOOL trusted_;
+}
+
+- (void) dealloc;
+
+- (Source *) initWithMetaIndex:(metaIndex *)index;
+
+- (BOOL) trusted;
+
+- (NSString *) uri;
+- (NSString *) distribution;
+- (NSString *) type;
+
+- (NSString *) description;
+- (NSString *) label;
+- (NSString *) origin;
+@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];
+
+    [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 == "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];
+            }
+        }
+    } 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_;
+}
+
+@end
+/* }}} */
+/* Source Cell {{{ */
+@interface SourceCell : UITableCell {
+    UITextLabel *description_;
+    UIRightTextLabel *label_;
+    UITextLabel *origin_;
+}
+
+- (void) dealloc;
+
+- (SourceCell *) initWithSource:(Source *)source;
+
+- (void) _setSelected:(float)fraction;
+- (void) setSelected:(BOOL)selected;
+- (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
+- (void) _setSelectionFadeFraction:(float)fraction;
+
+@end
+
+@implementation SourceCell
+
+- (void) dealloc {
+    [description_ release];
+    [label_ release];
+    [origin_ release];
+    [super dealloc];
+}
+
+- (SourceCell *) initWithSource:(Source *)source {
+    if ((self = [super init]) != nil) {
+        GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
+        GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
+
+        CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
+        float clear[] = {0, 0, 0, 0};
+
+        NSString *description = [source description];
+        if (description == nil)
+            description = [source uri];
+
+        description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 270, 25)];
+        [description_ setBackgroundColor:CGColorCreate(space, clear)];
+        [description_ setFont:bold];
+        [description_ setText:description];
+
+        NSString *label = [source label];
+        if (label == nil)
+            label = [source type];
+
+        label_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 32, 90, 25)];
+        [label_ setBackgroundColor:CGColorCreate(space, clear)];
+        [label_ setFont:small];
+        [label_ setText:label];
+
+        NSString *origin = [source origin];
+        if (origin == nil)
+            origin = [source distribution];
+
+        origin_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
+        [origin_ setBackgroundColor:CGColorCreate(space, clear)];
+        [origin_ setFont:small];
+        [origin_ setText:origin];
+
+        [self addSubview:description_];
+        [self addSubview:label_];
+        [self addSubview:origin_];
+
+        CFRelease(small);
+        CFRelease(bold);
+    } return self;
+}
+
+- (void) _setSelected:(float)fraction {
+    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
+
+    float black[] = {
+        interpolate(0.0, 1.0, fraction),
+        interpolate(0.0, 1.0, fraction),
+        interpolate(0.0, 1.0, fraction),
+    1.0};
+
+    float blue[] = {
+        interpolate(0.2, 1.0, fraction),
+        interpolate(0.2, 1.0, fraction),
+        interpolate(1.0, 1.0, fraction),
+    1.0};
+
+    float gray[] = {
+        interpolate(0.4, 1.0, fraction),
+        interpolate(0.4, 1.0, fraction),
+        interpolate(0.4, 1.0, fraction),
+    1.0};
+
+    [description_ setColor:CGColorCreate(space, black)];
+    [label_ setColor:CGColorCreate(space, blue)];
+    [origin_ setColor:CGColorCreate(space, gray)];
+}
+
+- (void) setSelected:(BOOL)selected {
+    [self _setSelected:(selected ? 1.0 : 0.0)];
+    [super setSelected:selected];
+}
+
+- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
+    if (!fade)
+        [self _setSelected:(selected ? 1.0 : 0.0)];
+    [super setSelected:selected withFade:fade];
+}
+
+- (void) _setSelectionFadeFraction:(float)fraction {
+    [self _setSelected:fraction];
+    [super _setSelectionFadeFraction:fraction];
+}
+
+@end
+/* }}} */
+/* Sources View {{{ */
+@interface SourcesView : ResetView {
+    UISectionList *list_;
+    Database *database_;
+    id delegate_;
+    NSMutableArray *sources_;
+    UIAlertSheet *alert_;
+}
+
+- (int) numberOfSectionsInSectionList:(UISectionList *)list;
+- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
+- (int) sectionList:(UISectionList *)list rowForSection:(int)section;
+
+- (int) numberOfRowsInTable:(UITable *)table;
+- (float) table:(UITable *)table heightForRow:(int)row;
+- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col;
+- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
+- (void) tableRowSelected:(NSNotification*)notification;
+
+- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
+
+- (void) dealloc;
+- (id) initWithFrame:(CGRect)frame database:(Database *)database;
+- (void) setDelegate:(id)delegate;
+- (void) reloadData;
+- (NSString *) leftTitle;
+- (NSString *) rightTitle;
+@end
+
+@implementation SourcesView
+
+- (int) numberOfSectionsInSectionList:(UISectionList *)list {
+    return 1;
+}
+
+- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
+    return @"sources";
+}
+
+- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
+    return 0;
+}
+
+- (int) numberOfRowsInTable:(UITable *)table {
+    return [sources_ count];
+}
+
+- (float) table:(UITable *)table heightForRow:(int)row {
+    return 64;
+}
+
+- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
+    return [[[SourceCell alloc] initWithSource:[sources_ objectAtIndex:row]] autorelease];
+}
+
+- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
+    return NO;
+}
+
+- (void) tableRowSelected:(NSNotification*)notification {
+    UITable *table([list_ table]);
+    int row([table selectedRow]);
+    if (row == INT_MAX)
+        return;
+
+    [table selectRow:-1 byExtendingSelection:NO withFade:YES];
+}
+
+- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
+    [alert_ dismiss];
+    [alert_ release];
+    alert_ = nil;
+}
+
+- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
+    switch (button) {
+        case 0:
+            alert_ = [[UIAlertSheet alloc]
+                initWithTitle:@"Unimplemented"
+                buttons:[NSArray arrayWithObjects:@"Okay", nil]
+                defaultButtonIndex:0
+                delegate:self
+                context:self
+            ];
+
+            [alert_ 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'. If you'd like to be in the default list, please contact the author of Packager."];
+            [alert_ popupAlertAnimated:YES];
+        break;
+
+        case 1:
+            [delegate_ update];
+        break;
+    }
+}
+
+- (void) dealloc {
+    if (sources_ != nil)
+        [sources_ release];
+    [list_ release];
+    [super dealloc];
+}
+
+- (id) initWithFrame:(CGRect)frame database:(Database *)database {
+    if ((self = [super initWithFrame:frame]) != nil) {
+        database_ = database;
+        sources_ = nil;
+
+        CGSize navsize = [UINavigationBar defaultSize];
+        CGRect navrect = {{0, 0}, navsize};
+        CGRect bounds = [self bounds];
+
+        navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
+        [self addSubview:navbar_];
+
+        [navbar_ setBarStyle:1];
+        [navbar_ setDelegate:self];
+
+        UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Sources"] autorelease];
+        [navbar_ pushNavigationItem:navitem];
+
+        list_ = [[UISectionList alloc] initWithFrame:CGRectMake(
+            0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
+        )];
 
-        description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
-        [description_ setText:[package tagline]];
-        [description_ setBackgroundColor:CGColorCreate(space, clear)];
-        [description_ setFont:small];
+        [self addSubview:list_];
 
-        [self addSubview:name_];
-        [self addSubview:version_];
-        [self addSubview:description_];
+        [list_ setDataSource:self];
+        [list_ setShouldHideHeaderInShortLists:NO];
 
-        CFRelease(small);
-        CFRelease(large);
-        CFRelease(bold);
+        UITableColumn *column = [[UITableColumn alloc]
+            initWithTitle:@"Name"
+            identifier:@"name"
+            width:frame.size.width
+        ];
+
+        UITable *table = [list_ table];
+        [table setSeparatorStyle:1];
+        [table addTableColumn:column];
+        [table setDelegate:self];
     } return self;
 }
 
-- (void) _setSelected:(float)fraction {
-    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
-
-    float black[] = {
-        interpolate(0.0, 1.0, fraction),
-        interpolate(0.0, 1.0, fraction),
-        interpolate(0.0, 1.0, fraction),
-    1.0};
+- (void) setDelegate:(id)delegate {
+    delegate_ = delegate;
+}
 
-    float blue[] = {
-        interpolate(0.2, 1.0, fraction),
-        interpolate(0.2, 1.0, fraction),
-        interpolate(1.0, 1.0, fraction),
-    1.0};
+- (void) reloadData {
+    pkgSourceList list;
+    _assert(list.ReadMainList());
 
-    float gray[] = {
-        interpolate(0.4, 1.0, fraction),
-        interpolate(0.4, 1.0, fraction),
-        interpolate(0.4, 1.0, fraction),
-    1.0};
+    if (sources_ != nil)
+        [sources_ release];
 
-    [name_ setColor:CGColorCreate(space, black)];
-    [version_ setColor:CGColorCreate(space, blue)];
-    [description_ setColor:CGColorCreate(space, gray)];
-}
+    sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
+    for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
+        [sources_ addObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]];
 
-- (void) setSelected:(BOOL)selected {
-    [self _setSelected:(selected ? 1.0 : 0.0)];
-    [super setSelected:selected];
+    [self resetView];
+    [list_ reloadData];
 }
 
-- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
-    if (!fade)
-        [self _setSelected:(selected ? 1.0 : 0.0)];
-    [super setSelected:selected withFade:fade];
+- (NSString *) leftTitle {
+    return @"Refresh All";
 }
 
-- (void) _setSelectionFadeFraction:(float)fraction {
-    [self _setSelected:fraction];
-    [super _setSelectionFadeFraction:fraction];
+- (NSString *) rightTitle {
+    return @"Edit";
 }
 
 @end
@@ -709,23 +1490,24 @@ inline float interpolate(float begin, float end, float fraction) {
 
     while (std::getline(is, line)) {
         const char *data(line.c_str());
-        fprintf(stderr, "fd(%s)\n", data);
 
         _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
 
-        std::string type(line.substr(matches[2], matches[3] - matches[2]));
-
         std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
         float percent;
         buffer >> percent;
         [delegate_ setPercent:(percent / 100)];
 
         NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
+        std::string type(line.substr(matches[2], matches[3] - matches[2]));
 
         if (type == "pmerror")
             [delegate_ setError:string];
         else if (type == "pmstatus")
             [delegate_ setTitle:string];
+        else if (type == "pmconffile")
+            ;
+        else _assert(false);
     }
 
     [pool release];
@@ -744,15 +1526,21 @@ inline float interpolate(float begin, float end, float fraction) {
     [pool release];
 }
 
+- (Package *) packageWithName:(NSString *)name {
+    pkgCache::PkgIterator iterator(cache_->FindPkg([name cString]));
+    return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
+}
+
 - (Database *) init {
     if ((self = [super init]) != nil) {
         records_ = NULL;
         resolver_ = NULL;
+        fetcher_ = NULL;
+        lock_ = NULL;
 
         int fds[2];
 
         _assert(pipe(fds) != -1);
-        printf("%d %d\n", fds[0], fds[1]);
         statusfd_ = fds[1];
 
         [NSThread
@@ -762,7 +1550,6 @@ inline float interpolate(float begin, float end, float fraction) {
         ];
 
         _assert(pipe(fds) != -1);
-        printf("%d %d\n", fds[0], fds[1]);
         _assert(dup2(fds[1], 1) != -1);
         _assert(close(fds[1]) != -1);
 
@@ -786,47 +1573,46 @@ inline float interpolate(float begin, float end, float fraction) {
     return resolver_;
 }
 
+- (pkgAcquire &) fetcher {
+    return *fetcher_;
+}
+
 - (void) reloadData {
-    _trace();
     _error->Discard();
-    _trace();
+    manager_ = NULL;
+    delete lock_;
+    delete fetcher_;
     delete resolver_;
-    _trace();
     delete records_;
-    _trace();
     cache_.Close();
-    _trace();
     cache_.Open(progress_, true);
-    _trace();
     records_ = new pkgRecords(cache_);
-    _trace();
     resolver_ = new pkgProblemResolver(cache_);
-    _trace();
+    fetcher_ = new pkgAcquire(&status_);
+    lock_ = NULL;
 }
 
-- (void) perform {
-    _trace();
+- (void) prepare {
     pkgRecords records(cache_);
 
-    _trace();
-    FileFd lock;
-    lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
+    lock_ = new FileFd();
+    lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
     _assert(!_error->PendingError());
 
-    _trace();
-    pkgAcquire fetcher(&status_);
     pkgSourceList list;
     _assert(list.ReadMainList());
 
-    _trace();
-    SPtr<pkgPackageManager> manager(_system->CreatePM(cache_));
-    _assert(manager->GetArchives(&fetcher, &list, &records));
+    manager_ = (_system->CreatePM(cache_));
+    _assert(manager_->GetArchives(fetcher_, &list, &records));
     _assert(!_error->PendingError());
-    _assert(fetcher.Run() != pkgAcquire::Failed);
+}
+
+- (void) perform {
+    if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
+        return;
 
-    _trace();
     _system->UnLock();
-    pkgPackageManager::OrderResult result = manager->DoInstall(statusfd_);
+    pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
 
     if (result == pkgPackageManager::Failed)
         return;
@@ -846,7 +1632,7 @@ inline float interpolate(float begin, float end, float fraction) {
 
     pkgAcquire fetcher(&status_);
     _assert(list.GetIndexes(&fetcher));
-    _assert(fetcher.Run() != pkgAcquire::Failed);
+    _assert(fetcher.Run(PulseInterval_) != pkgAcquire::Failed);
 
     bool failed = false;
     for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
@@ -872,8 +1658,6 @@ inline float interpolate(float begin, float end, float fraction) {
     }
 
     _assert(pkgDistUpgrade(cache_));
-
-    //InstallPackages(cache_, true);
 }
 
 - (void) setDelegate:(id)delegate {
@@ -927,6 +1711,7 @@ inline float interpolate(float begin, float end, float fraction) {
     ProgressDelegate
 > {
     UIView *view_;
+    UIView *background_;
     UITransitionView *transition_;
     UIView *overlay_;
     UINavigationBar *navbar_;
@@ -934,7 +1719,6 @@ inline float interpolate(float begin, float end, float fraction) {
     UITextView *output_;
     UITextLabel *status_;
     id delegate_;
-    UIAlertSheet *alert_;
 }
 
 - (void) dealloc;
@@ -960,13 +1744,6 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (void) addOutput:(NSString *)output;
 - (void) _addOutput:(NSString *)output;
-
-- (void) setStatusIMSHit;
-- (void) setStatusDone;
-- (void) setStatusFail;
-
-- (void) setStatusStart;
-- (void) setStatusStop;
 @end
 
 @protocol ProgressViewDelegate
@@ -977,6 +1754,7 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (void) dealloc {
     [view_ release];
+    [background_ release];
     [transition_ release];
     [overlay_ release];
     [navbar_ release];
@@ -989,18 +1767,20 @@ inline float interpolate(float begin, float end, float fraction) {
 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
     if ((self = [super initWithFrame:frame]) != nil) {
         delegate_ = delegate;
-        alert_ = nil;
-
-        transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
-        [self addSubview:transition_];
 
         CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
         float black[] = {0.0, 0.0, 0.0, 1.0};
         float white[] = {1.0, 1.0, 1.0, 1.0};
         float clear[] = {0.0, 0.0, 0.0, 0.0};
 
+        background_ = [[UIView alloc] initWithFrame:[self bounds]];
+        [background_ setBackgroundColor:CGColorCreate(space, black)];
+        [self addSubview:background_];
+
+        transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
+        [self addSubview:transition_];
+
         overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
-        [overlay_ setBackgroundColor:CGColorCreate(space, black)];
 
         CGSize navsize = [UINavigationBar defaultSize];
         CGRect navrect = {{0, 0}, navsize};
@@ -1036,6 +1816,7 @@ inline float interpolate(float begin, float end, float fraction) {
         [status_ setBackgroundColor:CGColorCreate(space, clear)];
 
         [status_ setCentersHorizontally:YES];
+        //[status_ setFont:font];
 
         output_ = [[UITextView alloc] initWithFrame:CGRectMake(
             10,
@@ -1066,31 +1847,22 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (void) resetView {
     [transition_ transition:6 toView:view_];
-    _trace();
 }
 
 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
-    [alert_ dismiss];
-    [alert_ release];
-    alert_ = nil;
+    [sheet dismiss];
 }
 
 - (void) _retachThread {
-    _trace();
     [delegate_ progressViewIsComplete:self];
-    _trace();
     [self resetView];
-    _trace();
 }
 
 - (void) _detachNewThreadData:(ProgressData *)data {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
-    _trace();
     [[data target] performSelector:[data selector] withObject:[data object]];
-    _trace();
     [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
-    _trace();
 
     [data release];
     [pool release];
@@ -1114,18 +1886,6 @@ inline float interpolate(float begin, float end, float fraction) {
     ];
 }
 
-- (void) setStatusIMSHit {
-    _trace();
-}
-
-- (void) setStatusDone {
-    _trace();
-}
-
-- (void) setStatusFail {
-    _trace();
-}
-
 - (void) setError:(NSString *)error {
     [self
         performSelectorOnMainThread:@selector(_setError:)
@@ -1135,18 +1895,16 @@ inline float interpolate(float begin, float end, float fraction) {
 }
 
 - (void) _setError:(NSString *)error {
-    _assert(alert_ == nil);
-
-    alert_ = [[UIAlertSheet alloc]
+    UIAlertSheet *sheet = [[[UIAlertSheet alloc]
         initWithTitle:@"Package Error"
         buttons:[NSArray arrayWithObjects:@"Okay", nil]
         defaultButtonIndex:0
         delegate:self
         context:self
-    ];
+    ] autorelease];
 
-    [alert_ setBodyText:error];
-    [alert_ popupAlertAnimated:YES];
+    [sheet setBodyText:error];
+    [sheet popupAlertAnimated:YES];
 }
 
 - (void) setTitle:(NSString *)title {
@@ -1183,38 +1941,33 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (void) _addOutput:(NSString *)output {
     [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
-}
-
-- (void) setStatusStart {
-    _trace();
-}
-
-- (void) setStatusStop {
-    _trace();
+    CGSize size = [output_ contentSize];
+    CGRect rect = {{0, size.height}, {size.width, 0}};
+    [output_ scrollRectToVisible:rect animated:YES];
 }
 
 @end
 /* }}} */
 
-@protocol PackagesDelegate
-
+@protocol PackagesViewDelegate
 - (void) perform;
-
+- (void) update;
+- (void) openURL:(NSString *)url;
 @end
 
-@interface Packages : UIView {
-    NSString *title_;
+/* PackagesView {{{ */
+@interface PackagesView : ResetView <
+    PackageCellDelegate
+> {
     Database *database_;
-    bool (*filter_)(Package *package);
     NSMutableArray *packages_;
     NSMutableArray *sections_;
     id delegate_;
     UISectionList *list_;
-    UINavigationBar *navbar_;
     UITransitionView *transition_;
     Package *package_;
+    NSString *pkgname_;
     PackageView *pkgview_;
-    SEL selector_;
 }
 
 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
@@ -1223,20 +1976,26 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (int) numberOfRowsInTable:(UITable *)table;
 - (float) table:(UITable *)table heightForRow:(int)row;
-- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col;
+- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
 - (void) tableRowSelected:(NSNotification*)notification;
 
 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item;
 
-- (Packages *) initWithFrame:(struct CGRect)frame title:(NSString *)title database:(Database *)database filter:(bool (*)(Package *))filter selector:(SEL)selector;
+- (id) initWithFrame:(struct CGRect)frame database:(Database *)database;
 - (void) setDelegate:(id)delegate;
 - (void) deselect;
-- (void) reloadData;
+- (void) reloadData:(BOOL)reset;
+
+- (NSMutableArray *) packages;
+- (NSString *) title;
+- (void) perform:(Package *)package;
+- (void) addPackage:(Package *)package;
+- (NSString *) versionWithPackage:(Package *)package;
 @end
 
-@implementation Packages
+@implementation PackagesView
 
 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
     return [sections_ count];
@@ -1258,10 +2017,11 @@ inline float interpolate(float begin, float end, float fraction) {
     return 64;
 }
 
-- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
-    Package *package = [packages_ objectAtIndex:row];
-    PackageCell *cell = [[[PackageCell alloc] initWithPackage:package] autorelease];
-    return cell;
+- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
+    if (reusing == nil)
+        reusing = [[PackageCell alloc] initWithDelegate:self];
+    [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
+    return reusing;
 }
 
 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
@@ -1274,11 +2034,12 @@ inline float interpolate(float begin, float end, float fraction) {
         return;
 
     package_ = [packages_ objectAtIndex:row];
+    pkgname_ = [[package_ name] retain];
 
     UINavigationItem *navitem = [[UINavigationItem alloc] initWithTitle:[package_ name]];
     [navbar_ pushNavigationItem:navitem];
 
-    [navbar_ showButtonsWithLeftTitle:nil rightTitle:title_];
+    [navbar_ showButtonsWithLeftTitle:nil rightTitle:[self title]];
 
     [pkgview_ setPackage:package_];
     [transition_ transition:1 toView:pkgview_];
@@ -1286,7 +2047,7 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
     if (button == 0) {
-        [package_ performSelector:selector_];
+        [self perform:package_];
 
         pkgProblemResolver *resolver = [database_ resolver];
 
@@ -1300,15 +2061,12 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
     [self deselect];
-    [navbar_ showButtonsWithLeftTitle:nil rightTitle:nil];
+    [super navigationBar:navbar poppedItem:item];
 }
 
-- (Packages *) initWithFrame:(struct CGRect)frame title:(NSString *)title database:(Database *)database filter:(bool (*)(Package *))filter selector:(SEL)selector {
+- (id) initWithFrame:(struct CGRect)frame database:(Database *)database {
     if ((self = [super initWithFrame:frame]) != nil) {
-        title_ = [title retain];
         database_ = [database retain];
-        filter_ = filter;
-        selector_ = selector;
 
         struct CGRect bounds = [self bounds];
         CGSize navsize = [UINavigationBar defaultSize];
@@ -1320,7 +2078,7 @@ inline float interpolate(float begin, float end, float fraction) {
         [navbar_ setBarStyle:1];
         [navbar_ setDelegate:self];
 
-        UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
+        UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:[self title]] autorelease];
         [navbar_ pushNavigationItem:navitem];
         [navitem setBackButtonTitle:@"Packages"];
 
@@ -1332,6 +2090,7 @@ inline float interpolate(float begin, float end, float fraction) {
 
         list_ = [[UISectionList alloc] initWithFrame:[transition_ bounds] showSectionIndex:NO];
         [list_ setDataSource:self];
+        [list_ setShouldHideHeaderInShortLists:NO];
 
         [transition_ transition:0 toView:list_];
 
@@ -1345,6 +2104,7 @@ inline float interpolate(float begin, float end, float fraction) {
         [table setSeparatorStyle:1];
         [table addTableColumn:column];
         [table setDelegate:self];
+        [table setReusesTableCells:YES];
 
         pkgview_ = [[PackageView alloc] initWithFrame:[transition_ bounds] database:database_];
     } return self;
@@ -1352,83 +2112,198 @@ inline float interpolate(float begin, float end, float fraction) {
 
 - (void) setDelegate:(id)delegate {
     delegate_ = delegate;
+    [pkgview_ setDelegate:delegate];
 }
 
 - (void) deselect {
-    [transition_ transition:2 toView:list_];
+    [transition_ transition:(resetting_ ? 0 : 2) toView:list_];
     UITable *table = [list_ table];
-    [table selectRow:-1 byExtendingSelection:NO withFade:YES];
+    [table selectRow:-1 byExtendingSelection:NO withFade:(resetting_ ? NO : YES)];
     package_ = nil;
 }
 
-- (void) reloadData {
-    packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
-
-    _trace();
-    if (sections_ != nil) {
+- (void) reloadData:(BOOL)reset {
+    if (sections_ != nil)
         [sections_ release];
-        sections_ = nil;
-    }
+    if (packages_ != nil)
+        [packages_ release];
 
-    _trace();
-    for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator) {
-        Package *package = [Package packageWithIterator:iterator database:database_];
-        if (package == nil)
-            continue;
-        if (filter_(package))
-            [packages_ addObject:package];
-    }
+    packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
+
+    for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator)
+        if (Package *package = [Package packageWithIterator:iterator database:database_])
+            [self addPackage:package];
 
-    _trace();
     [packages_ sortUsingSelector:@selector(compareBySectionAndName:)];
     sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
-    _trace();
     Section *section = nil;
-    _trace();
     for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
-        _trace();
         Package *package = [packages_ objectAtIndex:offset];
-        _trace();
         NSString *name = [package section];
 
-        _trace();
         if (section == nil || ![[section name] isEqual:name]) {
             section = [[Section alloc] initWithName:name row:offset];
             [sections_ addObject:section];
         }
 
-        _trace();
         [section addPackage:package];
-        _trace();
     }
 
-    _trace();
     [list_ reloadData];
-    _trace();
-    if (package_ != nil)
-        [navbar_ popNavigationItem];
-    _trace();
+    if (reset)
+        [self resetView];
+    else if (package_ != nil) {
+        package_ = [database_ packageWithName:pkgname_];
+        [pkgview_ setPackage:package_];
+    }
+}
+
+- (NSMutableArray *) packages {
+    return packages_;
+}
+
+- (NSString *) title {
+    return nil;
+}
+
+- (void) perform:(Package *)package {
+}
+
+- (void) addPackage:(Package *)package {
+    [packages_ addObject:package];
+}
+
+- (NSString *) versionWithPackage:(Package *)package {
+    return nil;
+}
+
+@end
+/* }}} */
+
+/* InstallView {{{ */
+@interface InstallView : PackagesView {
 }
 
+- (NSString *) title;
+- (void) addPackage:(Package *)package;
+- (void) perform:(Package *)package;
+- (NSString *) versionWithPackage:(Package *)package;
 @end
 
-bool IsInstalled(Package *package) {
-    return [package installed];
+@implementation InstallView
+
+- (NSString *) title {
+    return @"Install";
+}
+
+- (void) addPackage:(Package *)package {
+    if ([package installed] == nil)
+        [super addPackage:package];
+}
+
+- (void) perform:(Package *)package {
+    [package install];
+}
+
+- (NSString *) versionWithPackage:(Package *)package {
+    return [package latest];
+}
+
+@end
+/* }}} */
+/* UpgradeView {{{ */
+@interface UpgradeView : PackagesView {
+}
+
+- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
+
+- (NSString *) title;
+- (NSString *) leftTitle;
+- (void) addPackage:(Package *)package;
+- (void) perform:(Package *)package;
+- (NSString *) versionWithPackage:(Package *)package;
+@end
+
+@implementation UpgradeView
+
+- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
+    if (button != 1)
+        [super navigationBar:navbar buttonClicked:button];
+    else {
+        [database_ upgrade];
+        [delegate_ perform];
+    }
+}
+
+- (NSString *) title {
+    return @"Upgrade";
+}
+
+- (NSString *) leftTitle {
+    return [packages_ count] == 0 ? nil : @"Upgrade All";
+}
+
+- (void) addPackage:(Package *)package {
+    NSString *installed = [package installed];
+    if (installed != nil && [[package latest] compare:installed] != NSOrderedSame)
+        [super addPackage:package];
+}
+
+- (void) perform:(Package *)package {
+    [package install];
+}
+
+- (NSString *) versionWithPackage:(Package *)package {
+    return [package latest];
+}
+
+@end
+/* }}} */
+/* UninstallView {{{ */
+@interface UninstallView : PackagesView {
+}
+
+- (NSString *) title;
+- (void) addPackage:(Package *)package;
+- (void) perform:(Package *)package;
+- (NSString *) versionWithPackage:(Package *)package;
+@end
+
+@implementation UninstallView
+
+- (NSString *) title {
+    return @"Uninstall";
+}
+
+- (void) addPackage:(Package *)package {
+    if ([package installed] != nil)
+        [super addPackage:package];
+}
+
+- (void) perform:(Package *)package {
+    [package remove];
 }
 
-bool IsNotInstalled(Package *package) {
-    return ![package installed];
+- (NSString *) versionWithPackage:(Package *)package {
+    return [package installed];
 }
 
+@end
+/* }}} */
+
 @interface Cydia : UIApplication <
-    PackagesDelegate,
+    ConfirmationViewDelegate,
+    PackagesViewDelegate,
     ProgressViewDelegate
 > {
     UIWindow *window_;
+    UIView *underlay_;
+    UIView *overlay_;
     UITransitionView *transition_;
     UIButtonBar *buttonbar_;
-    UIAlertSheet *alert_;
+
+    ConfirmationView *confirm_;
 
     Database *database_;
     ProgressView *progress_;
@@ -1438,14 +2313,20 @@ bool IsNotInstalled(Package *package) {
     UIScroller *scroller_;
     UIWebView *webview_;
     NSURL *url_;
+    UIProgressIndicator *indicator_;
 
-    Packages *install_;
-    Packages *uninstall_;
+    InstallView *install_;
+    UpgradeView *upgrade_;
+    UninstallView *uninstall_;
+    SourcesView *sources_;
 }
 
 - (void) loadNews;
-- (void) reloadData;
+- (void) reloadData:(BOOL)reset;
 - (void) perform;
+- (void) cancel;
+- (void) confirm;
+- (void) update;
 
 - (void) progressViewIsComplete:(ProgressView *)progress;
 
@@ -1453,34 +2334,62 @@ bool IsNotInstalled(Package *package) {
 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
 - (void) buttonBarItemTapped:(id)sender;
 
-- (void) view:(UIView *)sender didSetFrame:(CGRect)frame;
-- (void) view:(UIView *)view didDrawInRect:(CGRect)rect duration:(float)duration;
+- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old;
 
 - (void) applicationDidFinishLaunching:(id)unused;
 @end
 
+#include <objc/objc-class.h>
+
 @implementation Cydia
 
 - (void) loadNews {
-    [webview_ loadRequest:[NSURLRequest
+    NSMutableURLRequest *request = [NSMutableURLRequest
         requestWithURL:url_
         cachePolicy:NSURLRequestReloadIgnoringCacheData
         timeoutInterval:30.0
-    ]];
+    ];
+
+    [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
+    [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
+
+    [webview_ loadRequest:request];
+    [indicator_ startAnimation];
 }
 
-- (void) reloadData {
-    _trace();
+- (void) reloadData:(BOOL)reset {
     [database_ reloadData];
-    _trace();
-    [install_ reloadData];
-    _trace();
-    [uninstall_ reloadData];
-    _trace();
+    [install_ reloadData:reset];
+    [upgrade_ reloadData:reset];
+    [uninstall_ reloadData:reset];
+    [sources_ reloadData];
+
+    if (size_t count = [[upgrade_ packages] count]) {
+        NSString *badge([[NSNumber numberWithInt:count] stringValue]);
+        [buttonbar_ setBadgeValue:badge forButton:3];
+        [buttonbar_ setBadgeAnimated:YES forButton:3];
+        [self setApplicationBadge:badge];
+    } else {
+        [buttonbar_ setBadgeValue:nil forButton:3];
+        [buttonbar_ setBadgeAnimated:NO forButton:3];
+        [self removeApplicationBadge];
+    }
 }
 
 - (void) perform {
-    _trace();
+    [database_ prepare];
+    confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
+}
+
+- (void) cancel {
+    [self reloadData:NO];
+    [confirm_ release];
+    confirm_ = nil;
+}
+
+- (void) confirm {
+    [overlay_ removeFromSuperview];
+
     [progress_
         detachNewThreadSelector:@selector(perform)
         toTarget:database_
@@ -1488,10 +2397,23 @@ bool IsNotInstalled(Package *package) {
     ];
 }
 
+- (void) update {
+    [progress_
+        detachNewThreadSelector:@selector(update)
+        toTarget:database_
+        withObject:nil
+    ];
+}
+
 - (void) progressViewIsComplete:(ProgressView *)progress {
-    _trace();
-    [self reloadData];
-    _trace();
+    [self reloadData:YES];
+
+    if (confirm_ != nil) {
+        [underlay_ addSubview:overlay_];
+        [confirm_ removeFromSuperview];
+        [confirm_ release];
+        confirm_ = nil;
+    }
 }
 
 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
@@ -1501,17 +2423,15 @@ bool IsNotInstalled(Package *package) {
         break;
 
         case 1:
-            _assert(alert_ == nil);
-
-            alert_ = [[UIAlertSheet alloc]
+            UIAlertSheet *sheet = [[[UIAlertSheet alloc]
                 initWithTitle:@"About Cydia Packager"
                 buttons:[NSArray arrayWithObjects:@"Close", nil]
                 defaultButtonIndex:0
                 delegate:self
                 context:self
-            ];
+            ] autorelease];
 
-            [alert_ setBodyText:
+            [sheet setBodyText:
                 @"Copyright (C) 2007\n"
                 "Jay Freeman (saurik)\n"
                 "saurik@saurik.com\n"
@@ -1532,15 +2452,13 @@ bool IsNotInstalled(Package *package) {
                 "lounger, rockabilly, tman, Wbiggs"
             ];
 
-            [alert_ presentSheetFromButtonBar:buttonbar_];
+            [sheet presentSheetFromButtonBar:buttonbar_];
         break;
     }
 }
 
 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
-    [alert_ dismiss];
-    [alert_ release];
-    alert_ = nil;
+    [sheet dismiss];
 }
 
 - (void) buttonBarItemTapped:(id)sender {
@@ -1549,27 +2467,30 @@ bool IsNotInstalled(Package *package) {
     switch ([sender tag]) {
         case 1: view = featured_; break;
         case 2: view = install_; break;
+        case 3: view = upgrade_; break;
         case 4: view = uninstall_; break;
+        case 5: view = sources_; break;
 
         default:
             _assert(false);
     }
 
+    if ([view respondsToSelector:@selector(resetView)])
+        [(id) view resetView];
     [transition_ transition:0 toView:view];
 }
 
-- (void) view:(UIView *)view didSetFrame:(CGRect)frame {
+- (void) view:(UIView *)view didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
     [scroller_ setContentSize:frame.size];
-}
-
-- (void) view:(UIView *)view didDrawInRect:(CGRect)rect duration:(float)duration {
-    [scroller_ setContentSize:[webview_ bounds].size];
+    [indicator_ stopAnimation];
 }
 
 - (void) applicationDidFinishLaunching:(id)unused {
     _assert(pkgInitConfig(*_config));
     _assert(pkgInitSystem(*_config, _system));
 
+    confirm_ = nil;
+
     CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
     window_ = [[UIWindow alloc] initWithContentRect:screenrect];
 
@@ -1580,14 +2501,17 @@ bool IsNotInstalled(Package *package) {
     progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
     [window_ setContentView:progress_];
 
-    UIView *view = [[UIView alloc] initWithFrame:[progress_ bounds]];
-    [progress_ setContentView:view];
+    underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
+    [progress_ setContentView:underlay_];
+
+    overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
+    [underlay_ addSubview:overlay_];
 
     transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
         0, 0, screenrect.size.width, screenrect.size.height - 48
     )];
 
-    [view addSubview:transition_];
+    [overlay_ addSubview:transition_];
 
     featured_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
 
@@ -1631,8 +2555,10 @@ bool IsNotInstalled(Package *package) {
     [webview_ setAutoresizes:YES];
     [webview_ setDelegate:self];
 
-    url_ = [NSURL URLWithString:@"http://cydia.saurik.com/"];
-    [self loadNews];
+    CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
+    indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(87, 15, indsize.width, indsize.height)];
+    [indicator_ setStyle:2];
+    [featured_ addSubview:indicator_];
 
     NSArray *buttonitems = [NSArray arrayWithObjects:
         [NSDictionary dictionaryWithObjectsAndKeys:
@@ -1687,7 +2613,7 @@ bool IsNotInstalled(Package *package) {
     nil];
 
     buttonbar_ = [[UIButtonBar alloc]
-        initInView:view
+        initInView:overlay_
         withFrame:CGRectMake(
             0, screenrect.size.height - 48,
             screenrect.size.width, 48
@@ -1711,47 +2637,56 @@ bool IsNotInstalled(Package *package) {
     [buttonbar_ showSelectionForButton:1];
     [transition_ transition:0 toView:featured_];
 
-    [view addSubview:buttonbar_];
+    [overlay_ addSubview:buttonbar_];
 
     database_ = [[Database alloc] init];
     [database_ setDelegate:progress_];
 
-    install_ = [[Packages alloc] initWithFrame:[transition_ bounds] title:@"Install" database:database_ filter:&IsNotInstalled selector:@selector(install)];
+    install_ = [[InstallView alloc] initWithFrame:[transition_ bounds] database:database_];
     [install_ setDelegate:self];
 
-    uninstall_ = [[Packages alloc] initWithFrame:[transition_ bounds] title:@"Uninstall" database:database_ filter:&IsInstalled selector:@selector(remove)];
-    [uninstall_ setDelegate:self];
-
-#if 0
+    upgrade_ = [[UpgradeView alloc] initWithFrame:[transition_ bounds] database:database_];
+    [upgrade_ setDelegate:self];
 
-    UIAlertSheet *alert = [[UIAlertSheet alloc]
-        initWithTitle:@"Alert Title"
-        buttons:[NSArray arrayWithObjects:@"Yes", nil]
-        defaultButtonIndex:0
-        delegate:self
-        context:self
-    ];
+    uninstall_ = [[UninstallView alloc] initWithFrame:[transition_ bounds] database:database_];
+    [uninstall_ setDelegate:self];
 
-    NSLog(@"%p\n", [alert table]);
-    [[alert table] setDelegate:self];
-    [[alert table] reloadData];
+    sources_ = [[SourcesView alloc] initWithFrame:[transition_ bounds] database:database_];
+    [sources_ setDelegate:self];
 
-    [alert addTextFieldWithValue:@"Title" label:@"Label"];
-    [alert setShowsOverSpringBoardAlerts:YES];
-    [alert setBodyText:@"This is an alert."];
-    [alert presentSheetFromButtonBar:buttonbar_];
-    //[alert popupAlertAnimated:YES];
+    [self reloadData:NO];
+    [progress_ resetView];
 
-#endif
+    Package *package([database_ packageWithName:@"cydia"]);
+    NSString *application = package == nil ? @"Cydia" : [NSString stringWithFormat:@"Cydia/%@", [package installed]];
+    WebView *webview = [webview_ webView];
+    [webview setApplicationNameForUserAgent:application];
 
-    [self reloadData];
-    [progress_ resetView];
+    url_ = [NSURL URLWithString:@"http://cydia.saurik.com/"];
+    [self loadNews];
 }
 
 @end
 
 int main(int argc, char *argv[]) {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    size_t size;
+    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
+    char *machine = new char[size];
+    sysctlbyname("hw.machine", machine, &size, NULL, 0);
+    Machine_ = machine;
+
+    if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
+        if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
+            if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
+                SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
+                CFRelease(serial);
+            }
+
+            IOObjectRelease(service);
+        }
+
     UIApplicationMain(argc, argv, [Cydia class]);
     [pool release];
 }