X-Git-Url: https://git.saurik.com/cydia.git/blobdiff_plain/70393dbc2bc6a9d5ff532ca1b30a22534164ea18..3f88f20507c0870b890db839c17186d6928fae29:/MobileCydia.mm diff --git a/MobileCydia.mm b/MobileCydia.mm index 76f8fefc..900e76ed 100644 --- a/MobileCydia.mm +++ b/MobileCydia.mm @@ -1,40 +1,22 @@ /* Cydia - iPhone UIKit Front-End for Debian APT - * Copyright (C) 2008-2011 Jay Freeman (saurik) + * Copyright (C) 2008-2013 Jay Freeman (saurik) */ -/* Modified BSD License {{{ */ +/* GNU General Public License, Version 3 {{{ */ /* - * Redistribution and use in source and binary - * forms, with or without modification, are permitted - * provided that the following conditions are met: + * Cydia is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. * - * 1. Redistributions of source code must retain the - * above copyright notice, this list of conditions - * and the following disclaimer. - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions - * and the following disclaimer in the documentation - * and/or other materials provided with the - * distribution. - * 3. The name of the author may not be used to endorse - * or promote products derived from this software - * without specific prior written permission. + * Cydia is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + * You should have received a copy of the GNU General Public License + * along with Cydia. If not, see . +**/ /* }}} */ // XXX: wtf/FastMalloc.h... wtf? @@ -56,7 +38,6 @@ #include #endif -#include #include #include @@ -66,10 +47,14 @@ #include +#include + #include +#include #include #include +#include #include #include @@ -119,17 +104,22 @@ extern "C" { #include #include +#include "Sources.h" #include #include "Menes/Menes.h" +#include "CyteKit/IndirectDelegate.h" #include "CyteKit/PerlCompatibleRegEx.hpp" #include "CyteKit/TableViewCell.h" +#include "CyteKit/TabBarController.h" #include "CyteKit/WebScriptObject-Cyte.h" #include "CyteKit/WebViewController.h" +#include "CyteKit/WebViewTableViewCell.h" #include "CyteKit/stringWithUTF8Bytes.h" #include "Cydia/MIMEAddress.h" +#include "Cydia/LoadingViewController.h" #include "Cydia/ProgressEvent.h" #include "SDURLCache/SDURLCache.h" @@ -205,7 +195,11 @@ void PrintTimes() { #define _end } /* }}} */ -#define Cydia_ CYDIA_VERSION +// XXX: I hate clang. Apple: please get over your petty hatred of GPL and fix your gcc fork +#define synchronized(lock) \ + synchronized(static_cast(lock)) + +extern NSString *Cydia_; #define lprintf(args...) fprintf(stderr, args) @@ -243,6 +237,44 @@ union SplitHash { }; // }}} +static NSString *Colon_; +NSString *Elision_; +static NSString *Error_; +static NSString *Warning_; + +static NSString *Cache_; + +static void (*$SBSSetInterceptsMenuButtonForever)(bool); + +static CFStringRef (*$MGCopyAnswer)(CFStringRef); + +static NSString *UniqueIdentifier(UIDevice *device = nil) { + if (kCFCoreFoundationVersionNumber < 800) // iOS 7.x + return [device ?: [UIDevice currentDevice] uniqueIdentifier]; + else + return [(id)$MGCopyAnswer(CFSTR("UniqueDeviceID")) autorelease]; +} + +static bool IsReachable(const char *name) { + SCNetworkReachabilityFlags flags; { + SCNetworkReachabilityRef reachability(SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, name)); + SCNetworkReachabilityGetFlags(reachability, &flags); + CFRelease(reachability); + } + + // XXX: this elaborate mess is what Apple is using to determine this? :( + // XXX: do we care if the user has to intervene? maybe that's ok? + return + (flags & kSCNetworkReachabilityFlagsReachable) != 0 && ( + (flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0 || ( + (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0 || + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0 + ) && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0 || + (flags & kSCNetworkReachabilityFlagsIsWWAN) != 0 + ) + ; +} + static const NSUInteger UIViewAutoresizingFlexibleBoth(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); static _finline NSString *CydiaURL(NSString *path) { @@ -256,6 +288,15 @@ static _finline NSString *CydiaURL(NSString *path) { return [[NSString stringWithUTF8String:page] stringByAppendingString:path]; } +static void ReapZombie(pid_t pid) { + int status; + wait: + if (waitpid(pid, &status, 0) == -1) + if (errno == EINTR) + goto wait; + else _assert(false); +} + static _finline void UpdateExternalStatus(uint64_t newStatus) { int notify_token; if (notify_register_check("com.saurik.Cydia.status", ¬ify_token) == NOTIFY_STATUS_OK) { @@ -265,6 +306,11 @@ static _finline void UpdateExternalStatus(uint64_t newStatus) { notify_post("com.saurik.Cydia.status"); } +static CGFloat CYStatusBarHeight() { + CGSize size([[UIApplication sharedApplication] statusBarFrame].size); + return UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) ? size.height : size.width; +} + /* NSForcedOrderingSearch doesn't work on the iPhone */ static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch; static const NSStringCompareOptions LaxCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSCaseInsensitiveSearch; @@ -390,7 +436,6 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s /* Cydia NSString Additions {{{ */ @interface NSString (Cydia) - (NSComparisonResult) compareByPath:(NSString *)other; -- (NSString *) stringByCachingURLWithCurrentCDN; - (NSString *) stringByAddingPercentEscapesIncludingReserved; @end @@ -426,13 +471,6 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s return result == NSOrderedSame ? value : result; } -- (NSString *) stringByCachingURLWithCurrentCDN { - return [self - stringByReplacingOccurrencesOfString:@"://cydia.saurik.com/" - withString:@"://cache.cydia.saurik.com/" - ]; -} - - (NSString *) stringByAddingPercentEscapesIncludingReserved { return [(id)CFURLCreateStringByAddingPercentEscapes( kCFAllocatorDefault, @@ -632,7 +670,7 @@ class CYColor { /* }}} */ /* Random Global Variables {{{ */ -static const int PulseInterval_ = 50000; +static int PulseInterval_ = 500000; static const NSString *UI_; @@ -648,6 +686,7 @@ static bool Queuing_; static CYColor Blue_; static CYColor Blueish_; static CYColor Black_; +static CYColor Folder_; static CYColor Off_; static CYColor White_; static CYColor Gray_; @@ -666,20 +705,20 @@ static BOOL Ignored_; static _H Font12_; static _H Font12Bold_; static _H Font14_; +static _H Font18_; static _H Font18Bold_; static _H Font22Bold_; static const char *Machine_ = NULL; -static NSString *System_ = nil; +static _H System_; static NSString *SerialNumber_ = nil; static NSString *ChipID_ = nil; static NSString *BBSNum_ = nil; static _H Token_; -static NSString *UniqueID_ = nil; -static NSString *PLMN_ = nil; -static NSString *Build_ = nil; -static NSString *Product_ = nil; -static NSString *Safari_ = nil; +static _H UniqueID_; +static _H UserAgent_; +static _H Product_; +static _H Safari_; static CFLocaleRef Locale_; static NSArray *Languages_; @@ -688,21 +727,27 @@ static CGColorSpaceRef space_; static NSDictionary *SectionMap_; static NSMutableDictionary *Metadata_; static _transient NSMutableDictionary *Settings_; -static _transient NSString *Role_; static _transient NSMutableDictionary *Packages_; +static _transient NSMutableDictionary *Values_; static _transient NSMutableDictionary *Sections_; -static _transient NSMutableDictionary *Sources_; -static bool Changed_; +_H Sources_; +static _transient NSNumber *Version_; +bool Changed_; static time_t now_; bool IsWildcat_; static CGFloat ScreenScale_; static NSString *Idiom_; +static _H Firmware_; +static NSString *Major_; static _H SessionData_; static _H HostConfig_; static _H BridgedHosts_; +static _H TokenHosts_; +static _H InsecureHosts_; static _H PipelinedHosts_; +static _H CachedURLs_; static NSString *kCydiaProgressEventTypeError = @"Error"; static NSString *kCydiaProgressEventTypeInformation = @"Information"; @@ -818,7 +863,19 @@ static NSString *CYHex(NSData *data, bool reverse = false) { @class CYPackageController; +@protocol SourceDelegate +- (void) setFetch:(NSNumber *)fetch; +@end + +@protocol FetchDelegate +- (bool) isSourceCancelled; +- (void) startSourceFetch:(NSString *)uri; +- (void) stopSourceFetch:(NSString *)uri; +@end + @protocol CydiaDelegate +- (void) returnToCydia; +- (void) saveState; - (void) retainNetworkActivityIndicator; - (void) releaseNetworkActivityIndicator; - (void) clearPackage:(Package *)package; @@ -827,48 +884,73 @@ static NSString *CYHex(NSData *data, bool reverse = false) { - (void) removePackage:(Package *)package; - (void) beginUpdate; - (BOOL) updating; +- (bool) requestUpdate; - (void) distUpgrade; - (void) loadData; - (void) updateData; +- (void) _saveConfig; - (void) syncData; +- (void) addSource:(NSDictionary *)source; - (void) addTrivialSource:(NSString *)href; -- (void) showSettings; - (UIProgressHUD *) addProgressHUD; - (void) removeProgressHUD:(UIProgressHUD *)hud; -- (CyteViewController *) pageForPackage:(NSString *)name; - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item; - (void) reloadDataWithInvocation:(NSInvocation *)invocation; @end /* }}} */ -/* Status Delegation {{{ */ -class Status : +/* CancelStatus {{{ */ +class CancelStatus : public pkgAcquireStatus { private: - _transient NSObject *delegate_; bool cancelled_; public: - Status() : - delegate_(nil), + CancelStatus() : cancelled_(false) { } - void setDelegate(NSObject *delegate) { - delegate_ = delegate; + virtual bool MediaChange(std::string media, std::string drive) { + return false; } - NSObject *getDelegate() const { - return delegate_; + virtual void IMSHit(pkgAcquire::ItemDesc &item) { + Done(item); } - virtual bool MediaChange(std::string media, std::string drive) { - return false; + virtual bool Pulse_(pkgAcquire *Owner) = 0; + + virtual bool Pulse(pkgAcquire *Owner) { + if (pkgAcquireStatus::Pulse(Owner) && Pulse_(Owner)) + return true; + else { + cancelled_ = true; + return false; + } } - virtual void IMSHit(pkgAcquire::ItemDesc &item) { + _finline bool WasCancelled() const { + return cancelled_; + } +}; +/* }}} */ +/* DelegateStatus {{{ */ +class CydiaStatus : + public CancelStatus +{ + private: + _transient NSObject *delegate_; + + public: + CydiaStatus() : + delegate_(nil) + { + } + + void setDelegate(NSObject *delegate) { + delegate_ = delegate; } virtual void Fetch(pkgAcquire::ItemDesc &item) { @@ -878,6 +960,9 @@ class Status : } virtual void Done(pkgAcquire::ItemDesc &item) { + NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]); + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithFormat:Colon_, UCLocalize("DONE"), name] ofType:kCydiaProgressEventTypeStatus forItem:item]); + [delegate_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } virtual void Fail(pkgAcquire::ItemDesc &item) { @@ -895,9 +980,7 @@ class Status : [delegate_ performSelectorOnMainThread:@selector(addProgressEvent:) withObject:event waitUntilDone:YES]; } - virtual bool Pulse(pkgAcquire *Owner) { - bool value = pkgAcquireStatus::Pulse(Owner); - + virtual bool Pulse_(pkgAcquire *Owner) { double percent( double(CurrentBytes + CurrentItems) / double(TotalBytes + TotalItems) @@ -911,16 +994,7 @@ class Status : [NSNumber numberWithDouble:CurrentCPS], @"Speed", nil] waitUntilDone:YES]; - if (value && ![delegate_ isProgressCancelled]) - return true; - else { - cancelled_ = true; - return false; - } - } - - _finline bool WasCancelled() const { - return cancelled_; + return ![delegate_ isProgressCancelled]; } virtual void Start() { @@ -961,7 +1035,7 @@ typedef std::map< unsigned long, _H > SourceMap; _transient NSObject *delegate_; _transient NSObject *progress_; - Status status_; + CydiaStatus status_; int cydiafd_; int statusfd_; @@ -998,7 +1072,7 @@ typedef std::map< unsigned long, _H > SourceMap; - (bool) upgrade; - (void) update; -- (void) updateWithStatus:(Status &)status; +- (void) updateWithStatus:(CancelStatus &)status; - (void) setDelegate:(NSObject *)delegate; @@ -1006,11 +1080,61 @@ typedef std::map< unsigned long, _H > SourceMap; - (NSObject *) progressDelegate; - (Source *) getSource:(pkgCache::PkgFileIterator)file; +- (void) setFetch:(bool)fetch forURI:(const char *)uri; +- (void) resetFetch; - (NSString *) mappedSectionForPointer:(const char *)pointer; @end /* }}} */ +/* SourceStatus {{{ */ +class SourceStatus : + public CancelStatus +{ + private: + _transient NSObject *delegate_; + _transient Database *database_; + + public: + SourceStatus(NSObject *delegate, Database *database) : + delegate_(delegate), + database_(database) + { + } + + void Set(bool fetch, pkgAcquire::ItemDesc &desc) { + desc.Owner->ID = 0; + [database_ setFetch:fetch forURI:desc.Owner->DescURI().c_str()]; + } + + virtual void Fetch(pkgAcquire::ItemDesc &desc) { + Set(true, desc); + } + + virtual void Done(pkgAcquire::ItemDesc &desc) { + Set(false, desc); + } + + virtual void Fail(pkgAcquire::ItemDesc &desc) { + Set(false, desc); + } + + virtual bool Pulse_(pkgAcquire *Owner) { + for (pkgAcquire::ItemCIterator item = Owner->ItemsBegin(); item != Owner->ItemsEnd(); ++item) + if ((*item)->ID != 0); + else if ((*item)->Status == pkgAcquire::Item::StatIdle) { + (*item)->ID = 1; + [database_ setFetch:true forURI:(*item)->DescURI().c_str()]; + } else (*item)->ID = 0; + return ![delegate_ isSourceCancelled]; + } + + virtual void Stop() { + pkgAcquireStatus::Stop(); + [database_ resetFetch]; + } +}; +/* }}} */ /* ProgressEvent Implementation {{{ */ @implementation CydiaProgressEvent @@ -1252,6 +1376,10 @@ static void PackageImport(const void *key, const void *value, void *context) { /* Source Class {{{ */ @interface Source : NSObject { + unsigned era_; + Database *database_; + metaIndex *index_; + CYString depiction_; CYString description_; CYString label_; @@ -1261,6 +1389,7 @@ static void PackageImport(const void *key, const void *value, void *context) { CYString uri_; CYString distribution_; CYString type_; + CYString base_; CYString version_; _H host_; @@ -1268,23 +1397,29 @@ static void PackageImport(const void *key, const void *value, void *context) { CYString defaultIcon_; - _H record_; + _H record_; BOOL trusted_; + + std::set fetches_; + std::set files_; + _transient NSObject *delegate_; } -- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool; +- (Source *) initWithMetaIndex:(metaIndex *)index forDatabase:(Database *)database inPool:(apr_pool_t *)pool; -- (NSComparisonResult) compareByNameAndType:(Source *)source; +- (NSComparisonResult) compareByName:(Source *)source; - (NSString *) depictionForPackage:(NSString *)package; - (NSString *) supportForPackage:(NSString *)package; +- (metaIndex *) metaIndex; - (NSDictionary *) record; - (BOOL) trusted; -- (NSString *) uri; +- (NSString *) rooturi; - (NSString *) distribution; - (NSString *) type; + - (NSString *) key; - (NSString *) host; @@ -1295,41 +1430,48 @@ static void PackageImport(const void *key, const void *value, void *context) { - (NSString *) version; - (NSString *) defaultIcon; +- (NSURL *) iconURL; + +- (void) setFetch:(bool)fetch forURI:(const char *)uri; +- (void) resetFetch; @end @implementation Source -- (void) _clear { - uri_.clear(); - distribution_.clear(); - type_.clear(); - - description_.clear(); - label_.clear(); - origin_.clear(); - depiction_.clear(); - support_.clear(); - version_.clear(); - defaultIcon_.clear(); ++ (NSString *) webScriptNameForSelector:(SEL)selector { + if (false); + else if (selector == @selector(addSection:)) + return @"addSection"; + else if (selector == @selector(getField:)) + return @"getField"; + else if (selector == @selector(removeSection:)) + return @"removeSection"; + else if (selector == @selector(remove)) + return @"remove"; + else + return nil; +} - record_ = nil; - host_ = nil; - authority_ = nil; ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { + return [self webScriptNameForSelector:selector] == nil; } + (NSArray *) _attributeKeys { return [NSArray arrayWithObjects: + @"baseuri", @"distribution", @"host", @"key", + @"iconuri", @"label", @"name", @"origin", + @"rooturi", + @"sections", @"shortDescription", @"trusted", @"type", - @"uri", @"version", nil]; } @@ -1342,9 +1484,11 @@ static void PackageImport(const void *key, const void *value, void *context) { return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; } -- (void) setMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool { - [self _clear]; +- (metaIndex *) metaIndex { + return index_; +} +- (void) setMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool { trusted_ = index->IsTrusted(); uri_.set(pool, index->GetURI()); @@ -1353,6 +1497,22 @@ static void PackageImport(const void *key, const void *value, void *context) { debReleaseIndex *dindex(dynamic_cast(index)); if (dindex != NULL) { + std::string file(dindex->MetaIndexURI("")); + base_.set(pool, file); + + pkgAcquire acquire; + dindex->GetIndexes(&acquire, true); + for (pkgAcquire::ItemIterator item(acquire.ItemsBegin()); item != acquire.ItemsEnd(); item++) { + std::string file((*item)->DescURI()); + files_.insert(file); + if (file.length() < sizeof("Packages.bz2") || file.substr(file.length() - sizeof("Packages.bz2")) != "/Packages.bz2") + continue; + file = file.substr(0, file.length() - 4); + files_.insert(file); + files_.insert(file + ".gz"); + files_.insert(file + "Index"); + } + FileFd fd; if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly)) _error->Discard(); @@ -1400,19 +1560,44 @@ static void PackageImport(const void *key, const void *value, void *context) { authority_ = [url path]; } -- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool { +- (Source *) initWithMetaIndex:(metaIndex *)index forDatabase:(Database *)database inPool:(apr_pool_t *)pool { if ((self = [super init]) != nil) { + era_ = [database era]; + database_ = database; + index_ = index; + [self setMetaIndex:index inPool:pool]; } return self; } -- (NSComparisonResult) compareByNameAndType:(Source *)source { - NSDictionary *lhr = [self record]; - NSDictionary *rhr = [source record]; +- (NSString *) getField:(NSString *)name { +@synchronized (database_) { + if ([database_ era] != era_ || index_ == NULL) + return nil; + + debReleaseIndex *dindex(dynamic_cast(index_)); + if (dindex == NULL) + return nil; + + FileFd fd; + if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly)) { + _error->Discard(); + return nil; + } + + pkgTagFile tags(&fd); + + pkgTagSection section; + tags.Step(section); + + const char *start, *end; + if (!section.Find([name UTF8String], start, end)) + return (NSString *) [NSNull null]; - if (lhr != rhr) - return lhr == nil ? NSOrderedDescending : NSOrderedAscending; + return [NSString stringWithString:[(NSString *) CYStringCreate(start, end - start) autorelease]]; +} } +- (NSComparisonResult) compareByName:(Source *)source { NSString *lhs = [self name]; NSString *rhs = [source name]; @@ -1437,6 +1622,62 @@ static void PackageImport(const void *key, const void *value, void *context) { return support_.empty() ? nil : [static_cast(support_) stringByReplacingOccurrencesOfString:@"*" withString:package]; } +- (NSArray *) sections { + return record_ == nil ? (id) [NSNull null] : [record_ objectForKey:@"Sections"] ?: [NSArray array]; +} + +- (void) _addSection:(NSString *)section { + if (record_ == nil) + return; + else if (NSMutableArray *sections = [record_ objectForKey:@"Sections"]) { + if (![sections containsObject:section]) { + [sections addObject:section]; + Changed_ = true; + } + } else { + [record_ setObject:[NSMutableArray arrayWithObject:section] forKey:@"Sections"]; + Changed_ = true; + } +} + +- (bool) addSection:(NSString *)section { + if (record_ == nil) + return false; + + [self performSelectorOnMainThread:@selector(_addSection:) withObject:section waitUntilDone:NO]; + return true; +} + +- (void) _removeSection:(NSString *)section { + if (record_ == nil) + return; + + if (NSMutableArray *sections = [record_ objectForKey:@"Sections"]) + if ([sections containsObject:section]) { + [sections removeObject:section]; + Changed_ = true; + } +} + +- (bool) removeSection:(NSString *)section { + if (record_ == nil) + return false; + + [self performSelectorOnMainThread:@selector(_removeSection:) withObject:section waitUntilDone:NO]; + return true; +} + +- (void) _remove { + [Sources_ removeObjectForKey:[self key]]; + Changed_ = true; +} + +- (bool) remove { + bool value(record_ != nil); + [self performSelectorOnMainThread:@selector(_remove) withObject:nil waitUntilDone:NO]; + return value; +} + - (NSDictionary *) record { return record_; } @@ -1445,7 +1686,7 @@ static void PackageImport(const void *key, const void *value, void *context) { return trusted_; } -- (NSString *) uri { +- (NSString *) rooturi { return uri_; } @@ -1457,6 +1698,23 @@ static void PackageImport(const void *key, const void *value, void *context) { return type_; } +- (NSString *) baseuri { + return base_.empty() ? nil : (id) base_; +} + +- (NSString *) iconuri { + if (NSString *base = [self baseuri]) + return [base stringByAppendingString:@"CydiaIcon.png"]; + + return nil; +} + +- (NSURL *) iconURL { + if (NSString *uri = [self iconuri]) + return [NSURL URLWithString:uri]; + return nil; +} + - (NSString *) key { return [NSString stringWithFormat:@"%@:%@:%@", (NSString *) type_, (NSString *) uri_, (NSString *) distribution_]; } @@ -1489,6 +1747,31 @@ static void PackageImport(const void *key, const void *value, void *context) { return defaultIcon_; } +- (void) setDelegate:(NSObject *)delegate { + delegate_ = delegate; +} + +- (bool) fetch { + return !fetches_.empty(); +} + +- (void) setFetch:(bool)fetch forURI:(const char *)uri { + if (!fetch) { + if (fetches_.erase(uri) == 0) + return; + } else if (files_.find(uri) == files_.end()) + return; + else if (!fetches_.insert(uri).second) + return; + + [delegate_ performSelectorOnMainThread:@selector(setFetch:) withObject:[NSNumber numberWithBool:[self fetch]] waitUntilDone:NO]; +} + +- (void) resetFetch { + fetches_.clear(); + [delegate_ performSelectorOnMainThread:@selector(setFetch:) withObject:[NSNumber numberWithBool:NO] waitUntilDone:NO]; +} + @end /* }}} */ /* CydiaOperation Class {{{ */ @@ -1649,29 +1932,31 @@ static void PackageImport(const void *key, const void *value, void *context) { /* }}} */ /* Package Class {{{ */ struct ParsedPackage { + CYString md5sum_; CYString tagline_; + CYString architecture_; CYString icon_; CYString depiction_; CYString homepage_; - - CYString sponsor_; CYString author_; - CYString bugs_; CYString support_; }; @interface Package : NSObject { - uint32_t era_ : 26; + uint32_t era_ : 25; uint32_t role_ : 3; uint32_t essential_ : 1; uint32_t obsolete_ : 1; uint32_t ignored_ : 1; + uint32_t pooled_ : 1; apr_pool_t *pool_; + uint32_t rank_; + _transient Database *database_; pkgCache::VerIterator version_; @@ -1755,9 +2040,9 @@ struct ParsedPackage { - (Source *) source; -- (BOOL) matches:(NSString *)text; +- (uint32_t) rank; +- (BOOL) matches:(NSArray *)query; -- (bool) hasSupportingRole; - (BOOL) hasTag:(NSString *)tag; - (NSString *) primaryPurpose; - (NSArray *) purposes; @@ -1772,10 +2057,10 @@ struct ParsedPackage { - (void) install; - (void) remove; -- (bool) isUnfilteredAndSearchedForBy:(NSString *)search; +- (bool) isUnfilteredAndSearchedForBy:(NSArray *)query; - (bool) isUnfilteredAndSelectedForBy:(NSString *)search; - (bool) isInstalledAndUnfiltered:(NSNumber *)number; -- (bool) isVisibleInSection:(NSString *)section; +- (bool) isVisibleInSection:(NSString *)section source:(Source *)source; - (bool) isVisibleInSource:(Source *)source; @end @@ -1871,9 +2156,9 @@ CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) { CFStringRef lhn(lhi), rhn(rhi); if (lhn == NULL) - return rhn == NULL ? NSOrderedSame : NSOrderedAscending; + return rhn == NULL ? kCFCompareEqualTo : kCFCompareLessThan; else if (rhn == NULL) - return NSOrderedDescending; + return kCFCompareGreaterThan; _profile(PackageNameCompare$NumbersLast) if (!lhi.empty() && !rhi.empty()) { @@ -1881,7 +2166,7 @@ CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) { UniChar rhc(CFStringGetCharacterAtIndex(rhn, 0)); bool lha(CFUniCharIsMemberOf(lhc, kCFUniCharLetterCharacterSet)); if (lha != CFUniCharIsMemberOf(rhc, kCFUniCharLetterCharacterSet)) - return lha ? NSOrderedAscending : NSOrderedDescending; + return lha ? kCFCompareLessThan : kCFCompareGreaterThan; } _end @@ -1901,7 +2186,7 @@ struct PackageNameOrdering : std::binary_function { _finline bool operator ()(Package *lhs, Package *rhs) const { - return PackageNameCompare(lhs, rhs, NULL) == NSOrderedAscending; + return PackageNameCompare(lhs, rhs, NULL) == kCFCompareLessThan; } }; @@ -1912,6 +2197,8 @@ struct PackageNameOrdering : } - (void) dealloc { + if (!pooled_) + apr_pool_destroy(pool_); if (parsed_ != NULL) delete parsed_; [super dealloc]; @@ -1923,6 +2210,8 @@ struct PackageNameOrdering : return @"clear"; else if (selector == @selector(getField:)) return @"getField"; + else if (selector == @selector(getRecord)) + return @"getRecord"; else if (selector == @selector(hasTag:)) return @"hasTag"; else if (selector == @selector(install)) @@ -1940,6 +2229,7 @@ struct PackageNameOrdering : + (NSArray *) _attributeKeys { return [NSArray arrayWithObjects: @"applications", + @"architecture", @"author", @"depiction", @"essential", @@ -1951,6 +2241,7 @@ struct PackageNameOrdering : @"longDescription", @"longSection", @"maintainer", + @"md5sum", @"mode", @"name", @"purposes", @@ -1962,7 +2253,6 @@ struct PackageNameOrdering : @"simpleSection", @"size", @"source", - @"sponsor", @"state", @"support", @"tags", @@ -1986,6 +2276,12 @@ struct PackageNameOrdering : return relations; } } +- (NSString *) architecture { + [self parse]; +@synchronized (database_) { + return parsed_->architecture_.empty() ? [NSNull null] : (id) parsed_->architecture_; +} } + - (NSString *) getField:(NSString *)name { @synchronized (database_) { if ([database_ era] != era_ || file_.end()) @@ -1997,7 +2293,20 @@ struct PackageNameOrdering : if (!parser.Find([name UTF8String], start, end)) return (NSString *) [NSNull null]; - return [(NSString *) CYStringCreate(start, end - start) autorelease]; + return [NSString stringWithString:[(NSString *) CYStringCreate(start, end - start) autorelease]]; +} } + +- (NSString *) getRecord { +@synchronized (database_) { + if ([database_ era] != era_ || file_.end()) + return nil; + + pkgRecords::Parser &parser([database_ records]->Lookup(file_)); + + const char *start, *end; + parser.GetRec(start, end); + + return [NSString stringWithString:[(NSString *) CYStringCreate(start, end - start) autorelease]]; } } - (void) parse { @@ -2017,6 +2326,7 @@ struct PackageNameOrdering : parser = &[database_ records]->Lookup(file_); _end + CYString bugs; CYString website; _profile(Package$parse$Find) @@ -2024,14 +2334,15 @@ struct PackageNameOrdering : const char *name_; CYString *value_; } names[] = { + {"architecture", &parsed->architecture_}, {"icon", &parsed->icon_}, {"depiction", &parsed->depiction_}, {"homepage", &parsed->homepage_}, {"website", &website}, - {"bugs", &parsed->bugs_}, + {"bugs", &bugs}, {"support", &parsed->support_}, - {"sponsor", &parsed->sponsor_}, {"author", &parsed->author_}, + {"md5sum", &parsed->md5sum_}, }; for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) { @@ -2063,6 +2374,8 @@ struct PackageNameOrdering : parsed->homepage_ = website; if (parsed->homepage_ == parsed->depiction_) parsed->homepage_.clear(); + if (parsed->support_.empty()) + parsed->support_ = bugs; _end _end } } @@ -2070,7 +2383,12 @@ struct PackageNameOrdering : - (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database { if ((self = [super init]) != nil) { _profile(Package$initWithVersion) - pool_ = pool; + if (pool == NULL) + apr_pool_create(&pool_, NULL); + else { + pool_ = pool; + pooled_ = true; + } database_ = database; era_ = [database era]; @@ -2103,9 +2421,14 @@ struct PackageNameOrdering : pkgCache::TagIterator tag(iterator.TagList()); if (!tag.end()) { tags_ = [NSMutableArray arrayWithCapacity:8]; - do { + + goto tag; for (; !tag.end(); ++tag) tag: { const char *name(tag.Name()); - [tags_ addObject:[(NSString *)CYStringCreate(name) autorelease]]; + NSString *string((NSString *) CYStringCreate(name)); + if (string == nil) + continue; + + [tags_ addObject:[string autorelease]]; if (role_ == 0 && strncmp(name, "role::", 6) == 0 /*&& strcmp(name, "role::leaper") != 0*/) { if (strcmp(name + 6, "enduser") == 0) @@ -2126,9 +2449,7 @@ struct PackageNameOrdering : else if (strcmp(name + 7, "obsolete") == 0) obsolete_ = true; } - - ++tag; - } while (!tag.end()); + } } _end @@ -2166,7 +2487,7 @@ struct PackageNameOrdering : _end _profile(Package$initWithVersion$Section) - section_ = iterator.Section(); + section_ = version_.Section(); _end _profile(Package$initWithVersion$Flags) @@ -2261,6 +2582,10 @@ struct PackageNameOrdering : return maintainer.empty() ? nil : [MIMEAddress addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]]; } } +- (NSString *) md5sum { + return parsed_ == NULL ? nil : (id) parsed_->md5sum_; +} + - (size_t) size { @synchronized (database_) { if ([database_ era] != era_ || version_.end()) @@ -2292,8 +2617,29 @@ struct PackageNameOrdering : } } - (NSString *) shortDescription { - return parsed_ == NULL ? nil : static_cast(parsed_->tagline_); -} + if (parsed_ != NULL) + return static_cast(parsed_->tagline_); + +@synchronized (database_) { + pkgRecords::Parser &parser([database_ records]->Lookup(file_)); + + const char *start, *end; + if (!parser.ShortDesc(start, end)) + return nil; + + if (end - start > 200) + end = start + 200; + + /* + if (const char *stop = reinterpret_cast(memchr(start, '\n', end - start))) + end = stop; + + while (end != start && end[-1] == '\r') + --end; + */ + + return [(id) CYStringCreate(start, end - start) autorelease]; +} } - (unichar) index { _profile(Package$index) @@ -2372,8 +2718,8 @@ struct PackageNameOrdering : return false; _end - _profile(Package$unfiltered$hasSupportingRole) - if (_unlikely(![self hasSupportingRole])) + _profile(Package$unfiltered$role) + if (_unlikely(role_ > 3)) return false; _end @@ -2412,11 +2758,19 @@ struct PackageNameOrdering : } - (BOOL) hasMode { +@synchronized (database_) { + if ([database_ era] != era_ || iterator_.end()) + return NO; + pkgDepCache::StateCache &state([database_ cache][iterator_]); return state.Mode != pkgDepCache::ModeKeep; -} +} } - (NSString *) mode { +@synchronized (database_) { + if ([database_ era] != era_ || iterator_.end()) + return nil; + pkgDepCache::StateCache &state([database_ cache][iterator_]); switch (state.Mode) { @@ -2448,7 +2802,7 @@ struct PackageNameOrdering : } _nodefault } -} +} } - (NSString *) id { return id_; @@ -2465,14 +2819,12 @@ struct PackageNameOrdering : if (parsed_ != NULL) if (NSString *href = parsed_->icon_) if ([href hasPrefix:@"file:///"]) - // XXX: correct escaping - icon = [UIImage imageAtPath:[href substringFromIndex:7]]; + icon = [UIImage imageAtPath:[[href substringFromIndex:7] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; if (icon == nil) if (section != nil) - icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]; + icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, [section stringByReplacingOccurrencesOfString:@" " withString:@"_"]]]; if (icon == nil) if (Source *source = [self source]) if (NSString *dicon = [source defaultIcon]) if ([dicon hasPrefix:@"file:///"]) - // XXX: correct escaping - icon = [UIImage imageAtPath:[dicon substringFromIndex:7]]; + icon = [UIImage imageAtPath:[[dicon substringFromIndex:7] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; if (icon == nil) icon = [UIImage applicationImageNamed:@"unknown.png"]; return icon; @@ -2486,16 +2838,12 @@ struct PackageNameOrdering : return parsed_ != NULL && !parsed_->depiction_.empty() ? parsed_->depiction_ : [[self source] depictionForPackage:id_]; } -- (MIMEAddress *) sponsor { - return parsed_ == NULL || parsed_->sponsor_.empty() ? nil : [MIMEAddress addressWithString:parsed_->sponsor_]; -} - - (MIMEAddress *) author { return parsed_ == NULL || parsed_->author_.empty() ? nil : [MIMEAddress addressWithString:parsed_->author_]; } - (NSString *) support { - return parsed_ != NULL && !parsed_->bugs_.empty() ? parsed_->bugs_ : [[self source] supportForPackage:id_]; + return parsed_ != NULL && !parsed_->support_.empty() ? parsed_->support_ : [[self source] supportForPackage:id_]; } - (NSArray *) files { @@ -2631,7 +2979,8 @@ struct PackageNameOrdering : NSString *bundle([file stringByDeletingLastPathComponent]); NSString *icon([info objectForKey:@"CFBundleIconFile"]); - if (icon == nil || [icon length] == 0) + // XXX: maybe this should check if this is really a string, not just for length + if (icon == nil || ![icon respondsToSelector:@selector(length)] || [icon length] == 0) icon = @"icon.png"; NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]); @@ -2659,48 +3008,51 @@ struct PackageNameOrdering : return source_ == (Source *) [NSNull null] ? nil : source_; } -- (BOOL) matches:(NSString *)text { - if (text == nil) +- (uint32_t) rank { + return rank_; +} + +- (BOOL) matches:(NSArray *)query { + if (query == nil || [query count] == 0) return NO; + rank_ = 0; + + NSString *string; NSRange range; + NSUInteger length; - range = [[self id] rangeOfString:text options:MatchCompareOptions_]; - if (range.location != NSNotFound) - return YES; + string = [self name]; + length = [string length]; - range = [[self name] rangeOfString:text options:MatchCompareOptions_]; - if (range.location != NSNotFound) - return YES; + for (NSString *term in query) { + range = [string rangeOfString:term options:MatchCompareOptions_]; + if (range.location != NSNotFound) + rank_ -= 6 * 1000000 / length; + } - [self parse]; + if (rank_ == 0) { + string = [self id]; + length = [string length]; - NSString *description([self shortDescription]); - NSUInteger length([description length]); + for (NSString *term in query) { + range = [string rangeOfString:term options:MatchCompareOptions_]; + if (range.location != NSNotFound) + rank_ -= 6 * 1000000 / length; + } + } - range = [[self shortDescription] rangeOfString:text options:MatchCompareOptions_ range:NSMakeRange(0, std::min(length, 100))]; - if (range.location != NSNotFound) - return YES; + string = [self shortDescription]; + length = [string length]; + NSUInteger stop(std::min(length, 200)); - return NO; -} + for (NSString *term in query) { + range = [string rangeOfString:term options:MatchCompareOptions_ range:NSMakeRange(0, stop)]; + if (range.location != NSNotFound) + rank_ -= 2 * 100000; + } -- (bool) hasSupportingRole { - if (role_ == 0) - return true; - if (role_ == 1) - return true; - if ([Role_ isEqualToString:@"User"]) - return false; - if (role_ == 2) - return true; - if ([Role_ isEqualToString:@"Hacker"]) - return false; - if (role_ == 3) - return true; - if ([Role_ isEqualToString:@"Developer"]) - return false; - _assert(false); + return rank_ != 0; } - (NSArray *) tags { @@ -2786,7 +3138,7 @@ struct PackageNameOrdering : cache->MarkDelete(iterator_, true); } } -- (bool) isUnfilteredAndSearchedForBy:(NSString *)search { +- (bool) isUnfilteredAndSearchedForBy:(NSArray *)query { _profile(Package$isUnfilteredAndSearchedForBy) bool value(true); @@ -2795,7 +3147,7 @@ struct PackageNameOrdering : _end _profile(Package$isUnfilteredAndSearchedForBy$Match) - value &= [self matches:search]; + value &= [self matches:query]; _end return value; @@ -2822,16 +3174,19 @@ struct PackageNameOrdering : } - (bool) isInstalledAndUnfiltered:(NSNumber *)number { - return ![self uninstalled] && (![number boolValue] && role_ != 7 || [self unfiltered]); + return ![self uninstalled] && role_ <= ([number boolValue] ? 1 : 3); } -- (bool) isVisibleInSection:(NSString *)name { +- (bool) isVisibleInSection:(NSString *)name source:(Source *)source { NSString *section([self section]); return ( name == nil || section == nil && [name length] == 0 || [name isEqualToString:section] + ) && ( + source == nil || + [self source] == source ) && [self visible]; } @@ -2953,11 +3308,6 @@ struct PackageNameOrdering : @end /* }}} */ -static NSString *Colon_; -static NSString *Elision_; -static NSString *Error_; -static NSString *Warning_; - class CydiaLogCleaner : public pkgArchiveCleaner { @@ -3103,11 +3453,13 @@ class CydiaLogCleaner : } - (Package *) packageWithName:(NSString *)name { + if (name == nil) + return nil; @synchronized (self) { if (static_cast(cache_) == NULL) return nil; pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String])); - return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:pool_ database:self]; + return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:NULL database:self]; } } - (id) init { @@ -3228,6 +3580,10 @@ class CydiaLogCleaner : lprintf("%c:[%s]\n", warning ? 'W' : 'E', error.c_str()); + static Pcre no_pubkey("^GPG error:.* NO_PUBKEY .*$"); + if (warning && no_pubkey(error.c_str())) + continue; + [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:error.c_str()] ofType:(warning ? kCydiaProgressEventTypeWarning : kCydiaProgressEventTypeError)] forTask:title]; } @@ -3279,29 +3635,47 @@ class CydiaLogCleaner : NSString *title(UCLocalize("DATABASE")); + list_ = new pkgSourceList(); + if ([self popErrorWithTitle:title forOperation:list_->ReadMainList()]) + return; + + for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) { + Source *object([[[Source alloc] initWithMetaIndex:*source forDatabase:self inPool:pool_] autorelease]); + [sourceList_ addObject:object]; + } + _trace(); OpProgress progress; - while (!cache_.Open(progress, true)) { pop: - std::string error; - bool warning(!_error->PopMessage(error)); - lprintf("cache_.Open():[%s]\n", error.c_str()); - - if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ") - [delegate_ repairWithSelector:@selector(configure)]; - else if (error == "The package lists or status file could not be parsed or opened.") - [delegate_ repairWithSelector:@selector(update)]; - // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)") - // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)") - // else if (error == "Malformed Status line") - // else if (error == "The list of sources could not be read.") - else { + open: + if (!cache_.Open(progress, true)) { + // XXX: what if there are errors, but Open() == true? this should be merged with popError: + while (!_error->empty()) { + std::string error; + bool warning(!_error->PopMessage(error)); + + lprintf("cache_.Open():[%s]\n", error.c_str()); + [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:error.c_str()] ofType:(warning ? kCydiaProgressEventTypeWarning : kCydiaProgressEventTypeError)] forTask:title]; - return; + + SEL repair(NULL); + if (false); + else if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ") + repair = @selector(configure); + //else if (error == "The package lists or status file could not be parsed or opened.") + // repair = @selector(update); + // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)") + // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)") + // else if (error == "Malformed Status line") + // else if (error == "The list of sources could not be read.") + + if (repair != NULL) { + _error->Discard(); + [delegate_ repairWithSelector:repair]; + goto open; + } } - if (warning) - goto pop; - _error->Discard(); + return; } _trace(); @@ -3315,10 +3689,6 @@ class CydiaLogCleaner : fetcher_ = new pkgAcquire(&status_); lock_ = NULL; - list_ = new pkgSourceList(); - if ([self popErrorWithTitle:title forOperation:list_->ReadMainList()]) - return; - if (cache_->DelCount() != 0 || cache_->InstCount() != 0) { [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:UCLocalize("COUNTS_NONZERO_EX") ofType:kCydiaProgressEventTypeError] forTask:title]; return; @@ -3340,11 +3710,9 @@ class CydiaLogCleaner : return; } - for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) { - Source *object([[[Source alloc] initWithMetaIndex:*source inPool:pool_] autorelease]); - [sourceList_ addObject:object]; - - std::vector *indices = (*source)->GetIndexFiles(); + for (Source *object in (id) sourceList_) { + metaIndex *source([object metaIndex]); + std::vector *indices = source->GetIndexFiles(); for (std::vector::const_iterator index = indices->begin(); index != indices->end(); ++index) // XXX: this could be more intelligent if (dynamic_cast(*index) != NULL) { @@ -3426,6 +3794,7 @@ class CydiaLogCleaner : } - (bool) clean { +@synchronized (self) { // XXX: I don't remember this condition if (lock_ != NULL) return false; @@ -3446,7 +3815,7 @@ class CydiaLogCleaner : return false; return true; -} +} } - (bool) prepare { fetcher_->Shutdown(); @@ -3501,7 +3870,14 @@ class CydiaLogCleaner : if ((*item)->Status == pkgAcquire::Item::StatIdle) continue; + std::string uri = (*item)->DescURI(); + std::string error = (*item)->ErrorText; + + lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str()); failed = true; + + CydiaProgressEvent *event([CydiaProgressEvent eventWithMessage:[NSString stringWithUTF8String:error.c_str()] ofType:kCydiaProgressEventTypeError]); + [delegate_ addProgressEventOnMainThread:event forTask:title]; } [delegate_ performSelectorOnMainThread:@selector(releaseNetworkActivityIndicator) withObject:nil waitUntilDone:YES]; @@ -3516,11 +3892,8 @@ class CydiaLogCleaner : _system->UnLock(); pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_); - - if (_error->PendingError()) { - _trace(); + if ([self popErrorWithTitle:title]) return; - } if (result == pkgPackageManager::Failed) { _trace(); @@ -3555,7 +3928,7 @@ class CydiaLogCleaner : [self updateWithStatus:status_]; } -- (void) updateWithStatus:(Status &)status { +- (void) updateWithStatus:(CancelStatus &)status { NSString *title(UCLocalize("REFRESHING_DATA")); pkgSourceList list; @@ -3599,6 +3972,16 @@ class CydiaLogCleaner : return i == sourceMap_.end() ? nil : i->second; } +- (void) setFetch:(bool)fetch forURI:(const char *)uri { + for (Source *source in (id) sourceList_) + [source setFetch:fetch forURI:uri]; +} + +- (void) resetFetch { + for (Source *source in (id) sourceList_) + [source resetFetch]; +} + - (NSString *) mappedSectionForPointer:(const char *)section { _H *mapped; @@ -3688,7 +4071,7 @@ static _H Diversions_; @end @interface CydiaObject : NSObject { - _H indirect_; + _H indirect_; _transient id delegate_; } @@ -3696,11 +4079,16 @@ static _H Diversions_; @end +@class CydiaObject; + @interface CydiaWebViewController : CyteWebViewController { _H cydia_; } + (void) addDiversion:(Diversion *)diversion; ++ (NSURLRequest *) requestWithHeaders:(NSURLRequest *)request; ++ (void) didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame withCydia:(CydiaObject *)cydia; +- (void) setDelegate:(id)delegate; @end @@ -3709,7 +4097,7 @@ static _H Diversions_; - (id) initWithDelegate:(IndirectDelegate *)indirect { if ((self = [super init]) != nil) { - indirect_ = indirect; + indirect_ = (CyteWebViewController *) indirect; } return self; } @@ -3720,13 +4108,17 @@ static _H Diversions_; + (NSArray *) _attributeKeys { return [NSArray arrayWithObjects: @"bbsnum", + @"build", + @"coreFoundationVersionNumber", @"device", @"ecid", @"firmware", @"hostname", @"idiom", + @"mcc", + @"mnc", @"model", - @"plmn", + @"operator", @"role", @"serial", @"token", @@ -3743,11 +4135,19 @@ static _H Diversions_; } - (NSString *) version { - return @ Cydia_; + return Cydia_; +} + +- (NSString *) build { + return System_; +} + +- (NSString *) coreFoundationVersionNumber { + return [NSString stringWithFormat:@"%.2f", kCFCoreFoundationVersionNumber]; } - (NSString *) device { - return [[UIDevice currentDevice] uniqueIdentifier]; + return UniqueIdentifier(); } - (NSString *) firmware { @@ -3762,8 +4162,22 @@ static _H Diversions_; return (id) Idiom_ ?: [NSNull null]; } -- (NSString *) plmn { - return (id) PLMN_ ?: [NSNull null]; +- (NSString *) mcc { + if (CFStringRef (*$CTSIMSupportCopyMobileSubscriberCountryCode)(CFAllocatorRef) = reinterpret_cast(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode"))) + return [(NSString *) (*$CTSIMSupportCopyMobileSubscriberCountryCode)(kCFAllocatorDefault) autorelease]; + return nil; +} + +- (NSString *) mnc { + if (CFStringRef (*$CTSIMSupportCopyMobileSubscriberNetworkCode)(CFAllocatorRef) = reinterpret_cast(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberNetworkCode"))) + return [(NSString *) (*$CTSIMSupportCopyMobileSubscriberNetworkCode)(kCFAllocatorDefault) autorelease]; + return nil; +} + +- (NSString *) operator { + if (CFStringRef (*$CTRegistrationCopyOperatorName)(CFAllocatorRef) = reinterpret_cast(dlsym(RTLD_DEFAULT, "CTRegistrationCopyOperatorName"))) + return [(NSString *) (*$CTRegistrationCopyOperatorName)(kCFAllocatorDefault) autorelease]; + return nil; } - (NSString *) bbsnum { @@ -3779,7 +4193,7 @@ static _H Diversions_; } - (NSString *) role { - return (id) Role_ ?: [NSNull null]; + return (id) [NSNull null]; } - (NSString *) model { @@ -3794,10 +4208,16 @@ static _H Diversions_; if (false); else if (selector == @selector(addBridgedHost:)) return @"addBridgedHost"; + else if (selector == @selector(addInsecureHost:)) + return @"addInsecureHost"; else if (selector == @selector(addInternalRedirect::)) return @"addInternalRedirect"; else if (selector == @selector(addPipelinedHost:scheme:)) return @"addPipelinedHost"; + else if (selector == @selector(addSource:::)) + return @"addSource"; + else if (selector == @selector(addTokenHost:)) + return @"addTokenHost"; else if (selector == @selector(addTrivialSource:)) return @"addTrivialSource"; else if (selector == @selector(close)) @@ -3807,7 +4227,9 @@ static _H Diversions_; else if (selector == @selector(stringWithFormat:arguments:)) return @"format"; else if (selector == @selector(getAllSources)) - return @"getAllSourcs"; + return @"getAllSources"; + else if (selector == @selector(getApplicationInfo:value:)) + return @"getApplicationInfoValue"; else if (selector == @selector(getKernelNumber:)) return @"getKernelNumber"; else if (selector == @selector(getKernelString:)) @@ -3822,18 +4244,30 @@ static _H Diversions_; return @"getPreferredLanguages"; else if (selector == @selector(getPackageById:)) return @"getPackageById"; + else if (selector == @selector(getMetadataKeys)) + return @"getMetadataKeys"; + else if (selector == @selector(getMetadataValue:)) + return @"getMetadataValue"; else if (selector == @selector(getSessionValue:)) return @"getSessionValue"; else if (selector == @selector(installPackages:)) return @"installPackages"; + else if (selector == @selector(isReachable:)) + return @"isReachable"; else if (selector == @selector(localizedStringForKey:value:table:)) return @"localize"; else if (selector == @selector(popViewController:)) return @"popViewController"; else if (selector == @selector(refreshSources)) return @"refreshSources"; + else if (selector == @selector(registerFrame:)) + return @"registerFrame"; else if (selector == @selector(removeButton)) return @"removeButton"; + else if (selector == @selector(saveConfig)) + return @"saveConfig"; + else if (selector == @selector(setMetadataValue::)) + return @"setMetadataValue"; else if (selector == @selector(setSessionValue::)) return @"setSessionValue"; else if (selector == @selector(substitutePackageNames:)) @@ -3860,6 +4294,10 @@ static _H Diversions_; return @"setPasteboardString"; else if (selector == @selector(setPasteboardURL:)) return @"setPasteboardURL"; + else if (selector == @selector(setScrollAlwaysBounceVertical:)) + return @"setScrollAlwaysBounceVertical"; + else if (selector == @selector(setScrollIndicatorStyle:)) + return @"setScrollIndicatorStyle"; else if (selector == @selector(setToken:)) return @"setToken"; else if (selector == @selector(setViewportWidth:)) @@ -3868,6 +4306,8 @@ static _H Diversions_; return @"statfs"; else if (selector == @selector(supports:)) return @"supports"; + else if (selector == @selector(unload)) + return @"unload"; else return nil; } @@ -3880,10 +4320,32 @@ static _H Diversions_; return [feature isEqualToString:@"window.open"]; } +- (void) unload { + [delegate_ performSelectorOnMainThread:@selector(unloadData) withObject:nil waitUntilDone:NO]; +} + +- (void) setScrollAlwaysBounceVertical:(NSNumber *)value { + [indirect_ performSelectorOnMainThread:@selector(setScrollAlwaysBounceVerticalNumber:) withObject:value waitUntilDone:NO]; +} + +- (void) setScrollIndicatorStyle:(NSString *)style { + [indirect_ performSelectorOnMainThread:@selector(setScrollIndicatorStyleWithName:) withObject:style waitUntilDone:NO]; +} + - (void) addInternalRedirect:(NSString *)from :(NSString *)to { [CydiaWebViewController performSelectorOnMainThread:@selector(addDiversion:) withObject:[[[Diversion alloc] initWithFrom:from to:to] autorelease] waitUntilDone:NO]; } +- (NSDictionary *) getApplicationInfo:(NSString *)display value:(NSString *)key { + char path[1024]; + if (SBBundlePathForDisplayIdentifier(SBSSpringBoardServerPort(), [display UTF8String], path) != 0) + return (id) [NSNull null]; + NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:[[NSString stringWithUTF8String:path] stringByAppendingString:@"/Info.plist"]]); + if (info == nil) + return (id) [NSNull null]; + return [info objectForKey:key]; +} + - (NSNumber *) getKernelNumber:(NSString *)name { const char *string([name UTF8String]); @@ -3928,6 +4390,31 @@ static _H Diversions_; return value; } +- (NSArray *) getMetadataKeys { +@synchronized (Values_) { + return [Values_ allKeys]; +} } + +- (void) registerFrame:(DOMHTMLIFrameElement *)iframe { + WebFrame *frame([iframe contentFrame]); + [indirect_ registerFrame:frame]; +} + +- (id) getMetadataValue:(NSString *)key { +@synchronized (Values_) { + return [Values_ objectForKey:key]; +} } + +- (void) setMetadataValue:(NSString *)key :(NSString *)value { +@synchronized (Values_) { + if (value == nil || value == (id) [WebUndefined undefined] || value == (id) [NSNull null]) + [Values_ removeObjectForKey:key]; + else + [Values_ setObject:value forKey:key]; + + [delegate_ performSelectorOnMainThread:@selector(updateValues) withObject:nil waitUntilDone:YES]; +} } + - (id) getSessionValue:(NSString *)key { @synchronized (SessionData_) { return [SessionData_ objectForKey:key]; @@ -3946,6 +4433,16 @@ static _H Diversions_; [BridgedHosts_ addObject:host]; } } +- (void) addInsecureHost:(NSString *)host { +@synchronized (HostConfig_) { + [InsecureHosts_ addObject:host]; +} } + +- (void) addTokenHost:(NSString *)host { +@synchronized (HostConfig_) { + [TokenHosts_ addObject:host]; +} } + - (void) addPipelinedHost:(NSString *)host scheme:(NSString *)scheme { @synchronized (HostConfig_) { if (scheme != (id) [WebUndefined undefined]) @@ -3960,6 +4457,20 @@ static _H Diversions_; [indirect_ performSelectorOnMainThread:@selector(popViewControllerWithNumber:) withObject:value waitUntilDone:NO]; } +- (void) addSource:(NSString *)href :(NSString *)distribution :(WebScriptObject *)sections { + NSMutableArray *array([NSMutableArray arrayWithCapacity:[sections count]]); + + for (NSString *section in sections) + [array addObject:section]; + + [delegate_ performSelectorOnMainThread:@selector(addSource:) withObject:[NSMutableDictionary dictionaryWithObjectsAndKeys: + @"deb", @"Type", + href, @"URI", + distribution, @"Distribution", + array, @"Sections", + nil] waitUntilDone:NO]; +} + - (void) addTrivialSource:(NSString *)href { [delegate_ performSelectorOnMainThread:@selector(addTrivialSource:) withObject:href waitUntilDone:NO]; } @@ -3968,6 +4479,10 @@ static _H Diversions_; [delegate_ performSelectorOnMainThread:@selector(syncData) withObject:nil waitUntilDone:NO]; } +- (void) saveConfig { + [delegate_ performSelectorOnMainThread:@selector(_saveConfig) withObject:nil waitUntilDone:NO]; +} + - (NSArray *) getAllSources { return [[Database sharedInstance] sources]; } @@ -4026,32 +4541,25 @@ static _H Diversions_; /* XXX: this should probably not use du */ execl("/usr/libexec/cydia/du", "du", "-s", [path UTF8String], NULL); exit(1); - _assert(false); - } - - _assert(close(fds[1]) != -1); + } else { + _assert(close(fds[1]) != -1); - if (FILE *du = fdopen(fds[0], "r")) { - char line[1024]; - while (fgets(line, sizeof(line), du) != NULL) { - size_t length(strlen(line)); - while (length != 0 && line[length - 1] == '\n') - line[--length] = '\0'; - if (char *tab = strchr(line, '\t')) { - *tab = '\0'; - value = [NSNumber numberWithUnsignedLong:strtoul(line, NULL, 0)]; + if (FILE *du = fdopen(fds[0], "r")) { + char line[1024]; + while (fgets(line, sizeof(line), du) != NULL) { + size_t length(strlen(line)); + while (length != 0 && line[length - 1] == '\n') + line[--length] = '\0'; + if (char *tab = strchr(line, '\t')) { + *tab = '\0'; + value = [NSNumber numberWithUnsignedLong:strtoul(line, NULL, 0)]; + } } - } - - fclose(du); - } else _assert(close(fds[0])); - int status; - wait: - if (waitpid(pid, &status, 0) == -1) - if (errno == EINTR) - goto wait; - else _assert(false); + fclose(du); + } else + _assert(close(fds[0]) != -1); + } ReapZombie(pid); return value; } @@ -4060,12 +4568,16 @@ static _H Diversions_; [indirect_ performSelectorOnMainThread:@selector(close) withObject:nil waitUntilDone:NO]; } +- (NSNumber *) isReachable:(NSString *)name { + return [NSNumber numberWithBool:IsReachable([name UTF8String])]; +} + - (void) installPackages:(NSArray *)packages { [delegate_ performSelectorOnMainThread:@selector(installPackages:) withObject:packages waitUntilDone:NO]; } - (NSString *) substitutePackageNames:(NSString *)message { - NSMutableArray *words([[message componentsSeparatedByString:@" "] mutableCopy]); + NSMutableArray *words([[[message componentsSeparatedByString:@" "] mutableCopy] autorelease]); for (size_t i(0), e([words count]); i != e; ++i) { NSString *word([words objectAtIndex:i]); if (Package *package = [[Database sharedInstance] packageWithName:word]) @@ -4164,114 +4676,24 @@ static _H Diversions_; @end /* }}} */ -/* @ Loading... Indicator {{{ */ -@interface CYLoadingIndicator : UIView { - _H spinner_; - _H label_; - _H container_; -} - -@property (readonly, nonatomic) UILabel *label; -@property (readonly, nonatomic) UIActivityIndicatorView *activityIndicatorView; - -@end - -@implementation CYLoadingIndicator - -- (id) initWithFrame:(CGRect)frame { - if ((self = [super initWithFrame:frame]) != nil) { - container_ = [[[UIView alloc] init] autorelease]; - [container_ setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin]; - - spinner_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease]; - [spinner_ startAnimating]; - [container_ addSubview:spinner_]; - - label_ = [[[UILabel alloc] init] autorelease]; - [label_ setFont:[UIFont boldSystemFontOfSize:15.0f]]; - [label_ setBackgroundColor:[UIColor clearColor]]; - [label_ setTextColor:[UIColor blackColor]]; - [label_ setShadowColor:[UIColor whiteColor]]; - [label_ setShadowOffset:CGSizeMake(0, 1)]; - [label_ setText:[NSString stringWithFormat:Elision_, UCLocalize("LOADING"), nil]]; - [container_ addSubview:label_]; - - CGSize viewsize = frame.size; - CGSize spinnersize = [spinner_ bounds].size; - CGSize textsize = [[label_ text] sizeWithFont:[label_ font]]; - float bothwidth = spinnersize.width + textsize.width + 5.0f; - - CGRect containrect = { - CGPointMake(floorf((viewsize.width / 2) - (bothwidth / 2)), floorf((viewsize.height / 2) - (spinnersize.height / 2))), - CGSizeMake(bothwidth, spinnersize.height) - }; - CGRect textrect = { - CGPointMake(spinnersize.width + 5.0f, floorf((spinnersize.height / 2) - (textsize.height / 2))), - textsize - }; - CGRect spinrect = { - CGPointZero, - spinnersize - }; - - [container_ setFrame:containrect]; - [spinner_ setFrame:spinrect]; - [label_ setFrame:textrect]; - [self addSubview:container_]; - } return self; -} - -- (UILabel *) label { - return label_; -} - -- (UIActivityIndicatorView *) activityIndicatorView { - return spinner_; -} - -@end -/* }}} */ -/* Emulated Loading Controller {{{ */ -@interface CYEmulatedLoadingController : CyteViewController { - _H indicator_; - _H tabbar_; - _H navbar_; -} - +@interface NSURL (CydiaSecure) @end -@implementation CYEmulatedLoadingController +@implementation NSURL (CydiaSecure) -- (void) loadView { - [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; - - UITableView *table([[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped] autorelease]); - [table setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; - [[self view] addSubview:table]; - - indicator_ = [[[CYLoadingIndicator alloc] initWithFrame:[[self view] bounds]] autorelease]; - [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; - [[self view] addSubview:indicator_]; - - tabbar_ = [[[UITabBar alloc] initWithFrame:CGRectMake(0, 0, 0, 49.0f)] autorelease]; - [tabbar_ setFrame:CGRectMake(0.0f, [[self view] bounds].size.height - [tabbar_ bounds].size.height, [[self view] bounds].size.width, [tabbar_ bounds].size.height)]; - [tabbar_ setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth]; - [[self view] addSubview:tabbar_]; +- (bool) isCydiaSecure { + if ([[[self scheme] lowercaseString] isEqualToString:@"https"]) + return true; - navbar_ = [[[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 0, 44.0f)] autorelease]; - [navbar_ setFrame:CGRectMake(0.0f, 0.0f, [[self view] bounds].size.width, [navbar_ bounds].size.height)]; - [navbar_ setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth]; - [[self view] addSubview:navbar_]; -} + @synchronized (HostConfig_) { + if ([InsecureHosts_ containsObject:[self host]]) + return true; + } -- (void) releaseSubviews { - indicator_ = nil; - tabbar_ = nil; - navbar_ = nil; + return false; } @end -/* }}} */ /* Cydia Browser Controller {{{ */ @implementation CydiaWebViewController @@ -4280,7 +4702,9 @@ static _H Diversions_; return request_ == nil ? nil : [NSURL URLWithString:[NSString stringWithFormat:@"cydia://url/%@", [[request_ URL] absoluteString]]]; } -+ (void) initialize { ++ (void) _initialize { + [super _initialize]; + Diversions_ = [NSMutableSet setWithCapacity:0]; } @@ -4290,7 +4714,10 @@ static _H Diversions_; - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { [super webView:view didClearWindowObject:window forFrame:frame]; + [CydiaWebViewController didClearWindowObject:window forFrame:frame withCydia:cydia_]; +} ++ (void) didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame withCydia:(CydiaObject *)cydia { WebDataSource *source([frame dataSource]); NSURLResponse *response([source response]); NSURL *url([response URL]); @@ -4307,7 +4734,14 @@ static _H Diversions_; } if (bridged) - [window setValue:cydia_ forKey:@"cydia"]; + [window setValue:cydia forKey:@"cydia"]; +} + +- (void) _setupMail:(MFMailComposeViewController *)controller { + [controller addAttachmentData:[NSData dataWithContentsOfFile:@"/tmp/cydia.log"] mimeType:@"text/plain" fileName:@"cydia.log"]; + + system("/usr/bin/dpkg -l >/tmp/dpkgl.log"); + [controller addAttachmentData:[NSData dataWithContentsOfFile:@"/tmp/dpkgl.log"] mimeType:@"text/plain" fileName:@"dpkgl.log"]; } - (NSURL *) URLWithURL:(NSURL *)url { @@ -4315,14 +4749,51 @@ static _H Diversions_; } - (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source { - NSMutableURLRequest *copy([[super webView:view resource:resource willSendRequest:request redirectResponse:response fromDataSource:source] mutableCopy]); + return [CydiaWebViewController requestWithHeaders:[super webView:view resource:resource willSendRequest:request redirectResponse:response fromDataSource:source]]; +} - if (System_ != NULL) - [copy setValue:System_ forHTTPHeaderField:@"X-System"]; - if (Machine_ != NULL) ++ (NSURLRequest *) requestWithHeaders:(NSURLRequest *)request { + NSMutableURLRequest *copy([[request mutableCopy] autorelease]); + + NSURL *url([copy URL]); + NSString *href([url absoluteString]); + NSString *host([url host]); + + if ([href hasPrefix:@"https://cydia.saurik.com/TSS/"]) { + if (NSString *agent = [copy valueForHTTPHeaderField:@"X-User-Agent"]) { + [copy setValue:agent forHTTPHeaderField:@"User-Agent"]; + [copy setValue:nil forHTTPHeaderField:@"X-User-Agent"]; + } + + [copy setValue:nil forHTTPHeaderField:@"Referer"]; + [copy setValue:nil forHTTPHeaderField:@"Origin"]; + + [copy setURL:[NSURL URLWithString:[@"http://gs.apple.com/TSS/" stringByAppendingString:[href substringFromIndex:29]]]]; + return copy; + } + + if ([copy valueForHTTPHeaderField:@"X-Cydia-Cf"] == nil) + [copy setValue:[NSString stringWithFormat:@"%.2f", kCFCoreFoundationVersionNumber] forHTTPHeaderField:@"X-Cydia-Cf"]; + if (Machine_ != NULL && [copy valueForHTTPHeaderField:@"X-Machine"] == nil) [copy setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; - if (Token_ != nil) - [copy setValue:Token_ forHTTPHeaderField:@"X-Cydia-Token"]; + + bool bridged; + bool token; + + @synchronized (HostConfig_) { + bridged = [BridgedHosts_ containsObject:host]; + token = [TokenHosts_ containsObject:host]; + } + + if ([url isCydiaSecure]) { + if (bridged) { + if (UniqueID_ != nil && [copy valueForHTTPHeaderField:@"X-Cydia-Id"] == nil) + [copy setValue:UniqueID_ forHTTPHeaderField:@"X-Cydia-Id"]; + } else if (token) { + if (Token_ != nil && [copy valueForHTTPHeaderField:@"X-Cydia-Token"] == nil) + [copy setValue:Token_ forHTTPHeaderField:@"X-Cydia-Token"]; + } + } return copy; } @@ -4332,23 +4803,31 @@ static _H Diversions_; [cydia_ setDelegate:delegate]; } +- (NSString *) applicationNameForUserAgent { + return UserAgent_; +} + - (id) init { if ((self = [super initWithWidth:0 ofClass:[CydiaWebViewController class]]) != nil) { cydia_ = [[[CydiaObject alloc] initWithDelegate:indirect_] autorelease]; + } return self; +} - WebView *webview([[webview_ _documentView] webView]); +@end - NSString *application([NSString stringWithFormat:@"Cydia/%@", @ Cydia_]); +@interface AppCacheController : CydiaWebViewController { +} - if (Safari_ != nil) - application = [NSString stringWithFormat:@"Safari/%@ %@", Safari_, application]; - if (Build_ != nil) - application = [NSString stringWithFormat:@"Mobile/%@ %@", Build_, application]; - if (Product_ != nil) - application = [NSString stringWithFormat:@"Version/%@ %@", Product_, application]; +@end - [webview setApplicationNameForUserAgent:application]; - } return self; +@implementation AppCacheController + +- (void) didReceiveMemoryWarning { + // XXX: this doesn't work +} + +- (bool) retainsNetworkActivityIndicator { + return false; } @end @@ -4443,7 +4922,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (button == [alert cancelButtonIndex]) [self dismissModalViewControllerAnimated:YES]; else if (button == [alert firstOtherButtonIndex]) { - [self complete]; + [self performSelector:@selector(complete) withObject:nil afterDelay:0]; } [alert dismissWithClickedButtonIndex:-1 animated:YES]; @@ -4456,8 +4935,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) _doContinue { - [self dismissModalViewControllerAnimated:YES]; [delegate_ cancelAndClear:NO]; + [self dismissModalViewControllerAnimated:YES]; } - (id) invokeDefaultMethodWithArguments:(NSArray *)args { @@ -4546,7 +5025,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { reason = @"virtual"; } - NSDictionary *version(start.TargetVer() == 0 ? [NSNull null] : [NSDictionary dictionaryWithObjectsAndKeys: + NSDictionary *version(start.TargetVer() == 0 ? (NSDictionary *) [NSNull null] : [NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithUTF8String:start.CompType()], @"operator", [NSString stringWithUTF8String:start.TargetVer()], @"value", nil]); @@ -4625,6 +5104,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { ] autorelease]; [essential_ setContext:@"remove"]; + [essential_ setNumberOfRows:2]; } else { essential_ = [[[UIAlertView alloc] initWithTitle:UCLocalize("UNABLE_TO_COMPLY") @@ -4678,8 +5158,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { #endif - (void) cancelButtonClicked { - [self dismissModalViewControllerAnimated:YES]; [delegate_ cancelAndClear:YES]; + [self dismissModalViewControllerAnimated:YES]; } #if !AlwaysReload @@ -4743,6 +5223,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } return self; } +- (id) delegate { + return delegate_; +} + - (void) setDelegate:(id)delegate { delegate_ = delegate; } @@ -4889,11 +5373,43 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super viewWillAppear:animated]; } +- (void) reloadSpringBoard { + if (kCFCoreFoundationVersionNumber > 700) { // XXX: iOS 6.x + system("/bin/launchctl stop com.apple.backboardd"); + sleep(15); + system("/usr/bin/killall backboardd SpringBoard sbreload"); + return; + } + + pid_t pid(ExecFork()); + if (pid == 0) { + if (setsid() == -1) + perror("setsid"); + + pid_t pid(ExecFork()); + if (pid == 0) { + execl("/usr/bin/sbreload", "sbreload", NULL); + perror("sbreload"); + + exit(0); + } ReapZombie(pid); + + exit(0); + } ReapZombie(pid); + + sleep(15); + system("/usr/bin/killall backboardd SpringBoard sbreload"); +} + - (void) close { UpdateExternalStatus(0); + if (Finish_ > 1) + [delegate_ saveState]; + switch (Finish_) { case 0: + [delegate_ returnToCydia]; break; case 1: @@ -4912,10 +5428,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _trace(); goto reload; - reload: - system("/usr/bin/sbreload"); - _trace(); - break; + reload: { + UIProgressHUD *hud([delegate_ addProgressHUD]); + [hud setText:UCLocalize("LOADING")]; + [self performSelector:@selector(reloadSpringBoard) withObject:nil afterDelay:0.5]; + return; + } case 4: _trace(); @@ -5020,10 +5538,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { case 4: [progress_ setFinish:UCLocalize("REBOOT_DEVICE")]; break; } - _trace(); - system("su -c /usr/bin/uicache mobile"); - _trace(); - UpdateExternalStatus(Finish_ == 0 ? 0 : 2); [progress_ setRunning:false]; @@ -5096,7 +5610,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { bool commercial_; _H source_; _H badge_; - _H package_; _H placard_; bool summarized_; } @@ -5126,7 +5639,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (NSString *) accessibilityLabel { - return [NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), (id) name_, (id) description_]; + return name_; } - (void) setPackage:(Package *)package asSummary:(bool)summary { @@ -5138,76 +5651,74 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { source_ = nil; badge_ = nil; placard_ = nil; - package_ = nil; - [package parse]; + if (package == nil) + [content_ setBackgroundColor:[UIColor whiteColor]]; + else { + [package parse]; - Source *source = [package source]; + Source *source = [package source]; - icon_ = [package icon]; - name_ = [package name]; + icon_ = [package icon]; - if (IsWildcat_) - description_ = [package longDescription]; - if (description_ == nil) - description_ = [package shortDescription]; + if (NSString *name = [package name]) + name_ = [NSString stringWithString:name]; - commercial_ = [package isCommercial]; + if (NSString *description = [package shortDescription]) + description_ = [NSString stringWithString:description]; - package_ = package; + commercial_ = [package isCommercial]; - NSString *label = nil; - bool trusted = false; + NSString *label = nil; + bool trusted = false; - if (source != nil) { - label = [source label]; - trusted = [source trusted]; - } else if ([[package id] isEqualToString:@"firmware"]) - label = UCLocalize("APPLE"); - else - label = [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("UNKNOWN"), UCLocalize("LOCAL")]; + if (source != nil) { + label = [source label]; + trusted = [source trusted]; + } else if ([[package id] isEqualToString:@"firmware"]) + label = UCLocalize("APPLE"); + else + label = [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("UNKNOWN"), UCLocalize("LOCAL")]; - NSString *from(label); + NSString *from(label); - NSString *section = [package simpleSection]; - if (section != nil && ![section isEqualToString:label]) { - section = [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"]; - from = [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), from, section]; - } + NSString *section = [package simpleSection]; + if (section != nil && ![section isEqualToString:label]) { + section = [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"]; + from = [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), from, section]; + } - source_ = [NSString stringWithFormat:UCLocalize("FROM"), from]; + source_ = [NSString stringWithFormat:UCLocalize("FROM"), from]; - if (NSString *purpose = [package primaryPurpose]) - badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]; + if (NSString *purpose = [package primaryPurpose]) + badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]; - UIColor *color; - NSString *placard; + UIColor *color; + NSString *placard; - if (NSString *mode = [package_ mode]) { - if ([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]) { - color = RemovingColor_; - //placard = @"removing"; + if (NSString *mode = [package mode]) { + if ([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]) { + color = RemovingColor_; + placard = @"removing"; + } else { + color = InstallingColor_; + placard = @"installing"; + } } else { - color = InstallingColor_; - //placard = @"installing"; + color = [UIColor whiteColor]; + + if ([package installed] != nil) + placard = @"installed"; + else + placard = nil; } - // XXX: the removing/installing placards are not @2x - placard = nil; - } else { - color = [UIColor whiteColor]; + [content_ setBackgroundColor:color]; - if ([package installed] != nil) - placard = @"installed"; - else - placard = nil; + if (placard != nil) + placard_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/%@.png", App_, placard]]; } - [content_ setBackgroundColor:color]; - - if (placard != nil) - placard_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/%@.png", App_, placard]]; - [self setNeedsDisplay]; [content_ setNeedsDisplay]; } @@ -5220,11 +5731,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { CGRect rect; rect.size = [(UIImage *) icon_ size]; - rect.size.width /= 4; - rect.size.height /= 4; + while (rect.size.width > 16 || rect.size.height > 16) { + rect.size.width /= 2; + rect.size.height /= 2; + } - rect.origin.x = 14 - rect.size.width / 4; - rect.origin.y = 14 - rect.size.height / 4; + rect.origin.x = 19 - rect.size.width / 2; + rect.origin.y = 19 - rect.size.height / 2; [icon_ drawInRect:rect]; } @@ -5236,21 +5749,21 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { rect.size.width /= 4; rect.size.height /= 4; - rect.origin.x = 20 - rect.size.width / 4; - rect.origin.y = 20 - rect.size.height / 4; + rect.origin.x = 25 - rect.size.width / 2; + rect.origin.y = 25 - rect.size.height / 2; [badge_ drawInRect:rect]; } - if (highlighted) + if (highlighted && kCFCoreFoundationVersionNumber < 800) UISetColor(White_); if (!highlighted) UISetColor(commercial_ ? Purple_ : Black_); - [name_ drawAtPoint:CGPointMake(36, 8) forWidth:(width - (placard_ == nil ? 68 : 94)) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation]; + [name_ drawAtPoint:CGPointMake(36, 8) forWidth:(width - (placard_ == nil ? 68 : 94)) withFont:Font18Bold_ lineBreakMode:NSLineBreakByTruncatingTail]; if (placard_ != nil) - [placard_ drawAtPoint:CGPointMake(width - 52, 9)]; + [placard_ drawAtPoint:CGPointMake(width - 52, 11)]; } - (void) drawNormalContentRect:(CGRect)rect { @@ -5261,8 +5774,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { CGRect rect; rect.size = [(UIImage *) icon_ size]; - rect.size.width /= 2; - rect.size.height /= 2; + while (rect.size.width > 32 || rect.size.height > 32) { + rect.size.width /= 2; + rect.size.height /= 2; + } rect.origin.x = 25 - rect.size.width / 2; rect.origin.y = 25 - rect.size.height / 2; @@ -5283,17 +5798,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [badge_ drawInRect:rect]; } - if (highlighted) + if (highlighted && kCFCoreFoundationVersionNumber < 800) UISetColor(White_); if (!highlighted) UISetColor(commercial_ ? Purple_ : Black_); - [name_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - (placard_ == nil ? 80 : 106)) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation]; - [source_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation]; + [name_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - (placard_ == nil ? 80 : 106)) withFont:Font18Bold_ lineBreakMode:NSLineBreakByTruncatingTail]; + [source_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:NSLineBreakByTruncatingTail]; if (!highlighted) UISetColor(commercial_ ? Purplish_ : Gray_); - [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 46) withFont:Font14_ lineBreakMode:UILineBreakModeTailTruncation]; + [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 46) withFont:Font14_ lineBreakMode:NSLineBreakByTruncatingTail]; if (placard_ != nil) [placard_ drawAtPoint:CGPointMake(width - 52, 9)]; @@ -5330,6 +5845,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier { if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) { icon_ = [UIImage applicationImageNamed:@"folder.png"]; + // XXX: this initial frame is wrong, but is fixed later switch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(218, 9, 60, 25)] autorelease]; [switch_ addTarget:self action:@selector(onSwitch:) forEvents:UIControlEventValueChanged]; @@ -5378,7 +5894,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { section_ = [section localized]; name_ = section_ == nil || [section_ length] == 0 ? UCLocalize("NO_SECTION") : (NSString *) section_; - count_ = [NSString stringWithFormat:@"%d", [section count]]; + count_ = [NSString stringWithFormat:@"%zd", [section count]]; if (editing_) [switch_ setOn:(isSectionVisible(basic_) ? 1 : 0) animated:NO]; @@ -5394,7 +5910,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super setFrame:frame]; CGRect rect([switch_ frame]); - [switch_ setFrame:CGRectMake(frame.size.width - 102, 9, rect.size.width, rect.size.height)]; + [switch_ setFrame:CGRectMake(frame.size.width - rect.size.width - 9, 9, rect.size.width, rect.size.height)]; } - (NSString *) accessibilityLabel { @@ -5404,24 +5920,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) drawContentRect:(CGRect)rect { bool highlighted(highlighted_ && !editing_); - [icon_ drawInRect:CGRectMake(8, 7, 32, 32)]; + [icon_ drawInRect:CGRectMake(7, 7, 32, 32)]; - if (highlighted) + if (highlighted && kCFCoreFoundationVersionNumber < 800) UISetColor(White_); float width(rect.size.width); if (editing_) - width -= 87; + width -= 9 + [switch_ frame].size.width; if (!highlighted) UISetColor(Black_); - [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(width - 70) withFont:Font22Bold_ lineBreakMode:UILineBreakModeTailTruncation]; + [name_ drawAtPoint:CGPointMake(48, 12) forWidth:(width - 58) withFont:Font18_ lineBreakMode:NSLineBreakByTruncatingTail]; CGSize size = [count_ sizeWithFont:Font14_]; - UISetColor(White_); + UISetColor(Folder_); if (count_ != nil) - [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_]; + [count_ drawAtPoint:CGPointMake(10 + (30 - size.width) / 2, 18) withFont:Font12Bold_]; } @end @@ -5473,14 +5989,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) loadView { - [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; - - list_ = [[[UITableView alloc] initWithFrame:[[self view] bounds]] autorelease]; + list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]; [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; [list_ setRowHeight:24.0f]; [(UITableView *) list_ setDataSource:self]; [list_ setDelegate:self]; - [[self view] addSubview:list_]; + [self setView:list_]; } - (void) viewDidLoad { @@ -5491,13 +6005,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) releaseSubviews { list_ = nil; + + package_ = nil; + files_ = nil; + + [super releaseSubviews]; } - (id) initWithDatabase:(Database *)database { if ((self = [super init]) != nil) { database_ = database; - - files_ = [NSMutableArray arrayWithCapacity:32]; } return self; } @@ -5505,7 +6022,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { package_ = nil; name_ = nil; - [files_ removeAllObjects]; + files_ = [NSMutableArray arrayWithCapacity:32]; if (package != nil) { package_ = package; @@ -5559,7 +6076,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _H button_; } -- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name; +- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer; @end @@ -5632,20 +6149,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } -// We don't want to allow non-commercial packages to do custom things to the install button, -// so it must call customButtonClicked with a custom commercial_ == 1 fallthrough. -- (void) customButtonClicked { - if (commercial_) - [super customButtonClicked]; - else - [self _customButtonClicked]; -} - - (void) reloadButtonClicked { - // Don't reload a commerical package by tapping the loading button, - // but if it's not an Install button, we should forward it on. - if (![package_ uninstalled]) - [self _customButtonClicked]; + if (commercial_ && function_ == nil && [package_ uninstalled]) + return; + [self customButtonClicked]; } - (void) applyLoadingTitle { @@ -5657,12 +6164,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } #endif -- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name { +- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer { if ((self = [super init]) != nil) { database_ = database; buttons_ = [NSMutableArray arrayWithCapacity:4]; - name_ = [NSString stringWithString:name]; - [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/package/%@", UI_, (id) name_]]]; + name_ = name == nil ? @"" : [NSString stringWithString:name]; + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/package/%@", UI_, (id) name_]] withReferrer:referrer]; } return self; } @@ -5738,10 +6245,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation PackageListController +- (NSURL *) referrerURL { + return [self navigationURL]; +} + - (bool) isSummarized { return false; } +- (bool) showsSections { + return true; +} + - (void) deselectWithAnimation:(BOOL)animated { [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated]; } @@ -5767,31 +6282,45 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self resizeForKeyboardBounds:bounds duration:0]; } +- (void) getKeyboardCurve:(UIViewAnimationCurve *)curve duration:(NSTimeInterval *)duration forNotification:(NSNotification *)notification { + if (&UIKeyboardAnimationCurveUserInfoKey == NULL) + *curve = UIViewAnimationCurveEaseInOut; + else + [[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:curve]; + + if (&UIKeyboardAnimationDurationUserInfoKey == NULL) + *duration = 0.3; + else + [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:duration]; +} + - (void) keyboardWillShow:(NSNotification *)notification { CGRect bounds; CGPoint center; - NSTimeInterval duration; - UIViewAnimationCurve curve; [[[notification userInfo] objectForKey:UIKeyboardBoundsUserInfoKey] getValue:&bounds]; [[[notification userInfo] objectForKey:UIKeyboardCenterEndUserInfoKey] getValue:¢er]; - [[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve]; - [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration]; + + NSTimeInterval duration; + UIViewAnimationCurve curve; + [self getKeyboardCurve:&curve duration:&duration forNotification:notification]; CGRect kbframe = CGRectMake(round(center.x - bounds.size.width / 2.0), round(center.y - bounds.size.height / 2.0), bounds.size.width, bounds.size.height); UIViewController *base = self; - while ([base parentViewController] != nil) - base = [base parentViewController]; + while ([base parentOrPresentingViewController] != nil) + base = [base parentOrPresentingViewController]; CGRect viewframe = [[base view] convertRect:[list_ frame] fromView:[list_ superview]]; CGRect intersection = CGRectIntersection(viewframe, kbframe); + if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) // XXX: _UIApplicationLinkedOnOrAfter(4) + intersection.size.height += CYStatusBarHeight(); + [self resizeForKeyboardBounds:intersection duration:duration curve:curve]; } - (void) keyboardWillHide:(NSNotification *)notification { NSTimeInterval duration; UIViewAnimationCurve curve; - [[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve]; - [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration]; + [self getKeyboardCurve:&curve duration:&duration forNotification:notification]; [self resizeForKeyboardBounds:CGRectZero duration:duration curve:curve]; } @@ -5818,7 +6347,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) didSelectPackage:(Package *)package { - CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id]] autorelease]); + CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id] withReferrer:[[self referrerURL] absoluteString]] autorelease]); [view setDelegate:delegate_]; [[self navigationController] pushViewController:view animated:YES]; } @@ -5861,7 +6390,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]); if (cell == nil) cell = [[[PackageCell alloc] init] autorelease]; - [cell setPackage:[self packageAtIndexPath:path] asSummary:[self isSummarized]]; + + Package *package([database_ packageWithName:[[self packageAtIndexPath:path] id]]); + [cell setPackage:package asSummary:[self isSummarized]]; return cell; } @@ -5872,7 +6403,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView { - if ([self isSummarized]) + if (![self showsSections]) return nil; return index_; @@ -5897,41 +6428,42 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { database_ = database; title_ = [title copy]; [[self navigationItem] setTitle:title_]; + } return self; +} -#if TryIndexedCollation - if ([[self class] hasIndexedCollation]) - index_ = [[objc_getClass("UILocalizedIndexedCollation") currentCollation] sectionIndexTitles]; - else -#endif - index_ = [NSMutableArray arrayWithCapacity:32]; +- (void) loadView { + UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]); + [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + [self setView:view]; + + list_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain] autorelease]; + [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [view addSubview:list_]; - indices_ = [NSMutableDictionary dictionaryWithCapacity:32]; + // XXX: is 20 the most optimal number here? + [list_ setSectionIndexMinimumDisplayRowCount:20]; - packages_ = [NSArray array]; - sections_ = [NSMutableArray arrayWithCapacity:16]; + [(UITableView *) list_ setDataSource:self]; + [list_ setDelegate:self]; - list_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain] autorelease]; - [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; - [[self view] addSubview:list_]; + [self updateHeight]; +} - // XXX: is 20 the most optimal number here? - [list_ setSectionIndexMinimumDisplayRowCount:20]; +- (void) releaseSubviews { + list_ = nil; - [(UITableView *) list_ setDataSource:self]; - [list_ setDelegate:self]; + packages_ = nil; + sections_ = nil; + index_ = nil; + indices_ = nil; - [self updateHeight]; - } return self; + [super releaseSubviews]; } - (void) setDelegate:(id)delegate { delegate_ = delegate; } -- (bool) hasPackage:(Package *)package { - return true; -} - - (bool) shouldYield { return false; } @@ -5940,18 +6472,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return false; } -- (NSArray *) _reloadPackages:(NSArray *)packages { -// XXX: maybe move @synchronized() to _reloadData? +- (NSMutableArray *) _reloadPackages { @synchronized (database_) { - NSMutableArray *filtered([NSMutableArray arrayWithCapacity:[packages count]]); - - _profile(PackageTable$reloadData$Filter) - for (Package *package in packages) - if ([self hasPackage:package]) - [filtered addObject:package]; - _end + era_ = [database_ era]; + NSArray *packages([database_ packages]); - return filtered; + return [NSMutableArray arrayWithArray:packages]; } } - (void) _reloadData { @@ -5960,39 +6486,46 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return; } - era_ = [database_ era]; - NSArray *packages = [database_ packages]; + NSArray *packages; + reload: if ([self shouldYield]) { - UIProgressHUD *hud; + do { + UIProgressHUD *hud; - if (![self shouldBlock]) - hud = nil; - else { - hud = [delegate_ addProgressHUD]; - [hud setText:UCLocalize("LOADING")]; - } + if (![self shouldBlock]) + hud = nil; + else { + hud = [delegate_ addProgressHUD]; + [hud setText:UCLocalize("LOADING")]; + } - do { reloading_ = 1; - packages_ = [self yieldToSelector:@selector(_reloadPackages:) withObject:packages]; - } while (reloading_ == 2); + packages = [self yieldToSelector:@selector(_reloadPackages)]; - reloading_ = 0; - - if (hud != nil) - [delegate_ removeProgressHUD:hud]; + if (hud != nil) + [delegate_ removeProgressHUD:hud]; + } while (reloading_ == 2); } else { - packages_ = [self _reloadPackages:packages]; + packages = [self _reloadPackages]; } - [indices_ removeAllObjects]; - [sections_ removeAllObjects]; +@synchronized (database_) { + if (era_ != [database_ era]) + goto reload; + reloading_ = 0; + + packages_ = packages; + + indices_ = [NSMutableDictionary dictionaryWithCapacity:32]; + sections_ = [NSMutableArray arrayWithCapacity:16]; Section *section = nil; #if TryIndexedCollation if ([[self class] hasIndexedCollation]) { + index_ = [[objc_getClass("UILocalizedIndexedCollation") currentCollation] sectionIndexTitles]; + id collation = [objc_getClass("UILocalizedIndexedCollation") currentCollation]; NSArray *titles = [collation sectionIndexTitles]; int secidx = -1; @@ -6025,10 +6558,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } else #endif { - [index_ removeAllObjects]; + index_ = [NSMutableArray arrayWithCapacity:32]; - bool summary([self isSummarized]); - if (summary) { + bool sectioned([self showsSections]); + if (!sectioned) { section = [[[Section alloc] initWithName:nil localize:false] autorelease]; [sections_ addObject:section]; } @@ -6043,7 +6576,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { index = [package index]; _end - if (!summary && (section == nil || [section index] != index)) { + if (sectioned && (section == nil || [section index] != index)) { _profile(PackageTable$reloadData$Section$Allocate) section = [[[Section alloc] initWithIndex:index row:offset] autorelease]; _end @@ -6067,11 +6600,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [(UITableView *) list_ setDataSource:self]; [list_ reloadData]; _end -} +} } - (void) reloadData { [super reloadData]; - [self performSelector:@selector(_reloadData) withObject:nil afterDelay:0]; + + if ([self shouldYield]) + [self performSelector:@selector(_reloadData) withObject:nil afterDelay:0]; + else + [self _reloadData]; } - (void) resetCursor { @@ -6094,10 +6631,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { SEL filter_; IMP imp_; _H object_; + _H stuff_; } - (void) setObject:(id)object; +- (void) setStuff:(id)object; +- (void) setObject:(id)object andStuff:(id)stuff; + - (void) setObject:(id)object forFilter:(SEL)filter; +- (void) setObject:(id)object andStuff:(id)stuff forFilter:(SEL)filter; - (SEL) filter; - (void) setFilter:(SEL)filter; @@ -6113,6 +6655,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) setFilter:(SEL)filter { +@synchronized (self) { filter_ = filter; /* XXX: this is an unsafe optimization of doomy hell */ @@ -6120,27 +6663,77 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _assert(method != NULL); imp_ = method_getImplementation(method); _assert(imp_ != NULL); -} +} } - (void) setObject:(id)object { +@synchronized (self) { object_ = object; -} +} } + +- (void) setStuff:(id)stuff { +@synchronized (self) { + stuff_ = stuff; +} } + +- (void) setObject:(id)object andStuff:(id)stuff { +@synchronized (self) { + object_ = object; + stuff_ = stuff; +} } - (void) setObject:(id)object forFilter:(SEL)filter { +@synchronized (self) { [self setFilter:filter]; - [self setObject:object]; -} + object_ = object; +} } + +- (void) setObject:(id)object andStuff:(id)stuff forFilter:(SEL)filter { +@synchronized (self) { + [self setFilter:filter]; + object_ = object; + stuff_ = stuff; +} } + +- (NSMutableArray *) _reloadPackages { +@synchronized (database_) { + era_ = [database_ era]; + NSArray *packages([database_ packages]); + + NSMutableArray *filtered([NSMutableArray arrayWithCapacity:[packages count]]); + + IMP imp; + SEL filter; + _H object; + _H stuff; + + @synchronized (self) { + imp = imp_; + filter = filter_; + object = object_; + stuff = stuff_; + } -- (bool) hasPackage:(Package *)package { - _profile(FilteredPackageTable$hasPackage) - return [package valid] && (*reinterpret_cast(imp_))(package, filter_, object_); + _profile(PackageTable$reloadData$Filter) + for (Package *package in packages) + if ([package valid] && (*reinterpret_cast(imp))(package, filter, object, stuff)) + [filtered addObject:package]; _end -} + + return filtered; +} } - (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object { if ((self = [super initWithDatabase:database title:title]) != nil) { [self setFilter:filter]; - [self setObject:object]; + object_ = object; + } return self; +} + +- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object with:(id)stuff { + if ((self = [super initWithDatabase:database title:title]) != nil) { + [self setFilter:filter]; + object_ = object; + stuff_ = stuff; } return self; } @@ -6149,19 +6742,41 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Home Controller {{{ */ @interface HomeController : CydiaWebViewController { + CFRunLoopRef runloop_; + SCNetworkReachabilityRef reachability_; } @end @implementation HomeController +static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachability, SCNetworkReachabilityFlags flags, void *info) { + [(HomeController *) info dispatchEvent:@"CydiaReachabilityCallback"]; +} + - (id) init { if ((self = [super init]) != nil) { [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/home/", UI_]]]; [self reloadData]; + + reachability_ = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, "cydia.saurik.com"); + if (reachability_ != NULL) { + SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; + SCNetworkReachabilitySetCallback(reachability_, HomeControllerReachabilityCallback, &context); + + CFRunLoopRef runloop(CFRunLoopGetCurrent()); + if (SCNetworkReachabilityScheduleWithRunLoop(reachability_, runloop, kCFRunLoopDefaultMode)) + runloop_ = runloop; + } } return self; } +- (void) dealloc { + if (reachability_ != NULL && runloop_ != NULL) + SCNetworkReachabilityUnscheduleFromRunLoop(reachability_, runloop_, kCFRunLoopDefaultMode); + [super dealloc]; +} + - (NSURL *) navigationURL { return [NSURL URLWithString:@"cydia://home"]; } @@ -6174,7 +6789,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [alert setCancelButtonIndex:0]; [alert setMessage: - @"Copyright \u00a9 2008-2011\n" + @"Copyright \u00a9 2008-2013\n" "SaurikIT, LLC\n" "\n" "Jay Freeman (saurik)\n" @@ -6194,176 +6809,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { ] autorelease]; } -- (void) unloadData { - [super unloadData]; - [self reloadData]; -} - -@end -/* }}} */ -/* Manage Controller {{{ */ -@interface ManageController : CydiaWebViewController { -} - -- (void) queueStatusDidChange; - -@end - -@implementation ManageController - -- (id) init { - if ((self = [super init]) != nil) { - [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/manage/", UI_]]]; - } return self; -} - -- (NSURL *) navigationURL { - return [NSURL URLWithString:@"cydia://manage"]; -} - -- (UIBarButtonItem *) leftButton { - return [[[UIBarButtonItem alloc] - initWithTitle:UCLocalize("SETTINGS") - style:UIBarButtonItemStylePlain - target:self - action:@selector(settingsButtonClicked) - ] autorelease]; -} - -- (void) settingsButtonClicked { - [delegate_ showSettings]; -} - -- (void) queueButtonClicked { - [delegate_ queue]; -} - -- (UIBarButtonItem *) customButton { - return Queuing_ ? [[[UIBarButtonItem alloc] - initWithTitle:UCLocalize("QUEUE") - style:UIBarButtonItemStyleDone - target:self - action:@selector(queueButtonClicked) - ] autorelease] : [super customButton]; -} - -- (void) queueStatusDidChange { - [self applyRightButton]; -} - -- (bool) isLoading { - return !Queuing_ && [super isLoading]; -} - -@end -/* }}} */ - -/* Refresh Bar {{{ */ -@interface RefreshBar : UINavigationBar { - _H indicator_; - _H prompt_; - _H progress_; - _H cancel_; -} - -@end - -@implementation RefreshBar - -- (void) positionViews { - CGRect frame = [cancel_ frame]; - frame.size = [cancel_ sizeThatFits:frame.size]; - frame.origin.x = [self frame].size.width - frame.size.width - 5; - frame.origin.y = ([self frame].size.height - frame.size.height) / 2; - [cancel_ setFrame:frame]; - - CGSize prgsize = {75, 100}; - CGRect prgrect = {{ - [self frame].size.width - prgsize.width - 10, - ([self frame].size.height - prgsize.height) / 2 - } , prgsize}; - [progress_ setFrame:prgrect]; - - CGSize indsize([UIProgressIndicator defaultSizeForStyle:[indicator_ activityIndicatorViewStyle]]); - unsigned indoffset = ([self frame].size.height - indsize.height) / 2; - CGRect indrect = {{indoffset, indoffset}, indsize}; - [indicator_ setFrame:indrect]; - - CGSize prmsize = {215, indsize.height + 4}; - CGRect prmrect = {{ - indoffset * 2 + indsize.width, - unsigned([self frame].size.height - prmsize.height) / 2 - 1 - }, prmsize}; - [prompt_ setFrame:prmrect]; -} - -- (void) setFrame:(CGRect)frame { - [super setFrame:frame]; - [self positionViews]; -} - -- (id) initWithFrame:(CGRect)frame delegate:(id)delegate { - if ((self = [super initWithFrame:frame]) != nil) { - [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; - - [self setBarStyle:UIBarStyleBlack]; - - UIBarStyle barstyle([self _barStyle:NO]); - bool ugly(barstyle == UIBarStyleDefault); - - UIProgressIndicatorStyle style = ugly ? - UIProgressIndicatorStyleMediumBrown : - UIProgressIndicatorStyleMediumWhite; - - indicator_ = [[[UIProgressIndicator alloc] initWithFrame:CGRectZero] autorelease]; - [(UIProgressIndicator *) indicator_ setStyle:style]; - [indicator_ startAnimation]; - [self addSubview:indicator_]; - - prompt_ = [[[UITextLabel alloc] initWithFrame:CGRectZero] autorelease]; - [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]]; - [prompt_ setBackgroundColor:[UIColor clearColor]]; - [prompt_ setFont:[UIFont systemFontOfSize:15]]; - [self addSubview:prompt_]; - - progress_ = [[[UIProgressBar alloc] initWithFrame:CGRectZero] autorelease]; - [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin]; - [(UIProgressBar *) progress_ setStyle:0]; - [self addSubview:progress_]; - - cancel_ = [[[UINavigationButton alloc] initWithTitle:UCLocalize("CANCEL") style:UINavigationButtonStyleHighlighted] autorelease]; - [cancel_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin]; - [cancel_ addTarget:delegate action:@selector(cancelPressed) forControlEvents:UIControlEventTouchUpInside]; - [cancel_ setBarStyle:barstyle]; - - [self positionViews]; - } return self; -} - -- (void) setCancellable:(bool)cancellable { - if (cancellable) - [self addSubview:cancel_]; - else - [cancel_ removeFromSuperview]; -} - -- (void) start { - [prompt_ setText:UCLocalize("UPDATING_DATABASE")]; - [progress_ setProgress:0]; -} - -- (void) stop { - [self setCancellable:NO]; -} - -- (void) setPrompt:(NSString *)prompt { - [prompt_ setText:prompt]; -} - -- (void) setProgress:(float)progress { - [progress_ setProgress:progress]; -} - @end /* }}} */ @@ -6377,62 +6822,26 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* }}} */ /* Cydia Tab Bar Controller {{{ */ -@interface CYTabBarController : UITabBarController < +@interface CydiaTabBarController : CyteTabBarController < UITabBarControllerDelegate, - ProgressDelegate + FetchDelegate > { _transient Database *database_; - _H refreshbar_; - bool dropped_; + _H indicator_; + bool updating_; // XXX: ok, "updatedelegate_"?... _transient NSObject *updatedelegate_; - - _H remembered_; - _transient UIViewController *transient_; } - (NSArray *) navigationURLCollection; -- (void) dropBar:(BOOL)animated; - (void) beginUpdate; -- (void) raiseBar:(BOOL)animated; - (BOOL) updating; -- (void) unloadData; @end -@implementation CYTabBarController - -- (void) setUnselectedViewController:(UIViewController *)transient { - NSMutableArray *controllers = [[self viewControllers] mutableCopy]; - if (transient != nil) { - if (transient_ == nil) - remembered_ = [controllers objectAtIndex:0]; - transient_ = transient; - [transient_ setTabBarItem:[remembered_ tabBarItem]]; - [controllers replaceObjectAtIndex:0 withObject:transient_]; - [self setSelectedIndex:0]; - [self setViewControllers:controllers]; - [self concealTabBarSelection]; - } else if (remembered_ != nil) { - [remembered_ setTabBarItem:[transient_ tabBarItem]]; - transient_ = transient; - [controllers replaceObjectAtIndex:0 withObject:remembered_]; - remembered_ = nil; - [self setViewControllers:controllers]; - [self revealTabBarSelection]; - } -} - -- (UIViewController *) unselectedViewController { - return transient_; -} - -- (void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController { - if ([self unselectedViewController]) - [self setUnselectedViewController:nil]; -} +@implementation CydiaTabBarController - (NSArray *) navigationURLCollection { NSMutableArray *items([NSMutableArray array]); @@ -6447,34 +6856,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return items; } -- (void) unloadData { - UIViewController *selected([self selectedViewController]); - for (UINavigationController *controller in [self viewControllers]) - [controller unloadData]; - - [selected reloadData]; - - if (UIViewController *unselected = [self unselectedViewController]) - [unselected reloadData]; - - [super unloadData]; -} - -- (void) dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [super dealloc]; -} - - (id) initWithDatabase:(Database *)database { if ((self = [super init]) != nil) { database_ = database; [self setDelegate:self]; - [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil]; + indicator_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteTiny] autorelease]; + [indicator_ setOrigin:CGPointMake(kCFCoreFoundationVersionNumber >= 800 ? 2 : 4, 2)]; - refreshbar_ = [[[RefreshBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, [UINavigationBar defaultSize].height) delegate:self] autorelease]; + [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; } return self; } @@ -6483,8 +6873,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) beginUpdate { - [(RefreshBar *) refreshbar_ start]; - [self dropBar:YES]; + if (updating_) + return; + + UIViewController *controller([[self viewControllers] objectAtIndex:1]); + UITabBarItem *item([controller tabBarItem]); + + [item setBadgeValue:@""]; + UIView *badge(MSHookIvar([item view], "_badge")); + + [indicator_ startAnimating]; + [badge addSubview:indicator_]; [updatedelegate_ retainNetworkActivityIndicator]; updating_ = true; @@ -6499,8 +6898,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) performUpdate { NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); - Status status; - status.setDelegate(self); + SourceStatus status(self, database_); [database_ updateWithStatus:status]; [self @@ -6516,8 +6914,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { updating_ = false; [updatedelegate_ releaseNetworkActivityIndicator]; - [self raiseBar:YES]; - [refreshbar_ stop]; + UIViewController *controller([[self viewControllers] objectAtIndex:1]); + [[controller tabBarItem] setBadgeValue:nil]; + + [indicator_ removeFromSuperview]; + [indicator_ stopAnimating]; [updatedelegate_ performSelector:selector withObject:nil afterDelay:0]; } @@ -6529,7 +6930,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) cancelUpdate { - [self stopUpdateWithSelector:@selector(updateData)]; + [self stopUpdateWithSelector:@selector(updateDataAndLoad)]; } - (void) cancelPressed { @@ -6540,118 +6941,27 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return updating_; } -- (void) addProgressEvent:(CydiaProgressEvent *)event { - [refreshbar_ setPrompt:[event compoundMessage]]; -} - -- (bool) isProgressCancelled { +- (bool) isSourceCancelled { return !updating_; } -- (void) setProgressCancellable:(NSNumber *)cancellable { - [refreshbar_ setCancellable:(updating_ && [cancellable boolValue])]; -} - -- (void) setProgressPercent:(NSNumber *)percent { - [refreshbar_ setProgress:[percent floatValue]]; +- (void) startSourceFetch:(NSString *)uri { } -- (void) setProgressStatus:(NSDictionary *)status { - if (status != nil) - [self setProgressPercent:[status objectForKey:@"Percent"]]; +- (void) stopSourceFetch:(NSString *)uri { } - (void) setUpdateDelegate:(id)delegate { updatedelegate_ = delegate; } -- (CGFloat) statusBarHeight { - if (UIInterfaceOrientationIsPortrait([self interfaceOrientation])) { - return [[UIApplication sharedApplication] statusBarFrame].size.height; - } else { - return [[UIApplication sharedApplication] statusBarFrame].size.width; - } -} - - (UIView *) transitionView { - if ([self respondsToSelector:@selector(_transitionView)]) + if (![self respondsToSelector:@selector(_transitionView)]) + return MSHookIvar(self, "_viewControllerTransitionView"); + else if (kCFCoreFoundationVersionNumber < 800) return [self _transitionView]; else - return MSHookIvar(self, "_viewControllerTransitionView"); -} - -- (void) dropBar:(BOOL)animated { - if (dropped_) - return; - dropped_ = true; - - UIView *transition([self transitionView]); - [[self view] addSubview:refreshbar_]; - - CGRect barframe([refreshbar_ frame]); - - if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_0) // XXX: _UIApplicationLinkedOnOrAfter(4) - barframe.origin.y = [self statusBarHeight]; - else - barframe.origin.y = 0; - - [refreshbar_ setFrame:barframe]; - - if (animated) - [UIView beginAnimations:nil context:NULL]; - - CGRect viewframe = [transition frame]; - viewframe.origin.y += barframe.size.height; - viewframe.size.height -= barframe.size.height; - [transition setFrame:viewframe]; - - if (animated) - [UIView commitAnimations]; - - // Ensure bar has the proper width for our view, it might have changed - barframe.size.width = viewframe.size.width; - [refreshbar_ setFrame:barframe]; -} - -- (void) raiseBar:(BOOL)animated { - if (!dropped_) - return; - dropped_ = false; - - UIView *transition([self transitionView]); - [refreshbar_ removeFromSuperview]; - - CGRect barframe([refreshbar_ frame]); - - if (animated) - [UIView beginAnimations:nil context:NULL]; - - CGRect viewframe = [transition frame]; - viewframe.origin.y -= barframe.size.height; - viewframe.size.height += barframe.size.height; - [transition setFrame:viewframe]; - - if (animated) - [UIView commitAnimations]; -} - -- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - bool dropped(dropped_); - - if (dropped) - [self raiseBar:NO]; - - [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; - - if (dropped) - [self dropBar:NO]; -} - -- (void) statusBarFrameChanged:(NSNotification *)notification { - if (dropped_) { - [self raiseBar:NO]; - [self dropBar:NO]; - } + return [[[self _transitionView] superview] superview]; } @end @@ -6675,8 +6985,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) reloadData { [super reloadData]; - if (UIViewController *visible = [self visibleViewController]) + UIViewController *visible([self visibleViewController]); + if (visible != nil) [visible reloadData]; + + // on the iPad, this view controller is ALSO visible. :( + if (IsWildcat_) + if (UIViewController *top = [self topViewController]) + if (top != visible) + [top reloadData]; } - (void) unloadData { @@ -6768,15 +7085,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [package parse]; 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; @@ -6787,8 +7095,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (path == nil) goto fail; path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - NSString *section(Simplify(path)); - UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, [section stringByReplacingOccurrencesOfString:@" " withString:@"_"]]]); + UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, [path stringByReplacingOccurrencesOfString:@" " withString:@"_"]]]); if (icon == nil) icon = [UIImage applicationImageNamed:@"unknown.png"]; [self _returnPNGWithImage:icon forRequest:request]; @@ -6805,37 +7112,52 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Section Controller {{{ */ @interface SectionController : FilteredPackageListController { + _H key_; _H section_; } -- (id) initWithDatabase:(Database *)database section:(NSString *)section; +- (id) initWithDatabase:(Database *)database source:(Source *)source section:(NSString *)section; @end @implementation SectionController -- (NSURL *) navigationURL { - NSString *name = section_; - if (name == nil) - name = @"all"; +- (NSURL *) referrerURL { + NSString *name(section_); + name = name ?: @"*"; + NSString *key(key_); + key = key ?: @"*"; + return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/sections/%@/%@", UI_, [key stringByAddingPercentEscapesIncludingReserved], [name stringByAddingPercentEscapesIncludingReserved]]]; +} - return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sections/%@", name]]; +- (NSURL *) navigationURL { + NSString *name(section_); + name = name ?: @"*"; + NSString *key(key_); + key = key ?: @"*"; + return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sections/%@/%@", [key stringByAddingPercentEscapesIncludingReserved], [name stringByAddingPercentEscapesIncludingReserved]]]; } -- (id) initWithDatabase:(Database *)database section:(NSString *)name { +- (id) initWithDatabase:(Database *)database source:(Source *)source section:(NSString *)section { NSString *title; - if (name == nil) + if (section == nil) title = UCLocalize("ALL_PACKAGES"); - else if (![name isEqual:@""]) - title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"]; + else if (![section isEqual:@""]) + title = [[NSBundle mainBundle] localizedStringForKey:Simplify(section) value:nil table:@"Sections"]; else title = UCLocalize("NO_SECTION"); - if ((self = [super initWithDatabase:database title:title filter:@selector(isVisibleInSection:) with:name]) != nil) { - section_ = name; + if ((self = [super initWithDatabase:database title:title filter:@selector(isVisibleInSection:source:) with:section with:source]) != nil) { + key_ = [source key]; + section_ = section; } return self; } +- (void) reloadData { + [super setStuff:[database_ sourceWithKey:key_]]; + [super reloadData]; +} + @end /* }}} */ /* Sections Controller {{{ */ @@ -6844,12 +7166,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UITableViewDelegate > { _transient Database *database_; + _H key_; _H sections_; _H filtered_; _H list_; } -- (id) initWithDatabase:(Database *)database; +- (id) initWithDatabase:(Database *)database source:(Source *)source; - (void) editButtonClicked; @end @@ -6857,7 +7180,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation SectionsController - (NSURL *) navigationURL { - return [NSURL URLWithString:@"cydia://sections"]; + return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sources/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]]; +} + +- (Source *) source { + if (key_ == nil) + return nil; + return [database_ sourceWithKey:key_]; } - (void) updateNavigationItem { @@ -6891,14 +7220,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - if ([self isEditing]) [self setEditing:NO]; + [self setEditing:NO]; } - (Section *) sectionAtIndexPath:(NSIndexPath *)indexPath { Section *section = nil; int index = [indexPath row]; if (![self isEditing]) { - index -= 1; + index -= 1; if (index >= 0) section = [filtered_ objectAtIndex:index]; } else { @@ -6938,6 +7267,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { SectionController *controller = [[[SectionController alloc] initWithDatabase:database_ + source:[self source] section:[section name] ] autorelease]; [controller setDelegate:delegate_]; @@ -6946,14 +7276,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) loadView { - [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; - - list_ = [[[UITableView alloc] initWithFrame:[[self view] bounds]] autorelease]; + list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]; [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; - [list_ setRowHeight:45.0f]; + [list_ setRowHeight:46]; [(UITableView *) list_ setDataSource:self]; [list_ setDelegate:self]; - [[self view] addSubview:list_]; + [self setView:list_]; } - (void) viewDidLoad { @@ -6964,14 +7292,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) releaseSubviews { list_ = nil; + + sections_ = nil; + filtered_ = nil; + + [super releaseSubviews]; } -- (id) initWithDatabase:(Database *)database { +- (id) initWithDatabase:(Database *)database source:(Source *)source { if ((self = [super init]) != nil) { database_ = database; - - sections_ = [NSMutableArray arrayWithCapacity:16]; - filtered_ = [NSMutableArray arrayWithCapacity:16]; + key_ = [source key]; } return self; } @@ -6980,13 +7311,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSArray *packages = [database_ packages]; - [sections_ removeAllObjects]; - [filtered_ removeAllObjects]; + sections_ = [NSMutableArray arrayWithCapacity:16]; + filtered_ = [NSMutableArray arrayWithCapacity:16]; NSMutableDictionary *sections([NSMutableDictionary dictionaryWithCapacity:32]); + Source *source([self source]); + _trace(); for (Package *package in packages) { + if (source != nil && [package source] != source) + continue; + NSString *name([package section]); NSString *key(name == nil ? @"" : name); @@ -7046,7 +7382,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { > { _transient Database *database_; unsigned era_; - _H packages_; + _H packages_; _H sections_; _H list_; unsigned upgrades_; @@ -7101,36 +7437,69 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]); if (cell == nil) cell = [[[PackageCell alloc] init] autorelease]; - [cell setPackage:[self packageAtIndexPath:path] asSummary:false]; + + Package *package([database_ packageWithName:[[self packageAtIndexPath:path] id]]); + [cell setPackage:package asSummary:false]; return cell; } - (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path { Package *package([self packageAtIndexPath:path]); - CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id]] autorelease]); + CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id] withReferrer:[NSString stringWithFormat:@"%@/#!/changes/", UI_]] autorelease]); [view setDelegate:delegate_]; [[self navigationController] pushViewController:view animated:YES]; return path; } +- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { + NSString *context([alert context]); + + if ([context isEqualToString:@"norefresh"]) + [alert dismissWithClickedButtonIndex:-1 animated:YES]; +} + +- (void) setLeftBarButtonItem { + if ([delegate_ updating]) + [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("CANCEL") + style:UIBarButtonItemStyleDone + target:self + action:@selector(cancelButtonClicked) + ] autorelease] animated:YES]; + else + [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("REFRESH") + style:UIBarButtonItemStylePlain + target:self + action:@selector(refreshButtonClicked) + ] autorelease] animated:YES]; +} + - (void) refreshButtonClicked { - [delegate_ beginUpdate]; - [[self navigationItem] setLeftBarButtonItem:nil animated:YES]; + if ([delegate_ requestUpdate]) + [self setLeftBarButtonItem]; +} + +- (void) cancelButtonClicked { + [delegate_ cancelUpdate]; } - (void) upgradeButtonClicked { [delegate_ distUpgrade]; + [[self navigationItem] setRightBarButtonItem:nil animated:YES]; } - (void) loadView { - [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; + UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]); + [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + [self setView:view]; - list_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain] autorelease]; + list_ = [[[UITableView alloc] initWithFrame:[view bounds] style:UITableViewStylePlain] autorelease]; [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; [list_ setRowHeight:73]; [(UITableView *) list_ setDataSource:self]; [list_ setDelegate:self]; - [[self view] addSubview:list_]; + [view addSubview:list_]; } - (void) viewDidLoad { @@ -7141,18 +7510,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) releaseSubviews { list_ = nil; + + packages_ = nil; + sections_ = nil; + + [super releaseSubviews]; } - (id) initWithDatabase:(Database *)database { if ((self = [super init]) != nil) { database_ = database; - - packages_ = [NSArray array]; - sections_ = [NSMutableArray arrayWithCapacity:16]; } return self; } -- (NSArray *) _reloadPackages:(NSArray *)packages { +- (NSMutableArray *) _reloadPackages { +@synchronized (database_) { + era_ = [database_ era]; + NSArray *packages([database_ packages]); + NSMutableArray *filtered([NSMutableArray arrayWithCapacity:[packages count]]); _trace(); @@ -7168,24 +7543,30 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _trace(); return filtered; -} +} } - (void) _reloadData { -@synchronized (database_) { - era_ = [database_ era]; - NSArray *packages = [database_ packages]; + [self setLeftBarButtonItem]; -#if 1 - UIProgressHUD *hud([delegate_ addProgressHUD]); - [hud setText:UCLocalize("LOADING")]; - //NSLog(@"HUD:%@::%@", delegate_, hud); - packages_ = [self yieldToSelector:@selector(_reloadPackages:) withObject:packages]; - [delegate_ removeProgressHUD:hud]; -#else - packages_ = [self _reloadPackages:packages]; -#endif + NSMutableArray *packages; + + reload: + if (true) { + UIProgressHUD *hud([delegate_ addProgressHUD]); + [hud setText:UCLocalize("LOADING")]; + //NSLog(@"HUD:%@::%@", delegate_, hud); + packages = [self yieldToSelector:@selector(_reloadPackages)]; + [delegate_ removeProgressHUD:hud]; + } else { + packages = [self _reloadPackages]; + } + +@synchronized (database_) { + if (era_ != [database_ era]) + goto reload; - [sections_ removeAllObjects]; + packages_ = packages; + sections_ = [NSMutableArray arrayWithCapacity:16]; Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease]; Section *ignored = nil; @@ -7249,21 +7630,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [list_ reloadData]; - if (upgrades_ > 0) - [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc] - initWithTitle:[NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", upgrades_]] - style:UIBarButtonItemStylePlain - target:self - action:@selector(upgradeButtonClicked) - ] autorelease]]; - - if (![delegate_ updating]) - [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] - initWithTitle:UCLocalize("REFRESH") - style:UIBarButtonItemStylePlain - target:self - action:@selector(refreshButtonClicked) - ] autorelease]]; + [[self navigationItem] setRightBarButtonItem:(upgrades_ == 0 ? nil : [[[UIBarButtonItem alloc] + initWithTitle:[NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", upgrades_]] + style:UIBarButtonItemStylePlain + target:self + action:@selector(upgradeButtonClicked) + ] autorelease]) animated:YES]; PrintTimes(); } } @@ -7290,26 +7662,32 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation SearchController +- (NSURL *) referrerURL { + return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/search?q=%@", UI_, [([search_ text] ?: @"") stringByAddingPercentEscapesIncludingReserved]]]; +} + - (NSURL *) navigationURL { if ([search_ text] == nil || [[search_ text] isEqualToString:@""]) return [NSURL URLWithString:@"cydia://search"]; else - return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://search/%@", [search_ text]]]; + return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://search/%@", [[search_ text] stringByAddingPercentEscapesIncludingReserved]]]; +} + +- (NSArray *) termsForQuery:(NSString *)query { + NSMutableArray *terms([NSMutableArray arrayWithCapacity:2]); + for (NSString *component in [query componentsSeparatedByString:@" "]) + if ([component length] != 0) + [terms addObject:component]; + + return terms; } - (void) useSearch { - [self setObject:[search_ text] forFilter:@selector(isUnfilteredAndSearchedForBy:)]; + [self setObject:[self termsForQuery:[search_ text]] forFilter:@selector(isUnfilteredAndSearchedForBy:)]; [self clearData]; [self reloadData]; } -- (void) viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - if ([self filter] == @selector(isUnfilteredAndSelectedForBy:)) - [self useSearch]; -} - - (void) searchBarTextDidBeginEditing:(UISearchBar *)searchBar { [self setObject:[search_ text] forFilter:@selector(isUnfilteredAndSelectedForBy:)]; [self clearData]; @@ -7347,8 +7725,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return [self filter] == @selector(isUnfilteredAndSelectedForBy:); } +- (bool) showsSections { + return false; +} + +- (NSMutableArray *) _reloadPackages { + NSMutableArray *packages([super _reloadPackages]); + if ([self filter] == @selector(isUnfilteredAndSearchedForBy:)) + [packages radixSortUsingSelector:@selector(rank)]; + return packages; +} + - (id) initWithDatabase:(Database *)database query:(NSString *)query { - if ((self = [super initWithDatabase:database title:UCLocalize("SEARCH") filter:@selector(isUnfilteredAndSearchedForBy:) with:query])) { + if ((self = [super initWithDatabase:database title:UCLocalize("SEARCH") filter:@selector(isUnfilteredAndSearchedForBy:) with:[self termsForQuery:query]])) { search_ = [[[UISearchBar alloc] init] autorelease]; [search_ setDelegate:self]; @@ -7376,10 +7765,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [textField setEnablesReturnKeyAutomatically:NO]; [[self navigationItem] setTitleView:textField]; } + + if ([self isSummarized]) + [search_ becomeFirstResponder]; } - (void) reloadData { - [self setObject:[search_ text]]; + id object([search_ text]); + if ([self filter] == @selector(isUnfilteredAndSearchedForBy:)) + object = [self termsForQuery:object]; + + [self setObject:object]; [self resetCursor]; [super reloadData]; @@ -7414,7 +7810,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation PackageSettingsController - (NSURL *) navigationURL { - return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@/settings", [package_ id]]]; + return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@/settings", (id) name_]]; } - (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView { @@ -7471,18 +7867,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { pclose(dpkg); exit(0); - _assert(false); - } - - _forever { - int status; - int result(waitpid(pid, &status, 0)); - - if (result != -1) { - _assert(result == pid); - break; - } - } + } ReapZombie(pid); } - (void) onIgnored:(id)control { @@ -7508,13 +7893,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) loadView { - [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; + UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]); + [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + [self setView:view]; table_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped] autorelease]; [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; [(UITableView *) table_ setDataSource:self]; [table_ setDelegate:self]; - [[self view] addSubview:table_]; + [view addSubview:table_]; subscribedSwitch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 20)] autorelease]; [subscribedSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin]; @@ -7547,6 +7934,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { table_ = nil; ignoredSwitch_ = nil; subscribedSwitch_ = nil; + + [super releaseSubviews]; } - (id) initWithDatabase:(Database *)database package:(NSString *)package { @@ -7574,25 +7963,32 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Installed Controller {{{ */ @interface InstalledController : FilteredPackageListController { - BOOL expert_; } - (id) initWithDatabase:(Database *)database; - -- (void) updateRoleButton; - (void) queueStatusDidChange; @end @implementation InstalledController +- (NSURL *) referrerURL { + return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/installed/", UI_]]; +} + - (NSURL *) navigationURL { return [NSURL URLWithString:@"cydia://installed"]; } - (id) initWithDatabase:(Database *)database { if ((self = [super initWithDatabase:database title:UCLocalize("INSTALLED") filter:@selector(isInstalledAndUnfiltered:) with:[NSNumber numberWithBool:YES]]) != nil) { - [self updateRoleButton]; + UISegmentedControl *segmented([[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:UCLocalize("SIMPLE"), UCLocalize("EXPERT"), nil]] autorelease]); + [segmented setSelectedSegmentIndex:0]; + [segmented setSegmentedControlStyle:UISegmentedControlStyleBar]; + [[self navigationItem] setTitleView:segmented]; + + [segmented addTarget:self action:@selector(modeChanged:) forEvents:UIControlEventValueChanged]; + [self queueStatusDidChange]; } return self; } @@ -7605,37 +8001,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) queueStatusDidChange { #if !AlwaysReload - if (IsWildcat_) { - if (Queuing_) { - [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] - initWithTitle:UCLocalize("QUEUE") - style:UIBarButtonItemStyleDone - target:self - action:@selector(queueButtonClicked) - ] autorelease]]; - } else { - [[self navigationItem] setLeftBarButtonItem:nil]; - } - } -#endif -} - -- (void) updateRoleButton { - if (Role_ != nil && ![Role_ isEqualToString:@"Developer"]) - [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc] - initWithTitle:(expert_ ? UCLocalize("EXPERT") : UCLocalize("SIMPLE")) - style:(expert_ ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain) + if (Queuing_) { + [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("QUEUE") + style:UIBarButtonItemStyleDone target:self - action:@selector(roleButtonClicked) + action:@selector(queueButtonClicked) ] autorelease]]; + } else { + [[self navigationItem] setLeftBarButtonItem:nil]; + } +#endif } -- (void) roleButtonClicked { - [self setObject:[NSNumber numberWithBool:expert_]]; +- (void) modeChanged:(UISegmentedControl *)segmented { + bool simple([segmented selectedSegmentIndex] == 0); + [self setObject:[NSNumber numberWithBool:simple]]; [self reloadData]; - expert_ = !expert_; - - [self updateRoleButton]; } @end @@ -7643,29 +8025,74 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Source Cell {{{ */ @interface SourceCell : CyteTableViewCell < - CyteTableViewCellDelegate + CyteTableViewCellDelegate, + SourceDelegate > { + _H source_; + _H url_; _H icon_; _H origin_; _H label_; + _H indicator_; } - (void) setSource:(Source *)source; +- (void) setFetch:(NSNumber *)fetch; @end @implementation SourceCell +- (void) _setImage:(NSArray *)data { + if ([url_ isEqual:[data objectAtIndex:0]]) { + icon_ = [data objectAtIndex:1]; + [content_ setNeedsDisplay]; + } +} + +- (void) _setSource:(NSURL *) url { + NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); + + if (NSData *data = [NSURLConnection + sendSynchronousRequest:[NSURLRequest + requestWithURL:url + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:10 + ] + + returningResponse:NULL + error:NULL + ]) + if (UIImage *image = [UIImage imageWithData:data]) + [self performSelectorOnMainThread:@selector(_setImage:) withObject:[NSArray arrayWithObjects:url, image, nil] waitUntilDone:NO]; + + [pool release]; +} + - (void) setSource:(Source *)source { - icon_ = nil; - if (icon_ == nil) - icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]]; - if (icon_ == nil) - icon_ = [UIImage applicationImageNamed:@"unknown.png"]; + source_ = source; + [source_ setDelegate:self]; + + [self setFetch:[NSNumber numberWithBool:[source_ fetch]]]; + + icon_ = [UIImage applicationImageNamed:@"unknown.png"]; origin_ = [source name]; - label_ = [source uri]; + label_ = [source rooturi]; + + [content_ setNeedsDisplay]; + + url_ = [source iconURL]; + [NSThread detachNewThreadSelector:@selector(_setSource:) toTarget:self withObject:url_]; +} +- (void) setAllSource { + source_ = nil; + [indicator_ stopAnimating]; + + icon_ = [UIImage applicationImageNamed:@"folder.png"]; + origin_ = UCLocalize("ALL_SOURCES"); + label_ = UCLocalize("ALL_SOURCES_EX"); [content_ setNeedsDisplay]; } @@ -7681,65 +8108,70 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [content_ setDelegate:self]; [content_ setOpaque:YES]; + + indicator_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGraySmall] autorelease]; + [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin];// | UIViewAutoresizingFlexibleBottomMargin]; + [content addSubview:indicator_]; + + [[content_ layer] setContentsGravity:kCAGravityTopLeft]; } return self; } +- (void) layoutSubviews { + [super layoutSubviews]; + + UIView *content([self contentView]); + CGRect bounds([content bounds]); + + CGRect frame([indicator_ frame]); + frame.origin.x = bounds.size.width - frame.size.width; + frame.origin.y = (bounds.size.height - frame.size.height) / 2; + + if (kCFCoreFoundationVersionNumber < 800) + frame.origin.x -= 8; + [indicator_ setFrame:frame]; +} + - (NSString *) accessibilityLabel { - return label_; + return origin_; } - (void) drawContentRect:(CGRect)rect { bool highlighted(highlighted_); float width(rect.size.width); - if (icon_ != nil) - [icon_ drawInRect:CGRectMake(10, 10, 30, 30)]; + if (icon_ != nil) { + CGRect rect; + rect.size = [(UIImage *) icon_ size]; - if (highlighted) + while (rect.size.width > 32 || rect.size.height > 32) { + rect.size.width /= 2; + rect.size.height /= 2; + } + + rect.origin.x = 26 - rect.size.width / 2; + rect.origin.y = 26 - rect.size.height / 2; + + [icon_ drawInRect:rect]; + } + + if (highlighted && kCFCoreFoundationVersionNumber < 800) UISetColor(White_); if (!highlighted) UISetColor(Black_); - [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - 80) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation]; + [origin_ drawAtPoint:CGPointMake(52, 8) forWidth:(width - 61) withFont:Font18Bold_ lineBreakMode:NSLineBreakByTruncatingTail]; if (!highlighted) - UISetColor(Blue_); - [label_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation]; -} - -@end -/* }}} */ -/* Source Controller {{{ */ -@interface SourceController : FilteredPackageListController { - _transient Source *source_; - _H key_; + UISetColor(Gray_); + [label_ drawAtPoint:CGPointMake(52, 29) forWidth:(width - 61) withFont:Font12_ lineBreakMode:NSLineBreakByTruncatingTail]; } -- (id) initWithDatabase:(Database *)database source:(Source *)source; - -@end - -@implementation SourceController - -- (NSURL *) navigationURL { - return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sources/%@", [source_ name]]]; -} - -- (id) initWithDatabase:(Database *)database source:(Source *)source { - if ((self = [super initWithDatabase:database title:[source label] filter:@selector(isVisibleInSource:) with:source]) != nil) { - source_ = source; - key_ = [source key]; - } return self; -} - -- (void) reloadData { - source_ = [database_ sourceWithKey:key_]; - key_ = [source_ key]; - [self setObject:source_]; - - [[self navigationItem] setTitle:[source_ label]]; - - [super reloadData]; +- (void) setFetch:(NSNumber *)fetch { + if ([fetch boolValue]) + [indicator_ startAnimating]; + else + [indicator_ stopAnimating]; } @end @@ -7750,6 +8182,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UITableViewDelegate > { _transient Database *database_; + unsigned era_; + _H list_; _H sources_; int offset_; @@ -7758,17 +8192,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _H hud_; _H error_; - //NSURLConnection *installer_; - NSURLConnection *trivial_; NSURLConnection *trivial_bz2_; NSURLConnection *trivial_gz_; - //NSURLConnection *automatic_; BOOL cydia_; } - (id) initWithDatabase:(Database *)database; -- (void) updateButtonsForEditingStatus:(BOOL)editing animated:(BOOL)animated; +- (void) updateButtonsForEditingStatusAnimated:(BOOL)animated; @end @@ -7783,11 +8214,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) dealloc { - //[self _releaseConnection:installer_]; - [self _releaseConnection:trivial_]; [self _releaseConnection:trivial_gz_]; [self _releaseConnection:trivial_bz2_]; - //[self _releaseConnection:automatic_]; [super dealloc]; } @@ -7802,78 +8230,90 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView { - return offset_ == 0 ? 1 : 2; + return 2; } - (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - switch (section + (offset_ == 0 ? 1 : 0)) { - case 0: return UCLocalize("ENTERED_BY_USER"); - case 1: return UCLocalize("INSTALLED_BY_PACKAGE"); - - _nodefault - } + if (section == 1) + return UCLocalize("INDIVIDUAL_SOURCES"); + return nil; } - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - int count = [sources_ count]; switch (section) { - case 0: return (offset_ == 0 ? count : offset_); - case 1: return count - offset_; - - _nodefault + case 0: return 1; + case 1: return [sources_ count]; + default: return 0; } } - (Source *) sourceAtIndexPath:(NSIndexPath *)indexPath { - unsigned idx = 0; - switch (indexPath.section) { - case 0: idx = indexPath.row; break; - case 1: idx = indexPath.row + offset_; break; - - _nodefault - } - return [sources_ objectAtIndex:idx]; -} +@synchronized (database_) { + if ([database_ era] != era_) + return nil; + if ([indexPath section] != 1) + return nil; + NSUInteger index([indexPath row]); + if (index >= [sources_ count]) + return nil; + return [sources_ objectAtIndex:index]; +} } - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"SourceCell"; SourceCell *cell = (SourceCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; - if(cell == nil) cell = [[[SourceCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellIdentifier] autorelease]; - [cell setSource:[self sourceAtIndexPath:indexPath]]; + if (cell == nil) cell = [[[SourceCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellIdentifier] autorelease]; [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; + Source *source([self sourceAtIndexPath:indexPath]); + if (source == nil) + [cell setAllSource]; + else + [cell setSource:source]; + return cell; } - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - Source *source = [self sourceAtIndexPath:indexPath]; - - SourceController *controller = [[[SourceController alloc] + SectionsController *controller([[[SectionsController alloc] initWithDatabase:database_ - source:source - ] autorelease]; + source:[self sourceAtIndexPath:indexPath] + ] autorelease]); [controller setDelegate:delegate_]; - [[self navigationController] pushViewController:controller animated:YES]; } - (BOOL) tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { + if ([indexPath section] != 1) + return false; Source *source = [self sourceAtIndexPath:indexPath]; return [source record] != nil; } - (void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { + _assert([indexPath section] == 1); if (editingStyle == UITableViewCellEditingStyleDelete) { Source *source = [self sourceAtIndexPath:indexPath]; + if (source == nil) return; + [Sources_ removeObjectForKey:[source key]]; - [delegate_ syncData]; + Changed_ = true; + + [delegate_ _saveConfig]; + [delegate_ reloadDataWithInvocation:nil]; } } +- (void) tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath { + [self updateButtonsForEditingStatusAnimated:YES]; +} + - (void) complete { [delegate_ addTrivialSource:href_]; + href_ = nil; + [delegate_ syncData]; } @@ -7884,7 +8324,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { href = [href substringFromIndex:(colon.location + 3)]; href = [href stringByAddingPercentEscapes]; href = [CydiaURL(@"api/repotag/") stringByAppendingString:href]; - href = [href stringByCachingURLWithCurrentCDN]; NSURL *url([NSURL URLWithString:href]); @@ -7900,9 +8339,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { // XXX: the memory management in this method is horribly awkward NSURLConnection **field = NULL; - if (connection == trivial_) - field = &trivial_; - else if (connection == trivial_bz2_) + if (connection == trivial_bz2_) field = &trivial_bz2_; else if (connection == trivial_gz_) field = &trivial_gz_; @@ -7911,21 +8348,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { *field = nil; if ( - trivial_ == nil && trivial_bz2_ == nil && trivial_gz_ == nil ) { + NSString *warning(cydia_ ? [self yieldToSelector:@selector(getWarning)] : nil); + [delegate_ releaseNetworkActivityIndicator]; [delegate_ removeProgressHUD:hud_]; hud_ = nil; - bool defer(false); - if (cydia_) { - if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) { - defer = true; - + if (warning != nil) { UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:UCLocalize("SOURCE_WARNING") message:warning @@ -7939,8 +8373,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [alert setContext:@"warning"]; [alert setNumberOfRows:1]; [alert show]; - } else - [self complete]; + + // XXX: there used to be this great mechanism called yieldToPopup... who deleted it? + error_ = nil; + return; + } + + [self complete]; } else if (error_ != nil) { UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:UCLocalize("VERIFICATION_ERROR") @@ -7952,6 +8391,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [alert setContext:@"urlerror"]; [alert show]; + + href_ = nil; } else { UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:UCLocalize("NOT_REPOSITORY") @@ -7963,9 +8404,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [alert setContext:@"trivial"]; [alert show]; + + href_ = nil; } - href_ = nil; error_ = nil; } } @@ -7978,7 +8420,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { - lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]); + lprintf("connection:\"%s\" didFailWithError:\"%s\"\n", [href_ UTF8String], [[error localizedDescription] UTF8String]); error_ = error; [self _endConnection:connection]; } @@ -7988,19 +8430,27 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method { + NSURL *url([NSURL URLWithString:href]); + NSMutableURLRequest *request = [NSMutableURLRequest - requestWithURL:[NSURL URLWithString:href] + requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:120.0 + timeoutInterval:10 ]; [request setHTTPMethod:method]; if (Machine_ != NULL) [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; + if (UniqueID_ != nil) [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"]; + if ([url isCydiaSecure]) { + if (UniqueID_ != nil) + [request setValue:UniqueID_ forHTTPHeaderField:@"X-Cydia-Id"]; + } + return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease]; } @@ -8012,17 +8462,29 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { case 1: { NSString *href = [[alert textField] text]; - //installer_ = [[self _requestHRef:href method:@"GET"] retain]; + static Pcre href_r("^http(s?)://[^# ]*$"); + if (!href_r(href)) { + UIAlertView *alert = [[[UIAlertView alloc] + initWithTitle:Error_ + message:UCLocalize("INVALID_URL") + delegate:self + cancelButtonTitle:UCLocalize("OK") + otherButtonTitles:nil + ] autorelease]; + + [alert setContext:@"badurl"]; + [alert show]; + + break; + } if (![href hasSuffix:@"/"]) href_ = [href stringByAppendingString:@"/"]; else href_ = href; - trivial_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages"] method:@"HEAD"] retain]; trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain]; trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain]; - //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain]; cydia_ = false; @@ -8046,7 +8508,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { else if ([context isEqualToString:@"warning"]) { switch (button) { case 1: - [self complete]; + [self performSelector:@selector(complete) withObject:nil afterDelay:0]; break; case 0: @@ -8055,323 +8517,151 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _nodefault } - href_ = nil; - [alert dismissWithClickedButtonIndex:-1 animated:YES]; } } -- (void) loadView { - [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; +- (void) updateButtonsForEditingStatusAnimated:(BOOL)animated { + BOOL editing([list_ isEditing]); - list_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain] autorelease]; + if (editing) + [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("ADD") + style:UIBarButtonItemStylePlain + target:self + action:@selector(addButtonClicked) + ] autorelease] animated:animated]; + else if ([delegate_ updating]) + [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("CANCEL") + style:UIBarButtonItemStyleDone + target:self + action:@selector(cancelButtonClicked) + ] autorelease] animated:animated]; + else + [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] + initWithTitle:UCLocalize("REFRESH") + style:UIBarButtonItemStylePlain + target:self + action:@selector(refreshButtonClicked) + ] autorelease] animated:animated]; + + [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc] + initWithTitle:(editing ? UCLocalize("DONE") : UCLocalize("EDIT")) + style:(editing ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain) + target:self + action:@selector(editButtonClicked) + ] autorelease] animated:animated]; +} + +- (void) loadView { + list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain] autorelease]; [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; - [list_ setRowHeight:56]; + [list_ setRowHeight:53]; [(UITableView *) list_ setDataSource:self]; [list_ setDelegate:self]; - [[self view] addSubview:list_]; + [self setView:list_]; } - (void) viewDidLoad { [super viewDidLoad]; [[self navigationItem] setTitle:UCLocalize("SOURCES")]; - [self updateButtonsForEditingStatus:NO animated:NO]; + [self updateButtonsForEditingStatusAnimated:NO]; +} + +- (void) viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + [list_ setEditing:NO]; + [self updateButtonsForEditingStatusAnimated:NO]; } - (void) releaseSubviews { list_ = nil; + + sources_ = nil; + + [super releaseSubviews]; } - (id) initWithDatabase:(Database *)database { if ((self = [super init]) != nil) { database_ = database; - sources_ = [NSMutableArray arrayWithCapacity:16]; } return self; } - (void) reloadData { [super reloadData]; + [self updateButtonsForEditingStatusAnimated:YES]; - pkgSourceList list; - if ([database_ popErrorWithTitle:UCLocalize("SOURCES") forOperation:list.ReadMainList()]) - return; +@synchronized (database_) { + era_ = [database_ era]; - [sources_ removeAllObjects]; + sources_ = [NSMutableArray arrayWithCapacity:16]; [sources_ addObjectsFromArray:[database_ sources]]; _trace(); - [sources_ sortUsingSelector:@selector(compareByNameAndType:)]; + [sources_ sortUsingSelector:@selector(compareByName:)]; _trace(); int count([sources_ count]); offset_ = 0; - for (int i = 0; i != count; i++) { - if ([[sources_ objectAtIndex:i] record] == nil) - break; - offset_++; - } - - [list_ setEditing:NO]; - [self updateButtonsForEditingStatus:NO animated:NO]; - [list_ reloadData]; -} - -- (void) showAddSourcePrompt { - UIAlertView *alert = [[[UIAlertView alloc] - initWithTitle:UCLocalize("ENTER_APT_URL") - message:nil - delegate:self - cancelButtonTitle:UCLocalize("CANCEL") - otherButtonTitles: - UCLocalize("ADD_SOURCE"), - nil - ] autorelease]; - - [alert setContext:@"source"]; - - [alert setNumberOfRows:1]; - [alert addTextFieldWithValue:@"http://" label:@""]; - - UITextInputTraits *traits = [[alert textField] textInputTraits]; - [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; - [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; - [traits setKeyboardType:UIKeyboardTypeURL]; - // XXX: UIReturnKeyDone - [traits setReturnKeyType:UIReturnKeyNext]; - - [alert show]; -} - -- (void) addButtonClicked { - [self showAddSourcePrompt]; -} - -- (void) updateButtonsForEditingStatus:(BOOL)editing animated:(BOOL)animated { - [[self navigationItem] setLeftBarButtonItem:(editing ? [[[UIBarButtonItem alloc] - initWithTitle:UCLocalize("ADD") - style:UIBarButtonItemStylePlain - target:self - action:@selector(addButtonClicked) - ] autorelease] : [[self navigationItem] backBarButtonItem]) animated:animated]; - - [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc] - initWithTitle:(editing ? UCLocalize("DONE") : UCLocalize("EDIT")) - style:(editing ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain) - target:self - action:@selector(editButtonClicked) - ] autorelease] animated:animated]; - - if (IsWildcat_ && !editing) - [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] - initWithTitle:UCLocalize("SETTINGS") - style:UIBarButtonItemStylePlain - target:self - action:@selector(settingsButtonClicked) - ] autorelease]]; -} - -- (void) settingsButtonClicked { - [delegate_ showSettings]; -} - -- (void) editButtonClicked { - [list_ setEditing:![list_ isEditing] animated:YES]; - - [self updateButtonsForEditingStatus:[list_ isEditing] animated:YES]; -} - -@end -/* }}} */ - -/* Settings Controller {{{ */ -@interface SettingsController : CyteViewController < - UITableViewDataSource, - UITableViewDelegate -> { - _transient Database *database_; - // XXX: ok, "roledelegate_"?... - _transient id roledelegate_; - _H table_; - _H segment_; - _H container_; -} - -- (void) showDoneButton; -- (void) resizeSegmentedControl; - -@end - -@implementation SettingsController - -- (void) loadView { - [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; - - table_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped] autorelease]; - [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; - [table_ setDelegate:self]; - [(UITableView *) table_ setDataSource:self]; - [[self view] addSubview:table_]; - - NSArray *items = [NSArray arrayWithObjects: - UCLocalize("USER"), - UCLocalize("HACKER"), - UCLocalize("DEVELOPER"), - nil]; - segment_ = [[[UISegmentedControl alloc] initWithItems:items] autorelease]; - container_ = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, 44.0f)] autorelease]; - [container_ addSubview:segment_]; -} - -- (void) viewDidLoad { - [super viewDidLoad]; - - [[self navigationItem] setTitle:UCLocalize("WHO_ARE_YOU")]; - - int index = -1; - if ([Role_ isEqualToString:@"User"]) index = 0; - if ([Role_ isEqualToString:@"Hacker"]) index = 1; - if ([Role_ isEqualToString:@"Developer"]) index = 2; - if (index != -1) { - [segment_ setSelectedSegmentIndex:index]; - [self showDoneButton]; - } - - [segment_ addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged]; - [self resizeSegmentedControl]; -} - -- (void) releaseSubviews { - table_ = nil; - segment_ = nil; - container_ = nil; -} - -- (id) initWithDatabase:(Database *)database delegate:(id)delegate { - if ((self = [super init]) != nil) { - database_ = database; - roledelegate_ = delegate; - } return self; -} - -- (void) resizeSegmentedControl { - CGFloat width = [[self view] frame].size.width; - [segment_ setFrame:CGRectMake(width / 32.0f, 0, width - (width / 32.0f * 2.0f), 44.0f)]; -} - -- (void) viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - [self resizeSegmentedControl]; -} - -- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration { - [self resizeSegmentedControl]; -} - -- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - [self resizeSegmentedControl]; -} - -- (void) save { - NSString *role(nil); - - switch ([segment_ selectedSegmentIndex]) { - case 0: role = @"User"; break; - case 1: role = @"Hacker"; break; - case 2: role = @"Developer"; break; - - _nodefault - } - - if (![role isEqualToString:Role_]) { - bool rolling(Role_ == nil); - Role_ = role; - - Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys: - Role_, @"Role", - nil]; - - [Metadata_ setObject:Settings_ forKey:@"Settings"]; - Changed_ = true; - - if (rolling) - [roledelegate_ loadData]; - else - [roledelegate_ updateData]; - } -} - -- (void) segmentChanged:(UISegmentedControl *)control { - [self showDoneButton]; -} - -- (void) saveAndClose { - [self save]; - - [[self navigationItem] setRightBarButtonItem:nil]; - [[self navigationController] dismissModalViewControllerAnimated:YES]; -} + for (int i = 0; i != count; i++) { + if ([[sources_ objectAtIndex:i] record] == nil) + break; + offset_++; + } -- (void) doneButtonClicked { - UIActivityIndicatorView *spinner = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20.0f, 20.0f)] autorelease]; - [spinner startAnimating]; - UIBarButtonItem *spinItem = [[[UIBarButtonItem alloc] initWithCustomView:spinner] autorelease]; - [[self navigationItem] setRightBarButtonItem:spinItem]; + [list_ reloadData]; +} } - [self performSelector:@selector(saveAndClose) withObject:nil afterDelay:0]; -} +- (void) showAddSourcePrompt { + UIAlertView *alert = [[[UIAlertView alloc] + initWithTitle:UCLocalize("ENTER_APT_URL") + message:nil + delegate:self + cancelButtonTitle:UCLocalize("CANCEL") + otherButtonTitles: + UCLocalize("ADD_SOURCE"), + nil + ] autorelease]; -- (void) showDoneButton { - [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc] - initWithTitle:UCLocalize("DONE") - style:UIBarButtonItemStyleDone - target:self - action:@selector(doneButtonClicked) - ] autorelease] animated:([[self navigationItem] rightBarButtonItem] == nil)]; -} + [alert setContext:@"source"]; -- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView { - // XXX: For not having a single cell in the table, this sure is a lot of sections. - return 6; -} + [alert setNumberOfRows:1]; + [alert addTextFieldWithValue:@"http://" label:@""]; -- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return 0; // :( -} + UITextInputTraits *traits = [[alert textField] textInputTraits]; + [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; + [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; + [traits setKeyboardType:UIKeyboardTypeURL]; + // XXX: UIReturnKeyDone + [traits setReturnKeyType:UIReturnKeyNext]; -- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - return nil; // This method is required by the protocol. + [alert show]; } -- (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { - if (section == 1) - return UCLocalize("ROLE_EX"); - if (section == 4) - return [NSString stringWithFormat: - @"%@: %@\n%@: %@\n%@: %@", - UCLocalize("USER"), UCLocalize("USER_EX"), - UCLocalize("HACKER"), UCLocalize("HACKER_EX"), - UCLocalize("DEVELOPER"), UCLocalize("DEVELOPER_EX") - ]; - else return nil; +- (void) addButtonClicked { + [self showAddSourcePrompt]; } -- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { - return section == 3 ? 44.0f : 0; +- (void) refreshButtonClicked { + if ([delegate_ requestUpdate]) + [self updateButtonsForEditingStatusAnimated:YES]; } -- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { - return section == 3 ? container_ : nil; +- (void) cancelButtonClicked { + [delegate_ cancelUpdate]; } -- (void) reloadData { - [super reloadData]; - - [table_ reloadData]; +- (void) editButtonClicked { + [list_ setEditing:![list_ isEditing] animated:YES]; + [self updateButtonsForEditingStatusAnimated:YES]; } @end /* }}} */ + /* Stash Controller {{{ */ @interface StashController : CyteViewController { _H spinner_; @@ -8384,8 +8674,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation StashController - (void) loadView { - [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; - [[self view] setBackgroundColor:[UIColor viewFlipsideBackgroundColor]]; + UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]); + [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + [self setView:view]; + + [view setBackgroundColor:[UIColor viewFlipsideBackgroundColor]]; spinner_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease]; CGRect spinrect = [spinner_ frame]; @@ -8393,7 +8686,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { spinrect.origin.y = [[self view] frame].size.height - 80.0f; [spinner_ setFrame:spinrect]; [spinner_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin]; - [[self view] addSubview:spinner_]; + [view addSubview:spinner_]; [spinner_ startAnimating]; CGRect captrect; @@ -8408,8 +8701,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [caption_ setTextColor:[UIColor whiteColor]]; [caption_ setBackgroundColor:[UIColor clearColor]]; [caption_ setShadowColor:[UIColor blackColor]]; - [caption_ setTextAlignment:UITextAlignmentCenter]; - [[self view] addSubview:caption_]; + [caption_ setTextAlignment:NSTextAlignmentCenter]; + [view addSubview:caption_]; CGRect statusrect; statusrect.size.width = [[self view] frame].size.width; @@ -8423,8 +8716,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [status_ setTextColor:[UIColor whiteColor]]; [status_ setBackgroundColor:[UIColor clearColor]]; [status_ setShadowColor:[UIColor blackColor]]; - [status_ setTextAlignment:UITextAlignmentCenter]; - [[self view] addSubview:status_]; + [status_ setTextAlignment:NSTextAlignmentCenter]; + [view addSubview:status_]; +} + +- (void) releaseSubviews { + spinner_ = nil; + status_ = nil; + caption_ = nil; + + [super releaseSubviews]; } @end @@ -8457,6 +8758,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { #endif } +- (void) storeCachedResponse:(NSCachedURLResponse *)cached forRequest:(NSURLRequest *)request { + if (NSURLResponse *response = [cached response]) + if (NSString *mime = [response MIMEType]) + if ([mime isEqualToString:@"text/cache-manifest"]) { + NSURL *url([response URL]); + +#if !ForRelease + NSLog(@"###: %@", [url absoluteString]); +#endif + + @synchronized (HostConfig_) { + [CachedURLs_ addObject:url]; + } + } + + [super storeCachedResponse:cached forRequest:request]; +} + @end @interface Cydia : UIApplication < @@ -8467,8 +8786,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UITabBarControllerDelegate > { _H window_; - _H tabbar_; - _H emulated_; + _H tabbar_; + _H emulated_; _H essential_; _H broken_; @@ -8491,10 +8810,52 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation Cydia +- (void) lockSuspend { + if (locked_++ == 0) { + if ($SBSSetInterceptsMenuButtonForever != NULL) + (*$SBSSetInterceptsMenuButtonForever)(true); + + [self setIdleTimerDisabled:YES]; + } +} + +- (void) unlockSuspend { + if (--locked_ == 0) { + [self setIdleTimerDisabled:NO]; + + if ($SBSSetInterceptsMenuButtonForever != NULL) + (*$SBSSetInterceptsMenuButtonForever)(false); + } +} + - (void) beginUpdate { [tabbar_ beginUpdate]; } +- (void) cancelUpdate { + [tabbar_ cancelUpdate]; +} + +- (bool) requestUpdate { + if (IsReachable("cydia.saurik.com")) { + [self beginUpdate]; + return true; + } else { + UIAlertView *alert = [[[UIAlertView alloc] + initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("REFRESH")] + message:@"Host Unreachable" // XXX: Localize + delegate:self + cancelButtonTitle:UCLocalize("OK") + otherButtonTitles:nil + ] autorelease]; + + [alert setContext:@"norefresh"]; + [alert show]; + + return false; + } +} + - (BOOL) updating { return [tabbar_ updating]; } @@ -8507,7 +8868,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { initWithTitle:(count == 1 ? UCLocalize("HALFINSTALLED_PACKAGE") : [NSString stringWithFormat:UCLocalize("HALFINSTALLED_PACKAGES"), count]) message:UCLocalize("HALFINSTALLED_PACKAGE_EX") delegate:self - cancelButtonTitle:UCLocalize("FORCIBLY_CLEAR") + cancelButtonTitle:[NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("FORCIBLY_CLEAR"), UCLocalize("UNSAFE")] otherButtonTitles: UCLocalize("TEMPORARY_IGNORE"), nil @@ -8535,10 +8896,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } +- (void) returnToCydia { + [self _loaded]; +} + - (void) _saveConfig { - _trace(); - MetaFile_.Sync(); - _trace(); + @synchronized (database_) { + _trace(); + MetaFile_.Sync(); + _trace(); + } if (Changed_) { NSString *error(nil); @@ -8555,6 +8922,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSLog(@"failure to serialize metadata: %@", error); } } + + CydiaWriteSources(); } // Navigation controller for the queuing badge. @@ -8569,7 +8938,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) _updateData { [self _saveConfig]; - [self unloadData]; UINavigationController *navigation = [self queueNavigationController]; @@ -8596,7 +8964,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { // - We already refreshed recently. // - We already auto-refreshed this launch. // - Auto-refresh is disabled. - if (recently || loaded_ || ManualRefresh) { + // - Cydia's server is not reachable + if (recently || loaded_ || ManualRefresh || !IsReachable("cydia.saurik.com")) { // If we are cancelling, we need to make sure it knows it's already loaded. loaded_ = true; @@ -8605,27 +8974,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { // We are going to load, so remember that. loaded_ = true; - SCNetworkReachabilityFlags flags; { - SCNetworkReachabilityRef reachability(SCNetworkReachabilityCreateWithName(NULL, "cydia.saurik.com")); - SCNetworkReachabilityGetFlags(reachability, &flags); - CFRelease(reachability); - } - - // XXX: this elaborate mess is what Apple is using to determine this? :( - // XXX: do we care if the user has to intervene? maybe that's ok? - bool reachable( - (flags & kSCNetworkReachabilityFlagsReachable) != 0 && ( - (flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0 || ( - (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0 || - (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0 - ) && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0 || - (flags & kSCNetworkReachabilityFlagsIsWWAN) != 0 - ) - ); - - // If we can reach the server, auto-refresh! - if (reachable) - [tabbar_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO]; + [tabbar_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO]; } [pool release]; @@ -8635,15 +8984,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [NSThread detachNewThreadSelector:@selector(_refreshIfPossible:) toTarget:self withObject:[Metadata_ objectForKey:@"LastUpdate"]]; } -- (void) _reloadDataWithInvocation:(NSInvocation *)invocation { +- (void) reloadDataWithInvocation:(NSInvocation *)invocation { +@synchronized (self) { UIProgressHUD *hud(loaded_ ? [self addProgressHUD] : nil); - [hud setText:UCLocalize("RELOADING_DATA")]; + if (hud != nil) + [hud setText:UCLocalize("RELOADING_DATA")]; [database_ yieldToSelector:@selector(reloadDataWithInvocation:) withObject:invocation]; - if (hud != nil) - [self removeProgressHUD:hud]; - size_t changes(0); [essential_ removeAllObjects]; @@ -8653,15 +9001,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { for (Package *package in packages) { if ([package half]) [broken_ addObject:package]; - if ([package upgradableAndEssential:NO]) { - if ([package essential]) + if ([package upgradableAndEssential:YES] && ![package ignored]) { + if ([package essential] && [package installed] != nil) [essential_ addObject:package]; ++changes; } } - NSLog(@"changes:#%u", changes); - UITabBarItem *changesItem = [[[tabbar_ viewControllers] objectAtIndex:2] tabBarItem]; if (changes != 0) { _trace(); @@ -8678,21 +9024,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self _updateData]; - [self refreshIfPossible]; -} + if (hud != nil) + [self removeProgressHUD:hud]; +} } - (void) updateData { [self _updateData]; } -- (void) update_ { - [database_ update]; +- (void) updateDataAndLoad { + [self _updateData]; + if ([database_ progressDelegate] == nil) + [self _loaded]; } -- (void) complete { - @synchronized (self) { - [self _reloadDataWithInvocation:nil]; - } +- (void) update_ { + [database_ update]; + [self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES]; } - (void) disemulate { @@ -8700,6 +9048,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return; [window_ addSubview:[tabbar_ view]]; + if ([window_ respondsToSelector:@selector(setRootViewController:)]) + [window_ setRootViewController:tabbar_]; [[emulated_ view] removeFromSuperview]; emulated_ = nil; [window_ setUserInteractionEnabled:YES]; @@ -8749,47 +9099,31 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self performSelectorOnMainThread:@selector(repairWithInvocation:) withObject:[NSInvocation invocationWithSelector:selector forTarget:database_] waitUntilDone:YES]; } +- (void) reloadData { + [self reloadDataWithInvocation:nil]; + if ([database_ progressDelegate] == nil) + [self _loaded]; +} + - (void) syncData { [self _saveConfig]; - - FILE *file(fopen("/etc/apt/sources.list.d/cydia.list", "w")); - _assert(file != NULL); - - for (NSString *key in [Sources_ allKeys]) { - NSDictionary *source([Sources_ objectForKey:key]); - - fprintf(file, "%s %s %s\n", - [[source objectForKey:@"Type"] UTF8String], - [[source objectForKey:@"URI"] UTF8String], - [[source objectForKey:@"Distribution"] UTF8String] - ); - } - - fclose(file); - [self detachNewProgressSelector:@selector(update_) toTarget:self forController:nil title:@"UPDATING_SOURCES"]; - - [self complete]; } -- (void) addTrivialSource:(NSString *)href { - [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: - @"deb", @"Type", - href, @"URI", - @"./", @"Distribution", - nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href]]; +- (void) addSource:(NSDictionary *) source { + CydiaAddSource(source); +} - Changed_ = true; +- (void) addSource:(NSString *)href withDistribution:(NSString *)distribution andSections:(NSArray *)sections { + CydiaAddSource(href, distribution, sections); } -- (void) reloadDataWithInvocation:(NSInvocation *)invocation { - @synchronized (self) { - [self _reloadDataWithInvocation:invocation]; - } +- (void) addTrivialSource:(NSString *)href { + CydiaAddSource(href, @"./"); } -- (void) reloadData { - [self reloadDataWithInvocation:nil]; +- (void) updateValues { + Changed_ = true; } - (void) resolve { @@ -8869,16 +9203,30 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } -- (void) confirmWithNavigationController:(UINavigationController *)navigation { - Queuing_ = false; - ++locked_; - [self detachNewProgressSelector:@selector(perform) toTarget:database_ forController:navigation title:@"RUNNING"]; - --locked_; - [self complete]; +- (void) _uicache { + _trace(); + system("su -c /usr/bin/uicache mobile"); + _trace(); +} + +- (void) uicache { + UIProgressHUD *hud([self addProgressHUD]); + [hud setText:UCLocalize("LOADING")]; + [self yieldToSelector:@selector(_uicache)]; + [self removeProgressHUD:hud]; +} + +- (void) perform_ { + [database_ perform]; + [self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES]; + [self performSelectorOnMainThread:@selector(uicache) withObject:nil waitUntilDone:YES]; } -- (void) showSettings { - [self presentModalViewController:[[[SettingsController alloc] initWithDatabase:database_ delegate:self] autorelease] force:NO]; +- (void) confirmWithNavigationController:(UINavigationController *)navigation { + Queuing_ = false; + [self lockSuspend]; + [self detachNewProgressSelector:@selector(perform_) toTarget:self forController:navigation title:@"RUNNING"]; + [self unlockSuspend]; } - (void) retainNetworkActivityIndicator { @@ -9028,7 +9376,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (UIProgressHUD *) addProgressHUD { - UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]); + UIProgressHUD *hud([[[UIProgressHUD alloc] init] autorelease]); [hud setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; [window_ setUserInteractionEnabled:NO]; @@ -9037,35 +9385,36 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (UIViewController *modal = [target modalViewController]) target = modal; - UIView *view([target view]); - [view addSubview:hud]; - - [hud show:YES]; + [hud showInView:[target view]]; - ++locked_; + [self lockSuspend]; return hud; } - (void) removeProgressHUD:(UIProgressHUD *)hud { - --locked_; - [hud show:NO]; + [self unlockSuspend]; + [hud hide]; [hud removeFromSuperview]; [window_ setUserInteractionEnabled:YES]; } -- (CyteViewController *) pageForPackage:(NSString *)name { - return [[[CYPackageController alloc] initWithDatabase:database_ forPackage:name] autorelease]; +- (CyteViewController *) pageForPackage:(NSString *)name withReferrer:(NSString *)referrer { + return [[[CYPackageController alloc] initWithDatabase:database_ forPackage:name withReferrer:referrer] autorelease]; } -- (CyteViewController *) pageForURL:(NSURL *)url forExternal:(BOOL)external { +- (CyteViewController *) pageForURL:(NSURL *)url forExternal:(BOOL)external withReferrer:(NSString *)referrer { NSString *scheme([[url scheme] lowercaseString]); if ([[url absoluteString] length] <= [scheme length] + 3) return nil; NSString *path([[url absoluteString] substringFromIndex:[scheme length] + 3]); - NSArray *components([path pathComponents]); + NSArray *components([path componentsSeparatedByString:@"/"]); - if ([scheme isEqualToString:@"apptapp"] && [components count] > 0 && [[components objectAtIndex:0] isEqualToString:@"package"]) - return [self pageForPackage:[components objectAtIndex:1]]; + if ([scheme isEqualToString:@"apptapp"] && [components count] > 0 && [[components objectAtIndex:0] isEqualToString:@"package"]) { + CyteViewController *controller([self pageForPackage:[components objectAtIndex:1] withReferrer:referrer]); + if (controller != nil) + [controller setDelegate:self]; + return controller; + } if ([components count] < 1 || ![scheme isEqualToString:@"cydia"]) return nil; @@ -9079,10 +9428,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSString *destination = [[url absoluteString] substringFromIndex:([scheme length] + [@"://" length] + [base length] + [@"/" length])]; controller = [[[CydiaWebViewController alloc] initWithURL:[NSURL URLWithString:destination]] autorelease]; } else if (!external && [components count] == 1) { - if ([base isEqualToString:@"manage"]) { - controller = [[[ManageController alloc] init] autorelease]; - } - if ([base isEqualToString:@"sources"]) { controller = [[[SourcesController alloc] initWithDatabase:database_] autorelease]; } @@ -9092,7 +9437,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } if ([base isEqualToString:@"sections"]) { - controller = [[[SectionsController alloc] initWithDatabase:database_] autorelease]; + controller = [[[SectionsController alloc] initWithDatabase:database_ source:nil] autorelease]; } if ([base isEqualToString:@"search"]) { @@ -9107,10 +9452,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { controller = [[[InstalledController alloc] initWithDatabase:database_] autorelease]; } } else if ([components count] == 2) { - NSString *argument = [components objectAtIndex:1]; + NSString *argument = [[components objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; if ([base isEqualToString:@"package"]) { - controller = [self pageForPackage:argument]; + controller = [self pageForPackage:argument withReferrer:referrer]; } if (!external && [base isEqualToString:@"search"]) { @@ -9118,9 +9463,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } if (!external && [base isEqualToString:@"sections"]) { - if ([argument isEqualToString:@"all"]) + if ([argument isEqualToString:@"all"] || [argument isEqualToString:@"*"]) argument = nil; - controller = [[[SectionController alloc] initWithDatabase:database_ section:argument] autorelease]; + controller = [[[SectionController alloc] initWithDatabase:database_ source:nil section:argument] autorelease]; } if (!external && [base isEqualToString:@"sources"]) { @@ -9128,8 +9473,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { controller = [[[SourcesController alloc] initWithDatabase:database_] autorelease]; [(SourcesController *)controller showAddSourcePrompt]; } else { - Source *source = [database_ sourceWithKey:argument]; - controller = [[[SourceController alloc] initWithDatabase:database_ source:source] autorelease]; + Source *source([database_ sourceWithKey:argument]); + controller = [[[SectionsController alloc] initWithDatabase:database_ source:source] autorelease]; } } @@ -9138,8 +9483,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return nil; } } else if (!external && [components count] == 3) { - NSString *arg1 = [components objectAtIndex:1]; - NSString *arg2 = [components objectAtIndex:2]; + NSString *arg1 = [[components objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *arg2 = [[components objectAtIndex:2] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; if ([base isEqualToString:@"package"]) { if ([arg2 isEqualToString:@"settings"]) { @@ -9151,6 +9496,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } } + + if ([base isEqualToString:@"sections"]) { + Source *source([arg1 isEqualToString:@"*"] ? nil : [database_ sourceWithKey:arg1]); + NSString *section([arg2 isEqualToString:@"*"] ? nil : arg2); + controller = [[[SectionController alloc] initWithDatabase:database_ source:source section:section] autorelease]; + } } [controller setDelegate:self]; @@ -9158,13 +9509,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (BOOL) openCydiaURL:(NSURL *)url forExternal:(BOOL)external { - CyteViewController *page([self pageForURL:url forExternal:external]); + CyteViewController *page([self pageForURL:url forExternal:external withReferrer:nil]); - if (page != nil) { - UINavigationController *nav = [[[UINavigationController alloc] init] autorelease]; - [nav setViewControllers:[NSArray arrayWithObject:page]]; - [tabbar_ setUnselectedViewController:nav]; - } + if (page != nil) + [tabbar_ setUnselectedViewController:page]; return page != nil; } @@ -9187,15 +9535,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super applicationWillResignActive:application]; } -- (void) applicationWillTerminate:(UIApplication *)application { - Changed_ = true; +- (void) saveState { [Metadata_ setObject:[tabbar_ navigationURLCollection] forKey:@"InterfaceState"]; [Metadata_ setObject:[NSDate date] forKey:@"LastClosed"]; [Metadata_ setObject:[NSNumber numberWithInt:[tabbar_ selectedIndex]] forKey:@"InterfaceIndex"]; + Changed_ = true; [self _saveConfig]; } +- (void) applicationWillTerminate:(UIApplication *)application { + [self saveState]; +} + - (void) setConfigurationData:(NSString *)data { static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$"); @@ -9224,7 +9576,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) addStashController { - ++locked_; + [self lockSuspend]; stash_ = [[[StashController alloc] init] autorelease]; [window_ addSubview:[stash_ view]]; } @@ -9232,12 +9584,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) removeStashController { [[stash_ view] removeFromSuperview]; stash_ = nil; - --locked_; + [self unlockSuspend]; } - (void) stash { - [self setIdleTimerDisabled:YES]; - [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque]; UpdateExternalStatus(1); [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"]; @@ -9245,27 +9595,35 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self removeStashController]; - if (ExecFork() == 0) { + pid_t pid(ExecFork()); + if (pid == 0) { execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL); perror("launchctl stop"); - } + + exit(0); + } ReapZombie(pid); } - (void) setupViewControllers { - tabbar_ = [[[CYTabBarController alloc] initWithDatabase:database_] autorelease]; - - NSMutableArray *items([NSMutableArray arrayWithObjects: - [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:0] autorelease], - [[[UITabBarItem alloc] initWithTitle:UCLocalize("SECTIONS") image:[UIImage applicationImageNamed:@"install.png"] tag:0] autorelease], - [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes.png"] tag:0] autorelease], - [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:0] autorelease], - nil]); - - if (IsWildcat_) { - [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"source.png"] tag:0] autorelease] atIndex:3]; - [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:0] autorelease] atIndex:3]; + tabbar_ = [[[CydiaTabBarController alloc] initWithDatabase:database_] autorelease]; + + NSMutableArray *items; + if (kCFCoreFoundationVersionNumber < 800) { + items = [NSMutableArray arrayWithObjects: + [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:0] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"install.png"] tag:0] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes.png"] tag:0] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:0] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:0] autorelease], + nil]; } else { - [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("MANAGE") image:[UIImage applicationImageNamed:@"manage.png"] tag:0] autorelease] atIndex:3]; + items = [NSMutableArray arrayWithObjects: + [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home7.png"] selectedImage:[UIImage applicationImageNamed:@"home7s.png"]] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"install7.png"] selectedImage:[UIImage applicationImageNamed:@"install7s.png"]] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes7.png"] selectedImage:[UIImage applicationImageNamed:@"changes7s.png"]] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage7.png"] selectedImage:[UIImage applicationImageNamed:@"manage7s.png"]] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search7.png"] selectedImage:[UIImage applicationImageNamed:@"search7s.png"]] autorelease], + nil]; } NSMutableArray *controllers([NSMutableArray array]); @@ -9279,7 +9637,29 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [tabbar_ setUpdateDelegate:self]; } +- (void) _sendMemoryWarningNotification { + if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) // XXX: maybe 4_0? + [[NSNotificationCenter defaultCenter] postNotificationName:@"UIApplicationMemoryWarningNotification" object:[UIApplication sharedApplication]]; + else + [[NSNotificationCenter defaultCenter] postNotificationName:@"UIApplicationDidReceiveMemoryWarningNotification" object:[UIApplication sharedApplication]]; +} + +- (void) _sendMemoryWarningNotifications { + while (true) { + [self performSelectorOnMainThread:@selector(_sendMemoryWarningNotification) withObject:nil waitUntilDone:NO]; + sleep(2); + //usleep(2000000); + } +} + +- (void) applicationDidReceiveMemoryWarning:(UIApplication *)application { + NSLog(@"--"); + [[NSURLCache sharedURLCache] removeAllCachedResponses]; +} + - (void) applicationDidFinishLaunching:(id)unused { + //[NSThread detachNewThreadSelector:@selector(_sendMemoryWarningNotifications) toTarget:self withObject:nil]; + _trace(); if ([self respondsToSelector:@selector(setApplicationSupportsShakeToEdit:)]) [self setApplicationSupportsShakeToEdit:NO]; @@ -9291,7 +9671,7 @@ _trace(); [NSURLCache setSharedURLCache:[[[CYURLCache alloc] initWithMemoryCapacity:524288 diskCapacity:10485760 - diskPath:[NSString stringWithFormat:@"%@/Library/Caches/com.saurik.Cydia/SDURLCache", @"/var/root"] + diskPath:[NSString stringWithFormat:@"%@/SDURLCache", Cache_] ] autorelease]]; [CydiaWebViewController _initialize]; @@ -9304,6 +9684,7 @@ _trace(); Font12_ = [UIFont systemFontOfSize:12]; Font12Bold_ = [UIFont boldSystemFontOfSize:12]; Font14_ = [UIFont systemFontOfSize:14]; + Font18_ = [UIFont systemFontOfSize:18]; Font18Bold_ = [UIFont boldSystemFontOfSize:18]; Font22Bold_ = [UIFont boldSystemFontOfSize:22]; @@ -9311,25 +9692,14 @@ _trace(); broken_ = [NSMutableArray arrayWithCapacity:4]; // XXX: I really need this thing... like, seriously... I'm sorry - [[[CydiaWebViewController alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/appcache/", UI_]]] reloadData]; + [[[AppCacheController alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/appcache/", UI_]]] reloadData]; window_ = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; [window_ orderFront:self]; [window_ makeKey:self]; [window_ setHidden:NO]; - if ( - readlink("/Applications", NULL, 0) == -1 && errno == EINVAL || - readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL || - readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL || - //readlink("/usr/bin", NULL, 0) == -1 && errno == EINVAL || - readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL || - readlink("/usr/lib/pam", NULL, 0) == -1 && errno == EINVAL || - readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL || - readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL || - //readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL || - false - ) { + if (false) stash: { [self addStashController]; // XXX: this would be much cleaner as a yieldToSelector: // that way the removeStashController could happen right here inline @@ -9338,14 +9708,41 @@ _trace(); return; } + struct stat root; + int error(stat("/", &root)); + _assert(error != -1); + + #define Stash_(path) do { \ + struct stat folder; \ + int error(lstat((path), &folder)); \ + if (error != -1 && ( \ + folder.st_dev == root.st_dev && \ + S_ISDIR(folder.st_mode) \ + ) || error == -1 && ( \ + errno == ENOENT || \ + errno == ENOTDIR \ + )) goto stash; \ + } while (false) + + Stash_("/Applications"); + Stash_("/Library/Ringtones"); + Stash_("/Library/Wallpaper"); + //Stash_("/usr/bin"); + Stash_("/usr/include"); + Stash_("/usr/lib/pam"); + Stash_("/usr/share"); + //Stash_("/var/lib"); + database_ = [Database sharedInstance]; [database_ setDelegate:self]; [window_ setUserInteractionEnabled:NO]; [self setupViewControllers]; - emulated_ = [[[CYEmulatedLoadingController alloc] init] autorelease]; + emulated_ = [[[CydiaLoadingViewController alloc] init] autorelease]; [window_ addSubview:[emulated_ view]]; + if ([window_ respondsToSelector:@selector(setRootViewController:)]) + [window_ setRootViewController:emulated_]; [self performSelector:@selector(loadData) withObject:nil afterDelay:0]; _trace(); @@ -9354,37 +9751,27 @@ _trace(); - (NSArray *) defaultStartPages { NSMutableArray *standard = [NSMutableArray array]; [standard addObject:[NSArray arrayWithObject:@"cydia://home"]]; - [standard addObject:[NSArray arrayWithObject:@"cydia://sections"]]; + [standard addObject:[NSArray arrayWithObject:@"cydia://sources"]]; [standard addObject:[NSArray arrayWithObject:@"cydia://changes"]]; - if (!IsWildcat_) { - [standard addObject:[NSArray arrayWithObject:@"cydia://manage"]]; - } else { - [standard addObject:[NSArray arrayWithObject:@"cydia://installed"]]; - [standard addObject:[NSArray arrayWithObject:@"cydia://sources"]]; - } + [standard addObject:[NSArray arrayWithObject:@"cydia://installed"]]; [standard addObject:[NSArray arrayWithObject:@"cydia://search"]]; return standard; } - (void) loadData { _trace(); - if (Role_ == nil) { - [window_ setUserInteractionEnabled:YES]; - [self showSettings]; - return; - } else { - if ([emulated_ modalViewController] != nil) - [emulated_ dismissModalViewControllerAnimated:YES]; - [window_ setUserInteractionEnabled:NO]; - } + if ([emulated_ modalViewController] != nil) + [emulated_ dismissModalViewControllerAnimated:YES]; + [window_ setUserInteractionEnabled:NO]; - [self reloadData]; + [self reloadDataWithInvocation:nil]; + [self refreshIfPossible]; PrintTimes(); [self disemulate]; int savedIndex = [[Metadata_ objectForKey:@"InterfaceIndex"] intValue]; - NSArray *saved = [[Metadata_ objectForKey:@"InterfaceState"] mutableCopy]; + NSArray *saved = [[[Metadata_ objectForKey:@"InterfaceState"] mutableCopy] autorelease]; int standardIndex = 0; NSArray *standard = [self defaultStartPages]; @@ -9396,8 +9783,8 @@ _trace(); NSDate *closed = [Metadata_ objectForKey:@"LastClosed"]; if (valid && closed != nil) { NSTimeInterval interval([closed timeIntervalSinceNow]); - // XXX: Is 15 minutes the optimal time here? - if (interval > 0 && interval <= -(15*60)) + // XXX: Is 30 minutes the optimal time here? + if (interval <= -(30*60)) valid = NO; } @@ -9433,7 +9820,7 @@ _trace(); for (unsigned int nav = 0; nav < [stack count]; nav++) { NSString *addr = [stack objectAtIndex:nav]; NSURL *url = [NSURL URLWithString:addr]; - CyteViewController *page = [self pageForURL:url forExternal:NO]; + CyteViewController *page = [self pageForURL:url forExternal:NO withReferrer:nil]; if (page != nil) [current addObject:page]; } @@ -9488,14 +9875,6 @@ id Dealloc_(id self, SEL selector) { return object; }*/ -Class $WebDefaultUIKitDelegate; - -MSHook(void, UIWebDocumentView$_setUIKitDelegate$, UIWebDocumentView *self, SEL _cmd, id delegate) { - if (delegate == nil && $WebDefaultUIKitDelegate != nil) - delegate = [$WebDefaultUIKitDelegate sharedUIKitDelegate]; - return _UIWebDocumentView$_setUIKitDelegate$(self, _cmd, delegate); -} - static NSSet *MobilizedFiles_; static NSURL *MobilizeURL(NSURL *url) { @@ -9537,9 +9916,10 @@ MSHook(void *, CFXPreferencesPropertyListSource$createPlistFromDisk, CFXPreferen Class $NSURLConnection; MSHook(id, NSURLConnection$init$, NSURLConnection *self, SEL _cmd, NSURLRequest *request, id delegate, BOOL usesCache, int64_t maxContentLength, BOOL startImmediately, NSDictionary *connectionProperties) { - NSMutableURLRequest *copy([request mutableCopy]); + NSMutableURLRequest *copy([[request mutableCopy] autorelease]); NSURL *url([copy URL]); + NSString *host([url host]); NSString *scheme([[url scheme] lowercaseString]); @@ -9549,12 +9929,44 @@ MSHook(id, NSURLConnection$init$, NSURLConnection *self, SEL _cmd, NSURLRequest if ([copy respondsToSelector:@selector(setHTTPShouldUsePipelining:)]) if ([PipelinedHosts_ containsObject:host] || [PipelinedHosts_ containsObject:compound]) [copy setHTTPShouldUsePipelining:YES]; + + if (NSString *control = [copy valueForHTTPHeaderField:@"Cache-Control"]) + if ([control isEqualToString:@"max-age=0"]) + if ([CachedURLs_ containsObject:url]) { +#if !ForRelease + NSLog(@"~~~: %@", url); +#endif + + [copy setCachePolicy:NSURLRequestReturnCacheDataDontLoad]; + + [copy setValue:nil forHTTPHeaderField:@"Cache-Control"]; + [copy setValue:nil forHTTPHeaderField:@"If-Modified-Since"]; + [copy setValue:nil forHTTPHeaderField:@"If-None-Match"]; + } } if ((self = _NSURLConnection$init$(self, _cmd, copy, delegate, usesCache, maxContentLength, startImmediately, connectionProperties)) != nil) { } return self; } +Class $WAKWindow; + +static CGSize $WAKWindow$screenSize(WAKWindow *self, SEL _cmd) { + CGSize size([[UIScreen mainScreen] bounds].size); + /*if ([$WAKWindow respondsToSelector:@selector(hasLandscapeOrientation)]) + if ([$WAKWindow hasLandscapeOrientation]) + std::swap(size.width, size.height);*/ + return size; +} + +Class $NSUserDefaults; + +MSHook(id, NSUserDefaults$objectForKey$, NSUserDefaults *self, SEL _cmd, NSString *key) { + if ([key respondsToSelector:@selector(isEqualToString:)] && [key isEqualToString:@"WebKitLocalStorageDatabasePathPreferenceKey"]) + return [NSString stringWithFormat:@"%@/LocalStorage", Cache_]; + return _NSUserDefaults$objectForKey$(self, _cmd, key); +} + int main(int argc, char *argv[]) { NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); @@ -9562,12 +9974,6 @@ int main(int argc, char *argv[]) { UpdateExternalStatus(0); - if (Class $UIDevice = objc_getClass("UIDevice")) { - UIDevice *device([$UIDevice currentDevice]); - IsWildcat_ = [device respondsToSelector:@selector(isWildcat)] && [device isWildcat]; - } else - IsWildcat_ = false; - UIScreen *screen([UIScreen mainScreen]); if ([screen respondsToSelector:@selector(scale)]) ScreenScale_ = [screen scale]; @@ -9575,27 +9981,37 @@ int main(int argc, char *argv[]) { ScreenScale_ = 1; UIDevice *device([UIDevice currentDevice]); - if (![device respondsToSelector:@selector(userInterfaceIdiom)]) - Idiom_ = @"iphone"; - else { + if ([device respondsToSelector:@selector(userInterfaceIdiom)]) { UIUserInterfaceIdiom idiom([device userInterfaceIdiom]); - if (idiom == UIUserInterfaceIdiomPhone) - Idiom_ = @"iphone"; - else if (idiom == UIUserInterfaceIdiomPad) - Idiom_ = @"ipad"; - else - NSLog(@"unknown UIUserInterfaceIdiom!"); + if (idiom == UIUserInterfaceIdiomPad) + IsWildcat_ = true; } + Idiom_ = IsWildcat_ ? @"ipad" : @"iphone"; + + Pcre pattern("^([0-9]+\\.[0-9]+)"); + + if (pattern([device systemVersion])) + Firmware_ = pattern[1]; + if (pattern(Cydia_)) + Major_ = pattern[1]; + SessionData_ = [NSMutableDictionary dictionaryWithCapacity:4]; HostConfig_ = [[[NSObject alloc] init] autorelease]; @synchronized (HostConfig_) { BridgedHosts_ = [NSMutableSet setWithCapacity:4]; + TokenHosts_ = [NSMutableSet setWithCapacity:4]; + InsecureHosts_ = [NSMutableSet setWithCapacity:4]; PipelinedHosts_ = [NSMutableSet setWithCapacity:4]; + CachedURLs_ = [NSMutableSet setWithCapacity:32]; } - UI_ = CydiaURL([NSString stringWithFormat:@"ui/ios~%@", Idiom_]); + NSString *ui(@"ui/ios"); + if (Idiom_ != nil) + ui = [ui stringByAppendingString:[NSString stringWithFormat:@"~%@", Idiom_]]; + ui = [ui stringByAppendingString:[NSString stringWithFormat:@"/%@", Major_]]; + UI_ = CydiaURL(ui); PackageName = reinterpret_cast(method_getImplementation(class_getInstanceMethod([Package class], @selector(cyname)))); @@ -9607,6 +10023,11 @@ int main(int argc, char *argv[]) { /* Library Hacks {{{ */ class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16"); + $WAKWindow = objc_getClass("WAKWindow"); + if ($WAKWindow != NULL) + if (Method method = class_getInstanceMethod($WAKWindow, @selector(screenSize))) + method_setImplementation(method, (IMP) &$WAKWindow$screenSize); + $CFXPreferencesPropertyListSource = objc_getClass("CFXPreferencesPropertyListSource"); Method CFXPreferencesPropertyListSource$_backingPlistChangedSinceLastSync(class_getInstanceMethod($CFXPreferencesPropertyListSource, @selector(_backingPlistChangedSinceLastSync))); @@ -9621,19 +10042,19 @@ int main(int argc, char *argv[]) { method_setImplementation(CFXPreferencesPropertyListSource$createPlistFromDisk, reinterpret_cast(&$CFXPreferencesPropertyListSource$createPlistFromDisk)); } - $WebDefaultUIKitDelegate = objc_getClass("WebDefaultUIKitDelegate"); - Method UIWebDocumentView$_setUIKitDelegate$(class_getInstanceMethod([WebView class], @selector(_setUIKitDelegate:))); - if (UIWebDocumentView$_setUIKitDelegate$ != NULL) { - _UIWebDocumentView$_setUIKitDelegate$ = reinterpret_cast(method_getImplementation(UIWebDocumentView$_setUIKitDelegate$)); - method_setImplementation(UIWebDocumentView$_setUIKitDelegate$, reinterpret_cast(&$UIWebDocumentView$_setUIKitDelegate$)); - } - $NSURLConnection = objc_getClass("NSURLConnection"); Method NSURLConnection$init$(class_getInstanceMethod($NSURLConnection, @selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:))); if (NSURLConnection$init$ != NULL) { _NSURLConnection$init$ = reinterpret_cast(method_getImplementation(NSURLConnection$init$)); method_setImplementation(NSURLConnection$init$, reinterpret_cast(&$NSURLConnection$init$)); } + + $NSUserDefaults = objc_getClass("NSUserDefaults"); + Method NSUserDefaults$objectForKey$(class_getInstanceMethod($NSUserDefaults, @selector(objectForKey:))); + if (NSUserDefaults$objectForKey$ != NULL) { + _NSUserDefaults$objectForKey$ = reinterpret_cast(method_getImplementation(NSUserDefaults$objectForKey$)); + method_setImplementation(NSUserDefaults$objectForKey$, reinterpret_cast(&$NSUserDefaults$objectForKey$)); + } /* }}} */ /* Set Locale {{{ */ Locale_ = CFLocaleCopyCurrent(); @@ -9696,6 +10117,11 @@ int main(int argc, char *argv[]) { setuid(0); setgid(0); + if (access("/var/mobile/Library/Keyboard/UserDictionary.sqlite", F_OK) == 0) + system("mkdir -p /var/root/Library/Keyboard; cp -af /var/mobile/Library/Keyboard/UserDictionary.sqlite /var/root/Library/Keyboard/"); + + Cache_ = [[NSString stringWithFormat:@"%@/Library/Caches/com.saurik.Cydia", @"/var/root"] retain]; + /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc)); alloc_ = alloc->method_imp; alloc->method_imp = (IMP) &Alloc_;*/ @@ -9704,6 +10130,9 @@ int main(int argc, char *argv[]) { dealloc_ = dealloc->method_imp; dealloc->method_imp = (IMP) &Dealloc_;*/ + void *gestalt(dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_GLOBAL | RTLD_LAZY)); + $MGCopyAnswer = reinterpret_cast(dlsym(gestalt, "MGCopyAnswer")); + /* System Information {{{ */ size_t size; @@ -9735,30 +10164,23 @@ int main(int argc, char *argv[]) { ChipID_ = [CYHex((NSData *) CYIOGetValue("IODeviceTree:/chosen", @"unique-chip-id"), true) uppercaseString]; BBSNum_ = CYHex((NSData *) CYIOGetValue("IOService:/AppleARMPE/baseband", @"snum"), false); - UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier]; - - CFStringRef (*$CTSIMSupportCopyMobileSubscriberCountryCode)(CFAllocatorRef); - $CTSIMSupportCopyMobileSubscriberCountryCode = reinterpret_cast(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode")); - CFStringRef mcc($CTSIMSupportCopyMobileSubscriberCountryCode == NULL ? NULL : (*$CTSIMSupportCopyMobileSubscriberCountryCode)(kCFAllocatorDefault)); + UniqueID_ = UniqueIdentifier(device); - CFStringRef (*$CTSIMSupportCopyMobileSubscriberNetworkCode)(CFAllocatorRef); - $CTSIMSupportCopyMobileSubscriberNetworkCode = reinterpret_cast(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode")); - CFStringRef mnc($CTSIMSupportCopyMobileSubscriberNetworkCode == NULL ? NULL : (*$CTSIMSupportCopyMobileSubscriberNetworkCode)(kCFAllocatorDefault)); - - if (mcc != NULL && mnc != NULL) - PLMN_ = [NSString stringWithFormat:@"%@%@", mcc, mnc]; - - if (mnc != NULL) - CFRelease(mnc); - if (mcc != NULL) - CFRelease(mcc); - - if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) - Build_ = [system objectForKey:@"ProductBuildVersion"]; if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) { Product_ = [info objectForKey:@"SafariProductVersion"]; Safari_ = [info objectForKey:@"CFBundleVersion"]; } + + NSString *agent([NSString stringWithFormat:@"Cydia/%@ CyF/%.2f", Cydia_, kCFCoreFoundationVersionNumber]); + + if (Pcre match = Pcre("^[0-9]+(\\.[0-9]+)+", Safari_)) + agent = [NSString stringWithFormat:@"Safari/%@ %@", match[0], agent]; + if (Pcre match = Pcre("^[0-9]+[A-Z][0-9]+[a-z]?", System_)) + agent = [NSString stringWithFormat:@"Mobile/%@ %@", match[0], agent]; + if (Pcre match = Pcre("^[0-9]+(\\.[0-9]+)+", Product_)) + agent = [NSString stringWithFormat:@"Version/%@ %@", match[0], agent]; + + UserAgent_ = agent; /* }}} */ /* Load Database {{{ */ _trace(); @@ -9772,14 +10194,20 @@ int main(int argc, char *argv[]) { Settings_ = [Metadata_ objectForKey:@"Settings"]; Packages_ = [Metadata_ objectForKey:@"Packages"]; + + Values_ = [Metadata_ objectForKey:@"Values"]; Sections_ = [Metadata_ objectForKey:@"Sections"]; Sources_ = [Metadata_ objectForKey:@"Sources"]; Token_ = [Metadata_ objectForKey:@"Token"]; + + Version_ = [Metadata_ objectForKey:@"Version"]; } - if (Settings_ != nil) - Role_ = [Settings_ objectForKey:@"Role"]; + if (Values_ == nil) { + Values_ = [[[NSMutableDictionary alloc] initWithCapacity:4] autorelease]; + [Metadata_ setObject:Values_ forKey:@"Values"]; + } if (Sections_ == nil) { Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease]; @@ -9790,8 +10218,39 @@ int main(int argc, char *argv[]) { Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease]; [Metadata_ setObject:Sources_ forKey:@"Sources"]; } + + if (Version_ == nil) { + Version_ = [NSNumber numberWithUnsignedInt:0]; + [Metadata_ setObject:Version_ forKey:@"Version"]; + } + + if ([Version_ unsignedIntValue] == 0) { + CydiaAddSource(@"http://apt.thebigboss.org/repofiles/cydia/", @"stable", [NSMutableArray arrayWithObject:@"main"]); + CydiaAddSource(@"http://apt.modmyi.com/", @"stable", [NSMutableArray arrayWithObject:@"main"]); + CydiaAddSource(@"http://cydia.zodttd.com/repo/cydia/", @"stable", [NSMutableArray arrayWithObject:@"main"]); + CydiaAddSource(@"http://repo666.ultrasn0w.com/", @"./"); + + Version_ = [NSNumber numberWithUnsignedInt:1]; + [Metadata_ setObject:Version_ forKey:@"Version"]; + + [Metadata_ removeObjectForKey:@"LastUpdate"]; + + Changed_ = true; + } + + _H broken([NSMutableArray array]); + for (NSString *key in (id) Sources_) + if ([key rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"# "]].location != NSNotFound) + [broken addObject:key]; + if ([broken count] != 0) { + for (NSString *key in (id) broken) + [Sources_ removeObjectForKey:key]; + Changed_ = true; + } broken = nil; /* }}} */ + CydiaWriteSources(); + _trace(); MetaFile_.Open("/var/lib/cydia/metadata.cb0"); _trace(); @@ -9827,11 +10286,7 @@ int main(int argc, char *argv[]) { int version([[NSString stringWithContentsOfFile:@"/var/lib/cydia/firmware.ver"] intValue]); - if (access("/tmp/.cydia.fw", F_OK) == 0) { - unlink("/tmp/.cydia.fw"); - goto firmware; - } else if (access("/User", F_OK) != 0 || version < 4) { - firmware: + if (access("/User", F_OK) != 0 || version != 6) { _trace(); system("/usr/libexec/cydia/firmware.sh"); _trace(); @@ -9869,6 +10324,7 @@ int main(int argc, char *argv[]) { Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0); Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0); Black_.Set(space_, 0.0, 0.0, 0.0, 1.0); + Folder_.Set(space_, 0x8e/255.f, 0x8e/255.f, 0x93/255.f, 1.0); Off_.Set(space_, 0.9, 0.9, 0.9, 1.0); White_.Set(space_, 1.0, 1.0, 1.0, 1.0); Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0); @@ -9880,14 +10336,18 @@ int main(int argc, char *argv[]) { RemovingColor_ = [UIColor colorWithRed:1.00f green:0.88f blue:0.88f alpha:1.00f]; /* }}}*/ /* UIKit Configuration {{{ */ - void (*$GSFontSetUseLegacyFontMetrics)(BOOL)(reinterpret_cast(dlsym(RTLD_DEFAULT, "GSFontSetUseLegacyFontMetrics"))); - if ($GSFontSetUseLegacyFontMetrics != NULL) - $GSFontSetUseLegacyFontMetrics(YES); - // XXX: I have a feeling this was important //UIKeyboardDisableAutomaticAppearance(); /* }}} */ + $SBSSetInterceptsMenuButtonForever = reinterpret_cast(dlsym(RTLD_DEFAULT, "SBSSetInterceptsMenuButtonForever")); + + const char *symbol(kCFCoreFoundationVersionNumber >= 800 ? "MGGetBoolAnswer" : "GSSystemHasCapability"); + BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast(dlsym(RTLD_DEFAULT, symbol)); + bool fast = GSSystemHasCapability != NULL && GSSystemHasCapability(CFSTR("armv7")); + + PulseInterval_ = fast ? 50000 : 500000; + Colon_ = UCLocalize("COLON_DELIMITED"); Elision_ = UCLocalize("ELISION"); Error_ = UCLocalize("ERROR");