X-Git-Url: https://git.saurik.com/cydia.git/blobdiff_plain/cb6e2ccf1e636ee709c709d68b379efe14540303..4e89e88014a46af14f9eb348844e4c674bfba64d:/MobileCydia.mm diff --git a/MobileCydia.mm b/MobileCydia.mm index 888c43eb..64c388c9 100644 --- a/MobileCydia.mm +++ b/MobileCydia.mm @@ -206,6 +206,35 @@ void PrintTimes() { while (false); \ [_pool release]; +#define Cydia_ CYDIA_VERSION + +#define lprintf(args...) fprintf(stderr, args) + +#define ForRelease 1 +#define TraceLogging (1 && !ForRelease) +#define HistogramInsertionSort (!ForRelease ? 0 : 0) +#define ProfileTimes (0 && !ForRelease) +#define ForSaurik (0 && !ForRelease) +#define LogBrowser (0 && !ForRelease) +#define TrackResize (0 && !ForRelease) +#define ManualRefresh (1 && !ForRelease) +#define ShowInternals (0 && !ForRelease) +#define AlwaysReload (0 && !ForRelease) +#define TryIndexedCollation (0 && !ForRelease) + +#if !TraceLogging +#undef _trace +#define _trace(args...) +#endif + +#if !ProfileTimes +#undef _profile +#define _profile(name) { +#undef _end +#define _end } +#define PrintTimes() do {} while (false) +#endif + // Hash Functions/Structures {{{ extern "C" uint32_t hashlittle(const void *key, size_t length, uint32_t initval = 0); @@ -218,12 +247,13 @@ union SplitHash { static const NSUInteger UIViewAutoresizingFlexibleBoth(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); static _finline NSString *CydiaURL(NSString *path) { - char page[25]; - page[0] = 'h'; page[1] = 't'; page[2] = 't'; page[3] = 'p'; page[4] = ':'; - page[5] = '/'; page[6] = '/'; page[7] = 'c'; page[8] = 'y'; page[9] = 'd'; - page[10] = 'i'; page[11] = 'a'; page[12] = '.'; page[13] = 's'; page[14] = 'a'; - page[15] = 'u'; page[16] = 'r'; page[17] = 'i'; page[18] = 'k'; page[19] = '.'; - page[20] = 'c'; page[21] = 'o'; page[22] = 'm'; page[23] = '/'; page[24] = '\0'; + char page[26]; + page[0] = 'h'; page[1] = 't'; page[2] = 't'; page[3] = 'p'; page[4] = 's'; + page[5] = ':'; page[6] = '/'; page[7] = '/'; page[8] = 'c'; page[9] = 'y'; + page[10] = 'd'; page[11] = 'i'; page[12] = 'a'; page[13] = '.'; page[14] = 's'; + page[15] = 'a'; page[16] = 'u'; page[17] = 'r'; page[18] = 'i'; page[19] = 'k'; + page[20] = '.'; page[21] = 'c'; page[22] = 'o'; page[23] = 'm'; page[24] = '/'; + page[25] = '\0'; return [[NSString stringWithUTF8String:page] stringByAppendingString:path]; } @@ -288,8 +318,11 @@ 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]); +_trace(); + while (!stopped && [loop runMode:mode beforeDate:future]); +_trace(); return [context count] == 0 ? nil : [context objectAtIndex:0]; } @@ -356,33 +389,6 @@ static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSC static const NSStringCompareOptions LaxCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSCaseInsensitiveSearch; static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareLocalized | kCFCompareNumerically | kCFCompareWidthInsensitive | kCFCompareForcedOrdering; -#define lprintf(args...) fprintf(stderr, args) - -#define ForRelease 1 -#define TraceLogging (1 && !ForRelease) -#define HistogramInsertionSort (!ForRelease ? 0 : 0) -#define ProfileTimes (0 && !ForRelease) -#define ForSaurik (0 && !ForRelease) -#define LogBrowser (0 && !ForRelease) -#define TrackResize (0 && !ForRelease) -#define ManualRefresh (1 && !ForRelease) -#define ShowInternals (0 && !ForRelease) -#define AlwaysReload (0 && !ForRelease) -#define TryIndexedCollation (0 && !ForRelease) - -#if !TraceLogging -#undef _trace -#define _trace(args...) -#endif - -#if !ProfileTimes -#undef _profile -#define _profile(name) { -#undef _end -#define _end } -#define PrintTimes() do {} while (false) -#endif - /* Radix Sort {{{ */ typedef uint32_t (*SKRadixFunction)(id, void *); @@ -564,6 +570,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 { @@ -823,9 +844,22 @@ class Pcre { const char *data_; public: + Pcre() : + code_(NULL), + study_(NULL) + { + } + Pcre(const char *regex) : + code_(NULL), study_(NULL) { + this->operator =(regex); + } + + void operator =(const char *regex) { + _assert(code_ == NULL); + const char *error; int offset; code_ = pcre_compile(regex, 0, &error, &offset, NULL); @@ -844,19 +878,31 @@ class Pcre { delete matches_; } - NSString *operator [](size_t match) { + NSString *operator [](size_t match) const { return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])]; } - bool operator ()(NSString *data) { + _finline bool operator ()(NSString *data) { // XXX: length is for characters, not for bytes return operator ()([data UTF8String], [data length]); } + _finline bool operator ()(const char *data) { + return operator ()(data, strlen(data)); + } + bool operator ()(const char *data, size_t size) { data_ = data; return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0; } + + NSString *operator ->*(NSString *format) const { + id values[capture_]; + for (int i(0); i != capture_; ++i) + values[i] = this->operator [](i + 1); + + return [[[NSString alloc] initWithFormat:format arguments:reinterpret_cast(values)] autorelease]; + } }; /* }}} */ /* Mime Addresses {{{ */ @@ -1009,7 +1055,6 @@ static UIColor *InstallingColor_; static UIColor *RemovingColor_; static NSString *App_; -static NSString *Home_; static BOOL Advanced_; static BOOL Ignored_; @@ -1024,7 +1069,7 @@ static const char *Machine_ = NULL; static NSString *System_ = nil; static NSString *SerialNumber_ = nil; static NSString *ChipID_ = nil; -static NSString *Token_ = nil; +static _H Token_; static NSString *UniqueID_ = nil; static NSString *PLMN_ = nil; static NSString *Build_ = nil; @@ -1047,6 +1092,17 @@ static time_t now_; bool IsWildcat_; static CGFloat ScreenScale_; +static NSString *Idiom_; + +static NSMutableDictionary *SessionData_; +static NSObject *HostConfig_; +static NSMutableSet *BridgedHosts_; +static NSMutableSet *PipelinedHosts_; + +static NSString *kCydiaProgressEventTypeError = @"Error"; +static NSString *kCydiaProgressEventTypeInformation = @"Information"; +static NSString *kCydiaProgressEventTypeStatus = @"Status"; +static NSString *kCydiaProgressEventTypeWarning = @"Warning"; /* }}} */ /* Display Helpers {{{ */ @@ -1119,22 +1175,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:(NSNumber *)percent; -- (void) startProgress; -- (void) addProgressOutput:(NSString *)output; -- (bool) isProgressCancelled; -@end - -@protocol ConfigurationDelegate +@protocol DatabaseDelegate - (void) repairWithSelector:(SEL)selector; - (void) setConfigurationData:(NSString *)data; +- (void) addProgressEventOnMainThread:(CydiaProgressEvent *)event forTask:(NSString *)task; @end @class CYPackageController; @@ -1160,24 +1206,69 @@ bool isSectionVisible(NSString *section) { - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item; - (void) reloadDataWithInvocation:(NSInvocation *)invocation; @end - -static NSObject *CydiaApp; /* }}} */ +/* ProgressEvent Interface/Delegate {{{ */ +@interface CydiaProgressEvent : NSObject { + _H message_; + _H type_; + + _H item_; + _H package_; + _H url_; + _H version_; +} + ++ (CydiaProgressEvent *) eventWithMessage:(NSString *)message ofType:(NSString *)type; ++ (CydiaProgressEvent *) eventWithMessage:(NSString *)message ofType:(NSString *)type forPackage:(NSString *)package; ++ (CydiaProgressEvent *) eventWithMessage:(NSString *)message ofType:(NSString *)type forItem:(pkgAcquire::ItemDesc &)item; + +- (id) initWithMessage:(NSString *)message ofType:(NSString *)type; + +- (NSString *) message; +- (NSString *) type; + +- (NSArray *) item; +- (NSString *) package; +- (NSString *) url; +- (NSString *) version; + +- (void) setItem:(NSArray *)item; +- (void) setPackage:(NSString *)package; +- (void) setURL:(NSString *)url; +- (void) setVersion:(NSString *)version; + +- (NSString *) compound:(NSString *)value; +- (NSString *) compoundMessage; +- (NSString *) compoundTitle; + +@end + +@protocol ProgressDelegate +- (void) addProgressEvent:(CydiaProgressEvent *)event; +- (void) setProgressPercent:(NSNumber *)percent; +- (void) setProgressStatus:(NSDictionary *)status; +- (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; } @@ -1193,12 +1284,9 @@ class Status : } virtual void Fetch(pkgAcquire::ItemDesc &item) { - //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]); - [delegate_ - performSelectorOnMainThread:@selector(setProgressTitle:) - withObject:[NSString stringWithFormat:UCLocalize("DOWNLOADING_"), [NSString stringWithUTF8String:item.ShortDesc.c_str()]] - waitUntilDone:YES - ]; + NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]); + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithFormat:UCLocalize("DOWNLOADING_"), name] ofType:kCydiaProgressEventTypeStatus forItem:item]); + [delegate_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } virtual void Done(pkgAcquire::ItemDesc &item) { @@ -1215,42 +1303,50 @@ 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]); - - [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:kCydiaProgressEventTypeError forItem:item]); + [delegate_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } virtual bool Pulse(pkgAcquire *Owner) { bool value = pkgAcquireStatus::Pulse(Owner); - float percent( + double percent( double(CurrentBytes + CurrentItems) / double(TotalBytes + TotalItems) ); - [delegate_ setProgressPercent:[NSNumber numberWithFloat:percent]]; - return [delegate_ isProgressCancelled] ? false : value; + [delegate_ performSelectorOnMainThread:@selector(setProgressStatus:) withObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithDouble:percent], @"Percent", + + [NSNumber numberWithDouble:CurrentBytes], @"Current", + [NSNumber numberWithDouble:TotalBytes], @"Total", + [NSNumber numberWithDouble:CurrentCPS], @"Speed", + nil] waitUntilDone:YES]; + + if (value && ![delegate_ isProgressCancelled]) + return true; + else { + cancelled_ = true; + return false; + } + } + + _finline bool WasCancelled() const { + return cancelled_; } virtual void Start() { pkgAcquireStatus::Start(); - [delegate_ startProgress]; + [delegate_ performSelectorOnMainThread:@selector(setProgressCancellable:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:YES]; } virtual void Stop() { pkgAcquireStatus::Stop(); + [delegate_ performSelectorOnMainThread:@selector(setProgressCancellable:) withObject:[NSNumber numberWithBool:NO] waitUntilDone:YES]; + [delegate_ performSelectorOnMainThread:@selector(setProgressStatus:) withObject:nil waitUntilDone:YES]; } }; /* }}} */ - /* Database Interface {{{ */ typedef std::map< unsigned long, _H > SourceMap; @@ -1274,7 +1370,9 @@ typedef std::map< unsigned long, _H > SourceMap; CFMutableArrayRef packages_; - _transient NSObject *delegate_; + _transient NSObject *delegate_; + _transient NSObject *progress_; + Status status_; int cydiafd_; @@ -1314,44 +1412,145 @@ 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 - ]; ++ (CydiaProgressEvent *) eventWithMessage:(NSString *)message ofType:(NSString *)type forItem:(pkgAcquire::ItemDesc &)item { + CydiaProgressEvent *event([self eventWithMessage:message ofType:type]); + + NSString *description([NSString stringWithUTF8String:item.Description.c_str()]); + NSArray *fields([description componentsSeparatedByString:@" "]); + [event setItem:fields]; + + if ([fields count] > 3) { + [event setPackage:[fields objectAtIndex:2]]; + [event setVersion:[fields objectAtIndex:3]]; + } + + [event setURL:[NSString stringWithUTF8String:item.URI.c_str()]]; + + return event; +} + ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects: + @"item", + @"message", + @"package", + @"type", + @"url", + @"version", + nil]; } -- (void) setProgressError:(NSString *)error forPackage:(NSString *)id { - Package *package = id == nil ? nil : [[Database sharedInstance] packageWithName:id]; +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} - [self performSelector:@selector(setProgressError:withTitle:) - withObject:error - withObject:(package == nil ? id : [package name]) - ]; ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript: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_; +} + +- (NSArray *) item { + return (id) item_ ?: [NSNull null]; +} + +- (void) setItem:(NSArray *)item { + item_ = item; +} + +- (NSString *) package { + return (id) package_ ?: [NSNull null]; +} + +- (void) setPackage:(NSString *)package { + package_ = package; +} + +- (NSString *) url { + return (id) url_ ?: [NSNull null]; +} + +- (void) setURL:(NSString *)url { + url_ = url; +} + +- (void) setVersion:(NSString *)version { + version_ = version; +} + +- (NSString *) version { + return (id) version_ ?: [NSNull null]; +} + +- (NSString *) compound:(NSString *)value { + if (value != nil) { + NSString *mode(nil); { + NSString *type([self type]); + if ([type isEqualToString:kCydiaProgressEventTypeError]) + mode = UCLocalize("ERROR"); + else if ([type isEqualToString:kCydiaProgressEventTypeWarning]) + 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 @@ -1476,12 +1675,12 @@ static void PackageImport(const void *key, const void *value, void *context) { CYString type_; CYString version_; - NSString *host_; - NSString *authority_; + _H host_; + _H authority_; CYString defaultIcon_; - NSDictionary *record_; + _H record_; BOOL trusted_; } @@ -1502,7 +1701,7 @@ static void PackageImport(const void *key, const void *value, void *context) { - (NSString *) host; - (NSString *) name; -- (NSString *) description; +- (NSString *) shortDescription; - (NSString *) label; - (NSString *) origin; - (NSString *) version; @@ -1526,20 +1725,9 @@ static void PackageImport(const void *key, const void *value, void *context) { version_.clear(); defaultIcon_.clear(); - if (record_ != nil) { - [record_ release]; - record_ = nil; - } - - if (host_ != nil) { - [host_ release]; - host_ = nil; - } - - if (authority_ != nil) { - [authority_ release]; - authority_ = nil; - } + record_ = nil; + host_ = nil; + authority_ = nil; } - (void) dealloc { @@ -1550,13 +1738,13 @@ static void PackageImport(const void *key, const void *value, void *context) { + (NSArray *) _attributeKeys { return [NSArray arrayWithObjects: - @"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", + @"shortDescription", @"trusted", @"type", @"uri", @@ -1617,22 +1805,18 @@ static void PackageImport(const void *key, const void *value, void *context) { } record_ = [Sources_ objectForKey:[self key]]; - if (record_ != nil) - record_ = [record_ retain]; NSURL *url([NSURL URLWithString:uri_]); host_ = [url host]; if (host_ != nil) - host_ = [[host_ lowercaseString] retain]; + host_ = [host_ lowercaseString]; if (host_ != nil) - authority_ = host_; + // XXX: this is due to a bug in _H<> + authority_ = (id) host_; else authority_ = [url path]; - - if (authority_ != nil) - authority_ = [authority_ retain]; } - (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool { @@ -1701,15 +1885,15 @@ static void PackageImport(const void *key, const void *value, void *context) { } - (NSString *) name { - return origin_.empty() ? authority_ : origin_; + return origin_.empty() ? (id) authority_ : origin_; } -- (NSString *) description { +- (NSString *) shortDescription { return description_; } - (NSString *) label { - return label_.empty() ? authority_ : label_; + return label_.empty() ? (id) authority_ : label_; } - (NSString *) origin { @@ -2213,13 +2397,16 @@ struct PackageNameOrdering : @"purposes", @"relations", @"section", + @"selection", @"shortDescription", @"shortSection", @"simpleSection", @"size", @"source", @"sponsor", + @"state", @"support", + @"tags", @"warnings", nil]; } @@ -2768,6 +2955,54 @@ struct PackageNameOrdering : return files; } +- (NSString *) state { +@synchronized (database_) { + if ([database_ era] != era_ || file_.end()) + return nil; + + switch (iterator_->CurrentState) { + case pkgCache::State::NotInstalled: + return @"NotInstalled"; + case pkgCache::State::UnPacked: + return @"UnPacked"; + case pkgCache::State::HalfConfigured: + return @"HalfConfigured"; + case pkgCache::State::HalfInstalled: + return @"HalfInstalled"; + case pkgCache::State::ConfigFiles: + return @"ConfigFiles"; + case pkgCache::State::Installed: + return @"Installed"; + case pkgCache::State::TriggersAwaited: + return @"TriggersAwaited"; + case pkgCache::State::TriggersPending: + return @"TriggersPending"; + } + + return (NSString *) [NSNull null]; +} } + +- (NSString *) selection { +@synchronized (database_) { + if ([database_ era] != era_ || file_.end()) + return nil; + + switch (iterator_->SelectedState) { + case pkgCache::State::Unknown: + return @"Unknown"; + case pkgCache::State::Install: + return @"Install"; + case pkgCache::State::Hold: + return @"Hold"; + case pkgCache::State::DeInstall: + return @"DeInstall"; + case pkgCache::State::Purge: + return @"Purge"; + } + + return (NSString *) [NSNull null]; +} } + - (NSArray *) warnings { NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]); const char *name(iterator_.Name()); @@ -2906,6 +3141,10 @@ struct PackageNameOrdering : _assert(false); } +- (NSArray *) tags { + return tags_; +} + - (BOOL) hasTag:(NSString *)tag { return tags_ == nil ? NO : [tags_ containsObject:tag]; } @@ -3229,27 +3468,36 @@ static NSString *Warning_; size_t size(line.size()); lprintf("S:%s\n", data); - if (conffile_r(data, size)) + if (conffile_r(data, size)) { + // status: /fail : conffile-prompt : '/fail' '/fail.dpkg-new' 1 1 [delegate_ performSelectorOnMainThread:@selector(setConfigurationData:) withObject:conffile_r[1] waitUntilDone:YES]; - else if (strncmp(data, "status: ", 8) == 0) - [delegate_ performSelectorOnMainThread:@selector(setProgressTitle:) withObject:[NSString stringWithUTF8String:(data + 8)] waitUntilDone:YES]; - else if (pmstatus_r(data, size)) { + } else if (strncmp(data, "status: ", 8) == 0) { + // status: : {unpacked,half-configured,installed} + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:(data + 8)] ofType:kCydiaProgressEventTypeStatus]); + [progress_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; + } else if (strncmp(data, "processing: ", 12) == 0) { + // processing: configure: config-test + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:(data + 12)] ofType:kCydiaProgressEventTypeStatus]); + [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]; + if ([package isEqualToString:@"dpkg-exec"]) + package = nil; float percent([pmstatus_r[3] floatValue]); - [delegate_ performSelectorOnMainThread:@selector(setProgressPercent:) withObject:[NSNumber numberWithFloat:(percent / 100)] waitUntilDone:YES]; + [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_ performSelectorOnMainThread:@selector(setProgressTitle:) withObject:string waitUntilDone:YES]; - else if (type == "pmconffile") + if (type == "pmerror") { + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:string ofType:kCydiaProgressEventTypeError forPackage:package]); + [progress_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; + } else if (type == "pmstatus") { + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:string ofType:kCydiaProgressEventTypeStatus forPackage:package]); + [progress_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; + } else if (type == "pmconffile") [delegate_ performSelectorOnMainThread:@selector(setConfigurationData:) withObject:string waitUntilDone:YES]; else lprintf("E:unknown pmstatus\n"); @@ -3268,7 +3516,8 @@ static NSString *Warning_; while (std::getline(is, line)) { lprintf("O:%s\n", line.c_str()); - [delegate_ performSelectorOnMainThread:@selector(addProgressOutput:) withObject:[NSString stringWithUTF8String:line.c_str()] waitUntilDone:YES]; + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:line.c_str()] ofType:kCydiaProgressEventTypeInformation]); + [progress_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } _assume(false); @@ -3404,7 +3653,7 @@ static NSString *Warning_; lprintf("%c:[%s]\n", warning ? 'W' : 'E', error.c_str()); - [delegate_ _setProgressError:[NSString stringWithUTF8String:error.c_str()] withTitle:[NSString stringWithFormat:Colon_, (warning ? Warning_ : Error_), title]]; + [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:error.c_str()] ofType:(warning ? kCydiaProgressEventTypeWarning : kCydiaProgressEventTypeError)] forTask:title]; } return fatal; @@ -3466,12 +3715,12 @@ static NSString *Warning_; [delegate_ repairWithSelector:@selector(configure)]; else if (error == "The package lists or status file could not be parsed or opened.") [delegate_ repairWithSelector:@selector(update)]; + // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)") // 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)") - [delegate_ _setProgressError:[NSString stringWithUTF8String:error.c_str()] withTitle:[NSString stringWithFormat:Colon_, Error_, title]]; + // else if (error == "Malformed Status line") // 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]]; + [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:error.c_str()] ofType:(warning ? kCydiaProgressEventTypeWarning : kCydiaProgressEventTypeError)] forTask:title]; return; } @@ -3496,7 +3745,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:kCydiaProgressEventTypeError] forTask:title]; return; } @@ -3508,7 +3757,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:kCydiaProgressEventTypeError] forTask:title]; return; } @@ -3671,7 +3920,7 @@ static NSString *Warning_; [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]]; } - [CydiaApp performSelectorOnMainThread:@selector(retainNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; + [delegate_ performSelectorOnMainThread:@selector(retainNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) { _trace(); @@ -3689,7 +3938,7 @@ static NSString *Warning_; failed = true; } - [CydiaApp performSelectorOnMainThread:@selector(releaseNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; + [delegate_ performSelectorOnMainThread:@selector(releaseNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; if (failed) { _trace(); @@ -3752,19 +4001,33 @@ static NSString *Warning_; 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); } +- (NSObject *) progressDelegate { + return progress_; +} + - (Source *) getSource:(pkgCache::PkgFileIterator)file { SourceMap::const_iterator i(sourceMap_.find(file->ID)); return i == sourceMap_.end() ? nil : i->second; @@ -3804,7 +4067,42 @@ static NSString *Warning_; @end /* }}} */ -/* Web Scripting {{{ */ +@interface Diversion : NSObject { + Pcre pattern_; + _H key_; + _H format_; +} + +@end + +@implementation Diversion + +- (id) initWithFrom:(NSString *)from to:(NSString *)to { + if ((self = [super init]) != nil) { + pattern_ = [from UTF8String]; + key_ = from; + format_ = to; + } return self; +} + +- (NSString *) divert:(NSString *)url { + return !pattern_(url) ? nil : pattern_->*format_; +} + +- (NSString *) key { + return key_; +} + +- (NSUInteger) hash { + return [key_ hash]; +} + +- (BOOL) isEqual:(Diversion *)object { + return self == object || [self class] == [object class] && [key_ isEqual:[object key]]; +} + +@end + @interface CydiaObject : NSObject { id indirect_; _transient id delegate_; @@ -3814,6 +4112,17 @@ static NSString *Warning_; @end +@interface CYBrowserController : BrowserController { + CydiaObject *cydia_; +} + ++ (void) addDiversion:(Diversion *)diversion; + +@end + +static NSMutableSet *Diversions_; + +/* Web Scripting {{{ */ @implementation CydiaObject - (void) dealloc { @@ -3835,10 +4144,15 @@ static NSString *Warning_; return [NSArray arrayWithObjects: @"device", @"ecid", + @"firmware", + @"hostname", + @"idiom", @"model", @"plmn", @"role", @"serial", + @"token", + @"version", nil]; } @@ -3850,16 +4164,32 @@ static NSString *Warning_; return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; } +- (NSString *) version { + return @ Cydia_; +} + - (NSString *) device { return [[UIDevice currentDevice] uniqueIdentifier]; } +- (NSString *) firmware { + return [[UIDevice currentDevice] systemVersion]; +} + +- (NSString *) hostname { + return [[UIDevice currentDevice] name]; +} + +- (NSString *) idiom { + return (id) Idiom_ ?: [NSNull null]; +} + - (NSString *) plmn { - return PLMN_; + return (id) PLMN_ ?: [NSNull null]; } - (NSString *) ecid { - return ChipID_; + return (id) ChipID_ ?: [NSNull null]; } - (NSString *) serial { @@ -3867,19 +4197,29 @@ static NSString *Warning_; } - (NSString *) role { - return Role_; + return (id) Role_ ?: [NSNull null]; } - (NSString *) model { return [NSString stringWithUTF8String:Machine_]; } +- (NSString *) token { + return (id) Token_ ?: [NSNull null]; +} + + (NSString *) webScriptNameForSelector:(SEL)selector { if (false); + else if (selector == @selector(addBridgedHost:)) + return @"addBridgedHost"; + else if (selector == @selector(addPipelinedHost:scheme:)) + return @"addPipelinedHost"; else if (selector == @selector(addTrivialSource:)) return @"addTrivialSource"; else if (selector == @selector(close)) return @"close"; + else if (selector == @selector(divert::)) + return @"divert"; else if (selector == @selector(du:)) return @"du"; else if (selector == @selector(stringWithFormat:arguments:)) @@ -3894,16 +4234,38 @@ static NSString *Warning_; return @"getInstalledPackages"; else if (selector == @selector(getPackageById:)) return @"getPackageById"; + else if (selector == @selector(getSessionValue:)) + return @"getSessionValue"; else if (selector == @selector(installPackages:)) return @"installPackages"; else if (selector == @selector(localizedStringForKey:value:table:)) return @"localize"; + else if (selector == @selector(popViewController:)) + return @"popViewController"; else if (selector == @selector(refreshSources)) return @"refreshSources"; + else if (selector == @selector(removeButton)) + return @"removeButton"; + else if (selector == @selector(setSessionValue::)) + return @"setSessionValue"; + else if (selector == @selector(substitutePackageNames:)) + return @"substitutePackageNames"; + else if (selector == @selector(scrollToBottom:)) + return @"scrollToBottom"; + else if (selector == @selector(setAllowsNavigationAction:)) + return @"setAllowsNavigationAction"; else if (selector == @selector(setButtonImage:withStyle:toFunction:)) return @"setButtonImage"; else if (selector == @selector(setButtonTitle:withStyle:toFunction:)) return @"setButtonTitle"; + else if (selector == @selector(setHidesBackButton:)) + return @"setHidesBackButton"; + else if (selector == @selector(setHidesNavigationBar:)) + return @"setHidesNavigationBar"; + else if (selector == @selector(setNavigationBarStyle:)) + return @"setNavigationBarStyle"; + else if (selector == @selector(setNavigationBarTintRed:green:blue:alpha:)) + return @"setNavigationBarTintColor"; else if (selector == @selector(setPopupHook:)) return @"setPopupHook"; else if (selector == @selector(setToken:)) @@ -3926,6 +4288,10 @@ static NSString *Warning_; return [feature isEqualToString:@"window.open"]; } +- (void) divert:(NSString *)from :(NSString *)to { + [CYBrowserController performSelectorOnMainThread:@selector(addDiversion:) withObject:[[[Diversion alloc] initWithFrom:from to:to] autorelease] waitUntilDone:NO]; +} + - (NSNumber *) getKernelNumber:(NSString *)name { const char *string([name UTF8String]); @@ -3960,6 +4326,38 @@ static NSString *Warning_; return [NSString stringWithCString:value]; } +- (id) getSessionValue:(NSString *)key { +@synchronized (SessionData_) { + return [SessionData_ objectForKey:key]; +} } + +- (void) setSessionValue:(NSString *)key :(NSString *)value { +@synchronized (SessionData_) { + if (value == (id) [WebUndefined undefined]) + [SessionData_ removeObjectForKey:key]; + else + [SessionData_ setObject:value forKey:key]; +} } + +- (void) addBridgedHost:(NSString *)host { +@synchronized (HostConfig_) { + [BridgedHosts_ addObject:host]; +} } + +- (void) addPipelinedHost:(NSString *)host scheme:(NSString *)scheme { +@synchronized (HostConfig_) { + if (scheme != (id) [WebUndefined undefined]) + host = [NSString stringWithFormat:@"%@:%@", [scheme lowercaseString], host]; + + [PipelinedHosts_ addObject:host]; +} } + +- (void) popViewController:(NSNumber *)value { + if (value == (id) [WebUndefined undefined]) + value = [NSNumber numberWithBool:YES]; + [indirect_ performSelectorOnMainThread:@selector(popViewControllerWithNumber:) withObject:value waitUntilDone:NO]; +} + - (void) addTrivialSource:(NSString *)href { [delegate_ performSelectorOnMainThread:@selector(addTrivialSource:) withObject:href waitUntilDone:NO]; } @@ -3984,9 +4382,11 @@ static NSString *Warning_; } } - (Package *) getPackageById:(NSString *)id { - Package *package([[Database sharedInstance] packageWithName:id]); - [package parse]; - return package; + if (Package *package = [[Database sharedInstance] packageWithName:id]) { + [package parse]; + return package; + } else + return (Package *) [NSNull null]; } - (NSArray *) statfs:(NSString *)path { @@ -4054,6 +4454,21 @@ static NSString *Warning_; [delegate_ performSelectorOnMainThread:@selector(installPackages:) withObject:packages waitUntilDone:NO]; } +- (NSString *) substitutePackageNames:(NSString *)message { + NSMutableArray *words([[message componentsSeparatedByString:@" "] mutableCopy]); + for (size_t i(0), e([words count]); i != e; ++i) { + NSString *word([words objectAtIndex:i]); + if (Package *package = [[Database sharedInstance] packageWithName:word]) + [words replaceObjectAtIndex:i withObject:[package name]]; + } + + return [words componentsJoinedByString:@" "]; +} + +- (void) removeButton { + [indirect_ removeButton]; +} + - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { [indirect_ setButtonImage:button withStyle:style toFunction:function]; } @@ -4062,12 +4477,36 @@ static NSString *Warning_; [indirect_ setButtonTitle:button withStyle:style toFunction:function]; } +- (void) setAllowsNavigationAction:(NSString *)value { + [indirect_ performSelectorOnMainThread:@selector(setAllowsNavigationActionByNumber:) withObject:value waitUntilDone:NO]; +} + +- (void) setHidesBackButton:(NSString *)value { + [indirect_ performSelectorOnMainThread:@selector(setHidesBackButtonByNumber:) withObject:value waitUntilDone:NO]; +} + +- (void) setHidesNavigationBar:(NSString *)value { + [indirect_ performSelectorOnMainThread:@selector(setHidesNavigationBarByNumber:) withObject:value waitUntilDone:NO]; +} + +- (void) setNavigationBarStyle:(NSString *)value { + [indirect_ performSelectorOnMainThread:@selector(setNavigationBarStyle:) withObject:value waitUntilDone:NO]; +} + +- (void) setNavigationBarTintRed:(NSNumber *)red green:(NSNumber *)green blue:(NSNumber *)blue alpha:(NSNumber *)alpha { + float opacity(alpha == (id) [WebUndefined undefined] ? 1 : [alpha floatValue]); + UIColor *color([UIColor colorWithRed:[red floatValue] green:[green floatValue] blue:[blue floatValue] alpha:opacity]); + [indirect_ performSelectorOnMainThread:@selector(setNavigationBarTintColor:) withObject:color waitUntilDone:NO]; +} + - (void) _setToken:(NSString *)token { - if (Token_ != nil) - [Token_ release]; - Token_ = [token retain]; + Token_ = token; + + if (token == nil) + [Metadata_ removeObjectForKey:@"Token"]; + else + [Metadata_ setObject:Token_ forKey:@"Token"]; - [Metadata_ setObject:Token_ forKey:@"Token"]; Changed_ = true; } @@ -4079,6 +4518,10 @@ static NSString *Warning_; [indirect_ setPopupHook:function]; } +- (void) scrollToBottom:(NSNumber *)animated { + [indirect_ performSelectorOnMainThread:@selector(scrollToBottomAnimated:) withObject:animated waitUntilDone:NO]; +} + - (void) setViewportWidth:(float)width { [indirect_ setViewportWidthOnMainThread:width]; } @@ -4105,9 +4548,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; @@ -4171,87 +4614,48 @@ static NSString *Warning_; @end /* }}} */ /* Emulated Loading Controller {{{ */ -@interface CYEmulatedLoadingController : CYViewController < - ProgressDelegate, - ConfigurationDelegate -> { +@interface CYEmulatedLoadingController : CYViewController { _transient Database *database_; - CYLoadingIndicator *indicator_; - UITabBar *tabbar_; - UINavigationBar *navbar_; + _H indicator_; + _H tabbar_; + _H navbar_; } @end @implementation CYEmulatedLoadingController -- (void) dealloc { - [self releaseSubviews]; - [database_ setDelegate:nil]; - - [super dealloc]; -} - -- (void) setProgressError:(NSString *)error withTitle:(NSString *)title { - CYAlertView *sheet([[[CYAlertView alloc] - initWithTitle:title - buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil] - defaultButtonIndex:0 - ] autorelease]); - - [sheet setMessage:error]; - [sheet yieldToPopupAlertAnimated:YES]; - [sheet dismiss]; -} - -- (void) setProgressTitle:(NSString *)title { } -- (void) setProgressPercent:(NSNumber *)percent { } -- (void) startProgress { } -- (void) addProgressOutput:(NSString *)output { } -- (bool) isProgressCancelled { return NO; } -- (void) setConfigurationData:(NSString *)data { } - -- (void) repairWithSelector:(SEL)selector { - [[indicator_ label] performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:Elision_, UCLocalize("REPAIRING"), nil] waitUntilDone:YES]; - [database_ performSelector:selector]; - sleep(10); - [[indicator_ label] performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:Elision_, UCLocalize("LOADING"), nil] waitUntilDone:YES]; -} - - (id) initWithDatabase:(Database *)database { if ((self = [super init]) != nil) { database_ = database; - [database_ setDelegate:self]; } 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]]; + UITableView *table([[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped] autorelease]); + [table setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [[self view] addSubview:table]; + + 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; } @@ -4259,12 +4663,6 @@ static NSString *Warning_; /* }}} */ /* Cydia Browser Controller {{{ */ -@interface CYBrowserController : BrowserController { - CydiaObject *cydia_; -} - -@end - @implementation CYBrowserController - (void) dealloc { @@ -4276,7 +4674,12 @@ static NSString *Warning_; return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://url/%@", [[[webview_ request] URL] absoluteString]]]; } -- (void) setHeaders:(NSDictionary *)headers forHost:(NSString *)host { ++ (void) initialize { + Diversions_ = [[NSMutableSet alloc] initWithCapacity:0]; +} + ++ (void) addDiversion:(Diversion *)diversion { + [Diversions_ addObject:diversion]; } - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { @@ -4284,39 +4687,35 @@ static NSString *Warning_; WebDataSource *source([frame dataSource]); NSURLResponse *response([source response]); - NSURL *url([response URL]); - NSString *scheme([url scheme]); - NSString *host([url host]); - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { - NSHTTPURLResponse *http((NSHTTPURLResponse *) response); - NSDictionary *headers([http allHeaderFields]); - [self setHeaders:headers forHost:host]; + @synchronized (HostConfig_) { + if ([[[url scheme] lowercaseString] isEqualToString:@"https"]) + if ([BridgedHosts_ containsObject:[url host]]) + [window setValue:cydia_ forKey:@"cydia"]; } - - if ( - [host isEqualToString:@"cydia.saurik.com"] || - [host hasSuffix:@".cydia.saurik.com"] || - [scheme isEqualToString:@"file"] - ) - [window setValue:cydia_ forKey:@"cydia"]; } -- (void) _setMoreHeaders:(NSMutableURLRequest *)request { +- (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source { + NSMutableURLRequest *copy([[super webView:view resource:resource willSendRequest:request redirectResponse:response fromDataSource:source] mutableCopy]); + + divert: + NSURL *url([copy URL]); + NSString *href([url absoluteString]); + + for (Diversion *diversion in Diversions_) + if (NSString *diverted = [diversion divert:href]) { + [copy setURL:[NSURL URLWithString:diverted]]; + goto divert; + } + if (System_ != NULL) - [request setValue:System_ forHTTPHeaderField:@"X-System"]; + [copy setValue:System_ forHTTPHeaderField:@"X-System"]; if (Machine_ != NULL) - [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; + [copy setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; if (Token_ != nil) - [request setValue:Token_ forHTTPHeaderField:@"X-Cydia-Token"]; - if (Role_ != nil) - [request setValue:Role_ forHTTPHeaderField:@"X-Role"]; -} + [copy setValue:Token_ forHTTPHeaderField:@"X-Cydia-Token"]; -- (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source { - NSMutableURLRequest *copy([[super webView:view resource:resource willSendRequest:request redirectResponse:response fromDataSource:source] mutableCopy]); - [self _setMoreHeaders:copy]; return copy; } @@ -4331,12 +4730,7 @@ static NSString *Warning_; WebView *webview([[webview_ _documentView] webView]); - Package *package([[Database sharedInstance] packageWithName:@"cydia"]); - - NSString *application = package == nil ? @"Cydia" : [NSString - stringWithFormat:@"Cydia/%@", - [package installed] - ]; + NSString *application([NSString stringWithFormat:@"Cydia/%@", @ Cydia_]); if (Safari_ != nil) application = [NSString stringWithFormat:@"Safari/%@ %@", Safari_, application]; @@ -4656,7 +5050,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [NSNumber numberWithInteger:[database_ fetcher].PartialPresent()], @"resuming", nil]; - [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/confirm/", UI_]]]; + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/confirm/", UI_]]]; [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:UCLocalize("CANCEL") @@ -4699,197 +5093,202 @@ 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_; + + bool running_; + float percent_; -- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object; + float current_; + float total_; + float speed_; -- (SEL) selector; -- (id) target; -- (id) object; + _H events_; + _H title_; + + _H status_; + _H finish_; +} @end -@implementation ProgressData +@implementation CydiaProgressData + ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects: + @"current", + @"events", + @"finish", + @"percent", + @"running", + @"speed", + @"title", + @"total", + nil]; +} -- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object { +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (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) setPercent:(float)value { + percent_ = value; +} + +- (NSNumber *) percent { + return [NSNumber numberWithFloat:percent_]; +} + +- (void) setCurrent:(float)value { + current_ = value; +} + +- (NSNumber *) current { + return [NSNumber numberWithFloat:current_]; +} + +- (void) setTotal:(float)value { + total_ = value; +} + +- (NSNumber *) total { + return [NSNumber numberWithFloat:total_]; +} + +- (void) setSpeed:(float)value { + speed_ = value; +} + +- (NSNumber *) speed { + return [NSNumber numberWithFloat:speed_]; } -- (id) target { - return target_; +- (NSArray *) events { + return events_; } -- (id) object { - return object_; +- (void) removeAllEvents { + [events_ removeAllObjects]; +} + +- (void) addEvent:(CydiaProgressEvent *)event { + [events_ addObject:event]; +} + +- (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) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title; - -- (BOOL) isRunning; +- (void) invoke:(NSInvocation *)invocation withTitle:(NSString *)title; -@end +- (void) setTitle:(NSString *)title; +- (void) setCancellable:(bool)cancellable; -@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]; } +- (void) updateCancel { + [[self navigationItem] setLeftBarButtonItem:(cancel_ == 1 ? [[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("CANCEL") + style:UIBarButtonItemStylePlain + target:self + action:@selector(cancel) + ] autorelease] : nil)]; +} + - (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]]; + [database_ setProgressDelegate:self]; - progress_ = [[UIProgressBar alloc] init]; - [progress_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)]; - [progress_ setStyle:0]; + progress_ = [[[CydiaProgressData alloc] init] autorelease]; + [progress_ setDelegate: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]; - } return self; -} + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/progress/", UI_]]]; -- (void) positionViews { - CGRect bounds = [[self view] bounds]; - CGSize prgsize = [UIProgressBar defaultSize]; + [scroller_ setBackgroundColor:[UIColor blackColor]]; - CGRect prgrect = {{ - (bounds.size.width - prgsize.width) / 2, - bounds.size.height - prgsize.height - 20 - }, prgsize}; + [[self navigationItem] setHidesBackButton:YES]; - float closewidth = std::min(bounds.size.width - 20, 300.0f); - - [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 - )]; + [self updateCancel]; + } return self; } -- (void) viewWillAppear:(BOOL)animated { - [super viewDidAppear:animated]; - [[self navigationItem] setHidesBackButton:YES]; - [[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlack]; - - [self positionViews]; +- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + [super webView:view didClearWindowObject:window forFrame:frame]; + [window setValue:progress_ forKey:@"cydiaProgress"]; } -- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - [self positionViews]; +- (void) updateProgress { + [self dispatchEvent:@"CydiaProgressUpdate"]; } -- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { - NSString *context([alert context]); - - 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]; - } +- (void) viewWillAppear:(BOOL)animated { + [[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlack]; + [super viewWillAppear:animated]; } -- (void) closeButtonPushed { - running_ = NO; - +- (void) close { UpdateExternalStatus(0); switch (Finish_) { case 0: - [self dismissModalViewControllerAnimated:YES]; break; case 1: @@ -4921,19 +5320,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]; +} - [[self view] addSubview:close_]; - [progress_ removeFromSuperview]; - [status_ removeFromSuperview]; +- (UIBarButtonItem *) rightButton { + return [[progress_ running] boolValue] ? nil : [[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("CLOSE") + style:UIBarButtonItemStylePlain + target:self + action:@selector(close) + ] autorelease]; +} - [database_ popErrorWithTitle:title_]; - [delegate_ progressControllerIsComplete:self]; +- (void) invoke:(NSInvocation *)invocation withTitle:(NSString *)title { + UpdateExternalStatus(1); - if (Finish_ < 4) { + [progress_ setRunning:true]; + [self setTitle:title]; + // implicit updateProgress + + SHA1SumValue notifyconf; { FileFd file; if (!file.Open(NotifyConfig_, FileFd::ReadOnly)) _error->Discard(); @@ -4941,12 +5353,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(); @@ -4954,64 +5365,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(); } } - if (Finish_ < 2) { - if (RestartSubstrate_) - Finish_ = 2; - } - - RestartSubstrate_ = false; - - 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) _detachNewThreadInvocation:(NSInvocation *)invocation { _pooled - [invocation invoke]; - [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(); @@ -5019,11 +5382,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(); @@ -5031,99 +5395,87 @@ 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; } } - NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]]); - [invocation setTarget:target]; - [invocation setSelector:selector]; - _assert(object == nil); + if (Finish_ < 2) { + if (RestartSubstrate_) + Finish_ = 2; + } - [NSThread detachNewThreadSelector:@selector(_detachNewThreadInvocation:) toTarget:self withObject:invocation]; -} + RestartSubstrate_ = false; -- (void) repairWithSelector:(SEL)selector { - [self - detachNewThreadSelector:selector - toTarget:database_ - withObject:nil - title:UCLocalize("REPAIRING") - ]; -} + 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; + } -- (void) setProgressError:(NSString *)error withTitle:(NSString *)title { - CYAlertView *sheet([[[CYAlertView alloc] - initWithTitle:title - buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil] - defaultButtonIndex:0 - ] autorelease]); + _trace(); + system("su -c /usr/bin/uicache mobile"); + _trace(); - [sheet setMessage:error]; - [sheet yieldToPopupAlertAnimated:YES]; - [sheet dismiss]; + UpdateExternalStatus(Finish_ == 0 ? 0 : 2); + + [progress_ setRunning:false]; + [self updateProgress]; + + [self applyRightButton]; } -- (void) startProgress { +- (void) addProgressEvent:(CydiaProgressEvent *)event { + [progress_ addEvent:event]; + [self updateProgress]; } - (bool) isProgressCancelled { - return false; + return cancel_ == 2; } -- (void) setConfigurationData:(NSString *)data { - static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$"); - - if (!conffile_r(data)) { - lprintf("E:invalid conffile\n"); - return; - } +- (void) cancel { + cancel_ = 2; + [self updateCancel]; +} - NSString *ofile = conffile_r[1]; - //NSString *nfile = conffile_r[2]; +- (void) setCancellable:(bool)cancellable { + unsigned cancel(cancel_); - 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]; + if (!cancellable) + cancel_ = 0; + else if (cancel_ == 0) + cancel_ = 1; - [alert setContext:@"conffile"]; - [alert show]; + if (cancel != cancel_) + [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]]; - } - - [status_ setText:[words componentsJoinedByString:@" "]]; +- (void) setProgressCancellable:(NSNumber *)cancellable { + [self setCancellable:[cancellable boolValue]]; } - (void) setProgressPercent:(NSNumber *)percent { - [progress_ setProgress:[percent floatValue]]; + [progress_ setPercent:[percent floatValue]]; + [self updateProgress]; } -- (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) setProgressStatus:(NSDictionary *)status { + if (status == nil) { + [progress_ setCurrent:0]; + [progress_ setTotal:0]; + [progress_ setSpeed:0]; + } else { + [progress_ setPercent:[[status objectForKey:@"Percent"] floatValue]]; + + [progress_ setCurrent:[[status objectForKey:@"Current"] floatValue]]; + [progress_ setTotal:[[status objectForKey:@"Total"] floatValue]]; + [progress_ setSpeed:[[status objectForKey:@"Speed"] floatValue]]; } -} -- (BOOL) isRunning { - return running_; + [self updateProgress]; } @end @@ -5702,11 +6054,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UIActionSheetDelegate > { _transient Database *database_; - Package *package_; - NSString *name_; + _H package_; + _H name_; bool commercial_; - NSMutableArray *buttons_; - UIBarButtonItem *button_; + _H buttons_; + _H button_; } - (id) initWithDatabase:(Database *)database forPackage:(NSString *)name; @@ -5715,22 +6067,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation CYPackageController -- (void) dealloc { - if (package_ != nil) - [package_ release]; - if (name_ != nil) - [name_ release]; - - [buttons_ release]; - - if (button_ != nil) - [button_ release]; - - [super dealloc]; -} - - (NSURL *) navigationURL { - return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", name_]]; + return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", (id) name_]]; } /* XXX: this is not safe at all... localization of /fail/ */ @@ -5824,23 +6162,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (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_]]]; + buttons_ = [NSMutableArray arrayWithCapacity:4]; + name_ = [NSString stringWithString:name]; + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/package/%@", UI_, (id) name_]]]; } return self; } - (void) reloadData { - if (package_ != nil) - [package_ autorelease]; + [super reloadData]; + package_ = [database_ packageWithName:name_]; [buttons_ removeAllObjects]; if (package_ != nil) { - [package_ parse]; + [(Package *) package_ parse]; - package_ = [package_ retain]; commercial_ = [package_ isCommercial]; if ([package_ mode] != nil) @@ -5856,9 +6193,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [buttons_ addObject:UCLocalize("REMOVE")]; } - if (button_ != nil) - [button_ release]; - NSString *title; switch ([buttons_ count]) { case 0: title = nil; break; @@ -5866,14 +6200,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { default: title = UCLocalize("MODIFY"); break; } - button_ = [[UIBarButtonItem alloc] + button_ = [[[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:self action:@selector(customButtonClicked) - ]; - - [super reloadData]; + ] autorelease]; } - (bool) isLoading { @@ -6261,25 +6593,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation HomeController -+ (BOOL) shouldHideNavigationBar { - return NO; +- (id) init { + if ((self = [super init]) != nil) { + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/home/", UI_]]]; + } return self; } - (NSURL *) navigationURL { return [NSURL URLWithString:@"cydia://home"]; } -- (void) _setMoreHeaders:(NSMutableURLRequest *)request { - [super _setMoreHeaders:request]; - - if (ChipID_ != nil) - [request setValue:ChipID_ forHTTPHeaderField:@"X-Chip-ID"]; - if (UniqueID_ != nil) - [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"]; - if (PLMN_ != nil) - [request setValue:PLMN_ forHTTPHeaderField:@"X-Carrier-ID"]; -} - - (void) aboutButtonClicked { UIAlertView *alert([[[UIAlertView alloc] init] autorelease]); @@ -6297,23 +6620,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [alert show]; } -- (void) viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - - if ([[self class] shouldHideNavigationBar]) - [[self navigationController] setNavigationBarHidden:NO animated:animated]; -} - -- (void) viewWillAppear:(BOOL)animated { - if (![self hasLoaded]) - [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/home/", UI_]]]; - - [super viewWillAppear:animated]; - - if ([[self class] shouldHideNavigationBar]) - [[self navigationController] setNavigationBarHidden:YES animated:animated]; -} - - (void) viewDidLoad { [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:UCLocalize("ABOUT") @@ -6335,15 +6641,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation ManageController -- (NSURL *) navigationURL { - return [NSURL URLWithString:@"cydia://manage"]; +- (id) init { + if ((self = [super init]) != nil) { + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/manage/", UI_]]]; + } return self; } -- (void) viewWillAppear:(BOOL)animated { - if (![self hasLoaded]) - [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/manage/", UI_]]]; - - [super viewWillAppear:animated]; +- (NSURL *) navigationURL { + return [NSURL URLWithString:@"cydia://manage"]; } - (void) viewDidLoad { @@ -6488,33 +6793,46 @@ 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 { [prompt_ setText:prompt]; } -- (void) setProgress:(float)progress { - [progress_ setProgress:progress]; -} +- (void) setProgress:(float)progress { + [progress_ setProgress:progress]; +} + +@end +/* }}} */ + +/* Cydia Navigation Controller Interface {{{ */ +@interface CYNavigationController : UINavigationController { + _transient Database *database_; + _transient id delegate_; +} + +- (NSArray *) navigationURLCollection; +- (id) initWithDatabase:(Database *)database; +- (void) unloadData:(BOOL)selected; @end /* }}} */ -@class CYNavigationController; - /* Cydia Tab Bar Controller {{{ */ @interface CYTabBarController : UITabBarController < UITabBarControllerDelegate, @@ -6538,6 +6856,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) beginUpdate; - (void) raiseBar:(BOOL)animated; - (BOOL) updating; +- (void) unloadData; @end @@ -6587,11 +6906,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return items; } -- (void) reloadData { - for (CYViewController *controller in [self viewControllers]) - [controller reloadData]; +- (void) unloadData { + CYNavigationController *selected((CYNavigationController *) [self selectedViewController]); + for (CYNavigationController *controller in [self viewControllers]) + [controller unloadData:(controller == selected)]; - [(CYNavigationController *)[self unselectedViewController] reloadData]; + if (CYNavigationController *unselected = (CYNavigationController *) [self unselectedViewController]) + [unselected unloadData:YES]; } - (void) dealloc { @@ -6671,26 +6992,25 @@ 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) addProgressEvent:(CydiaProgressEvent *)event { + [refreshbar_ setPrompt:[event compoundMessage]]; } - (bool) isProgressCancelled { return !updating_; } -- (void) setProgressTitle:(NSString *)title { - [refreshbar_ setPrompt:title]; +- (void) setProgressCancellable:(NSNumber *)cancellable { + [refreshbar_ setCancellable:(updating_ && [cancellable boolValue])]; } - (void) setProgressPercent:(NSNumber *)percent { [refreshbar_ setProgress:[percent floatValue]]; } -- (void) addProgressOutput:(NSString *)output { +- (void) setProgressStatus:(NSDictionary *)status { + if (status != nil) + [self setProgressPercent:[status objectForKey:@"Percent"]]; } - (void) setUpdateDelegate:(id)delegate { @@ -6804,19 +7124,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @end /* }}} */ -/* Cydia Navigation Controller {{{ */ -@interface CYNavigationController : UINavigationController { - _transient Database *database_; - _transient id delegate_; -} - -- (NSArray *) navigationURLCollection; -- (id) initWithDatabase:(Database *)database; -- (void) reloadData; - -@end - +/* Cydia Navigation Controller Implementation {{{ */ @implementation CYNavigationController - (NSArray *) navigationURLCollection { @@ -6831,14 +7140,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return stack; } -- (void) reloadData { +- (void) unloadData:(BOOL)selected { + CYViewController *top((CYViewController *) [self topViewController]); + bool loaded([top hasLoaded]); + 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]; + NSLog(@"%@ %@", page, top); + if (!selected || page != top) + [page unloadData]; } + + if (selected && loaded) + [top reloadData]; } - (void) setDelegate:(id)delegate { @@ -6866,10 +7179,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSURL *url([request URL]); if (url == nil) return NO; + NSString *scheme([[url scheme] lowercaseString]); - if (scheme == nil || ![scheme isEqualToString:@"cydia"]) - return NO; - return YES; + if (scheme != nil && [scheme isEqualToString:@"cydia"]) + return YES; + if ([[url absoluteString] hasPrefix:@"about:cydia-"]) + return YES; + + return NO; } + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request { @@ -6896,8 +7213,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSURL *url([request URL]); NSString *href([url absoluteString]); + NSString *scheme([[url scheme] lowercaseString]); + + NSString *path; + + if ([scheme isEqualToString:@"cydia"]) + path = [href substringFromIndex:8]; + else if ([scheme isEqualToString:@"about"]) + path = [href substringFromIndex:12]; + else _assert(false); - NSString *path([href substringFromIndex:8]); NSRange slash([path rangeOfString:@"/"]); NSString *command; @@ -6957,7 +7282,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Section Controller {{{ */ @interface SectionController : FilteredPackageListController { - NSString *section_; + _H section_; } - (id) initWithDatabase:(Database *)database section:(NSString *)section; @@ -6999,7 +7324,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSMutableArray *sections_; NSMutableArray *filtered_; UITableView *list_; - BOOL editing_; } - (id) initWithDatabase:(Database *)database; @@ -7022,24 +7346,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) updateNavigationItem { - [[self navigationItem] setTitle:editing_ ? UCLocalize("SECTION_VISIBILITY") : UCLocalize("SECTIONS")]; + [[self navigationItem] setTitle:[self isEditing] ? UCLocalize("SECTION_VISIBILITY") : UCLocalize("SECTIONS")]; if ([sections_ count] == 0) { [[self navigationItem] setRightBarButtonItem:nil]; } else { [[self navigationItem] setRightBarButtonItem:[[UIBarButtonItem alloc] - initWithBarButtonSystemItem:(editing_ ? UIBarButtonSystemItemDone : UIBarButtonSystemItemEdit) + initWithBarButtonSystemItem:([self isEditing] ? UIBarButtonSystemItemDone : UIBarButtonSystemItemEdit) target:self action:@selector(editButtonClicked) ] animated:([[self navigationItem] rightBarButtonItem] != nil)]; } } -- (BOOL) isEditing { - return editing_; -} +- (void) setEditing:(BOOL)editing animated:(BOOL)animated { + [super setEditing:editing animated:animated]; -- (void) setEditing:(BOOL)editing { - if ((editing_ = editing)) + if (editing) [list_ reloadData]; else [delegate_ updateData]; @@ -7054,16 +7376,27 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - if (editing_) [self setEditing:NO]; + if ([self isEditing]) [self setEditing:NO]; } - (Section *) sectionAtIndexPath:(NSIndexPath *)indexPath { - Section *section = (editing_ ? [sections_ objectAtIndex:[indexPath row]] : ([indexPath row] == 0 ? nil : [filtered_ objectAtIndex:([indexPath row] - 1)])); + Section *section = nil; + int index = [indexPath row]; + if (![self isEditing]) { + index -= 1; + if (index >= 0) + section = [filtered_ objectAtIndex:index]; + } else { + section = [sections_ objectAtIndex:index]; + } return section; } - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return editing_ ? [sections_ count] : [filtered_ count] + 1; + if ([self isEditing]) + return [sections_ count]; + else + return [filtered_ count] + 1; } /*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { @@ -7077,13 +7410,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (cell == nil) cell = [[[SectionCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease]; - [cell setSection:[self sectionAtIndexPath:indexPath] editing:editing_]; + [cell setSection:[self sectionAtIndexPath:indexPath] editing:[self isEditing]]; return cell; } - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - if (editing_) + if ([self isEditing]) return; Section *section = [self sectionAtIndexPath:indexPath]; @@ -7184,7 +7517,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) editButtonClicked { - [self setEditing:(!editing_)]; + [self setEditing:![self isEditing] animated:YES]; } @end @@ -7201,7 +7534,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSMutableArray *sections_; UITableView *list_; unsigned upgrades_; - BOOL hasSentFirstLoad_; } - (id) initWithDatabase:(Database *)database; @@ -7222,21 +7554,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return [NSURL URLWithString:@"cydia://changes"]; } -- (void) viewWillAppear:(BOOL)animated { - // Loads after it appears, so don't load beforehand. - loaded_ = YES; - [super viewWillAppear:animated]; -} - - (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - - if (!hasSentFirstLoad_) { - hasSentFirstLoad_ = YES; - [self performSelector:@selector(reloadData) withObject:nil afterDelay:0.0]; - } else { - [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated]; - } + [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated]; } - (NSInteger) numberOfSectionsInTableView:(UITableView *)list { @@ -7346,7 +7666,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _trace(); } -- (void) reloadData { +- (void) _reloadData { @synchronized (database_) { era_ = [database_ era]; NSArray *packages = [database_ packages]; @@ -7444,13 +7764,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { PrintTimes(); } } +- (void) reloadData { + [super reloadData]; + [self performSelector:@selector(_reloadData) withObject:nil afterDelay:0]; +} + @end /* }}} */ /* Search Controller {{{ */ @interface SearchController : FilteredPackageListController < UISearchBarDelegate > { - UISearchBar *search_; + _H search_; BOOL searchloaded_; } @@ -7463,7 +7788,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation SearchController - (void) dealloc { - [search_ release]; + [search_ setDelegate:nil]; [super dealloc]; } @@ -7492,7 +7817,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (id) initWithDatabase:(Database *)database { if ((self = [super initWithDatabase:database title:UCLocalize("SEARCH") filter:@selector(isUnfilteredAndSearchedForBy:) with:nil])) { - search_ = [[UISearchBar alloc] init]; + search_ = [[[UISearchBar alloc] init] autorelease]; + [search_ setDelegate:self]; } return self; } @@ -7512,7 +7838,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { textField = MSHookIvar(search_, "_searchField"); [textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin]; - [search_ setDelegate:self]; [textField setEnablesReturnKeyAutomatically:NO]; [[self navigationItem] setTitleView:textField]; } @@ -7520,8 +7845,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) reloadData { [self setObject:[search_ text]]; - [super reloadData]; [self resetCursor]; + + [super reloadData]; } - (void) didSelectPackage:(Package *)package { @@ -7915,6 +8241,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [key_ release]; key_ = [[source_ key] retain]; [self setObject:source_]; + [[self navigationItem] setTitle:[source_ label]]; [super reloadData]; @@ -8143,9 +8470,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [alert show]; } - [delegate_ setStatusBarShowsProgress:NO]; - [delegate_ removeProgressHUD:hud_]; + [delegate_ releaseNetworkActivityIndicator]; + [delegate_ removeProgressHUD:hud_]; [hud_ autorelease]; hud_ = nil; @@ -8192,8 +8519,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; if (UniqueID_ != nil) [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"]; - if (Role_ != nil) - [request setValue:Role_ forHTTPHeaderField:@"X-Role"]; return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease]; } @@ -8224,6 +8549,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: @@ -8570,6 +8896,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) reloadData { [super reloadData]; + [table_ reloadData]; } @@ -8652,7 +8979,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @interface Cydia : UIApplication < ConfirmationControllerDelegate, - ProgressControllerDelegate, + DatabaseDelegate, CydiaDelegate, UINavigationControllerDelegate, UITabBarControllerDelegate @@ -8661,6 +8988,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UIWindow *window_; CYTabBarController *tabbar_; + CYEmulatedLoadingController *emulated_; NSMutableArray *essential_; NSMutableArray *broken_; @@ -8706,6 +9034,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { ] autorelease]; [alert setContext:@"fixhalf"]; + [alert setNumberOfRows:2]; [alert show]; } else if (!Ignored_ && [essential_ count] != 0) { int count = [essential_ count]; @@ -8757,7 +9086,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) _updateData { [self _saveConfig]; - [tabbar_ reloadData]; + [tabbar_ unloadData]; CYNavigationController *navigation = [self queueNavigationController]; @@ -8877,6 +9206,67 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [database_ update]; } +- (void) complete { + @synchronized (self) { + [self _reloadDataWithInvocation:nil]; + } +} + +- (void) disemulate { + if (emulated_ == nil) + return; + + [window_ addSubview:[tabbar_ view]]; + [[emulated_ view] removeFromSuperview]; + [emulated_ release]; + emulated_ = nil; + [window_ setUserInteractionEnabled:YES]; +} + +- (void) presentModalViewController:(UIViewController *)controller force:(BOOL)force { + UINavigationController *navigation([[[CYNavigationController alloc] initWithRootViewController:controller] autorelease]); + if (IsWildcat_) + [navigation setModalPresentationStyle:UIModalPresentationFormSheet]; + + UIViewController *parent; + if (emulated_ == nil) + parent = tabbar_; + else if (!force) + parent = emulated_; + else { + [self disemulate]; + parent = tabbar_; + } + + [parent 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 force:YES]; + + [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:@"REPAIRING"]; + _trace(); +} + +- (void) repairWithSelector:(SEL)selector { + [self performSelectorOnMainThread:@selector(repairWithInvocation:) withObject:[NSInvocation invocationWithSelector:selector forTarget:database_] waitUntilDone:YES]; +} + - (void) syncData { [self _saveConfig]; @@ -8895,18 +9285,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { fclose(file); - 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 detachNewProgressSelector:@selector(update_) toTarget:self forController:nil title:@"UPDATING_SOURCES"]; - [progress - detachNewThreadSelector:@selector(update_) - toTarget:self - withObject:nil - title:UCLocalize("UPDATING_SOURCES") - ]; + [self complete]; } - (void) addTrivialSource:(NSString *)href { @@ -9007,37 +9388,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } -- (void) complete { - @synchronized (self) { - [self _reloadDataWithInvocation:nil]; - } -} - - (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:@"RUNNING"]; --locked_; [self complete]; } @@ -9053,11 +9407,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 { @@ -9076,7 +9439,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { NSString *context([alert context]); - if ([context isEqualToString:@"fixhalf"]) { + 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_) { @@ -9129,6 +9501,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (BOOL) isSafeToSuspend { + if (locked_ != 0) { +#if !ForRelease + NSLog(@"isSafeToSuspend: locked_ != 0"); +#endif + return false; + } + // Use external process status API internally. // This is probably a really bad idea. // XXX: what is the point of this? does this solve anything at all? @@ -9139,7 +9518,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { notify_cancel(notify_token); } - return locked_ == 0 && status == 0; + if (status != 0) { +#if !ForRelease + NSLog(@"isSafeToSuspend: status != 0"); +#endif + return false; + } + +#if !ForRelease + NSLog(@"isSafeToSuspend: -> true"); +#endif + return true; } - (void) applicationSuspend:(__GSEvent *)event { @@ -9321,6 +9710,33 @@ 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 setNumberOfRows:2]; + [alert show]; +} + - (void) addStashController { ++locked_; stash_ = [[StashController alloc] init]; @@ -9337,13 +9753,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]; @@ -9381,29 +9793,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [tabbar_ setUpdateDelegate:self]; } -- (CYEmulatedLoadingController *) showEmulatedLoadingControllerInView:(UIView *)view { - static CYEmulatedLoadingController *fake = nil; - - if (view != nil) { - if (fake == nil) - fake = [[CYEmulatedLoadingController alloc] initWithDatabase:database_]; - [view addSubview:[fake view]]; - } else { - [[fake view] removeFromSuperview]; - [fake release]; - fake = nil; - } - - return fake; -} - - (void) applicationDidFinishLaunching:(id)unused { _trace(); - CydiaApp = self; - if ([self respondsToSelector:@selector(setApplicationSupportsShakeToEdit:)]) [self setApplicationSupportsShakeToEdit:NO]; + @synchronized (HostConfig_) { + [BridgedHosts_ addObject:[[NSURL URLWithString:CydiaURL(@"")] host]]; + } + [NSURLCache setSharedURLCache:[[[SDURLCache alloc] initWithMemoryCapacity:524288 diskCapacity:10485760 @@ -9423,6 +9821,9 @@ _trace(); essential_ = [[NSMutableArray alloc] initWithCapacity:4]; broken_ = [[NSMutableArray alloc] initWithCapacity:4]; + // XXX: I really need this thing... like, seriously... I'm sorry + [[[CYBrowserController alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/appcache/", UI_]]] reloadData]; + window_ = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; [window_ orderFront:self]; [window_ makeKey:self]; @@ -9449,76 +9850,92 @@ _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(); } +- (NSArray *) defaultStartPages { + NSMutableArray *standard = [NSMutableArray array]; + [standard addObject:[NSArray arrayWithObject:@"cydia://home"]]; + [standard addObject:[NSArray arrayWithObject:@"cydia://sections"]]; + [standard addObject:[NSArray arrayWithObject:@"cydia://changes"]]; + if (!IsWildcat_) { + [standard addObject:[NSArray arrayWithObject:@"cydia://manage"]]; + } else { + [standard addObject:[NSArray arrayWithObject:@"cydia://installed"]]; + [standard addObject:[NSArray arrayWithObject:@"cydia://sources"]]; + } + [standard addObject:[NSArray arrayWithObject:@"cydia://search"]]; + return standard; +} + - (void) loadData { _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] force:NO]; return; } else { - if ([[self showEmulatedLoadingControllerInView:window_] modalViewController] != nil) - [[self showEmulatedLoadingControllerInView:window_] dismissModalViewControllerAnimated:YES]; + if ([emulated_ modalViewController] != nil) + [emulated_ dismissModalViewControllerAnimated:YES]; [window_ setUserInteractionEnabled:NO]; } [self reloadData]; PrintTimes(); - [window_ addSubview:[tabbar_ view]]; - [self showEmulatedLoadingControllerInView:nil]; - [window_ setUserInteractionEnabled:YES]; + [self disemulate]; - int selectedIndex = 0; - NSMutableArray *items = nil; + int savedIndex = [[Metadata_ objectForKey:@"InterfaceIndex"] intValue]; + NSArray *saved = [[Metadata_ objectForKey:@"InterfaceState"] mutableCopy]; + int standardIndex = 0; + NSArray *standard = [self defaultStartPages]; - bool recently = false; - NSDate *closed([Metadata_ objectForKey:@"LastClosed"]); - if (closed != nil) { + BOOL valid = YES; + + if (saved == nil) + valid = NO; + + NSDate *closed = [Metadata_ objectForKey:@"LastClosed"]; + if (valid && closed != nil) { NSTimeInterval interval([closed timeIntervalSinceNow]); // XXX: Is 15 minutes the optimal time here? - if (interval <= 0 && interval > -(15*60)) - recently = true; + if (interval > 0 && interval <= -(15*60)) + valid = NO; } - items = [[Metadata_ objectForKey:@"InterfaceState"] mutableCopy]; - selectedIndex = [[Metadata_ objectForKey:@"InterfaceIndex"] intValue]; + if (valid && [saved count] != [standard count]) + valid = NO; - 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"]]; - [items addObject:[NSArray arrayWithObject:@"cydia://changes"]]; - if (!IsWildcat_) { - [items addObject:[NSArray arrayWithObject:@"cydia://manage"]]; - } else { - [items addObject:[NSArray arrayWithObject:@"cydia://installed"]]; - [items addObject:[NSArray arrayWithObject:@"cydia://sources"]]; + if (valid) { + for (unsigned int i = 0; i < [standard count]; i++) { + NSArray *std = [standard objectAtIndex:i], *sav = [saved objectAtIndex:i]; + // XXX: The "hasPrefix" sanity check here could be, in theory, fooled, + // but it's good enough for now. + if ([sav count] == 0 || ![[sav objectAtIndex:0] hasPrefix:[std objectAtIndex:0]]) { + valid = NO; + break; + } } - [items addObject:[NSArray arrayWithObject:@"cydia://search"]]; } - [tabbar_ setSelectedIndex:selectedIndex]; + NSArray *items = nil; + if (valid) { + [tabbar_ setSelectedIndex:savedIndex]; + items = saved; + } else { + [tabbar_ setSelectedIndex:standardIndex]; + items = standard; + } + for (unsigned int tab = 0; tab < [[tabbar_ viewControllers] count]; tab++) { NSArray *stack = [items objectAtIndex:tab]; CYNavigationController *navigation = [[tabbar_ viewControllers] objectAtIndex:tab]; @@ -9551,6 +9968,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_; @@ -9575,52 +10008,63 @@ MSHook(void, UIWebDocumentView$_setUIKitDelegate$, UIWebDocumentView *self, SEL return _UIWebDocumentView$_setUIKitDelegate$(self, _cmd, delegate); } -static NSNumber *shouldPlayKeyboardSounds; +static NSSet *MobilizedFiles_; -Class $UIHardware; +static NSURL *MobilizeURL(NSURL *url) { + NSString *path([url path]); + if ([path hasPrefix:@"/var/root/"]) { + NSString *file([path substringFromIndex:10]); + if ([MobilizedFiles_ containsObject:file]) + url = [NSURL fileURLWithPath:[@"/var/mobile/" stringByAppendingString:file] isDirectory:NO]; + } -MSHook(void, UIHardware$_playSystemSound$, Class self, SEL _cmd, int sound) { - switch (sound) { - case 1104: // Keyboard Button Clicked - case 1105: // Keyboard Delete Repeated - if (shouldPlayKeyboardSounds == nil) { - NSDictionary *dict([[[NSDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.preferences.sounds.plist"] autorelease]); - shouldPlayKeyboardSounds = [([dict objectForKey:@"keyboard"] ?: (id) kCFBooleanTrue) retain]; - } + return url; +} - if (![shouldPlayKeyboardSounds boolValue]) - break; +Class $CFXPreferencesPropertyListSource; +@class CFXPreferencesPropertyListSource; - default: - _UIHardware$_playSystemSound$(self, _cmd, sound); - } +MSHook(BOOL, CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync, CFXPreferencesPropertyListSource *self, SEL _cmd) { + NSURL *&url(MSHookIvar(self, "_url")), *old(url); + NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); + url = MobilizeURL(url); + BOOL value(_CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync(self, _cmd)); + //NSLog(@"%@ %s", [url absoluteString], value ? "YES" : "NO"); + url = old; + [pool release]; + return value; +} + +MSHook(void *, CFXPreferencesPropertyListSource$createPlistFromDisk, CFXPreferencesPropertyListSource *self, SEL _cmd) { + NSURL *&url(MSHookIvar(self, "_url")), *old(url); + NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); + url = MobilizeURL(url); + void *value(_CFXPreferencesPropertyListSource$createPlistFromDisk(self, _cmd)); + //NSLog(@"%@ %@", [url absoluteString], value); + url = old; + [pool release]; + return value; } -Class $UIApplication; +Class $NSURLConnection; -MSHook(void, UIApplication$_updateApplicationAccessibility, UIApplication *self, SEL _cmd) { - static BOOL initialized = NO; - static BOOL started = NO; +MSHook(id, NSURLConnection$init$, NSURLConnection *self, SEL _cmd, NSURLRequest *request, id delegate, BOOL usesCache, int64_t maxContentLength, BOOL startImmediately, NSDictionary *connectionProperties) { + NSMutableURLRequest *copy([request mutableCopy]); - NSDictionary *dict([[[NSDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.Accessibility.plist"] autorelease]); - BOOL enabled = [[dict objectForKey:@"VoiceOverTouchEnabled"] boolValue] || [[dict objectForKey:@"VoiceOverTouchEnabledByiTunes"] boolValue]; + NSURL *url([copy URL]); + NSString *host([url host]); + NSString *scheme([[url scheme] lowercaseString]); - if ([self respondsToSelector:@selector(_accessibilityBundlePrincipalClass)]) { - id bundle = [self performSelector:@selector(_accessibilityBundlePrincipalClass)]; - if (![bundle respondsToSelector:@selector(_accessibilityStopServer)]) return; - if (![bundle respondsToSelector:@selector(_accessibilityStartServer)]) return; + NSString *compound([NSString stringWithFormat:@"%@:%@", scheme, host]); - if (initialized && !enabled) { - initialized = NO; - [bundle performSelector:@selector(_accessibilityStopServer)]; - } else if (enabled) { - initialized = YES; - if (!started) { - started = YES; - [bundle performSelector:@selector(_accessibilityStartServer)]; - } - } + @synchronized (HostConfig_) { + if ([copy respondsToSelector:@selector(setHTTPShouldUsePipelining:)]) + if ([PipelinedHosts_ containsObject:host] || [PipelinedHosts_ containsObject:compound]) + [copy setHTTPShouldUsePipelining:YES]; } + + if ((self = _NSURLConnection$init$(self, _cmd, copy, delegate, usesCache, maxContentLength, startImmediately, connectionProperties)) != nil) { + } return self; } int main(int argc, char *argv[]) { _pooled @@ -9640,17 +10084,53 @@ int main(int argc, char *argv[]) { _pooled 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:@""]]); + UIDevice *device([UIDevice currentDevice]); + if (![device respondsToSelector:@selector(userInterfaceIdiom)]) + Idiom_ = @"iphone"; + else { + UIUserInterfaceIdiom idiom([device userInterfaceIdiom]); + if (idiom == UIUserInterfaceIdiomPhone) + Idiom_ = @"iphone"; + else if (idiom == UIUserInterfaceIdiomPad) + Idiom_ = @"ipad"; + else + NSLog(@"unknown UIUserInterfaceIdiom!"); + } + + SessionData_ = [[NSMutableDictionary alloc] initWithCapacity:4]; + + HostConfig_ = [[NSObject alloc] init]; + @synchronized (HostConfig_) { + BridgedHosts_ = [NSMutableSet setWithCapacity:4]; + PipelinedHosts_ = [NSMutableSet setWithCapacity:4]; + } + + UI_ = CydiaURL([NSString stringWithFormat:@"ui/ios~%@", Idiom_]); PackageName = reinterpret_cast(method_getImplementation(class_getInstanceMethod([Package class], @selector(cyname)))); + MobilizedFiles_ = [NSMutableSet setWithObjects: + @"Library/Preferences/com.apple.Accessibility.plist", + @"Library/Preferences/com.apple.preferences.sounds.plist", + nil]; + /* Library Hacks {{{ */ class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16"); + $CFXPreferencesPropertyListSource = objc_getClass("CFXPreferencesPropertyListSource"); + + Method CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync(class_getInstanceMethod($CFXPreferencesPropertyListSource, @selector(_backingPlistChangedSinceLastSync))); + if (CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync != NULL) { + _CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync = reinterpret_cast(method_getImplementation(CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync)); + method_setImplementation(CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync, reinterpret_cast(&$CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync)); + } + + Method CFXPreferencesPropertyListSource$createPlistFromDisk(class_getInstanceMethod($CFXPreferencesPropertyListSource, @selector(createPlistFromDisk))); + if (CFXPreferencesPropertyListSource$createPlistFromDisk != NULL) { + _CFXPreferencesPropertyListSource$createPlistFromDisk = reinterpret_cast(method_getImplementation(CFXPreferencesPropertyListSource$createPlistFromDisk)); + method_setImplementation(CFXPreferencesPropertyListSource$createPlistFromDisk, reinterpret_cast(&$CFXPreferencesPropertyListSource$createPlistFromDisk)); + } + $WebDefaultUIKitDelegate = objc_getClass("WebDefaultUIKitDelegate"); Method UIWebDocumentView$_setUIKitDelegate$(class_getInstanceMethod([WebView class], @selector(_setUIKitDelegate:))); if (UIWebDocumentView$_setUIKitDelegate$ != NULL) { @@ -9658,37 +10138,40 @@ int main(int argc, char *argv[]) { _pooled method_setImplementation(UIWebDocumentView$_setUIKitDelegate$, reinterpret_cast(&$UIWebDocumentView$_setUIKitDelegate$)); } - $UIHardware = objc_getClass("UIHardware"); - Method UIHardware$_playSystemSound$(class_getClassMethod($UIHardware, @selector(_playSystemSound:))); - if (UIHardware$_playSystemSound$ != NULL) { - _UIHardware$_playSystemSound$ = reinterpret_cast(method_getImplementation(UIHardware$_playSystemSound$)); - method_setImplementation(UIHardware$_playSystemSound$, reinterpret_cast(&$UIHardware$_playSystemSound$)); - } - - $UIApplication = objc_getClass("UIApplication"); - Method UIApplication$_updateApplicationAccessibility(class_getInstanceMethod($UIApplication, @selector(_updateApplicationAccessibility))); - if (UIApplication$_updateApplicationAccessibility != NULL) { - _UIApplication$_updateApplicationAccessibility = reinterpret_cast(method_getImplementation(UIApplication$_updateApplicationAccessibility)); - method_setImplementation(UIApplication$_updateApplicationAccessibility, reinterpret_cast(&$UIApplication$_updateApplicationAccessibility)); + $NSURLConnection = objc_getClass("NSURLConnection"); + Method NSURLConnection$init$(class_getInstanceMethod($NSURLConnection, @selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:))); + if (NSURLConnection$init$ != NULL) { + _NSURLConnection$init$ = reinterpret_cast(method_getImplementation(NSURLConnection$init$)); + method_setImplementation(NSURLConnection$init$, reinterpret_cast(&$NSURLConnection$init$)); } /* }}} */ /* Set Locale {{{ */ Locale_ = CFLocaleCopyCurrent(); Languages_ = [NSLocale preferredLanguages]; + //CFStringRef locale(CFLocaleGetIdentifier(Locale_)); //NSLog(@"%@", [Languages_ description]); const char *lang; - if (Languages_ == nil || [Languages_ count] == 0) + if (Locale_ != NULL) + lang = [(NSString *) CFLocaleGetIdentifier(Locale_) UTF8String]; + else if (Languages_ != nil && [Languages_ count] != 0) + lang = [[Languages_ objectAtIndex:0] UTF8String]; + else // XXX: consider just setting to C and then falling through? lang = NULL; - else { - lang = [[Languages_ objectAtIndex:0] UTF8String]; - setenv("LANG", lang, true); + + if (lang != NULL) { + Pcre pattern("^([a-z][a-z])(?:-[A-Za-z]*)?(_[A-Z][A-Z])?$"); + lang = !pattern(lang) ? NULL : [pattern->*@"%1$@%2$@" UTF8String]; } - //std::setlocale(LC_ALL, lang); NSLog(@"Setting Language: %s", lang); + + if (lang != NULL) { + setenv("LANG", lang, true); + std::setlocale(LC_ALL, lang); + } /* }}} */ apr_app_initialize(&argc, const_cast(&argv), NULL); @@ -9718,7 +10201,6 @@ int main(int argc, char *argv[]) { _pooled /* }}} */ App_ = [[NSBundle mainBundle] bundlePath]; - Home_ = NSHomeDirectory(); Advanced_ = YES; setuid(0); @@ -9858,8 +10340,11 @@ int main(int argc, char *argv[]) { _pooled Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil]; #define MobileSubstrate_(name) \ - if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", F_OK) == 0) \ - dlopen("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", RTLD_LAZY | RTLD_GLOBAL); + if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", F_OK) == 0) { \ + void *handle(dlopen("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", RTLD_LAZY | RTLD_GLOBAL)); \ + if (handle == NULL) \ + NSLog(@"%s", dlerror()); \ + } MobileSubstrate_(Activator) MobileSubstrate_(libstatusbar) @@ -9874,7 +10359,7 @@ int main(int argc, char *argv[]) { _pooled if (access("/tmp/.cydia.fw", F_OK) == 0) { unlink("/tmp/.cydia.fw"); goto firmware; - } else if (access("/User", F_OK) != 0 || version < 2) { + } else if (access("/User", F_OK) != 0 || version < 4) { firmware: _trace(); system("/usr/libexec/cydia/firmware.sh");