X-Git-Url: https://git.saurik.com/cydia.git/blobdiff_plain/f464053e71c47280c84c901db3ea3faa7d0222c2..81bf90f6b33e7d58719923c8e1260bd2b4ff9907:/Cydia.mm diff --git a/Cydia.mm b/Cydia.mm index aa809a7d..a4b2e600 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__ @@ -206,18 +229,13 @@ extern NSString * const kCAFilterNearest; @implementation PopTransitionView -- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to { +- (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to { if (from != nil && to == nil) [self removeFromSuperview]; } @end -@interface UIView (PopUpView) -- (void) popFromSuperviewAnimated:(BOOL)animated; -- (void) popSubview:(UIView *)view; -@end - @implementation UIView (PopUpView) - (void) popFromSuperviewAnimated:(BOOL)animated { @@ -239,8 +257,123 @@ extern NSString * const kCAFilterNearest; #define lprintf(args...) fprintf(stderr, args) -#define ForSaurik 1 +#define ForRelease 1 +#define ForSaurik (1 && !ForRelease) +#define IgnoreInstall (0 && !ForRelease) #define RecycleWebViews 0 +#define AlwaysReload (1 && !ForRelease) + +#if ForRelease +#undef _trace +#define _trace(args...) +#endif + +/* 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_; @@ -473,9 +617,7 @@ static NSString *Home_; static BOOL Sounds_Keyboard_; static BOOL Advanced_; -#if !ForSaurik static BOOL Loaded_; -#endif static BOOL Ignored_; static UIFont *Font12_; @@ -485,20 +627,12 @@ 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_; @@ -548,7 +682,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 +762,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 +791,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()]]; } @@ -758,6 +894,8 @@ class Progress : FILE *input_; } ++ (Database *) sharedInstance; + - (void) _readCydia:(NSNumber *)fd; - (void) _readStatus:(NSNumber *)fd; - (void) _readOutput:(NSNumber *)fd; @@ -766,12 +904,12 @@ class Progress : - (Package *) packageWithName:(NSString *)name; -- (Database *) init; - (pkgCacheFile &) cache; - (pkgDepCache::Policy *) policy; - (pkgRecords *) records; - (pkgProblemResolver *) resolver; - (pkgAcquire &) fetcher; +- (pkgSourceList &) list; - (NSArray *) packages; - (NSArray *) sources; - (void) reloadData; @@ -831,24 +969,26 @@ class Progress : @implementation Source -- (void) dealloc { - [uri_ release]; - [distribution_ release]; - [type_ release]; +#define _clear(field) \ + if (field != nil) \ + [field release]; \ + field = nil; - if (description_ != nil) - [description_ release]; - if (label_ != nil) - [label_ release]; - if (origin_ != nil) - [origin_ release]; - if (version_ != nil) - [version_ release]; - if (defaultIcon_ != nil) - [defaultIcon_ release]; - if (record_ != nil) - [record_ release]; +- (void) _clear { + _clear(uri_) + _clear(distribution_) + _clear(type_) + _clear(description_) + _clear(label_) + _clear(origin_) + _clear(version_) + _clear(defaultIcon_) + _clear(record_) +} + +- (void) dealloc { + [self _clear]; [super dealloc]; } @@ -864,44 +1004,50 @@ class Progress : return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; } -- (Source *) initWithMetaIndex:(metaIndex *)index { - if ((self = [super init]) != nil) { - trusted_ = index->IsTrusted(); - - uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain]; - distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain]; - type_ = [[NSString stringWithUTF8String:index->GetType()] retain]; - - debReleaseIndex *dindex(dynamic_cast(index)); - if (dindex != NULL) { - std::ifstream release(dindex->MetaIndexFile("Release").c_str()); - std::string line; - while (std::getline(release, line)) { - std::string::size_type colon(line.find(':')); - if (colon == std::string::npos) - continue; +- (void) setMetaIndex:(metaIndex *)index { + [self _clear]; - std::string name(line.substr(0, colon)); - std::string value(line.substr(colon + 1)); - while (!value.empty() && value[0] == ' ') - value = value.substr(1); - - if (name == "Default-Icon") - defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain]; - else if (name == "Description") - description_ = [[NSString stringWithUTF8String:value.c_str()] retain]; - else if (name == "Label") - label_ = [[NSString stringWithUTF8String:value.c_str()] retain]; - else if (name == "Origin") - origin_ = [[NSString stringWithUTF8String:value.c_str()] retain]; - else if (name == "Version") - version_ = [[NSString stringWithUTF8String:value.c_str()] retain]; - } + trusted_ = index->IsTrusted(); + + uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain]; + distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain]; + type_ = [[NSString stringWithUTF8String:index->GetType()] retain]; + + debReleaseIndex *dindex(dynamic_cast(index)); + if (dindex != NULL) { + std::ifstream release(dindex->MetaIndexFile("Release").c_str()); + std::string line; + while (std::getline(release, line)) { + std::string::size_type colon(line.find(':')); + if (colon == std::string::npos) + continue; + + std::string name(line.substr(0, colon)); + std::string value(line.substr(colon + 1)); + while (!value.empty() && value[0] == ' ') + value = value.substr(1); + + if (name == "Default-Icon") + defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain]; + else if (name == "Description") + description_ = [[NSString stringWithUTF8String:value.c_str()] retain]; + else if (name == "Label") + label_ = [[NSString stringWithUTF8String:value.c_str()] retain]; + else if (name == "Origin") + origin_ = [[NSString stringWithUTF8String:value.c_str()] retain]; + else if (name == "Version") + version_ = [[NSString stringWithUTF8String:value.c_str()] retain]; } + } - record_ = [Sources_ objectForKey:[self key]]; - if (record_ != nil) - record_ = [record_ retain]; + record_ = [Sources_ objectForKey:[self key]]; + if (record_ != nil) + record_ = [record_ retain]; +} + +- (Source *) initWithMetaIndex:(metaIndex *)index { + if ((self = [super init]) != nil) { + [self setMetaIndex:index]; } return self; } @@ -925,7 +1071,7 @@ class Progress : return NSOrderedDescending; } - return [lhs compare:rhs options:CompareOptions_]; + return [lhs compare:rhs options:LaxCompareOptions_]; } - (NSDictionary *) record { @@ -1018,35 +1164,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_; @@ -1056,6 +1173,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { Source *source_; bool cached_; + NSString *section_; + NSString *latest_; NSString *installed_; @@ -1079,12 +1198,19 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (pkgCache::PkgIterator) iterator; - (NSString *) section; +- (NSString *) simpleSection; + +- (NSString *) uri; + - (Address *) maintainer; - (size_t) size; - (NSString *) description; - (NSString *) index; +- (NSMutableDictionary *) metadata; - (NSDate *) seen; +- (BOOL) subscribed; +- (BOOL) ignored; - (NSString *) latest; - (NSString *) installed; @@ -1105,7 +1231,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (NSString *) id; - (NSString *) name; - (NSString *) tagline; -- (NSString *) icon; +- (UIImage *) icon; - (NSString *) homepage; - (NSString *) depiction; - (Address *) author; @@ -1123,11 +1249,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (bool) hasSupportingRole; - (BOOL) hasTag:(NSString *)tag; - (NSString *) primaryPurpose; +- (NSArray *) purposes; - (NSComparisonResult) compareByName:(Package *)package; - (NSComparisonResult) compareBySection:(Package *)package; -- (NSComparisonResult) compareBySectionAndName:(Package *)package; -- (NSComparisonResult) compareForChanges:(Package *)package; + +- (uint32_t) compareForChanges; - (void) install; - (void) remove; @@ -1145,6 +1272,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]; @@ -1175,7 +1305,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", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil]; + return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil]; } - (NSArray *) attributeKeys { @@ -1192,7 +1322,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) { database_ = database; version_ = [database_ policy]->GetCandidateVer(iterator_); - latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain]; + NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()]; + latest_ = latest == nil ? nil : [StripVersion(latest) retain]; + + pkgCache::VerIterator current = iterator_.CurrentVer(); + NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()]; + installed_ = [StripVersion(installed) retain]; if (!version_.end()) file_ = version_.FileList(); @@ -1201,10 +1336,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { file_ = pkgCache::VerFileIterator(cache, cache.VerFileP); } - pkgCache::VerIterator current = iterator_.CurrentVer(); - installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain]; - - id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain]; + id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain]; if (!file_.end()) { pkgRecords::Parser *parser = &[database_ records]->Lookup(file_); @@ -1212,32 +1344,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) @@ -1249,13 +1426,45 @@ NSString *Scour(const char *field, const char *begin, const char *end) { } } - NSMutableDictionary *metadata = [Packages_ objectForKey:id_]; - if (metadata == nil || [metadata count] == 0) { - metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys: + NSString *solid(latest == nil ? installed : latest); + bool changed(false); + + NSString *key([id_ lowercaseString]); + + NSMutableDictionary *metadata = [Packages_ objectForKey:key]; + if (metadata == nil) { + metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys: now_, @"FirstSeen", - nil]; + nil] mutableCopy]; - [Packages_ setObject:metadata forKey:id_]; + if (solid != nil) + [metadata setObject:solid forKey:@"LastVersion"]; + changed = true; + } else { + NSDate *first([metadata objectForKey:@"FirstSeen"]); + NSDate *last([metadata objectForKey:@"LastSeen"]); + NSString *version([metadata objectForKey:@"LastVersion"]); + + if (first == nil) { + first = last == nil ? now_ : last; + [metadata setObject:first forKey:@"FirstSeen"]; + changed = true; + } + + if (solid != nil) + if (version == nil) { + [metadata setObject:solid forKey:@"LastVersion"]; + changed = true; + } else if (![version isEqualToString:solid]) { + [metadata setObject:solid forKey:@"LastVersion"]; + last = now_; + [metadata setObject:last forKey:@"LastSeen"]; + changed = true; + } + } + + if (changed) { + [Packages_ setObject:metadata forKey:key]; Changed_ = true; } } return self; @@ -1273,6 +1482,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; @@ -1286,14 +1498,36 @@ 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; +} + +- (NSString *) uri { + return nil; +#if 0 + pkgIndexFile *index; + pkgCache::PkgFileIterator file(file_.File()); + if (![database_ list].FindIndex(file, index)) + return nil; + return [NSString stringWithUTF8String:iterator_->Path]; + //return [NSString stringWithUTF8String:file.Site()]; + //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()]; +#endif } - (Address *) maintainer { if (file_.end()) return nil; pkgRecords::Parser *parser = &[database_ records]->Lookup(file_); - return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]]; + const std::string &maintainer(parser->Maintainer()); + return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]]; } - (size_t) size { @@ -1325,8 +1559,32 @@ 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 { - return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"]; + 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 { @@ -1344,12 +1602,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (BOOL) upgradableAndEssential:(BOOL)essential { pkgCache::VerIterator current = iterator_.CurrentVer(); + bool value; if (current.end()) - return essential && [self essential]; - else { - pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_); - return !candidate.end() && candidate != current; - } + value = essential && [self essential]; + else + value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep()); + return value; } - (BOOL) essential { @@ -1433,8 +1691,21 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return tagline_; } -- (NSString *) icon { - return icon_; +- (UIImage *) icon { + NSString *section = [self simpleSection]; + + UIImage *icon(nil); + if (icon_ != nil) + if ([icon_ hasPrefix:@"file:///"]) + icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]]; + if (icon == nil) if (section != nil) + icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]; + if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon]) + if ([dicon hasPrefix:@"file:///"]) + icon = [UIImage imageAtPath:[dicon substringFromIndex:7]]; + if (icon == nil) + icon = [UIImage applicationImageNamed:@"unknown.png"]; + return icon; } - (NSString *) homepage { @@ -1482,6 +1753,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] != '.') @@ -1489,17 +1762,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"]; } @@ -1605,6 +1886,14 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return nil; } +- (NSArray *) purposes { + NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]); + for (NSString *tag in tags_) + if ([tag hasPrefix:@"purpose::"]) + [purposes addObject:[tag substringFromIndex:9]]; + return [purposes count] == 0 ? nil : purposes; +} + - (NSComparisonResult) compareByName:(Package *)package { NSString *lhs = [self name]; NSString *rhs = [package name]; @@ -1619,7 +1908,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 { @@ -1631,51 +1920,38 @@ 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]; + bool upgradable([self upgradableAndEssential:YES]); + value.bits.upgradable = upgradable ? 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 (upgradable) { + 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 { @@ -1766,7 +2042,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 { @@ -1805,6 +2081,13 @@ static NSArray *Finishes_; /* Database Implementation {{{ */ @implementation Database ++ (Database *) sharedInstance { + static Database *instance; + if (instance == nil) + instance = [[Database alloc] init]; + return instance; +} + - (void) dealloc { _assert(false); [super dealloc]; @@ -1865,9 +2148,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); @@ -1972,6 +2255,10 @@ static NSArray *Finishes_; return *fetcher_; } +- (pkgSourceList &) list { + return *list_; +} + - (NSArray *) packages { return packages_; } @@ -2063,6 +2350,7 @@ static NSArray *Finishes_; cache_.Close(); + _trace(); if (!cache_.Open(progress_, true)) { std::string error; if (!_error->PopMessage(error)) @@ -2081,6 +2369,7 @@ static NSArray *Finishes_; return; } + _trace(); now_ = [[NSDate date] retain]; @@ -2113,11 +2402,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 { @@ -2193,7 +2485,9 @@ static NSArray *Finishes_; failed = true; [delegate_ performSelectorOnMainThread:@selector(_setProgressError:) - withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil] + withObject:[NSArray arrayWithObjects: + [NSString stringWithUTF8String:error.c_str()], + nil] waitUntilDone:YES ]; } @@ -2352,8 +2646,6 @@ static NSArray *Finishes_; [super dealloc]; } -#include "internals.h" - - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller { NSLog(@"will"); } @@ -2378,6 +2670,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]; } @@ -2425,6 +2720,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @end @interface ConfirmationView : BrowserView { + _transient Database *database_; UIActionSheet *essential_; NSArray *changes_; NSArray *issues_; @@ -2454,9 +2750,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]; @@ -2469,10 +2765,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 { @@ -2483,7 +2782,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (id) initWithBook:(RVBook *)book database:(Database *)database { - if ((self = [super initWithBook:book database:database]) != nil) { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16]; NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16]; NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16]; @@ -2573,11 +2874,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"; } @@ -2586,15 +2882,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 { @@ -2603,6 +2907,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [delegate_ confirm]; } } +#endif @end /* }}} */ @@ -2663,6 +2968,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { id delegate_; BOOL running_; SHA1SumValue springlist_; + SHA1SumValue notifyconf_; SHA1SumValue sandplate_; size_t received_; NSTimeInterval last_; @@ -2815,8 +3121,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; - if ([context isEqualToString:@"conffile"]) { + NSString *context([sheet context]); + + if ([context isEqualToString:@"error"]) + [sheet dismiss]; + else if ([context isEqualToString:@"conffile"]) { FILE *input = [database_ input]; switch (button) { @@ -2831,9 +3140,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { default: _assert(false); } - } - [sheet dismiss]; + [sheet dismiss]; + } } - (void) closeButtonPushed { @@ -2881,6 +3190,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); @@ -2923,9 +3241,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; @@ -2979,6 +3299,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); @@ -3101,7 +3429,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 { @@ -3192,21 +3527,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self clearPackage]; Source *source = [package source]; - NSString *section = [package section]; - if (section != nil) - section = Simplify(section); - - icon_ = nil; - if (NSString *icon = [package icon]) - icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]]; - if (icon_ == nil) if (section != nil) - icon_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]; - if (icon_ == nil) if (NSString *icon = [source defaultIcon]) - icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]]; - if (icon_ == nil) - icon_ = [UIImage applicationImageNamed:@"unknown.png"]; - - icon_ = [icon_ retain]; + NSString *section = [package simpleSection]; + + icon_ = [[package icon] retain]; name_ = [[package name] retain]; description_ = [[package tagline] retain]; @@ -3386,7 +3709,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]; } } @@ -3549,6 +3872,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* }}} */ /* Package View {{{ */ @interface PackageView : BrowserView { + _transient Database *database_; Package *package_; NSString *name_; NSMutableArray *buttons_; @@ -3583,18 +3907,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]; } @@ -3603,7 +3931,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super webView:sender didClearWindowObject:window forFrame:frame]; } -#if 0 +#if !AlwaysReload - (void) _rightButtonClicked { /*[super _rightButtonClicked]; return;*/ @@ -3623,13 +3951,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]; } @@ -3639,7 +3967,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (id) initWithBook:(RVBook *)book database:(Database *)database { - if ((self = [super initWithBook:book database:database]) != nil) { + if ((self = [super initWithBook:book]) != nil) { database_ = database; buttons_ = [[NSMutableArray alloc] initWithCapacity:4]; } return self; @@ -3676,6 +4004,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } +- (bool) _loading { + return false; +} + - (void) reloadData { [self setPackage:[database_ packageWithName:name_]]; [self reloadButtons]; @@ -3687,17 +4019,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @interface PackageTable : RVPage { _transient Database *database_; NSString *title_; - SEL filter_; - id object_; NSMutableArray *packages_; NSMutableArray *sections_; UISectionList *list_; } -- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object; +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title; - (void) setDelegate:(id)delegate; -- (void) setObject:(id)object; - (void) reloadData; - (void) resetCursor; @@ -3714,8 +4043,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [list_ setDataSource:nil]; [title_ release]; - if (object_ != nil) - [object_ release]; [packages_ release]; [sections_ release]; [list_ release]; @@ -3759,18 +4086,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return; Package *package = [packages_ objectAtIndex:row]; + package = [database_ packageWithName:[package id]]; PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease]; - [view setDelegate:delegate_]; [view setPackage:package]; + [view setDelegate:delegate_]; [book_ pushPage:view]; } -- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object { +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title { if ((self = [super initWithBook:book]) != nil) { database_ = database; title_ = [title retain]; - filter_ = filter; - object_ = object == nil ? nil : [object retain]; packages_ = [[NSMutableArray arrayWithCapacity:16] retain]; sections_ = [[NSMutableArray arrayWithCapacity:16] retain]; @@ -3791,7 +4117,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [table setReusesTableCells:YES]; [self addSubview:list_]; - [self reloadData]; [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; @@ -3802,13 +4127,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { delegate_ = delegate; } -- (void) setObject:(id)object { - if (object_ != nil) - [object_ release]; - if (object == nil) - object_ = nil; - else - object_ = [object retain]; +- (bool) hasPackage:(Package *)package { + return true; } - (void) reloadData { @@ -3819,7 +4139,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { for (size_t i(0); i != [packages count]; ++i) { Package *package([packages objectAtIndex:i]); - if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue]) + if ([self hasPackage:package]) [packages_ addObject:package]; } @@ -3862,20 +4182,64 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @end /* }}} */ - -/* Add Source View {{{ */ -@interface AddSourceView : RVPage { - _transient Database *database_; +/* Filtered Package Table {{{ */ +@interface FilteredPackageTable : PackageTable { + SEL filter_; + id object_; } -- (id) initWithBook:(RVBook *)book database:(Database *)database; +- (void) setObject:(id)object; + +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object; @end -@implementation AddSourceView +@implementation FilteredPackageTable -- (id) initWithBook:(RVBook *)book database:(Database *)database { - if ((self = [super initWithBook:book]) != nil) { +- (void) dealloc { + if (object_ != nil) + [object_ release]; + [super dealloc]; +} + +- (void) setObject:(id)object { + if (object_ != nil) + [object_ release]; + if (object == nil) + object_ = nil; + else + object_ = [object retain]; +} + +- (bool) hasPackage:(Package *)package { + return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue]; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object { + if ((self = [super initWithBook:book database:database title:title]) != nil) { + filter_ = filter; + object_ = object == nil ? nil : [object retain]; + + [self reloadData]; + } return self; +} + +@end +/* }}} */ + +/* Add Source View {{{ */ +@interface AddSourceView : RVPage { + _transient Database *database_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; + +@end + +@implementation AddSourceView + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { database_ = database; } return self; } @@ -4056,7 +4420,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { Source *source = [sources_ objectAtIndex:row]; - PackageTable *packages = [[[PackageTable alloc] + PackageTable *packages = [[[FilteredPackageTable alloc] initWithBook:book_ database:database_ title:[source label] @@ -4178,8 +4542,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]; @@ -4209,7 +4574,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 { @@ -4248,7 +4617,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_) { @@ -4278,12 +4649,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]; } @@ -4303,7 +4678,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit"; } @@ -4317,7 +4692,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Installed View {{{ */ @interface InstalledView : RVPage { _transient Database *database_; - PackageTable *packages_; + FilteredPackageTable *packages_; BOOL expert_; } @@ -4336,7 +4711,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if ((self = [super initWithBook:book]) != nil) { database_ = database; - packages_ = [[PackageTable alloc] + packages_ = [[FilteredPackageTable alloc] initWithBook:book database:database title:nil @@ -4374,7 +4749,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Packages"; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple"; } @@ -4399,7 +4774,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 { @@ -4455,543 +4835,378 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Settings"; } -- (NSString *) _rightButtonTitle { +#if !AlwaysReload +- (id) _rightButtonTitle { return nil; } +#endif -@end -/* }}} */ - -@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 { - _transient volatile id delegate_; +- (bool) _loading { + return false; } -- (void) setDelegate:(id)delegate; -- (id) initWithDelegate:(id)delegate; @end +/* }}} */ -@implementation IndirectDelegate - -- (void) setDelegate:(id)delegate { - delegate_ = delegate; -} - -- (id) initWithDelegate:(id)delegate { - delegate_ = delegate; - return self; -} +#include <BrowserView.m> -- (NSMethodSignature*) methodSignatureForSelector:(SEL)sel { - if (delegate_ != nil) - if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel]) - return sig; - // XXX: I fucking hate Apple so very very bad - return [NSMethodSignature signatureWithObjCTypes:"v@:"]; +/* 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_; } -- (void) forwardInvocation:(NSInvocation *)inv { - SEL sel = [inv selector]; - if (delegate_ != nil && [delegate_ respondsToSelector:sel]) - [inv invokeWithTarget:delegate_]; -} +- (id) initWithFrame:(CGRect)frame database:(Database *)database; +- (void) update; +- (BOOL) updating; @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]; - - //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - [webview close]; - -#if RecycleWebViews - [webview_ removeFromSuperview]; - [Documents_ addObject:[webview_ autorelease]]; -#else - [webview_ release]; -#endif - - [indirect_ setDelegate:nil]; - [indirect_ release]; - - [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]; - 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_ database:database_] 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 database:(Database *)database { - if ((self = [super initWithBook:book]) != nil) { - database_ = database; - 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_ packageWithName:@"cydia"]); - NSString *application = package == nil ? @"Cydia" : [NSString - stringWithFormat:@"Cydia/%@", - [package installed] - ]; [webview setApplicationNameForUserAgent:application]; - - indirect_ = [[IndirectDelegate alloc] initWithDelegate:self]; - - [webview setFrameLoadDelegate:self]; - [webview setResourceLoadDelegate:indirect_]; - [webview setUIDelegate:self]; - [webview setScriptDebugDelegate:self]; - [webview setPolicyDelegate:self]; - - urls_ = [[NSMutableArray alloc] initWithCapacity:16]; - - [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - } return self; +- (void) _addProgressOutput:(NSString *)output { } -- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event { - [webview_ redrawScaledDocument]; +@end +/* }}} */ +/* Cydia:// Protocol {{{ */ +@interface CydiaURLProtocol : NSURLProtocol { } -- (void) _rightButtonClicked { - reloading_ = true; - [self reloadURL]; -} +@end -- (NSString *) _rightButtonTitle { - return @"Reload"; -} +@implementation CydiaURLProtocol -- (NSString *) rightButtonTitle { - return loading_ ? @"" : [self _rightButtonTitle]; ++ (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; } -- (NSString *) title { - return title_ == nil ? @"Loading" : title_; ++ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request { + return request; } -- (NSString *) backButtonTitle { - return @"Browser"; -} +- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request { + id<NSURLProtocolClient> client([self client]); + if (icon == nil) + [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]]; + else { + NSData *data(UIImagePNGRepresentation(icon)); -- (void) setPageActive:(BOOL)active { - if (!active) - [indicator_ removeFromSuperview]; - else - [[book_ navigationBar] addSubview:indicator_]; + 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]; + } } -- (void) resetViewAnimated:(BOOL)animated { -} +- (void) startLoading { + id<NSURLProtocolClient> client([self client]); + NSURLRequest *request([self request]); -- (void) setPushed:(bool)pushed { - pushed_ = pushed; -} + NSURL *url([request URL]); + NSString *href([url absoluteString]); -@end -/* }}} */ + NSString *path([href substringFromIndex:8]); + NSRange slash([path rangeOfString:@"/"]); -@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_; + NSString *command; + if (slash.location == NSNotFound) { + command = path; + path = nil; + } else { + command = [path substringToIndex:slash.location]; + path = [path substringFromIndex:(slash.location + 1)]; + } + + Database *database([Database sharedInstance]); + + 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]]; + } +} + +- (void) stopLoading { } -- (id) initWithFrame:(CGRect)frame database:(Database *)database; -- (void) update; -- (BOOL) updating; - @end +/* }}} */ -/* Install View {{{ */ -@interface InstallView : RVPage { +/* Sections View {{{ */ +@interface SectionsView : RVPage { _transient Database *database_; NSMutableArray *sections_; NSMutableArray *filtered_; @@ -5007,7 +5222,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @end -@implementation InstallView +@implementation SectionsView - (void) dealloc { [list_ setDataSource:nil]; @@ -5072,7 +5287,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } - PackageTable *table = [[[PackageTable alloc] + PackageTable *table = [[[FilteredPackageTable alloc] initWithBook:book_ database:database_ title:title @@ -5126,6 +5341,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]); @@ -5141,11 +5357,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) { @@ -5161,8 +5380,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [section addToCount]; } + _trace(); [list_ reloadData]; + _trace(); } - (void) resetView { @@ -5177,10 +5398,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) _rightButtonClicked { if ((editing_ = !editing_)) [list_ reloadData]; - else { + else [delegate_ updateData]; - } - [book_ reloadTitleForPage:self]; [book_ reloadButtonsForPage:self]; } @@ -5193,7 +5412,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Sections"; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit"; } @@ -5323,53 +5542,57 @@ 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]); if ( [package installed] == nil && [package valid] && [package visible] || - [package upgradableAndEssential:NO] + [package upgradableAndEssential:YES] ) [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 (seen == nil) - name = [@"n/a ?" retain]; - else { - name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen); - } + if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) { + last = 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); @@ -5380,6 +5603,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]; @@ -5395,7 +5620,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_]; } @@ -5414,7 +5639,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UIView *accessory_; UISearchField *field_; UITransitionView *transition_; - PackageTable *table_; + FilteredPackageTable *table_; UIPreferencesTable *advanced_; UIView *dimmed_; bool flipped_; @@ -5532,10 +5757,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_]; @@ -5549,7 +5770,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { CGColor dimmed(space_, 0, 0, 0, 0.5); [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]]; - table_ = [[PackageTable alloc] + table_ = [[FilteredPackageTable alloc] initWithBook:book database:database title:nil @@ -5588,10 +5809,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}}; @@ -5667,237 +5888,236 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @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_; +@interface SettingsView : RVPage { + _transient Database *database_; + NSString *name_; + Package *package_; + UIPreferencesTable *table_; + _UISwitchSlider *subscribedSwitch_; + _UISwitchSlider *ignoredSwitch_; + UIPreferencesControlTableCell *subscribedCell_; + UIPreferencesControlTableCell *ignoredCell_; } -- (void) update { - [UIView beginAnimations:nil context:NULL]; +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package; - CGRect ovrframe = [overlay_ frame]; - ovrframe.origin.y = 0; - [overlay_ setFrame:ovrframe]; +@end - CGRect barframe = [navbar_ frame]; - barframe.origin.y += ovrframe.size.height; - [navbar_ setFrame:barframe]; +@implementation SettingsView - CGRect trnframe = [transition_ frame]; - trnframe.origin.y += ovrframe.size.height; - trnframe.size.height -= ovrframe.size.height; - [transition_ setFrame:trnframe]; - - [UIView endAnimations]; +- (void) dealloc { + [table_ setDataSource:nil]; - [indicator_ startAnimation]; - [prompt_ setText:@"Updating Database"]; - [progress_ setProgress:0]; + [name_ release]; + if (package_ != nil) + [package_ release]; + [table_ release]; + [subscribedSwitch_ release]; + [ignoredSwitch_ release]; + [subscribedCell_ release]; + [ignoredCell_ release]; + [super dealloc]; +} - received_ = 0; - last_ = [NSDate timeIntervalSinceReferenceDate]; - updating_ = true; - [overlay_ addSubview:cancel_]; +- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table { + if (package_ == nil) + return 0; - [NSThread - detachNewThreadSelector:@selector(_update) - toTarget:self - withObject:nil - ]; + return 2; } -- (void) _update_ { - updating_ = false; +- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group { + if (package_ == nil) + return nil; - [indicator_ stopAnimation]; + switch (group) { + case 0: return nil; + case 1: return nil; - [UIView beginAnimations:nil context:NULL]; + default: _assert(false); + } - CGRect ovrframe = [overlay_ frame]; - ovrframe.origin.y = -ovrframe.size.height; - [overlay_ setFrame:ovrframe]; + return nil; +} - CGRect barframe = [navbar_ frame]; - barframe.origin.y -= ovrframe.size.height; - [navbar_ setFrame:barframe]; +- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group { + if (package_ == nil) + return NO; - CGRect trnframe = [transition_ frame]; - trnframe.origin.y -= ovrframe.size.height; - trnframe.size.height += ovrframe.size.height; - [transition_ setFrame:trnframe]; + switch (group) { + case 0: return NO; + case 1: return YES; - [UIView commitAnimations]; + default: _assert(false); + } - [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0]; + return NO; } -- (id) initWithFrame:(CGRect)frame database:(Database *)database { - if ((self = [super initWithFrame:frame]) != nil) { - database_ = database; +- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group { + if (package_ == nil) + return 0; - CGRect ovrrect = [navbar_ bounds]; - ovrrect.size.height = [UINavigationBar defaultSize].height; - ovrrect.origin.y = -ovrrect.size.height; + switch (group) { + case 0: return 1; + case 1: return 1; - overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; - [self addSubview:overlay_]; + default: _assert(false); + } - 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_]; + return 0; +} - [overlay_ setBarStyle:1]; - [underlay_ setBarStyle:1]; +- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key { + if (package_ == nil) + return; - int barstyle = [overlay_ _barStyle:NO]; - bool ugly = barstyle == 0; + _UISwitchSlider *slider([cell control]); + BOOL value([slider value] != 0); + NSMutableDictionary *metadata([package_ metadata]); - UIProgressIndicatorStyle style = ugly ? - UIProgressIndicatorStyleMediumBrown : - UIProgressIndicatorStyleMediumWhite; + BOOL before; + if (NSNumber *number = [metadata objectForKey:key]) + before = [number boolValue]; + else + before = NO; - CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style]; - unsigned indoffset = (ovrrect.size.height - indsize.height) / 2; - CGRect indrect = {{indoffset, indoffset}, indsize}; + if (value != before) { + [metadata setObject:[NSNumber numberWithBool:value] forKey:key]; + Changed_ = true; + [delegate_ updateData]; + } +} - indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect]; - [indicator_ setStyle:style]; - [overlay_ addSubview:indicator_]; +- (void) onSubscribed:(UIPreferencesControlTableCell *)cell { + [self onSomething:cell withKey:@"IsSubscribed"]; +} - CGSize prmsize = {215, indsize.height + 4}; +- (void) onIgnored:(UIPreferencesControlTableCell *)cell { + [self onSomething:cell withKey:@"IsIgnored"]; +} - CGRect prmrect = {{ - indoffset * 2 + indsize.width, -#ifdef __OBJC2__ - -1 + -#endif - unsigned(ovrrect.size.height - prmsize.height) / 2 - }, prmsize}; +- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group { + if (package_ == nil) + return nil; - UIFont *font = [UIFont systemFontOfSize:15]; + 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; + } - prompt_ = [[UITextLabel alloc] initWithFrame:prmrect]; + default: _assert(false); + } break; - [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]]; - [prompt_ setBackgroundColor:[UIColor clearColor]]; - [prompt_ setFont:font]; + default: _assert(false); + } - [overlay_ addSubview:prompt_]; + return nil; +} - CGSize prgsize = {75, 100}; +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package { + if ((self = [super initWithBook:book])) { + database_ = database; + name_ = [package retain]; - CGRect prgrect = {{ - ovrrect.size.width - prgsize.width - 10, - (ovrrect.size.height - prgsize.height) / 2 - } , prgsize}; + table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]]; + [self addSubview:table_]; - progress_ = [[UIProgressBar alloc] initWithFrame:prgrect]; - [progress_ setStyle:0]; - [overlay_ addSubview:progress_]; + subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)]; + [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside]; - cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted]; - [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside]; + ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)]; + [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside]; - 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]; + subscribedCell_ = [[UIPreferencesControlTableCell alloc] init]; + [subscribedCell_ setShowSelection:NO]; + [subscribedCell_ setTitle:@"Show All Changes"]; + [subscribedCell_ setControl:subscribedSwitch_]; - [cancel_ setBarStyle:barstyle]; + ignoredCell_ = [[UIPreferencesControlTableCell alloc] init]; + [ignoredCell_ setShowSelection:NO]; + [ignoredCell_ setTitle:@"Ignore Upgrades"]; + [ignoredCell_ setControl:ignoredSwitch_]; + + [table_ setDataSource:self]; + [self reloadData]; } return self; } -- (void) _onCancel { - updating_ = false; - [cancel_ removeFromSuperview]; +- (void) resetViewAnimated:(BOOL)animated { + [table_ resetViewAnimated:animated]; } -- (void) _update { _pooled - Status status; - status.setDelegate(self); - - [database_ updateWithStatus:status]; +- (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]; + } - [self - performSelectorOnMainThread:@selector(_update_) - withObject:nil - waitUntilDone:NO - ]; + [table_ reloadData]; } -- (void) setProgressError:(NSString *)error forPackage:(NSString *)id { - [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]]; +- (NSString *) title { + return @"Settings"; } -- (void) setProgressTitle:(NSString *)title { - [self - performSelectorOnMainThread:@selector(_setProgressTitle:) - withObject:title - waitUntilDone:YES - ]; -} +@end -- (void) setProgressPercent:(float)percent { - [self - performSelectorOnMainThread:@selector(_setProgressPercent:) - withObject:[NSNumber numberWithFloat:percent] - waitUntilDone:YES - ]; +/* Signature View {{{ */ +@interface SignatureView : BrowserView { + _transient Database *database_; + NSString *package_; } -- (void) startProgress { -} +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package; -- (void) addProgressOutput:(NSString *)output { - [self - performSelectorOnMainThread:@selector(_addProgressOutput:) - withObject:output - waitUntilDone:YES - ]; -} +@end -- (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_; +@implementation SignatureView + +- (void) dealloc { + [package_ release]; + [super dealloc]; } -- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - [sheet dismiss]; +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + // XXX: dude! + [super webView:sender didClearWindowObject:window forFrame:frame]; } -- (void) _setProgressTitle:(NSString *)title { - [prompt_ setText:title]; +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + package_ = [package retain]; + [self reloadData]; + } return self; } -- (void) _setProgressPercent:(NSNumber *)percent { - [progress_ setProgress:[percent floatValue]]; +- (void) resetViewAnimated:(BOOL)animated { } -- (void) _addProgressOutput:(NSString *)output { +- (void) reloadData { + [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]]; } @end +/* }}} */ @interface Cydia : UIApplication < ConfirmationViewDelegate, @@ -5925,7 +6145,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UIKeyboard *keyboard_; UIProgressHUD *hud_; - InstallView *install_; + SectionsView *sections_; ChangesView *changes_; ManageView *manage_; SearchView *search_; @@ -5959,7 +6179,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 @@ -6011,17 +6231,21 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self updateData]; -#if !ForSaurik + // XXX: what is this line of code for? if ([packages count] == 0); - else if (Loaded_) -#endif + else if (Loaded_) loaded: [self _loaded]; -#if !ForSaurik else { Loaded_ = YES; + + if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) { + NSTimeInterval interval([update timeIntervalSinceNow]); + if (interval <= 0 && interval > -600) + goto loaded; + } + [book_ update]; } -#endif /*[hud show:NO]; [hud removeFromSuperview];*/ @@ -6029,7 +6253,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; } } @@ -6038,11 +6264,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self _saveConfig]; /* XXX: this is just stupid */ - if (tag_ != 2) - [install_ reloadData]; - if (tag_ != 3) + if (tag_ != 2 && sections_ != nil) + [sections_ reloadData]; + if (tag_ != 3 && changes_ != nil) [changes_ reloadData]; - if (tag_ != 5) + if (tag_ != 5 && search_ != nil) [search_ reloadData]; [book_ reloadData]; @@ -6096,17 +6322,25 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _error->Discard(); } +- (void) popUpBook:(RVBook *)book { + [underlay_ popSubview:book]; +} + +- (CGRect) popUpBounds { + return [underlay_ bounds]; +} + - (void) perform { [database_ prepare]; - confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]]; + confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]]; [confirm_ setDelegate:self]; ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]); [page setDelegate:self]; [confirm_ setPage:page]; - [underlay_ popSubview:confirm_]; + [self popUpBook:confirm_]; } - (void) installPackage:(Package *)package { @@ -6186,7 +6420,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class { - BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease]; + BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease]; [browser loadURL:url]; return browser; } @@ -6201,12 +6435,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [book_ resetViewAnimated:YES]; return; } else if (tag_ == 2 && tag != 2) - [install_ resetView]; + [sections_ resetView]; switch (tag) { case 1: [self _setHomePage]; break; - case 2: [self setPage:install_]; break; + case 2: [self setPage:sections_]; break; case 3: [self setPage:changes_]; break; case 4: [self setPage:manage_]; break; case 5: [self setPage:search_]; break; @@ -6354,10 +6588,15 @@ 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_]; - install_ = [[InstallView alloc] initWithBook:book_ database:database_]; + if (!bootstrap_) + [underlay_ addSubview:overlay_]; + + [self reloadData]; + + sections_ = [[SectionsView alloc] initWithBook:book_ database:database_]; changes_ = [[ChangesView alloc] initWithBook:book_ database:database_]; search_ = [[SearchView alloc] initWithBook:book_ database:database_]; @@ -6366,11 +6605,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { withClass:[ManageView class] ] retain]; - if (!bootstrap_) - [underlay_ addSubview:overlay_]; - - [self reloadData]; - if (bootstrap_) [self bootstrap]; else @@ -6378,8 +6612,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) { @@ -6407,7 +6644,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; @@ -6432,7 +6671,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) { @@ -6458,7 +6699,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _assert(false); } - [sheet dismiss]; + [sheet dismiss]; + } } - (void) reorganize { _pooled @@ -6489,7 +6731,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 { @@ -6516,25 +6768,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/"]) - 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]; @@ -6571,6 +6837,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { essential_ = [[NSMutableArray alloc] initWithCapacity:4]; broken_ = [[NSMutableArray alloc] initWithCapacity:4]; + [NSURLProtocol registerClass:[CydiaURLProtocol class]]; + CGRect screenrect = [UIHardware fullScreenApplicationContentRect]; window_ = [[UIWindow alloc] initWithContentRect:screenrect]; @@ -6578,7 +6846,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [window_ makeKey:self]; [window_ setHidden:NO]; - database_ = [[Database alloc] init]; + database_ = [Database sharedInstance]; progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self]; [database_ setDelegate:progress_]; [window_ setContentView:progress_]; @@ -6600,7 +6868,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]; @@ -6613,23 +6881,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 { - NSLog(@"exc:%s", sel_getName(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}; @@ -6717,7 +6968,32 @@ id Dealloc_(id self, SEL selector) { }*/ int main(int argc, char *argv[]) { _pooled - bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0; + _trace(); + 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(); @@ -6733,10 +7009,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; @@ -6767,10 +7045,17 @@ 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 ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL) + _trace(); + Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]; + _trace(); + + if (Metadata_ == NULL) Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2]; else { Settings_ = [Metadata_ objectForKey:@"Settings"]; @@ -6802,11 +7087,16 @@ 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) + if (access("/User", F_OK) != 0) { + _trace(); system("/usr/libexec/cydia/firmware.sh"); + _trace(); + } _assert([[NSFileManager defaultManager] createDirectoryAtPath:@"/var/cache/apt/archives/partial" @@ -6831,6 +7121,7 @@ int main(int argc, char *argv[]) { _pooled UIApplicationUseLegacyEvents(YES); UIKeyboardDisableAutomaticAppearance(); + _trace(); int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia"); CGColorSpaceRelease(space_);