1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #import "UICaboodle.h"
41 #include <objc/objc.h>
42 #include <objc/runtime.h>
44 #include <CoreGraphics/CoreGraphics.h>
45 #include <GraphicsServices/GraphicsServices.h>
46 #include <Foundation/Foundation.h>
48 #import <QuartzCore/CALayer.h>
50 #import <UIKit/UIKit.h>
53 #import <MessageUI/MailComposeController.h>
55 #import <WebCore/WebScriptObject.h>
56 //#include <WebCore/DOMHTML.h>
58 #include <WebKit/WebFrame.h>
59 #include <WebKit/WebPolicyDelegate.h>
60 #include <WebKit/WebView.h>
62 #import <WebKit/WebView-WebPrivate.h>
67 #include <ext/stdio_filebuf.h>
69 #include <apt-pkg/acquire.h>
70 #include <apt-pkg/acquire-item.h>
71 #include <apt-pkg/algorithms.h>
72 #include <apt-pkg/cachefile.h>
73 #include <apt-pkg/clean.h>
74 #include <apt-pkg/configuration.h>
75 #include <apt-pkg/debmetaindex.h>
76 #include <apt-pkg/error.h>
77 #include <apt-pkg/init.h>
78 #include <apt-pkg/mmap.h>
79 #include <apt-pkg/pkgrecords.h>
80 #include <apt-pkg/sha1.h>
81 #include <apt-pkg/sourcelist.h>
82 #include <apt-pkg/sptr.h>
83 #include <apt-pkg/strutl.h>
85 #include <sys/types.h>
87 #include <sys/sysctl.h>
93 #include <mach-o/nlist.h>
103 #import "BrowserView.h"
104 #import "ResetView.h"
107 //#define _finline __attribute__((force_inline))
108 #define _finline inline
113 #define _limit(count) do { \
114 static size_t _count(0); \
115 if (++_count == count) \
119 static uint64_t profile_;
121 #define _timestamp ({ \
123 gettimeofday(&tv, NULL); \
124 tv.tv_sec * 1000000 + tv.tv_usec; \
127 /* Objective-C Handle<> {{{ */
128 template <typename Type_>
130 typedef _H<Type_> This_;
135 _finline void Retain_() {
140 _finline void Clear_() {
146 _finline _H(Type_ *value = NULL, bool mended = false) :
157 _finline This_ &operator =(Type_ *value) {
158 if (value_ != value) {
167 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
169 void NSLogRect(const char *fix, const CGRect &rect) {
170 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
173 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
175 /* iPhoneOS 2.0 Compatibility {{{ */
177 @interface UITextView (iPhoneOS)
178 - (void) setTextSize:(float)size;
181 @implementation UITextView (iPhoneOS)
183 - (void) setTextSize:(float)size {
184 [self setFont:[[self font] fontWithSize:size]];
191 extern NSString * const kCAFilterNearest;
193 /* Information Dictionaries {{{ */
194 @interface NSMutableArray (Cydia)
195 - (void) addInfoDictionary:(NSDictionary *)info;
198 @implementation NSMutableArray (Cydia)
200 - (void) addInfoDictionary:(NSDictionary *)info {
201 [self addObject:info];
206 @interface NSMutableDictionary (Cydia)
207 - (void) addInfoDictionary:(NSDictionary *)info;
210 @implementation NSMutableDictionary (Cydia)
212 - (void) addInfoDictionary:(NSDictionary *)info {
213 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
214 [self setObject:info forKey:bundle];
219 /* Pop Transitions {{{ */
220 @interface PopTransitionView : UITransitionView {
225 @implementation PopTransitionView
227 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
228 if (from != nil && to == nil)
229 [self removeFromSuperview];
234 @interface UIView (PopUpView)
235 - (void) popFromSuperviewAnimated:(BOOL)animated;
236 - (void) popSubview:(UIView *)view;
239 @implementation UIView (PopUpView)
241 - (void) popFromSuperviewAnimated:(BOOL)animated {
242 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
245 - (void) popSubview:(UIView *)view {
246 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
247 [transition setDelegate:transition];
248 [self addSubview:transition];
250 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
251 [transition transition:UITransitionNone toView:blank];
252 [transition transition:UITransitionPushFromBottom toView:view];
258 #define lprintf(args...) fprintf(stderr, args)
261 #define ForSaurik 1 && !ForRelease
262 #define RecycleWebViews 0
263 #define AlwaysReload 1 && !ForRelease
266 @interface NSMutableArray (Radix)
267 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
270 @implementation NSMutableArray (Radix)
272 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
273 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
274 [invocation setSelector:selector];
275 [invocation setArgument:&object atIndex:2];
277 size_t count([self count]);
282 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
284 for (size_t i(0); i != count; ++i) {
285 RadixItem &item(lhs[i]);
288 id object([self objectAtIndex:i]);
289 [invocation setTarget:object];
292 [invocation getReturnValue:&item.key];
295 static const size_t width = 32;
296 static const size_t bits = 11;
297 static const size_t slots = 1 << bits;
298 static const size_t passes = (width + (bits - 1)) / bits;
300 size_t *hist(new size_t[slots]);
302 for (size_t pass(0); pass != passes; ++pass) {
303 memset(hist, 0, sizeof(size_t) * slots);
305 for (size_t i(0); i != count; ++i) {
306 uint32_t key(lhs[i].key);
308 key &= _not(uint32_t) >> width - bits;
313 for (size_t i(0); i != slots; ++i) {
314 size_t local(offset);
319 for (size_t i(0); i != count; ++i) {
320 uint32_t key(lhs[i].key);
322 key &= _not(uint32_t) >> width - bits;
323 rhs[hist[key]++] = lhs[i];
333 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
334 for (size_t i(0); i != count; ++i)
335 [values addObject:[self objectAtIndex:lhs[i].index]];
336 [self setArray:values];
345 kUIControlEventMouseDown = 1 << 0,
346 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
347 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
348 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
349 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
350 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
351 } UIControlEventMasks;
353 @interface NSString (UIKit)
354 - (NSString *) stringByAddingPercentEscapes;
355 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
358 @interface NSString (Cydia)
359 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
360 - (NSComparisonResult) compareByPath:(NSString *)other;
363 @implementation NSString (Cydia)
365 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
366 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
369 - (NSComparisonResult) compareByPath:(NSString *)other {
370 NSString *prefix = [self commonPrefixWithString:other options:0];
371 size_t length = [prefix length];
373 NSRange lrange = NSMakeRange(length, [self length] - length);
374 NSRange rrange = NSMakeRange(length, [other length] - length);
376 lrange = [self rangeOfString:@"/" options:0 range:lrange];
377 rrange = [other rangeOfString:@"/" options:0 range:rrange];
379 NSComparisonResult value;
381 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
382 value = NSOrderedSame;
383 else if (lrange.location == NSNotFound)
384 value = NSOrderedAscending;
385 else if (rrange.location == NSNotFound)
386 value = NSOrderedDescending;
388 value = NSOrderedSame;
390 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
391 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
392 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
393 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
395 NSComparisonResult result = [lpath compare:rpath];
396 return result == NSOrderedSame ? value : result;
401 /* Perl-Compatible RegEx {{{ */
411 Pcre(const char *regex) :
416 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
419 lprintf("%d:%s\n", offset, error);
423 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
424 matches_ = new int[(capture_ + 1) * 3];
432 NSString *operator [](size_t match) {
433 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
436 bool operator ()(NSString *data) {
437 // XXX: length is for characters, not for bytes
438 return operator ()([data UTF8String], [data length]);
441 bool operator ()(const char *data, size_t size) {
443 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
447 /* Mime Addresses {{{ */
448 @interface Address : NSObject {
454 - (NSString *) address;
456 + (Address *) addressWithString:(NSString *)string;
457 - (Address *) initWithString:(NSString *)string;
460 @implementation Address
469 - (NSString *) name {
473 - (NSString *) address {
477 + (Address *) addressWithString:(NSString *)string {
478 return [[[Address alloc] initWithString:string] autorelease];
481 + (NSArray *) _attributeKeys {
482 return [NSArray arrayWithObjects:@"address", @"name", nil];
485 - (NSArray *) attributeKeys {
486 return [[self class] _attributeKeys];
489 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
490 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
493 - (Address *) initWithString:(NSString *)string {
494 if ((self = [super init]) != nil) {
495 const char *data = [string UTF8String];
496 size_t size = [string length];
498 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
500 if (address_r(data, size)) {
501 name_ = [address_r[1] retain];
502 address_ = [address_r[2] retain];
504 name_ = [string retain];
512 /* CoreGraphics Primitives {{{ */
523 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
526 Set(space, red, green, blue, alpha);
531 CGColorRelease(color_);
538 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
540 float color[] = {red, green, blue, alpha};
541 color_ = CGColorCreate(space, color);
544 operator CGColorRef() {
550 extern "C" void UISetColor(CGColorRef color);
552 /* Random Global Variables {{{ */
553 static const int PulseInterval_ = 50000;
554 static const int ButtonBarHeight_ = 48;
555 static const float KeyboardTime_ = 0.3f;
557 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
558 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
560 static CGColor Blue_;
561 static CGColor Blueish_;
562 static CGColor Black_;
564 static CGColor White_;
565 static CGColor Gray_;
567 static NSString *App_;
568 static NSString *Home_;
569 static BOOL Sounds_Keyboard_;
571 static BOOL Advanced_;
575 static BOOL Ignored_;
577 static UIFont *Font12_;
578 static UIFont *Font12Bold_;
579 static UIFont *Font14_;
580 static UIFont *Font18Bold_;
581 static UIFont *Font22Bold_;
583 static const char *Machine_ = NULL;
584 static const NSString *UniqueID_ = NULL;
591 CGColorSpaceRef space_;
593 #define FW_LEAST(major, minor, bugfix) \
594 (major < Major_ || major == Major_ && \
595 (minor < Minor_ || minor == Minor_ && \
601 static NSDictionary *SectionMap_;
602 static NSMutableDictionary *Metadata_;
603 static _transient NSMutableDictionary *Settings_;
604 static _transient NSString *Role_;
605 static _transient NSMutableDictionary *Packages_;
606 static _transient NSMutableDictionary *Sections_;
607 static _transient NSMutableDictionary *Sources_;
608 static bool Changed_;
612 static NSMutableArray *Documents_;
615 NSString *GetLastUpdate() {
616 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
619 return @"Never or Unknown";
621 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
622 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
624 CFRelease(formatter);
626 return [(NSString *) formatted autorelease];
629 /* Display Helpers {{{ */
630 inline float Interpolate(float begin, float end, float fraction) {
631 return (end - begin) * fraction + begin;
634 NSString *SizeString(double size) {
635 bool negative = size < 0;
640 while (size > 1024) {
645 static const char *powers_[] = {"B", "kB", "MB", "GB"};
647 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
650 NSString *StripVersion(NSString *version) {
651 NSRange colon = [version rangeOfString:@":"];
652 if (colon.location != NSNotFound)
653 version = [version substringFromIndex:(colon.location + 1)];
657 NSString *Simplify(NSString *title) {
658 const char *data = [title UTF8String];
659 size_t size = [title length];
661 static Pcre square_r("^\\[(.*)\\]$");
662 if (square_r(data, size))
663 return Simplify(square_r[1]);
665 static Pcre paren_r("^\\((.*)\\)$");
666 if (paren_r(data, size))
667 return Simplify(paren_r[1]);
669 static Pcre title_r("^(.*?) \\(.*\\)$");
670 if (title_r(data, size))
671 return Simplify(title_r[1]);
677 bool isSectionVisible(NSString *section) {
678 NSDictionary *metadata = [Sections_ objectForKey:section];
679 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
680 return hidden == nil || ![hidden boolValue];
683 /* Delegate Prototypes {{{ */
687 @interface NSObject (ProgressDelegate)
690 @implementation NSObject(ProgressDelegate)
692 - (void) _setProgressError:(NSArray *)args {
693 [self performSelector:@selector(setProgressError:forPackage:)
694 withObject:[args objectAtIndex:0]
695 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
701 @protocol ProgressDelegate
702 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
703 - (void) setProgressTitle:(NSString *)title;
704 - (void) setProgressPercent:(float)percent;
705 - (void) startProgress;
706 - (void) addProgressOutput:(NSString *)output;
707 - (bool) isCancelling:(size_t)received;
710 @protocol ConfigurationDelegate
711 - (void) repairWithSelector:(SEL)selector;
712 - (void) setConfigurationData:(NSString *)data;
715 @protocol CydiaDelegate
716 - (void) installPackage:(Package *)package;
717 - (void) removePackage:(Package *)package;
718 - (void) slideUp:(UIActionSheet *)alert;
719 - (void) distUpgrade;
722 - (void) askForSettings;
723 - (UIProgressHUD *) addProgressHUD;
724 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
725 - (RVPage *) pageForPackage:(NSString *)name;
726 - (void) openMailToURL:(NSURL *)url;
730 /* Status Delegation {{{ */
732 public pkgAcquireStatus
735 _transient NSObject<ProgressDelegate> *delegate_;
743 void setDelegate(id delegate) {
744 delegate_ = delegate;
747 virtual bool MediaChange(std::string media, std::string drive) {
751 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
754 virtual void Fetch(pkgAcquire::ItemDesc &item) {
755 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
756 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
759 virtual void Done(pkgAcquire::ItemDesc &item) {
762 virtual void Fail(pkgAcquire::ItemDesc &item) {
764 item.Owner->Status == pkgAcquire::Item::StatIdle ||
765 item.Owner->Status == pkgAcquire::Item::StatDone
769 std::string &error(item.Owner->ErrorText);
773 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
774 NSArray *fields([description componentsSeparatedByString:@" "]);
775 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
777 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
778 withObject:[NSArray arrayWithObjects:
779 [NSString stringWithUTF8String:error.c_str()],
786 virtual bool Pulse(pkgAcquire *Owner) {
787 bool value = pkgAcquireStatus::Pulse(Owner);
790 double(CurrentBytes + CurrentItems) /
791 double(TotalBytes + TotalItems)
794 [delegate_ setProgressPercent:percent];
795 return [delegate_ isCancelling:CurrentBytes] ? false : value;
798 virtual void Start() {
799 [delegate_ startProgress];
802 virtual void Stop() {
806 /* Progress Delegation {{{ */
811 _transient id<ProgressDelegate> delegate_;
814 virtual void Update() {
815 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
816 [delegate_ setProgressPercent:(Percent / 100)];
825 void setDelegate(id delegate) {
826 delegate_ = delegate;
829 virtual void Done() {
830 [delegate_ setProgressPercent:1];
835 /* Database Interface {{{ */
836 @interface Database : NSObject {
838 pkgDepCache::Policy *policy_;
839 pkgRecords *records_;
840 pkgProblemResolver *resolver_;
841 pkgAcquire *fetcher_;
843 SPtr<pkgPackageManager> manager_;
844 pkgSourceList *list_;
846 NSMutableDictionary *sources_;
847 NSMutableArray *packages_;
849 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
858 + (Database *) sharedInstance;
860 - (void) _readCydia:(NSNumber *)fd;
861 - (void) _readStatus:(NSNumber *)fd;
862 - (void) _readOutput:(NSNumber *)fd;
866 - (Package *) packageWithName:(NSString *)name;
868 - (pkgCacheFile &) cache;
869 - (pkgDepCache::Policy *) policy;
870 - (pkgRecords *) records;
871 - (pkgProblemResolver *) resolver;
872 - (pkgAcquire &) fetcher;
873 - (NSArray *) packages;
874 - (NSArray *) sources;
883 - (void) updateWithStatus:(Status &)status;
885 - (void) setDelegate:(id)delegate;
886 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
890 /* Source Class {{{ */
891 @interface Source : NSObject {
892 NSString *description_;
897 NSString *distribution_;
901 NSString *defaultIcon_;
903 NSDictionary *record_;
907 - (Source *) initWithMetaIndex:(metaIndex *)index;
909 - (NSComparisonResult) compareByNameAndType:(Source *)source;
911 - (NSDictionary *) record;
915 - (NSString *) distribution;
921 - (NSString *) description;
922 - (NSString *) label;
923 - (NSString *) origin;
924 - (NSString *) version;
926 - (NSString *) defaultIcon;
930 @implementation Source
934 [distribution_ release];
937 if (description_ != nil)
938 [description_ release];
945 if (defaultIcon_ != nil)
946 [defaultIcon_ release];
953 + (NSArray *) _attributeKeys {
954 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
957 - (NSArray *) attributeKeys {
958 return [[self class] _attributeKeys];
961 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
962 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
965 - (Source *) initWithMetaIndex:(metaIndex *)index {
966 if ((self = [super init]) != nil) {
967 trusted_ = index->IsTrusted();
969 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
970 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
971 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
973 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
974 if (dindex != NULL) {
975 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
977 while (std::getline(release, line)) {
978 std::string::size_type colon(line.find(':'));
979 if (colon == std::string::npos)
982 std::string name(line.substr(0, colon));
983 std::string value(line.substr(colon + 1));
984 while (!value.empty() && value[0] == ' ')
985 value = value.substr(1);
987 if (name == "Default-Icon")
988 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
989 else if (name == "Description")
990 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
991 else if (name == "Label")
992 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
993 else if (name == "Origin")
994 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
995 else if (name == "Version")
996 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1000 record_ = [Sources_ objectForKey:[self key]];
1002 record_ = [record_ retain];
1006 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1007 NSDictionary *lhr = [self record];
1008 NSDictionary *rhr = [source record];
1011 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1013 NSString *lhs = [self name];
1014 NSString *rhs = [source name];
1016 if ([lhs length] != 0 && [rhs length] != 0) {
1017 unichar lhc = [lhs characterAtIndex:0];
1018 unichar rhc = [rhs characterAtIndex:0];
1020 if (isalpha(lhc) && !isalpha(rhc))
1021 return NSOrderedAscending;
1022 else if (!isalpha(lhc) && isalpha(rhc))
1023 return NSOrderedDescending;
1026 return [lhs compare:rhs options:CompareOptions_];
1029 - (NSDictionary *) record {
1037 - (NSString *) uri {
1041 - (NSString *) distribution {
1042 return distribution_;
1045 - (NSString *) type {
1049 - (NSString *) key {
1050 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1053 - (NSString *) host {
1054 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1057 - (NSString *) name {
1058 return origin_ == nil ? [self host] : origin_;
1061 - (NSString *) description {
1062 return description_;
1065 - (NSString *) label {
1066 return label_ == nil ? [self host] : label_;
1069 - (NSString *) origin {
1073 - (NSString *) version {
1077 - (NSString *) defaultIcon {
1078 return defaultIcon_;
1083 /* Relationship Class {{{ */
1084 @interface Relationship : NSObject {
1089 - (NSString *) type;
1091 - (NSString *) name;
1095 @implementation Relationship
1103 - (NSString *) type {
1111 - (NSString *) name {
1118 /* Package Class {{{ */
1119 @interface Package : NSObject {
1120 pkgCache::PkgIterator iterator_;
1121 _transient Database *database_;
1122 pkgCache::VerIterator version_;
1123 pkgCache::VerFileIterator file_;
1131 NSString *installed_;
1137 NSString *depiction_;
1138 NSString *homepage_;
1144 NSArray *relationships_;
1147 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1148 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1150 - (pkgCache::PkgIterator) iterator;
1152 - (NSString *) section;
1153 - (NSString *) simpleSection;
1155 - (Address *) maintainer;
1157 - (NSString *) description;
1158 - (NSString *) index;
1160 - (NSMutableDictionary *) metadata;
1162 - (BOOL) subscribed;
1165 - (NSString *) latest;
1166 - (NSString *) installed;
1169 - (BOOL) upgradableAndEssential:(BOOL)essential;
1172 - (BOOL) unfiltered;
1176 - (BOOL) halfConfigured;
1177 - (BOOL) halfInstalled;
1179 - (NSString *) mode;
1182 - (NSString *) name;
1183 - (NSString *) tagline;
1185 - (NSString *) homepage;
1186 - (NSString *) depiction;
1187 - (Address *) author;
1189 - (NSArray *) files;
1190 - (NSArray *) relationships;
1191 - (NSArray *) warnings;
1192 - (NSArray *) applications;
1194 - (Source *) source;
1195 - (NSString *) role;
1197 - (BOOL) matches:(NSString *)text;
1199 - (bool) hasSupportingRole;
1200 - (BOOL) hasTag:(NSString *)tag;
1201 - (NSString *) primaryPurpose;
1202 - (NSArray *) purposes;
1204 - (NSComparisonResult) compareByName:(Package *)package;
1205 - (NSComparisonResult) compareBySection:(Package *)package;
1207 - (uint32_t) compareForChanges;
1212 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1213 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1214 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1215 - (NSNumber *) isVisibleInSource:(Source *)source;
1219 @implementation Package
1225 if (section_ != nil)
1229 if (installed_ != nil)
1230 [installed_ release];
1238 if (depiction_ != nil)
1239 [depiction_ release];
1240 if (homepage_ != nil)
1241 [homepage_ release];
1242 if (sponsor_ != nil)
1251 if (relationships_ != nil)
1252 [relationships_ release];
1257 + (NSArray *) _attributeKeys {
1258 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1261 - (NSArray *) attributeKeys {
1262 return [[self class] _attributeKeys];
1265 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1266 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1269 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1270 if ((self = [super init]) != nil) {
1271 iterator_ = iterator;
1272 database_ = database;
1274 version_ = [database_ policy]->GetCandidateVer(iterator_);
1275 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1276 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1278 pkgCache::VerIterator current = iterator_.CurrentVer();
1279 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1280 installed_ = [StripVersion(installed) retain];
1282 if (!version_.end())
1283 file_ = version_.FileList();
1285 pkgCache &cache([database_ cache]);
1286 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1289 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1292 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1294 const char *begin, *end;
1295 parser->GetRec(begin, end);
1297 NSString *website(nil);
1298 NSString *sponsor(nil);
1299 NSString *author(nil);
1308 {"depiction", &depiction_},
1309 {"homepage", &homepage_},
1310 {"website", &website},
1311 {"sponsor", &sponsor},
1312 {"author", &author},
1316 while (begin != end)
1317 if (*begin == '\n') {
1320 } else if (isblank(*begin)) next: {
1321 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1324 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1325 const char *name(begin);
1326 size_t size(colon - begin);
1328 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1331 const char *stop(begin == NULL ? end : begin);
1332 while (stop[-1] == '\r')
1334 while (++colon != stop && isblank(*colon));
1336 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1337 if (strncasecmp(names[i].name_, name, size) == 0) {
1338 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1339 *names[i].value_ = value;
1350 name_ = [name_ retain];
1351 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1353 icon_ = [icon_ retain];
1354 if (depiction_ != nil)
1355 depiction_ = [depiction_ retain];
1356 if (homepage_ == nil)
1357 homepage_ = website;
1358 if ([homepage_ isEqualToString:depiction_])
1360 if (homepage_ != nil)
1361 homepage_ = [homepage_ retain];
1363 sponsor_ = [[Address addressWithString:sponsor] retain];
1365 author_ = [[Address addressWithString:author] retain];
1367 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1371 for (int i(0), e([tags_ count]); i != e; ++i) {
1372 NSString *tag = [tags_ objectAtIndex:i];
1373 if ([tag hasPrefix:@"role::"]) {
1374 role_ = [[tag substringFromIndex:6] retain];
1379 NSString *solid(latest == nil ? installed : latest);
1380 bool changed(false);
1382 NSString *key([id_ lowercaseString]);
1384 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1385 if (metadata == nil) {
1386 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1391 [metadata setObject:solid forKey:@"LastVersion"];
1394 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1395 NSDate *last([metadata objectForKey:@"LastSeen"]);
1396 NSString *version([metadata objectForKey:@"LastVersion"]);
1399 first = last == nil ? now_ : last;
1400 [metadata setObject:first forKey:@"FirstSeen"];
1405 if (version == nil) {
1406 [metadata setObject:solid forKey:@"LastVersion"];
1408 } else if (![version isEqualToString:solid]) {
1409 [metadata setObject:solid forKey:@"LastVersion"];
1411 [metadata setObject:last forKey:@"LastSeen"];
1417 [Packages_ setObject:metadata forKey:key];
1423 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1424 return [[[Package alloc]
1425 initWithIterator:iterator
1430 - (pkgCache::PkgIterator) iterator {
1434 - (NSString *) section {
1435 if (section_ != nil)
1438 const char *section = iterator_.Section();
1439 if (section == NULL)
1442 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1445 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1446 if (NSString *rename = [value objectForKey:@"Rename"]) {
1451 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1455 - (NSString *) simpleSection {
1456 if (NSString *section = [self section])
1457 return Simplify(section);
1463 - (Address *) maintainer {
1466 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1467 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1471 return version_.end() ? 0 : version_->InstalledSize;
1474 - (NSString *) description {
1477 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1478 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1480 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1481 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1482 if ([lines count] < 2)
1485 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1486 for (size_t i(1); i != [lines count]; ++i) {
1487 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1488 [trimmed addObject:trim];
1491 return [trimmed componentsJoinedByString:@"\n"];
1494 - (NSString *) index {
1495 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1496 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1499 - (NSMutableDictionary *) metadata {
1500 return [Packages_ objectForKey:[id_ lowercaseString]];
1504 NSDictionary *metadata([self metadata]);
1505 if ([self subscribed])
1506 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1508 return [metadata objectForKey:@"FirstSeen"];
1511 - (BOOL) subscribed {
1512 NSDictionary *metadata([self metadata]);
1513 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1514 return [subscribed boolValue];
1520 NSDictionary *metadata([self metadata]);
1521 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1522 return [ignored boolValue];
1527 - (NSString *) latest {
1531 - (NSString *) installed {
1536 return !version_.end();
1539 - (BOOL) upgradableAndEssential:(BOOL)essential {
1540 pkgCache::VerIterator current = iterator_.CurrentVer();
1543 return essential && [self essential];
1545 return !version_.end() && version_ != current;
1548 - (BOOL) essential {
1549 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1553 return [database_ cache][iterator_].InstBroken();
1556 - (BOOL) unfiltered {
1557 NSString *section = [self section];
1558 return section == nil || isSectionVisible(section);
1562 return [self hasSupportingRole] && [self unfiltered];
1566 unsigned char current = iterator_->CurrentState;
1567 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1570 - (BOOL) halfConfigured {
1571 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1574 - (BOOL) halfInstalled {
1575 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1579 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1580 return state.Mode != pkgDepCache::ModeKeep;
1583 - (NSString *) mode {
1584 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1586 switch (state.Mode) {
1587 case pkgDepCache::ModeDelete:
1588 if ((state.iFlags & pkgDepCache::Purge) != 0)
1592 case pkgDepCache::ModeKeep:
1593 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1597 case pkgDepCache::ModeInstall:
1598 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1599 return @"Reinstall";
1600 else switch (state.Status) {
1602 return @"Downgrade";
1608 return @"New Install";
1621 - (NSString *) name {
1622 return name_ == nil ? id_ : name_;
1625 - (NSString *) tagline {
1629 - (UIImage *) icon {
1630 NSString *section = [self simpleSection];
1633 if (NSString *icon = icon_)
1634 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1635 if (icon == nil) if (section != nil)
1636 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1637 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1638 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1640 icon = [UIImage applicationImageNamed:@"unknown.png"];
1644 - (NSString *) homepage {
1648 - (NSString *) depiction {
1652 - (Address *) sponsor {
1656 - (Address *) author {
1660 - (NSArray *) files {
1661 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1662 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1665 fin.open([path UTF8String]);
1670 while (std::getline(fin, line))
1671 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1676 - (NSArray *) relationships {
1677 return relationships_;
1680 - (NSArray *) warnings {
1681 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1682 const char *name(iterator_.Name());
1684 size_t length(strlen(name));
1685 if (length < 2) invalid:
1686 [warnings addObject:@"illegal package identifier"];
1687 else for (size_t i(0); i != length; ++i)
1689 (name[i] < 'a' || name[i] > 'z') &&
1690 (name[i] < '0' || name[i] > '9') &&
1691 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1694 if (strcmp(name, "cydia") != 0) {
1698 if (NSArray *files = [self files])
1699 for (NSString *file in files)
1700 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1702 else if (!stash && [file isEqualToString:@"/var/stash"])
1706 [warnings addObject:@"files installed into Cydia.app"];
1708 [warnings addObject:@"files installed to /var/stash"];
1711 return [warnings count] == 0 ? nil : warnings;
1714 - (NSArray *) applications {
1715 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1717 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1719 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1720 if (NSArray *files = [self files])
1721 for (NSString *file in files)
1722 if (application_r(file)) {
1723 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1724 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1725 if ([id isEqualToString:me])
1728 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1730 display = application_r[1];
1732 NSString *bundle([file stringByDeletingLastPathComponent]);
1733 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1734 if (icon == nil || [icon length] == 0)
1736 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1738 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1739 [applications addObject:application];
1741 [application addObject:id];
1742 [application addObject:display];
1743 [application addObject:url];
1746 return [applications count] == 0 ? nil : applications;
1749 - (Source *) source {
1751 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1758 - (NSString *) role {
1762 - (BOOL) matches:(NSString *)text {
1768 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1769 if (range.location != NSNotFound)
1772 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1773 if (range.location != NSNotFound)
1776 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1777 if (range.location != NSNotFound)
1783 - (bool) hasSupportingRole {
1786 if ([role_ isEqualToString:@"enduser"])
1788 if ([Role_ isEqualToString:@"User"])
1790 if ([role_ isEqualToString:@"hacker"])
1792 if ([Role_ isEqualToString:@"Hacker"])
1794 if ([role_ isEqualToString:@"developer"])
1796 if ([Role_ isEqualToString:@"Developer"])
1801 - (BOOL) hasTag:(NSString *)tag {
1802 return tags_ == nil ? NO : [tags_ containsObject:tag];
1805 - (NSString *) primaryPurpose {
1806 for (NSString *tag in tags_)
1807 if ([tag hasPrefix:@"purpose::"])
1808 return [tag substringFromIndex:9];
1812 - (NSArray *) purposes {
1813 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1814 for (NSString *tag in tags_)
1815 if ([tag hasPrefix:@"purpose::"])
1816 [purposes addObject:[tag substringFromIndex:9]];
1817 return [purposes count] == 0 ? nil : purposes;
1820 - (NSComparisonResult) compareByName:(Package *)package {
1821 NSString *lhs = [self name];
1822 NSString *rhs = [package name];
1824 if ([lhs length] != 0 && [rhs length] != 0) {
1825 unichar lhc = [lhs characterAtIndex:0];
1826 unichar rhc = [rhs characterAtIndex:0];
1828 if (isalpha(lhc) && !isalpha(rhc))
1829 return NSOrderedAscending;
1830 else if (!isalpha(lhc) && isalpha(rhc))
1831 return NSOrderedDescending;
1834 return [lhs compare:rhs options:CompareOptions_];
1837 - (NSComparisonResult) compareBySection:(Package *)package {
1838 NSString *lhs = [self section];
1839 NSString *rhs = [package section];
1841 if (lhs == NULL && rhs != NULL)
1842 return NSOrderedAscending;
1843 else if (lhs != NULL && rhs == NULL)
1844 return NSOrderedDescending;
1845 else if (lhs != NULL && rhs != NULL) {
1846 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1847 if (result != NSOrderedSame)
1851 return NSOrderedSame;
1854 - (uint32_t) compareForChanges {
1859 uint32_t timestamp : 30;
1860 uint32_t ignored : 1;
1861 uint32_t upgradable : 1;
1865 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1867 if ([self upgradableAndEssential:YES]) {
1868 value.bits.timestamp = 0;
1869 value.bits.ignored = [self ignored] ? 0 : 1;
1870 value.bits.upgradable = 1;
1872 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1873 value.bits.ignored = 0;
1874 value.bits.upgradable = 0;
1877 return _not(uint32_t) - value.key;
1881 pkgProblemResolver *resolver = [database_ resolver];
1882 resolver->Clear(iterator_);
1883 resolver->Protect(iterator_);
1884 pkgCacheFile &cache([database_ cache]);
1885 cache->MarkInstall(iterator_, false);
1886 pkgDepCache::StateCache &state((*cache)[iterator_]);
1887 if (!state.Install())
1888 cache->SetReInstall(iterator_, true);
1892 pkgProblemResolver *resolver = [database_ resolver];
1893 resolver->Clear(iterator_);
1894 resolver->Protect(iterator_);
1895 resolver->Remove(iterator_);
1896 [database_ cache]->MarkDelete(iterator_, true);
1899 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1900 return [NSNumber numberWithBool:(
1901 [self unfiltered] && [self matches:search]
1905 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1906 return [NSNumber numberWithBool:(
1907 (![number boolValue] || [self visible]) && [self installed] != nil
1911 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1912 NSString *section = [self section];
1914 return [NSNumber numberWithBool:(
1916 [self installed] == nil && (
1918 section == nil && [name length] == 0 ||
1919 [name isEqualToString:section]
1924 - (NSNumber *) isVisibleInSource:(Source *)source {
1925 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1930 /* Section Class {{{ */
1931 @interface Section : NSObject {
1937 - (NSComparisonResult) compareByName:(Section *)section;
1938 - (Section *) initWithName:(NSString *)name;
1939 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1940 - (NSString *) name;
1943 - (void) addToCount;
1947 @implementation Section
1954 - (NSComparisonResult) compareByName:(Section *)section {
1955 NSString *lhs = [self name];
1956 NSString *rhs = [section name];
1958 if ([lhs length] != 0 && [rhs length] != 0) {
1959 unichar lhc = [lhs characterAtIndex:0];
1960 unichar rhc = [rhs characterAtIndex:0];
1962 if (isalpha(lhc) && !isalpha(rhc))
1963 return NSOrderedAscending;
1964 else if (!isalpha(lhc) && isalpha(rhc))
1965 return NSOrderedDescending;
1968 return [lhs compare:rhs options:CompareOptions_];
1971 - (Section *) initWithName:(NSString *)name {
1972 return [self initWithName:name row:0];
1975 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1976 if ((self = [super init]) != nil) {
1977 name_ = [name retain];
1982 - (NSString *) name {
1994 - (void) addToCount {
2002 static NSArray *Finishes_;
2004 /* Database Implementation {{{ */
2005 @implementation Database
2007 + (Database *) sharedInstance {
2008 static Database *instance;
2009 if (instance == nil)
2010 instance = [[Database alloc] init];
2019 - (void) _readCydia:(NSNumber *)fd { _pooled
2020 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2021 std::istream is(&ib);
2024 static Pcre finish_r("^finish:([^:]*)$");
2026 while (std::getline(is, line)) {
2027 const char *data(line.c_str());
2028 size_t size = line.size();
2029 lprintf("C:%s\n", data);
2031 if (finish_r(data, size)) {
2032 NSString *finish = finish_r[1];
2033 int index = [Finishes_ indexOfObject:finish];
2034 if (index != INT_MAX && index > Finish_)
2042 - (void) _readStatus:(NSNumber *)fd { _pooled
2043 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2044 std::istream is(&ib);
2047 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2048 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2050 while (std::getline(is, line)) {
2051 const char *data(line.c_str());
2052 size_t size = line.size();
2053 lprintf("S:%s\n", data);
2055 if (conffile_r(data, size)) {
2056 [delegate_ setConfigurationData:conffile_r[1]];
2057 } else if (strncmp(data, "status: ", 8) == 0) {
2058 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2059 [delegate_ setProgressTitle:string];
2060 } else if (pmstatus_r(data, size)) {
2061 std::string type([pmstatus_r[1] UTF8String]);
2062 NSString *id = pmstatus_r[2];
2064 float percent([pmstatus_r[3] floatValue]);
2065 [delegate_ setProgressPercent:(percent / 100)];
2067 NSString *string = pmstatus_r[4];
2069 if (type == "pmerror")
2070 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2071 withObject:[NSArray arrayWithObjects:string, id, nil]
2074 else if (type == "pmstatus") {
2075 [delegate_ setProgressTitle:string];
2076 } else if (type == "pmconffile")
2077 [delegate_ setConfigurationData:string];
2078 else _assert(false);
2079 } else _assert(false);
2085 - (void) _readOutput:(NSNumber *)fd { _pooled
2086 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2087 std::istream is(&ib);
2090 while (std::getline(is, line)) {
2091 lprintf("O:%s\n", line.c_str());
2092 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2102 - (Package *) packageWithName:(NSString *)name {
2103 if (static_cast<pkgDepCache *>(cache_) == NULL)
2105 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2106 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2109 - (Database *) init {
2110 if ((self = [super init]) != nil) {
2117 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2118 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2122 _assert(pipe(fds) != -1);
2125 _config->Set("APT::Keep-Fds::", cydiafd_);
2126 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2129 detachNewThreadSelector:@selector(_readCydia:)
2131 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2134 _assert(pipe(fds) != -1);
2138 detachNewThreadSelector:@selector(_readStatus:)
2140 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2143 _assert(pipe(fds) != -1);
2144 _assert(dup2(fds[0], 0) != -1);
2145 _assert(close(fds[0]) != -1);
2147 input_ = fdopen(fds[1], "a");
2149 _assert(pipe(fds) != -1);
2150 _assert(dup2(fds[1], 1) != -1);
2151 _assert(close(fds[1]) != -1);
2154 detachNewThreadSelector:@selector(_readOutput:)
2156 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2161 - (pkgCacheFile &) cache {
2165 - (pkgDepCache::Policy *) policy {
2169 - (pkgRecords *) records {
2173 - (pkgProblemResolver *) resolver {
2177 - (pkgAcquire &) fetcher {
2181 - (NSArray *) packages {
2185 - (NSArray *) sources {
2186 return [sources_ allValues];
2189 - (NSArray *) issues {
2190 if (cache_->BrokenCount() == 0)
2193 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2195 for (Package *package in packages_) {
2196 if (![package broken])
2198 pkgCache::PkgIterator pkg([package iterator]);
2200 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2201 [entry addObject:[package name]];
2202 [issues addObject:entry];
2204 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2208 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2209 pkgCache::DepIterator start;
2210 pkgCache::DepIterator end;
2211 dep.GlobOr(start, end); // ++dep
2213 if (!cache_->IsImportantDep(end))
2215 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2218 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2219 [entry addObject:failure];
2220 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2222 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2223 [failure addObject:[package name]];
2225 pkgCache::PkgIterator target(start.TargetPkg());
2226 if (target->ProvidesList != 0)
2227 [failure addObject:@"?"];
2229 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2231 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2232 else if (!cache_[target].CandidateVerIter(cache_).end())
2233 [failure addObject:@"-"];
2234 else if (target->ProvidesList == 0)
2235 [failure addObject:@"!"];
2237 [failure addObject:@"%"];
2241 if (start.TargetVer() != 0)
2242 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2253 - (void) reloadData {
2273 if (!cache_.Open(progress_, true)) {
2275 if (!_error->PopMessage(error))
2278 lprintf("cache_.Open():[%s]\n", error.c_str());
2280 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2281 [delegate_ repairWithSelector:@selector(configure)];
2282 else if (error == "The package lists or status file could not be parsed or opened.")
2283 [delegate_ repairWithSelector:@selector(update)];
2284 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2285 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2286 // else if (error == "The list of sources could not be read.")
2287 else _assert(false);
2293 now_ = [[NSDate date] retain];
2295 policy_ = new pkgDepCache::Policy();
2296 records_ = new pkgRecords(cache_);
2297 resolver_ = new pkgProblemResolver(cache_);
2298 fetcher_ = new pkgAcquire(&status_);
2301 list_ = new pkgSourceList();
2302 _assert(list_->ReadMainList());
2304 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2305 _assert(pkgApplyStatus(cache_));
2307 if (cache_->BrokenCount() != 0) {
2308 _assert(pkgFixBroken(cache_));
2309 _assert(cache_->BrokenCount() == 0);
2310 _assert(pkgMinimizeUpgrade(cache_));
2313 [sources_ removeAllObjects];
2314 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2315 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2316 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2318 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2319 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2323 [packages_ removeAllObjects];
2326 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2327 if (Package *package = [Package packageWithIterator:iterator database:self])
2328 [packages_ addObject:package];
2330 [packages_ sortUsingSelector:@selector(compareByName:)];
2334 - (void) configure {
2335 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2336 system([dpkg UTF8String]);
2344 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2345 _assert(!_error->PendingError());
2348 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2351 public pkgArchiveCleaner
2354 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2359 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2361 while (_error->PopMessage(error))
2362 lprintf("ArchiveCleaner: %s\n", error.c_str());
2367 pkgRecords records(cache_);
2369 lock_ = new FileFd();
2370 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2371 _assert(!_error->PendingError());
2374 // XXX: explain this with an error message
2375 _assert(list.ReadMainList());
2377 manager_ = (_system->CreatePM(cache_));
2378 _assert(manager_->GetArchives(fetcher_, &list, &records));
2379 _assert(!_error->PendingError());
2383 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2385 _assert(list.ReadMainList());
2386 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2387 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2390 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2395 bool failed = false;
2396 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2397 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2400 std::string uri = (*item)->DescURI();
2401 std::string error = (*item)->ErrorText;
2403 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2406 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2407 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2418 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2420 if (_error->PendingError()) {
2425 if (result == pkgPackageManager::Failed) {
2430 if (result != pkgPackageManager::Completed) {
2435 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2437 _assert(list.ReadMainList());
2438 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2439 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2442 if (![before isEqualToArray:after])
2447 _assert(pkgDistUpgrade(cache_));
2451 [self updateWithStatus:status_];
2454 - (void) updateWithStatus:(Status &)status {
2456 _assert(list.ReadMainList());
2459 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2460 _assert(!_error->PendingError());
2462 pkgAcquire fetcher(&status);
2463 _assert(list.GetIndexes(&fetcher));
2465 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2466 bool failed = false;
2467 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2468 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2469 (*item)->Finished();
2473 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2474 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2475 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2478 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2483 - (void) setDelegate:(id)delegate {
2484 delegate_ = delegate;
2485 status_.setDelegate(delegate);
2486 progress_.setDelegate(delegate);
2489 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2490 pkgIndexFile *index(NULL);
2491 list_->FindIndex(file, index);
2492 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2498 /* PopUp Windows {{{ */
2499 @interface PopUpView : UIView {
2500 _transient id delegate_;
2501 UITransitionView *transition_;
2506 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2510 @implementation PopUpView
2513 [transition_ setDelegate:nil];
2514 [transition_ release];
2520 [transition_ transition:UITransitionPushFromTop toView:nil];
2523 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2524 if (from != nil && to == nil)
2525 [self removeFromSuperview];
2528 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2529 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2530 delegate_ = delegate;
2532 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2533 [self addSubview:transition_];
2535 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2537 [view addSubview:self];
2539 [transition_ setDelegate:self];
2541 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2542 [transition_ transition:UITransitionNone toView:blank];
2543 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2550 /* Mail Composition {{{ */
2551 @interface MailToView : PopUpView {
2552 MailComposeController *controller_;
2555 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2559 @implementation MailToView
2562 [controller_ release];
2566 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2570 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2571 NSLog(@"did:%@", delivery);
2572 // [UIApp setStatusBarShowsProgress:NO];
2573 if ([controller error]){
2574 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2575 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2576 [mailAlertSheet setBodyText:[controller error]];
2577 [mailAlertSheet popupAlertAnimated:YES];
2581 - (void) showError {
2582 NSLog(@"%@", [controller_ error]);
2583 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2584 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2585 [mailAlertSheet setBodyText:[controller_ error]];
2586 [mailAlertSheet popupAlertAnimated:YES];
2589 - (void) deliverMessage { _pooled
2593 if (![controller_ deliverMessage])
2594 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2597 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2598 if ([controller_ needsDelivery])
2599 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2604 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2605 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2606 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2607 [controller_ setDelegate:self];
2608 [controller_ initializeUI];
2609 [controller_ setupForURL:url];
2611 UIView *view([controller_ view]);
2612 [overlay_ addSubview:view];
2618 /* Confirmation View {{{ */
2619 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2620 if (!iterator.end())
2621 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2622 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2624 pkgCache::PkgIterator package(dep.TargetPkg());
2627 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2634 @protocol ConfirmationViewDelegate
2639 @interface ConfirmationView : BrowserView {
2640 _transient Database *database_;
2641 UIActionSheet *essential_;
2648 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2652 @implementation ConfirmationView
2659 if (essential_ != nil)
2660 [essential_ release];
2666 [book_ popFromSuperviewAnimated:YES];
2669 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2670 NSString *context = [sheet context];
2672 if ([context isEqualToString:@"remove"])
2680 [delegate_ confirm];
2685 else if ([context isEqualToString:@"unable"])
2691 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2692 [window setValue:changes_ forKey:@"changes"];
2693 [window setValue:issues_ forKey:@"issues"];
2694 [window setValue:sizes_ forKey:@"sizes"];
2695 [super webView:sender didClearWindowObject:window forFrame:frame];
2698 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2699 if ((self = [super initWithBook:book]) != nil) {
2700 database_ = database;
2702 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2703 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2704 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2705 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2706 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2710 pkgDepCache::Policy *policy([database_ policy]);
2712 pkgCacheFile &cache([database_ cache]);
2713 NSArray *packages = [database_ packages];
2714 for (size_t i(0), e = [packages count]; i != e; ++i) {
2715 Package *package = [packages objectAtIndex:i];
2716 pkgCache::PkgIterator iterator = [package iterator];
2717 pkgDepCache::StateCache &state(cache[iterator]);
2719 NSString *name([package name]);
2721 if (state.NewInstall())
2722 [installing addObject:name];
2723 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2724 [reinstalling addObject:name];
2725 else if (state.Upgrade())
2726 [upgrading addObject:name];
2727 else if (state.Downgrade())
2728 [downgrading addObject:name];
2729 else if (state.Delete()) {
2730 if ([package essential])
2732 [removing addObject:name];
2735 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2736 substrate_ |= DepSubstrate(iterator.CurrentVer());
2741 else if (Advanced_ || true) {
2742 essential_ = [[UIActionSheet alloc]
2743 initWithTitle:@"Removing Essentials"
2744 buttons:[NSArray arrayWithObjects:
2745 @"Cancel Operation (Safe)",
2746 @"Force Removal (Unsafe)",
2748 defaultButtonIndex:0
2754 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2756 [essential_ setBodyText:@"This operation involves the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. If you continue, you may not be able to use Cydia to repair any damage."];
2758 essential_ = [[UIActionSheet alloc]
2759 initWithTitle:@"Unable to Comply"
2760 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2761 defaultButtonIndex:0
2766 [essential_ setBodyText:@"This operation requires the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. In order to continue and force this operation you will need to be activate the Advanced mode under to continue and force this operation you will need to be activate the Advanced mode under Settings."];
2769 changes_ = [[NSArray alloc] initWithObjects:
2777 issues_ = [database_ issues];
2779 issues_ = [issues_ retain];
2781 sizes_ = [[NSArray alloc] initWithObjects:
2782 SizeString([database_ fetcher].FetchNeeded()),
2783 SizeString([database_ fetcher].PartialPresent()),
2784 SizeString([database_ cache]->UsrSize()),
2787 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2791 - (NSString *) backButtonTitle {
2795 - (NSString *) leftButtonTitle {
2799 - (NSString *) _rightButtonTitle {
2800 return issues_ == nil ? @"Confirm" : nil;
2803 - (void) _leftButtonClicked {
2808 - (void) _rightButtonClicked {
2809 if (essential_ != nil)
2810 [essential_ popupAlertAnimated:YES];
2814 [delegate_ confirm];
2822 /* Progress Data {{{ */
2823 @interface ProgressData : NSObject {
2829 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2836 @implementation ProgressData
2838 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2839 if ((self = [super init]) != nil) {
2840 selector_ = selector;
2860 /* Progress View {{{ */
2861 @interface ProgressView : UIView <
2862 ConfigurationDelegate,
2865 _transient Database *database_;
2867 UIView *background_;
2868 UITransitionView *transition_;
2870 UINavigationBar *navbar_;
2871 UIProgressBar *progress_;
2872 UITextView *output_;
2873 UITextLabel *status_;
2874 UIPushButton *close_;
2877 SHA1SumValue springlist_;
2878 SHA1SumValue sandplate_;
2880 NSTimeInterval last_;
2883 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2885 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2886 - (void) setContentView:(UIView *)view;
2889 - (void) _retachThread;
2890 - (void) _detachNewThreadData:(ProgressData *)data;
2891 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2897 @protocol ProgressViewDelegate
2898 - (void) progressViewIsComplete:(ProgressView *)sender;
2901 @implementation ProgressView
2904 [transition_ setDelegate:nil];
2905 [navbar_ setDelegate:nil];
2908 if (background_ != nil)
2909 [background_ release];
2910 [transition_ release];
2913 [progress_ release];
2920 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2921 if (bootstrap_ && from == overlay_ && to == view_)
2925 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2926 if ((self = [super initWithFrame:frame]) != nil) {
2927 database_ = database;
2928 delegate_ = delegate;
2930 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2931 [transition_ setDelegate:self];
2933 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2936 [overlay_ setBackgroundColor:[UIColor blackColor]];
2938 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2939 [background_ setBackgroundColor:[UIColor blackColor]];
2940 [self addSubview:background_];
2943 [self addSubview:transition_];
2945 CGSize navsize = [UINavigationBar defaultSize];
2946 CGRect navrect = {{0, 0}, navsize};
2948 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2949 [overlay_ addSubview:navbar_];
2951 [navbar_ setBarStyle:1];
2952 [navbar_ setDelegate:self];
2954 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2955 [navbar_ pushNavigationItem:navitem];
2957 CGRect bounds = [overlay_ bounds];
2958 CGSize prgsize = [UIProgressBar defaultSize];
2961 (bounds.size.width - prgsize.width) / 2,
2962 bounds.size.height - prgsize.height - 20
2965 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2966 [progress_ setStyle:0];
2968 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2970 bounds.size.height - prgsize.height - 50,
2971 bounds.size.width - 20,
2975 [status_ setColor:[UIColor whiteColor]];
2976 [status_ setBackgroundColor:[UIColor clearColor]];
2978 [status_ setCentersHorizontally:YES];
2979 //[status_ setFont:font];
2981 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2983 navrect.size.height + 20,
2984 bounds.size.width - 20,
2985 bounds.size.height - navsize.height - 62 - navrect.size.height
2988 //[output_ setTextFont:@"Courier New"];
2989 [output_ setTextSize:12];
2991 [output_ setTextColor:[UIColor whiteColor]];
2992 [output_ setBackgroundColor:[UIColor clearColor]];
2994 [output_ setMarginTop:0];
2995 [output_ setAllowsRubberBanding:YES];
2996 [output_ setEditable:NO];
2998 [overlay_ addSubview:output_];
3000 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3002 bounds.size.height - prgsize.height - 50,
3003 bounds.size.width - 20,
3007 [close_ setAutosizesToFit:NO];
3008 [close_ setDrawsShadow:YES];
3009 [close_ setStretchBackground:YES];
3010 [close_ setEnabled:YES];
3012 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3013 [close_ setTitleFont:bold];
3015 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3016 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3017 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3021 - (void) setContentView:(UIView *)view {
3022 view_ = [view retain];
3025 - (void) resetView {
3026 [transition_ transition:6 toView:view_];
3029 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3030 NSString *context = [sheet context];
3031 if ([context isEqualToString:@"conffile"]) {
3032 FILE *input = [database_ input];
3036 fprintf(input, "N\n");
3040 fprintf(input, "Y\n");
3051 - (void) closeButtonPushed {
3060 [delegate_ suspendWithAnimation:YES];
3064 system("launchctl stop com.apple.SpringBoard");
3068 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3077 - (void) _retachThread {
3078 UINavigationItem *item = [navbar_ topItem];
3079 [item setTitle:@"Complete"];
3081 [overlay_ addSubview:close_];
3082 [progress_ removeFromSuperview];
3083 [status_ removeFromSuperview];
3085 [delegate_ progressViewIsComplete:self];
3088 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3089 MMap mmap(file, MMap::ReadOnly);
3091 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3092 if (!(sandplate_ == sha1.Result()))
3097 FileFd file(SpringBoard_, FileFd::ReadOnly);
3098 MMap mmap(file, MMap::ReadOnly);
3100 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3101 if (!(springlist_ == sha1.Result()))
3106 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3107 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3108 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3109 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3110 case 4: [close_ setTitle:@"Reboot Device"]; break;
3113 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3115 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3116 [cache autorelease];
3118 NSFileManager *manager = [NSFileManager defaultManager];
3119 NSError *error = nil;
3121 id system = [cache objectForKey:@"System"];
3126 if (stat(Cache_, &info) == -1)
3129 [system removeAllObjects];
3131 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3132 for (NSString *app in apps)
3133 if ([app hasSuffix:@".app"]) {
3134 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3135 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3136 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3138 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3139 [info setObject:path forKey:@"Path"];
3140 [info setObject:@"System" forKey:@"ApplicationType"];
3141 [system addInfoDictionary:info];
3147 [cache writeToFile:@Cache_ atomically:YES];
3149 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3151 if (chmod(Cache_, info.st_mode) == -1)
3155 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3158 notify_post("com.apple.mobile.application_installed");
3160 [delegate_ setStatusBarShowsProgress:NO];
3163 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3164 [[data target] performSelector:[data selector] withObject:[data object]];
3167 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3170 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3171 UINavigationItem *item = [navbar_ topItem];
3172 [item setTitle:title];
3174 [status_ setText:nil];
3175 [output_ setText:@""];
3176 [progress_ setProgress:0];
3179 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3181 [close_ removeFromSuperview];
3182 [overlay_ addSubview:progress_];
3183 [overlay_ addSubview:status_];
3185 [delegate_ setStatusBarShowsProgress:YES];
3189 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3190 MMap mmap(file, MMap::ReadOnly);
3192 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3193 sandplate_ = sha1.Result();
3197 FileFd file(SpringBoard_, FileFd::ReadOnly);
3198 MMap mmap(file, MMap::ReadOnly);
3200 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3201 springlist_ = sha1.Result();
3204 [transition_ transition:6 toView:overlay_];
3207 detachNewThreadSelector:@selector(_detachNewThreadData:)
3209 withObject:[[ProgressData alloc]
3210 initWithSelector:selector
3217 - (void) repairWithSelector:(SEL)selector {
3219 detachNewThreadSelector:selector
3226 - (void) setConfigurationData:(NSString *)data {
3228 performSelectorOnMainThread:@selector(_setConfigurationData:)
3234 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3235 Package *package = id == nil ? nil : [database_ packageWithName:id];
3237 UIActionSheet *sheet = [[[UIActionSheet alloc]
3238 initWithTitle:(package == nil ? id : [package name])
3239 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3240 defaultButtonIndex:0
3245 [sheet setBodyText:error];
3246 [sheet popupAlertAnimated:YES];
3249 - (void) setProgressTitle:(NSString *)title {
3251 performSelectorOnMainThread:@selector(_setProgressTitle:)
3257 - (void) setProgressPercent:(float)percent {
3259 performSelectorOnMainThread:@selector(_setProgressPercent:)
3260 withObject:[NSNumber numberWithFloat:percent]
3265 - (void) startProgress {
3266 last_ = [NSDate timeIntervalSinceReferenceDate];
3269 - (void) addProgressOutput:(NSString *)output {
3271 performSelectorOnMainThread:@selector(_addProgressOutput:)
3277 - (bool) isCancelling:(size_t)received {
3279 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3280 if (received_ != received) {
3281 received_ = received;
3283 } else if (now - last_ > 30)
3290 - (void) _setConfigurationData:(NSString *)data {
3291 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3293 _assert(conffile_r(data));
3295 NSString *ofile = conffile_r[1];
3296 //NSString *nfile = conffile_r[2];
3298 UIActionSheet *sheet = [[[UIActionSheet alloc]
3299 initWithTitle:@"Configuration Upgrade"
3300 buttons:[NSArray arrayWithObjects:
3301 @"Keep My Old Copy",
3302 @"Accept The New Copy",
3303 // XXX: @"See What Changed",
3305 defaultButtonIndex:0
3310 [sheet setBodyText:[NSString stringWithFormat:
3311 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3314 [sheet popupAlertAnimated:YES];
3317 - (void) _setProgressTitle:(NSString *)title {
3318 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3319 for (size_t i(0), e([words count]); i != e; ++i) {
3320 NSString *word([words objectAtIndex:i]);
3321 if (Package *package = [database_ packageWithName:word])
3322 [words replaceObjectAtIndex:i withObject:[package name]];
3325 [status_ setText:[words componentsJoinedByString:@" "]];
3328 - (void) _setProgressPercent:(NSNumber *)percent {
3329 [progress_ setProgress:[percent floatValue]];
3332 - (void) _addProgressOutput:(NSString *)output {
3333 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3334 CGSize size = [output_ contentSize];
3335 CGRect rect = {{0, size.height}, {size.width, 0}};
3336 [output_ scrollRectToVisible:rect animated:YES];
3339 - (BOOL) isRunning {
3346 /* Package Cell {{{ */
3347 @interface PackageCell : UISimpleTableCell {
3350 NSString *description_;
3354 UITextLabel *status_;
3358 - (PackageCell *) init;
3359 - (void) setPackage:(Package *)package;
3361 + (int) heightForPackage:(Package *)package;
3365 @implementation PackageCell
3367 - (void) clearPackage {
3378 if (description_ != nil) {
3379 [description_ release];
3383 if (source_ != nil) {
3388 if (badge_ != nil) {
3395 [self clearPackage];
3402 - (PackageCell *) init {
3403 if ((self = [super init]) != nil) {
3405 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3406 [status_ setBackgroundColor:[UIColor clearColor]];
3407 [status_ setFont:small];
3412 - (void) setPackage:(Package *)package {
3413 [self clearPackage];
3415 Source *source = [package source];
3416 NSString *section = [package simpleSection];
3418 icon_ = [[package icon] retain];
3420 name_ = [[package name] retain];
3421 description_ = [[package tagline] retain];
3423 NSString *label = nil;
3424 bool trusted = false;
3426 if (source != nil) {
3427 label = [source label];
3428 trusted = [source trusted];
3429 } else if ([[package id] isEqualToString:@"firmware"])
3432 label = @"Unknown/Local";
3434 NSString *from = [NSString stringWithFormat:@"from %@", label];
3436 if (section != nil && ![section isEqualToString:label])
3437 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3439 source_ = [from retain];
3441 if (NSString *purpose = [package primaryPurpose])
3442 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3443 badge_ = [badge_ retain];
3446 if (NSString *mode = [package mode]) {
3447 [badge_ setImage:[UIImage applicationImageNamed:
3448 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3451 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3452 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3453 } else if ([package half]) {
3454 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3455 [status_ setText:@"Package Damaged"];
3456 [status_ setColor:[UIColor redColor]];
3458 [badge_ setImage:nil];
3459 [status_ setText:nil];
3464 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3467 rect.size = [icon_ size];
3469 rect.size.width /= 2;
3470 rect.size.height /= 2;
3472 rect.origin.x = 25 - rect.size.width / 2;
3473 rect.origin.y = 25 - rect.size.height / 2;
3475 [icon_ drawInRect:rect];
3478 if (badge_ != nil) {
3479 CGSize size = [badge_ size];
3481 [badge_ drawAtPoint:CGPointMake(
3482 36 - size.width / 2,
3483 36 - size.height / 2
3492 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3493 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3497 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3499 [super drawContentInRect:rect selected:selected];
3502 + (int) heightForPackage:(Package *)package {
3503 NSString *tagline([package tagline]);
3504 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3506 if ([package hasMode] || [package half])
3515 /* Section Cell {{{ */
3516 @interface SectionCell : UISimpleTableCell {
3521 _UISwitchSlider *switch_;
3526 - (void) setSection:(Section *)section editing:(BOOL)editing;
3530 @implementation SectionCell
3532 - (void) clearSection {
3533 if (section_ != nil) {
3543 if (count_ != nil) {
3550 [self clearSection];
3557 if ((self = [super init]) != nil) {
3558 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3560 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3561 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3565 - (void) onSwitch:(id)sender {
3566 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3567 if (metadata == nil) {
3568 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3569 [Sections_ setObject:metadata forKey:section_];
3573 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3576 - (void) setSection:(Section *)section editing:(BOOL)editing {
3577 if (editing != editing_) {
3579 [switch_ removeFromSuperview];
3581 [self addSubview:switch_];
3585 [self clearSection];
3587 if (section == nil) {
3588 name_ = [@"All Packages" retain];
3591 section_ = [section name];
3592 if (section_ != nil)
3593 section_ = [section_ retain];
3594 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3595 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3598 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3602 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3603 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3610 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3612 CGSize size = [count_ sizeWithFont:Font14_];
3616 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3618 [super drawContentInRect:rect selected:selected];
3624 /* File Table {{{ */
3625 @interface FileTable : RVPage {
3626 _transient Database *database_;
3629 NSMutableArray *files_;
3633 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3634 - (void) setPackage:(Package *)package;
3638 @implementation FileTable
3641 if (package_ != nil)
3650 - (int) numberOfRowsInTable:(UITable *)table {
3651 return files_ == nil ? 0 : [files_ count];
3654 - (float) table:(UITable *)table heightForRow:(int)row {
3658 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3659 if (reusing == nil) {
3660 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3661 UIFont *font = [UIFont systemFontOfSize:16];
3662 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3664 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3668 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3672 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3673 if ((self = [super initWithBook:book]) != nil) {
3674 database_ = database;
3676 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3678 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3679 [self addSubview:list_];
3681 UITableColumn *column = [[[UITableColumn alloc]
3682 initWithTitle:@"Name"
3684 width:[self frame].size.width
3687 [list_ setDataSource:self];
3688 [list_ setSeparatorStyle:1];
3689 [list_ addTableColumn:column];
3690 [list_ setDelegate:self];
3691 [list_ setReusesTableCells:YES];
3695 - (void) setPackage:(Package *)package {
3696 if (package_ != nil) {
3697 [package_ autorelease];
3706 [files_ removeAllObjects];
3708 if (package != nil) {
3709 package_ = [package retain];
3710 name_ = [[package id] retain];
3712 if (NSArray *files = [package files])
3713 [files_ addObjectsFromArray:files];
3715 if ([files_ count] != 0) {
3716 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3717 [files_ removeObjectAtIndex:0];
3718 [files_ sortUsingSelector:@selector(compareByPath:)];
3720 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3721 [stack addObject:@"/"];
3723 for (int i(0), e([files_ count]); i != e; ++i) {
3724 NSString *file = [files_ objectAtIndex:i];
3725 while (![file hasPrefix:[stack lastObject]])
3726 [stack removeLastObject];
3727 NSString *directory = [stack lastObject];
3728 [stack addObject:[file stringByAppendingString:@"/"]];
3729 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3730 ([stack count] - 2) * 3, "",
3731 [file substringFromIndex:[directory length]]
3740 - (void) resetViewAnimated:(BOOL)animated {
3741 [list_ resetViewAnimated:animated];
3744 - (void) reloadData {
3745 [self setPackage:[database_ packageWithName:name_]];
3746 [self reloadButtons];
3749 - (NSString *) title {
3750 return @"Installed Files";
3753 - (NSString *) backButtonTitle {
3759 /* Package View {{{ */
3760 @interface PackageView : BrowserView {
3761 _transient Database *database_;
3764 NSMutableArray *buttons_;
3767 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3768 - (void) setPackage:(Package *)package;
3772 @implementation PackageView
3775 if (package_ != nil)
3783 - (void) _clickButtonWithName:(NSString *)name {
3784 if ([name isEqualToString:@"Install"])
3785 [delegate_ installPackage:package_];
3786 else if ([name isEqualToString:@"Reinstall"])
3787 [delegate_ installPackage:package_];
3788 else if ([name isEqualToString:@"Remove"])
3789 [delegate_ removePackage:package_];
3790 else if ([name isEqualToString:@"Upgrade"])
3791 [delegate_ installPackage:package_];
3792 else _assert(false);
3795 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3796 int count = [buttons_ count];
3797 _assert(count != 0);
3798 _assert(button <= count + 1);
3800 if (count != button - 1)
3801 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3806 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3807 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3808 return [super webView:sender didFinishLoadForFrame:frame];
3811 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3812 [window setValue:package_ forKey:@"package"];
3813 [super webView:sender didClearWindowObject:window forFrame:frame];
3817 - (void) _rightButtonClicked {
3818 /*[super _rightButtonClicked];
3821 int count = [buttons_ count];
3822 _assert(count != 0);
3825 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3827 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3828 [buttons addObjectsFromArray:buttons_];
3829 [buttons addObject:@"Cancel"];
3831 [delegate_ slideUp:[[[UIActionSheet alloc]
3834 defaultButtonIndex:2
3842 - (NSString *) _rightButtonTitle {
3843 int count = [buttons_ count];
3844 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3847 - (NSString *) backButtonTitle {
3851 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3852 if ((self = [super initWithBook:book]) != nil) {
3853 database_ = database;
3854 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3858 - (void) setPackage:(Package *)package {
3859 if (package_ != nil) {
3860 [package_ autorelease];
3869 [buttons_ removeAllObjects];
3871 if (package != nil) {
3872 package_ = [package retain];
3873 name_ = [[package id] retain];
3875 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3877 if ([package_ source] == nil);
3878 else if ([package_ upgradableAndEssential:NO])
3879 [buttons_ addObject:@"Upgrade"];
3880 else if ([package_ installed] == nil)
3881 [buttons_ addObject:@"Install"];
3883 [buttons_ addObject:@"Reinstall"];
3884 if ([package_ installed] != nil)
3885 [buttons_ addObject:@"Remove"];
3889 - (void) reloadData {
3890 [self setPackage:[database_ packageWithName:name_]];
3891 [self reloadButtons];
3896 /* Package Table {{{ */
3897 @interface PackageTable : RVPage {
3898 _transient Database *database_;
3902 NSMutableArray *packages_;
3903 NSMutableArray *sections_;
3904 UISectionList *list_;
3907 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3909 - (void) setDelegate:(id)delegate;
3910 - (void) setObject:(id)object;
3912 - (void) reloadData;
3913 - (void) resetCursor;
3915 - (UISectionList *) list;
3917 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3921 @implementation PackageTable
3924 [list_ setDataSource:nil];
3929 [packages_ release];
3930 [sections_ release];
3935 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3936 return [sections_ count];
3939 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3940 return [[sections_ objectAtIndex:section] name];
3943 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3944 return [[sections_ objectAtIndex:section] row];
3947 - (int) numberOfRowsInTable:(UITable *)table {
3948 return [packages_ count];
3951 - (float) table:(UITable *)table heightForRow:(int)row {
3952 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3955 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3957 reusing = [[[PackageCell alloc] init] autorelease];
3958 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3962 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3966 - (void) tableRowSelected:(NSNotification *)notification {
3967 int row = [[notification object] selectedRow];
3971 Package *package = [packages_ objectAtIndex:row];
3972 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3973 [view setDelegate:delegate_];
3974 [view setPackage:package];
3975 [book_ pushPage:view];
3978 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3979 if ((self = [super initWithBook:book]) != nil) {
3980 database_ = database;
3981 title_ = [title retain];
3983 object_ = object == nil ? nil : [object retain];
3985 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3986 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3988 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3989 [list_ setDataSource:self];
3991 UITableColumn *column = [[[UITableColumn alloc]
3992 initWithTitle:@"Name"
3994 width:[self frame].size.width
3997 UITable *table = [list_ table];
3998 [table setSeparatorStyle:1];
3999 [table addTableColumn:column];
4000 [table setDelegate:self];
4001 [table setReusesTableCells:YES];
4003 [self addSubview:list_];
4006 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4007 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4011 - (void) setDelegate:(id)delegate {
4012 delegate_ = delegate;
4015 - (void) setObject:(id)object {
4021 object_ = [object retain];
4024 - (void) reloadData {
4025 NSArray *packages = [database_ packages];
4027 [packages_ removeAllObjects];
4028 [sections_ removeAllObjects];
4030 for (size_t i(0); i != [packages count]; ++i) {
4031 Package *package([packages objectAtIndex:i]);
4032 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4033 [packages_ addObject:package];
4036 Section *section = nil;
4038 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4039 Package *package = [packages_ objectAtIndex:offset];
4040 NSString *name = [package index];
4042 if (section == nil || ![[section name] isEqualToString:name]) {
4043 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4044 [sections_ addObject:section];
4047 [section addToCount];
4053 - (NSString *) title {
4057 - (void) resetViewAnimated:(BOOL)animated {
4058 [list_ resetViewAnimated:animated];
4061 - (void) resetCursor {
4062 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4065 - (UISectionList *) list {
4069 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4070 [list_ setShouldHideHeaderInShortLists:hide];
4076 /* Add Source View {{{ */
4077 @interface AddSourceView : RVPage {
4078 _transient Database *database_;
4081 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4085 @implementation AddSourceView
4087 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4088 if ((self = [super initWithBook:book]) != nil) {
4089 database_ = database;
4095 /* Source Cell {{{ */
4096 @interface SourceCell : UITableCell {
4099 NSString *description_;
4105 - (SourceCell *) initWithSource:(Source *)source;
4109 @implementation SourceCell
4114 [description_ release];
4119 - (SourceCell *) initWithSource:(Source *)source {
4120 if ((self = [super init]) != nil) {
4122 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4124 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4125 icon_ = [icon_ retain];
4127 origin_ = [[source name] retain];
4128 label_ = [[source uri] retain];
4129 description_ = [[source description] retain];
4133 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4135 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4142 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4146 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4150 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4152 [super drawContentInRect:rect selected:selected];
4157 /* Source Table {{{ */
4158 @interface SourceTable : RVPage {
4159 _transient Database *database_;
4160 UISectionList *list_;
4161 NSMutableArray *sources_;
4162 UIActionSheet *alert_;
4166 UIProgressHUD *hud_;
4169 //NSURLConnection *installer_;
4170 NSURLConnection *trivial_bz2_;
4171 NSURLConnection *trivial_gz_;
4172 //NSURLConnection *automatic_;
4177 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4181 @implementation SourceTable
4183 - (void) _deallocConnection:(NSURLConnection *)connection {
4184 if (connection != nil) {
4185 [connection cancel];
4186 //[connection setDelegate:nil];
4187 [connection release];
4192 [[list_ table] setDelegate:nil];
4193 [list_ setDataSource:nil];
4202 //[self _deallocConnection:installer_];
4203 [self _deallocConnection:trivial_gz_];
4204 [self _deallocConnection:trivial_bz2_];
4205 //[self _deallocConnection:automatic_];
4212 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4213 return offset_ == 0 ? 1 : 2;
4216 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4217 switch (section + (offset_ == 0 ? 1 : 0)) {
4218 case 0: return @"Entered by User";
4219 case 1: return @"Installed by Packages";
4227 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4228 switch (section + (offset_ == 0 ? 1 : 0)) {
4230 case 1: return offset_;
4238 - (int) numberOfRowsInTable:(UITable *)table {
4239 return [sources_ count];
4242 - (float) table:(UITable *)table heightForRow:(int)row {
4243 Source *source = [sources_ objectAtIndex:row];
4244 return [source description] == nil ? 56 : 73;
4247 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4248 Source *source = [sources_ objectAtIndex:row];
4249 // XXX: weird warning, stupid selectors ;P
4250 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4253 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4257 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4261 - (void) tableRowSelected:(NSNotification*)notification {
4262 UITable *table([list_ table]);
4263 int row([table selectedRow]);
4267 Source *source = [sources_ objectAtIndex:row];
4269 PackageTable *packages = [[[PackageTable alloc]
4272 title:[source label]
4273 filter:@selector(isVisibleInSource:)
4277 [packages setDelegate:delegate_];
4279 [book_ pushPage:packages];
4282 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4283 Source *source = [sources_ objectAtIndex:row];
4284 return [source record] != nil;
4287 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4288 [[list_ table] setDeleteConfirmationRow:row];
4291 - (void) table:(UITable *)table deleteRow:(int)row {
4292 Source *source = [sources_ objectAtIndex:row];
4293 [Sources_ removeObjectForKey:[source key]];
4294 [delegate_ syncData];
4297 - (void) _endConnection:(NSURLConnection *)connection {
4298 NSURLConnection **field = NULL;
4299 if (connection == trivial_bz2_)
4300 field = &trivial_bz2_;
4301 else if (connection == trivial_gz_)
4302 field = &trivial_gz_;
4303 _assert(field != NULL);
4304 [connection release];
4308 trivial_bz2_ == nil &&
4311 [delegate_ setStatusBarShowsProgress:NO];
4314 [hud_ removeFromSuperview];
4319 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4322 @"./", @"Distribution",
4323 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4325 [delegate_ syncData];
4326 } else if (error_ != nil) {
4327 UIActionSheet *sheet = [[[UIActionSheet alloc]
4328 initWithTitle:@"Verification Error"
4329 buttons:[NSArray arrayWithObjects:@"OK", nil]
4330 defaultButtonIndex:0
4335 [sheet setBodyText:[error_ localizedDescription]];
4336 [sheet popupAlertAnimated:YES];
4338 UIActionSheet *sheet = [[[UIActionSheet alloc]
4339 initWithTitle:@"Did not Find Repository"
4340 buttons:[NSArray arrayWithObjects:@"OK", nil]
4341 defaultButtonIndex:0
4346 [sheet setBodyText:@"The indicated repository could not be found. This could be because you are trying to add a legacy Installer repository (these are not supported). Also, this interface is only capable of working with exact repository URLs. If you host a repository and are having issues please contact the author of Cydia with any questions you have."];
4347 [sheet popupAlertAnimated:YES];
4353 if (error_ != nil) {
4360 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4361 switch ([response statusCode]) {
4367 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4368 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4370 error_ = [error retain];
4371 [self _endConnection:connection];
4374 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4375 [self _endConnection:connection];
4378 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4379 NSMutableURLRequest *request = [NSMutableURLRequest
4380 requestWithURL:[NSURL URLWithString:href]
4381 cachePolicy:NSURLRequestUseProtocolCachePolicy
4382 timeoutInterval:20.0
4385 [request setHTTPMethod:method];
4387 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4390 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4391 NSString *context = [sheet context];
4392 if ([context isEqualToString:@"source"])
4395 NSString *href = [[sheet textField] text];
4397 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4399 if (![href hasSuffix:@"/"])
4400 href_ = [href stringByAppendingString:@"/"];
4403 href_ = [href_ retain];
4405 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4406 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4407 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4411 hud_ = [delegate_ addProgressHUD];
4412 [hud_ setText:@"Verifying URL"];
4425 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4426 if ((self = [super initWithBook:book]) != nil) {
4427 database_ = database;
4428 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4430 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4431 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4432 [list_ setShouldHideHeaderInShortLists:NO];
4434 [self addSubview:list_];
4435 [list_ setDataSource:self];
4437 UITableColumn *column = [[UITableColumn alloc]
4438 initWithTitle:@"Name"
4440 width:[self frame].size.width
4443 UITable *table = [list_ table];
4444 [table setSeparatorStyle:1];
4445 [table addTableColumn:column];
4446 [table setDelegate:self];
4450 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4451 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4455 - (void) reloadData {
4457 _assert(list.ReadMainList());
4459 [sources_ removeAllObjects];
4460 [sources_ addObjectsFromArray:[database_ sources]];
4462 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4465 int count = [sources_ count];
4466 for (offset_ = 0; offset_ != count; ++offset_) {
4467 Source *source = [sources_ objectAtIndex:offset_];
4468 if ([source record] == nil)
4475 - (void) resetViewAnimated:(BOOL)animated {
4476 [list_ resetViewAnimated:animated];
4479 - (void) _leftButtonClicked {
4480 /*[book_ pushPage:[[[AddSourceView alloc]
4485 UIActionSheet *sheet = [[[UIActionSheet alloc]
4486 initWithTitle:@"Enter Cydia/APT URL"
4487 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4488 defaultButtonIndex:0
4493 [sheet addTextFieldWithValue:@"http://" label:@""];
4495 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4496 [traits setAutocapitalizationType:0];
4497 [traits setKeyboardType:3];
4498 [traits setAutocorrectionType:1];
4500 [sheet popupAlertAnimated:YES];
4503 - (void) _rightButtonClicked {
4504 UITable *table = [list_ table];
4505 BOOL editing = [table isRowDeletionEnabled];
4506 [table enableRowDeletion:!editing animated:YES];
4507 [book_ reloadButtonsForPage:self];
4510 - (NSString *) title {
4514 - (NSString *) leftButtonTitle {
4515 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4518 - (NSString *) rightButtonTitle {
4519 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4522 - (UINavigationButtonStyle) rightButtonStyle {
4523 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4529 /* Installed View {{{ */
4530 @interface InstalledView : RVPage {
4531 _transient Database *database_;
4532 PackageTable *packages_;
4536 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4540 @implementation InstalledView
4543 [packages_ release];
4547 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4548 if ((self = [super initWithBook:book]) != nil) {
4549 database_ = database;
4551 packages_ = [[PackageTable alloc]
4555 filter:@selector(isInstalledAndVisible:)
4556 with:[NSNumber numberWithBool:YES]
4559 [self addSubview:packages_];
4561 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4562 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4566 - (void) resetViewAnimated:(BOOL)animated {
4567 [packages_ resetViewAnimated:animated];
4570 - (void) reloadData {
4571 [packages_ reloadData];
4574 - (void) _rightButtonClicked {
4575 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4576 [packages_ reloadData];
4578 [book_ reloadButtonsForPage:self];
4581 - (NSString *) title {
4582 return @"Installed";
4585 - (NSString *) backButtonTitle {
4589 - (NSString *) rightButtonTitle {
4590 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4593 - (UINavigationButtonStyle) rightButtonStyle {
4594 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4597 - (void) setDelegate:(id)delegate {
4598 [super setDelegate:delegate];
4599 [packages_ setDelegate:delegate];
4606 @interface HomeView : BrowserView {
4611 @implementation HomeView
4613 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4617 - (void) _leftButtonClicked {
4618 UIActionSheet *sheet = [[[UIActionSheet alloc]
4619 initWithTitle:@"About Cydia Installer"
4620 buttons:[NSArray arrayWithObjects:@"Close", nil]
4621 defaultButtonIndex:0
4627 @"Copyright (C) 2008\n"
4628 "Jay Freeman (saurik)\n"
4629 "saurik@saurik.com\n"
4630 "http://www.saurik.com/\n"
4633 "http://www.theokorigroup.com/\n"
4635 "College of Creative Studies,\n"
4636 "University of California,\n"
4638 "http://www.ccs.ucsb.edu/"
4641 [sheet popupAlertAnimated:YES];
4644 - (NSString *) leftButtonTitle {
4650 /* Manage View {{{ */
4651 @interface ManageView : BrowserView {
4656 @implementation ManageView
4658 - (NSString *) title {
4662 - (void) _leftButtonClicked {
4663 [delegate_ askForSettings];
4666 - (NSString *) leftButtonTitle {
4670 - (NSString *) _rightButtonTitle {
4677 @interface WebView (Cydia)
4678 - (void) setScriptDebugDelegate:(id)delegate;
4679 - (void) _setFormDelegate:(id)delegate;
4680 - (void) _setUIKitDelegate:(id)delegate;
4681 - (void) setWebMailDelegate:(id)delegate;
4682 - (void) _setLayoutInterval:(float)interval;
4685 /* Indirect Delegate {{{ */
4686 @interface IndirectDelegate : NSProxy {
4687 _transient volatile id delegate_;
4690 - (void) setDelegate:(id)delegate;
4691 - (id) initWithDelegate:(id)delegate;
4694 @implementation IndirectDelegate
4696 - (void) setDelegate:(id)delegate {
4697 delegate_ = delegate;
4700 - (id) initWithDelegate:(id)delegate {
4701 delegate_ = delegate;
4705 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4706 if (delegate_ != nil)
4707 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4709 // XXX: I fucking hate Apple so very very bad
4710 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4713 - (void) forwardInvocation:(NSInvocation *)inv {
4714 SEL sel = [inv selector];
4715 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4716 [inv invokeWithTarget:delegate_];
4721 /* Browser Implementation {{{ */
4722 @implementation BrowserView
4725 WebView *webview = [webview_ webView];
4726 [webview setFrameLoadDelegate:nil];
4727 [webview setResourceLoadDelegate:nil];
4728 [webview setUIDelegate:nil];
4729 [webview setScriptDebugDelegate:nil];
4730 [webview setPolicyDelegate:nil];
4732 [webview setDownloadDelegate:nil];
4734 [webview _setFormDelegate:nil];
4735 [webview _setUIKitDelegate:nil];
4736 [webview setWebMailDelegate:nil];
4737 [webview setEditingDelegate:nil];
4739 [webview_ setDelegate:nil];
4740 [webview_ setGestureDelegate:nil];
4742 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4747 [webview_ removeFromSuperview];
4748 [Documents_ addObject:[webview_ autorelease]];
4753 [indirect_ setDelegate:nil];
4754 [indirect_ release];
4756 [scroller_ setDelegate:nil];
4758 [scroller_ release];
4760 [indicator_ release];
4766 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4767 [self loadRequest:[NSURLRequest
4770 timeoutInterval:30.0
4774 - (void) loadURL:(NSURL *)url {
4775 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4778 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4779 NSMutableURLRequest *copy = [request mutableCopy];
4781 if (Machine_ != NULL)
4782 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4783 if (UniqueID_ != nil)
4784 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4787 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4792 - (void) loadRequest:(NSURLRequest *)request {
4794 [webview_ loadRequest:request];
4797 - (void) reloadURL {
4798 if ([urls_ count] == 0)
4800 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4801 [urls_ removeLastObject];
4802 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4805 - (WebView *) webView {
4806 return [webview_ webView];
4809 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4810 [scroller_ setContentSize:frame.size];
4813 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4814 [self view:sender didSetFrame:frame];
4817 - (void) pushPage:(RVPage *)page {
4818 [self setBackButtonTitle:title_];
4819 [page setDelegate:delegate_];
4820 [book_ pushPage:page];
4823 - (BOOL) getSpecial:(NSURL *)url {
4824 NSString *href([url absoluteString]);
4825 NSString *scheme([[url scheme] lowercaseString]);
4829 if ([href hasPrefix:@"apptapp://package/"])
4830 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4831 else if ([scheme isEqualToString:@"cydia"]) {
4832 page = [delegate_ pageForURL:url hasTag:NULL];
4835 } else if (![scheme isEqualToString:@"apptapp"])
4839 [self pushPage:page];
4843 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4844 [window setValue:delegate_ forKey:@"cydia"];
4847 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4848 if (NSURL *url = [request URL]) {
4849 if (![self getSpecial:url]) {
4850 NSString *scheme([[url scheme] lowercaseString]);
4851 if ([scheme isEqualToString:@"mailto"])
4852 [delegate_ openMailToURL:url];
4861 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4862 if ([WebView canShowMIMEType:type])
4865 // XXX: handle more mime types!
4867 if (frame == [webView mainFrame])
4868 [UIApp openURL:[request URL]];
4872 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4873 NSURL *url([request URL]);
4875 if (url == nil) use: {
4880 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4883 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4884 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4887 [UIApp openURL:url];
4893 int store(_not(int));
4894 if (NSURL *itms = [url itmsURL:&store]) {
4895 NSLog(@"itms#%@#%u#%@", url, store, itms);
4897 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4898 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4905 NSString *scheme([[url scheme] lowercaseString]);
4907 if ([scheme isEqualToString:@"tel"]) {
4908 // XXX: intelligence
4912 if ([scheme isEqualToString:@"mailto"]) {
4913 [delegate_ openMailToURL:url];
4917 if ([self getSpecial:url])
4919 else if ([WebView _canHandleRequest:request])
4921 else if ([url isSpringboardHandledURL])
4927 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4928 //lprintf("Status:%s\n", [text UTF8String]);
4931 - (void) _pushPage {
4935 [book_ pushPage:self];
4938 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4939 NSURL *url = [request URL];
4940 if ([self getSpecial:url])
4943 return [self _addHeadersToRequest:request];
4946 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4947 [self setBackButtonTitle:title_];
4949 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4950 [browser setDelegate:delegate_];
4953 [browser loadRequest:[self _addHeadersToRequest:request]];
4954 [book_ pushPage:browser];
4957 return [browser webView];
4960 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4961 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4964 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4965 return [self _createWebViewWithRequest:request pushed:YES];
4968 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4969 if ([frame parentFrame] != nil)
4972 title_ = [title retain];
4973 [book_ reloadTitleForPage:self];
4976 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4977 if ([frame parentFrame] != nil)
4982 [indicator_ startAnimation];
4983 [self reloadButtons];
4985 if (title_ != nil) {
4990 [book_ reloadTitleForPage:self];
4992 WebView *webview = [webview_ webView];
4993 NSString *href = [webview mainFrameURL];
4994 [urls_ addObject:[NSURL URLWithString:href]];
4996 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4998 CGRect webrect = [scroller_ bounds];
4999 webrect.size.height = 0;
5000 [webview_ setFrame:webrect];
5003 - (void) _finishLoading {
5006 [indicator_ stopAnimation];
5007 [self reloadButtons];
5011 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5012 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5015 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5016 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5019 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5020 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5023 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5024 return [webview_ webView:sender didCommitLoadForFrame:frame];
5027 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5028 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5031 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5032 if ([frame parentFrame] == nil)
5033 [self _finishLoading];
5034 return [webview_ webView:sender didFinishLoadForFrame:frame];
5037 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5038 if ([frame parentFrame] != nil)
5040 [self _finishLoading];
5042 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5043 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5044 [[error localizedDescription] stringByAddingPercentEscapes]
5048 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5050 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5054 - (id) initWithBook:(RVBook *)book {
5055 if ((self = [super initWithBook:book]) != nil) {
5058 struct CGRect bounds = [self bounds];
5060 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5061 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5062 [self addSubview:pinstripe];
5064 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5065 [self addSubview:scroller_];
5067 [scroller_ setScrollingEnabled:YES];
5068 [scroller_ setAdjustForContentSizeChange:YES];
5069 [scroller_ setClipsSubviews:YES];
5070 [scroller_ setAllowsRubberBanding:YES];
5071 [scroller_ setScrollDecelerationFactor:0.99];
5072 [scroller_ setDelegate:self];
5074 CGRect webrect = [scroller_ bounds];
5075 webrect.size.height = 0;
5080 webview_ = [Documents_ lastObject];
5081 if (webview_ != nil) {
5082 webview_ = [webview_ retain];
5083 webview = [webview_ webView];
5084 [Documents_ removeLastObject];
5085 [webview_ setFrame:webrect];
5090 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5091 webview = [webview_ webView];
5093 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5095 [webview_ setAllowsMessaging:YES];
5097 [webview_ setTilingEnabled:YES];
5098 [webview_ setDrawsGrid:NO];
5099 [webview_ setLogsTilingChanges:NO];
5100 [webview_ setTileMinificationFilter:kCAFilterNearest];
5101 [webview_ setDetectsPhoneNumbers:NO];
5102 [webview_ setAutoresizes:YES];
5104 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5105 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5106 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5108 [webview_ _setDocumentType:0x4];
5110 [webview_ setZoomsFocusedFormControl:YES];
5111 [webview_ setContentsPosition:7];
5112 [webview_ setEnabledGestures:0xa];
5113 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
5114 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
5116 [webview_ setSmoothsFonts:YES];
5118 [webview _setUsesLoaderCache:YES];
5119 [webview setGroupName:@"Cydia"];
5120 //[webview _setLayoutInterval:0.5];
5123 [webview_ setDelegate:self];
5124 [webview_ setGestureDelegate:self];
5125 [scroller_ addSubview:webview_];
5127 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5129 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5130 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5131 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5133 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5134 NSString *application = package == nil ? @"Cydia" : [NSString
5135 stringWithFormat:@"Cydia/%@",
5137 ]; [webview setApplicationNameForUserAgent:application];
5139 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5141 [webview setFrameLoadDelegate:self];
5142 [webview setResourceLoadDelegate:indirect_];
5143 [webview setUIDelegate:self];
5144 [webview setScriptDebugDelegate:self];
5145 [webview setPolicyDelegate:self];
5147 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5149 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5150 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5151 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5155 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5156 [webview_ redrawScaledDocument];
5159 - (void) _rightButtonClicked {
5164 - (NSString *) _rightButtonTitle {
5168 - (NSString *) rightButtonTitle {
5169 return loading_ ? @"" : [self _rightButtonTitle];
5172 - (NSString *) title {
5173 return title_ == nil ? @"Loading" : title_;
5176 - (NSString *) backButtonTitle {
5180 - (void) setPageActive:(BOOL)active {
5182 [indicator_ removeFromSuperview];
5184 [[book_ navigationBar] addSubview:indicator_];
5187 - (void) resetViewAnimated:(BOOL)animated {
5190 - (void) setPushed:(bool)pushed {
5197 /* Cydia Book {{{ */
5198 @interface CYBook : RVBook <
5201 _transient Database *database_;
5202 UINavigationBar *overlay_;
5203 UINavigationBar *underlay_;
5204 UIProgressIndicator *indicator_;
5205 UITextLabel *prompt_;
5206 UIProgressBar *progress_;
5207 UINavigationButton *cancel_;
5210 NSTimeInterval last_;
5213 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5219 @implementation CYBook
5223 [indicator_ release];
5225 [progress_ release];
5230 - (NSString *) getTitleForPage:(RVPage *)page {
5231 return Simplify([super getTitleForPage:page]);
5239 [UIView beginAnimations:nil context:NULL];
5241 CGRect ovrframe = [overlay_ frame];
5242 ovrframe.origin.y = 0;
5243 [overlay_ setFrame:ovrframe];
5245 CGRect barframe = [navbar_ frame];
5246 barframe.origin.y += ovrframe.size.height;
5247 [navbar_ setFrame:barframe];
5249 CGRect trnframe = [transition_ frame];
5250 trnframe.origin.y += ovrframe.size.height;
5251 trnframe.size.height -= ovrframe.size.height;
5252 [transition_ setFrame:trnframe];
5254 [UIView endAnimations];
5256 [indicator_ startAnimation];
5257 [prompt_ setText:@"Updating Database"];
5258 [progress_ setProgress:0];
5261 last_ = [NSDate timeIntervalSinceReferenceDate];
5263 [overlay_ addSubview:cancel_];
5266 detachNewThreadSelector:@selector(_update)
5275 [indicator_ stopAnimation];
5277 [UIView beginAnimations:nil context:NULL];
5279 CGRect ovrframe = [overlay_ frame];
5280 ovrframe.origin.y = -ovrframe.size.height;
5281 [overlay_ setFrame:ovrframe];
5283 CGRect barframe = [navbar_ frame];
5284 barframe.origin.y -= ovrframe.size.height;
5285 [navbar_ setFrame:barframe];
5287 CGRect trnframe = [transition_ frame];
5288 trnframe.origin.y -= ovrframe.size.height;
5289 trnframe.size.height += ovrframe.size.height;
5290 [transition_ setFrame:trnframe];
5292 [UIView commitAnimations];
5294 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5297 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5298 if ((self = [super initWithFrame:frame]) != nil) {
5299 database_ = database;
5301 CGRect ovrrect = [navbar_ bounds];
5302 ovrrect.size.height = [UINavigationBar defaultSize].height;
5303 ovrrect.origin.y = -ovrrect.size.height;
5305 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5306 [self addSubview:overlay_];
5308 ovrrect.origin.y = frame.size.height;
5309 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5310 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5311 [self addSubview:underlay_];
5313 [overlay_ setBarStyle:1];
5314 [underlay_ setBarStyle:1];
5316 int barstyle = [overlay_ _barStyle:NO];
5317 bool ugly = barstyle == 0;
5319 UIProgressIndicatorStyle style = ugly ?
5320 UIProgressIndicatorStyleMediumBrown :
5321 UIProgressIndicatorStyleMediumWhite;
5323 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5324 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5325 CGRect indrect = {{indoffset, indoffset}, indsize};
5327 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5328 [indicator_ setStyle:style];
5329 [overlay_ addSubview:indicator_];
5331 CGSize prmsize = {215, indsize.height + 4};
5334 indoffset * 2 + indsize.width,
5338 unsigned(ovrrect.size.height - prmsize.height) / 2
5341 UIFont *font = [UIFont systemFontOfSize:15];
5343 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5345 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5346 [prompt_ setBackgroundColor:[UIColor clearColor]];
5347 [prompt_ setFont:font];
5349 [overlay_ addSubview:prompt_];
5351 CGSize prgsize = {75, 100};
5354 ovrrect.size.width - prgsize.width - 10,
5355 (ovrrect.size.height - prgsize.height) / 2
5358 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5359 [progress_ setStyle:0];
5360 [overlay_ addSubview:progress_];
5362 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5363 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5365 CGRect frame = [cancel_ frame];
5366 frame.size.width = 65;
5367 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5368 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5369 [cancel_ setFrame:frame];
5371 [cancel_ setBarStyle:barstyle];
5375 - (void) _onCancel {
5377 [cancel_ removeFromSuperview];
5380 - (void) _update { _pooled
5382 status.setDelegate(self);
5384 [database_ updateWithStatus:status];
5387 performSelectorOnMainThread:@selector(_update_)
5393 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5394 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5397 - (void) setProgressTitle:(NSString *)title {
5399 performSelectorOnMainThread:@selector(_setProgressTitle:)
5405 - (void) setProgressPercent:(float)percent {
5407 performSelectorOnMainThread:@selector(_setProgressPercent:)
5408 withObject:[NSNumber numberWithFloat:percent]
5413 - (void) startProgress {
5416 - (void) addProgressOutput:(NSString *)output {
5418 performSelectorOnMainThread:@selector(_addProgressOutput:)
5424 - (bool) isCancelling:(size_t)received {
5425 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5426 if (received_ != received) {
5427 received_ = received;
5429 } else if (now - last_ > 15)
5434 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5438 - (void) _setProgressTitle:(NSString *)title {
5439 [prompt_ setText:title];
5442 - (void) _setProgressPercent:(NSNumber *)percent {
5443 [progress_ setProgress:[percent floatValue]];
5446 - (void) _addProgressOutput:(NSString *)output {
5451 /* Cydia:// Protocol {{{ */
5452 @interface CydiaURLProtocol : NSURLProtocol {
5457 @implementation CydiaURLProtocol
5459 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5460 NSURL *url([request URL]);
5463 NSString *scheme([[url scheme] lowercaseString]);
5464 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5469 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5473 - (void) startLoading {
5474 id<NSURLProtocolClient> client([self client]);
5475 NSURLRequest *request([self request]);
5477 NSURL *url([request URL]);
5478 NSString *href([url absoluteString]);
5480 NSString *path([href substringFromIndex:8]);
5481 NSRange slash([path rangeOfString:@"/"]);
5484 if (slash.location == NSNotFound) {
5488 command = [path substringToIndex:slash.location];
5489 path = [path substringFromIndex:(slash.location + 1)];
5492 Database *database([Database sharedInstance]);
5494 if ([command isEqualToString:@"package-icon"]) {
5497 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5498 Package *package([database packageWithName:path]);
5502 UIImage *icon([package icon]);
5504 NSData *data(UIImagePNGRepresentation(icon));
5506 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5507 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5508 [client URLProtocol:self didLoadData:data];
5509 [client URLProtocolDidFinishLoading:self];
5510 } else if ([command isEqualToString:@"section-icon"]) {
5513 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5514 NSString *section(Simplify(path));
5516 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5518 icon = [UIImage applicationImageNamed:@"unknown.png"];
5520 NSData *data(UIImagePNGRepresentation(icon));
5522 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5523 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5524 [client URLProtocol:self didLoadData:data];
5525 [client URLProtocolDidFinishLoading:self];
5527 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5531 - (void) stopLoading {
5537 /* Install View {{{ */
5538 @interface InstallView : RVPage {
5539 _transient Database *database_;
5540 NSMutableArray *sections_;
5541 NSMutableArray *filtered_;
5542 UITransitionView *transition_;
5548 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5549 - (void) reloadData;
5554 @implementation InstallView
5557 [list_ setDataSource:nil];
5558 [list_ setDelegate:nil];
5560 [sections_ release];
5561 [filtered_ release];
5562 [transition_ release];
5564 [accessory_ release];
5568 - (int) numberOfRowsInTable:(UITable *)table {
5569 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5572 - (float) table:(UITable *)table heightForRow:(int)row {
5576 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5578 reusing = [[[SectionCell alloc] init] autorelease];
5579 [(SectionCell *)reusing setSection:(editing_ ?
5580 [sections_ objectAtIndex:row] :
5581 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5582 ) editing:editing_];
5586 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5590 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5594 - (void) tableRowSelected:(NSNotification *)notification {
5595 int row = [[notification object] selectedRow];
5606 title = @"All Packages";
5608 section = [filtered_ objectAtIndex:(row - 1)];
5609 name = [section name];
5615 title = @"(No Section)";
5619 PackageTable *table = [[[PackageTable alloc]
5623 filter:@selector(isVisiblyUninstalledInSection:)
5627 [table setDelegate:delegate_];
5629 [book_ pushPage:table];
5632 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5633 if ((self = [super initWithBook:book]) != nil) {
5634 database_ = database;
5636 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5637 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5639 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5640 [self addSubview:transition_];
5642 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5643 [transition_ transition:0 toView:list_];
5645 UITableColumn *column = [[[UITableColumn alloc]
5646 initWithTitle:@"Name"
5648 width:[self frame].size.width
5651 [list_ setDataSource:self];
5652 [list_ setSeparatorStyle:1];
5653 [list_ addTableColumn:column];
5654 [list_ setDelegate:self];
5655 [list_ setReusesTableCells:YES];
5659 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5660 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5664 - (void) reloadData {
5665 NSArray *packages = [database_ packages];
5667 [sections_ removeAllObjects];
5668 [filtered_ removeAllObjects];
5670 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5671 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5674 for (size_t i(0); i != [packages count]; ++i) {
5675 Package *package([packages objectAtIndex:i]);
5676 NSString *name([package section]);
5679 Section *section([sections objectForKey:name]);
5680 if (section == nil) {
5681 section = [[[Section alloc] initWithName:name] autorelease];
5682 [sections setObject:section forKey:name];
5686 if ([package valid] && [package installed] == nil && [package visible])
5687 [filtered addObject:package];
5691 [sections_ addObjectsFromArray:[sections allValues]];
5692 [sections_ sortUsingSelector:@selector(compareByName:)];
5695 [filtered sortUsingSelector:@selector(compareBySection:)];
5698 Section *section = nil;
5699 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5700 Package *package = [filtered objectAtIndex:offset];
5701 NSString *name = [package section];
5703 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5704 section = name == nil ?
5705 [[[Section alloc] initWithName:nil] autorelease] :
5706 [sections objectForKey:name];
5707 [filtered_ addObject:section];
5710 [section addToCount];
5718 - (void) resetView {
5720 [self _rightButtonClicked];
5723 - (void) resetViewAnimated:(BOOL)animated {
5724 [list_ resetViewAnimated:animated];
5727 - (void) _rightButtonClicked {
5728 if ((editing_ = !editing_))
5731 [delegate_ updateData];
5732 [book_ reloadTitleForPage:self];
5733 [book_ reloadButtonsForPage:self];
5736 - (NSString *) title {
5737 return editing_ ? @"Section Visibility" : @"Install by Section";
5740 - (NSString *) backButtonTitle {
5744 - (NSString *) rightButtonTitle {
5745 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5748 - (UINavigationButtonStyle) rightButtonStyle {
5749 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5752 - (UIView *) accessoryView {
5758 /* Changes View {{{ */
5759 @interface ChangesView : RVPage {
5760 _transient Database *database_;
5761 NSMutableArray *packages_;
5762 NSMutableArray *sections_;
5763 UISectionList *list_;
5767 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5768 - (void) reloadData;
5772 @implementation ChangesView
5775 [[list_ table] setDelegate:nil];
5776 [list_ setDataSource:nil];
5778 [packages_ release];
5779 [sections_ release];
5784 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5785 return [sections_ count];
5788 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5789 return [[sections_ objectAtIndex:section] name];
5792 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5793 return [[sections_ objectAtIndex:section] row];
5796 - (int) numberOfRowsInTable:(UITable *)table {
5797 return [packages_ count];
5800 - (float) table:(UITable *)table heightForRow:(int)row {
5801 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5804 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5806 reusing = [[[PackageCell alloc] init] autorelease];
5807 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5811 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5815 - (void) tableRowSelected:(NSNotification *)notification {
5816 int row = [[notification object] selectedRow];
5819 Package *package = [packages_ objectAtIndex:row];
5820 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5821 [view setDelegate:delegate_];
5822 [view setPackage:package];
5823 [book_ pushPage:view];
5826 - (void) _leftButtonClicked {
5827 [(CYBook *)book_ update];
5828 [self reloadButtons];
5831 - (void) _rightButtonClicked {
5832 [delegate_ distUpgrade];
5835 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5836 if ((self = [super initWithBook:book]) != nil) {
5837 database_ = database;
5839 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5840 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5842 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5843 [self addSubview:list_];
5845 [list_ setShouldHideHeaderInShortLists:NO];
5846 [list_ setDataSource:self];
5847 //[list_ setSectionListStyle:1];
5849 UITableColumn *column = [[[UITableColumn alloc]
5850 initWithTitle:@"Name"
5852 width:[self frame].size.width
5855 UITable *table = [list_ table];
5856 [table setSeparatorStyle:1];
5857 [table addTableColumn:column];
5858 [table setDelegate:self];
5859 [table setReusesTableCells:YES];
5863 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5864 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5868 - (void) reloadData {
5869 NSArray *packages = [database_ packages];
5871 [packages_ removeAllObjects];
5872 [sections_ removeAllObjects];
5875 for (size_t i(0); i != [packages count]; ++i) {
5876 Package *package([packages objectAtIndex:i]);
5879 [package installed] == nil && [package valid] && [package visible] ||
5880 [package upgradableAndEssential:NO]
5882 [packages_ addObject:package];
5886 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5889 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5890 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5891 Section *section = nil;
5895 bool unseens = false;
5897 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5900 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5901 Package *package = [packages_ objectAtIndex:offset];
5903 if (![package upgradableAndEssential:YES]) {
5905 NSDate *seen = [package seen];
5907 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5910 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5911 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5912 [sections_ addObject:section];
5916 [section addToCount];
5917 } else if ([package ignored])
5918 [ignored addToCount];
5921 [upgradable addToCount];
5926 CFRelease(formatter);
5929 Section *last = [sections_ lastObject];
5930 size_t count = [last count];
5931 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5932 [sections_ removeLastObject];
5935 if ([ignored count] != 0)
5936 [sections_ insertObject:ignored atIndex:0];
5938 [sections_ insertObject:upgradable atIndex:0];
5941 [self reloadButtons];
5944 - (void) resetViewAnimated:(BOOL)animated {
5945 [list_ resetViewAnimated:animated];
5948 - (NSString *) leftButtonTitle {
5949 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5952 - (NSString *) rightButtonTitle {
5953 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5956 - (NSString *) title {
5962 /* Search View {{{ */
5963 @protocol SearchViewDelegate
5964 - (void) showKeyboard:(BOOL)show;
5967 @interface SearchView : RVPage {
5969 UISearchField *field_;
5970 UITransitionView *transition_;
5971 PackageTable *table_;
5972 UIPreferencesTable *advanced_;
5978 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5979 - (void) reloadData;
5983 @implementation SearchView
5986 [field_ setDelegate:nil];
5988 [accessory_ release];
5990 [transition_ release];
5992 [advanced_ release];
5997 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6001 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6003 case 0: return @"Advanced Search (Coming Soon!)";
6005 default: _assert(false);
6009 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6013 default: _assert(false);
6017 - (void) _showKeyboard:(BOOL)show {
6018 CGSize keysize = [UIKeyboard defaultSize];
6019 CGRect keydown = [book_ pageBounds];
6020 CGRect keyup = keydown;
6021 keyup.size.height -= keysize.height - ButtonBarHeight_;
6023 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6025 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6026 [animation setSignificantRectFields:8];
6029 [animation setStartFrame:keydown];
6030 [animation setEndFrame:keyup];
6032 [animation setStartFrame:keyup];
6033 [animation setEndFrame:keydown];
6036 UIAnimator *animator = [UIAnimator sharedAnimator];
6039 addAnimations:[NSArray arrayWithObjects:animation, nil]
6040 withDuration:(KeyboardTime_ - delay)
6045 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6047 [delegate_ showKeyboard:show];
6050 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6051 [self _showKeyboard:YES];
6054 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6055 [self _showKeyboard:NO];
6058 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6060 NSString *text([field_ text]);
6061 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6067 - (void) textFieldClearButtonPressed:(UITextField *)field {
6071 - (void) keyboardInputShouldDelete:(id)input {
6075 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6076 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6080 [field_ resignFirstResponder];
6085 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6086 if ((self = [super initWithBook:book]) != nil) {
6087 CGRect pageBounds = [book_ pageBounds];
6089 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6090 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6091 [self addSubview:pinstripe];*/
6093 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6094 [self addSubview:transition_];
6096 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6098 [advanced_ setReusesTableCells:YES];
6099 [advanced_ setDataSource:self];
6100 [advanced_ reloadData];
6102 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6103 CGColor dimmed(space_, 0, 0, 0, 0.5);
6104 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6106 table_ = [[PackageTable alloc]
6110 filter:@selector(isUnfilteredAndSearchedForBy:)
6114 [table_ setShouldHideHeaderInShortLists:NO];
6115 [transition_ transition:0 toView:table_];
6124 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6131 [self bounds].size.width - area.origin.x - 18;
6133 area.size.height = [UISearchField defaultHeight];
6135 field_ = [[UISearchField alloc] initWithFrame:area];
6137 UIFont *font = [UIFont systemFontOfSize:16];
6138 [field_ setFont:font];
6140 [field_ setPlaceholder:@"Package Names & Descriptions"];
6141 [field_ setDelegate:self];
6143 [field_ setPaddingTop:5];
6145 UITextInputTraits *traits = [field_ textInputTraits];
6146 [traits setAutocapitalizationType:0];
6147 [traits setAutocorrectionType:1];
6148 [traits setReturnKeyType:6];
6150 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6152 accessory_ = [[UIView alloc] initWithFrame:accrect];
6153 [accessory_ addSubview:field_];
6155 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6156 [configure setShowPressFeedback:YES];
6157 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6158 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6159 [accessory_ addSubview:configure];*/
6161 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6162 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6168 LKAnimation *animation = [LKTransition animation];
6169 [animation setType:@"oglFlip"];
6170 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6171 [animation setFillMode:@"extended"];
6172 [animation setTransitionFlags:3];
6173 [animation setDuration:10];
6174 [animation setSpeed:0.35];
6175 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6176 [[transition_ _layer] addAnimation:animation forKey:0];
6177 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6178 flipped_ = !flipped_;
6182 - (void) configurePushed {
6183 [field_ resignFirstResponder];
6187 - (void) resetViewAnimated:(BOOL)animated {
6190 [table_ resetViewAnimated:animated];
6193 - (void) _reloadData {
6196 - (void) reloadData {
6199 [table_ setObject:[field_ text]];
6200 [table_ reloadData];
6201 [table_ resetCursor];
6204 - (UIView *) accessoryView {
6208 - (NSString *) title {
6212 - (NSString *) backButtonTitle {
6216 - (void) setDelegate:(id)delegate {
6217 [table_ setDelegate:delegate];
6218 [super setDelegate:delegate];
6224 @interface SettingsView : RVPage {
6225 _transient Database *database_;
6228 UIPreferencesTable *table_;
6229 _UISwitchSlider *subscribedSwitch_;
6230 _UISwitchSlider *ignoredSwitch_;
6231 UIPreferencesControlTableCell *subscribedCell_;
6232 UIPreferencesControlTableCell *ignoredCell_;
6235 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6239 @implementation SettingsView
6242 [table_ setDataSource:nil];
6245 if (package_ != nil)
6248 [subscribedSwitch_ release];
6249 [ignoredSwitch_ release];
6250 [subscribedCell_ release];
6251 [ignoredCell_ release];
6255 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6256 if (package_ == nil)
6262 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6263 if (package_ == nil)
6270 default: _assert(false);
6276 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6277 if (package_ == nil)
6284 default: _assert(false);
6290 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6291 if (package_ == nil)
6298 default: _assert(false);
6304 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6305 if (package_ == nil)
6308 _UISwitchSlider *slider([cell control]);
6309 BOOL value([slider value] != 0);
6310 NSMutableDictionary *metadata([package_ metadata]);
6313 if (NSNumber *number = [metadata objectForKey:key])
6314 before = [number boolValue];
6318 if (value != before) {
6319 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6321 [delegate_ updateData];
6325 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6326 [self onSomething:cell withKey:@"IsSubscribed"];
6329 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6330 [self onSomething:cell withKey:@"IsIgnored"];
6333 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6334 if (package_ == nil)
6338 case 0: switch (row) {
6340 return subscribedCell_;
6342 return ignoredCell_;
6343 default: _assert(false);
6346 case 1: switch (row) {
6348 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6349 [cell setShowSelection:NO];
6350 [cell setTitle:@"Changes only shows upgrades to installed packages so as to minimize spam from packagers. Activate this to see upgrades to this package even when it is not installed."];
6354 default: _assert(false);
6357 default: _assert(false);
6363 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6364 if ((self = [super initWithBook:book])) {
6365 database_ = database;
6366 name_ = [package retain];
6368 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6369 [self addSubview:table_];
6371 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6372 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6374 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6375 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6377 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6378 [subscribedCell_ setShowSelection:NO];
6379 [subscribedCell_ setTitle:@"Show All Changes"];
6380 [subscribedCell_ setControl:subscribedSwitch_];
6382 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6383 [ignoredCell_ setShowSelection:NO];
6384 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6385 [ignoredCell_ setControl:ignoredSwitch_];
6387 [table_ setDataSource:self];
6392 - (void) resetViewAnimated:(BOOL)animated {
6393 [table_ resetViewAnimated:animated];
6396 - (void) reloadData {
6397 if (package_ != nil)
6398 [package_ autorelease];
6399 package_ = [database_ packageWithName:name_];
6400 if (package_ != nil) {
6402 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6403 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6406 [table_ reloadData];
6409 - (NSString *) title {
6415 /* Signature View {{{ */
6416 @interface SignatureView : BrowserView {
6417 _transient Database *database_;
6421 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6425 @implementation SignatureView
6432 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6434 [super webView:sender didClearWindowObject:window forFrame:frame];
6437 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6438 if ((self = [super initWithBook:book]) != nil) {
6439 database_ = database;
6440 package_ = [package retain];
6445 - (void) resetViewAnimated:(BOOL)animated {
6448 - (void) reloadData {
6449 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6455 @interface Cydia : UIApplication <
6456 ConfirmationViewDelegate,
6457 ProgressViewDelegate,
6466 UIToolbar *buttonbar_;
6470 NSMutableArray *essential_;
6471 NSMutableArray *broken_;
6473 Database *database_;
6474 ProgressView *progress_;
6478 UIKeyboard *keyboard_;
6479 UIProgressHUD *hud_;
6481 InstallView *install_;
6482 ChangesView *changes_;
6483 ManageView *manage_;
6484 SearchView *search_;
6489 @implementation Cydia
6492 if ([broken_ count] != 0) {
6493 int count = [broken_ count];
6495 UIActionSheet *sheet = [[[UIActionSheet alloc]
6496 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6497 buttons:[NSArray arrayWithObjects:
6499 @"Ignore (Temporary)",
6501 defaultButtonIndex:0
6506 [sheet setBodyText:@"When the shell scripts associated with packages fail, they are left in a bad state known as either half-configured or half-installed. These errors don't go away and instead continue to cause issues. These scripts can be deleted and the packages forcibly removed."];
6507 [sheet popupAlertAnimated:YES];
6508 } else if (!Ignored_ && [essential_ count] != 0) {
6509 int count = [essential_ count];
6511 UIActionSheet *sheet = [[[UIActionSheet alloc]
6512 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6513 buttons:[NSArray arrayWithObjects:
6514 @"Upgrade Essential",
6515 @"Complete Upgrade",
6516 @"Ignore (Temporary)",
6518 defaultButtonIndex:0
6523 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6524 [sheet popupAlertAnimated:YES];
6528 - (void) _reloadData {
6529 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6530 [hud setText:@"Reloading Data"];
6531 [overlay_ addSubview:hud];
6534 [database_ reloadData];
6538 [essential_ removeAllObjects];
6539 [broken_ removeAllObjects];
6541 NSArray *packages = [database_ packages];
6542 for (Package *package in packages) {
6544 [broken_ addObject:package];
6545 if ([package upgradableAndEssential:NO]) {
6546 if ([package essential])
6547 [essential_ addObject:package];
6553 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6554 [buttonbar_ setBadgeValue:badge forButton:3];
6555 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6556 [buttonbar_ setBadgeAnimated:YES forButton:3];
6557 [self setApplicationBadge:badge];
6559 [buttonbar_ setBadgeValue:nil forButton:3];
6560 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6561 [buttonbar_ setBadgeAnimated:NO forButton:3];
6562 [self removeApplicationBadge];
6568 if ([packages count] == 0);
6580 [hud removeFromSuperview];*/
6583 - (void) _saveConfig {
6586 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6592 - (void) updateData {
6595 /* XXX: this is just stupid */
6596 if (tag_ != 2 && install_ != nil)
6597 [install_ reloadData];
6598 if (tag_ != 3 && changes_ != nil)
6599 [changes_ reloadData];
6600 if (tag_ != 5 && search_ != nil)
6601 [search_ reloadData];
6611 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6612 _assert(file != NULL);
6614 NSArray *keys = [Sources_ allKeys];
6616 for (int i(0), e([keys count]); i != e; ++i) {
6617 NSString *key = [keys objectAtIndex:i];
6618 NSDictionary *source = [Sources_ objectForKey:key];
6620 fprintf(file, "%s %s %s\n",
6621 [[source objectForKey:@"Type"] UTF8String],
6622 [[source objectForKey:@"URI"] UTF8String],
6623 [[source objectForKey:@"Distribution"] UTF8String]
6632 detachNewThreadSelector:@selector(update_)
6635 title:@"Updating Sources"
6639 - (void) reloadData {
6640 @synchronized (self) {
6641 if (confirm_ == nil)
6647 pkgProblemResolver *resolver = [database_ resolver];
6649 resolver->InstallProtect();
6650 if (!resolver->Resolve(true))
6655 [database_ prepare];
6657 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6658 [confirm_ setDelegate:self];
6660 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6661 [page setDelegate:self];
6663 [confirm_ setPage:page];
6664 [underlay_ popSubview:confirm_];
6667 - (void) installPackage:(Package *)package {
6668 @synchronized (self) {
6675 - (void) removePackage:(Package *)package {
6676 @synchronized (self) {
6683 - (void) distUpgrade {
6684 @synchronized (self) {
6685 [database_ upgrade];
6691 @synchronized (self) {
6693 if (confirm_ != nil) {
6701 [overlay_ removeFromSuperview];
6705 detachNewThreadSelector:@selector(perform)
6712 - (void) bootstrap_ {
6714 [database_ upgrade];
6715 [database_ prepare];
6716 [database_ perform];
6719 - (void) bootstrap {
6721 detachNewThreadSelector:@selector(bootstrap_)
6724 title:@"Bootstrap Install"
6728 - (void) progressViewIsComplete:(ProgressView *)progress {
6729 if (confirm_ != nil) {
6730 [underlay_ addSubview:overlay_];
6731 [confirm_ popFromSuperviewAnimated:NO];
6737 - (void) setPage:(RVPage *)page {
6738 [page resetViewAnimated:NO];
6739 [page setDelegate:self];
6740 [book_ setPage:page];
6743 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6744 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6745 [browser loadURL:url];
6749 - (void) _setHomePage {
6750 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6753 - (void) buttonBarItemTapped:(id)sender {
6754 unsigned tag = [sender tag];
6756 [book_ resetViewAnimated:YES];
6758 } else if (tag_ == 2 && tag != 2)
6759 [install_ resetView];
6762 case 1: [self _setHomePage]; break;
6764 case 2: [self setPage:install_]; break;
6765 case 3: [self setPage:changes_]; break;
6766 case 4: [self setPage:manage_]; break;
6767 case 5: [self setPage:search_]; break;
6769 default: _assert(false);
6775 - (void) applicationWillSuspend {
6777 [super applicationWillSuspend];
6780 - (void) askForSettings {
6781 UIActionSheet *role = [[[UIActionSheet alloc]
6782 initWithTitle:@"Who Are You?"
6783 buttons:[NSArray arrayWithObjects:
6784 @"User (Graphical Only)",
6785 @"Hacker (+ Command Line)",
6786 @"Developer (No Filters)",
6788 defaultButtonIndex:-1
6793 [role setBodyText:@"Not all of the packages available via Cydia are designed to be used by all users. Please categorize yourself so that Cydia can apply helpful filters.\n\nThis choice can be changed from \"Settings\" under the \"Manage\" tab."];
6794 [role popupAlertAnimated:YES];
6799 [self setStatusBarShowsProgress:NO];
6802 [hud_ removeFromSuperview];
6806 pid_t pid = ExecFork();
6808 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6809 perror("launchctl stop");
6816 [self askForSettings];
6820 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6822 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6823 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6824 0, 0, screenrect.size.width, screenrect.size.height - 48
6825 ) database:database_];
6827 [book_ setDelegate:self];
6829 [overlay_ addSubview:book_];
6831 NSArray *buttonitems = [NSArray arrayWithObjects:
6832 [NSDictionary dictionaryWithObjectsAndKeys:
6833 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6834 @"home-up.png", kUIButtonBarButtonInfo,
6835 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6836 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6837 self, kUIButtonBarButtonTarget,
6838 @"Home", kUIButtonBarButtonTitle,
6839 @"0", kUIButtonBarButtonType,
6842 [NSDictionary dictionaryWithObjectsAndKeys:
6843 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6844 @"install-up.png", kUIButtonBarButtonInfo,
6845 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6846 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6847 self, kUIButtonBarButtonTarget,
6848 @"Sections", kUIButtonBarButtonTitle,
6849 @"0", kUIButtonBarButtonType,
6852 [NSDictionary dictionaryWithObjectsAndKeys:
6853 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6854 @"changes-up.png", kUIButtonBarButtonInfo,
6855 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6856 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6857 self, kUIButtonBarButtonTarget,
6858 @"Changes", kUIButtonBarButtonTitle,
6859 @"0", kUIButtonBarButtonType,
6862 [NSDictionary dictionaryWithObjectsAndKeys:
6863 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6864 @"manage-up.png", kUIButtonBarButtonInfo,
6865 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6866 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6867 self, kUIButtonBarButtonTarget,
6868 @"Manage", kUIButtonBarButtonTitle,
6869 @"0", kUIButtonBarButtonType,
6872 [NSDictionary dictionaryWithObjectsAndKeys:
6873 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6874 @"search-up.png", kUIButtonBarButtonInfo,
6875 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6876 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6877 self, kUIButtonBarButtonTarget,
6878 @"Search", kUIButtonBarButtonTitle,
6879 @"0", kUIButtonBarButtonType,
6883 buttonbar_ = [[UIToolbar alloc]
6885 withFrame:CGRectMake(
6886 0, screenrect.size.height - ButtonBarHeight_,
6887 screenrect.size.width, ButtonBarHeight_
6889 withItemList:buttonitems
6892 [buttonbar_ setDelegate:self];
6893 [buttonbar_ setBarStyle:1];
6894 [buttonbar_ setButtonBarTrackingMode:2];
6896 int buttons[5] = {1, 2, 3, 4, 5};
6897 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6898 [buttonbar_ showButtonGroup:0 withDuration:0];
6900 for (int i = 0; i != 5; ++i)
6901 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6902 i * 64 + 2, 1, 60, ButtonBarHeight_
6905 [buttonbar_ showSelectionForButton:1];
6906 [overlay_ addSubview:buttonbar_];
6908 [UIKeyboard initImplementationNow];
6909 CGSize keysize = [UIKeyboard defaultSize];
6910 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6911 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6912 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6913 [overlay_ addSubview:keyboard_];
6916 [underlay_ addSubview:overlay_];
6920 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6921 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6922 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6924 manage_ = (ManageView *) [[self
6925 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6926 withClass:[ManageView class]
6932 [self _setHomePage];
6935 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6936 NSString *context = [sheet context];
6937 if ([context isEqualToString:@"fixhalf"])
6940 @synchronized (self) {
6941 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6942 Package *broken = [broken_ objectAtIndex:i];
6945 NSString *id = [broken id];
6946 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6947 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6948 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6949 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6958 [broken_ removeAllObjects];
6965 else if ([context isEqualToString:@"role"]) {
6967 case 1: Role_ = @"User"; break;
6968 case 2: Role_ = @"Hacker"; break;
6969 case 3: Role_ = @"Developer"; break;
6976 bool reset = Settings_ != nil;
6978 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6982 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6990 } else if ([context isEqualToString:@"upgrade"])
6993 @synchronized (self) {
6994 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6995 Package *essential = [essential_ objectAtIndex:i];
6996 [essential install];
7019 - (void) reorganize { _pooled
7020 system("/usr/libexec/cydia/free.sh");
7021 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7024 - (void) applicationSuspend:(__GSEvent *)event {
7025 if (hud_ == nil && ![progress_ isRunning])
7026 [super applicationSuspend:event];
7029 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7031 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7034 - (void) _setSuspended:(BOOL)value {
7036 [super _setSuspended:value];
7039 - (UIProgressHUD *) addProgressHUD {
7040 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7042 [underlay_ addSubview:hud];
7046 - (void) openMailToURL:(NSURL *)url {
7047 // XXX: this makes me sad
7049 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7051 [UIApp openURL:url];
7055 - (RVPage *) pageForPackage:(NSString *)name {
7056 if (Package *package = [database_ packageWithName:name]) {
7057 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7058 [view setPackage:package];
7061 UIActionSheet *sheet = [[[UIActionSheet alloc]
7062 initWithTitle:@"Cannot Locate Package"
7063 buttons:[NSArray arrayWithObjects:@"Close", nil]
7064 defaultButtonIndex:0
7069 [sheet setBodyText:[NSString stringWithFormat:
7070 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7073 [sheet popupAlertAnimated:YES];
7078 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7079 NSString *href = [url absoluteString];
7084 if ([href isEqualToString:@"cydia://add-source"])
7085 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7086 else if ([href isEqualToString:@"cydia://sources"])
7087 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7088 else if ([href isEqualToString:@"cydia://packages"])
7089 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7090 else if ([href hasPrefix:@"cydia://url/"])
7091 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7092 else if ([href hasPrefix:@"cydia://launch/"])
7093 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7094 else if ([href hasPrefix:@"cydia://package-settings/"])
7095 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7096 else if ([href hasPrefix:@"cydia://package-signature/"])
7097 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7098 else if ([href hasPrefix:@"cydia://package/"])
7099 return [self pageForPackage:[href substringFromIndex:16]];
7100 else if ([href hasPrefix:@"cydia://files/"]) {
7101 NSString *name = [href substringFromIndex:14];
7103 if (Package *package = [database_ packageWithName:name]) {
7104 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7105 [files setPackage:package];
7113 - (void) applicationOpenURL:(NSURL *)url {
7114 [super applicationOpenURL:url];
7116 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7117 [self setPage:page];
7118 [buttonbar_ showSelectionForButton:tag];
7123 - (void) applicationDidFinishLaunching:(id)unused {
7124 Font12_ = [[UIFont systemFontOfSize:12] retain];
7125 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7126 Font14_ = [[UIFont systemFontOfSize:14] retain];
7127 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7128 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7130 _assert(pkgInitConfig(*_config));
7131 _assert(pkgInitSystem(*_config, _system));
7135 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7136 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7138 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7140 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7141 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7143 [window_ orderFront:self];
7144 [window_ makeKey:self];
7145 [window_ setHidden:NO];
7147 database_ = [Database sharedInstance];
7148 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7149 [database_ setDelegate:progress_];
7150 [window_ setContentView:progress_];
7152 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7153 [progress_ setContentView:underlay_];
7155 [progress_ resetView];
7158 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7159 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7160 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7161 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7162 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7163 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7164 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7166 [self setIdleTimerDisabled:YES];
7168 hud_ = [self addProgressHUD];
7169 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
7171 [self setStatusBarShowsProgress:YES];
7174 detachNewThreadSelector:@selector(reorganize)
7182 /* Web Scripting {{{ */
7183 + (NSString *) webScriptNameForSelector:(SEL)selector {
7184 if (selector == @selector(supports:))
7189 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7190 return selector != @selector(supports:);
7193 - (BOOL) supports:(NSString *)feature {
7194 return [feature isEqualToString:@"window.open"];
7198 - (void) showKeyboard:(BOOL)show {
7199 CGSize keysize = [UIKeyboard defaultSize];
7200 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7201 CGRect keyup = keydown;
7202 keyup.origin.y -= keysize.height;
7204 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7205 [animation setSignificantRectFields:2];
7208 [animation setStartFrame:keydown];
7209 [animation setEndFrame:keyup];
7210 [keyboard_ activate];
7212 [animation setStartFrame:keyup];
7213 [animation setEndFrame:keydown];
7214 [keyboard_ deactivate];
7217 [[UIAnimator sharedAnimator]
7218 addAnimations:[NSArray arrayWithObjects:animation, nil]
7219 withDuration:KeyboardTime_
7224 - (void) slideUp:(UIActionSheet *)alert {
7226 [alert presentSheetFromButtonBar:buttonbar_];
7228 [alert presentSheetInView:overlay_];
7233 void AddPreferences(NSString *plist) { _pooled
7234 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7235 _assert(settings != NULL);
7236 NSMutableArray *items = [settings objectForKey:@"items"];
7240 for (size_t i(0); i != [items count]; ++i) {
7241 NSMutableDictionary *item([items objectAtIndex:i]);
7242 NSString *label = [item objectForKey:@"label"];
7243 if (label != nil && [label isEqualToString:@"Cydia"]) {
7250 for (size_t i(0); i != [items count]; ++i) {
7251 NSDictionary *item([items objectAtIndex:i]);
7252 NSString *label = [item objectForKey:@"label"];
7253 if (label != nil && [label isEqualToString:@"General"]) {
7254 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7255 @"CydiaSettings", @"bundle",
7256 @"PSLinkCell", @"cell",
7257 [NSNumber numberWithBool:YES], @"hasIcon",
7258 [NSNumber numberWithBool:YES], @"isController",
7260 nil] atIndex:(i + 1)];
7266 _assert([settings writeToFile:plist atomically:YES] == YES);
7271 id Alloc_(id self, SEL selector) {
7272 id object = alloc_(self, selector);
7273 lprintf("[%s]A-%p\n", self->isa->name, object);
7278 id Dealloc_(id self, SEL selector) {
7279 id object = dealloc_(self, selector);
7280 lprintf("[%s]D-%p\n", self->isa->name, object);
7284 int main(int argc, char *argv[]) { _pooled
7285 bool substrate(false);
7291 for (int argi(1); argi != argc; ++argi)
7292 if (strcmp(argv[argi], "--") == 0) {
7294 argv[argi] = argv[0];
7300 for (int argi(1); argi != arge; ++argi)
7301 if (strcmp(args[argi], "--bootstrap") == 0)
7303 else if (strcmp(args[argi], "--substrate") == 0)
7306 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7309 App_ = [[NSBundle mainBundle] bundlePath];
7310 Home_ = NSHomeDirectory();
7311 Locale_ = CFLocaleCopyCurrent();
7314 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7315 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7316 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7317 Sounds_Keyboard_ = [keyboard boolValue];
7323 #if 0 /* XXX: this costs 1.4s of startup performance */
7324 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7325 _assert(errno == ENOENT);
7326 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7327 _assert(errno == ENOENT);
7330 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7331 alloc_ = alloc->method_imp;
7332 alloc->method_imp = (IMP) &Alloc_;*/
7334 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7335 dealloc_ = dealloc->method_imp;
7336 dealloc->method_imp = (IMP) &Dealloc_;*/
7341 size = sizeof(maxproc);
7342 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7343 perror("sysctlbyname(\"kern.maxproc\", ?)");
7344 else if (maxproc < 64) {
7346 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7347 perror("sysctlbyname(\"kern.maxproc\", #)");
7350 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7351 char *machine = new char[size];
7352 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7353 perror("sysctlbyname(\"hw.machine\", ?)");
7357 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7359 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7360 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7362 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7363 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7365 Settings_ = [Metadata_ objectForKey:@"Settings"];
7367 Packages_ = [Metadata_ objectForKey:@"Packages"];
7368 Sections_ = [Metadata_ objectForKey:@"Sections"];
7369 Sources_ = [Metadata_ objectForKey:@"Sources"];
7372 if (Settings_ != nil)
7373 Role_ = [Settings_ objectForKey:@"Role"];
7375 if (Packages_ == nil) {
7376 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7377 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7380 if (Sections_ == nil) {
7381 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7382 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7385 if (Sources_ == nil) {
7386 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7387 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7391 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7394 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7395 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7397 if (access("/User", F_OK) != 0)
7398 system("/usr/libexec/cydia/firmware.sh");
7400 _assert([[NSFileManager defaultManager]
7401 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7402 withIntermediateDirectories:YES
7407 space_ = CGColorSpaceCreateDeviceRGB();
7409 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7410 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7411 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7412 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7413 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7414 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7416 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7418 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7420 UIApplicationUseLegacyEvents(YES);
7421 UIKeyboardDisableAutomaticAppearance();
7423 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7425 CGColorSpaceRelease(space_);