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];
2329 NSLog(@"profile_: %llu", profile_);
2331 [packages_ sortUsingSelector:@selector(compareByName:)];
2335 - (void) configure {
2336 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2337 system([dpkg UTF8String]);
2345 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2346 _assert(!_error->PendingError());
2349 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2352 public pkgArchiveCleaner
2355 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2360 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2362 while (_error->PopMessage(error))
2363 lprintf("ArchiveCleaner: %s\n", error.c_str());
2368 pkgRecords records(cache_);
2370 lock_ = new FileFd();
2371 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2372 _assert(!_error->PendingError());
2375 // XXX: explain this with an error message
2376 _assert(list.ReadMainList());
2378 manager_ = (_system->CreatePM(cache_));
2379 _assert(manager_->GetArchives(fetcher_, &list, &records));
2380 _assert(!_error->PendingError());
2384 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2386 _assert(list.ReadMainList());
2387 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2388 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2391 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2396 bool failed = false;
2397 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2398 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2401 std::string uri = (*item)->DescURI();
2402 std::string error = (*item)->ErrorText;
2404 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2407 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2408 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2419 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2421 if (_error->PendingError()) {
2426 if (result == pkgPackageManager::Failed) {
2431 if (result != pkgPackageManager::Completed) {
2436 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2438 _assert(list.ReadMainList());
2439 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2440 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2443 if (![before isEqualToArray:after])
2448 _assert(pkgDistUpgrade(cache_));
2452 [self updateWithStatus:status_];
2455 - (void) updateWithStatus:(Status &)status {
2457 _assert(list.ReadMainList());
2460 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2461 _assert(!_error->PendingError());
2463 pkgAcquire fetcher(&status);
2464 _assert(list.GetIndexes(&fetcher));
2466 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2467 bool failed = false;
2468 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2469 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2470 (*item)->Finished();
2474 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2475 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2476 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2479 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2484 - (void) setDelegate:(id)delegate {
2485 delegate_ = delegate;
2486 status_.setDelegate(delegate);
2487 progress_.setDelegate(delegate);
2490 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2491 pkgIndexFile *index(NULL);
2492 list_->FindIndex(file, index);
2493 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2499 /* PopUp Windows {{{ */
2500 @interface PopUpView : UIView {
2501 _transient id delegate_;
2502 UITransitionView *transition_;
2507 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2511 @implementation PopUpView
2514 [transition_ setDelegate:nil];
2515 [transition_ release];
2521 [transition_ transition:UITransitionPushFromTop toView:nil];
2524 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2525 if (from != nil && to == nil)
2526 [self removeFromSuperview];
2529 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2530 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2531 delegate_ = delegate;
2533 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2534 [self addSubview:transition_];
2536 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2538 [view addSubview:self];
2540 [transition_ setDelegate:self];
2542 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2543 [transition_ transition:UITransitionNone toView:blank];
2544 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2551 /* Mail Composition {{{ */
2552 @interface MailToView : PopUpView {
2553 MailComposeController *controller_;
2556 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2560 @implementation MailToView
2563 [controller_ release];
2567 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2571 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2572 NSLog(@"did:%@", delivery);
2573 // [UIApp setStatusBarShowsProgress:NO];
2574 if ([controller error]){
2575 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2576 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2577 [mailAlertSheet setBodyText:[controller error]];
2578 [mailAlertSheet popupAlertAnimated:YES];
2582 - (void) showError {
2583 NSLog(@"%@", [controller_ error]);
2584 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2585 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2586 [mailAlertSheet setBodyText:[controller_ error]];
2587 [mailAlertSheet popupAlertAnimated:YES];
2590 - (void) deliverMessage { _pooled
2594 if (![controller_ deliverMessage])
2595 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2598 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2599 if ([controller_ needsDelivery])
2600 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2605 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2606 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2607 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2608 [controller_ setDelegate:self];
2609 [controller_ initializeUI];
2610 [controller_ setupForURL:url];
2612 UIView *view([controller_ view]);
2613 [overlay_ addSubview:view];
2619 /* Confirmation View {{{ */
2620 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2621 if (!iterator.end())
2622 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2623 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2625 pkgCache::PkgIterator package(dep.TargetPkg());
2628 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2635 @protocol ConfirmationViewDelegate
2640 @interface ConfirmationView : BrowserView {
2641 _transient Database *database_;
2642 UIActionSheet *essential_;
2649 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2653 @implementation ConfirmationView
2660 if (essential_ != nil)
2661 [essential_ release];
2667 [book_ popFromSuperviewAnimated:YES];
2670 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2671 NSString *context = [sheet context];
2673 if ([context isEqualToString:@"remove"])
2681 [delegate_ confirm];
2686 else if ([context isEqualToString:@"unable"])
2692 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2693 [window setValue:changes_ forKey:@"changes"];
2694 [window setValue:issues_ forKey:@"issues"];
2695 [window setValue:sizes_ forKey:@"sizes"];
2696 [super webView:sender didClearWindowObject:window forFrame:frame];
2699 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2700 if ((self = [super initWithBook:book]) != nil) {
2701 database_ = database;
2703 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2704 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2705 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2706 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2707 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2711 pkgDepCache::Policy *policy([database_ policy]);
2713 pkgCacheFile &cache([database_ cache]);
2714 NSArray *packages = [database_ packages];
2715 for (size_t i(0), e = [packages count]; i != e; ++i) {
2716 Package *package = [packages objectAtIndex:i];
2717 pkgCache::PkgIterator iterator = [package iterator];
2718 pkgDepCache::StateCache &state(cache[iterator]);
2720 NSString *name([package name]);
2722 if (state.NewInstall())
2723 [installing addObject:name];
2724 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2725 [reinstalling addObject:name];
2726 else if (state.Upgrade())
2727 [upgrading addObject:name];
2728 else if (state.Downgrade())
2729 [downgrading addObject:name];
2730 else if (state.Delete()) {
2731 if ([package essential])
2733 [removing addObject:name];
2736 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2737 substrate_ |= DepSubstrate(iterator.CurrentVer());
2742 else if (Advanced_ || true) {
2743 essential_ = [[UIActionSheet alloc]
2744 initWithTitle:@"Removing Essentials"
2745 buttons:[NSArray arrayWithObjects:
2746 @"Cancel Operation (Safe)",
2747 @"Force Removal (Unsafe)",
2749 defaultButtonIndex:0
2755 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2757 [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."];
2759 essential_ = [[UIActionSheet alloc]
2760 initWithTitle:@"Unable to Comply"
2761 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2762 defaultButtonIndex:0
2767 [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."];
2770 changes_ = [[NSArray alloc] initWithObjects:
2778 issues_ = [database_ issues];
2780 issues_ = [issues_ retain];
2782 sizes_ = [[NSArray alloc] initWithObjects:
2783 SizeString([database_ fetcher].FetchNeeded()),
2784 SizeString([database_ fetcher].PartialPresent()),
2785 SizeString([database_ cache]->UsrSize()),
2788 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2792 - (NSString *) backButtonTitle {
2796 - (NSString *) leftButtonTitle {
2800 - (NSString *) _rightButtonTitle {
2801 return issues_ == nil ? @"Confirm" : nil;
2804 - (void) _leftButtonClicked {
2809 - (void) _rightButtonClicked {
2810 if (essential_ != nil)
2811 [essential_ popupAlertAnimated:YES];
2815 [delegate_ confirm];
2823 /* Progress Data {{{ */
2824 @interface ProgressData : NSObject {
2830 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2837 @implementation ProgressData
2839 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2840 if ((self = [super init]) != nil) {
2841 selector_ = selector;
2861 /* Progress View {{{ */
2862 @interface ProgressView : UIView <
2863 ConfigurationDelegate,
2866 _transient Database *database_;
2868 UIView *background_;
2869 UITransitionView *transition_;
2871 UINavigationBar *navbar_;
2872 UIProgressBar *progress_;
2873 UITextView *output_;
2874 UITextLabel *status_;
2875 UIPushButton *close_;
2878 SHA1SumValue springlist_;
2879 SHA1SumValue sandplate_;
2881 NSTimeInterval last_;
2884 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2886 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2887 - (void) setContentView:(UIView *)view;
2890 - (void) _retachThread;
2891 - (void) _detachNewThreadData:(ProgressData *)data;
2892 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2898 @protocol ProgressViewDelegate
2899 - (void) progressViewIsComplete:(ProgressView *)sender;
2902 @implementation ProgressView
2905 [transition_ setDelegate:nil];
2906 [navbar_ setDelegate:nil];
2909 if (background_ != nil)
2910 [background_ release];
2911 [transition_ release];
2914 [progress_ release];
2921 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2922 if (bootstrap_ && from == overlay_ && to == view_)
2926 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2927 if ((self = [super initWithFrame:frame]) != nil) {
2928 database_ = database;
2929 delegate_ = delegate;
2931 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2932 [transition_ setDelegate:self];
2934 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2937 [overlay_ setBackgroundColor:[UIColor blackColor]];
2939 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2940 [background_ setBackgroundColor:[UIColor blackColor]];
2941 [self addSubview:background_];
2944 [self addSubview:transition_];
2946 CGSize navsize = [UINavigationBar defaultSize];
2947 CGRect navrect = {{0, 0}, navsize};
2949 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2950 [overlay_ addSubview:navbar_];
2952 [navbar_ setBarStyle:1];
2953 [navbar_ setDelegate:self];
2955 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2956 [navbar_ pushNavigationItem:navitem];
2958 CGRect bounds = [overlay_ bounds];
2959 CGSize prgsize = [UIProgressBar defaultSize];
2962 (bounds.size.width - prgsize.width) / 2,
2963 bounds.size.height - prgsize.height - 20
2966 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2967 [progress_ setStyle:0];
2969 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2971 bounds.size.height - prgsize.height - 50,
2972 bounds.size.width - 20,
2976 [status_ setColor:[UIColor whiteColor]];
2977 [status_ setBackgroundColor:[UIColor clearColor]];
2979 [status_ setCentersHorizontally:YES];
2980 //[status_ setFont:font];
2982 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2984 navrect.size.height + 20,
2985 bounds.size.width - 20,
2986 bounds.size.height - navsize.height - 62 - navrect.size.height
2989 //[output_ setTextFont:@"Courier New"];
2990 [output_ setTextSize:12];
2992 [output_ setTextColor:[UIColor whiteColor]];
2993 [output_ setBackgroundColor:[UIColor clearColor]];
2995 [output_ setMarginTop:0];
2996 [output_ setAllowsRubberBanding:YES];
2997 [output_ setEditable:NO];
2999 [overlay_ addSubview:output_];
3001 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3003 bounds.size.height - prgsize.height - 50,
3004 bounds.size.width - 20,
3008 [close_ setAutosizesToFit:NO];
3009 [close_ setDrawsShadow:YES];
3010 [close_ setStretchBackground:YES];
3011 [close_ setEnabled:YES];
3013 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3014 [close_ setTitleFont:bold];
3016 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3017 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3018 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3022 - (void) setContentView:(UIView *)view {
3023 view_ = [view retain];
3026 - (void) resetView {
3027 [transition_ transition:6 toView:view_];
3030 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3031 NSString *context = [sheet context];
3032 if ([context isEqualToString:@"conffile"]) {
3033 FILE *input = [database_ input];
3037 fprintf(input, "N\n");
3041 fprintf(input, "Y\n");
3052 - (void) closeButtonPushed {
3061 [delegate_ suspendWithAnimation:YES];
3065 system("launchctl stop com.apple.SpringBoard");
3069 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3078 - (void) _retachThread {
3079 UINavigationItem *item = [navbar_ topItem];
3080 [item setTitle:@"Complete"];
3082 [overlay_ addSubview:close_];
3083 [progress_ removeFromSuperview];
3084 [status_ removeFromSuperview];
3086 [delegate_ progressViewIsComplete:self];
3089 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3090 MMap mmap(file, MMap::ReadOnly);
3092 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3093 if (!(sandplate_ == sha1.Result()))
3098 FileFd file(SpringBoard_, FileFd::ReadOnly);
3099 MMap mmap(file, MMap::ReadOnly);
3101 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3102 if (!(springlist_ == sha1.Result()))
3107 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3108 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3109 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3110 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3111 case 4: [close_ setTitle:@"Reboot Device"]; break;
3114 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3116 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3117 [cache autorelease];
3119 NSFileManager *manager = [NSFileManager defaultManager];
3120 NSError *error = nil;
3122 id system = [cache objectForKey:@"System"];
3127 if (stat(Cache_, &info) == -1)
3130 [system removeAllObjects];
3132 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3133 for (NSString *app in apps)
3134 if ([app hasSuffix:@".app"]) {
3135 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3136 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3137 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3139 [info setObject:path forKey:@"Path"];
3140 [info setObject:@"System" forKey:@"ApplicationType"];
3141 [system addInfoDictionary:info];
3146 [cache writeToFile:@Cache_ atomically:YES];
3148 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3150 if (chmod(Cache_, info.st_mode) == -1)
3154 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3157 notify_post("com.apple.mobile.application_installed");
3159 [delegate_ setStatusBarShowsProgress:NO];
3162 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3163 [[data target] performSelector:[data selector] withObject:[data object]];
3166 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3169 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3170 UINavigationItem *item = [navbar_ topItem];
3171 [item setTitle:title];
3173 [status_ setText:nil];
3174 [output_ setText:@""];
3175 [progress_ setProgress:0];
3178 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3180 [close_ removeFromSuperview];
3181 [overlay_ addSubview:progress_];
3182 [overlay_ addSubview:status_];
3184 [delegate_ setStatusBarShowsProgress:YES];
3188 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3189 MMap mmap(file, MMap::ReadOnly);
3191 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3192 sandplate_ = sha1.Result();
3196 FileFd file(SpringBoard_, FileFd::ReadOnly);
3197 MMap mmap(file, MMap::ReadOnly);
3199 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3200 springlist_ = sha1.Result();
3203 [transition_ transition:6 toView:overlay_];
3206 detachNewThreadSelector:@selector(_detachNewThreadData:)
3208 withObject:[[ProgressData alloc]
3209 initWithSelector:selector
3216 - (void) repairWithSelector:(SEL)selector {
3218 detachNewThreadSelector:selector
3225 - (void) setConfigurationData:(NSString *)data {
3227 performSelectorOnMainThread:@selector(_setConfigurationData:)
3233 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3234 Package *package = id == nil ? nil : [database_ packageWithName:id];
3236 UIActionSheet *sheet = [[[UIActionSheet alloc]
3237 initWithTitle:(package == nil ? id : [package name])
3238 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3239 defaultButtonIndex:0
3244 [sheet setBodyText:error];
3245 [sheet popupAlertAnimated:YES];
3248 - (void) setProgressTitle:(NSString *)title {
3250 performSelectorOnMainThread:@selector(_setProgressTitle:)
3256 - (void) setProgressPercent:(float)percent {
3258 performSelectorOnMainThread:@selector(_setProgressPercent:)
3259 withObject:[NSNumber numberWithFloat:percent]
3264 - (void) startProgress {
3265 last_ = [NSDate timeIntervalSinceReferenceDate];
3268 - (void) addProgressOutput:(NSString *)output {
3270 performSelectorOnMainThread:@selector(_addProgressOutput:)
3276 - (bool) isCancelling:(size_t)received {
3278 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3279 if (received_ != received) {
3280 received_ = received;
3282 } else if (now - last_ > 30)
3289 - (void) _setConfigurationData:(NSString *)data {
3290 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3292 _assert(conffile_r(data));
3294 NSString *ofile = conffile_r[1];
3295 //NSString *nfile = conffile_r[2];
3297 UIActionSheet *sheet = [[[UIActionSheet alloc]
3298 initWithTitle:@"Configuration Upgrade"
3299 buttons:[NSArray arrayWithObjects:
3300 @"Keep My Old Copy",
3301 @"Accept The New Copy",
3302 // XXX: @"See What Changed",
3304 defaultButtonIndex:0
3309 [sheet setBodyText:[NSString stringWithFormat:
3310 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3313 [sheet popupAlertAnimated:YES];
3316 - (void) _setProgressTitle:(NSString *)title {
3317 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3318 for (size_t i(0), e([words count]); i != e; ++i) {
3319 NSString *word([words objectAtIndex:i]);
3320 if (Package *package = [database_ packageWithName:word])
3321 [words replaceObjectAtIndex:i withObject:[package name]];
3324 [status_ setText:[words componentsJoinedByString:@" "]];
3327 - (void) _setProgressPercent:(NSNumber *)percent {
3328 [progress_ setProgress:[percent floatValue]];
3331 - (void) _addProgressOutput:(NSString *)output {
3332 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3333 CGSize size = [output_ contentSize];
3334 CGRect rect = {{0, size.height}, {size.width, 0}};
3335 [output_ scrollRectToVisible:rect animated:YES];
3338 - (BOOL) isRunning {
3345 /* Package Cell {{{ */
3346 @interface PackageCell : UISimpleTableCell {
3349 NSString *description_;
3353 UITextLabel *status_;
3357 - (PackageCell *) init;
3358 - (void) setPackage:(Package *)package;
3360 + (int) heightForPackage:(Package *)package;
3364 @implementation PackageCell
3366 - (void) clearPackage {
3377 if (description_ != nil) {
3378 [description_ release];
3382 if (source_ != nil) {
3387 if (badge_ != nil) {
3394 [self clearPackage];
3401 - (PackageCell *) init {
3402 if ((self = [super init]) != nil) {
3404 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3405 [status_ setBackgroundColor:[UIColor clearColor]];
3406 [status_ setFont:small];
3411 - (void) setPackage:(Package *)package {
3412 [self clearPackage];
3414 Source *source = [package source];
3415 NSString *section = [package simpleSection];
3417 icon_ = [[package icon] retain];
3419 name_ = [[package name] retain];
3420 description_ = [[package tagline] retain];
3422 NSString *label = nil;
3423 bool trusted = false;
3425 if (source != nil) {
3426 label = [source label];
3427 trusted = [source trusted];
3428 } else if ([[package id] isEqualToString:@"firmware"])
3431 label = @"Unknown/Local";
3433 NSString *from = [NSString stringWithFormat:@"from %@", label];
3435 if (section != nil && ![section isEqualToString:label])
3436 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3438 source_ = [from retain];
3440 if (NSString *purpose = [package primaryPurpose])
3441 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3442 badge_ = [badge_ retain];
3445 if (NSString *mode = [package mode]) {
3446 [badge_ setImage:[UIImage applicationImageNamed:
3447 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3450 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3451 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3452 } else if ([package half]) {
3453 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3454 [status_ setText:@"Package Damaged"];
3455 [status_ setColor:[UIColor redColor]];
3457 [badge_ setImage:nil];
3458 [status_ setText:nil];
3463 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3466 rect.size = [icon_ size];
3468 rect.size.width /= 2;
3469 rect.size.height /= 2;
3471 rect.origin.x = 25 - rect.size.width / 2;
3472 rect.origin.y = 25 - rect.size.height / 2;
3474 [icon_ drawInRect:rect];
3477 if (badge_ != nil) {
3478 CGSize size = [badge_ size];
3480 [badge_ drawAtPoint:CGPointMake(
3481 36 - size.width / 2,
3482 36 - size.height / 2
3491 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3492 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3496 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3498 [super drawContentInRect:rect selected:selected];
3501 + (int) heightForPackage:(Package *)package {
3502 NSString *tagline([package tagline]);
3503 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3505 if ([package hasMode] || [package half])
3514 /* Section Cell {{{ */
3515 @interface SectionCell : UISimpleTableCell {
3520 _UISwitchSlider *switch_;
3525 - (void) setSection:(Section *)section editing:(BOOL)editing;
3529 @implementation SectionCell
3531 - (void) clearSection {
3532 if (section_ != nil) {
3542 if (count_ != nil) {
3549 [self clearSection];
3556 if ((self = [super init]) != nil) {
3557 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3559 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3560 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3564 - (void) onSwitch:(id)sender {
3565 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3566 if (metadata == nil) {
3567 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3568 [Sections_ setObject:metadata forKey:section_];
3572 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3575 - (void) setSection:(Section *)section editing:(BOOL)editing {
3576 if (editing != editing_) {
3578 [switch_ removeFromSuperview];
3580 [self addSubview:switch_];
3584 [self clearSection];
3586 if (section == nil) {
3587 name_ = [@"All Packages" retain];
3590 section_ = [section name];
3591 if (section_ != nil)
3592 section_ = [section_ retain];
3593 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3594 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3597 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3601 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3602 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3609 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3611 CGSize size = [count_ sizeWithFont:Font14_];
3615 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3617 [super drawContentInRect:rect selected:selected];
3623 /* File Table {{{ */
3624 @interface FileTable : RVPage {
3625 _transient Database *database_;
3628 NSMutableArray *files_;
3632 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3633 - (void) setPackage:(Package *)package;
3637 @implementation FileTable
3640 if (package_ != nil)
3649 - (int) numberOfRowsInTable:(UITable *)table {
3650 return files_ == nil ? 0 : [files_ count];
3653 - (float) table:(UITable *)table heightForRow:(int)row {
3657 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3658 if (reusing == nil) {
3659 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3660 UIFont *font = [UIFont systemFontOfSize:16];
3661 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3663 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3667 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3671 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3672 if ((self = [super initWithBook:book]) != nil) {
3673 database_ = database;
3675 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3677 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3678 [self addSubview:list_];
3680 UITableColumn *column = [[[UITableColumn alloc]
3681 initWithTitle:@"Name"
3683 width:[self frame].size.width
3686 [list_ setDataSource:self];
3687 [list_ setSeparatorStyle:1];
3688 [list_ addTableColumn:column];
3689 [list_ setDelegate:self];
3690 [list_ setReusesTableCells:YES];
3694 - (void) setPackage:(Package *)package {
3695 if (package_ != nil) {
3696 [package_ autorelease];
3705 [files_ removeAllObjects];
3707 if (package != nil) {
3708 package_ = [package retain];
3709 name_ = [[package id] retain];
3711 if (NSArray *files = [package files])
3712 [files_ addObjectsFromArray:files];
3714 if ([files_ count] != 0) {
3715 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3716 [files_ removeObjectAtIndex:0];
3717 [files_ sortUsingSelector:@selector(compareByPath:)];
3719 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3720 [stack addObject:@"/"];
3722 for (int i(0), e([files_ count]); i != e; ++i) {
3723 NSString *file = [files_ objectAtIndex:i];
3724 while (![file hasPrefix:[stack lastObject]])
3725 [stack removeLastObject];
3726 NSString *directory = [stack lastObject];
3727 [stack addObject:[file stringByAppendingString:@"/"]];
3728 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3729 ([stack count] - 2) * 3, "",
3730 [file substringFromIndex:[directory length]]
3739 - (void) resetViewAnimated:(BOOL)animated {
3740 [list_ resetViewAnimated:animated];
3743 - (void) reloadData {
3744 [self setPackage:[database_ packageWithName:name_]];
3745 [self reloadButtons];
3748 - (NSString *) title {
3749 return @"Installed Files";
3752 - (NSString *) backButtonTitle {
3758 /* Package View {{{ */
3759 @interface PackageView : BrowserView {
3760 _transient Database *database_;
3763 NSMutableArray *buttons_;
3766 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3767 - (void) setPackage:(Package *)package;
3771 @implementation PackageView
3774 if (package_ != nil)
3782 - (void) _clickButtonWithName:(NSString *)name {
3783 if ([name isEqualToString:@"Install"])
3784 [delegate_ installPackage:package_];
3785 else if ([name isEqualToString:@"Reinstall"])
3786 [delegate_ installPackage:package_];
3787 else if ([name isEqualToString:@"Remove"])
3788 [delegate_ removePackage:package_];
3789 else if ([name isEqualToString:@"Upgrade"])
3790 [delegate_ installPackage:package_];
3791 else _assert(false);
3794 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3795 int count = [buttons_ count];
3796 _assert(count != 0);
3797 _assert(button <= count + 1);
3799 if (count != button - 1)
3800 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3805 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3806 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3807 return [super webView:sender didFinishLoadForFrame:frame];
3810 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3811 [window setValue:package_ forKey:@"package"];
3812 [super webView:sender didClearWindowObject:window forFrame:frame];
3816 - (void) _rightButtonClicked {
3817 /*[super _rightButtonClicked];
3820 int count = [buttons_ count];
3821 _assert(count != 0);
3824 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3826 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3827 [buttons addObjectsFromArray:buttons_];
3828 [buttons addObject:@"Cancel"];
3830 [delegate_ slideUp:[[[UIActionSheet alloc]
3833 defaultButtonIndex:2
3841 - (NSString *) _rightButtonTitle {
3842 int count = [buttons_ count];
3843 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3846 - (NSString *) backButtonTitle {
3850 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3851 if ((self = [super initWithBook:book]) != nil) {
3852 database_ = database;
3853 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3857 - (void) setPackage:(Package *)package {
3858 if (package_ != nil) {
3859 [package_ autorelease];
3868 [buttons_ removeAllObjects];
3870 if (package != nil) {
3871 package_ = [package retain];
3872 name_ = [[package id] retain];
3874 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3876 if ([package_ source] == nil);
3877 else if ([package_ upgradableAndEssential:NO])
3878 [buttons_ addObject:@"Upgrade"];
3879 else if ([package_ installed] == nil)
3880 [buttons_ addObject:@"Install"];
3882 [buttons_ addObject:@"Reinstall"];
3883 if ([package_ installed] != nil)
3884 [buttons_ addObject:@"Remove"];
3888 - (void) reloadData {
3889 [self setPackage:[database_ packageWithName:name_]];
3890 [self reloadButtons];
3895 /* Package Table {{{ */
3896 @interface PackageTable : RVPage {
3897 _transient Database *database_;
3901 NSMutableArray *packages_;
3902 NSMutableArray *sections_;
3903 UISectionList *list_;
3906 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3908 - (void) setDelegate:(id)delegate;
3909 - (void) setObject:(id)object;
3911 - (void) reloadData;
3912 - (void) resetCursor;
3914 - (UISectionList *) list;
3916 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3920 @implementation PackageTable
3923 [list_ setDataSource:nil];
3928 [packages_ release];
3929 [sections_ release];
3934 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3935 return [sections_ count];
3938 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3939 return [[sections_ objectAtIndex:section] name];
3942 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3943 return [[sections_ objectAtIndex:section] row];
3946 - (int) numberOfRowsInTable:(UITable *)table {
3947 return [packages_ count];
3950 - (float) table:(UITable *)table heightForRow:(int)row {
3951 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3954 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3956 reusing = [[[PackageCell alloc] init] autorelease];
3957 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3961 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3965 - (void) tableRowSelected:(NSNotification *)notification {
3966 int row = [[notification object] selectedRow];
3970 Package *package = [packages_ objectAtIndex:row];
3971 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3972 [view setDelegate:delegate_];
3973 [view setPackage:package];
3974 [book_ pushPage:view];
3977 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3978 if ((self = [super initWithBook:book]) != nil) {
3979 database_ = database;
3980 title_ = [title retain];
3982 object_ = object == nil ? nil : [object retain];
3984 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3985 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3987 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3988 [list_ setDataSource:self];
3990 UITableColumn *column = [[[UITableColumn alloc]
3991 initWithTitle:@"Name"
3993 width:[self frame].size.width
3996 UITable *table = [list_ table];
3997 [table setSeparatorStyle:1];
3998 [table addTableColumn:column];
3999 [table setDelegate:self];
4000 [table setReusesTableCells:YES];
4002 [self addSubview:list_];
4005 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4006 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4010 - (void) setDelegate:(id)delegate {
4011 delegate_ = delegate;
4014 - (void) setObject:(id)object {
4020 object_ = [object retain];
4023 - (void) reloadData {
4024 NSArray *packages = [database_ packages];
4026 [packages_ removeAllObjects];
4027 [sections_ removeAllObjects];
4029 for (size_t i(0); i != [packages count]; ++i) {
4030 Package *package([packages objectAtIndex:i]);
4031 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4032 [packages_ addObject:package];
4035 Section *section = nil;
4037 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4038 Package *package = [packages_ objectAtIndex:offset];
4039 NSString *name = [package index];
4041 if (section == nil || ![[section name] isEqualToString:name]) {
4042 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4043 [sections_ addObject:section];
4046 [section addToCount];
4052 - (NSString *) title {
4056 - (void) resetViewAnimated:(BOOL)animated {
4057 [list_ resetViewAnimated:animated];
4060 - (void) resetCursor {
4061 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4064 - (UISectionList *) list {
4068 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4069 [list_ setShouldHideHeaderInShortLists:hide];
4075 /* Add Source View {{{ */
4076 @interface AddSourceView : RVPage {
4077 _transient Database *database_;
4080 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4084 @implementation AddSourceView
4086 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4087 if ((self = [super initWithBook:book]) != nil) {
4088 database_ = database;
4094 /* Source Cell {{{ */
4095 @interface SourceCell : UITableCell {
4098 NSString *description_;
4104 - (SourceCell *) initWithSource:(Source *)source;
4108 @implementation SourceCell
4113 [description_ release];
4118 - (SourceCell *) initWithSource:(Source *)source {
4119 if ((self = [super init]) != nil) {
4121 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4123 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4124 icon_ = [icon_ retain];
4126 origin_ = [[source name] retain];
4127 label_ = [[source uri] retain];
4128 description_ = [[source description] retain];
4132 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4134 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4141 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4145 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4149 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4151 [super drawContentInRect:rect selected:selected];
4156 /* Source Table {{{ */
4157 @interface SourceTable : RVPage {
4158 _transient Database *database_;
4159 UISectionList *list_;
4160 NSMutableArray *sources_;
4161 UIActionSheet *alert_;
4165 UIProgressHUD *hud_;
4168 //NSURLConnection *installer_;
4169 NSURLConnection *trivial_bz2_;
4170 NSURLConnection *trivial_gz_;
4171 //NSURLConnection *automatic_;
4176 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4180 @implementation SourceTable
4182 - (void) _deallocConnection:(NSURLConnection *)connection {
4183 if (connection != nil) {
4184 [connection cancel];
4185 //[connection setDelegate:nil];
4186 [connection release];
4191 [[list_ table] setDelegate:nil];
4192 [list_ setDataSource:nil];
4201 //[self _deallocConnection:installer_];
4202 [self _deallocConnection:trivial_gz_];
4203 [self _deallocConnection:trivial_bz2_];
4204 //[self _deallocConnection:automatic_];
4211 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4212 return offset_ == 0 ? 1 : 2;
4215 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4216 switch (section + (offset_ == 0 ? 1 : 0)) {
4217 case 0: return @"Entered by User";
4218 case 1: return @"Installed by Packages";
4226 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4227 switch (section + (offset_ == 0 ? 1 : 0)) {
4229 case 1: return offset_;
4237 - (int) numberOfRowsInTable:(UITable *)table {
4238 return [sources_ count];
4241 - (float) table:(UITable *)table heightForRow:(int)row {
4242 Source *source = [sources_ objectAtIndex:row];
4243 return [source description] == nil ? 56 : 73;
4246 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4247 Source *source = [sources_ objectAtIndex:row];
4248 // XXX: weird warning, stupid selectors ;P
4249 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4252 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4256 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4260 - (void) tableRowSelected:(NSNotification*)notification {
4261 UITable *table([list_ table]);
4262 int row([table selectedRow]);
4266 Source *source = [sources_ objectAtIndex:row];
4268 PackageTable *packages = [[[PackageTable alloc]
4271 title:[source label]
4272 filter:@selector(isVisibleInSource:)
4276 [packages setDelegate:delegate_];
4278 [book_ pushPage:packages];
4281 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4282 Source *source = [sources_ objectAtIndex:row];
4283 return [source record] != nil;
4286 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4287 [[list_ table] setDeleteConfirmationRow:row];
4290 - (void) table:(UITable *)table deleteRow:(int)row {
4291 Source *source = [sources_ objectAtIndex:row];
4292 [Sources_ removeObjectForKey:[source key]];
4293 [delegate_ syncData];
4296 - (void) _endConnection:(NSURLConnection *)connection {
4297 NSURLConnection **field = NULL;
4298 if (connection == trivial_bz2_)
4299 field = &trivial_bz2_;
4300 else if (connection == trivial_gz_)
4301 field = &trivial_gz_;
4302 _assert(field != NULL);
4303 [connection release];
4307 trivial_bz2_ == nil &&
4310 [delegate_ setStatusBarShowsProgress:NO];
4313 [hud_ removeFromSuperview];
4318 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4321 @"./", @"Distribution",
4322 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4324 [delegate_ syncData];
4325 } else if (error_ != nil) {
4326 UIActionSheet *sheet = [[[UIActionSheet alloc]
4327 initWithTitle:@"Verification Error"
4328 buttons:[NSArray arrayWithObjects:@"OK", nil]
4329 defaultButtonIndex:0
4334 [sheet setBodyText:[error_ localizedDescription]];
4335 [sheet popupAlertAnimated:YES];
4337 UIActionSheet *sheet = [[[UIActionSheet alloc]
4338 initWithTitle:@"Did not Find Repository"
4339 buttons:[NSArray arrayWithObjects:@"OK", nil]
4340 defaultButtonIndex:0
4345 [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."];
4346 [sheet popupAlertAnimated:YES];
4352 if (error_ != nil) {
4359 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4360 switch ([response statusCode]) {
4366 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4367 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4369 error_ = [error retain];
4370 [self _endConnection:connection];
4373 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4374 [self _endConnection:connection];
4377 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4378 NSMutableURLRequest *request = [NSMutableURLRequest
4379 requestWithURL:[NSURL URLWithString:href]
4380 cachePolicy:NSURLRequestUseProtocolCachePolicy
4381 timeoutInterval:20.0
4384 [request setHTTPMethod:method];
4386 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4389 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4390 NSString *context = [sheet context];
4391 if ([context isEqualToString:@"source"])
4394 NSString *href = [[sheet textField] text];
4396 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4398 if (![href hasSuffix:@"/"])
4399 href_ = [href stringByAppendingString:@"/"];
4402 href_ = [href_ retain];
4404 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4405 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4406 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4410 hud_ = [delegate_ addProgressHUD];
4411 [hud_ setText:@"Verifying URL"];
4424 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4425 if ((self = [super initWithBook:book]) != nil) {
4426 database_ = database;
4427 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4429 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4430 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4431 [list_ setShouldHideHeaderInShortLists:NO];
4433 [self addSubview:list_];
4434 [list_ setDataSource:self];
4436 UITableColumn *column = [[UITableColumn alloc]
4437 initWithTitle:@"Name"
4439 width:[self frame].size.width
4442 UITable *table = [list_ table];
4443 [table setSeparatorStyle:1];
4444 [table addTableColumn:column];
4445 [table setDelegate:self];
4449 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4450 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4454 - (void) reloadData {
4456 _assert(list.ReadMainList());
4458 [sources_ removeAllObjects];
4459 [sources_ addObjectsFromArray:[database_ sources]];
4461 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4464 int count = [sources_ count];
4465 for (offset_ = 0; offset_ != count; ++offset_) {
4466 Source *source = [sources_ objectAtIndex:offset_];
4467 if ([source record] == nil)
4474 - (void) resetViewAnimated:(BOOL)animated {
4475 [list_ resetViewAnimated:animated];
4478 - (void) _leftButtonClicked {
4479 /*[book_ pushPage:[[[AddSourceView alloc]
4484 UIActionSheet *sheet = [[[UIActionSheet alloc]
4485 initWithTitle:@"Enter Cydia/APT URL"
4486 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4487 defaultButtonIndex:0
4492 [sheet addTextFieldWithValue:@"http://" label:@""];
4494 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4495 [traits setAutocapitalizationType:0];
4496 [traits setKeyboardType:3];
4497 [traits setAutocorrectionType:1];
4499 [sheet popupAlertAnimated:YES];
4502 - (void) _rightButtonClicked {
4503 UITable *table = [list_ table];
4504 BOOL editing = [table isRowDeletionEnabled];
4505 [table enableRowDeletion:!editing animated:YES];
4506 [book_ reloadButtonsForPage:self];
4509 - (NSString *) title {
4513 - (NSString *) leftButtonTitle {
4514 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4517 - (NSString *) rightButtonTitle {
4518 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4521 - (UINavigationButtonStyle) rightButtonStyle {
4522 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4528 /* Installed View {{{ */
4529 @interface InstalledView : RVPage {
4530 _transient Database *database_;
4531 PackageTable *packages_;
4535 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4539 @implementation InstalledView
4542 [packages_ release];
4546 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4547 if ((self = [super initWithBook:book]) != nil) {
4548 database_ = database;
4550 packages_ = [[PackageTable alloc]
4554 filter:@selector(isInstalledAndVisible:)
4555 with:[NSNumber numberWithBool:YES]
4558 [self addSubview:packages_];
4560 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4561 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4565 - (void) resetViewAnimated:(BOOL)animated {
4566 [packages_ resetViewAnimated:animated];
4569 - (void) reloadData {
4570 [packages_ reloadData];
4573 - (void) _rightButtonClicked {
4574 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4575 [packages_ reloadData];
4577 [book_ reloadButtonsForPage:self];
4580 - (NSString *) title {
4581 return @"Installed";
4584 - (NSString *) backButtonTitle {
4588 - (NSString *) rightButtonTitle {
4589 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4592 - (UINavigationButtonStyle) rightButtonStyle {
4593 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4596 - (void) setDelegate:(id)delegate {
4597 [super setDelegate:delegate];
4598 [packages_ setDelegate:delegate];
4605 @interface HomeView : BrowserView {
4610 @implementation HomeView
4612 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4616 - (void) _leftButtonClicked {
4617 UIActionSheet *sheet = [[[UIActionSheet alloc]
4618 initWithTitle:@"About Cydia Installer"
4619 buttons:[NSArray arrayWithObjects:@"Close", nil]
4620 defaultButtonIndex:0
4626 @"Copyright (C) 2008\n"
4627 "Jay Freeman (saurik)\n"
4628 "saurik@saurik.com\n"
4629 "http://www.saurik.com/\n"
4632 "http://www.theokorigroup.com/\n"
4634 "College of Creative Studies,\n"
4635 "University of California,\n"
4637 "http://www.ccs.ucsb.edu/"
4640 [sheet popupAlertAnimated:YES];
4643 - (NSString *) leftButtonTitle {
4649 /* Manage View {{{ */
4650 @interface ManageView : BrowserView {
4655 @implementation ManageView
4657 - (NSString *) title {
4661 - (void) _leftButtonClicked {
4662 [delegate_ askForSettings];
4665 - (NSString *) leftButtonTitle {
4669 - (NSString *) _rightButtonTitle {
4676 @interface WebView (Cydia)
4677 - (void) setScriptDebugDelegate:(id)delegate;
4678 - (void) _setFormDelegate:(id)delegate;
4679 - (void) _setUIKitDelegate:(id)delegate;
4680 - (void) setWebMailDelegate:(id)delegate;
4681 - (void) _setLayoutInterval:(float)interval;
4684 /* Indirect Delegate {{{ */
4685 @interface IndirectDelegate : NSProxy {
4686 _transient volatile id delegate_;
4689 - (void) setDelegate:(id)delegate;
4690 - (id) initWithDelegate:(id)delegate;
4693 @implementation IndirectDelegate
4695 - (void) setDelegate:(id)delegate {
4696 delegate_ = delegate;
4699 - (id) initWithDelegate:(id)delegate {
4700 delegate_ = delegate;
4704 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4705 if (delegate_ != nil)
4706 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4708 // XXX: I fucking hate Apple so very very bad
4709 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4712 - (void) forwardInvocation:(NSInvocation *)inv {
4713 SEL sel = [inv selector];
4714 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4715 [inv invokeWithTarget:delegate_];
4720 /* Browser Implementation {{{ */
4721 @implementation BrowserView
4724 WebView *webview = [webview_ webView];
4725 [webview setFrameLoadDelegate:nil];
4726 [webview setResourceLoadDelegate:nil];
4727 [webview setUIDelegate:nil];
4728 [webview setScriptDebugDelegate:nil];
4729 [webview setPolicyDelegate:nil];
4731 [webview setDownloadDelegate:nil];
4733 [webview _setFormDelegate:nil];
4734 [webview _setUIKitDelegate:nil];
4735 [webview setWebMailDelegate:nil];
4736 [webview setEditingDelegate:nil];
4738 [webview_ setDelegate:nil];
4739 [webview_ setGestureDelegate:nil];
4741 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4746 [webview_ removeFromSuperview];
4747 [Documents_ addObject:[webview_ autorelease]];
4752 [indirect_ setDelegate:nil];
4753 [indirect_ release];
4755 [scroller_ setDelegate:nil];
4757 [scroller_ release];
4759 [indicator_ release];
4765 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4766 [self loadRequest:[NSURLRequest
4769 timeoutInterval:30.0
4773 - (void) loadURL:(NSURL *)url {
4774 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4777 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4778 NSMutableURLRequest *copy = [request mutableCopy];
4780 if (Machine_ != NULL)
4781 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4782 if (UniqueID_ != nil)
4783 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4786 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4791 - (void) loadRequest:(NSURLRequest *)request {
4793 [webview_ loadRequest:request];
4796 - (void) reloadURL {
4797 if ([urls_ count] == 0)
4799 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4800 [urls_ removeLastObject];
4801 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4804 - (WebView *) webView {
4805 return [webview_ webView];
4808 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4809 [scroller_ setContentSize:frame.size];
4812 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4813 [self view:sender didSetFrame:frame];
4816 - (void) pushPage:(RVPage *)page {
4817 [self setBackButtonTitle:title_];
4818 [page setDelegate:delegate_];
4819 [book_ pushPage:page];
4822 - (BOOL) getSpecial:(NSURL *)url {
4823 NSString *href([url absoluteString]);
4824 NSString *scheme([[url scheme] lowercaseString]);
4828 if ([href hasPrefix:@"apptapp://package/"])
4829 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4830 else if ([scheme isEqualToString:@"cydia"]) {
4831 page = [delegate_ pageForURL:url hasTag:NULL];
4834 } else if (![scheme isEqualToString:@"apptapp"])
4838 [self pushPage:page];
4842 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4843 [window setValue:delegate_ forKey:@"cydia"];
4846 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4847 if (NSURL *url = [request URL]) {
4848 if (![self getSpecial:url]) {
4849 NSString *scheme([[url scheme] lowercaseString]);
4850 if ([scheme isEqualToString:@"mailto"])
4851 [delegate_ openMailToURL:url];
4860 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4861 if ([WebView canShowMIMEType:type])
4864 // XXX: handle more mime types!
4866 if (frame == [webView mainFrame])
4867 [UIApp openURL:[request URL]];
4871 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4872 NSURL *url([request URL]);
4874 if (url == nil) use: {
4879 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4882 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4883 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4886 [UIApp openURL:url];
4892 int store(_not(int));
4893 if (NSURL *itms = [url itmsURL:&store]) {
4894 NSLog(@"itms#%@#%u#%@", url, store, itms);
4896 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4897 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4904 NSString *scheme([[url scheme] lowercaseString]);
4906 if ([scheme isEqualToString:@"tel"]) {
4907 // XXX: intelligence
4911 if ([scheme isEqualToString:@"mailto"]) {
4912 [delegate_ openMailToURL:url];
4916 if ([self getSpecial:url])
4918 else if ([WebView _canHandleRequest:request])
4920 else if ([url isSpringboardHandledURL])
4926 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4927 //lprintf("Status:%s\n", [text UTF8String]);
4930 - (void) _pushPage {
4934 [book_ pushPage:self];
4937 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4938 NSURL *url = [request URL];
4939 if ([self getSpecial:url])
4942 return [self _addHeadersToRequest:request];
4945 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4946 [self setBackButtonTitle:title_];
4948 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4949 [browser setDelegate:delegate_];
4952 [browser loadRequest:[self _addHeadersToRequest:request]];
4953 [book_ pushPage:browser];
4956 return [browser webView];
4959 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4960 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4963 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4964 return [self _createWebViewWithRequest:request pushed:YES];
4967 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4968 if ([frame parentFrame] != nil)
4971 title_ = [title retain];
4972 [book_ reloadTitleForPage:self];
4975 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4976 if ([frame parentFrame] != nil)
4981 [indicator_ startAnimation];
4982 [self reloadButtons];
4984 if (title_ != nil) {
4989 [book_ reloadTitleForPage:self];
4991 WebView *webview = [webview_ webView];
4992 NSString *href = [webview mainFrameURL];
4993 [urls_ addObject:[NSURL URLWithString:href]];
4995 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4997 CGRect webrect = [scroller_ bounds];
4998 webrect.size.height = 0;
4999 [webview_ setFrame:webrect];
5002 - (void) _finishLoading {
5005 [indicator_ stopAnimation];
5006 [self reloadButtons];
5010 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5011 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5014 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5015 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5018 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5019 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5022 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5023 return [webview_ webView:sender didCommitLoadForFrame:frame];
5026 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5027 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5030 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5031 if ([frame parentFrame] == nil)
5032 [self _finishLoading];
5033 return [webview_ webView:sender didFinishLoadForFrame:frame];
5036 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5037 if ([frame parentFrame] != nil)
5039 [self _finishLoading];
5041 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5042 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5043 [[error localizedDescription] stringByAddingPercentEscapes]
5047 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5049 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5053 - (id) initWithBook:(RVBook *)book {
5054 if ((self = [super initWithBook:book]) != nil) {
5057 struct CGRect bounds = [self bounds];
5059 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5060 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5061 [self addSubview:pinstripe];
5063 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5064 [self addSubview:scroller_];
5066 [scroller_ setScrollingEnabled:YES];
5067 [scroller_ setAdjustForContentSizeChange:YES];
5068 [scroller_ setClipsSubviews:YES];
5069 [scroller_ setAllowsRubberBanding:YES];
5070 [scroller_ setScrollDecelerationFactor:0.99];
5071 [scroller_ setDelegate:self];
5073 CGRect webrect = [scroller_ bounds];
5074 webrect.size.height = 0;
5079 webview_ = [Documents_ lastObject];
5080 if (webview_ != nil) {
5081 webview_ = [webview_ retain];
5082 webview = [webview_ webView];
5083 [Documents_ removeLastObject];
5084 [webview_ setFrame:webrect];
5089 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5090 webview = [webview_ webView];
5092 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5094 [webview_ setAllowsMessaging:YES];
5096 [webview_ setTilingEnabled:YES];
5097 [webview_ setDrawsGrid:NO];
5098 [webview_ setLogsTilingChanges:NO];
5099 [webview_ setTileMinificationFilter:kCAFilterNearest];
5100 [webview_ setDetectsPhoneNumbers:NO];
5101 [webview_ setAutoresizes:YES];
5103 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5104 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5105 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5107 [webview_ _setDocumentType:0x4];
5109 [webview_ setZoomsFocusedFormControl:YES];
5110 [webview_ setContentsPosition:7];
5111 [webview_ setEnabledGestures:0xa];
5112 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
5113 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
5115 [webview_ setSmoothsFonts:YES];
5117 [webview _setUsesLoaderCache:YES];
5118 [webview setGroupName:@"Cydia"];
5119 //[webview _setLayoutInterval:0.5];
5122 [webview_ setDelegate:self];
5123 [webview_ setGestureDelegate:self];
5124 [scroller_ addSubview:webview_];
5126 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5128 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5129 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5130 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5132 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5133 NSString *application = package == nil ? @"Cydia" : [NSString
5134 stringWithFormat:@"Cydia/%@",
5136 ]; [webview setApplicationNameForUserAgent:application];
5138 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5140 [webview setFrameLoadDelegate:self];
5141 [webview setResourceLoadDelegate:indirect_];
5142 [webview setUIDelegate:self];
5143 [webview setScriptDebugDelegate:self];
5144 [webview setPolicyDelegate:self];
5146 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5148 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5149 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5150 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5154 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5155 [webview_ redrawScaledDocument];
5158 - (void) _rightButtonClicked {
5163 - (NSString *) _rightButtonTitle {
5167 - (NSString *) rightButtonTitle {
5168 return loading_ ? @"" : [self _rightButtonTitle];
5171 - (NSString *) title {
5172 return title_ == nil ? @"Loading" : title_;
5175 - (NSString *) backButtonTitle {
5179 - (void) setPageActive:(BOOL)active {
5181 [indicator_ removeFromSuperview];
5183 [[book_ navigationBar] addSubview:indicator_];
5186 - (void) resetViewAnimated:(BOOL)animated {
5189 - (void) setPushed:(bool)pushed {
5196 /* Cydia Book {{{ */
5197 @interface CYBook : RVBook <
5200 _transient Database *database_;
5201 UINavigationBar *overlay_;
5202 UINavigationBar *underlay_;
5203 UIProgressIndicator *indicator_;
5204 UITextLabel *prompt_;
5205 UIProgressBar *progress_;
5206 UINavigationButton *cancel_;
5209 NSTimeInterval last_;
5212 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5218 @implementation CYBook
5222 [indicator_ release];
5224 [progress_ release];
5229 - (NSString *) getTitleForPage:(RVPage *)page {
5230 return Simplify([super getTitleForPage:page]);
5238 [UIView beginAnimations:nil context:NULL];
5240 CGRect ovrframe = [overlay_ frame];
5241 ovrframe.origin.y = 0;
5242 [overlay_ setFrame:ovrframe];
5244 CGRect barframe = [navbar_ frame];
5245 barframe.origin.y += ovrframe.size.height;
5246 [navbar_ setFrame:barframe];
5248 CGRect trnframe = [transition_ frame];
5249 trnframe.origin.y += ovrframe.size.height;
5250 trnframe.size.height -= ovrframe.size.height;
5251 [transition_ setFrame:trnframe];
5253 [UIView endAnimations];
5255 [indicator_ startAnimation];
5256 [prompt_ setText:@"Updating Database"];
5257 [progress_ setProgress:0];
5260 last_ = [NSDate timeIntervalSinceReferenceDate];
5262 [overlay_ addSubview:cancel_];
5265 detachNewThreadSelector:@selector(_update)
5274 [indicator_ stopAnimation];
5276 [UIView beginAnimations:nil context:NULL];
5278 CGRect ovrframe = [overlay_ frame];
5279 ovrframe.origin.y = -ovrframe.size.height;
5280 [overlay_ setFrame:ovrframe];
5282 CGRect barframe = [navbar_ frame];
5283 barframe.origin.y -= ovrframe.size.height;
5284 [navbar_ setFrame:barframe];
5286 CGRect trnframe = [transition_ frame];
5287 trnframe.origin.y -= ovrframe.size.height;
5288 trnframe.size.height += ovrframe.size.height;
5289 [transition_ setFrame:trnframe];
5291 [UIView commitAnimations];
5293 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5296 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5297 if ((self = [super initWithFrame:frame]) != nil) {
5298 database_ = database;
5300 CGRect ovrrect = [navbar_ bounds];
5301 ovrrect.size.height = [UINavigationBar defaultSize].height;
5302 ovrrect.origin.y = -ovrrect.size.height;
5304 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5305 [self addSubview:overlay_];
5307 ovrrect.origin.y = frame.size.height;
5308 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5309 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5310 [self addSubview:underlay_];
5312 [overlay_ setBarStyle:1];
5313 [underlay_ setBarStyle:1];
5315 int barstyle = [overlay_ _barStyle:NO];
5316 bool ugly = barstyle == 0;
5318 UIProgressIndicatorStyle style = ugly ?
5319 UIProgressIndicatorStyleMediumBrown :
5320 UIProgressIndicatorStyleMediumWhite;
5322 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5323 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5324 CGRect indrect = {{indoffset, indoffset}, indsize};
5326 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5327 [indicator_ setStyle:style];
5328 [overlay_ addSubview:indicator_];
5330 CGSize prmsize = {215, indsize.height + 4};
5333 indoffset * 2 + indsize.width,
5337 unsigned(ovrrect.size.height - prmsize.height) / 2
5340 UIFont *font = [UIFont systemFontOfSize:15];
5342 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5344 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5345 [prompt_ setBackgroundColor:[UIColor clearColor]];
5346 [prompt_ setFont:font];
5348 [overlay_ addSubview:prompt_];
5350 CGSize prgsize = {75, 100};
5353 ovrrect.size.width - prgsize.width - 10,
5354 (ovrrect.size.height - prgsize.height) / 2
5357 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5358 [progress_ setStyle:0];
5359 [overlay_ addSubview:progress_];
5361 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5362 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5364 CGRect frame = [cancel_ frame];
5365 frame.size.width = 65;
5366 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5367 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5368 [cancel_ setFrame:frame];
5370 [cancel_ setBarStyle:barstyle];
5374 - (void) _onCancel {
5376 [cancel_ removeFromSuperview];
5379 - (void) _update { _pooled
5381 status.setDelegate(self);
5383 [database_ updateWithStatus:status];
5386 performSelectorOnMainThread:@selector(_update_)
5392 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5393 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5396 - (void) setProgressTitle:(NSString *)title {
5398 performSelectorOnMainThread:@selector(_setProgressTitle:)
5404 - (void) setProgressPercent:(float)percent {
5406 performSelectorOnMainThread:@selector(_setProgressPercent:)
5407 withObject:[NSNumber numberWithFloat:percent]
5412 - (void) startProgress {
5415 - (void) addProgressOutput:(NSString *)output {
5417 performSelectorOnMainThread:@selector(_addProgressOutput:)
5423 - (bool) isCancelling:(size_t)received {
5424 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5425 if (received_ != received) {
5426 received_ = received;
5428 } else if (now - last_ > 15)
5433 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5437 - (void) _setProgressTitle:(NSString *)title {
5438 [prompt_ setText:title];
5441 - (void) _setProgressPercent:(NSNumber *)percent {
5442 [progress_ setProgress:[percent floatValue]];
5445 - (void) _addProgressOutput:(NSString *)output {
5450 /* Cydia:// Protocol {{{ */
5451 @interface CydiaURLProtocol : NSURLProtocol {
5456 @implementation CydiaURLProtocol
5458 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5459 NSURL *url([request URL]);
5462 NSString *scheme([[url scheme] lowercaseString]);
5463 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5468 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5472 - (void) startLoading {
5473 id<NSURLProtocolClient> client([self client]);
5474 NSURLRequest *request([self request]);
5476 NSURL *url([request URL]);
5477 NSString *href([url absoluteString]);
5479 NSString *path([href substringFromIndex:8]);
5480 NSRange slash([path rangeOfString:@"/"]);
5483 if (slash.location == NSNotFound) {
5487 command = [path substringToIndex:slash.location];
5488 path = [path substringFromIndex:(slash.location + 1)];
5491 Database *database([Database sharedInstance]);
5493 if ([command isEqualToString:@"package-icon"]) {
5496 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5497 Package *package([database packageWithName:path]);
5501 UIImage *icon([package icon]);
5503 NSData *data(UIImagePNGRepresentation(icon));
5505 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5506 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5507 [client URLProtocol:self didLoadData:data];
5508 [client URLProtocolDidFinishLoading:self];
5509 } else if ([command isEqualToString:@"section-icon"]) {
5512 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5513 NSString *section(Simplify(path));
5515 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5517 icon = [UIImage applicationImageNamed:@"unknown.png"];
5519 NSData *data(UIImagePNGRepresentation(icon));
5521 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5522 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5523 [client URLProtocol:self didLoadData:data];
5524 [client URLProtocolDidFinishLoading:self];
5526 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5530 - (void) stopLoading {
5536 /* Install View {{{ */
5537 @interface InstallView : RVPage {
5538 _transient Database *database_;
5539 NSMutableArray *sections_;
5540 NSMutableArray *filtered_;
5541 UITransitionView *transition_;
5547 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5548 - (void) reloadData;
5553 @implementation InstallView
5556 [list_ setDataSource:nil];
5557 [list_ setDelegate:nil];
5559 [sections_ release];
5560 [filtered_ release];
5561 [transition_ release];
5563 [accessory_ release];
5567 - (int) numberOfRowsInTable:(UITable *)table {
5568 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5571 - (float) table:(UITable *)table heightForRow:(int)row {
5575 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5577 reusing = [[[SectionCell alloc] init] autorelease];
5578 [(SectionCell *)reusing setSection:(editing_ ?
5579 [sections_ objectAtIndex:row] :
5580 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5581 ) editing:editing_];
5585 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5589 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5593 - (void) tableRowSelected:(NSNotification *)notification {
5594 int row = [[notification object] selectedRow];
5605 title = @"All Packages";
5607 section = [filtered_ objectAtIndex:(row - 1)];
5608 name = [section name];
5614 title = @"(No Section)";
5618 PackageTable *table = [[[PackageTable alloc]
5622 filter:@selector(isVisiblyUninstalledInSection:)
5626 [table setDelegate:delegate_];
5628 [book_ pushPage:table];
5631 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5632 if ((self = [super initWithBook:book]) != nil) {
5633 database_ = database;
5635 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5636 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5638 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5639 [self addSubview:transition_];
5641 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5642 [transition_ transition:0 toView:list_];
5644 UITableColumn *column = [[[UITableColumn alloc]
5645 initWithTitle:@"Name"
5647 width:[self frame].size.width
5650 [list_ setDataSource:self];
5651 [list_ setSeparatorStyle:1];
5652 [list_ addTableColumn:column];
5653 [list_ setDelegate:self];
5654 [list_ setReusesTableCells:YES];
5658 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5659 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5663 - (void) reloadData {
5664 NSArray *packages = [database_ packages];
5666 [sections_ removeAllObjects];
5667 [filtered_ removeAllObjects];
5669 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5670 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5673 for (size_t i(0); i != [packages count]; ++i) {
5674 Package *package([packages objectAtIndex:i]);
5675 NSString *name([package section]);
5678 Section *section([sections objectForKey:name]);
5679 if (section == nil) {
5680 section = [[[Section alloc] initWithName:name] autorelease];
5681 [sections setObject:section forKey:name];
5685 if ([package valid] && [package installed] == nil && [package visible])
5686 [filtered addObject:package];
5690 [sections_ addObjectsFromArray:[sections allValues]];
5691 [sections_ sortUsingSelector:@selector(compareByName:)];
5694 [filtered sortUsingSelector:@selector(compareBySection:)];
5697 Section *section = nil;
5698 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5699 Package *package = [filtered objectAtIndex:offset];
5700 NSString *name = [package section];
5702 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5703 section = name == nil ?
5704 [[[Section alloc] initWithName:nil] autorelease] :
5705 [sections objectForKey:name];
5706 [filtered_ addObject:section];
5709 [section addToCount];
5717 - (void) resetView {
5719 [self _rightButtonClicked];
5722 - (void) resetViewAnimated:(BOOL)animated {
5723 [list_ resetViewAnimated:animated];
5726 - (void) _rightButtonClicked {
5727 if ((editing_ = !editing_))
5730 [delegate_ updateData];
5731 [book_ reloadTitleForPage:self];
5732 [book_ reloadButtonsForPage:self];
5735 - (NSString *) title {
5736 return editing_ ? @"Section Visibility" : @"Install by Section";
5739 - (NSString *) backButtonTitle {
5743 - (NSString *) rightButtonTitle {
5744 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5747 - (UINavigationButtonStyle) rightButtonStyle {
5748 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5751 - (UIView *) accessoryView {
5757 /* Changes View {{{ */
5758 @interface ChangesView : RVPage {
5759 _transient Database *database_;
5760 NSMutableArray *packages_;
5761 NSMutableArray *sections_;
5762 UISectionList *list_;
5766 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5767 - (void) reloadData;
5771 @implementation ChangesView
5774 [[list_ table] setDelegate:nil];
5775 [list_ setDataSource:nil];
5777 [packages_ release];
5778 [sections_ release];
5783 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5784 return [sections_ count];
5787 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5788 return [[sections_ objectAtIndex:section] name];
5791 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5792 return [[sections_ objectAtIndex:section] row];
5795 - (int) numberOfRowsInTable:(UITable *)table {
5796 return [packages_ count];
5799 - (float) table:(UITable *)table heightForRow:(int)row {
5800 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5803 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5805 reusing = [[[PackageCell alloc] init] autorelease];
5806 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5810 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5814 - (void) tableRowSelected:(NSNotification *)notification {
5815 int row = [[notification object] selectedRow];
5818 Package *package = [packages_ objectAtIndex:row];
5819 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5820 [view setDelegate:delegate_];
5821 [view setPackage:package];
5822 [book_ pushPage:view];
5825 - (void) _leftButtonClicked {
5826 [(CYBook *)book_ update];
5827 [self reloadButtons];
5830 - (void) _rightButtonClicked {
5831 [delegate_ distUpgrade];
5834 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5835 if ((self = [super initWithBook:book]) != nil) {
5836 database_ = database;
5838 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5839 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5841 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5842 [self addSubview:list_];
5844 [list_ setShouldHideHeaderInShortLists:NO];
5845 [list_ setDataSource:self];
5846 //[list_ setSectionListStyle:1];
5848 UITableColumn *column = [[[UITableColumn alloc]
5849 initWithTitle:@"Name"
5851 width:[self frame].size.width
5854 UITable *table = [list_ table];
5855 [table setSeparatorStyle:1];
5856 [table addTableColumn:column];
5857 [table setDelegate:self];
5858 [table setReusesTableCells:YES];
5862 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5863 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5867 - (void) reloadData {
5868 NSArray *packages = [database_ packages];
5870 [packages_ removeAllObjects];
5871 [sections_ removeAllObjects];
5874 for (size_t i(0); i != [packages count]; ++i) {
5875 Package *package([packages objectAtIndex:i]);
5878 [package installed] == nil && [package valid] && [package visible] ||
5879 [package upgradableAndEssential:NO]
5881 [packages_ addObject:package];
5885 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5888 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5889 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5890 Section *section = nil;
5894 bool unseens = false;
5896 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5899 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5900 Package *package = [packages_ objectAtIndex:offset];
5902 if (![package upgradableAndEssential:YES]) {
5904 NSDate *seen = [package seen];
5906 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5909 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5910 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5911 [sections_ addObject:section];
5915 [section addToCount];
5916 } else if ([package ignored])
5917 [ignored addToCount];
5920 [upgradable addToCount];
5925 CFRelease(formatter);
5928 Section *last = [sections_ lastObject];
5929 size_t count = [last count];
5930 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5931 [sections_ removeLastObject];
5934 if ([ignored count] != 0)
5935 [sections_ insertObject:ignored atIndex:0];
5937 [sections_ insertObject:upgradable atIndex:0];
5940 [self reloadButtons];
5943 - (void) resetViewAnimated:(BOOL)animated {
5944 [list_ resetViewAnimated:animated];
5947 - (NSString *) leftButtonTitle {
5948 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5951 - (NSString *) rightButtonTitle {
5952 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5955 - (NSString *) title {
5961 /* Search View {{{ */
5962 @protocol SearchViewDelegate
5963 - (void) showKeyboard:(BOOL)show;
5966 @interface SearchView : RVPage {
5968 UISearchField *field_;
5969 UITransitionView *transition_;
5970 PackageTable *table_;
5971 UIPreferencesTable *advanced_;
5977 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5978 - (void) reloadData;
5982 @implementation SearchView
5985 [field_ setDelegate:nil];
5987 [accessory_ release];
5989 [transition_ release];
5991 [advanced_ release];
5996 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6000 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6002 case 0: return @"Advanced Search (Coming Soon!)";
6004 default: _assert(false);
6008 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6012 default: _assert(false);
6016 - (void) _showKeyboard:(BOOL)show {
6017 CGSize keysize = [UIKeyboard defaultSize];
6018 CGRect keydown = [book_ pageBounds];
6019 CGRect keyup = keydown;
6020 keyup.size.height -= keysize.height - ButtonBarHeight_;
6022 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6024 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6025 [animation setSignificantRectFields:8];
6028 [animation setStartFrame:keydown];
6029 [animation setEndFrame:keyup];
6031 [animation setStartFrame:keyup];
6032 [animation setEndFrame:keydown];
6035 UIAnimator *animator = [UIAnimator sharedAnimator];
6038 addAnimations:[NSArray arrayWithObjects:animation, nil]
6039 withDuration:(KeyboardTime_ - delay)
6044 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6046 [delegate_ showKeyboard:show];
6049 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6050 [self _showKeyboard:YES];
6053 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6054 [self _showKeyboard:NO];
6057 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6059 NSString *text([field_ text]);
6060 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6066 - (void) textFieldClearButtonPressed:(UITextField *)field {
6070 - (void) keyboardInputShouldDelete:(id)input {
6074 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6075 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6079 [field_ resignFirstResponder];
6084 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6085 if ((self = [super initWithBook:book]) != nil) {
6086 CGRect pageBounds = [book_ pageBounds];
6088 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6089 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6090 [self addSubview:pinstripe];*/
6092 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6093 [self addSubview:transition_];
6095 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6097 [advanced_ setReusesTableCells:YES];
6098 [advanced_ setDataSource:self];
6099 [advanced_ reloadData];
6101 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6102 CGColor dimmed(space_, 0, 0, 0, 0.5);
6103 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6105 table_ = [[PackageTable alloc]
6109 filter:@selector(isUnfilteredAndSearchedForBy:)
6113 [table_ setShouldHideHeaderInShortLists:NO];
6114 [transition_ transition:0 toView:table_];
6123 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6130 [self bounds].size.width - area.origin.x - 18;
6132 area.size.height = [UISearchField defaultHeight];
6134 field_ = [[UISearchField alloc] initWithFrame:area];
6136 UIFont *font = [UIFont systemFontOfSize:16];
6137 [field_ setFont:font];
6139 [field_ setPlaceholder:@"Package Names & Descriptions"];
6140 [field_ setDelegate:self];
6142 [field_ setPaddingTop:5];
6144 UITextInputTraits *traits = [field_ textInputTraits];
6145 [traits setAutocapitalizationType:0];
6146 [traits setAutocorrectionType:1];
6147 [traits setReturnKeyType:6];
6149 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6151 accessory_ = [[UIView alloc] initWithFrame:accrect];
6152 [accessory_ addSubview:field_];
6154 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6155 [configure setShowPressFeedback:YES];
6156 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6157 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6158 [accessory_ addSubview:configure];*/
6160 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6161 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6167 LKAnimation *animation = [LKTransition animation];
6168 [animation setType:@"oglFlip"];
6169 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6170 [animation setFillMode:@"extended"];
6171 [animation setTransitionFlags:3];
6172 [animation setDuration:10];
6173 [animation setSpeed:0.35];
6174 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6175 [[transition_ _layer] addAnimation:animation forKey:0];
6176 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6177 flipped_ = !flipped_;
6181 - (void) configurePushed {
6182 [field_ resignFirstResponder];
6186 - (void) resetViewAnimated:(BOOL)animated {
6189 [table_ resetViewAnimated:animated];
6192 - (void) _reloadData {
6195 - (void) reloadData {
6198 [table_ setObject:[field_ text]];
6199 [table_ reloadData];
6200 [table_ resetCursor];
6203 - (UIView *) accessoryView {
6207 - (NSString *) title {
6211 - (NSString *) backButtonTitle {
6215 - (void) setDelegate:(id)delegate {
6216 [table_ setDelegate:delegate];
6217 [super setDelegate:delegate];
6223 @interface SettingsView : RVPage {
6224 _transient Database *database_;
6227 UIPreferencesTable *table_;
6228 _UISwitchSlider *subscribedSwitch_;
6229 _UISwitchSlider *ignoredSwitch_;
6230 UIPreferencesControlTableCell *subscribedCell_;
6231 UIPreferencesControlTableCell *ignoredCell_;
6234 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6238 @implementation SettingsView
6241 [table_ setDataSource:nil];
6244 if (package_ != nil)
6247 [subscribedSwitch_ release];
6248 [ignoredSwitch_ release];
6249 [subscribedCell_ release];
6250 [ignoredCell_ release];
6254 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6255 if (package_ == nil)
6261 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6262 if (package_ == nil)
6269 default: _assert(false);
6275 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6276 if (package_ == nil)
6283 default: _assert(false);
6289 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6290 if (package_ == nil)
6297 default: _assert(false);
6303 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6304 if (package_ == nil)
6307 _UISwitchSlider *slider([cell control]);
6308 BOOL value([slider value] != 0);
6309 NSMutableDictionary *metadata([package_ metadata]);
6312 if (NSNumber *number = [metadata objectForKey:key])
6313 before = [number boolValue];
6317 if (value != before) {
6318 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6320 [delegate_ updateData];
6324 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6325 [self onSomething:cell withKey:@"IsSubscribed"];
6328 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6329 [self onSomething:cell withKey:@"IsIgnored"];
6332 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6333 if (package_ == nil)
6337 case 0: switch (row) {
6339 return subscribedCell_;
6341 return ignoredCell_;
6342 default: _assert(false);
6345 case 1: switch (row) {
6347 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6348 [cell setShowSelection:NO];
6349 [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."];
6353 default: _assert(false);
6356 default: _assert(false);
6362 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6363 if ((self = [super initWithBook:book])) {
6364 database_ = database;
6365 name_ = [package retain];
6367 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6368 [self addSubview:table_];
6370 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6371 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6373 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6374 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6376 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6377 [subscribedCell_ setShowSelection:NO];
6378 [subscribedCell_ setTitle:@"Show All Changes"];
6379 [subscribedCell_ setControl:subscribedSwitch_];
6381 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6382 [ignoredCell_ setShowSelection:NO];
6383 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6384 [ignoredCell_ setControl:ignoredSwitch_];
6386 [table_ setDataSource:self];
6391 - (void) resetViewAnimated:(BOOL)animated {
6392 [table_ resetViewAnimated:animated];
6395 - (void) reloadData {
6396 if (package_ != nil)
6397 [package_ autorelease];
6398 package_ = [database_ packageWithName:name_];
6399 if (package_ != nil) {
6401 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6402 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6405 [table_ reloadData];
6408 - (NSString *) title {
6414 /* Signature View {{{ */
6415 @interface SignatureView : BrowserView {
6416 _transient Database *database_;
6420 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6424 @implementation SignatureView
6431 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6433 [super webView:sender didClearWindowObject:window forFrame:frame];
6436 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6437 if ((self = [super initWithBook:book]) != nil) {
6438 database_ = database;
6439 package_ = [package retain];
6444 - (void) resetViewAnimated:(BOOL)animated {
6447 - (void) reloadData {
6448 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6454 @interface Cydia : UIApplication <
6455 ConfirmationViewDelegate,
6456 ProgressViewDelegate,
6465 UIToolbar *buttonbar_;
6469 NSMutableArray *essential_;
6470 NSMutableArray *broken_;
6472 Database *database_;
6473 ProgressView *progress_;
6477 UIKeyboard *keyboard_;
6478 UIProgressHUD *hud_;
6480 InstallView *install_;
6481 ChangesView *changes_;
6482 ManageView *manage_;
6483 SearchView *search_;
6488 @implementation Cydia
6491 if ([broken_ count] != 0) {
6492 int count = [broken_ count];
6494 UIActionSheet *sheet = [[[UIActionSheet alloc]
6495 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6496 buttons:[NSArray arrayWithObjects:
6498 @"Ignore (Temporary)",
6500 defaultButtonIndex:0
6505 [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."];
6506 [sheet popupAlertAnimated:YES];
6507 } else if (!Ignored_ && [essential_ count] != 0) {
6508 int count = [essential_ count];
6510 UIActionSheet *sheet = [[[UIActionSheet alloc]
6511 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6512 buttons:[NSArray arrayWithObjects:
6513 @"Upgrade Essential",
6514 @"Complete Upgrade",
6515 @"Ignore (Temporary)",
6517 defaultButtonIndex:0
6522 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6523 [sheet popupAlertAnimated:YES];
6527 - (void) _reloadData {
6528 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6529 [hud setText:@"Reloading Data"];
6530 [overlay_ addSubview:hud];
6533 [database_ reloadData];
6537 [essential_ removeAllObjects];
6538 [broken_ removeAllObjects];
6540 NSArray *packages = [database_ packages];
6541 for (Package *package in packages) {
6543 [broken_ addObject:package];
6544 if ([package upgradableAndEssential:NO]) {
6545 if ([package essential])
6546 [essential_ addObject:package];
6552 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6553 [buttonbar_ setBadgeValue:badge forButton:3];
6554 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6555 [buttonbar_ setBadgeAnimated:YES forButton:3];
6556 [self setApplicationBadge:badge];
6558 [buttonbar_ setBadgeValue:nil forButton:3];
6559 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6560 [buttonbar_ setBadgeAnimated:NO forButton:3];
6561 [self removeApplicationBadge];
6567 if ([packages count] == 0);
6579 [hud removeFromSuperview];*/
6582 - (void) _saveConfig {
6585 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6591 - (void) updateData {
6594 /* XXX: this is just stupid */
6595 if (tag_ != 2 && install_ != nil)
6596 [install_ reloadData];
6597 if (tag_ != 3 && changes_ != nil)
6598 [changes_ reloadData];
6599 if (tag_ != 5 && search_ != nil)
6600 [search_ reloadData];
6610 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6611 _assert(file != NULL);
6613 NSArray *keys = [Sources_ allKeys];
6615 for (int i(0), e([keys count]); i != e; ++i) {
6616 NSString *key = [keys objectAtIndex:i];
6617 NSDictionary *source = [Sources_ objectForKey:key];
6619 fprintf(file, "%s %s %s\n",
6620 [[source objectForKey:@"Type"] UTF8String],
6621 [[source objectForKey:@"URI"] UTF8String],
6622 [[source objectForKey:@"Distribution"] UTF8String]
6631 detachNewThreadSelector:@selector(update_)
6634 title:@"Updating Sources"
6638 - (void) reloadData {
6639 @synchronized (self) {
6640 if (confirm_ == nil)
6646 pkgProblemResolver *resolver = [database_ resolver];
6648 resolver->InstallProtect();
6649 if (!resolver->Resolve(true))
6654 [database_ prepare];
6656 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6657 [confirm_ setDelegate:self];
6659 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6660 [page setDelegate:self];
6662 [confirm_ setPage:page];
6663 [underlay_ popSubview:confirm_];
6666 - (void) installPackage:(Package *)package {
6667 @synchronized (self) {
6674 - (void) removePackage:(Package *)package {
6675 @synchronized (self) {
6682 - (void) distUpgrade {
6683 @synchronized (self) {
6684 [database_ upgrade];
6690 @synchronized (self) {
6692 if (confirm_ != nil) {
6700 [overlay_ removeFromSuperview];
6704 detachNewThreadSelector:@selector(perform)
6711 - (void) bootstrap_ {
6713 [database_ upgrade];
6714 [database_ prepare];
6715 [database_ perform];
6718 - (void) bootstrap {
6720 detachNewThreadSelector:@selector(bootstrap_)
6723 title:@"Bootstrap Install"
6727 - (void) progressViewIsComplete:(ProgressView *)progress {
6728 if (confirm_ != nil) {
6729 [underlay_ addSubview:overlay_];
6730 [confirm_ popFromSuperviewAnimated:NO];
6736 - (void) setPage:(RVPage *)page {
6737 [page resetViewAnimated:NO];
6738 [page setDelegate:self];
6739 [book_ setPage:page];
6742 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6743 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6744 [browser loadURL:url];
6748 - (void) _setHomePage {
6749 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6752 - (void) buttonBarItemTapped:(id)sender {
6753 unsigned tag = [sender tag];
6755 [book_ resetViewAnimated:YES];
6757 } else if (tag_ == 2 && tag != 2)
6758 [install_ resetView];
6761 case 1: [self _setHomePage]; break;
6763 case 2: [self setPage:install_]; break;
6764 case 3: [self setPage:changes_]; break;
6765 case 4: [self setPage:manage_]; break;
6766 case 5: [self setPage:search_]; break;
6768 default: _assert(false);
6774 - (void) applicationWillSuspend {
6776 [super applicationWillSuspend];
6779 - (void) askForSettings {
6780 UIActionSheet *role = [[[UIActionSheet alloc]
6781 initWithTitle:@"Who Are You?"
6782 buttons:[NSArray arrayWithObjects:
6783 @"User (Graphical Only)",
6784 @"Hacker (+ Command Line)",
6785 @"Developer (No Filters)",
6787 defaultButtonIndex:-1
6792 [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."];
6793 [role popupAlertAnimated:YES];
6798 [self setStatusBarShowsProgress:NO];
6801 [hud_ removeFromSuperview];
6805 pid_t pid = ExecFork();
6807 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6808 perror("launchctl stop");
6815 [self askForSettings];
6819 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6821 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6822 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6823 0, 0, screenrect.size.width, screenrect.size.height - 48
6824 ) database:database_];
6826 [book_ setDelegate:self];
6828 [overlay_ addSubview:book_];
6830 NSArray *buttonitems = [NSArray arrayWithObjects:
6831 [NSDictionary dictionaryWithObjectsAndKeys:
6832 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6833 @"home-up.png", kUIButtonBarButtonInfo,
6834 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6835 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6836 self, kUIButtonBarButtonTarget,
6837 @"Home", kUIButtonBarButtonTitle,
6838 @"0", kUIButtonBarButtonType,
6841 [NSDictionary dictionaryWithObjectsAndKeys:
6842 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6843 @"install-up.png", kUIButtonBarButtonInfo,
6844 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6845 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6846 self, kUIButtonBarButtonTarget,
6847 @"Sections", kUIButtonBarButtonTitle,
6848 @"0", kUIButtonBarButtonType,
6851 [NSDictionary dictionaryWithObjectsAndKeys:
6852 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6853 @"changes-up.png", kUIButtonBarButtonInfo,
6854 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6855 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6856 self, kUIButtonBarButtonTarget,
6857 @"Changes", kUIButtonBarButtonTitle,
6858 @"0", kUIButtonBarButtonType,
6861 [NSDictionary dictionaryWithObjectsAndKeys:
6862 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6863 @"manage-up.png", kUIButtonBarButtonInfo,
6864 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6865 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6866 self, kUIButtonBarButtonTarget,
6867 @"Manage", kUIButtonBarButtonTitle,
6868 @"0", kUIButtonBarButtonType,
6871 [NSDictionary dictionaryWithObjectsAndKeys:
6872 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6873 @"search-up.png", kUIButtonBarButtonInfo,
6874 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6875 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6876 self, kUIButtonBarButtonTarget,
6877 @"Search", kUIButtonBarButtonTitle,
6878 @"0", kUIButtonBarButtonType,
6882 buttonbar_ = [[UIToolbar alloc]
6884 withFrame:CGRectMake(
6885 0, screenrect.size.height - ButtonBarHeight_,
6886 screenrect.size.width, ButtonBarHeight_
6888 withItemList:buttonitems
6891 [buttonbar_ setDelegate:self];
6892 [buttonbar_ setBarStyle:1];
6893 [buttonbar_ setButtonBarTrackingMode:2];
6895 int buttons[5] = {1, 2, 3, 4, 5};
6896 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6897 [buttonbar_ showButtonGroup:0 withDuration:0];
6899 for (int i = 0; i != 5; ++i)
6900 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6901 i * 64 + 2, 1, 60, ButtonBarHeight_
6904 [buttonbar_ showSelectionForButton:1];
6905 [overlay_ addSubview:buttonbar_];
6907 [UIKeyboard initImplementationNow];
6908 CGSize keysize = [UIKeyboard defaultSize];
6909 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6910 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6911 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6912 [overlay_ addSubview:keyboard_];
6915 [underlay_ addSubview:overlay_];
6919 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6920 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6921 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6923 manage_ = (ManageView *) [[self
6924 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6925 withClass:[ManageView class]
6931 [self _setHomePage];
6934 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6935 NSString *context = [sheet context];
6936 if ([context isEqualToString:@"fixhalf"])
6939 @synchronized (self) {
6940 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6941 Package *broken = [broken_ objectAtIndex:i];
6944 NSString *id = [broken id];
6945 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6946 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6947 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6948 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6957 [broken_ removeAllObjects];
6964 else if ([context isEqualToString:@"role"]) {
6966 case 1: Role_ = @"User"; break;
6967 case 2: Role_ = @"Hacker"; break;
6968 case 3: Role_ = @"Developer"; break;
6975 bool reset = Settings_ != nil;
6977 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6981 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6989 } else if ([context isEqualToString:@"upgrade"])
6992 @synchronized (self) {
6993 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6994 Package *essential = [essential_ objectAtIndex:i];
6995 [essential install];
7018 - (void) reorganize { _pooled
7019 system("/usr/libexec/cydia/free.sh");
7020 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7023 - (void) applicationSuspend:(__GSEvent *)event {
7024 if (hud_ == nil && ![progress_ isRunning])
7025 [super applicationSuspend:event];
7028 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7030 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7033 - (void) _setSuspended:(BOOL)value {
7035 [super _setSuspended:value];
7038 - (UIProgressHUD *) addProgressHUD {
7039 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7041 [underlay_ addSubview:hud];
7045 - (void) openMailToURL:(NSURL *)url {
7046 // XXX: this makes me sad
7048 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7050 [UIApp openURL:url];
7054 - (RVPage *) pageForPackage:(NSString *)name {
7055 if (Package *package = [database_ packageWithName:name]) {
7056 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7057 [view setPackage:package];
7060 UIActionSheet *sheet = [[[UIActionSheet alloc]
7061 initWithTitle:@"Cannot Locate Package"
7062 buttons:[NSArray arrayWithObjects:@"Close", nil]
7063 defaultButtonIndex:0
7068 [sheet setBodyText:[NSString stringWithFormat:
7069 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7072 [sheet popupAlertAnimated:YES];
7077 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7078 NSString *href = [url absoluteString];
7083 if ([href isEqualToString:@"cydia://add-source"])
7084 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7085 else if ([href isEqualToString:@"cydia://sources"])
7086 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7087 else if ([href isEqualToString:@"cydia://packages"])
7088 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7089 else if ([href hasPrefix:@"cydia://url/"])
7090 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7091 else if ([href hasPrefix:@"cydia://launch/"])
7092 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7093 else if ([href hasPrefix:@"cydia://package-settings/"])
7094 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7095 else if ([href hasPrefix:@"cydia://package-signature/"])
7096 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7097 else if ([href hasPrefix:@"cydia://package/"])
7098 return [self pageForPackage:[href substringFromIndex:16]];
7099 else if ([href hasPrefix:@"cydia://files/"]) {
7100 NSString *name = [href substringFromIndex:14];
7102 if (Package *package = [database_ packageWithName:name]) {
7103 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7104 [files setPackage:package];
7112 - (void) applicationOpenURL:(NSURL *)url {
7113 [super applicationOpenURL:url];
7115 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7116 [self setPage:page];
7117 [buttonbar_ showSelectionForButton:tag];
7122 - (void) applicationDidFinishLaunching:(id)unused {
7123 Font12_ = [[UIFont systemFontOfSize:12] retain];
7124 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7125 Font14_ = [[UIFont systemFontOfSize:14] retain];
7126 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7127 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7129 _assert(pkgInitConfig(*_config));
7130 _assert(pkgInitSystem(*_config, _system));
7134 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7135 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7137 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7139 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7140 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7142 [window_ orderFront:self];
7143 [window_ makeKey:self];
7144 [window_ setHidden:NO];
7146 database_ = [Database sharedInstance];
7147 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7148 [database_ setDelegate:progress_];
7149 [window_ setContentView:progress_];
7151 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7152 [progress_ setContentView:underlay_];
7154 [progress_ resetView];
7157 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7158 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7159 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7160 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7161 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7162 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7163 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7165 [self setIdleTimerDisabled:YES];
7167 hud_ = [self addProgressHUD];
7168 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
7170 [self setStatusBarShowsProgress:YES];
7173 detachNewThreadSelector:@selector(reorganize)
7181 /* Web Scripting {{{ */
7182 + (NSString *) webScriptNameForSelector:(SEL)selector {
7183 if (selector == @selector(supports:))
7188 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7189 return selector != @selector(supports:);
7192 - (BOOL) supports:(NSString *)feature {
7193 return [feature isEqualToString:@"window.open"];
7197 - (void) showKeyboard:(BOOL)show {
7198 CGSize keysize = [UIKeyboard defaultSize];
7199 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7200 CGRect keyup = keydown;
7201 keyup.origin.y -= keysize.height;
7203 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7204 [animation setSignificantRectFields:2];
7207 [animation setStartFrame:keydown];
7208 [animation setEndFrame:keyup];
7209 [keyboard_ activate];
7211 [animation setStartFrame:keyup];
7212 [animation setEndFrame:keydown];
7213 [keyboard_ deactivate];
7216 [[UIAnimator sharedAnimator]
7217 addAnimations:[NSArray arrayWithObjects:animation, nil]
7218 withDuration:KeyboardTime_
7223 - (void) slideUp:(UIActionSheet *)alert {
7225 [alert presentSheetFromButtonBar:buttonbar_];
7227 [alert presentSheetInView:overlay_];
7232 void AddPreferences(NSString *plist) { _pooled
7233 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7234 _assert(settings != NULL);
7235 NSMutableArray *items = [settings objectForKey:@"items"];
7239 for (size_t i(0); i != [items count]; ++i) {
7240 NSMutableDictionary *item([items objectAtIndex:i]);
7241 NSString *label = [item objectForKey:@"label"];
7242 if (label != nil && [label isEqualToString:@"Cydia"]) {
7249 for (size_t i(0); i != [items count]; ++i) {
7250 NSDictionary *item([items objectAtIndex:i]);
7251 NSString *label = [item objectForKey:@"label"];
7252 if (label != nil && [label isEqualToString:@"General"]) {
7253 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7254 @"CydiaSettings", @"bundle",
7255 @"PSLinkCell", @"cell",
7256 [NSNumber numberWithBool:YES], @"hasIcon",
7257 [NSNumber numberWithBool:YES], @"isController",
7259 nil] atIndex:(i + 1)];
7265 _assert([settings writeToFile:plist atomically:YES] == YES);
7270 id Alloc_(id self, SEL selector) {
7271 id object = alloc_(self, selector);
7272 lprintf("[%s]A-%p\n", self->isa->name, object);
7277 id Dealloc_(id self, SEL selector) {
7278 id object = dealloc_(self, selector);
7279 lprintf("[%s]D-%p\n", self->isa->name, object);
7283 int main(int argc, char *argv[]) { _pooled
7284 bool substrate(false);
7290 for (int argi(1); argi != argc; ++argi)
7291 if (strcmp(argv[argi], "--") == 0) {
7293 argv[argi] = argv[0];
7299 for (int argi(1); argi != arge; ++argi)
7300 if (strcmp(args[argi], "--bootstrap") == 0)
7302 else if (strcmp(args[argi], "--substrate") == 0)
7305 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7308 App_ = [[NSBundle mainBundle] bundlePath];
7309 Home_ = NSHomeDirectory();
7310 Locale_ = CFLocaleCopyCurrent();
7313 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7314 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7315 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7316 Sounds_Keyboard_ = [keyboard boolValue];
7322 #if 1 /* XXX: this costs 1.4s of startup performance */
7323 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7324 _assert(errno == ENOENT);
7325 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7326 _assert(errno == ENOENT);
7329 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7330 alloc_ = alloc->method_imp;
7331 alloc->method_imp = (IMP) &Alloc_;*/
7333 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7334 dealloc_ = dealloc->method_imp;
7335 dealloc->method_imp = (IMP) &Dealloc_;*/
7340 size = sizeof(maxproc);
7341 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7342 perror("sysctlbyname(\"kern.maxproc\", ?)");
7343 else if (maxproc < 64) {
7345 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7346 perror("sysctlbyname(\"kern.maxproc\", #)");
7349 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7350 char *machine = new char[size];
7351 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7352 perror("sysctlbyname(\"hw.machine\", ?)");
7356 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7358 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7359 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7361 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7362 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7364 Settings_ = [Metadata_ objectForKey:@"Settings"];
7366 Packages_ = [Metadata_ objectForKey:@"Packages"];
7367 Sections_ = [Metadata_ objectForKey:@"Sections"];
7368 Sources_ = [Metadata_ objectForKey:@"Sources"];
7371 if (Settings_ != nil)
7372 Role_ = [Settings_ objectForKey:@"Role"];
7374 if (Packages_ == nil) {
7375 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7376 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7379 if (Sections_ == nil) {
7380 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7381 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7384 if (Sources_ == nil) {
7385 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7386 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7390 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7393 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7394 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7396 if (access("/User", F_OK) != 0)
7397 system("/usr/libexec/cydia/firmware.sh");
7399 _assert([[NSFileManager defaultManager]
7400 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7401 withIntermediateDirectories:YES
7406 space_ = CGColorSpaceCreateDeviceRGB();
7408 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7409 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7410 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7411 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7412 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7413 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7415 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7417 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7419 UIApplicationUseLegacyEvents(YES);
7420 UIKeyboardDisableAutomaticAppearance();
7422 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7424 CGColorSpaceRelease(space_);