X-Git-Url: https://git.saurik.com/cydia.git/blobdiff_plain/6b92acabc8c971bc5e1a0d3a5f8cc9b4e19e93fb..12762dfadae0a8f450bc8fc25ad31384d4c096ef:/Cydia.mm diff --git a/Cydia.mm b/Cydia.mm index 55048243..7a5a33d1 100644 --- a/Cydia.mm +++ b/Cydia.mm @@ -35,7 +35,12 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// XXX: wtf/FastMalloc.h... wtf? +#define USE_SYSTEM_MALLOC 1 + /* #include Directives {{{ */ +#import "UICaboodle.h" + #include #include @@ -50,15 +55,6 @@ // XXX: remove #import -#import -//#include - -#include -#include -#include - -#import - #include #include @@ -83,6 +79,8 @@ #include #include #include +#include +#include #include #include @@ -100,12 +98,30 @@ extern "C" { #import "BrowserView.h" #import "ResetView.h" -#import "UICaboodle.h" + +#import "substrate.h" /* }}} */ //#define _finline __attribute__((force_inline)) #define _finline inline +struct timeval _ltv; +bool _itv; + +#define _limit(count) do { \ + static size_t _count(0); \ + if (++_count == count) \ + exit(0); \ +} while (false) + +static uint64_t profile_; + +#define _timestamp ({ \ + struct timeval tv; \ + gettimeofday(&tv, NULL); \ + tv.tv_sec * 1000000 + tv.tv_usec; \ +}) + /* Objective-C Handle<> {{{ */ template class _H { @@ -148,11 +164,18 @@ class _H { #define _pooled _H _pool([[NSAutoreleasePool alloc] init], true); +void NSLogPoint(const char *fix, const CGPoint &point) { + NSLog(@"%s(%g,%g)", fix, point.x, point.y); +} + void NSLogRect(const char *fix, const CGRect &rect) { NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); } -static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch; +/* NSForcedOrderingSearch doesn't work on the iPhone */ +static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch; +static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_; +static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch; /* iPhoneOS 2.0 Compatibility {{{ */ #ifdef __OBJC2__ @@ -239,8 +262,118 @@ extern NSString * const kCAFilterNearest; #define lprintf(args...) fprintf(stderr, args) -#define ForSaurik 1 +#define ForRelease 0 +#define ForSaurik (1 && !ForRelease) +#define IgnoreInstall (0 && !ForRelease) #define RecycleWebViews 0 +#define AlwaysReload (1 && !ForRelease) + +/* Radix Sort {{{ */ +@interface NSMutableArray (Radix) +- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object; +@end + +@implementation NSMutableArray (Radix) + +- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object { + NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]); + [invocation setSelector:selector]; + [invocation setArgument:&object atIndex:2]; + + size_t count([self count]); + + struct RadixItem { + size_t index; + uint32_t key; + } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count); + + for (size_t i(0); i != count; ++i) { + RadixItem &item(lhs[i]); + item.index = i; + + id object([self objectAtIndex:i]); + [invocation setTarget:object]; + + [invocation invoke]; + [invocation getReturnValue:&item.key]; + } + + static const size_t width = 32; + static const size_t bits = 11; + static const size_t slots = 1 << bits; + static const size_t passes = (width + (bits - 1)) / bits; + + size_t *hist(new size_t[slots]); + + for (size_t pass(0); pass != passes; ++pass) { + memset(hist, 0, sizeof(size_t) * slots); + + for (size_t i(0); i != count; ++i) { + uint32_t key(lhs[i].key); + key >>= pass * bits; + key &= _not(uint32_t) >> width - bits; + ++hist[key]; + } + + size_t offset(0); + for (size_t i(0); i != slots; ++i) { + size_t local(offset); + offset += hist[i]; + hist[i] = local; + } + + for (size_t i(0); i != count; ++i) { + uint32_t key(lhs[i].key); + key >>= pass * bits; + key &= _not(uint32_t) >> width - bits; + rhs[hist[key]++] = lhs[i]; + } + + RadixItem *tmp(lhs); + lhs = rhs; + rhs = tmp; + } + + delete [] hist; + + NSMutableArray *values([NSMutableArray arrayWithCapacity:count]); + for (size_t i(0); i != count; ++i) + [values addObject:[self objectAtIndex:lhs[i].index]]; + [self setArray:values]; + + delete [] swap; +} + +@end +/* }}} */ + +/* Apple Bug Fixes {{{ */ +@implementation UIWebDocumentView (Cydia) + +- (void) _setScrollerOffset:(CGPoint)offset { + UIScroller *scroller([self _scroller]); + + CGSize size([scroller contentSize]); + CGSize bounds([scroller bounds].size); + + CGPoint max; + max.x = size.width - bounds.width; + max.y = size.height - bounds.height; + + // wtf Apple?! + if (max.x < 0) + max.x = 0; + if (max.y < 0) + max.y = 0; + + offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x; + offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y; + + [scroller setOffset:offset]; +} + +@end +/* }}} */ typedef enum { kUIControlEventMouseDown = 1 << 0, @@ -251,6 +384,19 @@ typedef enum { kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside) } UIControlEventMasks; +NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) { + size_t length([self length] - state->state); + if (length <= 0) + return 0; + else if (length > count) + length = count; + for (size_t i(0); i != length; ++i) + objects[i] = [self item:state->state++]; + state->itemsPtr = objects; + state->mutationsPtr = (unsigned long *) self; + return length; +} + @interface NSString (UIKit) - (NSString *) stringByAddingPercentEscapes; - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1; @@ -264,10 +410,7 @@ typedef enum { @implementation NSString (Cydia) + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length { - char data[length + 1]; - memcpy(data, bytes, length); - data[length] = '\0'; - return [NSString stringWithUTF8String:data]; + return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease]; } - (NSComparisonResult) compareByPath:(NSString *)other { @@ -460,6 +603,7 @@ static const float KeyboardTime_ = 0.3f; #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist" #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb" +#define NotifyConfig_ "/etc/notify.conf" static CGColor Blue_; static CGColor Blueish_; @@ -485,25 +629,18 @@ static UIFont *Font18Bold_; static UIFont *Font22Bold_; static const char *Machine_ = NULL; -static const NSString *UniqueID_ = NULL; - -unsigned Major_; -unsigned Minor_; -unsigned BugFix_; +static const NSString *UniqueID_ = nil; +static const NSString *Build_ = nil; CFLocaleRef Locale_; CGColorSpaceRef space_; -#define FW_LEAST(major, minor, bugfix) \ - (major < Major_ || major == Major_ && \ - (minor < Minor_ || minor == Minor_ && \ - bugfix <= BugFix_)) - bool bootstrap_; bool reload_; static NSDictionary *SectionMap_; static NSMutableDictionary *Metadata_; +static NSMutableDictionary *Indices_; static _transient NSMutableDictionary *Settings_; static _transient NSString *Role_; static _transient NSMutableDictionary *Packages_; @@ -548,7 +685,7 @@ NSString *SizeString(double size) { static const char *powers_[] = {"B", "kB", "MB", "GB"}; - return [NSString stringWithFormat:@"%s%.1f%s", (negative ? "-" : ""), size, powers_[power]]; + return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]]; } NSString *StripVersion(NSString *version) { @@ -628,6 +765,7 @@ bool isSectionVisible(NSString *section) { - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag; - (RVPage *) pageForPackage:(NSString *)name; - (void) openMailToURL:(NSURL *)url; +- (void) clearFirstResponder; @end /* }}} */ @@ -656,6 +794,7 @@ class Status : } virtual void Fetch(pkgAcquire::ItemDesc &item) { + //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]); [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]]; } @@ -926,7 +1065,7 @@ class Progress : return NSOrderedDescending; } - return [lhs compare:rhs options:CompareOptions_]; + return [lhs compare:rhs options:LaxCompareOptions_]; } - (NSDictionary *) record { @@ -1019,35 +1158,6 @@ class Progress : @end /* }}} */ /* Package Class {{{ */ -NSString *Scour(const char *field, const char *begin, const char *end) { - size_t i(0), l(strlen(field)); - - for (;;) { - const char *name = begin + i; - const char *colon = name + l; - const char *value = colon + 1; - - if ( - value < end && - *colon == ':' && - strncasecmp(name, field, l) == 0 - ) { - while (value != end && value[0] == ' ') - ++value; - const char *line = std::find(value, end, '\n'); - while (line != value && line[-1] == ' ') - --line; - - return [NSString stringWithUTF8Bytes:value length:(line - value)]; - } else { - begin = std::find(begin, end, '\n'); - if (begin == end) - return nil; - ++begin; - } - } -} - @interface Package : NSObject { pkgCache::PkgIterator iterator_; _transient Database *database_; @@ -1057,6 +1167,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { Source *source_; bool cached_; + NSString *section_; + NSString *latest_; NSString *installed_; @@ -1080,12 +1192,17 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (pkgCache::PkgIterator) iterator; - (NSString *) section; +- (NSString *) simpleSection; + - (Address *) maintainer; - (size_t) size; - (NSString *) description; - (NSString *) index; +- (NSMutableDictionary *) metadata; - (NSDate *) seen; +- (BOOL) subscribed; +- (BOOL) ignored; - (NSString *) latest; - (NSString *) installed; @@ -1118,6 +1235,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (Source *) source; - (NSString *) role; +- (NSString *) rating; - (BOOL) matches:(NSString *)text; @@ -1128,8 +1246,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (NSComparisonResult) compareByName:(Package *)package; - (NSComparisonResult) compareBySection:(Package *)package; -- (NSComparisonResult) compareBySectionAndName:(Package *)package; -- (NSComparisonResult) compareForChanges:(Package *)package; + +- (uint32_t) compareForChanges; - (void) install; - (void) remove; @@ -1147,6 +1265,9 @@ NSString *Scour(const char *field, const char *begin, const char *end) { if (source_ != nil) [source_ release]; + if (section_ != nil) + [section_ release]; + [latest_ release]; if (installed_ != nil) [installed_ release]; @@ -1177,7 +1298,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { } + (NSArray *) _attributeKeys { - return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil]; + return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil]; } - (NSArray *) attributeKeys { @@ -1195,7 +1316,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { version_ = [database_ policy]->GetCandidateVer(iterator_); NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()]; - latest_ = [StripVersion(latest) retain]; + latest_ = latest == nil ? nil : [StripVersion(latest) retain]; pkgCache::VerIterator current = iterator_.CurrentVer(); NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()]; @@ -1208,7 +1329,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { file_ = pkgCache::VerFileIterator(cache, cache.VerFileP); } - id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain]; + id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain]; if (!file_.end()) { pkgRecords::Parser *parser = &[database_ records]->Lookup(file_); @@ -1216,32 +1337,77 @@ NSString *Scour(const char *field, const char *begin, const char *end) { const char *begin, *end; parser->GetRec(begin, end); - name_ = Scour("name", begin, end); + NSString *website(nil); + NSString *sponsor(nil); + NSString *author(nil); + NSString *tag(nil); + + struct { + const char *name_; + NSString **value_; + } names[] = { + {"name", &name_}, + {"icon", &icon_}, + {"depiction", &depiction_}, + {"homepage", &homepage_}, + {"website", &website}, + {"sponsor", &sponsor}, + {"author", &author}, + {"tag", &tag}, + }; + + while (begin != end) + if (*begin == '\n') { + ++begin; + continue; + } else if (isblank(*begin)) next: { + begin = static_cast(memchr(begin + 1, '\n', end - begin - 1)); + if (begin == NULL) + break; + } else if (const char *colon = static_cast(memchr(begin, ':', end - begin))) { + const char *name(begin); + size_t size(colon - begin); + + begin = static_cast(memchr(begin, '\n', end - begin)); + + { + const char *stop(begin == NULL ? end : begin); + while (stop[-1] == '\r') + --stop; + while (++colon != stop && isblank(*colon)); + + for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) + if (strncasecmp(names[i].name_, name, size) == 0) { + NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]); + *names[i].value_ = value; + break; + } + } + + if (begin == NULL) + break; + ++begin; + } else goto next; + if (name_ != nil) name_ = [name_ retain]; tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain]; - icon_ = Scour("icon", begin, end); if (icon_ != nil) icon_ = [icon_ retain]; - depiction_ = Scour("depiction", begin, end); if (depiction_ != nil) depiction_ = [depiction_ retain]; - homepage_ = Scour("homepage", begin, end); if (homepage_ == nil) - homepage_ = Scour("website", begin, end); + homepage_ = website; if ([homepage_ isEqualToString:depiction_]) homepage_ = nil; if (homepage_ != nil) homepage_ = [homepage_ retain]; - NSString *sponsor = Scour("sponsor", begin, end); if (sponsor != nil) sponsor_ = [[Address addressWithString:sponsor] retain]; - NSString *author = Scour("author", begin, end); if (author != nil) author_ = [[Address addressWithString:author] retain]; - NSString *tags = Scour("tag", begin, end); - if (tags != nil) - tags_ = [[tags componentsSeparatedByString:@", "] retain]; + if (tag != nil) + tags_ = [[tag componentsSeparatedByString:@", "] retain]; } if (tags_ != nil) @@ -1256,11 +1422,13 @@ NSString *Scour(const char *field, const char *begin, const char *end) { NSString *solid(latest == nil ? installed : latest); bool changed(false); - NSMutableDictionary *metadata = [Packages_ objectForKey:id_]; + NSString *key([id_ lowercaseString]); + + NSMutableDictionary *metadata = [Packages_ objectForKey:key]; if (metadata == nil) { - metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys: + metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys: now_, @"FirstSeen", - nil]; + nil] mutableCopy]; if (solid != nil) [metadata setObject:solid forKey:@"LastVersion"]; @@ -1289,7 +1457,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { } if (changed) { - [Packages_ setObject:metadata forKey:id_]; + [Packages_ setObject:metadata forKey:key]; Changed_ = true; } } return self; @@ -1307,6 +1475,9 @@ NSString *Scour(const char *field, const char *begin, const char *end) { } - (NSString *) section { + if (section_ != nil) + return section_; + const char *section = iterator_.Section(); if (section == NULL) return nil; @@ -1320,7 +1491,16 @@ NSString *Scour(const char *field, const char *begin, const char *end) { goto lookup; } - return [name stringByReplacingCharacter:'_' withCharacter:' ']; + section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain]; + return section_; +} + +- (NSString *) simpleSection { + if (NSString *section = [self section]) + return Simplify(section); + else + return nil; + } - (Address *) maintainer { @@ -1359,19 +1539,34 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123"; } +- (NSMutableDictionary *) metadata { + return [Packages_ objectForKey:[id_ lowercaseString]]; +} + - (NSDate *) seen { - NSDictionary *metadata([Packages_ objectForKey:id_]); - bool subscribed; - if (NSNumber *isSubscribed = [metadata objectForKey:@"IsSubscribed"]) - subscribed = [isSubscribed boolValue]; - else - subscribed = false; - if (subscribed) + NSDictionary *metadata([self metadata]); + if ([self subscribed]) if (NSDate *last = [metadata objectForKey:@"LastSeen"]) return last; return [metadata objectForKey:@"FirstSeen"]; } +- (BOOL) subscribed { + NSDictionary *metadata([self metadata]); + if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"]) + return [subscribed boolValue]; + else + return false; +} + +- (BOOL) ignored { + NSDictionary *metadata([self metadata]); + if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"]) + return [ignored boolValue]; + else + return false; +} + - (NSString *) latest { return latest_; } @@ -1389,10 +1584,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { if (current.end()) return essential && [self essential]; - else { - pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_); - return !candidate.end() && candidate != current; - } + else + return !version_.end() && version_ != current; } - (BOOL) essential { @@ -1477,9 +1670,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { } - (UIImage *) icon { - NSString *section = [self section]; - if (section != nil) - section = Simplify(section); + NSString *section = [self simpleSection]; UIImage *icon(nil); if (NSString *icon = icon_) @@ -1538,6 +1729,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { [warnings addObject:@"illegal package identifier"]; else for (size_t i(0); i != length; ++i) if ( + /* XXX: technically this is not allowed */ + (name[i] < 'A' || name[i] > 'Z') && (name[i] < 'a' || name[i] > 'z') && (name[i] < '0' || name[i] > '9') && (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.') @@ -1545,17 +1738,25 @@ NSString *Scour(const char *field, const char *begin, const char *end) { if (strcmp(name, "cydia") != 0) { bool cydia = false; + bool _private = false; bool stash = false; + bool repository = [[self section] isEqualToString:@"Repositories"]; + if (NSArray *files = [self files]) for (NSString *file in files) if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"]) cydia = true; + else if (!_private && [file isEqualToString:@"/private"]) + _private = true; else if (!stash && [file isEqualToString:@"/var/stash"]) stash = true; - if (cydia) + /* XXX: this is not sensitive enough. only some folders are valid. */ + if (cydia && !repository) [warnings addObject:@"files installed into Cydia.app"]; + if (_private) + [warnings addObject:@"files installed with /private/*"]; if (stash) [warnings addObject:@"files installed to /var/stash"]; } @@ -1611,6 +1812,13 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return role_; } +- (NSString *) rating { + if (NSString *rating = [Indices_ objectForKey:@"Rating"]) + return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + else + return nil; +} + - (BOOL) matches:(NSString *)text { if (text == nil) return NO; @@ -1683,7 +1891,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return NSOrderedDescending; } - return [lhs compare:rhs options:CompareOptions_]; + return [lhs compare:rhs options:LaxCompareOptions_]; } - (NSComparisonResult) compareBySection:(Package *)package { @@ -1695,51 +1903,37 @@ NSString *Scour(const char *field, const char *begin, const char *end) { else if (lhs != NULL && rhs == NULL) return NSOrderedDescending; else if (lhs != NULL && rhs != NULL) { - NSComparisonResult result = [lhs compare:rhs options:CompareOptions_]; - if (result != NSOrderedSame) - return result; + NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]); + return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_]; } return NSOrderedSame; } -- (NSComparisonResult) compareBySectionAndName:(Package *)package { - NSString *lhs = [self section]; - NSString *rhs = [package section]; - - if (lhs == NULL && rhs != NULL) - return NSOrderedAscending; - else if (lhs != NULL && rhs == NULL) - return NSOrderedDescending; - else if (lhs != NULL && rhs != NULL) { - NSComparisonResult result = [lhs compare:rhs]; - if (result != NSOrderedSame) - return result; - } +- (uint32_t) compareForChanges { + union { + uint32_t key; - return [self compareByName:package]; -} + struct { + uint32_t timestamp : 30; + uint32_t ignored : 1; + uint32_t upgradable : 1; + } bits; + } value; -- (NSComparisonResult) compareForChanges:(Package *)package { - BOOL lhs = [self upgradableAndEssential:YES]; - BOOL rhs = [package upgradableAndEssential:YES]; + value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0; - if (lhs != rhs) - return lhs ? NSOrderedAscending : NSOrderedDescending; - else if (!lhs) { - switch ([[self seen] compare:[package seen]]) { - case NSOrderedAscending: - return NSOrderedDescending; - case NSOrderedSame: - break; - case NSOrderedDescending: - return NSOrderedAscending; - default: - _assert(false); - } + if ([self upgradableAndEssential:YES]) { + value.bits.timestamp = 0; + value.bits.ignored = [self ignored] ? 0 : 1; + value.bits.upgradable = 1; + } else { + value.bits.timestamp = static_cast([[self seen] timeIntervalSince1970]) >> 2; + value.bits.ignored = 0; + value.bits.upgradable = 0; } - return [self compareByName:package]; + return _not(uint32_t) - value.key; } - (void) install { @@ -1830,7 +2024,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return NSOrderedDescending; } - return [lhs compare:rhs options:CompareOptions_]; + return [lhs compare:rhs options:LaxCompareOptions_]; } - (Section *) initWithName:(NSString *)name { @@ -1936,9 +2130,9 @@ static NSArray *Finishes_; withObject:[NSArray arrayWithObjects:string, id, nil] waitUntilDone:YES ]; - else if (type == "pmstatus") + else if (type == "pmstatus") { [delegate_ setProgressTitle:string]; - else if (type == "pmconffile") + } else if (type == "pmconffile") [delegate_ setConfigurationData:string]; else _assert(false); } else _assert(false); @@ -2134,6 +2328,7 @@ static NSArray *Finishes_; cache_.Close(); + _trace(); if (!cache_.Open(progress_, true)) { std::string error; if (!_error->PopMessage(error)) @@ -2152,6 +2347,7 @@ static NSArray *Finishes_; return; } + _trace(); now_ = [[NSDate date] retain]; @@ -2184,11 +2380,14 @@ static NSArray *Finishes_; } [packages_ removeAllObjects]; + _trace(); + profile_ = 0; for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator) if (Package *package = [Package packageWithIterator:iterator database:self]) [packages_ addObject:package]; - + _trace(); [packages_ sortUsingSelector:@selector(compareByName:)]; + _trace(); } - (void) configure { @@ -2447,6 +2646,9 @@ UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" bu } - (void) deliverMessage { _pooled + setuid(501); + setgid(501); + if (![controller_ deliverMessage]) [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO]; } @@ -2524,9 +2726,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; + NSString *context([sheet context]); - if ([context isEqualToString:@"remove"]) + if ([context isEqualToString:@"remove"]) { switch (button) { case 1: [self cancel]; @@ -2539,10 +2741,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { default: _assert(false); } - else if ([context isEqualToString:@"unable"]) - [self cancel]; - [sheet dismiss]; + [sheet dismiss]; + } else if ([context isEqualToString:@"unable"]) { + [self cancel]; + [sheet dismiss]; + } else + [super alertSheet:sheet buttonClicked:button]; } - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { @@ -2645,11 +2850,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } return self; } -// XXX: replace with -- (NSString *) title { - return issues_ == nil ? @"Confirm Changes" : @"Cannot Comply"; -} - - (NSString *) backButtonTitle { return @"Confirm"; } @@ -2658,15 +2858,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Cancel"; } -- (NSString *) _rightButtonTitle { +- (id) _rightButtonTitle { +#if AlwaysReload || IgnoreInstall + return @"Reload"; +#else return issues_ == nil ? @"Confirm" : nil; +#endif } - (void) _leftButtonClicked { [self cancel]; } +#if !AlwaysReload - (void) _rightButtonClicked { +#if IgnoreInstall + return [super _rightButtonClicked]; +#endif if (essential_ != nil) [essential_ popupAlertAnimated:YES]; else { @@ -2675,6 +2883,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [delegate_ confirm]; } } +#endif @end /* }}} */ @@ -2735,6 +2944,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { id delegate_; BOOL running_; SHA1SumValue springlist_; + SHA1SumValue notifyconf_; SHA1SumValue sandplate_; size_t received_; NSTimeInterval last_; @@ -2887,7 +3097,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; + NSString *context([sheet context]); + if ([context isEqualToString:@"conffile"]) { FILE *input = [database_ input]; @@ -2903,9 +3114,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { default: _assert(false); } - } - [sheet dismiss]; + [sheet dismiss]; + } } - (void) closeButtonPushed { @@ -2953,6 +3164,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { Finish_ = 4; } + if (Finish_ < 4) { + FileFd file(NotifyConfig_, FileFd::ReadOnly); + MMap mmap(file, MMap::ReadOnly); + SHA1Summation sha1; + sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size()); + if (!(notifyconf_ == sha1.Result())) + Finish_ = 4; + } + if (Finish_ < 3) { FileFd file(SpringBoard_, FileFd::ReadOnly); MMap mmap(file, MMap::ReadOnly); @@ -2995,9 +3215,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"]; if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) { [info autorelease]; - [info setObject:path forKey:@"Path"]; - [info setObject:@"System" forKey:@"ApplicationType"]; - [system addInfoDictionary:info]; + if ([info objectForKey:@"CFBundleIdentifier"] != nil) { + [info setObject:path forKey:@"Path"]; + [info setObject:@"System" forKey:@"ApplicationType"]; + [system addInfoDictionary:info]; + } } } } else goto error; @@ -3051,6 +3273,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { sandplate_ = sha1.Result(); } + { + FileFd file(NotifyConfig_, FileFd::ReadOnly); + MMap mmap(file, MMap::ReadOnly); + SHA1Summation sha1; + sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size()); + notifyconf_ = sha1.Result(); + } + { FileFd file(SpringBoard_, FileFd::ReadOnly); MMap mmap(file, MMap::ReadOnly); @@ -3173,7 +3403,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) _setProgressTitle:(NSString *)title { - [status_ setText: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) _setProgressPercent:(NSNumber *)percent { @@ -3264,9 +3501,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self clearPackage]; Source *source = [package source]; - NSString *section = [package section]; - if (section != nil) - section = Simplify(section); + NSString *section = [package simpleSection]; icon_ = [[package icon] retain]; @@ -3448,7 +3683,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { count_ = [[NSString stringWithFormat:@"%d", [section count]] retain]; if (editing_) - [switch_ setValue:isSectionVisible(section_) animated:NO]; + [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO]; } } @@ -3646,18 +3881,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - int count = [buttons_ count]; - _assert(count != 0); - _assert(button <= count + 1); + NSString *context([sheet context]); - if (count != button - 1) - [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]]; + if ([context isEqualToString:@"modify"]) { + int count = [buttons_ count]; + _assert(count != 0); + _assert(button <= count + 1); - [sheet dismiss]; + if (count != button - 1) + [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]]; + + [sheet dismiss]; + } else + [super alertSheet:sheet buttonClicked:button]; } - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { - [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"]; return [super webView:sender didFinishLoadForFrame:frame]; } @@ -3666,7 +3905,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super webView:sender didClearWindowObject:window forFrame:frame]; } -#if 0 +#if !AlwaysReload - (void) _rightButtonClicked { /*[super _rightButtonClicked]; return;*/ @@ -3686,13 +3925,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { buttons:buttons defaultButtonIndex:2 delegate:self - context:@"manage" + context:@"modify" ] autorelease]]; } } #endif -- (NSString *) _rightButtonTitle { +- (id) _rightButtonTitle { int count = [buttons_ count]; return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0]; } @@ -3739,6 +3978,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } +- (bool) _loading { + return false; +} + - (void) reloadData { [self setPackage:[database_ packageWithName:name_]]; [self reloadButtons]; @@ -4241,8 +4484,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; - if ([context isEqualToString:@"source"]) + NSString *context([sheet context]); + + if ([context isEqualToString:@"source"]) { switch (button) { case 1: { NSString *href = [[sheet textField] text]; @@ -4272,7 +4516,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _assert(false); } - [sheet dismiss]; + [sheet dismiss]; + } else if ([context isEqualToString:@"trivial"]) + [sheet dismiss]; + else if ([context isEqualToString:@"urlerror"]) + [sheet dismiss]; } - (id) initWithBook:(RVBook *)book database:(Database *)database { @@ -4311,7 +4559,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [sources_ removeAllObjects]; [sources_ addObjectsFromArray:[database_ sources]]; + _trace(); [sources_ sortUsingSelector:@selector(compareByNameAndType:)]; + _trace(); int count = [sources_ count]; for (offset_ = 0; offset_ != count; ++offset_) { @@ -4341,12 +4591,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { context:@"source" ] autorelease]; + [sheet setNumberOfRows:1]; + [sheet addTextFieldWithValue:@"http://" label:@""]; UITextInputTraits *traits = [[sheet textField] textInputTraits]; - [traits setAutocapitalizationType:0]; - [traits setKeyboardType:3]; - [traits setAutocorrectionType:1]; + [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; + [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; + [traits setKeyboardType:UIKeyboardTypeURL]; + // XXX: UIReturnKeyDone + [traits setReturnKeyType:UIReturnKeyNext]; [sheet popupAlertAnimated:YES]; } @@ -4366,7 +4620,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit"; } @@ -4437,7 +4691,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Packages"; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple"; } @@ -4462,7 +4716,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation HomeView - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - [sheet dismiss]; + NSString *context([sheet context]); + + if ([context isEqualToString:@"about"]) + [sheet dismiss]; + else + [super alertSheet:sheet buttonClicked:button]; } - (void) _leftButtonClicked { @@ -4518,20 +4777,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Settings"; } -- (NSString *) _rightButtonTitle { +#if !AlwaysReload +- (id) _rightButtonTitle { return nil; } +#endif -@end -/* }}} */ +- (bool) _loading { + return false; +} -@interface WebView (Cydia) -- (void) setScriptDebugDelegate:(id)delegate; -- (void) _setFormDelegate:(id)delegate; -- (void) _setUIKitDelegate:(id)delegate; -- (void) setWebMailDelegate:(id)delegate; -- (void) _setLayoutInterval:(float)interval; @end +/* }}} */ /* Indirect Delegate {{{ */ @interface IndirectDelegate : NSProxy { @@ -4569,490 +4826,359 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @end /* }}} */ -/* Browser Implementation {{{ */ -@implementation BrowserView - -- (void) dealloc { - WebView *webview = [webview_ webView]; - [webview setFrameLoadDelegate:nil]; - [webview setResourceLoadDelegate:nil]; - [webview setUIDelegate:nil]; - [webview setScriptDebugDelegate:nil]; - [webview setPolicyDelegate:nil]; - - [webview setDownloadDelegate:nil]; - - [webview _setFormDelegate:nil]; - [webview _setUIKitDelegate:nil]; - [webview setWebMailDelegate:nil]; - [webview setEditingDelegate:nil]; - [webview_ setDelegate:nil]; - [webview_ setGestureDelegate:nil]; +#include <BrowserView.m> - //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - [webview close]; +/* Cydia Book {{{ */ +@interface CYBook : RVBook < + ProgressDelegate +> { + _transient Database *database_; + UINavigationBar *overlay_; + UINavigationBar *underlay_; + UIProgressIndicator *indicator_; + UITextLabel *prompt_; + UIProgressBar *progress_; + UINavigationButton *cancel_; + bool updating_; + size_t received_; + NSTimeInterval last_; +} -#if RecycleWebViews - [webview_ removeFromSuperview]; - [Documents_ addObject:[webview_ autorelease]]; -#else - [webview_ release]; -#endif +- (id) initWithFrame:(CGRect)frame database:(Database *)database; +- (void) update; +- (BOOL) updating; - [indirect_ setDelegate:nil]; - [indirect_ release]; +@end - [scroller_ setDelegate:nil]; +@implementation CYBook - [scroller_ release]; - [urls_ release]; +- (void) dealloc { + [overlay_ release]; [indicator_ release]; - if (title_ != nil) - [title_ release]; + [prompt_ release]; + [progress_ release]; + [cancel_ release]; [super dealloc]; } -- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy { - [self loadRequest:[NSURLRequest - requestWithURL:url - cachePolicy:policy - timeoutInterval:30.0 - ]]; +- (NSString *) getTitleForPage:(RVPage *)page { + return Simplify([super getTitleForPage:page]); } -- (void) loadURL:(NSURL *)url { - [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy]; +- (BOOL) updating { + return updating_; } -- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request { - NSMutableURLRequest *copy = [request mutableCopy]; - - if (Machine_ != NULL) - [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; - if (UniqueID_ != nil) - [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"]; +- (void) update { + [UIView beginAnimations:nil context:NULL]; - if (Role_ != nil) - [copy addValue:Role_ forHTTPHeaderField:@"X-Role"]; + CGRect ovrframe = [overlay_ frame]; + ovrframe.origin.y = 0; + [overlay_ setFrame:ovrframe]; - return copy; -} + CGRect barframe = [navbar_ frame]; + barframe.origin.y += ovrframe.size.height; + [navbar_ setFrame:barframe]; -- (void) loadRequest:(NSURLRequest *)request { - pushed_ = true; - [webview_ loadRequest:request]; -} + CGRect trnframe = [transition_ frame]; + trnframe.origin.y += ovrframe.size.height; + trnframe.size.height -= ovrframe.size.height; + [transition_ setFrame:trnframe]; -- (void) reloadURL { - if ([urls_ count] == 0) - return; - NSURL *url = [[[urls_ lastObject] retain] autorelease]; - [urls_ removeLastObject]; - [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData]; -} + [UIView endAnimations]; -- (WebView *) webView { - return [webview_ webView]; -} + [indicator_ startAnimation]; + [prompt_ setText:@"Updating Database"]; + [progress_ setProgress:0]; -- (void) view:(UIView *)sender didSetFrame:(CGRect)frame { - [scroller_ setContentSize:frame.size]; -} + received_ = 0; + last_ = [NSDate timeIntervalSinceReferenceDate]; + updating_ = true; + [overlay_ addSubview:cancel_]; -- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old { - [self view:sender didSetFrame:frame]; + [NSThread + detachNewThreadSelector:@selector(_update) + toTarget:self + withObject:nil + ]; } -- (void) pushPage:(RVPage *)page { - [self setBackButtonTitle:title_]; - [page setDelegate:delegate_]; - [book_ pushPage:page]; -} +- (void) _update_ { + updating_ = false; -- (BOOL) getSpecial:(NSURL *)url { - NSString *href([url absoluteString]); - NSString *scheme([[url scheme] lowercaseString]); + [indicator_ stopAnimation]; - RVPage *page = nil; + [UIView beginAnimations:nil context:NULL]; - if ([href hasPrefix:@"apptapp://package/"]) - page = [delegate_ pageForPackage:[href substringFromIndex:18]]; - else if ([scheme isEqualToString:@"cydia"]) { - page = [delegate_ pageForURL:url hasTag:NULL]; - if (page == nil) - return false; - } else if (![scheme isEqualToString:@"apptapp"]) - return false; + CGRect ovrframe = [overlay_ frame]; + ovrframe.origin.y = -ovrframe.size.height; + [overlay_ setFrame:ovrframe]; - if (page != nil) - [self pushPage:page]; - return true; -} + CGRect barframe = [navbar_ frame]; + barframe.origin.y -= ovrframe.size.height; + [navbar_ setFrame:barframe]; -- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { - [window setValue:delegate_ forKey:@"cydia"]; -} + CGRect trnframe = [transition_ frame]; + trnframe.origin.y -= ovrframe.size.height; + trnframe.size.height += ovrframe.size.height; + [transition_ setFrame:trnframe]; -- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener { - if (NSURL *url = [request URL]) { - if (![self getSpecial:url]) { - NSString *scheme([[url scheme] lowercaseString]); - if ([scheme isEqualToString:@"mailto"]) - [delegate_ openMailToURL:url]; - else goto use; - } + [UIView commitAnimations]; - [listener ignore]; - } else use: - [listener use]; + [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0]; } -- (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener { - NSURL *url([request URL]); - - if (url == nil) use: { - [listener use]; - return; - } - - const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability))); +- (id) initWithFrame:(CGRect)frame database:(Database *)database { + if ((self = [super initWithFrame:frame]) != nil) { + database_ = database; - if ( - [capability containsObject:@"com.apple.Maps"] && [url mapsURL] || - [capability containsObject:@"com.apple.youtube"] && [url youTubeURL] - ) { - open: - [UIApp openURL:url]; - ignore: - [listener ignore]; - return; - } + CGRect ovrrect = [navbar_ bounds]; + ovrrect.size.height = [UINavigationBar defaultSize].height; + ovrrect.origin.y = -ovrrect.size.height; - int store(_not(int)); - if (NSURL *itms = [url itmsURL:&store]) { - if ( - store == 1 && [capability containsObject:@"com.apple.MobileStore"] || - store == 2 && [capability containsObject:@"com.apple.AppStore"] - ) { - url = itms; - goto open; - } - } + overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; + [self addSubview:overlay_]; - NSString *scheme([[url scheme] lowercaseString]); + ovrrect.origin.y = frame.size.height; + underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; + [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]]; + [self addSubview:underlay_]; - if ([scheme isEqualToString:@"tel"]) { - // XXX: intelligence - goto open; - } + [overlay_ setBarStyle:1]; + [underlay_ setBarStyle:1]; - if ([scheme isEqualToString:@"mailto"]) { - [delegate_ openMailToURL:url]; - goto ignore; - } + int barstyle = [overlay_ _barStyle:NO]; + bool ugly = barstyle == 0; - if ([self getSpecial:url]) - goto ignore; - else if ([WebView _canHandleRequest:request]) - goto use; - else if ([url isSpringboardHandledURL]) - goto open; - else - goto use; -} + UIProgressIndicatorStyle style = ugly ? + UIProgressIndicatorStyleMediumBrown : + UIProgressIndicatorStyleMediumWhite; -- (void) webView:(WebView *)sender setStatusText:(NSString *)text { - //lprintf("Status:%s\n", [text UTF8String]); -} + CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style]; + unsigned indoffset = (ovrrect.size.height - indsize.height) / 2; + CGRect indrect = {{indoffset, indoffset}, indsize}; -- (void) _pushPage { - if (pushed_) - return; - pushed_ = true; - [book_ pushPage:self]; -} + indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect]; + [indicator_ setStyle:style]; + [overlay_ addSubview:indicator_]; -- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource { - NSURL *url = [request URL]; - if ([self getSpecial:url]) - return nil; - [self _pushPage]; - return [self _addHeadersToRequest:request]; -} + CGSize prmsize = {215, indsize.height + 4}; -- (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed { - [self setBackButtonTitle:title_]; + CGRect prmrect = {{ + indoffset * 2 + indsize.width, +#ifdef __OBJC2__ + -1 + +#endif + unsigned(ovrrect.size.height - prmsize.height) / 2 + }, prmsize}; - BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease]; - [browser setDelegate:delegate_]; + UIFont *font = [UIFont systemFontOfSize:15]; - if (pushed) { - [browser loadRequest:[self _addHeadersToRequest:request]]; - [book_ pushPage:browser]; - } + prompt_ = [[UITextLabel alloc] initWithFrame:prmrect]; - return [browser webView]; -} + [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]]; + [prompt_ setBackgroundColor:[UIColor clearColor]]; + [prompt_ setFont:font]; -- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request { - return [self _createWebViewWithRequest:request pushed:(request != nil)]; -} + [overlay_ addSubview:prompt_]; -- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features { - return [self _createWebViewWithRequest:request pushed:YES]; -} + CGSize prgsize = {75, 100}; -- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame { - if ([frame parentFrame] != nil) - return; + CGRect prgrect = {{ + ovrrect.size.width - prgsize.width - 10, + (ovrrect.size.height - prgsize.height) / 2 + } , prgsize}; - title_ = [title retain]; - [book_ reloadTitleForPage:self]; -} + progress_ = [[UIProgressBar alloc] initWithFrame:prgrect]; + [progress_ setStyle:0]; + [overlay_ addSubview:progress_]; -- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame { - if ([frame parentFrame] != nil) - return; + cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted]; + [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside]; - reloading_ = false; - loading_ = true; - [indicator_ startAnimation]; - [self reloadButtons]; + CGRect frame = [cancel_ frame]; + frame.size.width = 65; + frame.origin.x = ovrrect.size.width - frame.size.width - 5; + frame.origin.y = (ovrrect.size.height - frame.size.height) / 2; + [cancel_ setFrame:frame]; - if (title_ != nil) { - [title_ release]; - title_ = nil; - } + [cancel_ setBarStyle:barstyle]; + } return self; +} - [book_ reloadTitleForPage:self]; +- (void) _onCancel { + updating_ = false; + [cancel_ removeFromSuperview]; +} - WebView *webview = [webview_ webView]; - NSString *href = [webview mainFrameURL]; - [urls_ addObject:[NSURL URLWithString:href]]; +- (void) _update { _pooled + Status status; + status.setDelegate(self); - [scroller_ scrollPointVisibleAtTopLeft:CGPointZero]; + [database_ updateWithStatus:status]; - CGRect webrect = [scroller_ bounds]; - webrect.size.height = 0; - [webview_ setFrame:webrect]; + [self + performSelectorOnMainThread:@selector(_update_) + withObject:nil + waitUntilDone:NO + ]; } -- (void) _finishLoading { - if (!reloading_) { - loading_ = false; - [indicator_ stopAnimation]; - [self reloadButtons]; - } +- (void) setProgressError:(NSString *)error forPackage:(NSString *)id { + [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]]; } -- (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame { - return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame]; +- (void) setProgressTitle:(NSString *)title { + [self + performSelectorOnMainThread:@selector(_setProgressTitle:) + withObject:title + waitUntilDone:YES + ]; } -- (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame { - return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame]; +- (void) setProgressPercent:(float)percent { + [self + performSelectorOnMainThread:@selector(_setProgressPercent:) + withObject:[NSNumber numberWithFloat:percent] + waitUntilDone:YES + ]; } -- (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame { - return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame]; +- (void) startProgress { } -- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame { - return [webview_ webView:sender didCommitLoadForFrame:frame]; +- (void) addProgressOutput:(NSString *)output { + [self + performSelectorOnMainThread:@selector(_addProgressOutput:) + withObject:output + waitUntilDone:YES + ]; } -- (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame { - return [webview_ webView:sender didReceiveDocTypeForFrame:frame]; +- (bool) isCancelling:(size_t)received { + NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; + if (received_ != received) { + received_ = received; + last_ = now; + } else if (now - last_ > 15) + return true; + return !updating_; } -- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { - if ([frame parentFrame] == nil) - [self _finishLoading]; - return [webview_ webView:sender didFinishLoadForFrame:frame]; +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + [sheet dismiss]; } -- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { - if ([frame parentFrame] != nil) - return; - [self _finishLoading]; - - [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", - [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString], - [[error localizedDescription] stringByAddingPercentEscapes] - ]]]; +- (void) _setProgressTitle:(NSString *)title { + [prompt_ setText:title]; } -- (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary { -#if ForSaurik - lprintf("Console:%s\n", [[dictionary description] UTF8String]); -#endif +- (void) _setProgressPercent:(NSNumber *)percent { + [progress_ setProgress:[percent floatValue]]; } -- (id) initWithBook:(RVBook *)book { - if ((self = [super initWithBook:book]) != nil) { - loading_ = false; - - struct CGRect bounds = [self bounds]; - - UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease]; - [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]]; - [self addSubview:pinstripe]; - - scroller_ = [[UIScroller alloc] initWithFrame:bounds]; - [self addSubview:scroller_]; - - [scroller_ setScrollingEnabled:YES]; - [scroller_ setAdjustForContentSizeChange:YES]; - [scroller_ setClipsSubviews:YES]; - [scroller_ setAllowsRubberBanding:YES]; - [scroller_ setScrollDecelerationFactor:0.99]; - [scroller_ setDelegate:self]; - - CGRect webrect = [scroller_ bounds]; - webrect.size.height = 0; - - WebView *webview; - -#if RecycleWebViews - webview_ = [Documents_ lastObject]; - if (webview_ != nil) { - webview_ = [webview_ retain]; - webview = [webview_ webView]; - [Documents_ removeLastObject]; - [webview_ setFrame:webrect]; - } else { -#else - if (true) { -#endif - webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect]; - webview = [webview_ webView]; - - [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)]; - - [webview_ setAllowsMessaging:YES]; - - [webview_ setTilingEnabled:YES]; - [webview_ setDrawsGrid:NO]; - [webview_ setLogsTilingChanges:NO]; - [webview_ setTileMinificationFilter:kCAFilterNearest]; - [webview_ setDetectsPhoneNumbers:NO]; - [webview_ setAutoresizes:YES]; - - [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10]; - [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2]; - [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8]; - - [webview_ _setDocumentType:0x4]; - - [webview_ setZoomsFocusedFormControl:YES]; - [webview_ setContentsPosition:7]; - [webview_ setEnabledGestures:0xa]; - [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4]; - [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7]; - - [webview_ setSmoothsFonts:YES]; - - [webview _setUsesLoaderCache:YES]; - [webview setGroupName:@"Cydia"]; - //[webview _setLayoutInterval:0.5]; - } - - [webview_ setDelegate:self]; - [webview_ setGestureDelegate:self]; - [scroller_ addSubview:webview_]; - - //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite]; - indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)]; - [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite]; - - Package *package([[Database sharedInstance] packageWithName:@"cydia"]); - NSString *application = package == nil ? @"Cydia" : [NSString - stringWithFormat:@"Cydia/%@", - [package installed] - ]; [webview setApplicationNameForUserAgent:application]; +- (void) _addProgressOutput:(NSString *)output { +} - indirect_ = [[IndirectDelegate alloc] initWithDelegate:self]; +@end +/* }}} */ +/* Cydia:// Protocol {{{ */ +@interface CydiaURLProtocol : NSURLProtocol { +} - [webview setFrameLoadDelegate:self]; - [webview setResourceLoadDelegate:indirect_]; - [webview setUIDelegate:self]; - [webview setScriptDebugDelegate:self]; - [webview setPolicyDelegate:self]; +@end - urls_ = [[NSMutableArray alloc] initWithCapacity:16]; +@implementation CydiaURLProtocol - [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - } return self; ++ (BOOL) canInitWithRequest:(NSURLRequest *)request { + NSURL *url([request URL]); + if (url == nil) + return NO; + NSString *scheme([[url scheme] lowercaseString]); + if (scheme == nil || ![scheme isEqualToString:@"cydia"]) + return NO; + return YES; } -- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event { - [webview_ redrawScaledDocument]; ++ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request { + return request; } -- (void) _rightButtonClicked { - reloading_ = true; - [self reloadURL]; -} +- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request { + id<NSURLProtocolClient> client([self client]); + NSData *data(UIImagePNGRepresentation(icon)); -- (NSString *) _rightButtonTitle { - return @"Reload"; + NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]); + [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; + [client URLProtocol:self didLoadData:data]; + [client URLProtocolDidFinishLoading:self]; } -- (NSString *) rightButtonTitle { - return loading_ ? @"" : [self _rightButtonTitle]; -} +- (void) startLoading { + id<NSURLProtocolClient> client([self client]); + NSURLRequest *request([self request]); -- (NSString *) title { - return title_ == nil ? @"Loading" : title_; -} + NSURL *url([request URL]); + NSString *href([url absoluteString]); -- (NSString *) backButtonTitle { - return @"Browser"; -} + NSString *path([href substringFromIndex:8]); + NSRange slash([path rangeOfString:@"/"]); -- (void) setPageActive:(BOOL)active { - if (!active) - [indicator_ removeFromSuperview]; - else - [[book_ navigationBar] addSubview:indicator_]; -} + NSString *command; + if (slash.location == NSNotFound) { + command = path; + path = nil; + } else { + command = [path substringToIndex:slash.location]; + path = [path substringFromIndex:(slash.location + 1)]; + } -- (void) resetViewAnimated:(BOOL)animated { -} + Database *database([Database sharedInstance]); -- (void) setPushed:(bool)pushed { - pushed_ = pushed; + if ([command isEqualToString:@"package-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + Package *package([database packageWithName:path]); + if (package == nil) + goto fail; + UIImage *icon([package icon]); + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"source-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *source(Simplify(path)); + UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]); + if (icon == nil) + icon = [UIImage applicationImageNamed:@"unknown.png"]; + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"uikit-image"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + UIImage *icon(_UIImageWithName(path)); + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"section-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *section(Simplify(path)); + UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]); + if (icon == nil) + icon = [UIImage applicationImageNamed:@"unknown.png"]; + [self _returnPNGWithImage:icon forRequest:request]; + } else fail: { + [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]]; + } } -@end -/* }}} */ - -@interface CYBook : RVBook < - ProgressDelegate -> { - _transient Database *database_; - UINavigationBar *overlay_; - UINavigationBar *underlay_; - UIProgressIndicator *indicator_; - UITextLabel *prompt_; - UIProgressBar *progress_; - UINavigationButton *cancel_; - bool updating_; - size_t received_; - NSTimeInterval last_; +- (void) stopLoading { } -- (id) initWithFrame:(CGRect)frame database:(Database *)database; -- (void) update; -- (BOOL) updating; - @end +/* }}} */ /* Install View {{{ */ @interface InstallView : RVPage { @@ -5190,6 +5316,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]]; NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32]; + _trace(); for (size_t i(0); i != [packages count]; ++i) { Package *package([packages objectAtIndex:i]); NSString *name([package section]); @@ -5205,11 +5332,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if ([package valid] && [package installed] == nil && [package visible]) [filtered addObject:package]; } + _trace(); [sections_ addObjectsFromArray:[sections allValues]]; [sections_ sortUsingSelector:@selector(compareByName:)]; + _trace(); [filtered sortUsingSelector:@selector(compareBySection:)]; + _trace(); Section *section = nil; for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) { @@ -5225,8 +5355,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [section addToCount]; } + _trace(); [list_ reloadData]; + _trace(); } - (void) resetView { @@ -5241,10 +5373,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) _rightButtonClicked { if ((editing_ = !editing_)) [list_ reloadData]; - else { + else [delegate_ updateData]; - } - [book_ reloadTitleForPage:self]; [book_ reloadButtonsForPage:self]; } @@ -5257,7 +5387,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Sections"; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit"; } @@ -5387,6 +5517,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [packages_ removeAllObjects]; [sections_ removeAllObjects]; + _trace(); for (size_t i(0); i != [packages count]; ++i) { Package *package([packages objectAtIndex:i]); @@ -5397,43 +5528,46 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [packages_ addObject:package]; } - [packages_ sortUsingSelector:@selector(compareForChanges:)]; + _trace(); + [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil]; + _trace(); Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease]; + Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease]; Section *section = nil; + NSDate *last = nil; upgrades_ = 0; bool unseens = false; CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle); + _trace(); for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) { Package *package = [packages_ objectAtIndex:offset]; - if ([package upgradableAndEssential:YES]) { - ++upgrades_; - [upgradable addToCount]; - } else { + if (![package upgradableAndEssential:YES]) { unseens = true; NSDate *seen = [package seen]; - NSString *name; + if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) { + last = seen; - if (seen == nil) - name = [@"n/a ?" retain]; - else { - name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen); - } - - if (section == nil || ![[section name] isEqualToString:name]) { + NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen)); section = [[[Section alloc] initWithName:name row:offset] autorelease]; [sections_ addObject:section]; + [name release]; } - [name release]; [section addToCount]; + } else if ([package ignored]) + [ignored addToCount]; + else { + ++upgrades_; + [upgradable addToCount]; } } + _trace(); CFRelease(formatter); @@ -5444,6 +5578,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [sections_ removeLastObject]; } + if ([ignored count] != 0) + [sections_ insertObject:ignored atIndex:0]; if (upgrades_ != 0) [sections_ insertObject:upgradable atIndex:0]; @@ -5459,7 +5595,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return [(CYBook *)book_ updating] ? nil : @"Refresh"; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_]; } @@ -5596,10 +5732,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if ((self = [super initWithBook:book]) != nil) { CGRect pageBounds = [book_ pageBounds]; - /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease]; - [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]]; - [self addSubview:pinstripe];*/ - transition_ = [[UITransitionView alloc] initWithFrame:pageBounds]; [self addSubview:transition_]; @@ -5652,10 +5784,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [field_ setPaddingTop:5]; - UITextInputTraits *traits = [field_ textInputTraits]; - [traits setAutocapitalizationType:0]; - [traits setAutocorrectionType:1]; - [traits setReturnKeyType:6]; + UITextInputTraits *traits([field_ textInputTraits]); + [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; + [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; + [traits setReturnKeyType:UIReturnKeySearch]; CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}}; @@ -5717,319 +5849,212 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (NSString *) title { return nil; -} - -- (NSString *) backButtonTitle { - return @"Search"; -} - -- (void) setDelegate:(id)delegate { - [table_ setDelegate:delegate]; - [super setDelegate:delegate]; -} - -@end -/* }}} */ - -@implementation CYBook - -- (void) dealloc { - [overlay_ release]; - [indicator_ release]; - [prompt_ release]; - [progress_ release]; - [cancel_ release]; - [super dealloc]; -} - -- (NSString *) getTitleForPage:(RVPage *)page { - return Simplify([super getTitleForPage:page]); -} - -- (BOOL) updating { - return updating_; -} - -- (void) update { - [UIView beginAnimations:nil context:NULL]; - - CGRect ovrframe = [overlay_ frame]; - ovrframe.origin.y = 0; - [overlay_ setFrame:ovrframe]; - - CGRect barframe = [navbar_ frame]; - barframe.origin.y += ovrframe.size.height; - [navbar_ setFrame:barframe]; - - CGRect trnframe = [transition_ frame]; - trnframe.origin.y += ovrframe.size.height; - trnframe.size.height -= ovrframe.size.height; - [transition_ setFrame:trnframe]; - - [UIView endAnimations]; - - [indicator_ startAnimation]; - [prompt_ setText:@"Updating Database"]; - [progress_ setProgress:0]; - - received_ = 0; - last_ = [NSDate timeIntervalSinceReferenceDate]; - updating_ = true; - [overlay_ addSubview:cancel_]; - - [NSThread - detachNewThreadSelector:@selector(_update) - toTarget:self - withObject:nil - ]; -} - -- (void) _update_ { - updating_ = false; - - [indicator_ stopAnimation]; - - [UIView beginAnimations:nil context:NULL]; - - CGRect ovrframe = [overlay_ frame]; - ovrframe.origin.y = -ovrframe.size.height; - [overlay_ setFrame:ovrframe]; - - CGRect barframe = [navbar_ frame]; - barframe.origin.y -= ovrframe.size.height; - [navbar_ setFrame:barframe]; - - CGRect trnframe = [transition_ frame]; - trnframe.origin.y -= ovrframe.size.height; - trnframe.size.height += ovrframe.size.height; - [transition_ setFrame:trnframe]; - - [UIView commitAnimations]; - - [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0]; -} - -- (id) initWithFrame:(CGRect)frame database:(Database *)database { - if ((self = [super initWithFrame:frame]) != nil) { - database_ = database; - - CGRect ovrrect = [navbar_ bounds]; - ovrrect.size.height = [UINavigationBar defaultSize].height; - ovrrect.origin.y = -ovrrect.size.height; - - overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; - [self addSubview:overlay_]; - - ovrrect.origin.y = frame.size.height; - underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; - [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]]; - [self addSubview:underlay_]; - - [overlay_ setBarStyle:1]; - [underlay_ setBarStyle:1]; - - int barstyle = [overlay_ _barStyle:NO]; - bool ugly = barstyle == 0; - - UIProgressIndicatorStyle style = ugly ? - UIProgressIndicatorStyleMediumBrown : - UIProgressIndicatorStyleMediumWhite; +} - CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style]; - unsigned indoffset = (ovrrect.size.height - indsize.height) / 2; - CGRect indrect = {{indoffset, indoffset}, indsize}; +- (NSString *) backButtonTitle { + return @"Search"; +} - indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect]; - [indicator_ setStyle:style]; - [overlay_ addSubview:indicator_]; +- (void) setDelegate:(id)delegate { + [table_ setDelegate:delegate]; + [super setDelegate:delegate]; +} - CGSize prmsize = {215, indsize.height + 4}; +@end +/* }}} */ - CGRect prmrect = {{ - indoffset * 2 + indsize.width, -#ifdef __OBJC2__ - -1 + -#endif - unsigned(ovrrect.size.height - prmsize.height) / 2 - }, prmsize}; +@interface SettingsView : RVPage { + _transient Database *database_; + NSString *name_; + Package *package_; + UIPreferencesTable *table_; + _UISwitchSlider *subscribedSwitch_; + _UISwitchSlider *ignoredSwitch_; + UIPreferencesControlTableCell *subscribedCell_; + UIPreferencesControlTableCell *ignoredCell_; +} - UIFont *font = [UIFont systemFontOfSize:15]; +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package; - prompt_ = [[UITextLabel alloc] initWithFrame:prmrect]; +@end - [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]]; - [prompt_ setBackgroundColor:[UIColor clearColor]]; - [prompt_ setFont:font]; +@implementation SettingsView - [overlay_ addSubview:prompt_]; +- (void) dealloc { + [table_ setDataSource:nil]; - CGSize prgsize = {75, 100}; + [name_ release]; + if (package_ != nil) + [package_ release]; + [table_ release]; + [subscribedSwitch_ release]; + [ignoredSwitch_ release]; + [subscribedCell_ release]; + [ignoredCell_ release]; + [super dealloc]; +} - CGRect prgrect = {{ - ovrrect.size.width - prgsize.width - 10, - (ovrrect.size.height - prgsize.height) / 2 - } , prgsize}; +- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table { + if (package_ == nil) + return 0; - progress_ = [[UIProgressBar alloc] initWithFrame:prgrect]; - [progress_ setStyle:0]; - [overlay_ addSubview:progress_]; + return 2; +} - cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted]; - [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside]; +- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group { + if (package_ == nil) + return nil; - CGRect frame = [cancel_ frame]; - frame.size.width = 65; - frame.origin.x = ovrrect.size.width - frame.size.width - 5; - frame.origin.y = (ovrrect.size.height - frame.size.height) / 2; - [cancel_ setFrame:frame]; + switch (group) { + case 0: return nil; + case 1: return nil; - [cancel_ setBarStyle:barstyle]; - } return self; -} + default: _assert(false); + } -- (void) _onCancel { - updating_ = false; - [cancel_ removeFromSuperview]; + return nil; } -- (void) _update { _pooled - Status status; - status.setDelegate(self); +- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group { + if (package_ == nil) + return NO; - [database_ updateWithStatus:status]; + switch (group) { + case 0: return NO; + case 1: return YES; - [self - performSelectorOnMainThread:@selector(_update_) - withObject:nil - waitUntilDone:NO - ]; -} + default: _assert(false); + } -- (void) setProgressError:(NSString *)error forPackage:(NSString *)id { - [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]]; + return NO; } -- (void) setProgressTitle:(NSString *)title { - [self - performSelectorOnMainThread:@selector(_setProgressTitle:) - withObject:title - waitUntilDone:YES - ]; -} +- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group { + if (package_ == nil) + return 0; -- (void) setProgressPercent:(float)percent { - [self - performSelectorOnMainThread:@selector(_setProgressPercent:) - withObject:[NSNumber numberWithFloat:percent] - waitUntilDone:YES - ]; -} + switch (group) { + case 0: return 1; + case 1: return 1; -- (void) startProgress { -} + default: _assert(false); + } -- (void) addProgressOutput:(NSString *)output { - [self - performSelectorOnMainThread:@selector(_addProgressOutput:) - withObject:output - waitUntilDone:YES - ]; + return 0; } -- (bool) isCancelling:(size_t)received { - NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; - if (received_ != received) { - received_ = received; - last_ = now; - } else if (now - last_ > 15) - return true; - return !updating_; -} +- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key { + if (package_ == nil) + return; -- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - [sheet dismiss]; -} + _UISwitchSlider *slider([cell control]); + BOOL value([slider value] != 0); + NSMutableDictionary *metadata([package_ metadata]); -- (void) _setProgressTitle:(NSString *)title { - [prompt_ setText:title]; -} + BOOL before; + if (NSNumber *number = [metadata objectForKey:key]) + before = [number boolValue]; + else + before = NO; -- (void) _setProgressPercent:(NSNumber *)percent { - [progress_ setProgress:[percent floatValue]]; + if (value != before) { + [metadata setObject:[NSNumber numberWithBool:value] forKey:key]; + Changed_ = true; + [delegate_ updateData]; + } } -- (void) _addProgressOutput:(NSString *)output { +- (void) onSubscribed:(UIPreferencesControlTableCell *)cell { + [self onSomething:cell withKey:@"IsSubscribed"]; } -@end - -@interface CydiaURLProtocol : NSURLProtocol { +- (void) onIgnored:(UIPreferencesControlTableCell *)cell { + [self onSomething:cell withKey:@"IsIgnored"]; } -@end +- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group { + if (package_ == nil) + return nil; -@implementation CydiaURLProtocol + switch (group) { + case 0: switch (row) { + case 0: + return subscribedCell_; + case 1: + return ignoredCell_; + default: _assert(false); + } break; + + case 1: switch (row) { + case 0: { + UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]); + [cell setShowSelection:NO]; + [cell setTitle:@"Changes only shows upgrades to installed packages so as to minimize spam from packagers. Activate this to see upgrades to this package even when it is not installed."]; + return cell; + } -+ (BOOL) canInitWithRequest:(NSURLRequest *)request { - NSURL *url([request URL]); - if (url == nil) - return NO; - NSString *scheme([[url scheme] lowercaseString]); - if (scheme == nil || ![scheme isEqualToString:@"cydia"]) - return NO; - return YES; -} + default: _assert(false); + } break; -+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request { - return request; + default: _assert(false); + } + + return nil; } -- (void) startLoading { - id<NSURLProtocolClient> client([self client]); - NSURLRequest *request([self request]); +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package { + if ((self = [super initWithBook:book])) { + database_ = database; + name_ = [package retain]; - NSURL *url([request URL]); - NSString *href([url absoluteString]); + table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]]; + [self addSubview:table_]; - NSString *path([href substringFromIndex:8]); - NSRange slash([path rangeOfString:@"/"]); + subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)]; + [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside]; - NSString *command; - if (slash.location == NSNotFound) { - command = path; - path = nil; - } else { - command = [path substringToIndex:slash.location]; - path = [path substringFromIndex:(slash.location + 1)]; - } + ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)]; + [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside]; - Database *database([Database sharedInstance]); + subscribedCell_ = [[UIPreferencesControlTableCell alloc] init]; + [subscribedCell_ setShowSelection:NO]; + [subscribedCell_ setTitle:@"Show All Changes"]; + [subscribedCell_ setControl:subscribedSwitch_]; - if ([command isEqualToString:@"package-icon"]) { - if (path == nil) - goto fail; - Package *package([database packageWithName:path]); - if (package == nil) - goto fail; + ignoredCell_ = [[UIPreferencesControlTableCell alloc] init]; + [ignoredCell_ setShowSelection:NO]; + [ignoredCell_ setTitle:@"Ignore Upgrades"]; + [ignoredCell_ setControl:ignoredSwitch_]; - NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]); + [table_ setDataSource:self]; + [self reloadData]; + } return self; +} - UIImage *icon([package icon]); - NSData *data(UIImagePNGRepresentation(icon)); +- (void) resetViewAnimated:(BOOL)animated { + [table_ resetViewAnimated:animated]; +} - [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; - [client URLProtocol:self didLoadData:data]; - [client URLProtocolDidFinishLoading:self]; - } else fail: { - [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]]; +- (void) reloadData { + if (package_ != nil) + [package_ autorelease]; + package_ = [database_ packageWithName:name_]; + if (package_ != nil) { + [package_ retain]; + [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO]; + [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO]; } + + [table_ reloadData]; } -- (void) stopLoading { +- (NSString *) title { + return @"Settings"; } @end +/* Signature View {{{ */ @interface SignatureView : BrowserView { _transient Database *database_; NSString *package_; @@ -6059,11 +6084,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } return self; } +- (void) resetViewAnimated:(BOOL)animated { +} + - (void) reloadData { [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]]; } @end +/* }}} */ @interface Cydia : UIApplication < ConfirmationViewDelegate, @@ -6125,7 +6154,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")] buttons:[NSArray arrayWithObjects: @"Upgrade Essential", - @"Upgrade Everything", + @"Complete Upgrade", @"Ignore (Temporary)", nil] defaultButtonIndex:0 @@ -6195,7 +6224,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) _saveConfig { if (Changed_) { + _trace(); _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES); + _trace(); Changed_ = false; } } @@ -6204,11 +6235,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self _saveConfig]; /* XXX: this is just stupid */ - if (tag_ != 2) + if (tag_ != 2 && install_ != nil) [install_ reloadData]; - if (tag_ != 3) + if (tag_ != 3 && changes_ != nil) [changes_ reloadData]; - if (tag_ != 5) + if (tag_ != 5 && search_ != nil) [search_ reloadData]; [book_ reloadData]; @@ -6520,9 +6551,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { CGSize keysize = [UIKeyboard defaultSize]; CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize}; keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect]; - [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)]; + //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)]; [overlay_ addSubview:keyboard_]; + if (!bootstrap_) + [underlay_ addSubview:overlay_]; + + [self reloadData]; + install_ = [[InstallView alloc] initWithBook:book_ database:database_]; changes_ = [[ChangesView alloc] initWithBook:book_ database:database_]; search_ = [[SearchView alloc] initWithBook:book_ database:database_]; @@ -6532,11 +6568,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { withClass:[ManageView class] ] retain]; - if (!bootstrap_) - [underlay_ addSubview:overlay_]; - - [self reloadData]; - if (bootstrap_) [self bootstrap]; else @@ -6544,8 +6575,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; - if ([context isEqualToString:@"fixhalf"]) + NSString *context([sheet context]); + + if ([context isEqualToString:@"missing"]) + [sheet dismiss]; + else if ([context isEqualToString:@"fixhalf"]) { switch (button) { case 1: @synchronized (self) { @@ -6573,7 +6607,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { default: _assert(false); } - else if ([context isEqualToString:@"role"]) { + + [sheet dismiss]; + } else if ([context isEqualToString:@"role"]) { switch (button) { case 1: Role_ = @"User"; break; case 2: Role_ = @"Hacker"; break; @@ -6598,7 +6634,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self updateData]; else [self finish]; - } else if ([context isEqualToString:@"upgrade"]) + + [sheet dismiss]; + } else if ([context isEqualToString:@"upgrade"]) { switch (button) { case 1: @synchronized (self) { @@ -6624,7 +6662,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _assert(false); } - [sheet dismiss]; + [sheet dismiss]; + } } - (void) reorganize { _pooled @@ -6655,7 +6694,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) openMailToURL:(NSURL *)url { +// XXX: this makes me sad +#if 0 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease]; +#else + [UIApp openURL:url];// asPanel:YES]; +#endif +} + +- (void) clearFirstResponder { + if (id responder = [window_ firstResponder]) + [responder resignFirstResponder]; } - (RVPage *) pageForPackage:(NSString *)name { @@ -6682,27 +6731,39 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag { - NSString *href = [url absoluteString]; - if (tag != NULL) tag = 0; - if ([href isEqualToString:@"cydia://add-source"]) + NSString *scheme([[url scheme] lowercaseString]); + if (![scheme isEqualToString:@"cydia"]) + return nil; + NSString *path([url absoluteString]); + if ([path length] < 8) + return nil; + path = [path substringFromIndex:8]; + if (![path hasPrefix:@"/"]) + path = [@"/" stringByAppendingString:path]; + + if ([path isEqualToString:@"/add-source"]) return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease]; - else if ([href isEqualToString:@"cydia://sources"]) + else if ([path isEqualToString:@"/storage"]) + return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]]; + else if ([path isEqualToString:@"/sources"]) return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease]; - else if ([href isEqualToString:@"cydia://packages"]) + else if ([path isEqualToString:@"/packages"]) return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease]; - else if ([href hasPrefix:@"cydia://url/"]) - return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]]; - else if ([href hasPrefix:@"cydia://launch/"]) - [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO]; - else if ([href hasPrefix:@"cydia://package-signature/"]) - return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease]; - else if ([href hasPrefix:@"cydia://package/"]) - return [self pageForPackage:[href substringFromIndex:16]]; - else if ([href hasPrefix:@"cydia://files/"]) { - NSString *name = [href substringFromIndex:14]; + else if ([path hasPrefix:@"/url/"]) + return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]]; + else if ([path hasPrefix:@"/launch/"]) + [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO]; + else if ([path hasPrefix:@"/package-settings/"]) + return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease]; + else if ([path hasPrefix:@"/package-signature/"]) + return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease]; + else if ([path hasPrefix:@"/package/"]) + return [self pageForPackage:[path substringFromIndex:9]]; + else if ([path hasPrefix:@"/files/"]) { + NSString *name = [path substringFromIndex:7]; if (Package *package = [database_ packageWithName:name]) { FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease]; @@ -6770,7 +6831,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self setIdleTimerDisabled:YES]; hud_ = [self addProgressHUD]; - [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"]; + [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"]; [self setStatusBarShowsProgress:YES]; @@ -6783,22 +6844,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self finish]; } -/* Web Scripting {{{ */ -+ (NSString *) webScriptNameForSelector:(SEL)selector { - if (selector == @selector(supports:)) - return @"supports"; - return nil; -} - -+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { - return selector != @selector(supports:); -} - -- (BOOL) supports:(NSString *)feature { - return [feature isEqualToString:@"window.open"]; -} -/* }}} */ - - (void) showKeyboard:(BOOL)show { CGSize keysize = [UIKeyboard defaultSize]; CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize}; @@ -6886,7 +6931,31 @@ id Dealloc_(id self, SEL selector) { }*/ int main(int argc, char *argv[]) { _pooled - bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0; + class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16"); + + bool substrate(false); + + if (argc != 0) { + char **args(argv); + int arge(1); + + for (int argi(1); argi != argc; ++argi) + if (strcmp(argv[argi], "--") == 0) { + arge = argi; + argv[argi] = argv[0]; + argv += argi; + argc -= argi; + break; + } + + for (int argi(1); argi != arge; ++argi) + if (strcmp(args[argi], "--bootstrap") == 0) + bootstrap_ = true; + else if (strcmp(args[argi], "--substrate") == 0) + substrate = true; + else + fprintf(stderr, "unknown argument: %s\n", args[argi]); + } App_ = [[NSBundle mainBundle] bundlePath]; Home_ = NSHomeDirectory(); @@ -6902,10 +6971,12 @@ int main(int argc, char *argv[]) { _pooled setuid(0); setgid(0); +#if 1 /* XXX: this costs 1.4s of startup performance */ if (unlink("/var/cache/apt/pkgcache.bin") == -1) _assert(errno == ENOENT); if (unlink("/var/cache/apt/srcpkgcache.bin") == -1) _assert(errno == ENOENT); +#endif /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc)); alloc_ = alloc->method_imp; @@ -6936,9 +7007,20 @@ int main(int argc, char *argv[]) { _pooled UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier]; + if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) + Build_ = [system objectForKey:@"ProductBuildVersion"]; + /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist"); AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/ + /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL) + Indices_ = [[NSMutableDictionary alloc] init];*/ + + Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating", + @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag", + nil]; + if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL) Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2]; else { @@ -6971,8 +7053,10 @@ int main(int argc, char *argv[]) { _pooled Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease]; #endif - if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0) - dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL); + if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0) + dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL); + /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0) + dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/ if (access("/User", F_OK) != 0) system("/usr/libexec/cydia/firmware.sh");