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 /* Objective-C Handle<> {{{ */
120 template <typename Type_>
122 typedef _H<Type_> This_;
127 _finline void Retain_() {
132 _finline void Clear_() {
138 _finline _H(Type_ *value = NULL, bool mended = false) :
149 _finline This_ &operator =(Type_ *value) {
150 if (value_ != value) {
159 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
161 void NSLogRect(const char *fix, const CGRect &rect) {
162 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
165 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
167 /* iPhoneOS 2.0 Compatibility {{{ */
169 @interface UITextView (iPhoneOS)
170 - (void) setTextSize:(float)size;
173 @implementation UITextView (iPhoneOS)
175 - (void) setTextSize:(float)size {
176 [self setFont:[[self font] fontWithSize:size]];
183 extern NSString * const kCAFilterNearest;
185 /* Information Dictionaries {{{ */
186 @interface NSMutableArray (Cydia)
187 - (void) addInfoDictionary:(NSDictionary *)info;
190 @implementation NSMutableArray (Cydia)
192 - (void) addInfoDictionary:(NSDictionary *)info {
193 [self addObject:info];
198 @interface NSMutableDictionary (Cydia)
199 - (void) addInfoDictionary:(NSDictionary *)info;
202 @implementation NSMutableDictionary (Cydia)
204 - (void) addInfoDictionary:(NSDictionary *)info {
205 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
206 [self setObject:info forKey:bundle];
211 /* Pop Transitions {{{ */
212 @interface PopTransitionView : UITransitionView {
217 @implementation PopTransitionView
219 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
220 if (from != nil && to == nil)
221 [self removeFromSuperview];
226 @interface UIView (PopUpView)
227 - (void) popFromSuperviewAnimated:(BOOL)animated;
228 - (void) popSubview:(UIView *)view;
231 @implementation UIView (PopUpView)
233 - (void) popFromSuperviewAnimated:(BOOL)animated {
234 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
237 - (void) popSubview:(UIView *)view {
238 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
239 [transition setDelegate:transition];
240 [self addSubview:transition];
242 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
243 [transition transition:UITransitionNone toView:blank];
244 [transition transition:UITransitionPushFromBottom toView:view];
250 #define lprintf(args...) fprintf(stderr, args)
253 #define RecycleWebViews 0
256 @interface NSMutableArray (Radix)
257 - (void) radixUsingSelector:(SEL)selector withObject:(id)object;
260 @implementation NSMutableArray (Radix)
262 - (void) radixUsingSelector:(SEL)selector withObject:(id)object {
263 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
264 [invocation setSelector:selector];
265 [invocation setArgument:&object atIndex:2];
267 size_t count([self count]);
272 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
274 for (size_t i(0); i != count; ++i) {
275 RadixItem &item(lhs[i]);
278 id object([self objectAtIndex:i]);
279 [invocation setTarget:object];
282 [invocation getReturnValue:&item.key];
285 static const size_t bits = 11;
286 static const size_t slots = 1 << bits;
287 static const size_t passes = (32 + (bits - 1)) / bits;
289 size_t *hist(new size_t[slots]);
291 for (size_t pass(0); pass != passes; ++pass) {
292 memset(hist, 0, sizeof(size_t) * slots);
294 for (size_t i(0); i != count; ++i) {
295 uint32_t key(lhs[i].key);
297 key &= _not(uint32_t) >> 32 - bits;
302 for (size_t i(0); i != slots; ++i) {
303 size_t local(offset);
308 for (size_t i(0); i != count; ++i) {
309 uint32_t key(lhs[i].key);
311 key &= _not(uint32_t) >> 32 - bits;
312 rhs[hist[key]++] = lhs[i];
322 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
323 for (size_t i(0); i != count; ++i)
324 [values addObject:[self objectAtIndex:lhs[i].index]];
325 [self setArray:values];
334 kUIControlEventMouseDown = 1 << 0,
335 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
336 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
337 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
338 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
339 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
340 } UIControlEventMasks;
342 @interface NSString (UIKit)
343 - (NSString *) stringByAddingPercentEscapes;
344 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
347 @interface NSString (Cydia)
348 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
349 - (NSComparisonResult) compareByPath:(NSString *)other;
352 @implementation NSString (Cydia)
354 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
355 char data[length + 1];
356 memcpy(data, bytes, length);
358 return [NSString stringWithUTF8String:data];
361 - (NSComparisonResult) compareByPath:(NSString *)other {
362 NSString *prefix = [self commonPrefixWithString:other options:0];
363 size_t length = [prefix length];
365 NSRange lrange = NSMakeRange(length, [self length] - length);
366 NSRange rrange = NSMakeRange(length, [other length] - length);
368 lrange = [self rangeOfString:@"/" options:0 range:lrange];
369 rrange = [other rangeOfString:@"/" options:0 range:rrange];
371 NSComparisonResult value;
373 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
374 value = NSOrderedSame;
375 else if (lrange.location == NSNotFound)
376 value = NSOrderedAscending;
377 else if (rrange.location == NSNotFound)
378 value = NSOrderedDescending;
380 value = NSOrderedSame;
382 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
383 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
384 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
385 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
387 NSComparisonResult result = [lpath compare:rpath];
388 return result == NSOrderedSame ? value : result;
393 /* Perl-Compatible RegEx {{{ */
403 Pcre(const char *regex) :
408 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
411 lprintf("%d:%s\n", offset, error);
415 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
416 matches_ = new int[(capture_ + 1) * 3];
424 NSString *operator [](size_t match) {
425 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
428 bool operator ()(NSString *data) {
429 // XXX: length is for characters, not for bytes
430 return operator ()([data UTF8String], [data length]);
433 bool operator ()(const char *data, size_t size) {
435 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
439 /* Mime Addresses {{{ */
440 @interface Address : NSObject {
446 - (NSString *) address;
448 + (Address *) addressWithString:(NSString *)string;
449 - (Address *) initWithString:(NSString *)string;
452 @implementation Address
461 - (NSString *) name {
465 - (NSString *) address {
469 + (Address *) addressWithString:(NSString *)string {
470 return [[[Address alloc] initWithString:string] autorelease];
473 + (NSArray *) _attributeKeys {
474 return [NSArray arrayWithObjects:@"address", @"name", nil];
477 - (NSArray *) attributeKeys {
478 return [[self class] _attributeKeys];
481 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
482 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
485 - (Address *) initWithString:(NSString *)string {
486 if ((self = [super init]) != nil) {
487 const char *data = [string UTF8String];
488 size_t size = [string length];
490 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
492 if (address_r(data, size)) {
493 name_ = [address_r[1] retain];
494 address_ = [address_r[2] retain];
496 name_ = [string retain];
504 /* CoreGraphics Primitives {{{ */
515 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
518 Set(space, red, green, blue, alpha);
523 CGColorRelease(color_);
530 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
532 float color[] = {red, green, blue, alpha};
533 color_ = CGColorCreate(space, color);
536 operator CGColorRef() {
542 extern "C" void UISetColor(CGColorRef color);
544 /* Random Global Variables {{{ */
545 static const int PulseInterval_ = 50000;
546 static const int ButtonBarHeight_ = 48;
547 static const float KeyboardTime_ = 0.3f;
549 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
550 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
552 static CGColor Blue_;
553 static CGColor Blueish_;
554 static CGColor Black_;
556 static CGColor White_;
557 static CGColor Gray_;
559 static NSString *App_;
560 static NSString *Home_;
561 static BOOL Sounds_Keyboard_;
563 static BOOL Advanced_;
567 static BOOL Ignored_;
569 static UIFont *Font12_;
570 static UIFont *Font12Bold_;
571 static UIFont *Font14_;
572 static UIFont *Font18Bold_;
573 static UIFont *Font22Bold_;
575 static const char *Machine_ = NULL;
576 static const NSString *UniqueID_ = NULL;
583 CGColorSpaceRef space_;
585 #define FW_LEAST(major, minor, bugfix) \
586 (major < Major_ || major == Major_ && \
587 (minor < Minor_ || minor == Minor_ && \
593 static NSDictionary *SectionMap_;
594 static NSMutableDictionary *Metadata_;
595 static _transient NSMutableDictionary *Settings_;
596 static _transient NSString *Role_;
597 static _transient NSMutableDictionary *Packages_;
598 static _transient NSMutableDictionary *Sections_;
599 static _transient NSMutableDictionary *Sources_;
600 static bool Changed_;
604 static NSMutableArray *Documents_;
607 NSString *GetLastUpdate() {
608 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
611 return @"Never or Unknown";
613 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
614 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
616 CFRelease(formatter);
618 return [(NSString *) formatted autorelease];
621 /* Display Helpers {{{ */
622 inline float Interpolate(float begin, float end, float fraction) {
623 return (end - begin) * fraction + begin;
626 NSString *SizeString(double size) {
627 bool negative = size < 0;
632 while (size > 1024) {
637 static const char *powers_[] = {"B", "kB", "MB", "GB"};
639 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
642 NSString *StripVersion(NSString *version) {
643 NSRange colon = [version rangeOfString:@":"];
644 if (colon.location != NSNotFound)
645 version = [version substringFromIndex:(colon.location + 1)];
649 NSString *Simplify(NSString *title) {
650 const char *data = [title UTF8String];
651 size_t size = [title length];
653 static Pcre square_r("^\\[(.*)\\]$");
654 if (square_r(data, size))
655 return Simplify(square_r[1]);
657 static Pcre paren_r("^\\((.*)\\)$");
658 if (paren_r(data, size))
659 return Simplify(paren_r[1]);
661 static Pcre title_r("^(.*?) \\(.*\\)$");
662 if (title_r(data, size))
663 return Simplify(title_r[1]);
669 bool isSectionVisible(NSString *section) {
670 NSDictionary *metadata = [Sections_ objectForKey:section];
671 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
672 return hidden == nil || ![hidden boolValue];
675 /* Delegate Prototypes {{{ */
679 @interface NSObject (ProgressDelegate)
682 @implementation NSObject(ProgressDelegate)
684 - (void) _setProgressError:(NSArray *)args {
685 [self performSelector:@selector(setProgressError:forPackage:)
686 withObject:[args objectAtIndex:0]
687 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
693 @protocol ProgressDelegate
694 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
695 - (void) setProgressTitle:(NSString *)title;
696 - (void) setProgressPercent:(float)percent;
697 - (void) startProgress;
698 - (void) addProgressOutput:(NSString *)output;
699 - (bool) isCancelling:(size_t)received;
702 @protocol ConfigurationDelegate
703 - (void) repairWithSelector:(SEL)selector;
704 - (void) setConfigurationData:(NSString *)data;
707 @protocol CydiaDelegate
708 - (void) installPackage:(Package *)package;
709 - (void) removePackage:(Package *)package;
710 - (void) slideUp:(UIActionSheet *)alert;
711 - (void) distUpgrade;
714 - (void) askForSettings;
715 - (UIProgressHUD *) addProgressHUD;
716 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
717 - (RVPage *) pageForPackage:(NSString *)name;
718 - (void) openMailToURL:(NSURL *)url;
722 /* Status Delegation {{{ */
724 public pkgAcquireStatus
727 _transient NSObject<ProgressDelegate> *delegate_;
735 void setDelegate(id delegate) {
736 delegate_ = delegate;
739 virtual bool MediaChange(std::string media, std::string drive) {
743 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
746 virtual void Fetch(pkgAcquire::ItemDesc &item) {
747 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
750 virtual void Done(pkgAcquire::ItemDesc &item) {
753 virtual void Fail(pkgAcquire::ItemDesc &item) {
755 item.Owner->Status == pkgAcquire::Item::StatIdle ||
756 item.Owner->Status == pkgAcquire::Item::StatDone
760 std::string &error(item.Owner->ErrorText);
764 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
765 NSArray *fields([description componentsSeparatedByString:@" "]);
766 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
768 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
769 withObject:[NSArray arrayWithObjects:
770 [NSString stringWithUTF8String:error.c_str()],
777 virtual bool Pulse(pkgAcquire *Owner) {
778 bool value = pkgAcquireStatus::Pulse(Owner);
781 double(CurrentBytes + CurrentItems) /
782 double(TotalBytes + TotalItems)
785 [delegate_ setProgressPercent:percent];
786 return [delegate_ isCancelling:CurrentBytes] ? false : value;
789 virtual void Start() {
790 [delegate_ startProgress];
793 virtual void Stop() {
797 /* Progress Delegation {{{ */
802 _transient id<ProgressDelegate> delegate_;
805 virtual void Update() {
806 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
807 [delegate_ setProgressPercent:(Percent / 100)];
816 void setDelegate(id delegate) {
817 delegate_ = delegate;
820 virtual void Done() {
821 [delegate_ setProgressPercent:1];
826 /* Database Interface {{{ */
827 @interface Database : NSObject {
829 pkgDepCache::Policy *policy_;
830 pkgRecords *records_;
831 pkgProblemResolver *resolver_;
832 pkgAcquire *fetcher_;
834 SPtr<pkgPackageManager> manager_;
835 pkgSourceList *list_;
837 NSMutableDictionary *sources_;
838 NSMutableArray *packages_;
840 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
849 + (Database *) sharedInstance;
851 - (void) _readCydia:(NSNumber *)fd;
852 - (void) _readStatus:(NSNumber *)fd;
853 - (void) _readOutput:(NSNumber *)fd;
857 - (Package *) packageWithName:(NSString *)name;
859 - (pkgCacheFile &) cache;
860 - (pkgDepCache::Policy *) policy;
861 - (pkgRecords *) records;
862 - (pkgProblemResolver *) resolver;
863 - (pkgAcquire &) fetcher;
864 - (NSArray *) packages;
865 - (NSArray *) sources;
874 - (void) updateWithStatus:(Status &)status;
876 - (void) setDelegate:(id)delegate;
877 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
881 /* Source Class {{{ */
882 @interface Source : NSObject {
883 NSString *description_;
888 NSString *distribution_;
892 NSString *defaultIcon_;
894 NSDictionary *record_;
898 - (Source *) initWithMetaIndex:(metaIndex *)index;
900 - (NSComparisonResult) compareByNameAndType:(Source *)source;
902 - (NSDictionary *) record;
906 - (NSString *) distribution;
912 - (NSString *) description;
913 - (NSString *) label;
914 - (NSString *) origin;
915 - (NSString *) version;
917 - (NSString *) defaultIcon;
921 @implementation Source
925 [distribution_ release];
928 if (description_ != nil)
929 [description_ release];
936 if (defaultIcon_ != nil)
937 [defaultIcon_ release];
944 + (NSArray *) _attributeKeys {
945 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
948 - (NSArray *) attributeKeys {
949 return [[self class] _attributeKeys];
952 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
953 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
956 - (Source *) initWithMetaIndex:(metaIndex *)index {
957 if ((self = [super init]) != nil) {
958 trusted_ = index->IsTrusted();
960 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
961 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
962 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
964 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
965 if (dindex != NULL) {
966 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
968 while (std::getline(release, line)) {
969 std::string::size_type colon(line.find(':'));
970 if (colon == std::string::npos)
973 std::string name(line.substr(0, colon));
974 std::string value(line.substr(colon + 1));
975 while (!value.empty() && value[0] == ' ')
976 value = value.substr(1);
978 if (name == "Default-Icon")
979 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
980 else if (name == "Description")
981 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
982 else if (name == "Label")
983 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
984 else if (name == "Origin")
985 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
986 else if (name == "Version")
987 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
991 record_ = [Sources_ objectForKey:[self key]];
993 record_ = [record_ retain];
997 - (NSComparisonResult) compareByNameAndType:(Source *)source {
998 NSDictionary *lhr = [self record];
999 NSDictionary *rhr = [source record];
1002 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1004 NSString *lhs = [self name];
1005 NSString *rhs = [source name];
1007 if ([lhs length] != 0 && [rhs length] != 0) {
1008 unichar lhc = [lhs characterAtIndex:0];
1009 unichar rhc = [rhs characterAtIndex:0];
1011 if (isalpha(lhc) && !isalpha(rhc))
1012 return NSOrderedAscending;
1013 else if (!isalpha(lhc) && isalpha(rhc))
1014 return NSOrderedDescending;
1017 return [lhs compare:rhs options:CompareOptions_];
1020 - (NSDictionary *) record {
1028 - (NSString *) uri {
1032 - (NSString *) distribution {
1033 return distribution_;
1036 - (NSString *) type {
1040 - (NSString *) key {
1041 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1044 - (NSString *) host {
1045 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1048 - (NSString *) name {
1049 return origin_ == nil ? [self host] : origin_;
1052 - (NSString *) description {
1053 return description_;
1056 - (NSString *) label {
1057 return label_ == nil ? [self host] : label_;
1060 - (NSString *) origin {
1064 - (NSString *) version {
1068 - (NSString *) defaultIcon {
1069 return defaultIcon_;
1074 /* Relationship Class {{{ */
1075 @interface Relationship : NSObject {
1080 - (NSString *) type;
1082 - (NSString *) name;
1086 @implementation Relationship
1094 - (NSString *) type {
1102 - (NSString *) name {
1109 /* Package Class {{{ */
1110 NSString *Scour(const char *field, const char *begin, const char *end) {
1111 size_t i(0), l(strlen(field));
1114 const char *name = begin + i;
1115 const char *colon = name + l;
1116 const char *value = colon + 1;
1121 strncasecmp(name, field, l) == 0
1123 while (value != end && value[0] == ' ')
1125 const char *line = std::find(value, end, '\n');
1126 while (line != value && line[-1] == ' ')
1129 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1131 begin = std::find(begin, end, '\n');
1139 @interface Package : NSObject {
1140 pkgCache::PkgIterator iterator_;
1141 _transient Database *database_;
1142 pkgCache::VerIterator version_;
1143 pkgCache::VerFileIterator file_;
1151 NSString *installed_;
1157 NSString *depiction_;
1158 NSString *homepage_;
1164 NSArray *relationships_;
1167 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1168 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1170 - (pkgCache::PkgIterator) iterator;
1172 - (NSString *) section;
1173 - (NSString *) simpleSection;
1175 - (Address *) maintainer;
1177 - (NSString *) description;
1178 - (NSString *) index;
1180 - (NSMutableDictionary *) metadata;
1182 - (BOOL) subscribed;
1185 - (NSString *) latest;
1186 - (NSString *) installed;
1189 - (BOOL) upgradableAndEssential:(BOOL)essential;
1192 - (BOOL) unfiltered;
1196 - (BOOL) halfConfigured;
1197 - (BOOL) halfInstalled;
1199 - (NSString *) mode;
1202 - (NSString *) name;
1203 - (NSString *) tagline;
1205 - (NSString *) homepage;
1206 - (NSString *) depiction;
1207 - (Address *) author;
1209 - (NSArray *) files;
1210 - (NSArray *) relationships;
1211 - (NSArray *) warnings;
1212 - (NSArray *) applications;
1214 - (Source *) source;
1215 - (NSString *) role;
1217 - (BOOL) matches:(NSString *)text;
1219 - (bool) hasSupportingRole;
1220 - (BOOL) hasTag:(NSString *)tag;
1221 - (NSString *) primaryPurpose;
1222 - (NSArray *) purposes;
1224 - (NSComparisonResult) compareByName:(Package *)package;
1225 - (NSComparisonResult) compareBySection:(Package *)package;
1227 - (uint32_t) compareForChanges;
1232 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1233 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1234 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1235 - (NSNumber *) isVisibleInSource:(Source *)source;
1239 @implementation Package
1245 if (section_ != nil)
1249 if (installed_ != nil)
1250 [installed_ release];
1258 if (depiction_ != nil)
1259 [depiction_ release];
1260 if (homepage_ != nil)
1261 [homepage_ release];
1262 if (sponsor_ != nil)
1271 if (relationships_ != nil)
1272 [relationships_ release];
1277 + (NSArray *) _attributeKeys {
1278 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1281 - (NSArray *) attributeKeys {
1282 return [[self class] _attributeKeys];
1285 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1286 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1289 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1290 if ((self = [super init]) != nil) {
1291 iterator_ = iterator;
1292 database_ = database;
1294 version_ = [database_ policy]->GetCandidateVer(iterator_);
1295 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1296 latest_ = [StripVersion(latest) retain];
1298 pkgCache::VerIterator current = iterator_.CurrentVer();
1299 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1300 installed_ = [StripVersion(installed) retain];
1302 if (!version_.end())
1303 file_ = version_.FileList();
1305 pkgCache &cache([database_ cache]);
1306 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1309 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1312 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1314 const char *begin, *end;
1315 parser->GetRec(begin, end);
1317 name_ = Scour("name", begin, end);
1319 name_ = [name_ retain];
1320 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1321 icon_ = Scour("icon", begin, end);
1323 icon_ = [icon_ retain];
1324 depiction_ = Scour("depiction", begin, end);
1325 if (depiction_ != nil)
1326 depiction_ = [depiction_ retain];
1327 homepage_ = Scour("homepage", begin, end);
1328 if (homepage_ == nil)
1329 homepage_ = Scour("website", begin, end);
1330 if ([homepage_ isEqualToString:depiction_])
1332 if (homepage_ != nil)
1333 homepage_ = [homepage_ retain];
1334 NSString *sponsor = Scour("sponsor", begin, end);
1336 sponsor_ = [[Address addressWithString:sponsor] retain];
1337 NSString *author = Scour("author", begin, end);
1339 author_ = [[Address addressWithString:author] retain];
1340 NSString *tags = Scour("tag", begin, end);
1342 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1346 for (int i(0), e([tags_ count]); i != e; ++i) {
1347 NSString *tag = [tags_ objectAtIndex:i];
1348 if ([tag hasPrefix:@"role::"]) {
1349 role_ = [[tag substringFromIndex:6] retain];
1354 NSString *solid(latest == nil ? installed : latest);
1355 bool changed(false);
1357 NSString *key([id_ lowercaseString]);
1359 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1360 if (metadata == nil) {
1361 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1366 [metadata setObject:solid forKey:@"LastVersion"];
1369 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1370 NSDate *last([metadata objectForKey:@"LastSeen"]);
1371 NSString *version([metadata objectForKey:@"LastVersion"]);
1374 first = last == nil ? now_ : last;
1375 [metadata setObject:first forKey:@"FirstSeen"];
1380 if (version == nil) {
1381 [metadata setObject:solid forKey:@"LastVersion"];
1383 } else if (![version isEqualToString:solid]) {
1384 [metadata setObject:solid forKey:@"LastVersion"];
1386 [metadata setObject:last forKey:@"LastSeen"];
1392 [Packages_ setObject:metadata forKey:key];
1398 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1399 return [[[Package alloc]
1400 initWithIterator:iterator
1405 - (pkgCache::PkgIterator) iterator {
1409 - (NSString *) section {
1410 if (section_ != nil)
1413 const char *section = iterator_.Section();
1414 if (section == NULL)
1417 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1420 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1421 if (NSString *rename = [value objectForKey:@"Rename"]) {
1426 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1430 - (NSString *) simpleSection {
1431 if (NSString *section = [self section])
1432 return Simplify(section);
1438 - (Address *) maintainer {
1441 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1442 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1446 return version_.end() ? 0 : version_->InstalledSize;
1449 - (NSString *) description {
1452 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1453 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1455 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1456 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1457 if ([lines count] < 2)
1460 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1461 for (size_t i(1); i != [lines count]; ++i) {
1462 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1463 [trimmed addObject:trim];
1466 return [trimmed componentsJoinedByString:@"\n"];
1469 - (NSString *) index {
1470 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1471 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1474 - (NSMutableDictionary *) metadata {
1475 return [Packages_ objectForKey:[id_ lowercaseString]];
1479 NSDictionary *metadata([self metadata]);
1480 if ([self subscribed])
1481 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1483 return [metadata objectForKey:@"FirstSeen"];
1486 - (BOOL) subscribed {
1487 NSDictionary *metadata([self metadata]);
1488 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1489 return [subscribed boolValue];
1495 NSDictionary *metadata([self metadata]);
1496 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1497 return [ignored boolValue];
1502 - (NSString *) latest {
1506 - (NSString *) installed {
1511 return !version_.end();
1514 - (BOOL) upgradableAndEssential:(BOOL)essential {
1515 pkgCache::VerIterator current = iterator_.CurrentVer();
1518 return essential && [self essential];
1520 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1521 return !candidate.end() && candidate != current;
1525 - (BOOL) essential {
1526 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1530 return [database_ cache][iterator_].InstBroken();
1533 - (BOOL) unfiltered {
1534 NSString *section = [self section];
1535 return section == nil || isSectionVisible(section);
1539 return [self hasSupportingRole] && [self unfiltered];
1543 unsigned char current = iterator_->CurrentState;
1544 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1547 - (BOOL) halfConfigured {
1548 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1551 - (BOOL) halfInstalled {
1552 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1556 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1557 return state.Mode != pkgDepCache::ModeKeep;
1560 - (NSString *) mode {
1561 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1563 switch (state.Mode) {
1564 case pkgDepCache::ModeDelete:
1565 if ((state.iFlags & pkgDepCache::Purge) != 0)
1569 case pkgDepCache::ModeKeep:
1570 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1574 case pkgDepCache::ModeInstall:
1575 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1576 return @"Reinstall";
1577 else switch (state.Status) {
1579 return @"Downgrade";
1585 return @"New Install";
1598 - (NSString *) name {
1599 return name_ == nil ? id_ : name_;
1602 - (NSString *) tagline {
1606 - (UIImage *) icon {
1607 NSString *section = [self simpleSection];
1610 if (NSString *icon = icon_)
1611 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1612 if (icon == nil) if (section != nil)
1613 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1614 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1615 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1617 icon = [UIImage applicationImageNamed:@"unknown.png"];
1621 - (NSString *) homepage {
1625 - (NSString *) depiction {
1629 - (Address *) sponsor {
1633 - (Address *) author {
1637 - (NSArray *) files {
1638 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1639 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1642 fin.open([path UTF8String]);
1647 while (std::getline(fin, line))
1648 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1653 - (NSArray *) relationships {
1654 return relationships_;
1657 - (NSArray *) warnings {
1658 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1659 const char *name(iterator_.Name());
1661 size_t length(strlen(name));
1662 if (length < 2) invalid:
1663 [warnings addObject:@"illegal package identifier"];
1664 else for (size_t i(0); i != length; ++i)
1666 (name[i] < 'a' || name[i] > 'z') &&
1667 (name[i] < '0' || name[i] > '9') &&
1668 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1671 if (strcmp(name, "cydia") != 0) {
1675 if (NSArray *files = [self files])
1676 for (NSString *file in files)
1677 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1679 else if (!stash && [file isEqualToString:@"/var/stash"])
1683 [warnings addObject:@"files installed into Cydia.app"];
1685 [warnings addObject:@"files installed to /var/stash"];
1688 return [warnings count] == 0 ? nil : warnings;
1691 - (NSArray *) applications {
1692 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1694 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1696 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1697 if (NSArray *files = [self files])
1698 for (NSString *file in files)
1699 if (application_r(file)) {
1700 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1701 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1702 if ([id isEqualToString:me])
1705 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1707 display = application_r[1];
1709 NSString *bundle([file stringByDeletingLastPathComponent]);
1710 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1711 if (icon == nil || [icon length] == 0)
1713 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1715 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1716 [applications addObject:application];
1718 [application addObject:id];
1719 [application addObject:display];
1720 [application addObject:url];
1723 return [applications count] == 0 ? nil : applications;
1726 - (Source *) source {
1728 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1735 - (NSString *) role {
1739 - (BOOL) matches:(NSString *)text {
1745 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1746 if (range.location != NSNotFound)
1749 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1750 if (range.location != NSNotFound)
1753 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1754 if (range.location != NSNotFound)
1760 - (bool) hasSupportingRole {
1763 if ([role_ isEqualToString:@"enduser"])
1765 if ([Role_ isEqualToString:@"User"])
1767 if ([role_ isEqualToString:@"hacker"])
1769 if ([Role_ isEqualToString:@"Hacker"])
1771 if ([role_ isEqualToString:@"developer"])
1773 if ([Role_ isEqualToString:@"Developer"])
1778 - (BOOL) hasTag:(NSString *)tag {
1779 return tags_ == nil ? NO : [tags_ containsObject:tag];
1782 - (NSString *) primaryPurpose {
1783 for (NSString *tag in tags_)
1784 if ([tag hasPrefix:@"purpose::"])
1785 return [tag substringFromIndex:9];
1789 - (NSArray *) purposes {
1790 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1791 for (NSString *tag in tags_)
1792 if ([tag hasPrefix:@"purpose::"])
1793 [purposes addObject:[tag substringFromIndex:9]];
1794 return [purposes count] == 0 ? nil : purposes;
1797 - (NSComparisonResult) compareByName:(Package *)package {
1798 NSString *lhs = [self name];
1799 NSString *rhs = [package name];
1801 if ([lhs length] != 0 && [rhs length] != 0) {
1802 unichar lhc = [lhs characterAtIndex:0];
1803 unichar rhc = [rhs characterAtIndex:0];
1805 if (isalpha(lhc) && !isalpha(rhc))
1806 return NSOrderedAscending;
1807 else if (!isalpha(lhc) && isalpha(rhc))
1808 return NSOrderedDescending;
1811 return [lhs compare:rhs options:CompareOptions_];
1814 - (NSComparisonResult) compareBySection:(Package *)package {
1815 NSString *lhs = [self section];
1816 NSString *rhs = [package section];
1818 if (lhs == NULL && rhs != NULL)
1819 return NSOrderedAscending;
1820 else if (lhs != NULL && rhs == NULL)
1821 return NSOrderedDescending;
1822 else if (lhs != NULL && rhs != NULL) {
1823 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1824 if (result != NSOrderedSame)
1828 return NSOrderedSame;
1831 - (uint32_t) compareForChanges {
1836 uint32_t timestamp : 30;
1837 uint32_t ignored : 1;
1838 uint32_t upgradable : 1;
1842 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1844 if ([self upgradableAndEssential:YES]) {
1845 value.bits.timestamp = 0;
1846 value.bits.ignored = [self ignored] ? 0 : 1;
1847 value.bits.upgradable = 1;
1849 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1850 value.bits.ignored = 0;
1851 value.bits.upgradable = 0;
1854 return _not(uint32_t) - value.key;
1858 pkgProblemResolver *resolver = [database_ resolver];
1859 resolver->Clear(iterator_);
1860 resolver->Protect(iterator_);
1861 pkgCacheFile &cache([database_ cache]);
1862 cache->MarkInstall(iterator_, false);
1863 pkgDepCache::StateCache &state((*cache)[iterator_]);
1864 if (!state.Install())
1865 cache->SetReInstall(iterator_, true);
1869 pkgProblemResolver *resolver = [database_ resolver];
1870 resolver->Clear(iterator_);
1871 resolver->Protect(iterator_);
1872 resolver->Remove(iterator_);
1873 [database_ cache]->MarkDelete(iterator_, true);
1876 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1877 return [NSNumber numberWithBool:(
1878 [self unfiltered] && [self matches:search]
1882 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1883 return [NSNumber numberWithBool:(
1884 (![number boolValue] || [self visible]) && [self installed] != nil
1888 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1889 NSString *section = [self section];
1891 return [NSNumber numberWithBool:(
1893 [self installed] == nil && (
1895 section == nil && [name length] == 0 ||
1896 [name isEqualToString:section]
1901 - (NSNumber *) isVisibleInSource:(Source *)source {
1902 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1907 /* Section Class {{{ */
1908 @interface Section : NSObject {
1914 - (NSComparisonResult) compareByName:(Section *)section;
1915 - (Section *) initWithName:(NSString *)name;
1916 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1917 - (NSString *) name;
1920 - (void) addToCount;
1924 @implementation Section
1931 - (NSComparisonResult) compareByName:(Section *)section {
1932 NSString *lhs = [self name];
1933 NSString *rhs = [section name];
1935 if ([lhs length] != 0 && [rhs length] != 0) {
1936 unichar lhc = [lhs characterAtIndex:0];
1937 unichar rhc = [rhs characterAtIndex:0];
1939 if (isalpha(lhc) && !isalpha(rhc))
1940 return NSOrderedAscending;
1941 else if (!isalpha(lhc) && isalpha(rhc))
1942 return NSOrderedDescending;
1945 return [lhs compare:rhs options:CompareOptions_];
1948 - (Section *) initWithName:(NSString *)name {
1949 return [self initWithName:name row:0];
1952 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1953 if ((self = [super init]) != nil) {
1954 name_ = [name retain];
1959 - (NSString *) name {
1971 - (void) addToCount {
1979 static NSArray *Finishes_;
1981 /* Database Implementation {{{ */
1982 @implementation Database
1984 + (Database *) sharedInstance {
1985 static Database *instance;
1986 if (instance == nil)
1987 instance = [[Database alloc] init];
1996 - (void) _readCydia:(NSNumber *)fd { _pooled
1997 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1998 std::istream is(&ib);
2001 static Pcre finish_r("^finish:([^:]*)$");
2003 while (std::getline(is, line)) {
2004 const char *data(line.c_str());
2005 size_t size = line.size();
2006 lprintf("C:%s\n", data);
2008 if (finish_r(data, size)) {
2009 NSString *finish = finish_r[1];
2010 int index = [Finishes_ indexOfObject:finish];
2011 if (index != INT_MAX && index > Finish_)
2019 - (void) _readStatus:(NSNumber *)fd { _pooled
2020 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2021 std::istream is(&ib);
2024 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2025 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2027 while (std::getline(is, line)) {
2028 const char *data(line.c_str());
2029 size_t size = line.size();
2030 lprintf("S:%s\n", data);
2032 if (conffile_r(data, size)) {
2033 [delegate_ setConfigurationData:conffile_r[1]];
2034 } else if (strncmp(data, "status: ", 8) == 0) {
2035 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2036 [delegate_ setProgressTitle:string];
2037 } else if (pmstatus_r(data, size)) {
2038 std::string type([pmstatus_r[1] UTF8String]);
2039 NSString *id = pmstatus_r[2];
2041 float percent([pmstatus_r[3] floatValue]);
2042 [delegate_ setProgressPercent:(percent / 100)];
2044 NSString *string = pmstatus_r[4];
2046 if (type == "pmerror")
2047 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2048 withObject:[NSArray arrayWithObjects:string, id, nil]
2051 else if (type == "pmstatus")
2052 [delegate_ setProgressTitle:string];
2053 else if (type == "pmconffile")
2054 [delegate_ setConfigurationData:string];
2055 else _assert(false);
2056 } else _assert(false);
2062 - (void) _readOutput:(NSNumber *)fd { _pooled
2063 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2064 std::istream is(&ib);
2067 while (std::getline(is, line)) {
2068 lprintf("O:%s\n", line.c_str());
2069 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2079 - (Package *) packageWithName:(NSString *)name {
2080 if (static_cast<pkgDepCache *>(cache_) == NULL)
2082 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2083 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2086 - (Database *) init {
2087 if ((self = [super init]) != nil) {
2094 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2095 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2099 _assert(pipe(fds) != -1);
2102 _config->Set("APT::Keep-Fds::", cydiafd_);
2103 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2106 detachNewThreadSelector:@selector(_readCydia:)
2108 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2111 _assert(pipe(fds) != -1);
2115 detachNewThreadSelector:@selector(_readStatus:)
2117 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2120 _assert(pipe(fds) != -1);
2121 _assert(dup2(fds[0], 0) != -1);
2122 _assert(close(fds[0]) != -1);
2124 input_ = fdopen(fds[1], "a");
2126 _assert(pipe(fds) != -1);
2127 _assert(dup2(fds[1], 1) != -1);
2128 _assert(close(fds[1]) != -1);
2131 detachNewThreadSelector:@selector(_readOutput:)
2133 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2138 - (pkgCacheFile &) cache {
2142 - (pkgDepCache::Policy *) policy {
2146 - (pkgRecords *) records {
2150 - (pkgProblemResolver *) resolver {
2154 - (pkgAcquire &) fetcher {
2158 - (NSArray *) packages {
2162 - (NSArray *) sources {
2163 return [sources_ allValues];
2166 - (NSArray *) issues {
2167 if (cache_->BrokenCount() == 0)
2170 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2172 for (Package *package in packages_) {
2173 if (![package broken])
2175 pkgCache::PkgIterator pkg([package iterator]);
2177 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2178 [entry addObject:[package name]];
2179 [issues addObject:entry];
2181 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2185 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2186 pkgCache::DepIterator start;
2187 pkgCache::DepIterator end;
2188 dep.GlobOr(start, end); // ++dep
2190 if (!cache_->IsImportantDep(end))
2192 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2195 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2196 [entry addObject:failure];
2197 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2199 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2200 [failure addObject:[package name]];
2202 pkgCache::PkgIterator target(start.TargetPkg());
2203 if (target->ProvidesList != 0)
2204 [failure addObject:@"?"];
2206 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2208 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2209 else if (!cache_[target].CandidateVerIter(cache_).end())
2210 [failure addObject:@"-"];
2211 else if (target->ProvidesList == 0)
2212 [failure addObject:@"!"];
2214 [failure addObject:@"%"];
2218 if (start.TargetVer() != 0)
2219 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2230 - (void) reloadData {
2249 if (!cache_.Open(progress_, true)) {
2251 if (!_error->PopMessage(error))
2254 lprintf("cache_.Open():[%s]\n", error.c_str());
2256 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2257 [delegate_ repairWithSelector:@selector(configure)];
2258 else if (error == "The package lists or status file could not be parsed or opened.")
2259 [delegate_ repairWithSelector:@selector(update)];
2260 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2261 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2262 // else if (error == "The list of sources could not be read.")
2263 else _assert(false);
2268 now_ = [[NSDate date] retain];
2270 policy_ = new pkgDepCache::Policy();
2271 records_ = new pkgRecords(cache_);
2272 resolver_ = new pkgProblemResolver(cache_);
2273 fetcher_ = new pkgAcquire(&status_);
2276 list_ = new pkgSourceList();
2277 _assert(list_->ReadMainList());
2279 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2280 _assert(pkgApplyStatus(cache_));
2282 if (cache_->BrokenCount() != 0) {
2283 _assert(pkgFixBroken(cache_));
2284 _assert(cache_->BrokenCount() == 0);
2285 _assert(pkgMinimizeUpgrade(cache_));
2288 [sources_ removeAllObjects];
2289 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2290 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2291 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2293 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2294 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2298 [packages_ removeAllObjects];
2299 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2300 if (Package *package = [Package packageWithIterator:iterator database:self])
2301 [packages_ addObject:package];
2303 [packages_ sortUsingSelector:@selector(compareByName:)];
2306 - (void) configure {
2307 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2308 system([dpkg UTF8String]);
2316 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2317 _assert(!_error->PendingError());
2320 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2323 public pkgArchiveCleaner
2326 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2331 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2333 while (_error->PopMessage(error))
2334 lprintf("ArchiveCleaner: %s\n", error.c_str());
2339 pkgRecords records(cache_);
2341 lock_ = new FileFd();
2342 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2343 _assert(!_error->PendingError());
2346 // XXX: explain this with an error message
2347 _assert(list.ReadMainList());
2349 manager_ = (_system->CreatePM(cache_));
2350 _assert(manager_->GetArchives(fetcher_, &list, &records));
2351 _assert(!_error->PendingError());
2355 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2357 _assert(list.ReadMainList());
2358 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2359 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2362 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2367 bool failed = false;
2368 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2369 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2372 std::string uri = (*item)->DescURI();
2373 std::string error = (*item)->ErrorText;
2375 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2378 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2379 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2390 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2392 if (_error->PendingError()) {
2397 if (result == pkgPackageManager::Failed) {
2402 if (result != pkgPackageManager::Completed) {
2407 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2409 _assert(list.ReadMainList());
2410 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2411 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2414 if (![before isEqualToArray:after])
2419 _assert(pkgDistUpgrade(cache_));
2423 [self updateWithStatus:status_];
2426 - (void) updateWithStatus:(Status &)status {
2428 _assert(list.ReadMainList());
2431 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2432 _assert(!_error->PendingError());
2434 pkgAcquire fetcher(&status);
2435 _assert(list.GetIndexes(&fetcher));
2437 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2438 bool failed = false;
2439 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2440 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2441 (*item)->Finished();
2445 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2446 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2447 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2450 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2455 - (void) setDelegate:(id)delegate {
2456 delegate_ = delegate;
2457 status_.setDelegate(delegate);
2458 progress_.setDelegate(delegate);
2461 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2462 pkgIndexFile *index(NULL);
2463 list_->FindIndex(file, index);
2464 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2470 /* PopUp Windows {{{ */
2471 @interface PopUpView : UIView {
2472 _transient id delegate_;
2473 UITransitionView *transition_;
2478 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2482 @implementation PopUpView
2485 [transition_ setDelegate:nil];
2486 [transition_ release];
2492 [transition_ transition:UITransitionPushFromTop toView:nil];
2495 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2496 if (from != nil && to == nil)
2497 [self removeFromSuperview];
2500 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2501 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2502 delegate_ = delegate;
2504 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2505 [self addSubview:transition_];
2507 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2509 [view addSubview:self];
2511 [transition_ setDelegate:self];
2513 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2514 [transition_ transition:UITransitionNone toView:blank];
2515 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2522 /* Mail Composition {{{ */
2523 @interface MailToView : PopUpView {
2524 MailComposeController *controller_;
2527 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2531 @implementation MailToView
2534 [controller_ release];
2538 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2542 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2543 NSLog(@"did:%@", delivery);
2544 // [UIApp setStatusBarShowsProgress:NO];
2545 if ([controller error]){
2546 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2547 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2548 [mailAlertSheet setBodyText:[controller error]];
2549 [mailAlertSheet popupAlertAnimated:YES];
2553 - (void) showError {
2554 NSLog(@"%@", [controller_ error]);
2555 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2556 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2557 [mailAlertSheet setBodyText:[controller_ error]];
2558 [mailAlertSheet popupAlertAnimated:YES];
2561 - (void) deliverMessage { _pooled
2565 if (![controller_ deliverMessage])
2566 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2569 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2570 if ([controller_ needsDelivery])
2571 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2576 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2577 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2578 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2579 [controller_ setDelegate:self];
2580 [controller_ initializeUI];
2581 [controller_ setupForURL:url];
2583 UIView *view([controller_ view]);
2584 [overlay_ addSubview:view];
2590 /* Confirmation View {{{ */
2591 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2592 if (!iterator.end())
2593 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2594 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2596 pkgCache::PkgIterator package(dep.TargetPkg());
2599 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2606 @protocol ConfirmationViewDelegate
2611 @interface ConfirmationView : BrowserView {
2612 _transient Database *database_;
2613 UIActionSheet *essential_;
2620 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2624 @implementation ConfirmationView
2631 if (essential_ != nil)
2632 [essential_ release];
2638 [book_ popFromSuperviewAnimated:YES];
2641 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2642 NSString *context = [sheet context];
2644 if ([context isEqualToString:@"remove"])
2652 [delegate_ confirm];
2657 else if ([context isEqualToString:@"unable"])
2663 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2664 [window setValue:changes_ forKey:@"changes"];
2665 [window setValue:issues_ forKey:@"issues"];
2666 [window setValue:sizes_ forKey:@"sizes"];
2667 [super webView:sender didClearWindowObject:window forFrame:frame];
2670 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2671 if ((self = [super initWithBook:book]) != nil) {
2672 database_ = database;
2674 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2675 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2676 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2677 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2678 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2682 pkgDepCache::Policy *policy([database_ policy]);
2684 pkgCacheFile &cache([database_ cache]);
2685 NSArray *packages = [database_ packages];
2686 for (size_t i(0), e = [packages count]; i != e; ++i) {
2687 Package *package = [packages objectAtIndex:i];
2688 pkgCache::PkgIterator iterator = [package iterator];
2689 pkgDepCache::StateCache &state(cache[iterator]);
2691 NSString *name([package name]);
2693 if (state.NewInstall())
2694 [installing addObject:name];
2695 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2696 [reinstalling addObject:name];
2697 else if (state.Upgrade())
2698 [upgrading addObject:name];
2699 else if (state.Downgrade())
2700 [downgrading addObject:name];
2701 else if (state.Delete()) {
2702 if ([package essential])
2704 [removing addObject:name];
2707 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2708 substrate_ |= DepSubstrate(iterator.CurrentVer());
2713 else if (Advanced_ || true) {
2714 essential_ = [[UIActionSheet alloc]
2715 initWithTitle:@"Removing Essentials"
2716 buttons:[NSArray arrayWithObjects:
2717 @"Cancel Operation (Safe)",
2718 @"Force Removal (Unsafe)",
2720 defaultButtonIndex:0
2726 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2728 [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."];
2730 essential_ = [[UIActionSheet alloc]
2731 initWithTitle:@"Unable to Comply"
2732 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2733 defaultButtonIndex:0
2738 [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."];
2741 changes_ = [[NSArray alloc] initWithObjects:
2749 issues_ = [database_ issues];
2751 issues_ = [issues_ retain];
2753 sizes_ = [[NSArray alloc] initWithObjects:
2754 SizeString([database_ fetcher].FetchNeeded()),
2755 SizeString([database_ fetcher].PartialPresent()),
2756 SizeString([database_ cache]->UsrSize()),
2759 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2763 - (NSString *) backButtonTitle {
2767 - (NSString *) leftButtonTitle {
2771 - (NSString *) _rightButtonTitle {
2772 return issues_ == nil ? @"Confirm" : nil;
2775 - (void) _leftButtonClicked {
2779 - (void) _rightButtonClicked {
2780 if (essential_ != nil)
2781 [essential_ popupAlertAnimated:YES];
2785 [delegate_ confirm];
2792 /* Progress Data {{{ */
2793 @interface ProgressData : NSObject {
2799 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2806 @implementation ProgressData
2808 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2809 if ((self = [super init]) != nil) {
2810 selector_ = selector;
2830 /* Progress View {{{ */
2831 @interface ProgressView : UIView <
2832 ConfigurationDelegate,
2835 _transient Database *database_;
2837 UIView *background_;
2838 UITransitionView *transition_;
2840 UINavigationBar *navbar_;
2841 UIProgressBar *progress_;
2842 UITextView *output_;
2843 UITextLabel *status_;
2844 UIPushButton *close_;
2847 SHA1SumValue springlist_;
2848 SHA1SumValue sandplate_;
2850 NSTimeInterval last_;
2853 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2855 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2856 - (void) setContentView:(UIView *)view;
2859 - (void) _retachThread;
2860 - (void) _detachNewThreadData:(ProgressData *)data;
2861 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2867 @protocol ProgressViewDelegate
2868 - (void) progressViewIsComplete:(ProgressView *)sender;
2871 @implementation ProgressView
2874 [transition_ setDelegate:nil];
2875 [navbar_ setDelegate:nil];
2878 if (background_ != nil)
2879 [background_ release];
2880 [transition_ release];
2883 [progress_ release];
2890 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2891 if (bootstrap_ && from == overlay_ && to == view_)
2895 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2896 if ((self = [super initWithFrame:frame]) != nil) {
2897 database_ = database;
2898 delegate_ = delegate;
2900 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2901 [transition_ setDelegate:self];
2903 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2906 [overlay_ setBackgroundColor:[UIColor blackColor]];
2908 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2909 [background_ setBackgroundColor:[UIColor blackColor]];
2910 [self addSubview:background_];
2913 [self addSubview:transition_];
2915 CGSize navsize = [UINavigationBar defaultSize];
2916 CGRect navrect = {{0, 0}, navsize};
2918 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2919 [overlay_ addSubview:navbar_];
2921 [navbar_ setBarStyle:1];
2922 [navbar_ setDelegate:self];
2924 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2925 [navbar_ pushNavigationItem:navitem];
2927 CGRect bounds = [overlay_ bounds];
2928 CGSize prgsize = [UIProgressBar defaultSize];
2931 (bounds.size.width - prgsize.width) / 2,
2932 bounds.size.height - prgsize.height - 20
2935 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2936 [progress_ setStyle:0];
2938 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2940 bounds.size.height - prgsize.height - 50,
2941 bounds.size.width - 20,
2945 [status_ setColor:[UIColor whiteColor]];
2946 [status_ setBackgroundColor:[UIColor clearColor]];
2948 [status_ setCentersHorizontally:YES];
2949 //[status_ setFont:font];
2951 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2953 navrect.size.height + 20,
2954 bounds.size.width - 20,
2955 bounds.size.height - navsize.height - 62 - navrect.size.height
2958 //[output_ setTextFont:@"Courier New"];
2959 [output_ setTextSize:12];
2961 [output_ setTextColor:[UIColor whiteColor]];
2962 [output_ setBackgroundColor:[UIColor clearColor]];
2964 [output_ setMarginTop:0];
2965 [output_ setAllowsRubberBanding:YES];
2966 [output_ setEditable:NO];
2968 [overlay_ addSubview:output_];
2970 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2972 bounds.size.height - prgsize.height - 50,
2973 bounds.size.width - 20,
2977 [close_ setAutosizesToFit:NO];
2978 [close_ setDrawsShadow:YES];
2979 [close_ setStretchBackground:YES];
2980 [close_ setEnabled:YES];
2982 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2983 [close_ setTitleFont:bold];
2985 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2986 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2987 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2991 - (void) setContentView:(UIView *)view {
2992 view_ = [view retain];
2995 - (void) resetView {
2996 [transition_ transition:6 toView:view_];
2999 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3000 NSString *context = [sheet context];
3001 if ([context isEqualToString:@"conffile"]) {
3002 FILE *input = [database_ input];
3006 fprintf(input, "N\n");
3010 fprintf(input, "Y\n");
3021 - (void) closeButtonPushed {
3030 [delegate_ suspendWithAnimation:YES];
3034 system("launchctl stop com.apple.SpringBoard");
3038 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3047 - (void) _retachThread {
3048 UINavigationItem *item = [navbar_ topItem];
3049 [item setTitle:@"Complete"];
3051 [overlay_ addSubview:close_];
3052 [progress_ removeFromSuperview];
3053 [status_ removeFromSuperview];
3055 [delegate_ progressViewIsComplete:self];
3058 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3059 MMap mmap(file, MMap::ReadOnly);
3061 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3062 if (!(sandplate_ == sha1.Result()))
3067 FileFd file(SpringBoard_, FileFd::ReadOnly);
3068 MMap mmap(file, MMap::ReadOnly);
3070 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3071 if (!(springlist_ == sha1.Result()))
3076 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3077 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3078 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3079 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3080 case 4: [close_ setTitle:@"Reboot Device"]; break;
3083 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3085 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3086 [cache autorelease];
3088 NSFileManager *manager = [NSFileManager defaultManager];
3089 NSError *error = nil;
3091 id system = [cache objectForKey:@"System"];
3096 if (stat(Cache_, &info) == -1)
3099 [system removeAllObjects];
3101 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3102 for (NSString *app in apps)
3103 if ([app hasSuffix:@".app"]) {
3104 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3105 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3106 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3108 [info setObject:path forKey:@"Path"];
3109 [info setObject:@"System" forKey:@"ApplicationType"];
3110 [system addInfoDictionary:info];
3115 [cache writeToFile:@Cache_ atomically:YES];
3117 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3119 if (chmod(Cache_, info.st_mode) == -1)
3123 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3126 notify_post("com.apple.mobile.application_installed");
3128 [delegate_ setStatusBarShowsProgress:NO];
3131 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3132 [[data target] performSelector:[data selector] withObject:[data object]];
3135 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3138 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3139 UINavigationItem *item = [navbar_ topItem];
3140 [item setTitle:title];
3142 [status_ setText:nil];
3143 [output_ setText:@""];
3144 [progress_ setProgress:0];
3147 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3149 [close_ removeFromSuperview];
3150 [overlay_ addSubview:progress_];
3151 [overlay_ addSubview:status_];
3153 [delegate_ setStatusBarShowsProgress:YES];
3157 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3158 MMap mmap(file, MMap::ReadOnly);
3160 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3161 sandplate_ = sha1.Result();
3165 FileFd file(SpringBoard_, FileFd::ReadOnly);
3166 MMap mmap(file, MMap::ReadOnly);
3168 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3169 springlist_ = sha1.Result();
3172 [transition_ transition:6 toView:overlay_];
3175 detachNewThreadSelector:@selector(_detachNewThreadData:)
3177 withObject:[[ProgressData alloc]
3178 initWithSelector:selector
3185 - (void) repairWithSelector:(SEL)selector {
3187 detachNewThreadSelector:selector
3194 - (void) setConfigurationData:(NSString *)data {
3196 performSelectorOnMainThread:@selector(_setConfigurationData:)
3202 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3203 Package *package = id == nil ? nil : [database_ packageWithName:id];
3205 UIActionSheet *sheet = [[[UIActionSheet alloc]
3206 initWithTitle:(package == nil ? id : [package name])
3207 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3208 defaultButtonIndex:0
3213 [sheet setBodyText:error];
3214 [sheet popupAlertAnimated:YES];
3217 - (void) setProgressTitle:(NSString *)title {
3219 performSelectorOnMainThread:@selector(_setProgressTitle:)
3225 - (void) setProgressPercent:(float)percent {
3227 performSelectorOnMainThread:@selector(_setProgressPercent:)
3228 withObject:[NSNumber numberWithFloat:percent]
3233 - (void) startProgress {
3234 last_ = [NSDate timeIntervalSinceReferenceDate];
3237 - (void) addProgressOutput:(NSString *)output {
3239 performSelectorOnMainThread:@selector(_addProgressOutput:)
3245 - (bool) isCancelling:(size_t)received {
3247 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3248 if (received_ != received) {
3249 received_ = received;
3251 } else if (now - last_ > 30)
3258 - (void) _setConfigurationData:(NSString *)data {
3259 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3261 _assert(conffile_r(data));
3263 NSString *ofile = conffile_r[1];
3264 //NSString *nfile = conffile_r[2];
3266 UIActionSheet *sheet = [[[UIActionSheet alloc]
3267 initWithTitle:@"Configuration Upgrade"
3268 buttons:[NSArray arrayWithObjects:
3269 @"Keep My Old Copy",
3270 @"Accept The New Copy",
3271 // XXX: @"See What Changed",
3273 defaultButtonIndex:0
3278 [sheet setBodyText:[NSString stringWithFormat:
3279 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3282 [sheet popupAlertAnimated:YES];
3285 - (void) _setProgressTitle:(NSString *)title {
3286 [status_ setText:title];
3289 - (void) _setProgressPercent:(NSNumber *)percent {
3290 [progress_ setProgress:[percent floatValue]];
3293 - (void) _addProgressOutput:(NSString *)output {
3294 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3295 CGSize size = [output_ contentSize];
3296 CGRect rect = {{0, size.height}, {size.width, 0}};
3297 [output_ scrollRectToVisible:rect animated:YES];
3300 - (BOOL) isRunning {
3307 /* Package Cell {{{ */
3308 @interface PackageCell : UISimpleTableCell {
3311 NSString *description_;
3315 UITextLabel *status_;
3319 - (PackageCell *) init;
3320 - (void) setPackage:(Package *)package;
3322 + (int) heightForPackage:(Package *)package;
3326 @implementation PackageCell
3328 - (void) clearPackage {
3339 if (description_ != nil) {
3340 [description_ release];
3344 if (source_ != nil) {
3349 if (badge_ != nil) {
3356 [self clearPackage];
3363 - (PackageCell *) init {
3364 if ((self = [super init]) != nil) {
3366 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3367 [status_ setBackgroundColor:[UIColor clearColor]];
3368 [status_ setFont:small];
3373 - (void) setPackage:(Package *)package {
3374 [self clearPackage];
3376 Source *source = [package source];
3377 NSString *section = [package simpleSection];
3379 icon_ = [[package icon] retain];
3381 name_ = [[package name] retain];
3382 description_ = [[package tagline] retain];
3384 NSString *label = nil;
3385 bool trusted = false;
3387 if (source != nil) {
3388 label = [source label];
3389 trusted = [source trusted];
3390 } else if ([[package id] isEqualToString:@"firmware"])
3393 label = @"Unknown/Local";
3395 NSString *from = [NSString stringWithFormat:@"from %@", label];
3397 if (section != nil && ![section isEqualToString:label])
3398 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3400 source_ = [from retain];
3402 if (NSString *purpose = [package primaryPurpose])
3403 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3404 badge_ = [badge_ retain];
3407 if (NSString *mode = [package mode]) {
3408 [badge_ setImage:[UIImage applicationImageNamed:
3409 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3412 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3413 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3414 } else if ([package half]) {
3415 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3416 [status_ setText:@"Package Damaged"];
3417 [status_ setColor:[UIColor redColor]];
3419 [badge_ setImage:nil];
3420 [status_ setText:nil];
3425 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3428 rect.size = [icon_ size];
3430 rect.size.width /= 2;
3431 rect.size.height /= 2;
3433 rect.origin.x = 25 - rect.size.width / 2;
3434 rect.origin.y = 25 - rect.size.height / 2;
3436 [icon_ drawInRect:rect];
3439 if (badge_ != nil) {
3440 CGSize size = [badge_ size];
3442 [badge_ drawAtPoint:CGPointMake(
3443 36 - size.width / 2,
3444 36 - size.height / 2
3453 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3454 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3458 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3460 [super drawContentInRect:rect selected:selected];
3463 + (int) heightForPackage:(Package *)package {
3464 NSString *tagline([package tagline]);
3465 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3467 if ([package hasMode] || [package half])
3476 /* Section Cell {{{ */
3477 @interface SectionCell : UISimpleTableCell {
3482 _UISwitchSlider *switch_;
3487 - (void) setSection:(Section *)section editing:(BOOL)editing;
3491 @implementation SectionCell
3493 - (void) clearSection {
3494 if (section_ != nil) {
3504 if (count_ != nil) {
3511 [self clearSection];
3518 if ((self = [super init]) != nil) {
3519 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3521 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3522 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3526 - (void) onSwitch:(id)sender {
3527 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3528 if (metadata == nil) {
3529 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3530 [Sections_ setObject:metadata forKey:section_];
3534 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3537 - (void) setSection:(Section *)section editing:(BOOL)editing {
3538 if (editing != editing_) {
3540 [switch_ removeFromSuperview];
3542 [self addSubview:switch_];
3546 [self clearSection];
3548 if (section == nil) {
3549 name_ = [@"All Packages" retain];
3552 section_ = [section name];
3553 if (section_ != nil)
3554 section_ = [section_ retain];
3555 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3556 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3559 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3563 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3564 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3571 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3573 CGSize size = [count_ sizeWithFont:Font14_];
3577 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3579 [super drawContentInRect:rect selected:selected];
3585 /* File Table {{{ */
3586 @interface FileTable : RVPage {
3587 _transient Database *database_;
3590 NSMutableArray *files_;
3594 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3595 - (void) setPackage:(Package *)package;
3599 @implementation FileTable
3602 if (package_ != nil)
3611 - (int) numberOfRowsInTable:(UITable *)table {
3612 return files_ == nil ? 0 : [files_ count];
3615 - (float) table:(UITable *)table heightForRow:(int)row {
3619 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3620 if (reusing == nil) {
3621 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3622 UIFont *font = [UIFont systemFontOfSize:16];
3623 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3625 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3629 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3633 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3634 if ((self = [super initWithBook:book]) != nil) {
3635 database_ = database;
3637 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3639 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3640 [self addSubview:list_];
3642 UITableColumn *column = [[[UITableColumn alloc]
3643 initWithTitle:@"Name"
3645 width:[self frame].size.width
3648 [list_ setDataSource:self];
3649 [list_ setSeparatorStyle:1];
3650 [list_ addTableColumn:column];
3651 [list_ setDelegate:self];
3652 [list_ setReusesTableCells:YES];
3656 - (void) setPackage:(Package *)package {
3657 if (package_ != nil) {
3658 [package_ autorelease];
3667 [files_ removeAllObjects];
3669 if (package != nil) {
3670 package_ = [package retain];
3671 name_ = [[package id] retain];
3673 if (NSArray *files = [package files])
3674 [files_ addObjectsFromArray:files];
3676 if ([files_ count] != 0) {
3677 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3678 [files_ removeObjectAtIndex:0];
3679 [files_ sortUsingSelector:@selector(compareByPath:)];
3681 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3682 [stack addObject:@"/"];
3684 for (int i(0), e([files_ count]); i != e; ++i) {
3685 NSString *file = [files_ objectAtIndex:i];
3686 while (![file hasPrefix:[stack lastObject]])
3687 [stack removeLastObject];
3688 NSString *directory = [stack lastObject];
3689 [stack addObject:[file stringByAppendingString:@"/"]];
3690 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3691 ([stack count] - 2) * 3, "",
3692 [file substringFromIndex:[directory length]]
3701 - (void) resetViewAnimated:(BOOL)animated {
3702 [list_ resetViewAnimated:animated];
3705 - (void) reloadData {
3706 [self setPackage:[database_ packageWithName:name_]];
3707 [self reloadButtons];
3710 - (NSString *) title {
3711 return @"Installed Files";
3714 - (NSString *) backButtonTitle {
3720 /* Package View {{{ */
3721 @interface PackageView : BrowserView {
3722 _transient Database *database_;
3725 NSMutableArray *buttons_;
3728 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3729 - (void) setPackage:(Package *)package;
3733 @implementation PackageView
3736 if (package_ != nil)
3744 - (void) _clickButtonWithName:(NSString *)name {
3745 if ([name isEqualToString:@"Install"])
3746 [delegate_ installPackage:package_];
3747 else if ([name isEqualToString:@"Reinstall"])
3748 [delegate_ installPackage:package_];
3749 else if ([name isEqualToString:@"Remove"])
3750 [delegate_ removePackage:package_];
3751 else if ([name isEqualToString:@"Upgrade"])
3752 [delegate_ installPackage:package_];
3753 else _assert(false);
3756 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3757 int count = [buttons_ count];
3758 _assert(count != 0);
3759 _assert(button <= count + 1);
3761 if (count != button - 1)
3762 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3767 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3768 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3769 return [super webView:sender didFinishLoadForFrame:frame];
3772 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3773 [window setValue:package_ forKey:@"package"];
3774 [super webView:sender didClearWindowObject:window forFrame:frame];
3777 - (void) _rightButtonClicked {
3778 /*[super _rightButtonClicked];
3781 int count = [buttons_ count];
3782 _assert(count != 0);
3785 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3787 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3788 [buttons addObjectsFromArray:buttons_];
3789 [buttons addObject:@"Cancel"];
3791 [delegate_ slideUp:[[[UIActionSheet alloc]
3794 defaultButtonIndex:2
3801 - (NSString *) _rightButtonTitle {
3802 int count = [buttons_ count];
3803 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3806 - (NSString *) backButtonTitle {
3810 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3811 if ((self = [super initWithBook:book]) != nil) {
3812 database_ = database;
3813 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3817 - (void) setPackage:(Package *)package {
3818 if (package_ != nil) {
3819 [package_ autorelease];
3828 [buttons_ removeAllObjects];
3830 if (package != nil) {
3831 package_ = [package retain];
3832 name_ = [[package id] retain];
3834 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3836 if ([package_ source] == nil);
3837 else if ([package_ upgradableAndEssential:NO])
3838 [buttons_ addObject:@"Upgrade"];
3839 else if ([package_ installed] == nil)
3840 [buttons_ addObject:@"Install"];
3842 [buttons_ addObject:@"Reinstall"];
3843 if ([package_ installed] != nil)
3844 [buttons_ addObject:@"Remove"];
3848 - (void) reloadData {
3849 [self setPackage:[database_ packageWithName:name_]];
3850 [self reloadButtons];
3855 /* Package Table {{{ */
3856 @interface PackageTable : RVPage {
3857 _transient Database *database_;
3861 NSMutableArray *packages_;
3862 NSMutableArray *sections_;
3863 UISectionList *list_;
3866 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3868 - (void) setDelegate:(id)delegate;
3869 - (void) setObject:(id)object;
3871 - (void) reloadData;
3872 - (void) resetCursor;
3874 - (UISectionList *) list;
3876 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3880 @implementation PackageTable
3883 [list_ setDataSource:nil];
3888 [packages_ release];
3889 [sections_ release];
3894 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3895 return [sections_ count];
3898 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3899 return [[sections_ objectAtIndex:section] name];
3902 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3903 return [[sections_ objectAtIndex:section] row];
3906 - (int) numberOfRowsInTable:(UITable *)table {
3907 return [packages_ count];
3910 - (float) table:(UITable *)table heightForRow:(int)row {
3911 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3914 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3916 reusing = [[[PackageCell alloc] init] autorelease];
3917 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3921 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3925 - (void) tableRowSelected:(NSNotification *)notification {
3926 int row = [[notification object] selectedRow];
3930 Package *package = [packages_ objectAtIndex:row];
3931 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3932 [view setDelegate:delegate_];
3933 [view setPackage:package];
3934 [book_ pushPage:view];
3937 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3938 if ((self = [super initWithBook:book]) != nil) {
3939 database_ = database;
3940 title_ = [title retain];
3942 object_ = object == nil ? nil : [object retain];
3944 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3945 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3947 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3948 [list_ setDataSource:self];
3950 UITableColumn *column = [[[UITableColumn alloc]
3951 initWithTitle:@"Name"
3953 width:[self frame].size.width
3956 UITable *table = [list_ table];
3957 [table setSeparatorStyle:1];
3958 [table addTableColumn:column];
3959 [table setDelegate:self];
3960 [table setReusesTableCells:YES];
3962 [self addSubview:list_];
3965 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3966 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3970 - (void) setDelegate:(id)delegate {
3971 delegate_ = delegate;
3974 - (void) setObject:(id)object {
3980 object_ = [object retain];
3983 - (void) reloadData {
3984 NSArray *packages = [database_ packages];
3986 [packages_ removeAllObjects];
3987 [sections_ removeAllObjects];
3989 for (size_t i(0); i != [packages count]; ++i) {
3990 Package *package([packages objectAtIndex:i]);
3991 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3992 [packages_ addObject:package];
3995 Section *section = nil;
3997 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3998 Package *package = [packages_ objectAtIndex:offset];
3999 NSString *name = [package index];
4001 if (section == nil || ![[section name] isEqualToString:name]) {
4002 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4003 [sections_ addObject:section];
4006 [section addToCount];
4012 - (NSString *) title {
4016 - (void) resetViewAnimated:(BOOL)animated {
4017 [list_ resetViewAnimated:animated];
4020 - (void) resetCursor {
4021 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4024 - (UISectionList *) list {
4028 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4029 [list_ setShouldHideHeaderInShortLists:hide];
4035 /* Add Source View {{{ */
4036 @interface AddSourceView : RVPage {
4037 _transient Database *database_;
4040 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4044 @implementation AddSourceView
4046 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4047 if ((self = [super initWithBook:book]) != nil) {
4048 database_ = database;
4054 /* Source Cell {{{ */
4055 @interface SourceCell : UITableCell {
4058 NSString *description_;
4064 - (SourceCell *) initWithSource:(Source *)source;
4068 @implementation SourceCell
4073 [description_ release];
4078 - (SourceCell *) initWithSource:(Source *)source {
4079 if ((self = [super init]) != nil) {
4081 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4083 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4084 icon_ = [icon_ retain];
4086 origin_ = [[source name] retain];
4087 label_ = [[source uri] retain];
4088 description_ = [[source description] retain];
4092 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4094 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4101 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4105 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4109 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4111 [super drawContentInRect:rect selected:selected];
4116 /* Source Table {{{ */
4117 @interface SourceTable : RVPage {
4118 _transient Database *database_;
4119 UISectionList *list_;
4120 NSMutableArray *sources_;
4121 UIActionSheet *alert_;
4125 UIProgressHUD *hud_;
4128 //NSURLConnection *installer_;
4129 NSURLConnection *trivial_bz2_;
4130 NSURLConnection *trivial_gz_;
4131 //NSURLConnection *automatic_;
4136 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4140 @implementation SourceTable
4142 - (void) _deallocConnection:(NSURLConnection *)connection {
4143 if (connection != nil) {
4144 [connection cancel];
4145 //[connection setDelegate:nil];
4146 [connection release];
4151 [[list_ table] setDelegate:nil];
4152 [list_ setDataSource:nil];
4161 //[self _deallocConnection:installer_];
4162 [self _deallocConnection:trivial_gz_];
4163 [self _deallocConnection:trivial_bz2_];
4164 //[self _deallocConnection:automatic_];
4171 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4172 return offset_ == 0 ? 1 : 2;
4175 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4176 switch (section + (offset_ == 0 ? 1 : 0)) {
4177 case 0: return @"Entered by User";
4178 case 1: return @"Installed by Packages";
4186 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4187 switch (section + (offset_ == 0 ? 1 : 0)) {
4189 case 1: return offset_;
4197 - (int) numberOfRowsInTable:(UITable *)table {
4198 return [sources_ count];
4201 - (float) table:(UITable *)table heightForRow:(int)row {
4202 Source *source = [sources_ objectAtIndex:row];
4203 return [source description] == nil ? 56 : 73;
4206 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4207 Source *source = [sources_ objectAtIndex:row];
4208 // XXX: weird warning, stupid selectors ;P
4209 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4212 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4216 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4220 - (void) tableRowSelected:(NSNotification*)notification {
4221 UITable *table([list_ table]);
4222 int row([table selectedRow]);
4226 Source *source = [sources_ objectAtIndex:row];
4228 PackageTable *packages = [[[PackageTable alloc]
4231 title:[source label]
4232 filter:@selector(isVisibleInSource:)
4236 [packages setDelegate:delegate_];
4238 [book_ pushPage:packages];
4241 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4242 Source *source = [sources_ objectAtIndex:row];
4243 return [source record] != nil;
4246 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4247 [[list_ table] setDeleteConfirmationRow:row];
4250 - (void) table:(UITable *)table deleteRow:(int)row {
4251 Source *source = [sources_ objectAtIndex:row];
4252 [Sources_ removeObjectForKey:[source key]];
4253 [delegate_ syncData];
4256 - (void) _endConnection:(NSURLConnection *)connection {
4257 NSURLConnection **field = NULL;
4258 if (connection == trivial_bz2_)
4259 field = &trivial_bz2_;
4260 else if (connection == trivial_gz_)
4261 field = &trivial_gz_;
4262 _assert(field != NULL);
4263 [connection release];
4267 trivial_bz2_ == nil &&
4270 [delegate_ setStatusBarShowsProgress:NO];
4273 [hud_ removeFromSuperview];
4278 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4281 @"./", @"Distribution",
4282 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4284 [delegate_ syncData];
4285 } else if (error_ != nil) {
4286 UIActionSheet *sheet = [[[UIActionSheet alloc]
4287 initWithTitle:@"Verification Error"
4288 buttons:[NSArray arrayWithObjects:@"OK", nil]
4289 defaultButtonIndex:0
4294 [sheet setBodyText:[error_ localizedDescription]];
4295 [sheet popupAlertAnimated:YES];
4297 UIActionSheet *sheet = [[[UIActionSheet alloc]
4298 initWithTitle:@"Did not Find Repository"
4299 buttons:[NSArray arrayWithObjects:@"OK", nil]
4300 defaultButtonIndex:0
4305 [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."];
4306 [sheet popupAlertAnimated:YES];
4312 if (error_ != nil) {
4319 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4320 switch ([response statusCode]) {
4326 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4327 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4329 error_ = [error retain];
4330 [self _endConnection:connection];
4333 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4334 [self _endConnection:connection];
4337 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4338 NSMutableURLRequest *request = [NSMutableURLRequest
4339 requestWithURL:[NSURL URLWithString:href]
4340 cachePolicy:NSURLRequestUseProtocolCachePolicy
4341 timeoutInterval:20.0
4344 [request setHTTPMethod:method];
4346 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4349 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4350 NSString *context = [sheet context];
4351 if ([context isEqualToString:@"source"])
4354 NSString *href = [[sheet textField] text];
4356 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4358 if (![href hasSuffix:@"/"])
4359 href_ = [href stringByAppendingString:@"/"];
4362 href_ = [href_ retain];
4364 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4365 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4366 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4370 hud_ = [delegate_ addProgressHUD];
4371 [hud_ setText:@"Verifying URL"];
4384 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4385 if ((self = [super initWithBook:book]) != nil) {
4386 database_ = database;
4387 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4389 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4390 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4391 [list_ setShouldHideHeaderInShortLists:NO];
4393 [self addSubview:list_];
4394 [list_ setDataSource:self];
4396 UITableColumn *column = [[UITableColumn alloc]
4397 initWithTitle:@"Name"
4399 width:[self frame].size.width
4402 UITable *table = [list_ table];
4403 [table setSeparatorStyle:1];
4404 [table addTableColumn:column];
4405 [table setDelegate:self];
4409 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4410 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4414 - (void) reloadData {
4416 _assert(list.ReadMainList());
4418 [sources_ removeAllObjects];
4419 [sources_ addObjectsFromArray:[database_ sources]];
4421 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4424 int count = [sources_ count];
4425 for (offset_ = 0; offset_ != count; ++offset_) {
4426 Source *source = [sources_ objectAtIndex:offset_];
4427 if ([source record] == nil)
4434 - (void) resetViewAnimated:(BOOL)animated {
4435 [list_ resetViewAnimated:animated];
4438 - (void) _leftButtonClicked {
4439 /*[book_ pushPage:[[[AddSourceView alloc]
4444 UIActionSheet *sheet = [[[UIActionSheet alloc]
4445 initWithTitle:@"Enter Cydia/APT URL"
4446 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4447 defaultButtonIndex:0
4452 [sheet addTextFieldWithValue:@"http://" label:@""];
4454 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4455 [traits setAutocapitalizationType:0];
4456 [traits setKeyboardType:3];
4457 [traits setAutocorrectionType:1];
4459 [sheet popupAlertAnimated:YES];
4462 - (void) _rightButtonClicked {
4463 UITable *table = [list_ table];
4464 BOOL editing = [table isRowDeletionEnabled];
4465 [table enableRowDeletion:!editing animated:YES];
4466 [book_ reloadButtonsForPage:self];
4469 - (NSString *) title {
4473 - (NSString *) leftButtonTitle {
4474 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4477 - (NSString *) rightButtonTitle {
4478 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4481 - (UINavigationButtonStyle) rightButtonStyle {
4482 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4488 /* Installed View {{{ */
4489 @interface InstalledView : RVPage {
4490 _transient Database *database_;
4491 PackageTable *packages_;
4495 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4499 @implementation InstalledView
4502 [packages_ release];
4506 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4507 if ((self = [super initWithBook:book]) != nil) {
4508 database_ = database;
4510 packages_ = [[PackageTable alloc]
4514 filter:@selector(isInstalledAndVisible:)
4515 with:[NSNumber numberWithBool:YES]
4518 [self addSubview:packages_];
4520 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4521 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4525 - (void) resetViewAnimated:(BOOL)animated {
4526 [packages_ resetViewAnimated:animated];
4529 - (void) reloadData {
4530 [packages_ reloadData];
4533 - (void) _rightButtonClicked {
4534 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4535 [packages_ reloadData];
4537 [book_ reloadButtonsForPage:self];
4540 - (NSString *) title {
4541 return @"Installed";
4544 - (NSString *) backButtonTitle {
4548 - (NSString *) rightButtonTitle {
4549 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4552 - (UINavigationButtonStyle) rightButtonStyle {
4553 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4556 - (void) setDelegate:(id)delegate {
4557 [super setDelegate:delegate];
4558 [packages_ setDelegate:delegate];
4565 @interface HomeView : BrowserView {
4570 @implementation HomeView
4572 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4576 - (void) _leftButtonClicked {
4577 UIActionSheet *sheet = [[[UIActionSheet alloc]
4578 initWithTitle:@"About Cydia Installer"
4579 buttons:[NSArray arrayWithObjects:@"Close", nil]
4580 defaultButtonIndex:0
4586 @"Copyright (C) 2008\n"
4587 "Jay Freeman (saurik)\n"
4588 "saurik@saurik.com\n"
4589 "http://www.saurik.com/\n"
4592 "http://www.theokorigroup.com/\n"
4594 "College of Creative Studies,\n"
4595 "University of California,\n"
4597 "http://www.ccs.ucsb.edu/"
4600 [sheet popupAlertAnimated:YES];
4603 - (NSString *) leftButtonTitle {
4609 /* Manage View {{{ */
4610 @interface ManageView : BrowserView {
4615 @implementation ManageView
4617 - (NSString *) title {
4621 - (void) _leftButtonClicked {
4622 [delegate_ askForSettings];
4625 - (NSString *) leftButtonTitle {
4629 - (NSString *) _rightButtonTitle {
4636 @interface WebView (Cydia)
4637 - (void) setScriptDebugDelegate:(id)delegate;
4638 - (void) _setFormDelegate:(id)delegate;
4639 - (void) _setUIKitDelegate:(id)delegate;
4640 - (void) setWebMailDelegate:(id)delegate;
4641 - (void) _setLayoutInterval:(float)interval;
4644 /* Indirect Delegate {{{ */
4645 @interface IndirectDelegate : NSProxy {
4646 _transient volatile id delegate_;
4649 - (void) setDelegate:(id)delegate;
4650 - (id) initWithDelegate:(id)delegate;
4653 @implementation IndirectDelegate
4655 - (void) setDelegate:(id)delegate {
4656 delegate_ = delegate;
4659 - (id) initWithDelegate:(id)delegate {
4660 delegate_ = delegate;
4664 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4665 if (delegate_ != nil)
4666 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4668 // XXX: I fucking hate Apple so very very bad
4669 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4672 - (void) forwardInvocation:(NSInvocation *)inv {
4673 SEL sel = [inv selector];
4674 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4675 [inv invokeWithTarget:delegate_];
4680 /* Browser Implementation {{{ */
4681 @implementation BrowserView
4684 WebView *webview = [webview_ webView];
4685 [webview setFrameLoadDelegate:nil];
4686 [webview setResourceLoadDelegate:nil];
4687 [webview setUIDelegate:nil];
4688 [webview setScriptDebugDelegate:nil];
4689 [webview setPolicyDelegate:nil];
4691 [webview setDownloadDelegate:nil];
4693 [webview _setFormDelegate:nil];
4694 [webview _setUIKitDelegate:nil];
4695 [webview setWebMailDelegate:nil];
4696 [webview setEditingDelegate:nil];
4698 [webview_ setDelegate:nil];
4699 [webview_ setGestureDelegate:nil];
4701 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4706 [webview_ removeFromSuperview];
4707 [Documents_ addObject:[webview_ autorelease]];
4712 [indirect_ setDelegate:nil];
4713 [indirect_ release];
4715 [scroller_ setDelegate:nil];
4717 [scroller_ release];
4719 [indicator_ release];
4725 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4726 [self loadRequest:[NSURLRequest
4729 timeoutInterval:30.0
4733 - (void) loadURL:(NSURL *)url {
4734 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4737 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4738 NSMutableURLRequest *copy = [request mutableCopy];
4740 if (Machine_ != NULL)
4741 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4742 if (UniqueID_ != nil)
4743 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4746 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4751 - (void) loadRequest:(NSURLRequest *)request {
4753 [webview_ loadRequest:request];
4756 - (void) reloadURL {
4757 if ([urls_ count] == 0)
4759 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4760 [urls_ removeLastObject];
4761 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4764 - (WebView *) webView {
4765 return [webview_ webView];
4768 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4769 [scroller_ setContentSize:frame.size];
4772 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4773 [self view:sender didSetFrame:frame];
4776 - (void) pushPage:(RVPage *)page {
4777 [self setBackButtonTitle:title_];
4778 [page setDelegate:delegate_];
4779 [book_ pushPage:page];
4782 - (BOOL) getSpecial:(NSURL *)url {
4783 NSString *href([url absoluteString]);
4784 NSString *scheme([[url scheme] lowercaseString]);
4788 if ([href hasPrefix:@"apptapp://package/"])
4789 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4790 else if ([scheme isEqualToString:@"cydia"]) {
4791 page = [delegate_ pageForURL:url hasTag:NULL];
4794 } else if (![scheme isEqualToString:@"apptapp"])
4798 [self pushPage:page];
4802 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4803 [window setValue:delegate_ forKey:@"cydia"];
4806 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4807 if (NSURL *url = [request URL]) {
4808 if (![self getSpecial:url]) {
4809 NSString *scheme([[url scheme] lowercaseString]);
4810 if ([scheme isEqualToString:@"mailto"])
4811 [delegate_ openMailToURL:url];
4820 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4821 if ([WebView canShowMIMEType:type])
4824 // XXX: handle more mime types!
4826 if (frame == [webView mainFrame])
4827 [UIApp openURL:[request URL]];
4831 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4832 NSURL *url([request URL]);
4834 if (url == nil) use: {
4839 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4842 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4843 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4846 [UIApp openURL:url];
4852 int store(_not(int));
4853 if (NSURL *itms = [url itmsURL:&store]) {
4854 NSLog(@"itms#%@#%u#%@", url, store, itms);
4856 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4857 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4864 NSString *scheme([[url scheme] lowercaseString]);
4866 if ([scheme isEqualToString:@"tel"]) {
4867 // XXX: intelligence
4871 if ([scheme isEqualToString:@"mailto"]) {
4872 [delegate_ openMailToURL:url];
4876 if ([self getSpecial:url])
4878 else if ([WebView _canHandleRequest:request])
4880 else if ([url isSpringboardHandledURL])
4886 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4887 //lprintf("Status:%s\n", [text UTF8String]);
4890 - (void) _pushPage {
4894 [book_ pushPage:self];
4897 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4898 NSURL *url = [request URL];
4899 if ([self getSpecial:url])
4902 return [self _addHeadersToRequest:request];
4905 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4906 [self setBackButtonTitle:title_];
4908 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4909 [browser setDelegate:delegate_];
4912 [browser loadRequest:[self _addHeadersToRequest:request]];
4913 [book_ pushPage:browser];
4916 return [browser webView];
4919 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4920 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4923 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4924 return [self _createWebViewWithRequest:request pushed:YES];
4927 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4928 if ([frame parentFrame] != nil)
4931 title_ = [title retain];
4932 [book_ reloadTitleForPage:self];
4935 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4936 if ([frame parentFrame] != nil)
4941 [indicator_ startAnimation];
4942 [self reloadButtons];
4944 if (title_ != nil) {
4949 [book_ reloadTitleForPage:self];
4951 WebView *webview = [webview_ webView];
4952 NSString *href = [webview mainFrameURL];
4953 [urls_ addObject:[NSURL URLWithString:href]];
4955 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4957 CGRect webrect = [scroller_ bounds];
4958 webrect.size.height = 0;
4959 [webview_ setFrame:webrect];
4962 - (void) _finishLoading {
4965 [indicator_ stopAnimation];
4966 [self reloadButtons];
4970 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4971 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4974 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4975 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4978 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4979 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4982 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4983 return [webview_ webView:sender didCommitLoadForFrame:frame];
4986 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4987 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4990 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4991 if ([frame parentFrame] == nil)
4992 [self _finishLoading];
4993 return [webview_ webView:sender didFinishLoadForFrame:frame];
4996 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4997 if ([frame parentFrame] != nil)
4999 [self _finishLoading];
5001 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5002 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5003 [[error localizedDescription] stringByAddingPercentEscapes]
5007 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5009 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5013 - (id) initWithBook:(RVBook *)book {
5014 if ((self = [super initWithBook:book]) != nil) {
5017 struct CGRect bounds = [self bounds];
5019 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5020 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5021 [self addSubview:pinstripe];
5023 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5024 [self addSubview:scroller_];
5026 [scroller_ setScrollingEnabled:YES];
5027 [scroller_ setAdjustForContentSizeChange:YES];
5028 [scroller_ setClipsSubviews:YES];
5029 [scroller_ setAllowsRubberBanding:YES];
5030 [scroller_ setScrollDecelerationFactor:0.99];
5031 [scroller_ setDelegate:self];
5033 CGRect webrect = [scroller_ bounds];
5034 webrect.size.height = 0;
5039 webview_ = [Documents_ lastObject];
5040 if (webview_ != nil) {
5041 webview_ = [webview_ retain];
5042 webview = [webview_ webView];
5043 [Documents_ removeLastObject];
5044 [webview_ setFrame:webrect];
5049 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5050 webview = [webview_ webView];
5052 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5054 [webview_ setAllowsMessaging:YES];
5056 [webview_ setTilingEnabled:YES];
5057 [webview_ setDrawsGrid:NO];
5058 [webview_ setLogsTilingChanges:NO];
5059 [webview_ setTileMinificationFilter:kCAFilterNearest];
5060 [webview_ setDetectsPhoneNumbers:NO];
5061 [webview_ setAutoresizes:YES];
5063 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5064 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5065 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5067 [webview_ _setDocumentType:0x4];
5069 [webview_ setZoomsFocusedFormControl:YES];
5070 [webview_ setContentsPosition:7];
5071 [webview_ setEnabledGestures:0xa];
5072 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
5073 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
5075 [webview_ setSmoothsFonts:YES];
5077 [webview _setUsesLoaderCache:YES];
5078 [webview setGroupName:@"Cydia"];
5079 //[webview _setLayoutInterval:0.5];
5082 [webview_ setDelegate:self];
5083 [webview_ setGestureDelegate:self];
5084 [scroller_ addSubview:webview_];
5086 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5088 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5089 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5090 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5092 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5093 NSString *application = package == nil ? @"Cydia" : [NSString
5094 stringWithFormat:@"Cydia/%@",
5096 ]; [webview setApplicationNameForUserAgent:application];
5098 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5100 [webview setFrameLoadDelegate:self];
5101 [webview setResourceLoadDelegate:indirect_];
5102 [webview setUIDelegate:self];
5103 [webview setScriptDebugDelegate:self];
5104 [webview setPolicyDelegate:self];
5106 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5108 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5109 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5110 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5114 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5115 [webview_ redrawScaledDocument];
5118 - (void) _rightButtonClicked {
5123 - (NSString *) _rightButtonTitle {
5127 - (NSString *) rightButtonTitle {
5128 return loading_ ? @"" : [self _rightButtonTitle];
5131 - (NSString *) title {
5132 return title_ == nil ? @"Loading" : title_;
5135 - (NSString *) backButtonTitle {
5139 - (void) setPageActive:(BOOL)active {
5141 [indicator_ removeFromSuperview];
5143 [[book_ navigationBar] addSubview:indicator_];
5146 - (void) resetViewAnimated:(BOOL)animated {
5149 - (void) setPushed:(bool)pushed {
5156 /* Cydia Book {{{ */
5157 @interface CYBook : RVBook <
5160 _transient Database *database_;
5161 UINavigationBar *overlay_;
5162 UINavigationBar *underlay_;
5163 UIProgressIndicator *indicator_;
5164 UITextLabel *prompt_;
5165 UIProgressBar *progress_;
5166 UINavigationButton *cancel_;
5169 NSTimeInterval last_;
5172 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5178 @implementation CYBook
5182 [indicator_ release];
5184 [progress_ release];
5189 - (NSString *) getTitleForPage:(RVPage *)page {
5190 return Simplify([super getTitleForPage:page]);
5198 [UIView beginAnimations:nil context:NULL];
5200 CGRect ovrframe = [overlay_ frame];
5201 ovrframe.origin.y = 0;
5202 [overlay_ setFrame:ovrframe];
5204 CGRect barframe = [navbar_ frame];
5205 barframe.origin.y += ovrframe.size.height;
5206 [navbar_ setFrame:barframe];
5208 CGRect trnframe = [transition_ frame];
5209 trnframe.origin.y += ovrframe.size.height;
5210 trnframe.size.height -= ovrframe.size.height;
5211 [transition_ setFrame:trnframe];
5213 [UIView endAnimations];
5215 [indicator_ startAnimation];
5216 [prompt_ setText:@"Updating Database"];
5217 [progress_ setProgress:0];
5220 last_ = [NSDate timeIntervalSinceReferenceDate];
5222 [overlay_ addSubview:cancel_];
5225 detachNewThreadSelector:@selector(_update)
5234 [indicator_ stopAnimation];
5236 [UIView beginAnimations:nil context:NULL];
5238 CGRect ovrframe = [overlay_ frame];
5239 ovrframe.origin.y = -ovrframe.size.height;
5240 [overlay_ setFrame:ovrframe];
5242 CGRect barframe = [navbar_ frame];
5243 barframe.origin.y -= ovrframe.size.height;
5244 [navbar_ setFrame:barframe];
5246 CGRect trnframe = [transition_ frame];
5247 trnframe.origin.y -= ovrframe.size.height;
5248 trnframe.size.height += ovrframe.size.height;
5249 [transition_ setFrame:trnframe];
5251 [UIView commitAnimations];
5253 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5256 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5257 if ((self = [super initWithFrame:frame]) != nil) {
5258 database_ = database;
5260 CGRect ovrrect = [navbar_ bounds];
5261 ovrrect.size.height = [UINavigationBar defaultSize].height;
5262 ovrrect.origin.y = -ovrrect.size.height;
5264 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5265 [self addSubview:overlay_];
5267 ovrrect.origin.y = frame.size.height;
5268 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5269 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5270 [self addSubview:underlay_];
5272 [overlay_ setBarStyle:1];
5273 [underlay_ setBarStyle:1];
5275 int barstyle = [overlay_ _barStyle:NO];
5276 bool ugly = barstyle == 0;
5278 UIProgressIndicatorStyle style = ugly ?
5279 UIProgressIndicatorStyleMediumBrown :
5280 UIProgressIndicatorStyleMediumWhite;
5282 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5283 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5284 CGRect indrect = {{indoffset, indoffset}, indsize};
5286 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5287 [indicator_ setStyle:style];
5288 [overlay_ addSubview:indicator_];
5290 CGSize prmsize = {215, indsize.height + 4};
5293 indoffset * 2 + indsize.width,
5297 unsigned(ovrrect.size.height - prmsize.height) / 2
5300 UIFont *font = [UIFont systemFontOfSize:15];
5302 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5304 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5305 [prompt_ setBackgroundColor:[UIColor clearColor]];
5306 [prompt_ setFont:font];
5308 [overlay_ addSubview:prompt_];
5310 CGSize prgsize = {75, 100};
5313 ovrrect.size.width - prgsize.width - 10,
5314 (ovrrect.size.height - prgsize.height) / 2
5317 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5318 [progress_ setStyle:0];
5319 [overlay_ addSubview:progress_];
5321 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5322 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5324 CGRect frame = [cancel_ frame];
5325 frame.size.width = 65;
5326 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5327 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5328 [cancel_ setFrame:frame];
5330 [cancel_ setBarStyle:barstyle];
5334 - (void) _onCancel {
5336 [cancel_ removeFromSuperview];
5339 - (void) _update { _pooled
5341 status.setDelegate(self);
5343 [database_ updateWithStatus:status];
5346 performSelectorOnMainThread:@selector(_update_)
5352 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5353 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5356 - (void) setProgressTitle:(NSString *)title {
5358 performSelectorOnMainThread:@selector(_setProgressTitle:)
5364 - (void) setProgressPercent:(float)percent {
5366 performSelectorOnMainThread:@selector(_setProgressPercent:)
5367 withObject:[NSNumber numberWithFloat:percent]
5372 - (void) startProgress {
5375 - (void) addProgressOutput:(NSString *)output {
5377 performSelectorOnMainThread:@selector(_addProgressOutput:)
5383 - (bool) isCancelling:(size_t)received {
5384 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5385 if (received_ != received) {
5386 received_ = received;
5388 } else if (now - last_ > 15)
5393 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5397 - (void) _setProgressTitle:(NSString *)title {
5398 [prompt_ setText:title];
5401 - (void) _setProgressPercent:(NSNumber *)percent {
5402 [progress_ setProgress:[percent floatValue]];
5405 - (void) _addProgressOutput:(NSString *)output {
5410 /* Cydia:// Protocol {{{ */
5411 @interface CydiaURLProtocol : NSURLProtocol {
5416 @implementation CydiaURLProtocol
5418 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5419 NSURL *url([request URL]);
5422 NSString *scheme([[url scheme] lowercaseString]);
5423 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5428 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5432 - (void) startLoading {
5433 id<NSURLProtocolClient> client([self client]);
5434 NSURLRequest *request([self request]);
5436 NSURL *url([request URL]);
5437 NSString *href([url absoluteString]);
5439 NSString *path([href substringFromIndex:8]);
5440 NSRange slash([path rangeOfString:@"/"]);
5443 if (slash.location == NSNotFound) {
5447 command = [path substringToIndex:slash.location];
5448 path = [path substringFromIndex:(slash.location + 1)];
5451 Database *database([Database sharedInstance]);
5453 if ([command isEqualToString:@"package-icon"]) {
5456 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5457 Package *package([database packageWithName:path]);
5461 UIImage *icon([package icon]);
5463 NSData *data(UIImagePNGRepresentation(icon));
5465 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5466 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5467 [client URLProtocol:self didLoadData:data];
5468 [client URLProtocolDidFinishLoading:self];
5469 } else if ([command isEqualToString:@"section-icon"]) {
5472 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5473 NSString *section(Simplify(path));
5475 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5477 icon = [UIImage applicationImageNamed:@"unknown.png"];
5479 NSData *data(UIImagePNGRepresentation(icon));
5481 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5482 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5483 [client URLProtocol:self didLoadData:data];
5484 [client URLProtocolDidFinishLoading:self];
5486 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5490 - (void) stopLoading {
5496 /* Install View {{{ */
5497 @interface InstallView : RVPage {
5498 _transient Database *database_;
5499 NSMutableArray *sections_;
5500 NSMutableArray *filtered_;
5501 UITransitionView *transition_;
5507 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5508 - (void) reloadData;
5513 @implementation InstallView
5516 [list_ setDataSource:nil];
5517 [list_ setDelegate:nil];
5519 [sections_ release];
5520 [filtered_ release];
5521 [transition_ release];
5523 [accessory_ release];
5527 - (int) numberOfRowsInTable:(UITable *)table {
5528 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5531 - (float) table:(UITable *)table heightForRow:(int)row {
5535 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5537 reusing = [[[SectionCell alloc] init] autorelease];
5538 [(SectionCell *)reusing setSection:(editing_ ?
5539 [sections_ objectAtIndex:row] :
5540 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5541 ) editing:editing_];
5545 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5549 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5553 - (void) tableRowSelected:(NSNotification *)notification {
5554 int row = [[notification object] selectedRow];
5565 title = @"All Packages";
5567 section = [filtered_ objectAtIndex:(row - 1)];
5568 name = [section name];
5574 title = @"(No Section)";
5578 PackageTable *table = [[[PackageTable alloc]
5582 filter:@selector(isVisiblyUninstalledInSection:)
5586 [table setDelegate:delegate_];
5588 [book_ pushPage:table];
5591 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5592 if ((self = [super initWithBook:book]) != nil) {
5593 database_ = database;
5595 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5596 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5598 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5599 [self addSubview:transition_];
5601 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5602 [transition_ transition:0 toView:list_];
5604 UITableColumn *column = [[[UITableColumn alloc]
5605 initWithTitle:@"Name"
5607 width:[self frame].size.width
5610 [list_ setDataSource:self];
5611 [list_ setSeparatorStyle:1];
5612 [list_ addTableColumn:column];
5613 [list_ setDelegate:self];
5614 [list_ setReusesTableCells:YES];
5618 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5619 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5623 - (void) reloadData {
5624 NSArray *packages = [database_ packages];
5626 [sections_ removeAllObjects];
5627 [filtered_ removeAllObjects];
5629 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5630 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5633 for (size_t i(0); i != [packages count]; ++i) {
5634 Package *package([packages objectAtIndex:i]);
5635 NSString *name([package section]);
5638 Section *section([sections objectForKey:name]);
5639 if (section == nil) {
5640 section = [[[Section alloc] initWithName:name] autorelease];
5641 [sections setObject:section forKey:name];
5645 if ([package valid] && [package installed] == nil && [package visible])
5646 [filtered addObject:package];
5650 [sections_ addObjectsFromArray:[sections allValues]];
5651 [sections_ sortUsingSelector:@selector(compareByName:)];
5654 [filtered sortUsingSelector:@selector(compareBySection:)];
5657 Section *section = nil;
5658 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5659 Package *package = [filtered objectAtIndex:offset];
5660 NSString *name = [package section];
5662 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5663 section = name == nil ?
5664 [[[Section alloc] initWithName:nil] autorelease] :
5665 [sections objectForKey:name];
5666 [filtered_ addObject:section];
5669 [section addToCount];
5677 - (void) resetView {
5679 [self _rightButtonClicked];
5682 - (void) resetViewAnimated:(BOOL)animated {
5683 [list_ resetViewAnimated:animated];
5686 - (void) _rightButtonClicked {
5687 if ((editing_ = !editing_))
5690 [delegate_ updateData];
5691 [book_ reloadTitleForPage:self];
5692 [book_ reloadButtonsForPage:self];
5695 - (NSString *) title {
5696 return editing_ ? @"Section Visibility" : @"Install by Section";
5699 - (NSString *) backButtonTitle {
5703 - (NSString *) rightButtonTitle {
5704 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5707 - (UINavigationButtonStyle) rightButtonStyle {
5708 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5711 - (UIView *) accessoryView {
5717 /* Changes View {{{ */
5718 @interface ChangesView : RVPage {
5719 _transient Database *database_;
5720 NSMutableArray *packages_;
5721 NSMutableArray *sections_;
5722 UISectionList *list_;
5726 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5727 - (void) reloadData;
5731 @implementation ChangesView
5734 [[list_ table] setDelegate:nil];
5735 [list_ setDataSource:nil];
5737 [packages_ release];
5738 [sections_ release];
5743 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5744 return [sections_ count];
5747 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5748 return [[sections_ objectAtIndex:section] name];
5751 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5752 return [[sections_ objectAtIndex:section] row];
5755 - (int) numberOfRowsInTable:(UITable *)table {
5756 return [packages_ count];
5759 - (float) table:(UITable *)table heightForRow:(int)row {
5760 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5763 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5765 reusing = [[[PackageCell alloc] init] autorelease];
5766 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5770 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5774 - (void) tableRowSelected:(NSNotification *)notification {
5775 int row = [[notification object] selectedRow];
5778 Package *package = [packages_ objectAtIndex:row];
5779 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5780 [view setDelegate:delegate_];
5781 [view setPackage:package];
5782 [book_ pushPage:view];
5785 - (void) _leftButtonClicked {
5786 [(CYBook *)book_ update];
5787 [self reloadButtons];
5790 - (void) _rightButtonClicked {
5791 [delegate_ distUpgrade];
5794 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5795 if ((self = [super initWithBook:book]) != nil) {
5796 database_ = database;
5798 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5799 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5801 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5802 [self addSubview:list_];
5804 [list_ setShouldHideHeaderInShortLists:NO];
5805 [list_ setDataSource:self];
5806 //[list_ setSectionListStyle:1];
5808 UITableColumn *column = [[[UITableColumn alloc]
5809 initWithTitle:@"Name"
5811 width:[self frame].size.width
5814 UITable *table = [list_ table];
5815 [table setSeparatorStyle:1];
5816 [table addTableColumn:column];
5817 [table setDelegate:self];
5818 [table setReusesTableCells:YES];
5822 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5823 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5827 - (void) reloadData {
5828 NSArray *packages = [database_ packages];
5830 [packages_ removeAllObjects];
5831 [sections_ removeAllObjects];
5834 for (size_t i(0); i != [packages count]; ++i) {
5835 Package *package([packages objectAtIndex:i]);
5838 [package installed] == nil && [package valid] && [package visible] ||
5839 [package upgradableAndEssential:NO]
5841 [packages_ addObject:package];
5845 [packages_ radixUsingSelector:@selector(compareForChanges) withObject:nil];
5848 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5849 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5850 Section *section = nil;
5854 bool unseens = false;
5856 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5859 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5860 Package *package = [packages_ objectAtIndex:offset];
5862 if (![package upgradableAndEssential:YES]) {
5864 NSDate *seen = [package seen];
5866 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5869 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5870 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5871 [sections_ addObject:section];
5875 [section addToCount];
5876 } else if ([package ignored])
5877 [ignored addToCount];
5880 [upgradable addToCount];
5885 CFRelease(formatter);
5888 Section *last = [sections_ lastObject];
5889 size_t count = [last count];
5890 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5891 [sections_ removeLastObject];
5894 if ([ignored count] != 0)
5895 [sections_ insertObject:ignored atIndex:0];
5897 [sections_ insertObject:upgradable atIndex:0];
5900 [self reloadButtons];
5903 - (void) resetViewAnimated:(BOOL)animated {
5904 [list_ resetViewAnimated:animated];
5907 - (NSString *) leftButtonTitle {
5908 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5911 - (NSString *) rightButtonTitle {
5912 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5915 - (NSString *) title {
5921 /* Search View {{{ */
5922 @protocol SearchViewDelegate
5923 - (void) showKeyboard:(BOOL)show;
5926 @interface SearchView : RVPage {
5928 UISearchField *field_;
5929 UITransitionView *transition_;
5930 PackageTable *table_;
5931 UIPreferencesTable *advanced_;
5937 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5938 - (void) reloadData;
5942 @implementation SearchView
5945 [field_ setDelegate:nil];
5947 [accessory_ release];
5949 [transition_ release];
5951 [advanced_ release];
5956 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5960 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5962 case 0: return @"Advanced Search (Coming Soon!)";
5964 default: _assert(false);
5968 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5972 default: _assert(false);
5976 - (void) _showKeyboard:(BOOL)show {
5977 CGSize keysize = [UIKeyboard defaultSize];
5978 CGRect keydown = [book_ pageBounds];
5979 CGRect keyup = keydown;
5980 keyup.size.height -= keysize.height - ButtonBarHeight_;
5982 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5984 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5985 [animation setSignificantRectFields:8];
5988 [animation setStartFrame:keydown];
5989 [animation setEndFrame:keyup];
5991 [animation setStartFrame:keyup];
5992 [animation setEndFrame:keydown];
5995 UIAnimator *animator = [UIAnimator sharedAnimator];
5998 addAnimations:[NSArray arrayWithObjects:animation, nil]
5999 withDuration:(KeyboardTime_ - delay)
6004 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6006 [delegate_ showKeyboard:show];
6009 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6010 [self _showKeyboard:YES];
6013 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6014 [self _showKeyboard:NO];
6017 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6019 NSString *text([field_ text]);
6020 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6026 - (void) textFieldClearButtonPressed:(UITextField *)field {
6030 - (void) keyboardInputShouldDelete:(id)input {
6034 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6035 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6039 [field_ resignFirstResponder];
6044 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6045 if ((self = [super initWithBook:book]) != nil) {
6046 CGRect pageBounds = [book_ pageBounds];
6048 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6049 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6050 [self addSubview:pinstripe];*/
6052 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6053 [self addSubview:transition_];
6055 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6057 [advanced_ setReusesTableCells:YES];
6058 [advanced_ setDataSource:self];
6059 [advanced_ reloadData];
6061 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6062 CGColor dimmed(space_, 0, 0, 0, 0.5);
6063 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6065 table_ = [[PackageTable alloc]
6069 filter:@selector(isUnfilteredAndSearchedForBy:)
6073 [table_ setShouldHideHeaderInShortLists:NO];
6074 [transition_ transition:0 toView:table_];
6083 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6090 [self bounds].size.width - area.origin.x - 18;
6092 area.size.height = [UISearchField defaultHeight];
6094 field_ = [[UISearchField alloc] initWithFrame:area];
6096 UIFont *font = [UIFont systemFontOfSize:16];
6097 [field_ setFont:font];
6099 [field_ setPlaceholder:@"Package Names & Descriptions"];
6100 [field_ setDelegate:self];
6102 [field_ setPaddingTop:5];
6104 UITextInputTraits *traits = [field_ textInputTraits];
6105 [traits setAutocapitalizationType:0];
6106 [traits setAutocorrectionType:1];
6107 [traits setReturnKeyType:6];
6109 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6111 accessory_ = [[UIView alloc] initWithFrame:accrect];
6112 [accessory_ addSubview:field_];
6114 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6115 [configure setShowPressFeedback:YES];
6116 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6117 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6118 [accessory_ addSubview:configure];*/
6120 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6121 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6127 LKAnimation *animation = [LKTransition animation];
6128 [animation setType:@"oglFlip"];
6129 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6130 [animation setFillMode:@"extended"];
6131 [animation setTransitionFlags:3];
6132 [animation setDuration:10];
6133 [animation setSpeed:0.35];
6134 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6135 [[transition_ _layer] addAnimation:animation forKey:0];
6136 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6137 flipped_ = !flipped_;
6141 - (void) configurePushed {
6142 [field_ resignFirstResponder];
6146 - (void) resetViewAnimated:(BOOL)animated {
6149 [table_ resetViewAnimated:animated];
6152 - (void) _reloadData {
6155 - (void) reloadData {
6158 [table_ setObject:[field_ text]];
6159 [table_ reloadData];
6160 [table_ resetCursor];
6163 - (UIView *) accessoryView {
6167 - (NSString *) title {
6171 - (NSString *) backButtonTitle {
6175 - (void) setDelegate:(id)delegate {
6176 [table_ setDelegate:delegate];
6177 [super setDelegate:delegate];
6183 @interface SettingsView : RVPage {
6184 _transient Database *database_;
6187 UIPreferencesTable *table_;
6188 _UISwitchSlider *subscribedSwitch_;
6189 _UISwitchSlider *ignoredSwitch_;
6190 UIPreferencesControlTableCell *subscribedCell_;
6191 UIPreferencesControlTableCell *ignoredCell_;
6194 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6198 @implementation SettingsView
6201 [table_ setDataSource:nil];
6204 if (package_ != nil)
6207 [subscribedSwitch_ release];
6208 [ignoredSwitch_ release];
6209 [subscribedCell_ release];
6210 [ignoredCell_ release];
6214 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6215 if (package_ == nil)
6221 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6222 if (package_ == nil)
6229 default: _assert(false);
6235 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6236 if (package_ == nil)
6243 default: _assert(false);
6249 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6250 if (package_ == nil)
6257 default: _assert(false);
6263 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6264 if (package_ == nil)
6267 _UISwitchSlider *slider([cell control]);
6268 // XXX: this is just weird
6269 BOOL value([slider value] != 0);
6270 NSMutableDictionary *metadata([package_ metadata]);
6273 if (NSNumber *number = [metadata objectForKey:key])
6274 before = [number boolValue];
6277 NSLog(@"%@:%@ %@:%@ : %u:%u", cell, slider, name_, key, value, before);
6279 if (value != before) {
6280 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6282 //[delegate_ performSelector:@selector(updateData) withObject:nil afterDelay:0];
6283 [delegate_ updateData];
6287 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6288 [self onSomething:cell withKey:@"IsSubscribed"];
6291 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6292 [self onSomething:cell withKey:@"IsIgnored"];
6295 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6296 if (package_ == nil)
6300 case 0: switch (row) {
6302 return subscribedCell_;
6304 return ignoredCell_;
6305 default: _assert(false);
6308 case 1: switch (row) {
6310 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6311 [cell setTitle:@"Changes only shows upgrades to installed packages. This minimizes spam from packagers. Activate this to see upgrades to this package even when it is not installed."];
6315 default: _assert(false);
6318 default: _assert(false);
6324 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6325 if ((self = [super initWithBook:book])) {
6326 database_ = database;
6327 name_ = [package retain];
6329 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6330 [self addSubview:table_];
6332 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6333 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6335 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6336 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6338 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6339 [subscribedCell_ setTitle:@"Show All Changes"];
6340 [subscribedCell_ setControl:subscribedSwitch_];
6342 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6343 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6344 [ignoredCell_ setControl:ignoredSwitch_];
6346 [table_ setDataSource:self];
6351 - (void) resetViewAnimated:(BOOL)animated {
6352 [table_ resetViewAnimated:animated];
6355 - (void) reloadData {
6356 if (package_ != nil)
6357 [package_ autorelease];
6358 package_ = [database_ packageWithName:name_];
6359 if (package_ != nil) {
6361 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6362 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6365 [table_ reloadData];
6368 - (NSString *) title {
6374 /* Signature View {{{ */
6375 @interface SignatureView : BrowserView {
6376 _transient Database *database_;
6380 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6384 @implementation SignatureView
6391 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6393 [super webView:sender didClearWindowObject:window forFrame:frame];
6396 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6397 if ((self = [super initWithBook:book]) != nil) {
6398 database_ = database;
6399 package_ = [package retain];
6404 - (void) resetViewAnimated:(BOOL)animated {
6407 - (void) reloadData {
6408 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6414 @interface Cydia : UIApplication <
6415 ConfirmationViewDelegate,
6416 ProgressViewDelegate,
6425 UIToolbar *buttonbar_;
6429 NSMutableArray *essential_;
6430 NSMutableArray *broken_;
6432 Database *database_;
6433 ProgressView *progress_;
6437 UIKeyboard *keyboard_;
6438 UIProgressHUD *hud_;
6440 InstallView *install_;
6441 ChangesView *changes_;
6442 ManageView *manage_;
6443 SearchView *search_;
6448 @implementation Cydia
6451 if ([broken_ count] != 0) {
6452 int count = [broken_ count];
6454 UIActionSheet *sheet = [[[UIActionSheet alloc]
6455 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6456 buttons:[NSArray arrayWithObjects:
6458 @"Ignore (Temporary)",
6460 defaultButtonIndex:0
6465 [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."];
6466 [sheet popupAlertAnimated:YES];
6467 } else if (!Ignored_ && [essential_ count] != 0) {
6468 int count = [essential_ count];
6470 UIActionSheet *sheet = [[[UIActionSheet alloc]
6471 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6472 buttons:[NSArray arrayWithObjects:
6473 @"Upgrade Essential",
6474 @"Upgrade Everything",
6475 @"Ignore (Temporary)",
6477 defaultButtonIndex:0
6482 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6483 [sheet popupAlertAnimated:YES];
6487 - (void) _reloadData {
6488 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6489 [hud setText:@"Reloading Data"];
6490 [overlay_ addSubview:hud];
6494 [database_ reloadData];
6499 [essential_ removeAllObjects];
6500 [broken_ removeAllObjects];
6502 NSArray *packages = [database_ packages];
6503 for (Package *package in packages) {
6505 [broken_ addObject:package];
6506 if ([package upgradableAndEssential:NO]) {
6507 if ([package essential])
6508 [essential_ addObject:package];
6514 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6515 [buttonbar_ setBadgeValue:badge forButton:3];
6516 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6517 [buttonbar_ setBadgeAnimated:YES forButton:3];
6518 [self setApplicationBadge:badge];
6520 [buttonbar_ setBadgeValue:nil forButton:3];
6521 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6522 [buttonbar_ setBadgeAnimated:NO forButton:3];
6523 [self removeApplicationBadge];
6529 if ([packages count] == 0);
6541 [hud removeFromSuperview];*/
6544 - (void) _saveConfig {
6547 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6553 - (void) updateData {
6556 /* XXX: this is just stupid */
6557 if (tag_ != 2 && install_ != nil)
6558 [install_ reloadData];
6559 if (tag_ != 3 && changes_ != nil)
6560 [changes_ reloadData];
6561 if (tag_ != 5 && search_ != nil)
6562 [search_ reloadData];
6572 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6573 _assert(file != NULL);
6575 NSArray *keys = [Sources_ allKeys];
6577 for (int i(0), e([keys count]); i != e; ++i) {
6578 NSString *key = [keys objectAtIndex:i];
6579 NSDictionary *source = [Sources_ objectForKey:key];
6581 fprintf(file, "%s %s %s\n",
6582 [[source objectForKey:@"Type"] UTF8String],
6583 [[source objectForKey:@"URI"] UTF8String],
6584 [[source objectForKey:@"Distribution"] UTF8String]
6593 detachNewThreadSelector:@selector(update_)
6596 title:@"Updating Sources"
6600 - (void) reloadData {
6601 @synchronized (self) {
6602 if (confirm_ == nil)
6608 pkgProblemResolver *resolver = [database_ resolver];
6610 resolver->InstallProtect();
6611 if (!resolver->Resolve(true))
6616 [database_ prepare];
6618 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6619 [confirm_ setDelegate:self];
6621 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6622 [page setDelegate:self];
6624 [confirm_ setPage:page];
6625 [underlay_ popSubview:confirm_];
6628 - (void) installPackage:(Package *)package {
6629 @synchronized (self) {
6636 - (void) removePackage:(Package *)package {
6637 @synchronized (self) {
6644 - (void) distUpgrade {
6645 @synchronized (self) {
6646 [database_ upgrade];
6652 @synchronized (self) {
6654 if (confirm_ != nil) {
6662 [overlay_ removeFromSuperview];
6666 detachNewThreadSelector:@selector(perform)
6673 - (void) bootstrap_ {
6675 [database_ upgrade];
6676 [database_ prepare];
6677 [database_ perform];
6680 - (void) bootstrap {
6682 detachNewThreadSelector:@selector(bootstrap_)
6685 title:@"Bootstrap Install"
6689 - (void) progressViewIsComplete:(ProgressView *)progress {
6690 if (confirm_ != nil) {
6691 [underlay_ addSubview:overlay_];
6692 [confirm_ popFromSuperviewAnimated:NO];
6698 - (void) setPage:(RVPage *)page {
6699 [page resetViewAnimated:NO];
6700 [page setDelegate:self];
6701 [book_ setPage:page];
6704 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6705 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6706 [browser loadURL:url];
6710 - (void) _setHomePage {
6711 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6714 - (void) buttonBarItemTapped:(id)sender {
6715 unsigned tag = [sender tag];
6717 [book_ resetViewAnimated:YES];
6719 } else if (tag_ == 2 && tag != 2)
6720 [install_ resetView];
6723 case 1: [self _setHomePage]; break;
6725 case 2: [self setPage:install_]; break;
6726 case 3: [self setPage:changes_]; break;
6727 case 4: [self setPage:manage_]; break;
6728 case 5: [self setPage:search_]; break;
6730 default: _assert(false);
6736 - (void) applicationWillSuspend {
6738 [super applicationWillSuspend];
6741 - (void) askForSettings {
6742 UIActionSheet *role = [[[UIActionSheet alloc]
6743 initWithTitle:@"Who Are You?"
6744 buttons:[NSArray arrayWithObjects:
6745 @"User (Graphical Only)",
6746 @"Hacker (+ Command Line)",
6747 @"Developer (No Filters)",
6749 defaultButtonIndex:-1
6754 [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."];
6755 [role popupAlertAnimated:YES];
6760 [self setStatusBarShowsProgress:NO];
6763 [hud_ removeFromSuperview];
6767 pid_t pid = ExecFork();
6769 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6770 perror("launchctl stop");
6777 [self askForSettings];
6781 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6783 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6784 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6785 0, 0, screenrect.size.width, screenrect.size.height - 48
6786 ) database:database_];
6788 [book_ setDelegate:self];
6790 [overlay_ addSubview:book_];
6792 NSArray *buttonitems = [NSArray arrayWithObjects:
6793 [NSDictionary dictionaryWithObjectsAndKeys:
6794 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6795 @"home-up.png", kUIButtonBarButtonInfo,
6796 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6797 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6798 self, kUIButtonBarButtonTarget,
6799 @"Home", kUIButtonBarButtonTitle,
6800 @"0", kUIButtonBarButtonType,
6803 [NSDictionary dictionaryWithObjectsAndKeys:
6804 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6805 @"install-up.png", kUIButtonBarButtonInfo,
6806 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6807 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6808 self, kUIButtonBarButtonTarget,
6809 @"Sections", kUIButtonBarButtonTitle,
6810 @"0", kUIButtonBarButtonType,
6813 [NSDictionary dictionaryWithObjectsAndKeys:
6814 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6815 @"changes-up.png", kUIButtonBarButtonInfo,
6816 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6817 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6818 self, kUIButtonBarButtonTarget,
6819 @"Changes", kUIButtonBarButtonTitle,
6820 @"0", kUIButtonBarButtonType,
6823 [NSDictionary dictionaryWithObjectsAndKeys:
6824 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6825 @"manage-up.png", kUIButtonBarButtonInfo,
6826 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6827 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6828 self, kUIButtonBarButtonTarget,
6829 @"Manage", kUIButtonBarButtonTitle,
6830 @"0", kUIButtonBarButtonType,
6833 [NSDictionary dictionaryWithObjectsAndKeys:
6834 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6835 @"search-up.png", kUIButtonBarButtonInfo,
6836 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6837 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6838 self, kUIButtonBarButtonTarget,
6839 @"Search", kUIButtonBarButtonTitle,
6840 @"0", kUIButtonBarButtonType,
6844 buttonbar_ = [[UIToolbar alloc]
6846 withFrame:CGRectMake(
6847 0, screenrect.size.height - ButtonBarHeight_,
6848 screenrect.size.width, ButtonBarHeight_
6850 withItemList:buttonitems
6853 [buttonbar_ setDelegate:self];
6854 [buttonbar_ setBarStyle:1];
6855 [buttonbar_ setButtonBarTrackingMode:2];
6857 int buttons[5] = {1, 2, 3, 4, 5};
6858 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6859 [buttonbar_ showButtonGroup:0 withDuration:0];
6861 for (int i = 0; i != 5; ++i)
6862 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6863 i * 64 + 2, 1, 60, ButtonBarHeight_
6866 [buttonbar_ showSelectionForButton:1];
6867 [overlay_ addSubview:buttonbar_];
6869 [UIKeyboard initImplementationNow];
6870 CGSize keysize = [UIKeyboard defaultSize];
6871 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6872 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6873 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6874 [overlay_ addSubview:keyboard_];
6877 [underlay_ addSubview:overlay_];
6881 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6882 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6883 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6885 manage_ = (ManageView *) [[self
6886 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6887 withClass:[ManageView class]
6893 [self _setHomePage];
6896 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6897 NSString *context = [sheet context];
6898 if ([context isEqualToString:@"fixhalf"])
6901 @synchronized (self) {
6902 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6903 Package *broken = [broken_ objectAtIndex:i];
6906 NSString *id = [broken id];
6907 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6908 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6909 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6910 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6919 [broken_ removeAllObjects];
6926 else if ([context isEqualToString:@"role"]) {
6928 case 1: Role_ = @"User"; break;
6929 case 2: Role_ = @"Hacker"; break;
6930 case 3: Role_ = @"Developer"; break;
6937 bool reset = Settings_ != nil;
6939 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6943 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6951 } else if ([context isEqualToString:@"upgrade"])
6954 @synchronized (self) {
6955 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6956 Package *essential = [essential_ objectAtIndex:i];
6957 [essential install];
6980 - (void) reorganize { _pooled
6981 system("/usr/libexec/cydia/free.sh");
6982 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6985 - (void) applicationSuspend:(__GSEvent *)event {
6986 if (hud_ == nil && ![progress_ isRunning])
6987 [super applicationSuspend:event];
6990 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6992 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6995 - (void) _setSuspended:(BOOL)value {
6997 [super _setSuspended:value];
7000 - (UIProgressHUD *) addProgressHUD {
7001 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7003 [underlay_ addSubview:hud];
7007 - (void) openMailToURL:(NSURL *)url {
7008 // XXX: this makes me sad
7010 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7012 [UIApp openURL:url];
7016 - (RVPage *) pageForPackage:(NSString *)name {
7017 if (Package *package = [database_ packageWithName:name]) {
7018 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7019 [view setPackage:package];
7022 UIActionSheet *sheet = [[[UIActionSheet alloc]
7023 initWithTitle:@"Cannot Locate Package"
7024 buttons:[NSArray arrayWithObjects:@"Close", nil]
7025 defaultButtonIndex:0
7030 [sheet setBodyText:[NSString stringWithFormat:
7031 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7034 [sheet popupAlertAnimated:YES];
7039 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7040 NSString *href = [url absoluteString];
7045 if ([href isEqualToString:@"cydia://add-source"])
7046 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7047 else if ([href isEqualToString:@"cydia://sources"])
7048 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7049 else if ([href isEqualToString:@"cydia://packages"])
7050 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7051 else if ([href hasPrefix:@"cydia://url/"])
7052 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7053 else if ([href hasPrefix:@"cydia://launch/"])
7054 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7055 else if ([href hasPrefix:@"cydia://package-settings/"])
7056 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7057 else if ([href hasPrefix:@"cydia://package-signature/"])
7058 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7059 else if ([href hasPrefix:@"cydia://package/"])
7060 return [self pageForPackage:[href substringFromIndex:16]];
7061 else if ([href hasPrefix:@"cydia://files/"]) {
7062 NSString *name = [href substringFromIndex:14];
7064 if (Package *package = [database_ packageWithName:name]) {
7065 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7066 [files setPackage:package];
7074 - (void) applicationOpenURL:(NSURL *)url {
7075 [super applicationOpenURL:url];
7077 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7078 [self setPage:page];
7079 [buttonbar_ showSelectionForButton:tag];
7084 - (void) applicationDidFinishLaunching:(id)unused {
7085 Font12_ = [[UIFont systemFontOfSize:12] retain];
7086 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7087 Font14_ = [[UIFont systemFontOfSize:14] retain];
7088 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7089 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7091 _assert(pkgInitConfig(*_config));
7092 _assert(pkgInitSystem(*_config, _system));
7096 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7097 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7099 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7101 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7102 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7104 [window_ orderFront:self];
7105 [window_ makeKey:self];
7106 [window_ setHidden:NO];
7108 database_ = [Database sharedInstance];
7109 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7110 [database_ setDelegate:progress_];
7111 [window_ setContentView:progress_];
7113 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7114 [progress_ setContentView:underlay_];
7116 [progress_ resetView];
7119 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7120 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7121 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7122 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7123 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7124 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7125 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7127 [self setIdleTimerDisabled:YES];
7129 hud_ = [self addProgressHUD];
7130 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
7132 [self setStatusBarShowsProgress:YES];
7135 detachNewThreadSelector:@selector(reorganize)
7143 /* Web Scripting {{{ */
7144 + (NSString *) webScriptNameForSelector:(SEL)selector {
7145 if (selector == @selector(supports:))
7150 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7151 return selector != @selector(supports:);
7154 - (BOOL) supports:(NSString *)feature {
7155 return [feature isEqualToString:@"window.open"];
7159 - (void) showKeyboard:(BOOL)show {
7160 CGSize keysize = [UIKeyboard defaultSize];
7161 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7162 CGRect keyup = keydown;
7163 keyup.origin.y -= keysize.height;
7165 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7166 [animation setSignificantRectFields:2];
7169 [animation setStartFrame:keydown];
7170 [animation setEndFrame:keyup];
7171 [keyboard_ activate];
7173 [animation setStartFrame:keyup];
7174 [animation setEndFrame:keydown];
7175 [keyboard_ deactivate];
7178 [[UIAnimator sharedAnimator]
7179 addAnimations:[NSArray arrayWithObjects:animation, nil]
7180 withDuration:KeyboardTime_
7185 - (void) slideUp:(UIActionSheet *)alert {
7187 [alert presentSheetFromButtonBar:buttonbar_];
7189 [alert presentSheetInView:overlay_];
7194 void AddPreferences(NSString *plist) { _pooled
7195 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7196 _assert(settings != NULL);
7197 NSMutableArray *items = [settings objectForKey:@"items"];
7201 for (size_t i(0); i != [items count]; ++i) {
7202 NSMutableDictionary *item([items objectAtIndex:i]);
7203 NSString *label = [item objectForKey:@"label"];
7204 if (label != nil && [label isEqualToString:@"Cydia"]) {
7211 for (size_t i(0); i != [items count]; ++i) {
7212 NSDictionary *item([items objectAtIndex:i]);
7213 NSString *label = [item objectForKey:@"label"];
7214 if (label != nil && [label isEqualToString:@"General"]) {
7215 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7216 @"CydiaSettings", @"bundle",
7217 @"PSLinkCell", @"cell",
7218 [NSNumber numberWithBool:YES], @"hasIcon",
7219 [NSNumber numberWithBool:YES], @"isController",
7221 nil] atIndex:(i + 1)];
7227 _assert([settings writeToFile:plist atomically:YES] == YES);
7232 id Alloc_(id self, SEL selector) {
7233 id object = alloc_(self, selector);
7234 lprintf("[%s]A-%p\n", self->isa->name, object);
7239 id Dealloc_(id self, SEL selector) {
7240 id object = dealloc_(self, selector);
7241 lprintf("[%s]D-%p\n", self->isa->name, object);
7245 int main(int argc, char *argv[]) { _pooled
7246 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
7248 App_ = [[NSBundle mainBundle] bundlePath];
7249 Home_ = NSHomeDirectory();
7250 Locale_ = CFLocaleCopyCurrent();
7253 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7254 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7255 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7256 Sounds_Keyboard_ = [keyboard boolValue];
7262 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7263 _assert(errno == ENOENT);
7264 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7265 _assert(errno == ENOENT);
7267 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7268 alloc_ = alloc->method_imp;
7269 alloc->method_imp = (IMP) &Alloc_;*/
7271 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7272 dealloc_ = dealloc->method_imp;
7273 dealloc->method_imp = (IMP) &Dealloc_;*/
7278 size = sizeof(maxproc);
7279 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7280 perror("sysctlbyname(\"kern.maxproc\", ?)");
7281 else if (maxproc < 64) {
7283 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7284 perror("sysctlbyname(\"kern.maxproc\", #)");
7287 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7288 char *machine = new char[size];
7289 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7290 perror("sysctlbyname(\"hw.machine\", ?)");
7294 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7296 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7297 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7299 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7300 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7302 Settings_ = [Metadata_ objectForKey:@"Settings"];
7304 Packages_ = [Metadata_ objectForKey:@"Packages"];
7305 Sections_ = [Metadata_ objectForKey:@"Sections"];
7306 Sources_ = [Metadata_ objectForKey:@"Sources"];
7309 if (Settings_ != nil)
7310 Role_ = [Settings_ objectForKey:@"Role"];
7312 if (Packages_ == nil) {
7313 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7314 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7317 if (Sections_ == nil) {
7318 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7319 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7322 if (Sources_ == nil) {
7323 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7324 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7328 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7331 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7332 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7334 if (access("/User", F_OK) != 0)
7335 system("/usr/libexec/cydia/firmware.sh");
7337 _assert([[NSFileManager defaultManager]
7338 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7339 withIntermediateDirectories:YES
7344 space_ = CGColorSpaceCreateDeviceRGB();
7346 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7347 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7348 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7349 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7350 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7351 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7353 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7355 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7357 UIApplicationUseLegacyEvents(YES);
7358 UIKeyboardDisableAutomaticAppearance();
7360 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7362 CGColorSpaceRelease(space_);