X-Git-Url: https://git.saurik.com/cydia.git/blobdiff_plain/d0cee7a2f9232e064138eeb1e2d1613a19bd47d7..380d0443f88a39194a5e7b9f91e56eb560637b43:/MobileCydia.mm diff --git a/MobileCydia.mm b/MobileCydia.mm index 569bab1e..b6a51d54 100644 --- a/MobileCydia.mm +++ b/MobileCydia.mm @@ -288,8 +288,9 @@ static _finline void UpdateExternalStatus(uint64_t newStatus) { NSRunLoop *loop([NSRunLoop currentRunLoop]); NSDate *future([NSDate distantFuture]); + NSString *mode([loop currentMode] ?: NSDefaultRunLoopMode); - while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); + while (!stopped && [loop runMode:mode beforeDate:future]); return [context count] == 0 ? nil : [context objectAtIndex:0]; } @@ -307,12 +308,13 @@ static _finline void UpdateExternalStatus(uint64_t newStatus) { } - (int) yieldToPopupAlertAnimated:(BOOL)animated; + @end @implementation CYAlertView - (id) initWithTitle:(NSString *)title buttons:(NSArray *)buttons defaultButtonIndex:(int)index { - if ((self = [super init])) { + if ((self = [super init]) != nil) { [self setTitle:title]; [self setDelegate:self]; for (NSString *button in buttons) [self addButtonWithTitle:button]; @@ -366,7 +368,6 @@ static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive | #define TrackResize (0 && !ForRelease) #define ManualRefresh (1 && !ForRelease) #define ShowInternals (0 && !ForRelease) -#define IgnoreInstall (0 && !ForRelease) #define AlwaysReload (0 && !ForRelease) #define TryIndexedCollation (0 && !ForRelease) @@ -564,6 +565,21 @@ void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFCompar @end /* }}} */ +@interface NSInvocation (Cydia) ++ (NSInvocation *) invocationWithSelector:(SEL)selector forTarget:(id)target; +@end + +@implementation NSInvocation (Cydia) + ++ (NSInvocation *) invocationWithSelector:(SEL)selector forTarget:(id)target { + NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]]); + [invocation setTarget:target]; + [invocation setSelector:selector]; + return invocation; +} + +@end + @implementation WebScriptObject (NSFastEnumeration) - (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)objects count:(NSUInteger)count { @@ -872,6 +888,7 @@ class Pcre { + (Address *) addressWithString:(NSString *)string; - (Address *) initWithString:(NSString *)string; + @end @implementation Address @@ -905,7 +922,10 @@ class Pcre { } + (NSArray *) _attributeKeys { - return [NSArray arrayWithObjects:@"address", @"name", nil]; + return [NSArray arrayWithObjects: + @"address", + @"name", + nil]; } - (NSArray *) attributeKeys { @@ -980,7 +1000,10 @@ class CYColor { /* Random Global Variables {{{ */ static const int PulseInterval_ = 50000; +static const NSString *UI_; + static int Finish_; +static bool RestartSubstrate_; static NSArray *Finishes_; #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist" @@ -1039,6 +1062,7 @@ static bool Changed_; static time_t now_; bool IsWildcat_; +static CGFloat ScreenScale_; /* }}} */ /* Display Helpers {{{ */ @@ -1046,23 +1070,6 @@ inline float Interpolate(float begin, float end, float fraction) { return (end - begin) * fraction + begin; } -/* XXX: localize this! */ -NSString *SizeString(double size) { - bool negative = size < 0; - if (negative) - size = -size; - - unsigned power = 0; - while (size > 1024) { - size /= 1024; - ++power; - } - - static const char *powers_[] = {"B", "kB", "MB", "GB"}; - - return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]]; -} - static _finline const char *StripVersion_(const char *version) { const char *colon(strchr(version, ':')); return colon == NULL ? version : colon + 1; @@ -1118,7 +1125,7 @@ NSString *GetLastUpdate() { } bool isSectionVisible(NSString *section) { - NSDictionary *metadata([Sections_ objectForKey:section]); + NSDictionary *metadata([Sections_ objectForKey:(section ?: @"")]); NSNumber *hidden(metadata == nil ? nil : [metadata objectForKey:@"Hidden"]); return hidden == nil || ![hidden boolValue]; } @@ -1128,22 +1135,12 @@ bool isSectionVisible(NSString *section) { /* Delegate Prototypes {{{ */ @class Package; @class Source; +@class CydiaProgressEvent; -@interface NSObject (ProgressDelegate) -@end - -@protocol ProgressDelegate -- (void) setProgressError:(NSString *)error withTitle:(NSString *)id; -- (void) setProgressTitle:(NSString *)title; -- (void) setProgressPercent:(float)percent; -- (void) startProgress; -- (void) addProgressOutput:(NSString *)output; -- (bool) isCancelling:(size_t)received; -@end - -@protocol ConfigurationDelegate +@protocol DatabaseDelegate - (void) repairWithSelector:(SEL)selector; - (void) setConfigurationData:(NSString *)data; +- (void) addProgressEventOnMainThread:(CydiaProgressEvent *)event forTask:(NSString *)task; @end @class CYPackageController; @@ -1161,30 +1158,66 @@ bool isSectionVisible(NSString *section) { - (void) loadData; - (void) updateData; - (void) syncData; +- (void) addTrivialSource:(NSString *)href; - (void) showSettings; - (UIProgressHUD *) addProgressHUD; - (void) removeProgressHUD:(UIProgressHUD *)hud; - (CYViewController *) pageForPackage:(NSString *)name; - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item; +- (void) reloadDataWithInvocation:(NSInvocation *)invocation; @end - -static id CydiaApp; /* }}} */ +/* ProgressEvent Interface/Delegate {{{ */ +@interface CydiaProgressEvent : NSObject { + _H message_; + _H type_; + _H package_; + _H uri_; +} + ++ (CydiaProgressEvent *) eventWithMessage:(NSString *)message ofType:(NSString *)type; ++ (CydiaProgressEvent *) eventWithMessage:(NSString *)message ofType:(NSString *)type forPackage:(NSString *)package; + +- (id) initWithMessage:(NSString *)message ofType:(NSString *)type; + +- (NSString *) message; +- (NSString *) type; +- (NSString *) package; +- (NSString *) uri; + +- (void) setPackage:(NSString *)package; + +- (NSString *) compound:(NSString *)value; +- (NSString *) compoundMessage; +- (NSString *) compoundTitle; + +@end + +@protocol ProgressDelegate +- (void) addProgressEvent:(CydiaProgressEvent *)event; +- (void) setProgressPercent:(NSNumber *)percent; +- (void) setProgressCancellable:(NSNumber *)cancellable; +- (bool) isProgressCancelled; +- (void) setTitle:(NSString *)title; +@end +/* }}} */ /* Status Delegation {{{ */ class Status : public pkgAcquireStatus { private: _transient NSObject *delegate_; + bool cancelled_; public: Status() : - delegate_(nil) + delegate_(nil), + cancelled_(false) { } - void setDelegate(id delegate) { + void setDelegate(NSObject *delegate) { delegate_ = delegate; } @@ -1200,8 +1233,9 @@ class Status : } virtual void Fetch(pkgAcquire::ItemDesc &item) { - //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]); - [delegate_ setProgressTitle:[NSString stringWithFormat:UCLocalize("DOWNLOADING_"), [NSString stringWithUTF8String:item.ShortDesc.c_str()]]]; + NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]); + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithFormat:UCLocalize("DOWNLOADING_"), name] ofType:@"STATUS"]); + [delegate_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } virtual void Done(pkgAcquire::ItemDesc &item) { @@ -1218,17 +1252,12 @@ class Status : if (error.empty()) return; - NSString *description([NSString stringWithUTF8String:item.Description.c_str()]); - NSArray *fields([description componentsSeparatedByString:@" "]); - NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]); + //NSString *description([NSString stringWithUTF8String:item.Description.c_str()]); + //NSArray *fields([description componentsSeparatedByString:@" "]); + //NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]); - [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:) - withObject:[NSArray arrayWithObjects: - [NSString stringWithUTF8String:error.c_str()], - source, - nil] - waitUntilDone:YES - ]; + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:error.c_str()] ofType:@"ERROR"]); + [delegate_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } virtual bool Pulse(pkgAcquire *Owner) { @@ -1239,59 +1268,30 @@ class Status : double(TotalBytes + TotalItems) ); - [delegate_ setProgressPercent:percent]; - return [delegate_ isCancelling:CurrentBytes] ? false : value; - } - - virtual void Start() { - [delegate_ startProgress]; - } - - virtual void Stop() { - } -}; -/* }}} */ -/* Progress Delegation {{{ */ -class Progress : - public OpProgress -{ - private: - _transient id delegate_; - float percent_; - - protected: - virtual void Update() { - /*if (abs(Percent - percent_) > 2) - //NSLog(@"%s:%s:%f", Op.c_str(), SubOp.c_str(), Percent); - percent_ = Percent; - }*/ - - /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]]; - [delegate_ setProgressPercent:(Percent / 100)];*/ - } - - public: - Progress() : - delegate_(nil), - percent_(0) - { + [delegate_ performSelectorOnMainThread:@selector(setProgressPercent:) withObject:[NSNumber numberWithFloat:percent] waitUntilDone:YES]; + if (value && ![delegate_ isProgressCancelled]) + return true; + else { + cancelled_ = true; + return false; + } } - void setDelegate(id delegate) { - delegate_ = delegate; + _finline bool WasCancelled() const { + return cancelled_; } - id getDelegate() const { - return delegate_; + virtual void Start() { + pkgAcquireStatus::Start(); + [delegate_ performSelectorOnMainThread:@selector(setProgressCancellable:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:YES]; } - virtual void Done() { - //NSLog(@"DONE"); - //[delegate_ setProgressPercent:1]; + virtual void Stop() { + pkgAcquireStatus::Stop(); + [delegate_ performSelectorOnMainThread:@selector(setProgressCancellable:) withObject:[NSNumber numberWithBool:NO] waitUntilDone:YES]; } }; /* }}} */ - /* Database Interface {{{ */ typedef std::map< unsigned long, _H > SourceMap; @@ -1310,13 +1310,15 @@ typedef std::map< unsigned long, _H > SourceMap; SPtr manager_; pkgSourceList *list_; - SourceMap sources_; - CFMutableArrayRef deadSources_; + SourceMap sourceMap_; + NSMutableArray *sourceList_; + CFMutableArrayRef packages_; - _transient NSObject *delegate_; + _transient NSObject *delegate_; + _transient NSObject *progress_; + Status status_; - Progress progress_; int cydiafd_; int statusfd_; @@ -1344,7 +1346,8 @@ typedef std::map< unsigned long, _H > SourceMap; - (pkgSourceList &) list; - (NSArray *) packages; - (NSArray *) sources; -- (void) reloadData; +- (Source *) sourceWithKey:(NSString *)key; +- (void) reloadDataWithInvocation:(NSInvocation *)invocation; - (void) configure; - (bool) prepare; @@ -1354,44 +1357,106 @@ typedef std::map< unsigned long, _H > SourceMap; - (void) updateWithStatus:(Status &)status; -- (void) setDelegate:(id)delegate; +- (void) setDelegate:(NSObject *)delegate; + +- (void) setProgressDelegate:(NSObject *)delegate; +- (NSObject *) progressDelegate; + - (Source *) getSource:(pkgCache::PkgFileIterator)file; - (NSString *) mappedSectionForPointer:(const char *)pointer; @end /* }}} */ -/* Delegate Helpers {{{ */ -@implementation NSObject (ProgressDelegate) +/* ProgressEvent Implementation {{{ */ +@implementation CydiaProgressEvent -- (void) _setProgressErrorPackage:(NSArray *)args { - [self performSelector:@selector(setProgressError:forPackage:) - withObject:[args objectAtIndex:0] - withObject:([args count] == 1 ? nil : [args objectAtIndex:1]) - ]; ++ (CydiaProgressEvent *) eventWithMessage:(NSString *)message ofType:(NSString *)type { + return [[[CydiaProgressEvent alloc] initWithMessage:message ofType:type] autorelease]; } -- (void) _setProgressErrorTitle:(NSArray *)args { - [self performSelector:@selector(setProgressError:withTitle:) - withObject:[args objectAtIndex:0] - withObject:([args count] == 1 ? nil : [args objectAtIndex:1]) - ]; ++ (CydiaProgressEvent *) eventWithMessage:(NSString *)message ofType:(NSString *)type forPackage:(NSString *)package { + CydiaProgressEvent *event([self eventWithMessage:message ofType:type]); + [event setPackage:package]; + return event; } -- (void) _setProgressError:(NSString *)error withTitle:(NSString *)title { - [self performSelectorOnMainThread:@selector(_setProgressErrorTitle:) - withObject:[NSArray arrayWithObjects:error, title, nil] - waitUntilDone:YES - ]; ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects: + @"message", + @"package", + @"type", + @"uri", + nil]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; } -- (void) setProgressError:(NSString *)error forPackage:(NSString *)id { - Package *package = id == nil ? nil : [[Database sharedInstance] packageWithName:id]; ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} - [self performSelector:@selector(setProgressError:withTitle:) - withObject:error - withObject:(package == nil ? id : [package name]) - ]; +- (id) initWithMessage:(NSString *)message ofType:(NSString *)type { + if ((self = [super init]) != nil) { + message_ = message; + type_ = type; + } return self; +} + +- (NSString *) message { + return message_; +} + +- (NSString *) type { + return type_; +} + +- (NSString *) package { + return package_; +} + +- (NSString *) uri { + return uri_; +} + +- (void) setPackage:(NSString *)package { + package_ = package; +} + +- (NSString *) compound:(NSString *)value { + if (value != nil) { + NSString *mode(nil); { + NSString *type([self type]); + if ([type isEqualToString:@"ERROR"]) + mode = UCLocalize("ERROR"); + else if ([type isEqualToString:@"WARNING"]) + mode = UCLocalize("WARNING"); + } + + if (mode != nil) + value = [NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), mode, value]; + } + + return value; +} + +- (NSString *) compoundMessage { + return [self compound:[self message]]; +} + +- (NSString *) compoundTitle { + NSString *title; + + if (package_ == nil) + title = nil; + else if (Package *package = [[Database sharedInstance] packageWithName:package_]) + title = [package name]; + else + title = package_; + + return [self compound:title]; } @end @@ -1589,7 +1654,19 @@ static void PackageImport(const void *key, const void *value, void *context) { } + (NSArray *) _attributeKeys { - return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil]; + return [NSArray arrayWithObjects: + @"description", + @"distribution", + @"host", + @"key", + @"label", + @"name", + @"origin", + @"trusted", + @"type", + @"uri", + @"version", + nil]; } - (NSArray *) attributeKeys { @@ -1754,37 +1831,176 @@ static void PackageImport(const void *key, const void *value, void *context) { @end /* }}} */ -/* Relationship Class {{{ */ -@interface Relationship : NSObject { - NSString *type_; - NSString *id_; +/* CydiaOperation Class {{{ */ +@interface CydiaOperation : NSObject { + NSString *operator_; + NSString *value_; } -- (NSString *) type; -- (NSString *) id; -- (NSString *) name; +- (NSString *) operator; +- (NSString *) value; @end -@implementation Relationship +@implementation CydiaOperation - (void) dealloc { - [type_ release]; - [id_ release]; + [operator_ release]; + [value_ release]; [super dealloc]; } -- (NSString *) type { - return type_; +- (id) initWithOperator:(const char *)_operator value:(const char *)value { + if ((self = [super init]) != nil) { + operator_ = [[NSString alloc] initWithUTF8String:_operator]; + value_ = [[NSString alloc] initWithUTF8String:value]; + } return self; } -- (NSString *) id { - return id_; ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects: + @"operator", + @"value", + nil]; } -- (NSString *) name { - _assert(false); - return nil; +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (NSString *) operator { + return operator_; +} + +- (NSString *) value { + return value_; +} + +@end +/* }}} */ +/* CydiaClause Class {{{ */ +@interface CydiaClause : NSObject { + NSString *package_; + CydiaOperation *version_; +} + +- (NSString *) package; +- (CydiaOperation *) version; + +@end + +@implementation CydiaClause + +- (void) dealloc { + [package_ release]; + [version_ release]; + [super dealloc]; +} + +- (id) initWithIterator:(pkgCache::DepIterator &)dep { + if ((self = [super init]) != nil) { + package_ = [[NSString alloc] initWithUTF8String:dep.TargetPkg().Name()]; + + if (const char *version = dep.TargetVer()) + version_ = [[CydiaOperation alloc] initWithOperator:dep.CompType() value:version]; + else + version_ = [[NSNull null] retain]; + } return self; +} + ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects: + @"package", + @"version", + nil]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (NSString *) package { + return package_; +} + +- (CydiaOperation *) version { + return version_; +} + +@end +/* }}} */ +/* CydiaRelation Class {{{ */ +@interface CydiaRelation : NSObject { + NSString *relationship_; + NSMutableArray *clauses_; +} + +- (NSString *) relationship; +- (NSArray *) clauses; + +@end + +@implementation CydiaRelation + +- (void) dealloc { + [relationship_ release]; + [clauses_ release]; + [super dealloc]; +} + +- (id) initWithIterator:(pkgCache::DepIterator &)dep { + if ((self = [super init]) != nil) { + relationship_ = [[NSString alloc] initWithUTF8String:dep.DepType()]; + clauses_ = [[NSMutableArray alloc] initWithCapacity:8]; + + pkgCache::DepIterator start; + pkgCache::DepIterator end; + dep.GlobOr(start, end); // ++dep + + _forever { + [clauses_ addObject:[[[CydiaClause alloc] initWithIterator:start] autorelease]]; + + // yes, seriously. (wtf?) + if (start == end) + break; + ++start; + } + } return self; +} + ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects: + @"clauses", + @"relationship", + nil]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (NSString *) relationship { + return relationship_; +} + +- (NSArray *) clauses { + return clauses_; +} + +- (void) addClause:(CydiaClause *)clause { + [clauses_ addObject:clause]; } @end @@ -2064,8 +2280,17 @@ struct PackageNameOrdering : } + (NSString *) webScriptNameForSelector:(SEL)selector { - if (selector == @selector(hasTag:)) + if (false); + else if (selector == @selector(clear)) + return @"clear"; + else if (selector == @selector(getField:)) + return @"getField"; + else if (selector == @selector(hasTag:)) return @"hasTag"; + else if (selector == @selector(install)) + return @"install"; + else if (selector == @selector(remove)) + return @"remove"; else return nil; } @@ -2075,7 +2300,33 @@ struct PackageNameOrdering : } + (NSArray *) _attributeKeys { - return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"longDescription", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"longSection", @"maintainer", @"mode", @"name", @"purposes", @"section", @"shortDescription", @"shortSection", @"simpleSection", @"size", @"source", @"sponsor", @"support", @"warnings", nil]; + return [NSArray arrayWithObjects: + @"applications", + @"author", + @"depiction", + @"essential", + @"homepage", + @"icon", + @"id", + @"installed", + @"latest", + @"longDescription", + @"longSection", + @"maintainer", + @"mode", + @"name", + @"purposes", + @"relations", + @"section", + @"shortDescription", + @"shortSection", + @"simpleSection", + @"size", + @"source", + @"sponsor", + @"support", + @"warnings", + nil]; } - (NSArray *) attributeKeys { @@ -2086,6 +2337,28 @@ struct PackageNameOrdering : return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; } +- (NSArray *) relations { +@synchronized (database_) { + NSMutableArray *relations([NSMutableArray arrayWithCapacity:16]); + for (pkgCache::DepIterator dep(version_.DependsList()); !dep.end(); ++dep) + [relations addObject:[[[CydiaRelation alloc] initWithIterator:dep] autorelease]]; + return relations; +} } + +- (NSString *) getField:(NSString *)name { +@synchronized (database_) { + if ([database_ era] != era_ || file_.end()) + return nil; + + pkgRecords::Parser &parser([database_ records]->Lookup(file_)); + + const char *start, *end; + if (!parser.Find([name UTF8String], start, end)) + return (NSString *) [NSNull null]; + + return [(NSString *) CYStringCreate(start, end - start) autorelease]; +} } + - (void) parse { if (parsed_ != NULL) return; @@ -2477,7 +2750,7 @@ struct PackageNameOrdering : _end _profile(Package$visible$isSectionVisible) - if (section != nil && !isSectionVisible(section)) + if (!isSectionVisible(section)) return false; _end @@ -3018,8 +3291,7 @@ static NSString *Warning_; - (void) dealloc { // XXX: actually implement this thing _assert(false); - if (deadSources_) - CFRelease(deadSources_); + [sourceList_ release]; [self releasePackages]; apr_pool_destroy(pool_); NSRecycleZone(zone_); @@ -3062,29 +3334,28 @@ static NSString *Warning_; size_t size(line.size()); lprintf("S:%s\n", data); - if (conffile_r(data, size)) { - [delegate_ setConfigurationData:conffile_r[1]]; - } else if (strncmp(data, "status: ", 8) == 0) { - NSString *string = [NSString stringWithUTF8String:(data + 8)]; - [delegate_ setProgressTitle:string]; + if (conffile_r(data, size)) + [delegate_ performSelectorOnMainThread:@selector(setConfigurationData:) withObject:conffile_r[1] waitUntilDone:YES]; + else if (strncmp(data, "status: ", 8) == 0) { + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:(data + 8)] ofType:@"STATUS"]); + [progress_ performSelectorOnMainThread:@selector(addProgressEvent) withObject:event waitUntilDone:YES]; } else if (pmstatus_r(data, size)) { std::string type([pmstatus_r[1] UTF8String]); - NSString *id = pmstatus_r[2]; + NSString *package = pmstatus_r[2]; float percent([pmstatus_r[3] floatValue]); - [delegate_ setProgressPercent:(percent / 100)]; + [progress_ performSelectorOnMainThread:@selector(setProgressPercent:) withObject:[NSNumber numberWithFloat:(percent / 100)] waitUntilDone:YES]; NSString *string = pmstatus_r[4]; - if (type == "pmerror") - [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:) - withObject:[NSArray arrayWithObjects:string, id, nil] - waitUntilDone:YES - ]; - else if (type == "pmstatus") { - [delegate_ setProgressTitle:string]; + if (type == "pmerror") { + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:string ofType:@"ERROR" forPackage:package]); + [progress_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; + } else if (type == "pmstatus") { + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:string ofType:@"STATUS" forPackage:package]); + [progress_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } else if (type == "pmconffile") - [delegate_ setConfigurationData:string]; + [delegate_ performSelectorOnMainThread:@selector(setConfigurationData:) withObject:string waitUntilDone:YES]; else lprintf("E:unknown pmstatus\n"); } else @@ -3101,7 +3372,9 @@ static NSString *Warning_; while (std::getline(is, line)) { lprintf("O:%s\n", line.c_str()); - [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]]; + + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:line.c_str()] ofType:@"INFORMATION"]); + [progress_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } _assume(false); @@ -3137,7 +3410,7 @@ static NSString *Warning_; capacity += 1024; packages_ = CFArrayCreateMutable(kCFAllocatorDefault, capacity, NULL); - deadSources_ = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + sourceList_ = [[NSMutableArray alloc] initWithCapacity:16]; int fds[2]; @@ -3209,103 +3482,36 @@ static NSString *Warning_; } - (NSArray *) sources { - NSMutableArray *sources([NSMutableArray arrayWithCapacity:sources_.size()]); - for (SourceMap::const_iterator i(sources_.begin()); i != sources_.end(); ++i) - [sources addObject:i->second]; - [sources addObjectsFromArray:(NSArray *)deadSources_]; - return sources; + return sourceList_; } -- (NSArray *) issues { - if (cache_->BrokenCount() == 0) - return nil; +- (Source *) sourceWithKey:(NSString *)key { + for (Source *source in [self sources]) { + if ([[source key] isEqualToString:key]) + return source; + } return nil; +} - NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]); +- (bool) popErrorWithTitle:(NSString *)title { + bool fatal(false); - for (Package *package in [self packages]) { - if (![package broken]) - continue; - pkgCache::PkgIterator pkg([package iterator]); + while (!_error->empty()) { + std::string error; + bool warning(!_error->PopMessage(error)); + if (!warning) + fatal = true; - NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]); - [entry addObject:[package name]]; - [issues addObject:entry]; + for (;;) { + size_t size(error.size()); + if (size == 0 || error[size - 1] != '\n') + break; + error.resize(size - 1); + } - pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_)); - if (ver.end()) - continue; + lprintf("%c:[%s]\n", warning ? 'W' : 'E', error.c_str()); - for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) { - pkgCache::DepIterator start; - pkgCache::DepIterator end; - dep.GlobOr(start, end); // ++dep - - if (!cache_->IsImportantDep(end)) - continue; - if ((cache_[end] & pkgDepCache::DepGInstall) != 0) - continue; - - NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]); - [entry addObject:failure]; - [failure addObject:[NSString stringWithUTF8String:start.DepType()]]; - - NSString *name([NSString stringWithUTF8String:start.TargetPkg().Name()]); - if (Package *package = [self packageWithName:name]) - name = [package name]; - [failure addObject:name]; - - pkgCache::PkgIterator target(start.TargetPkg()); - if (target->ProvidesList != 0) - [failure addObject:@"?"]; - else { - pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_)); - if (!ver.end()) - [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]]; - else if (!cache_[target].CandidateVerIter(cache_).end()) - [failure addObject:@"-"]; - else if (target->ProvidesList == 0) - [failure addObject:@"!"]; - else - [failure addObject:@"%"]; - } - - _forever { - if (start.TargetVer() != 0) - [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]]; - if (start == end) - break; - ++start; - } - } - } - - return issues; -} - -- (bool) popErrorWithTitle:(NSString *)title { - bool fatal(false); - std::string message; - - while (!_error->empty()) { - std::string error; - bool warning(!_error->PopMessage(error)); - if (!warning) - fatal = true; - for (;;) { - size_t size(error.size()); - if (size == 0 || error[size - 1] != '\n') - break; - error.resize(size - 1); - } - lprintf("%c:[%s]\n", warning ? 'W' : 'E', error.c_str()); - - if (!message.empty()) - message += "\n\n"; - message += error; - } - - if (fatal && !message.empty()) - [delegate_ _setProgressError:[NSString stringWithUTF8String:message.c_str()] withTitle:[NSString stringWithFormat:Colon_, fatal ? Error_ : Warning_, title]]; + [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:error.c_str()] ofType:(warning ? @"WARNING" : @"ERROR")] forTask:title]; + } return fatal; } @@ -3314,14 +3520,14 @@ static NSString *Warning_; return [self popErrorWithTitle:title] || !success; } -- (void) reloadData { CYPoolStart() { +- (void) reloadDataWithInvocation:(NSInvocation *)invocation { CYPoolStart() { @synchronized (self) { ++era_; [self releasePackages]; - sources_.clear(); - CFArrayRemoveAllValues(deadSources_); + sourceMap_.clear(); + [sourceList_ removeAllObjects]; _error->Discard(); @@ -3350,10 +3556,14 @@ static NSString *Warning_; if (chk != -1) close(chk); + if (invocation != nil) + [invocation invoke]; + NSString *title(UCLocalize("DATABASE")); _trace(); - if (!cache_.Open(progress_, true)) { pop: + OpProgress progress; + while (!cache_.Open(progress, true)) { pop: std::string error; bool warning(!_error->PopMessage(error)); lprintf("cache_.Open():[%s]\n", error.c_str()); @@ -3365,13 +3575,14 @@ static NSString *Warning_; // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)") // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)") // else if (error == "The list of sources could not be read.") - else - [delegate_ _setProgressError:[NSString stringWithUTF8String:error.c_str()] withTitle:[NSString stringWithFormat:Colon_, warning ? Warning_ : Error_, title]]; + else { + [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:error.c_str()] ofType:(warning ? @"WARNING" : @"ERROR")] forTask:title]; + return; + } if (warning) goto pop; _error->Discard(); - return; } _trace(); @@ -3390,7 +3601,7 @@ static NSString *Warning_; return; if (cache_->DelCount() != 0 || cache_->InstCount() != 0) { - [delegate_ _setProgressError:@"COUNTS_NONZERO_EX" withTitle:title]; + [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:UCLocalize("COUNTS_NONZERO_EX") ofType:@"ERROR"] forTask:title]; return; } @@ -3402,7 +3613,7 @@ static NSString *Warning_; return; if (cache_->BrokenCount() != 0) { - [delegate_ _setProgressError:@"STILL_BROKEN_EX" withTitle:title]; + [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:UCLocalize("STILL_BROKEN_EX") ofType:@"ERROR"] forTask:title]; return; } @@ -3411,19 +3622,17 @@ static NSString *Warning_; } for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) { - bool found = false; + Source *object([[[Source alloc] initWithMetaIndex:*source inPool:pool_] autorelease]); + [sourceList_ addObject:object]; + std::vector *indices = (*source)->GetIndexFiles(); for (std::vector::const_iterator index = indices->begin(); index != indices->end(); ++index) // XXX: this could be more intelligent if (dynamic_cast(*index) != NULL) { pkgCache::PkgFileIterator cached((*index)->FindInCache(cache_)); - if (!cached.end()) { - sources_[cached->ID] = [[[Source alloc] initWithMetaIndex:*source inPool:pool_] autorelease]; - found = true; - } + if (!cached.end()) + sourceMap_[cached->ID] = object; } - if (!found) - CFArrayAppendValue(deadSources_, [[[Source alloc] initWithMetaIndex:*source inPool:pool_] autorelease]); } { @@ -3484,12 +3693,11 @@ static NSString *Warning_; delete resolver_; resolver_ = new pkgProblemResolver(cache_); - for (pkgCache::PkgIterator iterator(cache_->PkgBegin()); !iterator.end(); ++iterator) { - if (!cache_[iterator].Keep()) { + for (pkgCache::PkgIterator iterator(cache_->PkgBegin()); !iterator.end(); ++iterator) + if (!cache_[iterator].Keep()) cache_->MarkKeep(iterator, false); + else if ((cache_[iterator].iFlags & pkgDepCache::ReInstall) != 0) cache_->SetReInstall(iterator, false); - } - } } } - (void) configure { @@ -3555,6 +3763,9 @@ static NSString *Warning_; } - (void) perform { + bool substrate(RestartSubstrate_); + RestartSubstrate_ = false; + NSString *title(UCLocalize("PERFORM_SELECTIONS")); NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; { @@ -3565,13 +3776,14 @@ static NSString *Warning_; [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]]; } + [delegate_ performSelectorOnMainThread:@selector(retainNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; + if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) { _trace(); + [self popErrorWithTitle:title]; return; } - [CydiaApp retainNetworkActivityIndicator]; - bool failed = false; for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) { if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete) @@ -3579,27 +3791,19 @@ static NSString *Warning_; if ((*item)->Status == pkgAcquire::Item::StatIdle) continue; - std::string uri = (*item)->DescURI(); - std::string error = (*item)->ErrorText; - - lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str()); failed = true; - - [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:) - withObject:[NSArray arrayWithObjects: - [NSString stringWithUTF8String:error.c_str()], - nil] - waitUntilDone:YES - ]; } - [CydiaApp releaseNetworkActivityIndicator]; + [delegate_ performSelectorOnMainThread:@selector(releaseNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; if (failed) { _trace(); return; } + if (substrate) + RestartSubstrate_ = true; + _system->UnLock(); pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_); @@ -3642,35 +3846,47 @@ static NSString *Warning_; } - (void) updateWithStatus:(Status &)status { - _transient NSObject *delegate(status.getDelegate()); NSString *title(UCLocalize("REFRESHING_DATA")); pkgSourceList list; - if (!list.ReadMainList()) - [delegate _setProgressError:@"Unable to read source list." withTitle:title]; + if ([self popErrorWithTitle:title forOperation:list.ReadMainList()]) + return; FileFd lock; lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock")); if ([self popErrorWithTitle:title]) return; - if ([self popErrorWithTitle:title forOperation:ListUpdate(status, list, PulseInterval_)]) - /* XXX: ignore this because users suck and don't understand why refreshing is important: return */ - /* XXX: why the hell is an empty if statement a clang error? */ (void) 0; + [delegate_ performSelectorOnMainThread:@selector(retainNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; + + bool success(ListUpdate(status, list, PulseInterval_)); + if (status.WasCancelled()) + _error->Discard(); + else + [self popErrorWithTitle:title forOperation:success]; + + [delegate_ performSelectorOnMainThread:@selector(releaseNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"]; Changed_ = true; } -- (void) setDelegate:(id)delegate { +- (void) setDelegate:(NSObject *)delegate { delegate_ = delegate; +} + +- (void) setProgressDelegate:(NSObject *)delegate { + progress_ = delegate; status_.setDelegate(delegate); - progress_.setDelegate(delegate); +} + +- (NSObject *) progressDelegate { + return progress_; } - (Source *) getSource:(pkgCache::PkgFileIterator)file { - SourceMap::const_iterator i(sources_.find(file->ID)); - return i == sources_.end() ? nil : i->second; + SourceMap::const_iterator i(sourceMap_.find(file->ID)); + return i == sourceMap_.end() ? nil : i->second; } - (NSString *) mappedSectionForPointer:(const char *)section { @@ -3714,6 +3930,7 @@ static NSString *Warning_; } - (id) initWithDelegate:(IndirectDelegate *)indirect; + @end @implementation CydiaObject @@ -3734,7 +3951,14 @@ static NSString *Warning_; } + (NSArray *) _attributeKeys { - return [NSArray arrayWithObjects:@"device", @"firewire", @"imei", @"mac", @"serial", nil]; + return [NSArray arrayWithObjects: + @"device", + @"ecid", + @"model", + @"plmn", + @"role", + @"serial", + nil]; } - (NSArray *) attributeKeys { @@ -3749,59 +3973,70 @@ static NSString *Warning_; return [[UIDevice currentDevice] uniqueIdentifier]; } -#if 0 // XXX: implement! -- (NSString *) mac { - if (![indirect_ promptForSensitive:@"Mac Address"]) - return nil; +- (NSString *) plmn { + return PLMN_; +} + +- (NSString *) ecid { + return ChipID_; } - (NSString *) serial { - if (![indirect_ promptForSensitive:@"Serial #"]) - return nil; + return SerialNumber_; } -- (NSString *) firewire { - if (![indirect_ promptForSensitive:@"Firewire GUID"]) - return nil; +- (NSString *) role { + return Role_; } -- (NSString *) imei { - if (![indirect_ promptForSensitive:@"IMEI"]) - return nil; +- (NSString *) model { + return [NSString stringWithUTF8String:Machine_]; } -#endif + (NSString *) webScriptNameForSelector:(SEL)selector { - if (selector == @selector(close)) + if (false); + else if (selector == @selector(addTrivialSource:)) + return @"addTrivialSource"; + else if (selector == @selector(close)) return @"close"; + else if (selector == @selector(du:)) + return @"du"; + else if (selector == @selector(stringWithFormat:arguments:)) + return @"format"; + else if (selector == @selector(getAllSources)) + return @"getAllSourcs"; + else if (selector == @selector(getKernelNumber:)) + return @"getKernelNumber"; + else if (selector == @selector(getKernelString:)) + return @"getKernelString"; else if (selector == @selector(getInstalledPackages)) return @"getInstalledPackages"; else if (selector == @selector(getPackageById:)) return @"getPackageById"; else if (selector == @selector(installPackages:)) return @"installPackages"; + else if (selector == @selector(localizedStringForKey:value:table:)) + return @"localize"; + else if (selector == @selector(refreshSources)) + return @"refreshSources"; + else if (selector == @selector(removeButton)) + return @"removeButton"; + else if (selector == @selector(scrollToBottom:)) + return @"scrollToBottom"; else if (selector == @selector(setButtonImage:withStyle:toFunction:)) return @"setButtonImage"; else if (selector == @selector(setButtonTitle:withStyle:toFunction:)) return @"setButtonTitle"; else if (selector == @selector(setPopupHook:)) return @"setPopupHook"; - else if (selector == @selector(setSpecial:)) - return @"setSpecial"; else if (selector == @selector(setToken:)) return @"setToken"; else if (selector == @selector(setViewportWidth:)) return @"setViewportWidth"; - else if (selector == @selector(supports:)) - return @"supports"; - else if (selector == @selector(stringWithFormat:arguments:)) - return @"format"; - else if (selector == @selector(localizedStringForKey:value:table:)) - return @"localize"; - else if (selector == @selector(du:)) - return @"du"; else if (selector == @selector(statfs:)) return @"statfs"; + else if (selector == @selector(supports:)) + return @"supports"; else return nil; } @@ -3814,14 +4049,62 @@ static NSString *Warning_; return [feature isEqualToString:@"window.open"]; } +- (NSNumber *) getKernelNumber:(NSString *)name { + const char *string([name UTF8String]); + + size_t size; + if (sysctlbyname(string, NULL, &size, NULL, 0) == -1) + return (id) [NSNull null]; + + if (size != sizeof(int)) + return (id) [NSNull null]; + + int value; + if (sysctlbyname(string, &value, &size, NULL, 0) == -1) + return (id) [NSNull null]; + + return [NSNumber numberWithInt:value]; +} + +- (NSString *) getKernelString:(NSString *)name { + const char *string([name UTF8String]); + + size_t size; + if (sysctlbyname(string, NULL, &size, NULL, 0) == -1) + return (id) [NSNull null]; + + char value[size + 1]; + if (sysctlbyname(string, value, &size, NULL, 0) == -1) + return (id) [NSNull null]; + + // XXX: just in case you request something ludicrous + value[size] = '\0'; + + return [NSString stringWithCString:value]; +} + +- (void) addTrivialSource:(NSString *)href { + [delegate_ performSelectorOnMainThread:@selector(addTrivialSource:) withObject:href waitUntilDone:NO]; +} + +- (void) refreshSources { + [delegate_ performSelectorOnMainThread:@selector(syncData) withObject:nil waitUntilDone:NO]; +} + +- (NSArray *) getAllSources { + return [[Database sharedInstance] sources]; +} + - (NSArray *) getInstalledPackages { - NSArray *packages([[Database sharedInstance] packages]); + Database *database([Database sharedInstance]); +@synchronized (database) { + NSArray *packages([database packages]); NSMutableArray *installed([NSMutableArray arrayWithCapacity:1024]); for (Package *package in packages) - if ([package installed] != nil) + if (![package uninstalled]) [installed addObject:package]; return installed; -} +} } - (Package *) getPackageById:(NSString *)id { Package *package([[Database sharedInstance] packageWithName:id]); @@ -3887,13 +4170,17 @@ static NSString *Warning_; } - (void) close { - [indirect_ close]; + [indirect_ performSelectorOnMainThread:@selector(close) withObject:nil waitUntilDone:NO]; } - (void) installPackages:(NSArray *)packages { [delegate_ performSelectorOnMainThread:@selector(installPackages:) withObject:packages waitUntilDone:NO]; } +- (void) removeButton { + [indirect_ removeButton]; +} + - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { [indirect_ setButtonImage:button withStyle:style toFunction:function]; } @@ -3902,11 +4189,7 @@ static NSString *Warning_; [indirect_ setButtonTitle:button withStyle:style toFunction:function]; } -- (void) setSpecial:(id)function { - [indirect_ setSpecial:function]; -} - -- (void) setToken:(NSString *)token { +- (void) _setToken:(NSString *)token { if (Token_ != nil) [Token_ release]; Token_ = [token retain]; @@ -3915,12 +4198,20 @@ static NSString *Warning_; Changed_ = true; } +- (void) setToken:(NSString *)token { + [self performSelectorOnMainThread:@selector(_setToken:) withObject:token waitUntilDone:NO]; +} + - (void) setPopupHook:(id)function { [indirect_ setPopupHook:function]; } +- (void) scrollToBottom:(NSNumber *)animated { + [indirect_ performSelectorOnMainThread:@selector(scrollToBottomAnimated:) withObject:animated waitUntilDone:NO]; +} + - (void) setViewportWidth:(float)width { - [indirect_ setViewportWidth:width]; + [indirect_ setViewportWidthOnMainThread:width]; } - (NSString *) stringWithFormat:(NSString *)format arguments:(WebScriptObject *)arguments { @@ -3945,9 +4236,9 @@ static NSString *Warning_; /* @ Loading... Indicator {{{ */ @interface CYLoadingIndicator : UIView { - UIActivityIndicatorView *spinner_; - UILabel *label_; - UIView *container_; + _H spinner_; + _H label_; + _H container_; } @property (readonly, nonatomic) UILabel *label; @@ -3957,8 +4248,8 @@ static NSString *Warning_; @implementation CYLoadingIndicator -- (id)initWithFrame:(CGRect)frame { - if ((self = [super initWithFrame:frame])) { +- (id) initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame]) != nil) { container_ = [[[UIView alloc] init] autorelease]; [container_ setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin]; @@ -3997,59 +4288,59 @@ static NSString *Warning_; [spinner_ setFrame:spinrect]; [label_ setFrame:textrect]; [self addSubview:container_]; - } + } return self; +} - return self; +- (UILabel *) label { + return label_; } -- (UILabel *)label { return label_; } -- (UIActivityIndicatorView *)activityIndicatorView { return spinner_; } +- (UIActivityIndicatorView *) activityIndicatorView { + return spinner_; +} @end /* }}} */ /* Emulated Loading Controller {{{ */ @interface CYEmulatedLoadingController : CYViewController { - CYLoadingIndicator *indicator_; - UITabBar *tabbar_; - UINavigationBar *navbar_; + _transient Database *database_; + _H indicator_; + _H tabbar_; + _H navbar_; } + @end @implementation CYEmulatedLoadingController -- (void) dealloc { - [self releaseSubviews]; - - [super dealloc]; +- (id) initWithDatabase:(Database *)database { + if ((self = [super init]) != nil) { + database_ = database; + } return self; } - (void) loadView { [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; [[self view] setBackgroundColor:[UIColor pinStripeColor]]; - indicator_ = [[CYLoadingIndicator alloc] initWithFrame:[[self view] bounds]]; + indicator_ = [[[CYLoadingIndicator alloc] initWithFrame:[[self view] bounds]] autorelease]; [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; [[self view] addSubview:indicator_]; - tabbar_ = [[UITabBar alloc] initWithFrame:CGRectMake(0, 0, 0, 49.0f)]; + tabbar_ = [[[UITabBar alloc] initWithFrame:CGRectMake(0, 0, 0, 49.0f)] autorelease]; [tabbar_ setFrame:CGRectMake(0.0f, [[self view] bounds].size.height - [tabbar_ bounds].size.height, [[self view] bounds].size.width, [tabbar_ bounds].size.height)]; [tabbar_ setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth]; [[self view] addSubview:tabbar_]; - navbar_ = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 0, 44.0f)]; + navbar_ = [[[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 0, 44.0f)] autorelease]; [navbar_ setFrame:CGRectMake(0.0f, 0.0f, [[self view] bounds].size.width, [navbar_ bounds].size.height)]; [navbar_ setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth]; [[self view] addSubview:navbar_]; } - (void) releaseSubviews { - [indicator_ release]; indicator_ = nil; - - [tabbar_ release]; tabbar_ = nil; - - [navbar_ release]; navbar_ = nil; } @@ -4150,6 +4441,42 @@ static NSString *Warning_; @end /* }}} */ +// CydiaScript {{{ +@interface NSObject (CydiaScript) +- (id) Cydia$webScriptObjectInContext:(WebScriptObject *)context; +@end + +@implementation NSObject (CydiaScript) + +- (id) Cydia$webScriptObjectInContext:(WebScriptObject *)context { + return self; +} + +@end + +@implementation NSArray (CydiaScript) + +- (id) Cydia$webScriptObjectInContext:(WebScriptObject *)context { + WebScriptObject *object([context evaluateWebScript:@"[]"]); + for (size_t i(0), e([self count]); i != e; ++i) + [object setWebScriptValueAtIndex:i value:[[self objectAtIndex:i] Cydia$webScriptObjectInContext:context]]; + return object; +} + +@end + +@implementation NSDictionary (CydiaScript) + +- (id) Cydia$webScriptObjectInContext:(WebScriptObject *)context { + WebScriptObject *object([context evaluateWebScript:@"({})"]); + for (id i in self) + [object setValue:[[self objectForKey:i] Cydia$webScriptObjectInContext:context] forKey:i]; + return object; +} + +@end +// }}} + /* Confirmation Controller {{{ */ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (!iterator.end()) @@ -4174,10 +4501,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @interface ConfirmationController : CYBrowserController { _transient Database *database_; + UIAlertView *essential_; - NSArray *changes_; - NSArray *issues_; - NSArray *sizes_; + + NSDictionary *changes_; + NSMutableArray *issues_; + NSDictionary *sizes_; + BOOL substrate_; } @@ -4189,24 +4519,29 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) dealloc { [changes_ release]; - if (issues_ != nil) - [issues_ release]; + [issues_ release]; [sizes_ release]; + if (essential_ != nil) [essential_ release]; + [super dealloc]; } +- (void) complete { + if (substrate_) + RestartSubstrate_ = true; + [delegate_ confirmWithNavigationController:[self navigationController]]; +} + - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { NSString *context([alert context]); if ([context isEqualToString:@"remove"]) { - if (button == [alert cancelButtonIndex]) { + if (button == [alert cancelButtonIndex]) [self dismissModalViewControllerAnimated:YES]; - } else if (button == [alert firstOtherButtonIndex]) { - if (substrate_) - Finish_ = 2; - [delegate_ confirmWithNavigationController:[self navigationController]]; + else if (button == [alert firstOtherButtonIndex]) { + [self complete]; } [alert dismissWithClickedButtonIndex:-1 animated:YES]; @@ -4230,49 +4565,140 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { [super webView:view didClearWindowObject:window forFrame:frame]; - [window setValue:changes_ forKey:@"changes"]; - [window setValue:issues_ forKey:@"issues"]; - [window setValue:sizes_ forKey:@"sizes"]; - [window setValue:self forKey:@"queue"]; + + [window setValue:[[NSDictionary dictionaryWithObjectsAndKeys: + changes_, @"changes", + issues_, @"issues", + sizes_, @"sizes", + self, @"queue", + nil] Cydia$webScriptObjectInContext:window] forKey:@"cydiaConfirm"]; } - (id) initWithDatabase:(Database *)database { if ((self = [super init]) != nil) { database_ = database; - [[self navigationItem] setTitle:UCLocalize("CONFIRM")]; - - NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16]; - NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16]; - NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16]; - NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16]; - NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16]; + NSMutableArray *installs([NSMutableArray arrayWithCapacity:16]); + NSMutableArray *reinstalls([NSMutableArray arrayWithCapacity:16]); + NSMutableArray *upgrades([NSMutableArray arrayWithCapacity:16]); + NSMutableArray *downgrades([NSMutableArray arrayWithCapacity:16]); + NSMutableArray *removes([NSMutableArray arrayWithCapacity:16]); bool remove(false); + pkgCacheFile &cache([database_ cache]); + NSArray *packages([database_ packages]); pkgDepCache::Policy *policy([database_ policy]); - pkgCacheFile &cache([database_ cache]); - NSArray *packages = [database_ packages]; + issues_ = [[NSMutableArray arrayWithCapacity:4] retain]; + for (Package *package in packages) { - pkgCache::PkgIterator iterator = [package iterator]; + pkgCache::PkgIterator iterator([package iterator]); + NSString *name([package id]); + + if ([package broken]) { + NSMutableArray *reasons([NSMutableArray arrayWithCapacity:4]); + + [issues_ addObject:[NSDictionary dictionaryWithObjectsAndKeys: + name, @"package", + reasons, @"reasons", + nil]]; + + pkgCache::VerIterator ver(cache[iterator].InstVerIter(cache)); + if (ver.end()) + continue; + + for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) { + pkgCache::DepIterator start; + pkgCache::DepIterator end; + dep.GlobOr(start, end); // ++dep + + if (!cache->IsImportantDep(end)) + continue; + if ((cache[end] & pkgDepCache::DepGInstall) != 0) + continue; + + NSMutableArray *clauses([NSMutableArray arrayWithCapacity:4]); + + [reasons addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:start.DepType()], @"relationship", + clauses, @"clauses", + nil]]; + + _forever { + NSString *reason, *installed((NSString *) [WebUndefined undefined]); + + pkgCache::PkgIterator target(start.TargetPkg()); + if (target->ProvidesList != 0) + reason = @"missing"; + else { + pkgCache::VerIterator ver(cache[target].InstVerIter(cache)); + if (!ver.end()) { + reason = @"installed"; + installed = [NSString stringWithUTF8String:ver.VerStr()]; + } else if (!cache[target].CandidateVerIter(cache).end()) + reason = @"uninstalled"; + else if (target->ProvidesList == 0) + reason = @"uninstallable"; + else + reason = @"virtual"; + } + + NSDictionary *version(start.TargetVer() == 0 ? [NSNull null] : [NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:start.CompType()], @"operator", + [NSString stringWithUTF8String:start.TargetVer()], @"value", + nil]); + + [clauses addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:start.TargetPkg().Name()], @"package", + version, @"version", + reason, @"reason", + installed, @"installed", + nil]]; + + // yes, seriously. (wtf?) + if (start == end) + break; + ++start; + } + } + } + pkgDepCache::StateCache &state(cache[iterator]); - NSString *name([package name]); + static Pcre special_r("^(firmware$|gsc\\.|cy\\+)"); if (state.NewInstall()) - [installing addObject:name]; + [installs addObject:name]; else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall) - [reinstalling addObject:name]; + [reinstalls addObject:name]; else if (state.Upgrade()) - [upgrading addObject:name]; + [upgrades addObject:name]; else if (state.Downgrade()) - [downgrading addObject:name]; - else if (state.Delete()) { + [downgrades addObject:name]; + else if (!state.Delete()) + continue; + else if (special_r(name)) + [issues_ addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNull null], @"package", + [NSArray arrayWithObjects: + [NSDictionary dictionaryWithObjectsAndKeys: + @"Conflicts", @"relationship", + [NSArray arrayWithObjects: + [NSDictionary dictionaryWithObjectsAndKeys: + name, @"package", + [NSNull null], @"version", + @"installed", @"reason", + nil], + nil], @"clauses", + nil], + nil], @"reasons", + nil]]; + else { if ([package essential]) remove = true; - [removing addObject:name]; - } else continue; + [removes addObject:name]; + } substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator)); substrate_ |= DepSubstrate(iterator.CurrentVer()); @@ -4288,7 +4714,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { message:UCLocalize("REMOVING_ESSENTIALS_EX") delegate:self cancelButtonTitle:[NSString stringWithFormat:parenthetical, UCLocalize("CANCEL_OPERATION"), UCLocalize("SAFE")] - otherButtonTitles:[NSString stringWithFormat:parenthetical, UCLocalize("FORCE_REMOVAL"), UCLocalize("UNSAFE")], nil + otherButtonTitles: + [NSString stringWithFormat:parenthetical, UCLocalize("FORCE_REMOVAL"), UCLocalize("UNSAFE")], + nil ]; [essential_ setContext:@"remove"]; @@ -4304,24 +4732,20 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [essential_ setContext:@"unable"]; } - changes_ = [[NSArray alloc] initWithObjects: - installing, - reinstalling, - upgrading, - downgrading, - removing, + changes_ = [[NSDictionary alloc] initWithObjectsAndKeys: + installs, @"installs", + reinstalls, @"reinstalls", + upgrades, @"upgrades", + downgrades, @"downgrades", + removes, @"removes", nil]; - issues_ = [database_ issues]; - if (issues_ != nil) - issues_ = [issues_ retain]; - - sizes_ = [[NSArray alloc] initWithObjects: - SizeString([database_ fetcher].FetchNeeded()), - SizeString([database_ fetcher].PartialPresent()), + sizes_ = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInteger:[database_ fetcher].FetchNeeded()], @"downloading", + [NSNumber numberWithInteger:[database_ fetcher].PartialPresent()], @"resuming", nil]; - [self loadURL:[NSURL URLWithString:CydiaURL(@"ui/ios/confirm/")]]; + [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/confirm/", UI_]]]; [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:UCLocalize("CANCEL") @@ -4332,9 +4756,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } return self; } +#if !AlwaysReload - (void) applyRightButton { -#if !AlwaysReload && !IgnoreInstall - if (issues_ == nil && ![self isLoading]) + if ([issues_ count] == 0 && ![self isLoading]) [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:UCLocalize("CONFIRM") style:UIBarButtonItemStyleDone @@ -4342,11 +4766,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { action:@selector(confirmButtonClicked) ] autorelease]]; else - [super applyRightButton]; -#else - [[self navigationItem] setRightBarButtonItem:nil]; -#endif + [[self navigationItem] setRightBarButtonItem:nil]; } +#endif - (void) cancelButtonClicked { [self dismissModalViewControllerAnimated:YES]; @@ -4355,16 +4777,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { #if !AlwaysReload - (void) confirmButtonClicked { -#if IgnoreInstall - return; -#endif if (essential_ != nil) [essential_ show]; - else { - if (substrate_) - Finish_ = 2; - [delegate_ confirmWithNavigationController:[self navigationController]]; - } + else + [self complete]; } #endif @@ -4372,195 +4788,173 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* }}} */ /* Progress Data {{{ */ -@interface ProgressData : NSObject { - SEL selector_; - // XXX: should these really both be _transient? - _transient id target_; - _transient id object_; -} +@interface CydiaProgressData : NSObject { + _transient id delegate_; -- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object; + bool running_; + float progress_; + + _H events_; + _H title_; + + _H status_; + _H finish_; +} -- (SEL) selector; -- (id) target; -- (id) object; @end -@implementation ProgressData +@implementation CydiaProgressData + ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects: + @"events", + @"finish", + @"progress", + @"running", + @"title", + nil]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} -- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object { +- (id) init { if ((self = [super init]) != nil) { - selector_ = selector; - target_ = target; - object_ = object; + events_ = [NSMutableArray arrayWithCapacity:32]; } return self; } -- (SEL) selector { - return selector_; +- (void) setDelegate:(id)delegate { + delegate_ = delegate; +} + +- (void) setProgress:(float)value { + progress_ = value; +} + +- (NSNumber *) progress { + return [NSNumber numberWithFloat:progress_]; +} + +- (NSArray *) events { + return events_; +} + +- (void) removeAllEvents { + [events_ removeAllObjects]; } -- (id) target { - return target_; +- (void) addEvent:(CydiaProgressEvent *)event { + [events_ addObject:event]; } -- (id) object { - return object_; +- (void) setTitle:(NSString *)text { + title_ = text; +} + +- (NSString *) title { + return title_; +} + +- (void) setFinish:(NSString *)text { + finish_ = text; +} + +- (NSString *) finish { + return (id) finish_ ?: [NSNull null]; +} + +- (void) setRunning:(bool)running { + running_ = running; +} + +- (NSNumber *) running { + return running_ ? (NSNumber *) kCFBooleanTrue : (NSNumber *) kCFBooleanFalse; } @end /* }}} */ /* Progress Controller {{{ */ -@interface ProgressController : CYViewController < - ConfigurationDelegate, +@interface ProgressController : CYBrowserController < ProgressDelegate > { _transient Database *database_; - UIProgressBar *progress_; - UITextView *output_; - UITextLabel *status_; - UIPushButton *close_; - BOOL running_; - SHA1SumValue springlist_; - SHA1SumValue notifyconf_; - NSString *title_; + _H progress_; + unsigned cancel_; } - (id) initWithDatabase:(Database *)database delegate:(id)delegate; -- (void) _retachThread; -- (void) _detachNewThreadData:(ProgressData *)data; -- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title; +- (void) invoke:(NSInvocation *)invocation withTitle:(NSString *)title; -- (BOOL) isRunning; +- (void) setTitle:(NSString *)title; +- (void) setCancellable:(bool)cancellable; @end -@protocol ProgressControllerDelegate -- (void) progressControllerIsComplete:(ProgressController *)sender; -@end - @implementation ProgressController - (void) dealloc { - [database_ setDelegate:nil]; - [progress_ release]; - [output_ release]; - [status_ release]; - [close_ release]; - if (title_ != nil) - [title_ release]; + [database_ setProgressDelegate:nil]; + [progress_ setDelegate:nil]; [super dealloc]; } - (id) initWithDatabase:(Database *)database delegate:(id)delegate { if ((self = [super init]) != nil) { database_ = database; - [database_ setDelegate:self]; delegate_ = delegate; - [[self view] setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]]; - - progress_ = [[UIProgressBar alloc] init]; - [progress_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)]; - [progress_ setStyle:0]; + [database_ setProgressDelegate:self]; - status_ = [[UITextLabel alloc] init]; - [status_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)]; - [status_ setColor:[UIColor whiteColor]]; - [status_ setBackgroundColor:[UIColor clearColor]]; - [status_ setCentersHorizontally:YES]; - //[status_ setFont:font]; - - output_ = [[UITextView alloc] init]; - [output_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; - //[output_ setTextFont:@"Courier New"]; - [output_ setFont:[[output_ font] fontWithSize:12]]; - [output_ setTextColor:[UIColor whiteColor]]; - [output_ setBackgroundColor:[UIColor clearColor]]; - [output_ setMarginTop:0]; - [output_ setAllowsRubberBanding:YES]; - [output_ setEditable:NO]; - [[self view] addSubview:output_]; - - close_ = [[UIPushButton alloc] init]; - [close_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)]; - [close_ setAutosizesToFit:NO]; - [close_ setDrawsShadow:YES]; - [close_ setStretchBackground:YES]; - [close_ setEnabled:YES]; - [close_ setTitleFont:[UIFont boldSystemFontOfSize:22]]; - [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:UIControlEventTouchUpInside]; - [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0]; - [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1]; + progress_ = [[[CydiaProgressData alloc] init] autorelease]; + [progress_ setDelegate:self]; } return self; } -- (void) positionViews { - CGRect bounds = [[self view] bounds]; - CGSize prgsize = [UIProgressBar defaultSize]; - - CGRect prgrect = {{ - (bounds.size.width - prgsize.width) / 2, - bounds.size.height - prgsize.height - 20 - }, prgsize}; +- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + [super webView:view didClearWindowObject:window forFrame:frame]; + [window setValue:progress_ forKey:@"cydiaProgress"]; +} - float closewidth = std::min(bounds.size.width - 20, 300.0f); +- (void) updateProgress { + [self dispatchEvent:@"CydiaProgressUpdate"]; +} - [progress_ setFrame:prgrect]; - [status_ setFrame:CGRectMake( - 10, - bounds.size.height - prgsize.height - 50, - bounds.size.width - 20, - 24 - )]; - [output_ setFrame:CGRectMake( - 10, - 20, - bounds.size.width - 20, - bounds.size.height - 96 - )]; - [close_ setFrame:CGRectMake( - (bounds.size.width - closewidth) / 2, - bounds.size.height - prgsize.height - 50, - closewidth, - 32 + prgsize.height - )]; +- (void) updateCancel { + [[self navigationItem] setLeftBarButtonItem:(cancel_ == 1 ? [[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("CANCEL") + style:UIBarButtonItemStylePlain + target:self + action:@selector(cancel) + ] autorelease] : nil)]; } - (void) viewWillAppear:(BOOL)animated { - [super viewDidAppear:animated]; - [[self navigationItem] setHidesBackButton:YES]; - [[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlack]; + if (![self hasLoaded]) { + [scroller_ setBackgroundColor:[UIColor blackColor]]; + [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/progress/", UI_]]]; + } - [self positionViews]; -} + [super viewDidAppear:animated]; -- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - [self positionViews]; -} + [[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlack]; -- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { - NSString *context([alert context]); + [[self navigationItem] setHidesBackButton:YES]; - if ([context isEqualToString:@"conffile"]) { - FILE *input = [database_ input]; - if (button == [alert cancelButtonIndex]) - fprintf(input, "N\n"); - else if (button == [alert firstOtherButtonIndex]) - fprintf(input, "Y\n"); - fflush(input); - } + [self updateCancel]; } -- (void) closeButtonPushed { - running_ = NO; - +- (void) close { UpdateExternalStatus(0); switch (Finish_) { case 0: - [self dismissModalViewControllerAnimated:YES]; break; case 1: @@ -4592,19 +4986,32 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { reboot2(RB_AUTOBOOT); break; } + + [super close]; } -- (void) _retachThread { - [[self navigationItem] setTitle:UCLocalize("COMPLETE")]; +- (void) setTitle:(NSString *)title { + [progress_ setTitle:title]; + [self updateProgress]; +} + +- (UIBarButtonItem *) rightButton { + return [[progress_ running] boolValue] ? nil : [[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("CLOSE") + style:UIBarButtonItemStylePlain + target:self + action:@selector(close) + ] autorelease]; +} - [[self view] addSubview:close_]; - [progress_ removeFromSuperview]; - [status_ removeFromSuperview]; +- (void) invoke:(NSInvocation *)invocation withTitle:(NSString *)title { + UpdateExternalStatus(1); - [database_ popErrorWithTitle:title_]; - [delegate_ progressControllerIsComplete:self]; + [progress_ setRunning:true]; + [self setTitle:title]; + // implicit updateProgress - if (Finish_ < 4) { + SHA1SumValue notifyconf; { FileFd file; if (!file.Open(NotifyConfig_, FileFd::ReadOnly)) _error->Discard(); @@ -4612,12 +5019,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { MMap mmap(file, MMap::ReadOnly); SHA1Summation sha1; sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); - if (!(notifyconf_ == sha1.Result())) - Finish_ = 4; + notifyconf = sha1.Result(); } } - if (Finish_ < 3) { + SHA1SumValue springlist; { FileFd file; if (!file.Open(SpringBoard_, FileFd::ReadOnly)) _error->Discard(); @@ -4625,57 +5031,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { MMap mmap(file, MMap::ReadOnly); SHA1Summation sha1; sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); - if (!(springlist_ == sha1.Result())) - Finish_ = 3; + springlist = sha1.Result(); } } - switch (Finish_) { - case 0: [close_ setTitle:UCLocalize("RETURN_TO_CYDIA")]; break; /* XXX: Maybe UCLocalize("DONE")? */ - case 1: [close_ setTitle:UCLocalize("CLOSE_CYDIA")]; break; - case 2: [close_ setTitle:UCLocalize("RESTART_SPRINGBOARD")]; break; - case 3: [close_ setTitle:UCLocalize("RELOAD_SPRINGBOARD")]; break; - case 4: [close_ setTitle:UCLocalize("REBOOT_DEVICE")]; break; + if (invocation != nil) { + [invocation yieldToSelector:@selector(invoke)]; + [self setTitle:@"COMPLETE"]; } - _trace(); - system("su -c /usr/bin/uicache mobile"); - _trace(); - - UpdateExternalStatus(Finish_ == 0 ? 2 : 0); - - [delegate_ setStatusBarShowsProgress:NO]; -} - -- (void) _detachNewThreadData:(ProgressData *)data { _pooled - [[data target] performSelector:[data selector] withObject:[data object]]; - [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES]; -} - -- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title { - UpdateExternalStatus(1); - - if (title_ != nil) - [title_ release]; - if (title == nil) - title_ = nil; - else - title_ = [title retain]; - - [[self navigationItem] setTitle:title_]; - - [status_ setText:nil]; - [output_ setText:@""]; - [progress_ setProgress:0]; - - [close_ removeFromSuperview]; - [[self view] addSubview:progress_]; - [[self view] addSubview:status_]; - - [delegate_ setStatusBarShowsProgress:YES]; - running_ = YES; - - { + if (Finish_ < 4) { FileFd file; if (!file.Open(NotifyConfig_, FileFd::ReadOnly)) _error->Discard(); @@ -4683,11 +5048,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { MMap mmap(file, MMap::ReadOnly); SHA1Summation sha1; sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); - notifyconf_ = sha1.Result(); + if (!(notifyconf == sha1.Result())) + Finish_ = 4; } } - { + if (Finish_ < 3) { FileFd file; if (!file.Open(SpringBoard_, FileFd::ReadOnly)) _error->Discard(); @@ -4695,133 +5061,71 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { MMap mmap(file, MMap::ReadOnly); SHA1Summation sha1; sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); - springlist_ = sha1.Result(); + if (!(springlist == sha1.Result())) + Finish_ = 3; } } - [NSThread - detachNewThreadSelector:@selector(_detachNewThreadData:) - toTarget:self - withObject:[[[ProgressData alloc] - initWithSelector:selector - target:target - object:object - ] autorelease] - ]; -} - -- (void) repairWithSelector:(SEL)selector { - [self - detachNewThreadSelector:selector - toTarget:database_ - withObject:nil - title:UCLocalize("REPAIRING") - ]; -} + if (Finish_ < 2) { + if (RestartSubstrate_) + Finish_ = 2; + } -- (void) setConfigurationData:(NSString *)data { - [self - performSelectorOnMainThread:@selector(_setConfigurationData:) - withObject:data - waitUntilDone:YES - ]; -} + RestartSubstrate_ = false; -- (void) setProgressError:(NSString *)error withTitle:(NSString *)title { - CYAlertView *sheet([[[CYAlertView alloc] - initWithTitle:title - buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil] - defaultButtonIndex:0 - ] autorelease]); + switch (Finish_) { + case 0: [progress_ setFinish:UCLocalize("RETURN_TO_CYDIA")]; break; /* XXX: Maybe UCLocalize("DONE")? */ + case 1: [progress_ setFinish:UCLocalize("CLOSE_CYDIA")]; break; + case 2: [progress_ setFinish:UCLocalize("RESTART_SPRINGBOARD")]; break; + case 3: [progress_ setFinish:UCLocalize("RELOAD_SPRINGBOARD")]; break; + case 4: [progress_ setFinish:UCLocalize("REBOOT_DEVICE")]; break; + } - [sheet setMessage:error]; - [sheet yieldToPopupAlertAnimated:YES]; - [sheet dismiss]; -} + _trace(); + system("su -c /usr/bin/uicache mobile"); + _trace(); -- (void) setProgressTitle:(NSString *)title { - [self - performSelectorOnMainThread:@selector(_setProgressTitle:) - withObject:title - waitUntilDone:YES - ]; -} + UpdateExternalStatus(Finish_ == 0 ? 0 : 2); -- (void) setProgressPercent:(float)percent { - [self - performSelectorOnMainThread:@selector(_setProgressPercent:) - withObject:[NSNumber numberWithFloat:percent] - waitUntilDone:YES - ]; -} + [progress_ setRunning:false]; + [self updateProgress]; -- (void) startProgress { + [self applyRightButton]; } -- (void) addProgressOutput:(NSString *)output { - [self - performSelectorOnMainThread:@selector(_addProgressOutput:) - withObject:output - waitUntilDone:YES - ]; +- (void) addProgressEvent:(CydiaProgressEvent *)event { + [progress_ addEvent:event]; + [self updateProgress]; } -- (bool) isCancelling:(size_t)received { - return false; +- (bool) isProgressCancelled { + return cancel_ == 2; } -- (void) _setConfigurationData:(NSString *)data { - static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$"); - - if (!conffile_r(data)) { - lprintf("E:invalid conffile\n"); - return; - } - - NSString *ofile = conffile_r[1]; - //NSString *nfile = conffile_r[2]; - - UIAlertView *alert = [[[UIAlertView alloc] - initWithTitle:UCLocalize("CONFIGURATION_UPGRADE") - message:[NSString stringWithFormat:@"%@\n\n%@", UCLocalize("CONFIGURATION_UPGRADE_EX"), ofile] - delegate:self - cancelButtonTitle:UCLocalize("KEEP_OLD_COPY") - otherButtonTitles:UCLocalize("ACCEPT_NEW_COPY"), - // XXX: UCLocalize("SEE_WHAT_CHANGED"), - nil - ] autorelease]; - - [alert setContext:@"conffile"]; - [alert show]; +- (void) cancel { + cancel_ = 2; + [self updateCancel]; } -- (void) _setProgressTitle:(NSString *)title { - NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]); - for (size_t i(0), e([words count]); i != e; ++i) { - NSString *word([words objectAtIndex:i]); - if (Package *package = [database_ packageWithName:word]) - [words replaceObjectAtIndex:i withObject:[package name]]; - } +- (void) setCancellable:(bool)cancellable { + unsigned cancel(cancel_); - [status_ setText:[words componentsJoinedByString:@" "]]; -} + if (!cancellable) + cancel_ = 0; + else if (cancel_ == 0) + cancel_ = 1; -- (void) _setProgressPercent:(NSNumber *)percent { - [progress_ setProgress:[percent floatValue]]; + if (cancel != cancel_) + [self updateCancel]; } -- (void) _addProgressOutput:(NSString *)output { - [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]]; - CGSize size = [output_ contentSize]; - CGPoint offset = [output_ contentOffset]; - if (size.height - offset.y < [output_ frame].size.height + 20.f) { - CGRect rect = {{0, size.height-1}, {size.width, 1}}; - [output_ scrollRectToVisible:rect animated:YES]; - } +- (void) setProgressCancellable:(NSNumber *)cancellable { + [self setCancellable:[cancellable boolValue]]; } -- (BOOL) isRunning { - return running_; +- (void) setProgressPercent:(NSNumber *)percent { + [progress_ setProgress:[percent floatValue]]; + [self updateProgress]; } @end @@ -5407,10 +5711,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UIBarButtonItem *button_; } -- (id) initWithDatabase:(Database *)database; - -- (void) setPackage:(Package *)package withName:(NSString *)name; -- (void) setPackage:(Package *)package; +- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name; @end @@ -5430,12 +5731,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super dealloc]; } -- (void) release { - [super release]; -} - - (NSURL *) navigationURL { - return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", [package_ id]]]; + return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", name_]]; } /* XXX: this is not safe at all... localization of /fail/ */ @@ -5526,36 +5823,27 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } #endif -- (void) viewWillAppear:(BOOL)animated { - if (![self hasLoaded]) - [self loadURL:[NSURL URLWithString:CydiaURL(@"ui/ios/package/")]]; - [super viewWillAppear:animated]; -} - -- (id) initWithDatabase:(Database *)database { +- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name { if ((self = [super init]) != nil) { database_ = database; buttons_ = [[NSMutableArray alloc] initWithCapacity:4]; + name_ = [[NSString alloc] initWithString:name]; + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/package/#!/%@", UI_, name_]]]; } return self; } -- (void) setPackage:(Package *)package withName:(NSString *)name { - if (package_ != nil) { +- (void) reloadData { + if (package_ != nil) [package_ autorelease]; - package_ = nil; - } - - if (name_ != nil) - [name_ autorelease]; - name_ = [[NSString alloc] initWithString:name]; + package_ = [database_ packageWithName:name_]; [buttons_ removeAllObjects]; - if (package != nil) { - [package parse]; + if (package_ != nil) { + [package_ parse]; - package_ = [package retain]; - commercial_ = [package isCommercial]; + package_ = [package_ retain]; + commercial_ = [package_ isCommercial]; if ([package_ mode] != nil) [buttons_ addObject:UCLocalize("CLEAR")]; @@ -5587,22 +5875,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { action:@selector(customButtonClicked) ]; - [self loadURL:[NSURL URLWithString:CydiaURL([NSString stringWithFormat:@"ui/ios/package/#!/%@", name])]]; -} - -- (void) setPackage:(Package *)package { - [self setPackage:package withName:[package id]]; + [super reloadData]; } - (bool) isLoading { return commercial_ ? [super isLoading] : false; } -- (void) reloadData { - [super reloadData]; - [self setPackage:[database_ packageWithName:name_] withName:name_]; -} - @end /* }}} */ @@ -5716,8 +5995,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) didSelectPackage:(Package *)package { - CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_] autorelease]); - [view setPackage:package]; + CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id]] autorelease]); [view setDelegate:delegate_]; [[self navigationController] pushViewController:view animated:YES]; } @@ -5971,7 +6249,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if ((self = [super initWithDatabase:database title:title]) != nil) { [self setFilter:filter]; [self setObject:object]; - [self reloadData]; } return self; } @@ -5981,6 +6258,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Home Controller {{{ */ @interface HomeController : CYBrowserController { } + @end @implementation HomeController @@ -6030,7 +6308,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) viewWillAppear:(BOOL)animated { if (![self hasLoaded]) - [self loadURL:[NSURL URLWithString:CydiaURL(@"ui/ios/home/")]]; + [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/home/", UI_]]]; [super viewWillAppear:animated]; @@ -6054,6 +6332,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) queueStatusDidChange; + @end @implementation ManageController @@ -6064,14 +6343,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) viewWillAppear:(BOOL)animated { if (![self hasLoaded]) - [self loadURL:[NSURL URLWithString:CydiaURL(@"ui/ios/manage/")]]; + [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/manage/", UI_]]]; [super viewWillAppear:animated]; } - (void) viewDidLoad { - [[self navigationItem] setTitle:UCLocalize("MANAGE")]; - [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:UCLocalize("SETTINGS") style:UIBarButtonItemStylePlain @@ -6170,14 +6447,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [prompt_ setFrame:prmrect]; } -- (void)setFrame:(CGRect)frame { +- (void) setFrame:(CGRect)frame { [super setFrame:frame]; - [self positionViews]; } - (id) initWithFrame:(CGRect)frame delegate:(id)delegate { - if ((self = [super initWithFrame:frame])) { + if ((self = [super initWithFrame:frame]) != nil) { [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; [self setBarStyle:UIBarStyleBlack]; @@ -6214,18 +6490,20 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } return self; } -- (void) cancel { - [cancel_ removeFromSuperview]; +- (void) setCancellable:(bool)cancellable { + if (cancellable) + [self addSubview:cancel_]; + else + [cancel_ removeFromSuperview]; } - (void) start { [prompt_ setText:UCLocalize("UPDATING_DATABASE")]; [progress_ setProgress:0]; - [self addSubview:cancel_]; } - (void) stop { - [cancel_ removeFromSuperview]; + [self setCancellable:NO]; } - (void) setPrompt:(NSString *)prompt { @@ -6243,6 +6521,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Cydia Tab Bar Controller {{{ */ @interface CYTabBarController : UITabBarController < + UITabBarControllerDelegate, ProgressDelegate > { _transient Database *database_; @@ -6254,6 +6533,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _transient NSObject *updatedelegate_; id root_; + UIViewController *remembered_; + _transient UIViewController *transient_; } - (NSArray *) navigationURLCollection; @@ -6266,6 +6547,37 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation CYTabBarController +- (void) setUnselectedViewController:(UIViewController *)transient { + NSMutableArray *controllers = [[self viewControllers] mutableCopy]; + if (transient != nil) { + if (transient_ == nil) + remembered_ = [[controllers objectAtIndex:0] retain]; + transient_ = transient; + [transient_ setTabBarItem:[remembered_ tabBarItem]]; + [controllers replaceObjectAtIndex:0 withObject:transient_]; + [self setSelectedIndex:0]; + [self setViewControllers:controllers]; + [self concealTabBarSelection]; + } else if (remembered_ != nil) { + [remembered_ setTabBarItem:[transient_ tabBarItem]]; + transient_ = transient; + [controllers replaceObjectAtIndex:0 withObject:remembered_]; + [remembered_ release]; + remembered_ = nil; + [self setViewControllers:controllers]; + [self revealTabBarSelection]; + } +} + +- (UIViewController *) unselectedViewController { + return transient_; +} + +- (void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController { + if ([self unselectedViewController]) + [self setUnselectedViewController:nil]; +} + - (NSArray *) navigationURLCollection { NSMutableArray *items([NSMutableArray array]); @@ -6283,7 +6595,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { for (CYViewController *controller in [self viewControllers]) [controller reloadData]; - [(CYNavigationController *)[self transientViewController] reloadData]; + [(CYNavigationController *)[self unselectedViewController] reloadData]; } - (void) dealloc { @@ -6296,6 +6608,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (id) initWithDatabase:(Database *)database { if ((self = [super init]) != nil) { database_ = database; + [self setDelegate:self]; [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil]; @@ -6362,52 +6675,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return updating_; } -- (void) setProgressError:(NSString *)error withTitle:(NSString *)title { - [refreshbar_ setPrompt:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), error]]; -} - -- (void) startProgress { -} - -- (void) setProgressTitle:(NSString *)title { - [self - performSelectorOnMainThread:@selector(_setProgressTitle:) - withObject:title - waitUntilDone:YES - ]; +- (void) addProgressEvent:(CydiaProgressEvent *)event { + [refreshbar_ setPrompt:[event compoundMessage]]; } -- (bool) isCancelling:(size_t)received { +- (bool) isProgressCancelled { return !updating_; } -- (void) setProgressPercent:(float)percent { - [self - performSelectorOnMainThread:@selector(_setProgressPercent:) - withObject:[NSNumber numberWithFloat:percent] - waitUntilDone:YES - ]; -} - -- (void) addProgressOutput:(NSString *)output { - [self - performSelectorOnMainThread:@selector(_addProgressOutput:) - withObject:output - waitUntilDone:YES - ]; -} - -- (void) _setProgressTitle:(NSString *)title { - [refreshbar_ setPrompt:title]; +- (void) setProgressCancellable:(NSNumber *)cancellable { + [refreshbar_ setCancellable:(updating_ && [cancellable boolValue])]; } -- (void) _setProgressPercent:(NSNumber *)percent { +- (void) setProgressPercent:(NSNumber *)percent { [refreshbar_ setProgress:[percent floatValue]]; } -- (void) _addProgressOutput:(NSString *)output { -} - - (void) setUpdateDelegate:(id)delegate { updatedelegate_ = delegate; } @@ -6534,10 +6817,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation CYNavigationController -- (void) dealloc { - [super dealloc]; -} - - (NSArray *) navigationURLCollection { NSMutableArray *stack([NSMutableArray array]); @@ -6552,6 +6831,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) reloadData { for (CYViewController *page in [self viewControllers]) { + // Only reload controllers that have already loaded. + // This prevents a page from accidentally loading too + // early if it hasn't been shown on the screen yet. if ([page hasLoaded]) [page reloadData]; } @@ -6863,7 +7145,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { section = [sections objectForKey:key]; if (section == nil) { _profile(SectionsView$reloadData$Section$Allocate) - section = [[[Section alloc] initWithName:name localize:YES] autorelease]; + section = [[[Section alloc] initWithName:key localize:YES] autorelease]; [sections setObject:section forKey:key]; _end } @@ -6899,8 +7181,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _trace(); } -- (void)editButtonClicked { - [self setEditing:!editing_]; +- (void) editButtonClicked { + [self setEditing:(!editing_)]; } @end @@ -6999,9 +7281,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path { Package *package([self packageAtIndexPath:path]); - CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_] autorelease]); + CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id]] autorelease]); [view setDelegate:delegate_]; - [view setPackage:package]; [[self navigationController] pushViewController:view animated:YES]; return path; } @@ -7044,6 +7325,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } return self; } +// this mostly works because reloadData (below) is @synchronized (database_) +// XXX: that said, I've been running into problems with NSRangeExceptions :( - (void) _reloadPackages:(NSArray *)packages { CFRelease(packages_); packages_ = CFArrayCreateMutable(kCFAllocatorDefault, [packages count], NULL); @@ -7211,7 +7494,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } return self; } -- (void)viewDidAppear:(BOOL)animated { +- (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (!searchloaded_) { @@ -7283,22 +7566,29 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (package_ == nil) return 0; - return 1; + if ([package_ installed] == nil) + return 1; + else + return 2; } - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (package_ == nil) return 0; - return 2; + // both sections contain just one item right now. + return 1; } - (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - return UCLocalize("CHANGE_PACKAGE_SETTINGS"); + return nil; } - (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { - return UCLocalize("SHOW_ALL_CHANGES_EX"); + if (section == 0) + return UCLocalize("SHOW_ALL_CHANGES_EX"); + else + return UCLocalize("IGNORE_UPGRADES_EX"); } - (void) onSubscribed:(id)control { @@ -7309,15 +7599,50 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [delegate_ updateData]; } +- (void) _updateIgnored { + const char *package([name_ UTF8String]); + bool on([ignoredSwitch_ isOn]); + + pid_t pid(ExecFork()); + if (pid == 0) { + FILE *dpkg(popen("dpkg --set-selections", "w")); + fwrite(package, strlen(package), 1, dpkg); + + if (on) + fwrite(" hold\n", 6, 1, dpkg); + else + fwrite(" install\n", 9, 1, dpkg); + + pclose(dpkg); + + exit(0); + _assert(false); + } + + _forever { + int status; + int result(waitpid(pid, &status, 0)); + + if (result != -1) { + _assert(result == pid); + break; + } + } +} + - (void) onIgnored:(id)control { - // TODO: set Held state - possibly call out to dpkg, etc. + NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(_updateIgnored)]]); + [invocation setTarget:self]; + [invocation setSelector:@selector(_updateIgnored)]; + + [delegate_ reloadDataWithInvocation:invocation]; } - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (package_ == nil) return nil; - switch ([indexPath row]) { + switch ([indexPath section]) { case 0: return subscribedCell_; case 1: return ignoredCell_; @@ -7343,8 +7668,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { ignoredSwitch_ = [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 20)]; [ignoredSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin]; [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:UIControlEventValueChanged]; - // Disable this switch, since it only reflects (not modifies) the ignored state. - [ignoredSwitch_ setUserInteractionEnabled:NO]; subscribedCell_ = [[UITableViewCell alloc] init]; [subscribedCell_ setText:UCLocalize("SHOW_ALL_CHANGES")]; @@ -7355,8 +7678,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [ignoredCell_ setText:UCLocalize("IGNORE_UPGRADES")]; [ignoredCell_ setAccessoryView:ignoredSwitch_]; [ignoredCell_ setSelectionStyle:UITableViewCellSelectionStyleNone]; - // FIXME: Ignored state is not saved. - [ignoredCell_ setUserInteractionEnabled:NO]; } - (void) viewDidLoad { @@ -7381,7 +7702,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (id) initWithDatabase:(Database *)database package:(NSString *)package { - if ((self = [super init])) { + if ((self = [super init]) != nil) { database_ = database; name_ = [package retain]; } return self; @@ -7393,11 +7714,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (package_ != nil) [package_ autorelease]; package_ = [database_ packageWithName:name_]; + if (package_ != nil) { - [package_ retain]; + package_ = [package_ retain]; [subscribedSwitch_ setOn:([package_ subscribed] ? 1 : 0) animated:NO]; [ignoredSwitch_ setOn:([package_ ignored] ? 1 : 0) animated:NO]; - } + } // XXX: what now, G? [table_ reloadData]; } @@ -7565,7 +7887,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* }}} */ /* Source Controller {{{ */ @interface SourceController : FilteredPackageListController { - Source *source_; + _transient Source *source_; + NSString *key_; } - (id) initWithDatabase:(Database *)database source:(Source *)source; @@ -7579,12 +7902,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (id) initWithDatabase:(Database *)database source:(Source *)source { - source_ = source; - if ((self = [super initWithDatabase:database title:[source label] filter:@selector(isVisibleInSource:) with:source]) != nil) { + source_ = source; + key_ = [[source key] retain]; } return self; } +- (void) reloadData { + source_ = [database_ sourceWithKey:key_]; + [key_ release]; + key_ = [[source_ key] retain]; + [self setObject:source_]; + [[self navigationItem] setTitle:[source_ label]]; + + [super reloadData]; +} + @end /* }}} */ /* Sources Controller {{{ */ @@ -7709,24 +8042,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [[self navigationController] pushViewController:controller animated:YES]; } -- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { +- (BOOL) tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { Source *source = [self sourceAtIndexPath:indexPath]; return [source record] != nil; } -- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { +- (void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { Source *source = [self sourceAtIndexPath:indexPath]; [Sources_ removeObjectForKey:[source key]]; [delegate_ syncData]; } - (void) complete { - [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: - @"deb", @"Type", - href_, @"URI", - @"./", @"Distribution", - nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]]; - + [delegate_ addTrivialSource:href_]; [delegate_ syncData]; } @@ -7779,7 +8107,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { message:warning delegate:self cancelButtonTitle:UCLocalize("CANCEL") - otherButtonTitles:UCLocalize("ADD_ANYWAY"), nil + otherButtonTitles: + UCLocalize("ADD_ANYWAY"), + nil ] autorelease]; [alert setContext:@"warning"]; @@ -7811,9 +8141,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [alert show]; } - [delegate_ setStatusBarShowsProgress:NO]; - [delegate_ removeProgressHUD:hud_]; + [delegate_ releaseNetworkActivityIndicator]; + [delegate_ removeProgressHUD:hud_]; [hud_ autorelease]; hud_ = nil; @@ -7866,7 +8196,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease]; } -- (void)alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { +- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { NSString *context([alert context]); if ([context isEqualToString:@"source"]) { @@ -7892,6 +8222,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { // XXX: this is stupid hud_ = [[delegate_ addProgressHUD] retain]; [hud_ setText:UCLocalize("VERIFYING_URL")]; + [delegate_ retainNetworkActivityIndicator]; } break; case 0: @@ -7956,7 +8287,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super reloadData]; pkgSourceList list; - if (!list.ReadMainList()) + if ([database_ popErrorWithTitle:UCLocalize("SOURCES") forOperation:list.ReadMainList()]) return; [sources_ removeAllObjects]; @@ -7984,7 +8315,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { message:nil delegate:self cancelButtonTitle:UCLocalize("CANCEL") - otherButtonTitles:UCLocalize("ADD_SOURCE"), nil + otherButtonTitles: + UCLocalize("ADD_SOURCE"), + nil ] autorelease]; [alert setContext:@"source"]; @@ -8117,7 +8450,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (id) initWithDatabase:(Database *)database delegate:(id)delegate { - if ((self = [super init])) { + if ((self = [super init]) != nil) { database_ = database; roledelegate_ = delegate; } return self; @@ -8209,7 +8542,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return 0; // :( } -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { +- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { return nil; // This method is required by the protocol. } @@ -8247,6 +8580,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UILabel *status_; UILabel *caption_; } + @end @implementation StashController @@ -8317,7 +8651,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @interface Cydia : UIApplication < ConfirmationControllerDelegate, - ProgressControllerDelegate, + DatabaseDelegate, CydiaDelegate, UINavigationControllerDelegate, UITabBarControllerDelegate @@ -8326,6 +8660,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UIWindow *window_; CYTabBarController *tabbar_; + CYEmulatedLoadingController *emulated_; NSMutableArray *essential_; NSMutableArray *broken_; @@ -8365,7 +8700,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { message:UCLocalize("HALFINSTALLED_PACKAGE_EX") delegate:self cancelButtonTitle:UCLocalize("FORCIBLY_CLEAR") - otherButtonTitles:UCLocalize("TEMPORARY_IGNORE"), nil + otherButtonTitles: + UCLocalize("TEMPORARY_IGNORE"), + nil ] autorelease]; [alert setContext:@"fixhalf"]; @@ -8378,7 +8715,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { message:UCLocalize("ESSENTIAL_UPGRADE_EX") delegate:self cancelButtonTitle:UCLocalize("TEMPORARY_IGNORE") - otherButtonTitles:UCLocalize("UPGRADE_ESSENTIAL"), UCLocalize("COMPLETE_UPGRADE"), nil + otherButtonTitles: + UCLocalize("UPGRADE_ESSENTIAL"), + UCLocalize("COMPLETE_UPGRADE"), + nil ] autorelease]; [alert setContext:@"upgrade"]; @@ -8429,11 +8769,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [[navigation tabBarItem] setBadgeValue:(Queuing_ ? UCLocalize("Q_D") : nil)]; } -- (void) _refreshIfPossible { +- (void) _refreshIfPossible:(NSDate *)update { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; bool recently = false; - NSDate *update([Metadata_ objectForKey:@"LastUpdate"]); if (update != nil) { NSTimeInterval interval([update timeIntervalSinceNow]); if (interval <= 0 && interval > -(15*60)) @@ -8481,14 +8820,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) refreshIfPossible { - [NSThread detachNewThreadSelector:@selector(_refreshIfPossible) toTarget:self withObject:nil]; + [NSThread detachNewThreadSelector:@selector(_refreshIfPossible:) toTarget:self withObject:[Metadata_ objectForKey:@"LastUpdate"]]; } -- (void) _reloadData { +- (void) _reloadDataWithInvocation:(NSInvocation *)invocation { UIProgressHUD *hud(loaded_ ? [self addProgressHUD] : nil); [hud setText:UCLocalize("RELOADING_DATA")]; - [database_ yieldToSelector:@selector(reloadData) withObject:nil]; + [database_ yieldToSelector:@selector(reloadDataWithInvocation:) withObject:invocation]; if (hud != nil) [self removeProgressHUD:hud]; @@ -8538,7 +8877,48 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [database_ update]; } +- (void) complete { + @synchronized (self) { + [self _reloadDataWithInvocation:nil]; + } +} + +- (void) presentModalViewController:(UIViewController *)controller { + UINavigationController *navigation([[[CYNavigationController alloc] initWithRootViewController:controller] autorelease]); + if (IsWildcat_) + [navigation setModalPresentationStyle:UIModalPresentationFormSheet]; + [((UIViewController *) emulated_ ?: tabbar_) presentModalViewController:navigation animated:YES]; +} + +- (ProgressController *) invokeNewProgress:(NSInvocation *)invocation forController:(UINavigationController *)navigation withTitle:(NSString *)title { + ProgressController *progress([[[ProgressController alloc] initWithDatabase:database_ delegate:self] autorelease]); + + if (navigation != nil) + [navigation pushViewController:progress animated:YES]; + else + [self presentModalViewController:progress]; + + [progress invoke:invocation withTitle:title]; + return progress; +} + +- (void) detachNewProgressSelector:(SEL)selector toTarget:(id)target forController:(UINavigationController *)navigation title:(NSString *)title { + [self invokeNewProgress:[NSInvocation invocationWithSelector:selector forTarget:target] forController:navigation withTitle:title]; +} + +- (void) repairWithInvocation:(NSInvocation *)invocation { + _trace(); + [self invokeNewProgress:invocation forController:nil withTitle:UCLocalize("REPAIRING")]; + _trace(); +} + +- (void) repairWithSelector:(SEL)selector { + [self performSelectorOnMainThread:@selector(repairWithInvocation:) withObject:[NSInvocation invocationWithSelector:selector forTarget:database_] waitUntilDone:YES]; +} + - (void) syncData { + [self _saveConfig]; + FILE *file(fopen("/etc/apt/sources.list.d/cydia.list", "w")); _assert(file != NULL); @@ -8554,28 +8934,31 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { fclose(file); - [self _saveConfig]; + [self detachNewProgressSelector:@selector(update_) toTarget:self forController:nil title:UCLocalize("UPDATING_SOURCES")]; - ProgressController *progress = [[[ProgressController alloc] initWithDatabase:database_ delegate:self] autorelease]; - CYNavigationController *navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease]; - if (IsWildcat_) - [navigation setModalPresentationStyle:UIModalPresentationFormSheet]; - [tabbar_ presentModalViewController:navigation animated:YES]; + [self complete]; +} - [progress - detachNewThreadSelector:@selector(update_) - toTarget:self - withObject:nil - title:UCLocalize("UPDATING_SOURCES") - ]; +- (void) addTrivialSource:(NSString *)href { + [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: + @"deb", @"Type", + href, @"URI", + @"./", @"Distribution", + nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href]]; + + Changed_ = true; } -- (void) reloadData { +- (void) reloadDataWithInvocation:(NSInvocation *)invocation { @synchronized (self) { - [self _reloadData]; + [self _reloadDataWithInvocation:invocation]; } } +- (void) reloadData { + [self reloadDataWithInvocation:nil]; +} + - (void) resolve { pkgProblemResolver *resolver = [database_ resolver]; @@ -8585,6 +8968,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (bool) perform { + // XXX: this is a really crappy way of doing this. + // like, seriously: this state machine is still broken, and cancelling this here doesn't really /fix/ that. + // for one, the user can still /start/ a reloading data event while they have a queue, which is stupid + // for two, this just means there is a race condition between the refresh completing and the confirmation controller appearing. + if ([tabbar_ updating]) + [tabbar_ cancelUpdate]; + if (![database_ prepare]) return false; @@ -8647,37 +9037,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } -- (void) complete { - @synchronized (self) { - [self _reloadData]; - } -} - - (void) confirmWithNavigationController:(UINavigationController *)navigation { Queuing_ = false; - - ProgressController *progress = [[[ProgressController alloc] initWithDatabase:database_ delegate:self] autorelease]; - - if (navigation != nil) { - [navigation pushViewController:progress animated:YES]; - } else { - navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease]; - if (IsWildcat_) - [navigation setModalPresentationStyle:UIModalPresentationFormSheet]; - [tabbar_ presentModalViewController:navigation animated:YES]; - } - - [progress - detachNewThreadSelector:@selector(perform) - toTarget:database_ - withObject:nil - title:UCLocalize("RUNNING") - ]; - ++locked_; -} - -- (void) progressControllerIsComplete:(ProgressController *)progress { + [self detachNewProgressSelector:@selector(perform) toTarget:database_ forController:navigation title:UCLocalize("RUNNING")]; --locked_; [self complete]; } @@ -8693,11 +9056,20 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) retainNetworkActivityIndicator { if (activity_++ == 0) [self setNetworkActivityIndicatorVisible:YES]; + +#if TraceLogging + NSLog(@"retainNetworkActivityIndicator->%d", activity_); +#endif } - (void) releaseNetworkActivityIndicator { if (--activity_ == 0) [self setNetworkActivityIndicatorVisible:NO]; + +#if TraceLogging + NSLog(@"releaseNetworkActivityIndicator->%d", activity_); +#endif + } - (void) cancelAndClear:(bool)clear { @@ -8716,8 +9088,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { NSString *context([alert context]); - if ([context isEqualToString:@"fixhalf"]) { - if (button == [alert firstOtherButtonIndex]) { + if ([context isEqualToString:@"conffile"]) { + FILE *input = [database_ input]; + if (button == [alert cancelButtonIndex]) + fprintf(input, "N\n"); + else if (button == [alert firstOtherButtonIndex]) + fprintf(input, "Y\n"); + fflush(input); + + [alert dismissWithClickedButtonIndex:-1 animated:YES]; + } else if ([context isEqualToString:@"fixhalf"]) { + if (button == [alert cancelButtonIndex]) { @synchronized (self) { for (Package *broken in broken_) { [broken remove]; @@ -8732,7 +9113,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self resolve]; [self perform]; } - } else if (button == [alert cancelButtonIndex]) { + } else if (button == [alert firstOtherButtonIndex]) { [broken_ removeAllObjects]; [self _loaded]; } @@ -8820,12 +9201,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (CYViewController *) pageForPackage:(NSString *)name { - CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_] autorelease]); - [view setPackage:[database_ packageWithName:name] withName:name]; - return view; + return [[[CYPackageController alloc] initWithDatabase:database_ forPackage:name] autorelease]; } -- (CYViewController *) pageForURL:(NSURL *)url { +- (CYViewController *) pageForURL:(NSURL *)url forExternal:(BOOL)external { NSString *scheme([[url scheme] lowercaseString]); if ([[url absoluteString] length] <= [scheme length] + 3) return nil; @@ -8845,14 +9224,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if ([base isEqualToString:@"url"]) { // This kind of URL can contain slashes in the argument, so we can't parse them below. NSString *destination = [[url absoluteString] substringFromIndex:([scheme length] + [@"://" length] + [base length] + [@"/" length])]; - controller = [[[CYBrowserController alloc] init] autorelease]; - [(CYBrowserController *)controller loadURL:[NSURL URLWithString:destination]]; - } else if ([components count] == 1) { - if ([base isEqualToString:@"storage"]) { - controller = [[[CYBrowserController alloc] init] autorelease]; - [(CYBrowserController *)controller loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]]]; - } - + controller = [[[CYBrowserController alloc] initWithURL:[NSURL URLWithString:destination]] autorelease]; + } else if (!external && [components count] == 1) { if ([base isEqualToString:@"manage"]) { controller = [[[ManageController alloc] init] autorelease]; } @@ -8887,37 +9260,32 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { controller = [self pageForPackage:argument]; } - if ([base isEqualToString:@"search"]) { + if (!external && [base isEqualToString:@"search"]) { controller = [[[SearchController alloc] initWithDatabase:database_] autorelease]; [(SearchController *)controller setSearchTerm:argument]; } - if ([base isEqualToString:@"sections"]) { + if (!external && [base isEqualToString:@"sections"]) { if ([argument isEqualToString:@"all"]) argument = nil; controller = [[[SectionController alloc] initWithDatabase:database_ section:argument] autorelease]; } - if ([base isEqualToString:@"sources"]) { + if (!external && [base isEqualToString:@"sources"]) { if ([argument isEqualToString:@"add"]) { controller = [[[SourcesController alloc] initWithDatabase:database_] autorelease]; [(SourcesController *)controller showAddSourcePrompt]; } else { - NSArray *sources = [database_ sources]; - for (Source *source in sources) { - if ([[source name] caseInsensitiveCompare:argument] == NSOrderedSame) { - controller = [[[SourceController alloc] initWithDatabase:database_ source:source] autorelease]; - break; - } - } + Source *source = [database_ sourceWithKey:argument]; + controller = [[[SourceController alloc] initWithDatabase:database_ source:source] autorelease]; } } - if ([base isEqualToString:@"launch"]) { + if (!external && [base isEqualToString:@"launch"]) { [self launchApplicationWithIdentifier:argument suspended:NO]; return nil; } - } else if ([components count] == 3) { + } else if (!external && [components count] == 3) { NSString *arg1 = [components objectAtIndex:1]; NSString *arg2 = [components objectAtIndex:2]; @@ -8937,13 +9305,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return controller; } -- (BOOL) openCydiaURL:(NSURL *)url { - CYViewController *page([self pageForURL:url]); +- (BOOL) openCydiaURL:(NSURL *)url forExternal:(BOOL)external { + CYViewController *page([self pageForURL:url forExternal:external]); if (page != nil) { CYNavigationController *nav = [[[CYNavigationController alloc] init] autorelease]; [nav setViewControllers:[NSArray arrayWithObject:page]]; - [tabbar_ setTransientViewController:nav]; + [tabbar_ setUnselectedViewController:nav]; } return page != nil; @@ -8953,7 +9321,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super applicationOpenURL:url]; if (!loaded_) starturl_ = [url retain]; - else [self openCydiaURL:url]; + else [self openCydiaURL:url forExternal:YES]; } - (void) applicationWillResignActive:(UIApplication *)application { @@ -8974,6 +9342,32 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self _saveConfig]; } +- (void) setConfigurationData:(NSString *)data { + static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$"); + + if (!conffile_r(data)) { + lprintf("E:invalid conffile\n"); + return; + } + + NSString *ofile = conffile_r[1]; + //NSString *nfile = conffile_r[2]; + + UIAlertView *alert = [[[UIAlertView alloc] + initWithTitle:UCLocalize("CONFIGURATION_UPGRADE") + message:[NSString stringWithFormat:@"%@\n\n%@", UCLocalize("CONFIGURATION_UPGRADE_EX"), ofile] + delegate:self + cancelButtonTitle:UCLocalize("KEEP_OLD_COPY") + otherButtonTitles: + UCLocalize("ACCEPT_NEW_COPY"), + // XXX: UCLocalize("SEE_WHAT_CHANGED"), + nil + ] autorelease]; + + [alert setContext:@"conffile"]; + [alert show]; +} + - (void) addStashController { ++locked_; stash_ = [[StashController alloc] init]; @@ -8990,13 +9384,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self setIdleTimerDisabled:YES]; [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque]; - [self setStatusBarShowsProgress:YES]; UpdateExternalStatus(1); - [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"]; - UpdateExternalStatus(0); - [self setStatusBarShowsProgress:NO]; [self removeStashController]; @@ -9008,7 +9398,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) setupViewControllers { tabbar_ = [[CYTabBarController alloc] initWithDatabase:database_]; - [tabbar_ setDelegate:self]; NSMutableArray *items([NSMutableArray arrayWithObjects: [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:0] autorelease], @@ -9035,21 +9424,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [tabbar_ setUpdateDelegate:self]; } -- (CYEmulatedLoadingController *)showEmulatedLoadingControllerInView:(UIView *)view { - static CYEmulatedLoadingController *fake = [[CYEmulatedLoadingController alloc] init]; - if (view != nil) { - [view addSubview:[fake view]]; - } else { - [[fake view] removeFromSuperview]; - } - - return fake; -} - - (void) applicationDidFinishLaunching:(id)unused { _trace(); - CydiaApp = self; - if ([self respondsToSelector:@selector(setApplicationSupportsShakeToEdit:)]) [self setApplicationSupportsShakeToEdit:NO]; @@ -9098,10 +9474,13 @@ _trace(); } database_ = [Database sharedInstance]; + [database_ setDelegate:self]; [window_ setUserInteractionEnabled:NO]; [self setupViewControllers]; - [self showEmulatedLoadingControllerInView:window_]; + + emulated_ = [[CYEmulatedLoadingController alloc] initWithDatabase:database_]; + [window_ addSubview:[emulated_ view]]; [self performSelector:@selector(loadData) withObject:nil afterDelay:0]; _trace(); @@ -9111,17 +9490,11 @@ _trace(); _trace(); if (Role_ == nil) { [window_ setUserInteractionEnabled:YES]; - - SettingsController *role = [[[SettingsController alloc] initWithDatabase:database_ delegate:self] autorelease]; - CYNavigationController *nav = [[[CYNavigationController alloc] initWithRootViewController:role] autorelease]; - if (IsWildcat_) - [nav setModalPresentationStyle:UIModalPresentationFormSheet]; - [[self showEmulatedLoadingControllerInView:window_] presentModalViewController:nav animated:YES]; - + [self presentModalViewController:[[[SettingsController alloc] initWithDatabase:database_ delegate:self] autorelease]]; return; } else { - if ([[self showEmulatedLoadingControllerInView:window_] modalViewController] != nil) - [[self showEmulatedLoadingControllerInView:window_] dismissModalViewControllerAnimated:YES]; + if ([emulated_ modalViewController] != nil) + [emulated_ dismissModalViewControllerAnimated:YES]; [window_ setUserInteractionEnabled:NO]; } @@ -9129,7 +9502,11 @@ _trace(); PrintTimes(); [window_ addSubview:[tabbar_ view]]; - [self showEmulatedLoadingControllerInView:nil]; + + [[emulated_ view] removeFromSuperview]; + [emulated_ release]; + emulated_ = nil; + [window_ setUserInteractionEnabled:YES]; int selectedIndex = 0; @@ -9144,10 +9521,16 @@ _trace(); recently = true; } - if (recently && [Metadata_ objectForKey:@"InterfaceState"]) { - items = [[Metadata_ objectForKey:@"InterfaceState"] mutableCopy]; - selectedIndex = [[Metadata_ objectForKey:@"InterfaceIndex"] intValue]; - } else { + items = [[Metadata_ objectForKey:@"InterfaceState"] mutableCopy]; + selectedIndex = [[Metadata_ objectForKey:@"InterfaceIndex"] intValue]; + + BOOL enough = YES; + for (NSArray *entry in items) + if ([entry count] <= 0) + enough = NO; + + if (!recently || !items || !enough) { + selectedIndex = 0; items = [NSMutableArray array]; [items addObject:[NSArray arrayWithObject:@"cydia://home"]]; [items addObject:[NSArray arrayWithObject:@"cydia://sections"]]; @@ -9170,7 +9553,7 @@ _trace(); for (unsigned int nav = 0; nav < [stack count]; nav++) { NSString *addr = [stack objectAtIndex:nav]; NSURL *url = [NSURL URLWithString:addr]; - CYViewController *page = [self pageForURL:url]; + CYViewController *page = [self pageForURL:url forExternal:NO]; if (page != nil) [current addObject:page]; } @@ -9180,7 +9563,7 @@ _trace(); // (Try to) show the startup URL. if (starturl_ != nil) { - [self openCydiaURL:starturl_]; + [self openCydiaURL:starturl_ forExternal:NO]; [starturl_ release]; starturl_ = nil; } @@ -9194,6 +9577,22 @@ _trace(); } } +- (void) addProgressEvent:(CydiaProgressEvent *)event forTask:(NSString *)task { + id progress([database_ progressDelegate] ?: [self invokeNewProgress:nil forController:nil withTitle:task]); + [progress setTitle:task]; + [progress addProgressEvent:event]; +} + +- (void) addProgressEventForTask:(NSArray *)data { + CydiaProgressEvent *event([data objectAtIndex:0]); + NSString *task([data count] < 2 ? nil : [data objectAtIndex:1]); + [self addProgressEvent:event forTask:task]; +} + +- (void) addProgressEventOnMainThread:(CydiaProgressEvent *)event forTask:(NSString *)task { + [self performSelectorOnMainThread:@selector(addProgressEventForTask:) withObject:[NSArray arrayWithObjects:event, task, nil] waitUntilDone:YES]; +} + @end /*IMP alloc_; @@ -9269,12 +9668,26 @@ MSHook(void, UIApplication$_updateApplicationAccessibility, UIApplication *self, int main(int argc, char *argv[]) { _pooled _trace(); + UpdateExternalStatus(0); + if (Class $UIDevice = objc_getClass("UIDevice")) { UIDevice *device([$UIDevice currentDevice]); IsWildcat_ = [device respondsToSelector:@selector(isWildcat)] && [device isWildcat]; } else IsWildcat_ = false; + UIScreen *screen([UIScreen mainScreen]); + if ([screen respondsToSelector:@selector(scale)]) + ScreenScale_ = [screen scale]; + else + ScreenScale_ = 1; + + NSMutableArray *parts([NSMutableArray arrayWithCapacity:2]); + if (ScreenScale_ > 1) + [parts addObject:@"@2x"]; + [parts addObject:(IsWildcat_ ? @"~ipad" : @"~iphone")]; + UI_ = CydiaURL([NSString stringWithFormat:@"ui/ios%@", [parts componentsJoinedByString:@""]]); + PackageName = reinterpret_cast(method_getImplementation(class_getInstanceMethod([Package class], @selector(cyname)))); /* Library Hacks {{{ */ @@ -9486,10 +9899,15 @@ int main(int argc, char *argv[]) { _pooled Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil]; - if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", F_OK) == 0) - dlopen("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", RTLD_LAZY | RTLD_GLOBAL); - if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0) - dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL); +#define MobileSubstrate_(name) \ + if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", F_OK) == 0) \ + dlopen("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", RTLD_LAZY | RTLD_GLOBAL); + + MobileSubstrate_(Activator) + MobileSubstrate_(libstatusbar) + MobileSubstrate_(SimulatedKeyEvents) + MobileSubstrate_(WinterBoard) + /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0) dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/