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 width = 32;
286 static const size_t bits = 11;
287 static const size_t slots = 1 << bits;
288 static const size_t passes = (width + (bits - 1)) / bits;
290 size_t *hist(new size_t[slots]);
292 for (size_t pass(0); pass != passes; ++pass) {
293 memset(hist, 0, sizeof(size_t) * slots);
295 for (size_t i(0); i != count; ++i) {
296 uint32_t key(lhs[i].key);
298 key &= _not(uint32_t) >> width - bits;
303 for (size_t i(0); i != slots; ++i) {
304 size_t local(offset);
309 for (size_t i(0); i != count; ++i) {
310 uint32_t key(lhs[i].key);
312 key &= _not(uint32_t) >> width - bits;
313 rhs[hist[key]++] = lhs[i];
323 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
324 for (size_t i(0); i != count; ++i)
325 [values addObject:[self objectAtIndex:lhs[i].index]];
326 [self setArray:values];
335 kUIControlEventMouseDown = 1 << 0,
336 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
337 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
338 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
339 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
340 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
341 } UIControlEventMasks;
343 @interface NSString (UIKit)
344 - (NSString *) stringByAddingPercentEscapes;
345 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
348 @interface NSString (Cydia)
349 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
350 - (NSComparisonResult) compareByPath:(NSString *)other;
353 @implementation NSString (Cydia)
355 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
356 char data[length + 1];
357 memcpy(data, bytes, length);
359 return [NSString stringWithUTF8String:data];
362 - (NSComparisonResult) compareByPath:(NSString *)other {
363 NSString *prefix = [self commonPrefixWithString:other options:0];
364 size_t length = [prefix length];
366 NSRange lrange = NSMakeRange(length, [self length] - length);
367 NSRange rrange = NSMakeRange(length, [other length] - length);
369 lrange = [self rangeOfString:@"/" options:0 range:lrange];
370 rrange = [other rangeOfString:@"/" options:0 range:rrange];
372 NSComparisonResult value;
374 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
375 value = NSOrderedSame;
376 else if (lrange.location == NSNotFound)
377 value = NSOrderedAscending;
378 else if (rrange.location == NSNotFound)
379 value = NSOrderedDescending;
381 value = NSOrderedSame;
383 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
384 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
385 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
386 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
388 NSComparisonResult result = [lpath compare:rpath];
389 return result == NSOrderedSame ? value : result;
394 /* Perl-Compatible RegEx {{{ */
404 Pcre(const char *regex) :
409 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
412 lprintf("%d:%s\n", offset, error);
416 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
417 matches_ = new int[(capture_ + 1) * 3];
425 NSString *operator [](size_t match) {
426 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
429 bool operator ()(NSString *data) {
430 // XXX: length is for characters, not for bytes
431 return operator ()([data UTF8String], [data length]);
434 bool operator ()(const char *data, size_t size) {
436 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
440 /* Mime Addresses {{{ */
441 @interface Address : NSObject {
447 - (NSString *) address;
449 + (Address *) addressWithString:(NSString *)string;
450 - (Address *) initWithString:(NSString *)string;
453 @implementation Address
462 - (NSString *) name {
466 - (NSString *) address {
470 + (Address *) addressWithString:(NSString *)string {
471 return [[[Address alloc] initWithString:string] autorelease];
474 + (NSArray *) _attributeKeys {
475 return [NSArray arrayWithObjects:@"address", @"name", nil];
478 - (NSArray *) attributeKeys {
479 return [[self class] _attributeKeys];
482 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
483 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
486 - (Address *) initWithString:(NSString *)string {
487 if ((self = [super init]) != nil) {
488 const char *data = [string UTF8String];
489 size_t size = [string length];
491 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
493 if (address_r(data, size)) {
494 name_ = [address_r[1] retain];
495 address_ = [address_r[2] retain];
497 name_ = [string retain];
505 /* CoreGraphics Primitives {{{ */
516 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
519 Set(space, red, green, blue, alpha);
524 CGColorRelease(color_);
531 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
533 float color[] = {red, green, blue, alpha};
534 color_ = CGColorCreate(space, color);
537 operator CGColorRef() {
543 extern "C" void UISetColor(CGColorRef color);
545 /* Random Global Variables {{{ */
546 static const int PulseInterval_ = 50000;
547 static const int ButtonBarHeight_ = 48;
548 static const float KeyboardTime_ = 0.3f;
550 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
551 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
553 static CGColor Blue_;
554 static CGColor Blueish_;
555 static CGColor Black_;
557 static CGColor White_;
558 static CGColor Gray_;
560 static NSString *App_;
561 static NSString *Home_;
562 static BOOL Sounds_Keyboard_;
564 static BOOL Advanced_;
568 static BOOL Ignored_;
570 static UIFont *Font12_;
571 static UIFont *Font12Bold_;
572 static UIFont *Font14_;
573 static UIFont *Font18Bold_;
574 static UIFont *Font22Bold_;
576 static const char *Machine_ = NULL;
577 static const NSString *UniqueID_ = NULL;
584 CGColorSpaceRef space_;
586 #define FW_LEAST(major, minor, bugfix) \
587 (major < Major_ || major == Major_ && \
588 (minor < Minor_ || minor == Minor_ && \
594 static NSDictionary *SectionMap_;
595 static NSMutableDictionary *Metadata_;
596 static _transient NSMutableDictionary *Settings_;
597 static _transient NSString *Role_;
598 static _transient NSMutableDictionary *Packages_;
599 static _transient NSMutableDictionary *Sections_;
600 static _transient NSMutableDictionary *Sources_;
601 static bool Changed_;
605 static NSMutableArray *Documents_;
608 NSString *GetLastUpdate() {
609 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
612 return @"Never or Unknown";
614 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
615 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
617 CFRelease(formatter);
619 return [(NSString *) formatted autorelease];
622 /* Display Helpers {{{ */
623 inline float Interpolate(float begin, float end, float fraction) {
624 return (end - begin) * fraction + begin;
627 NSString *SizeString(double size) {
628 bool negative = size < 0;
633 while (size > 1024) {
638 static const char *powers_[] = {"B", "kB", "MB", "GB"};
640 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
643 NSString *StripVersion(NSString *version) {
644 NSRange colon = [version rangeOfString:@":"];
645 if (colon.location != NSNotFound)
646 version = [version substringFromIndex:(colon.location + 1)];
650 NSString *Simplify(NSString *title) {
651 const char *data = [title UTF8String];
652 size_t size = [title length];
654 static Pcre square_r("^\\[(.*)\\]$");
655 if (square_r(data, size))
656 return Simplify(square_r[1]);
658 static Pcre paren_r("^\\((.*)\\)$");
659 if (paren_r(data, size))
660 return Simplify(paren_r[1]);
662 static Pcre title_r("^(.*?) \\(.*\\)$");
663 if (title_r(data, size))
664 return Simplify(title_r[1]);
670 bool isSectionVisible(NSString *section) {
671 NSDictionary *metadata = [Sections_ objectForKey:section];
672 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
673 return hidden == nil || ![hidden boolValue];
676 /* Delegate Prototypes {{{ */
680 @interface NSObject (ProgressDelegate)
683 @implementation NSObject(ProgressDelegate)
685 - (void) _setProgressError:(NSArray *)args {
686 [self performSelector:@selector(setProgressError:forPackage:)
687 withObject:[args objectAtIndex:0]
688 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
694 @protocol ProgressDelegate
695 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
696 - (void) setProgressTitle:(NSString *)title;
697 - (void) setProgressPercent:(float)percent;
698 - (void) startProgress;
699 - (void) addProgressOutput:(NSString *)output;
700 - (bool) isCancelling:(size_t)received;
703 @protocol ConfigurationDelegate
704 - (void) repairWithSelector:(SEL)selector;
705 - (void) setConfigurationData:(NSString *)data;
708 @protocol CydiaDelegate
709 - (void) installPackage:(Package *)package;
710 - (void) removePackage:(Package *)package;
711 - (void) slideUp:(UIActionSheet *)alert;
712 - (void) distUpgrade;
715 - (void) askForSettings;
716 - (UIProgressHUD *) addProgressHUD;
717 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
718 - (RVPage *) pageForPackage:(NSString *)name;
719 - (void) openMailToURL:(NSURL *)url;
723 /* Status Delegation {{{ */
725 public pkgAcquireStatus
728 _transient NSObject<ProgressDelegate> *delegate_;
736 void setDelegate(id delegate) {
737 delegate_ = delegate;
740 virtual bool MediaChange(std::string media, std::string drive) {
744 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
747 virtual void Fetch(pkgAcquire::ItemDesc &item) {
748 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
751 virtual void Done(pkgAcquire::ItemDesc &item) {
754 virtual void Fail(pkgAcquire::ItemDesc &item) {
756 item.Owner->Status == pkgAcquire::Item::StatIdle ||
757 item.Owner->Status == pkgAcquire::Item::StatDone
761 std::string &error(item.Owner->ErrorText);
765 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
766 NSArray *fields([description componentsSeparatedByString:@" "]);
767 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
769 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
770 withObject:[NSArray arrayWithObjects:
771 [NSString stringWithUTF8String:error.c_str()],
778 virtual bool Pulse(pkgAcquire *Owner) {
779 bool value = pkgAcquireStatus::Pulse(Owner);
782 double(CurrentBytes + CurrentItems) /
783 double(TotalBytes + TotalItems)
786 [delegate_ setProgressPercent:percent];
787 return [delegate_ isCancelling:CurrentBytes] ? false : value;
790 virtual void Start() {
791 [delegate_ startProgress];
794 virtual void Stop() {
798 /* Progress Delegation {{{ */
803 _transient id<ProgressDelegate> delegate_;
806 virtual void Update() {
807 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
808 [delegate_ setProgressPercent:(Percent / 100)];
817 void setDelegate(id delegate) {
818 delegate_ = delegate;
821 virtual void Done() {
822 [delegate_ setProgressPercent:1];
827 /* Database Interface {{{ */
828 @interface Database : NSObject {
830 pkgDepCache::Policy *policy_;
831 pkgRecords *records_;
832 pkgProblemResolver *resolver_;
833 pkgAcquire *fetcher_;
835 SPtr<pkgPackageManager> manager_;
836 pkgSourceList *list_;
838 NSMutableDictionary *sources_;
839 NSMutableArray *packages_;
841 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
850 + (Database *) sharedInstance;
852 - (void) _readCydia:(NSNumber *)fd;
853 - (void) _readStatus:(NSNumber *)fd;
854 - (void) _readOutput:(NSNumber *)fd;
858 - (Package *) packageWithName:(NSString *)name;
860 - (pkgCacheFile &) cache;
861 - (pkgDepCache::Policy *) policy;
862 - (pkgRecords *) records;
863 - (pkgProblemResolver *) resolver;
864 - (pkgAcquire &) fetcher;
865 - (NSArray *) packages;
866 - (NSArray *) sources;
875 - (void) updateWithStatus:(Status &)status;
877 - (void) setDelegate:(id)delegate;
878 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
882 /* Source Class {{{ */
883 @interface Source : NSObject {
884 NSString *description_;
889 NSString *distribution_;
893 NSString *defaultIcon_;
895 NSDictionary *record_;
899 - (Source *) initWithMetaIndex:(metaIndex *)index;
901 - (NSComparisonResult) compareByNameAndType:(Source *)source;
903 - (NSDictionary *) record;
907 - (NSString *) distribution;
913 - (NSString *) description;
914 - (NSString *) label;
915 - (NSString *) origin;
916 - (NSString *) version;
918 - (NSString *) defaultIcon;
922 @implementation Source
926 [distribution_ release];
929 if (description_ != nil)
930 [description_ release];
937 if (defaultIcon_ != nil)
938 [defaultIcon_ release];
945 + (NSArray *) _attributeKeys {
946 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
949 - (NSArray *) attributeKeys {
950 return [[self class] _attributeKeys];
953 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
954 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
957 - (Source *) initWithMetaIndex:(metaIndex *)index {
958 if ((self = [super init]) != nil) {
959 trusted_ = index->IsTrusted();
961 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
962 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
963 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
965 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
966 if (dindex != NULL) {
967 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
969 while (std::getline(release, line)) {
970 std::string::size_type colon(line.find(':'));
971 if (colon == std::string::npos)
974 std::string name(line.substr(0, colon));
975 std::string value(line.substr(colon + 1));
976 while (!value.empty() && value[0] == ' ')
977 value = value.substr(1);
979 if (name == "Default-Icon")
980 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
981 else if (name == "Description")
982 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
983 else if (name == "Label")
984 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
985 else if (name == "Origin")
986 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
987 else if (name == "Version")
988 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
992 record_ = [Sources_ objectForKey:[self key]];
994 record_ = [record_ retain];
998 - (NSComparisonResult) compareByNameAndType:(Source *)source {
999 NSDictionary *lhr = [self record];
1000 NSDictionary *rhr = [source record];
1003 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1005 NSString *lhs = [self name];
1006 NSString *rhs = [source name];
1008 if ([lhs length] != 0 && [rhs length] != 0) {
1009 unichar lhc = [lhs characterAtIndex:0];
1010 unichar rhc = [rhs characterAtIndex:0];
1012 if (isalpha(lhc) && !isalpha(rhc))
1013 return NSOrderedAscending;
1014 else if (!isalpha(lhc) && isalpha(rhc))
1015 return NSOrderedDescending;
1018 return [lhs compare:rhs options:CompareOptions_];
1021 - (NSDictionary *) record {
1029 - (NSString *) uri {
1033 - (NSString *) distribution {
1034 return distribution_;
1037 - (NSString *) type {
1041 - (NSString *) key {
1042 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1045 - (NSString *) host {
1046 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1049 - (NSString *) name {
1050 return origin_ == nil ? [self host] : origin_;
1053 - (NSString *) description {
1054 return description_;
1057 - (NSString *) label {
1058 return label_ == nil ? [self host] : label_;
1061 - (NSString *) origin {
1065 - (NSString *) version {
1069 - (NSString *) defaultIcon {
1070 return defaultIcon_;
1075 /* Relationship Class {{{ */
1076 @interface Relationship : NSObject {
1081 - (NSString *) type;
1083 - (NSString *) name;
1087 @implementation Relationship
1095 - (NSString *) type {
1103 - (NSString *) name {
1110 /* Package Class {{{ */
1111 NSString *Scour(const char *field, const char *begin, const char *end) {
1112 size_t i(0), l(strlen(field));
1115 const char *name = begin + i;
1116 const char *colon = name + l;
1117 const char *value = colon + 1;
1122 strncasecmp(name, field, l) == 0
1124 while (value != end && value[0] == ' ')
1126 const char *line = std::find(value, end, '\n');
1127 while (line != value && line[-1] == ' ')
1130 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1132 begin = std::find(begin, end, '\n');
1140 @interface Package : NSObject {
1141 pkgCache::PkgIterator iterator_;
1142 _transient Database *database_;
1143 pkgCache::VerIterator version_;
1144 pkgCache::VerFileIterator file_;
1152 NSString *installed_;
1158 NSString *depiction_;
1159 NSString *homepage_;
1165 NSArray *relationships_;
1168 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1169 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1171 - (pkgCache::PkgIterator) iterator;
1173 - (NSString *) section;
1174 - (NSString *) simpleSection;
1176 - (Address *) maintainer;
1178 - (NSString *) description;
1179 - (NSString *) index;
1181 - (NSMutableDictionary *) metadata;
1183 - (BOOL) subscribed;
1186 - (NSString *) latest;
1187 - (NSString *) installed;
1190 - (BOOL) upgradableAndEssential:(BOOL)essential;
1193 - (BOOL) unfiltered;
1197 - (BOOL) halfConfigured;
1198 - (BOOL) halfInstalled;
1200 - (NSString *) mode;
1203 - (NSString *) name;
1204 - (NSString *) tagline;
1206 - (NSString *) homepage;
1207 - (NSString *) depiction;
1208 - (Address *) author;
1210 - (NSArray *) files;
1211 - (NSArray *) relationships;
1212 - (NSArray *) warnings;
1213 - (NSArray *) applications;
1215 - (Source *) source;
1216 - (NSString *) role;
1218 - (BOOL) matches:(NSString *)text;
1220 - (bool) hasSupportingRole;
1221 - (BOOL) hasTag:(NSString *)tag;
1222 - (NSString *) primaryPurpose;
1223 - (NSArray *) purposes;
1225 - (NSComparisonResult) compareByName:(Package *)package;
1226 - (NSComparisonResult) compareBySection:(Package *)package;
1228 - (uint32_t) compareForChanges;
1233 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1234 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1235 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1236 - (NSNumber *) isVisibleInSource:(Source *)source;
1240 @implementation Package
1246 if (section_ != nil)
1250 if (installed_ != nil)
1251 [installed_ release];
1259 if (depiction_ != nil)
1260 [depiction_ release];
1261 if (homepage_ != nil)
1262 [homepage_ release];
1263 if (sponsor_ != nil)
1272 if (relationships_ != nil)
1273 [relationships_ release];
1278 + (NSArray *) _attributeKeys {
1279 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1282 - (NSArray *) attributeKeys {
1283 return [[self class] _attributeKeys];
1286 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1287 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1290 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1291 if ((self = [super init]) != nil) {
1292 iterator_ = iterator;
1293 database_ = database;
1295 version_ = [database_ policy]->GetCandidateVer(iterator_);
1296 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1297 latest_ = [StripVersion(latest) retain];
1299 pkgCache::VerIterator current = iterator_.CurrentVer();
1300 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1301 installed_ = [StripVersion(installed) retain];
1303 if (!version_.end())
1304 file_ = version_.FileList();
1306 pkgCache &cache([database_ cache]);
1307 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1310 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1313 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1315 const char *begin, *end;
1316 parser->GetRec(begin, end);
1318 name_ = Scour("name", begin, end);
1320 name_ = [name_ retain];
1321 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1322 icon_ = Scour("icon", begin, end);
1324 icon_ = [icon_ retain];
1325 depiction_ = Scour("depiction", begin, end);
1326 if (depiction_ != nil)
1327 depiction_ = [depiction_ retain];
1328 homepage_ = Scour("homepage", begin, end);
1329 if (homepage_ == nil)
1330 homepage_ = Scour("website", begin, end);
1331 if ([homepage_ isEqualToString:depiction_])
1333 if (homepage_ != nil)
1334 homepage_ = [homepage_ retain];
1335 NSString *sponsor = Scour("sponsor", begin, end);
1337 sponsor_ = [[Address addressWithString:sponsor] retain];
1338 NSString *author = Scour("author", begin, end);
1340 author_ = [[Address addressWithString:author] retain];
1341 NSString *tags = Scour("tag", begin, end);
1343 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1347 for (int i(0), e([tags_ count]); i != e; ++i) {
1348 NSString *tag = [tags_ objectAtIndex:i];
1349 if ([tag hasPrefix:@"role::"]) {
1350 role_ = [[tag substringFromIndex:6] retain];
1355 NSString *solid(latest == nil ? installed : latest);
1356 bool changed(false);
1358 NSString *key([id_ lowercaseString]);
1360 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1361 if (metadata == nil) {
1362 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1367 [metadata setObject:solid forKey:@"LastVersion"];
1370 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1371 NSDate *last([metadata objectForKey:@"LastSeen"]);
1372 NSString *version([metadata objectForKey:@"LastVersion"]);
1375 first = last == nil ? now_ : last;
1376 [metadata setObject:first forKey:@"FirstSeen"];
1381 if (version == nil) {
1382 [metadata setObject:solid forKey:@"LastVersion"];
1384 } else if (![version isEqualToString:solid]) {
1385 [metadata setObject:solid forKey:@"LastVersion"];
1387 [metadata setObject:last forKey:@"LastSeen"];
1393 [Packages_ setObject:metadata forKey:key];
1399 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1400 return [[[Package alloc]
1401 initWithIterator:iterator
1406 - (pkgCache::PkgIterator) iterator {
1410 - (NSString *) section {
1411 if (section_ != nil)
1414 const char *section = iterator_.Section();
1415 if (section == NULL)
1418 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1421 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1422 if (NSString *rename = [value objectForKey:@"Rename"]) {
1427 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1431 - (NSString *) simpleSection {
1432 if (NSString *section = [self section])
1433 return Simplify(section);
1439 - (Address *) maintainer {
1442 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1443 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1447 return version_.end() ? 0 : version_->InstalledSize;
1450 - (NSString *) description {
1453 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1454 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1456 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1457 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1458 if ([lines count] < 2)
1461 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1462 for (size_t i(1); i != [lines count]; ++i) {
1463 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1464 [trimmed addObject:trim];
1467 return [trimmed componentsJoinedByString:@"\n"];
1470 - (NSString *) index {
1471 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1472 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1475 - (NSMutableDictionary *) metadata {
1476 return [Packages_ objectForKey:[id_ lowercaseString]];
1480 NSDictionary *metadata([self metadata]);
1481 if ([self subscribed])
1482 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1484 return [metadata objectForKey:@"FirstSeen"];
1487 - (BOOL) subscribed {
1488 NSDictionary *metadata([self metadata]);
1489 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1490 return [subscribed boolValue];
1496 NSDictionary *metadata([self metadata]);
1497 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1498 return [ignored boolValue];
1503 - (NSString *) latest {
1507 - (NSString *) installed {
1512 return !version_.end();
1515 - (BOOL) upgradableAndEssential:(BOOL)essential {
1516 pkgCache::VerIterator current = iterator_.CurrentVer();
1519 return essential && [self essential];
1521 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1522 return !candidate.end() && candidate != current;
1526 - (BOOL) essential {
1527 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1531 return [database_ cache][iterator_].InstBroken();
1534 - (BOOL) unfiltered {
1535 NSString *section = [self section];
1536 return section == nil || isSectionVisible(section);
1540 return [self hasSupportingRole] && [self unfiltered];
1544 unsigned char current = iterator_->CurrentState;
1545 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1548 - (BOOL) halfConfigured {
1549 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1552 - (BOOL) halfInstalled {
1553 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1557 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1558 return state.Mode != pkgDepCache::ModeKeep;
1561 - (NSString *) mode {
1562 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1564 switch (state.Mode) {
1565 case pkgDepCache::ModeDelete:
1566 if ((state.iFlags & pkgDepCache::Purge) != 0)
1570 case pkgDepCache::ModeKeep:
1571 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1575 case pkgDepCache::ModeInstall:
1576 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1577 return @"Reinstall";
1578 else switch (state.Status) {
1580 return @"Downgrade";
1586 return @"New Install";
1599 - (NSString *) name {
1600 return name_ == nil ? id_ : name_;
1603 - (NSString *) tagline {
1607 - (UIImage *) icon {
1608 NSString *section = [self simpleSection];
1611 if (NSString *icon = icon_)
1612 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1613 if (icon == nil) if (section != nil)
1614 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1615 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1616 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1618 icon = [UIImage applicationImageNamed:@"unknown.png"];
1622 - (NSString *) homepage {
1626 - (NSString *) depiction {
1630 - (Address *) sponsor {
1634 - (Address *) author {
1638 - (NSArray *) files {
1639 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1640 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1643 fin.open([path UTF8String]);
1648 while (std::getline(fin, line))
1649 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1654 - (NSArray *) relationships {
1655 return relationships_;
1658 - (NSArray *) warnings {
1659 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1660 const char *name(iterator_.Name());
1662 size_t length(strlen(name));
1663 if (length < 2) invalid:
1664 [warnings addObject:@"illegal package identifier"];
1665 else for (size_t i(0); i != length; ++i)
1667 (name[i] < 'a' || name[i] > 'z') &&
1668 (name[i] < '0' || name[i] > '9') &&
1669 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1672 if (strcmp(name, "cydia") != 0) {
1676 if (NSArray *files = [self files])
1677 for (NSString *file in files)
1678 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1680 else if (!stash && [file isEqualToString:@"/var/stash"])
1684 [warnings addObject:@"files installed into Cydia.app"];
1686 [warnings addObject:@"files installed to /var/stash"];
1689 return [warnings count] == 0 ? nil : warnings;
1692 - (NSArray *) applications {
1693 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1695 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1697 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1698 if (NSArray *files = [self files])
1699 for (NSString *file in files)
1700 if (application_r(file)) {
1701 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1702 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1703 if ([id isEqualToString:me])
1706 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1708 display = application_r[1];
1710 NSString *bundle([file stringByDeletingLastPathComponent]);
1711 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1712 if (icon == nil || [icon length] == 0)
1714 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1716 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1717 [applications addObject:application];
1719 [application addObject:id];
1720 [application addObject:display];
1721 [application addObject:url];
1724 return [applications count] == 0 ? nil : applications;
1727 - (Source *) source {
1729 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1736 - (NSString *) role {
1740 - (BOOL) matches:(NSString *)text {
1746 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1747 if (range.location != NSNotFound)
1750 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1751 if (range.location != NSNotFound)
1754 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1755 if (range.location != NSNotFound)
1761 - (bool) hasSupportingRole {
1764 if ([role_ isEqualToString:@"enduser"])
1766 if ([Role_ isEqualToString:@"User"])
1768 if ([role_ isEqualToString:@"hacker"])
1770 if ([Role_ isEqualToString:@"Hacker"])
1772 if ([role_ isEqualToString:@"developer"])
1774 if ([Role_ isEqualToString:@"Developer"])
1779 - (BOOL) hasTag:(NSString *)tag {
1780 return tags_ == nil ? NO : [tags_ containsObject:tag];
1783 - (NSString *) primaryPurpose {
1784 for (NSString *tag in tags_)
1785 if ([tag hasPrefix:@"purpose::"])
1786 return [tag substringFromIndex:9];
1790 - (NSArray *) purposes {
1791 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1792 for (NSString *tag in tags_)
1793 if ([tag hasPrefix:@"purpose::"])
1794 [purposes addObject:[tag substringFromIndex:9]];
1795 return [purposes count] == 0 ? nil : purposes;
1798 - (NSComparisonResult) compareByName:(Package *)package {
1799 NSString *lhs = [self name];
1800 NSString *rhs = [package name];
1802 if ([lhs length] != 0 && [rhs length] != 0) {
1803 unichar lhc = [lhs characterAtIndex:0];
1804 unichar rhc = [rhs characterAtIndex:0];
1806 if (isalpha(lhc) && !isalpha(rhc))
1807 return NSOrderedAscending;
1808 else if (!isalpha(lhc) && isalpha(rhc))
1809 return NSOrderedDescending;
1812 return [lhs compare:rhs options:CompareOptions_];
1815 - (NSComparisonResult) compareBySection:(Package *)package {
1816 NSString *lhs = [self section];
1817 NSString *rhs = [package section];
1819 if (lhs == NULL && rhs != NULL)
1820 return NSOrderedAscending;
1821 else if (lhs != NULL && rhs == NULL)
1822 return NSOrderedDescending;
1823 else if (lhs != NULL && rhs != NULL) {
1824 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1825 if (result != NSOrderedSame)
1829 return NSOrderedSame;
1832 - (uint32_t) compareForChanges {
1837 uint32_t timestamp : 30;
1838 uint32_t ignored : 1;
1839 uint32_t upgradable : 1;
1843 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1845 if ([self upgradableAndEssential:YES]) {
1846 value.bits.timestamp = 0;
1847 value.bits.ignored = [self ignored] ? 0 : 1;
1848 value.bits.upgradable = 1;
1850 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1851 value.bits.ignored = 0;
1852 value.bits.upgradable = 0;
1855 return _not(uint32_t) - value.key;
1859 pkgProblemResolver *resolver = [database_ resolver];
1860 resolver->Clear(iterator_);
1861 resolver->Protect(iterator_);
1862 pkgCacheFile &cache([database_ cache]);
1863 cache->MarkInstall(iterator_, false);
1864 pkgDepCache::StateCache &state((*cache)[iterator_]);
1865 if (!state.Install())
1866 cache->SetReInstall(iterator_, true);
1870 pkgProblemResolver *resolver = [database_ resolver];
1871 resolver->Clear(iterator_);
1872 resolver->Protect(iterator_);
1873 resolver->Remove(iterator_);
1874 [database_ cache]->MarkDelete(iterator_, true);
1877 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1878 return [NSNumber numberWithBool:(
1879 [self unfiltered] && [self matches:search]
1883 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1884 return [NSNumber numberWithBool:(
1885 (![number boolValue] || [self visible]) && [self installed] != nil
1889 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1890 NSString *section = [self section];
1892 return [NSNumber numberWithBool:(
1894 [self installed] == nil && (
1896 section == nil && [name length] == 0 ||
1897 [name isEqualToString:section]
1902 - (NSNumber *) isVisibleInSource:(Source *)source {
1903 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1908 /* Section Class {{{ */
1909 @interface Section : NSObject {
1915 - (NSComparisonResult) compareByName:(Section *)section;
1916 - (Section *) initWithName:(NSString *)name;
1917 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1918 - (NSString *) name;
1921 - (void) addToCount;
1925 @implementation Section
1932 - (NSComparisonResult) compareByName:(Section *)section {
1933 NSString *lhs = [self name];
1934 NSString *rhs = [section name];
1936 if ([lhs length] != 0 && [rhs length] != 0) {
1937 unichar lhc = [lhs characterAtIndex:0];
1938 unichar rhc = [rhs characterAtIndex:0];
1940 if (isalpha(lhc) && !isalpha(rhc))
1941 return NSOrderedAscending;
1942 else if (!isalpha(lhc) && isalpha(rhc))
1943 return NSOrderedDescending;
1946 return [lhs compare:rhs options:CompareOptions_];
1949 - (Section *) initWithName:(NSString *)name {
1950 return [self initWithName:name row:0];
1953 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1954 if ((self = [super init]) != nil) {
1955 name_ = [name retain];
1960 - (NSString *) name {
1972 - (void) addToCount {
1980 static NSArray *Finishes_;
1982 /* Database Implementation {{{ */
1983 @implementation Database
1985 + (Database *) sharedInstance {
1986 static Database *instance;
1987 if (instance == nil)
1988 instance = [[Database alloc] init];
1997 - (void) _readCydia:(NSNumber *)fd { _pooled
1998 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1999 std::istream is(&ib);
2002 static Pcre finish_r("^finish:([^:]*)$");
2004 while (std::getline(is, line)) {
2005 const char *data(line.c_str());
2006 size_t size = line.size();
2007 lprintf("C:%s\n", data);
2009 if (finish_r(data, size)) {
2010 NSString *finish = finish_r[1];
2011 int index = [Finishes_ indexOfObject:finish];
2012 if (index != INT_MAX && index > Finish_)
2020 - (void) _readStatus:(NSNumber *)fd { _pooled
2021 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2022 std::istream is(&ib);
2025 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2026 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2028 while (std::getline(is, line)) {
2029 const char *data(line.c_str());
2030 size_t size = line.size();
2031 lprintf("S:%s\n", data);
2033 if (conffile_r(data, size)) {
2034 [delegate_ setConfigurationData:conffile_r[1]];
2035 } else if (strncmp(data, "status: ", 8) == 0) {
2036 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2037 [delegate_ setProgressTitle:string];
2038 } else if (pmstatus_r(data, size)) {
2039 std::string type([pmstatus_r[1] UTF8String]);
2040 NSString *id = pmstatus_r[2];
2042 float percent([pmstatus_r[3] floatValue]);
2043 [delegate_ setProgressPercent:(percent / 100)];
2045 NSString *string = pmstatus_r[4];
2047 if (type == "pmerror")
2048 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2049 withObject:[NSArray arrayWithObjects:string, id, nil]
2052 else if (type == "pmstatus")
2053 [delegate_ setProgressTitle:string];
2054 else if (type == "pmconffile")
2055 [delegate_ setConfigurationData:string];
2056 else _assert(false);
2057 } else _assert(false);
2063 - (void) _readOutput:(NSNumber *)fd { _pooled
2064 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2065 std::istream is(&ib);
2068 while (std::getline(is, line)) {
2069 lprintf("O:%s\n", line.c_str());
2070 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2080 - (Package *) packageWithName:(NSString *)name {
2081 if (static_cast<pkgDepCache *>(cache_) == NULL)
2083 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2084 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2087 - (Database *) init {
2088 if ((self = [super init]) != nil) {
2095 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2096 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2100 _assert(pipe(fds) != -1);
2103 _config->Set("APT::Keep-Fds::", cydiafd_);
2104 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2107 detachNewThreadSelector:@selector(_readCydia:)
2109 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2112 _assert(pipe(fds) != -1);
2116 detachNewThreadSelector:@selector(_readStatus:)
2118 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2121 _assert(pipe(fds) != -1);
2122 _assert(dup2(fds[0], 0) != -1);
2123 _assert(close(fds[0]) != -1);
2125 input_ = fdopen(fds[1], "a");
2127 _assert(pipe(fds) != -1);
2128 _assert(dup2(fds[1], 1) != -1);
2129 _assert(close(fds[1]) != -1);
2132 detachNewThreadSelector:@selector(_readOutput:)
2134 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2139 - (pkgCacheFile &) cache {
2143 - (pkgDepCache::Policy *) policy {
2147 - (pkgRecords *) records {
2151 - (pkgProblemResolver *) resolver {
2155 - (pkgAcquire &) fetcher {
2159 - (NSArray *) packages {
2163 - (NSArray *) sources {
2164 return [sources_ allValues];
2167 - (NSArray *) issues {
2168 if (cache_->BrokenCount() == 0)
2171 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2173 for (Package *package in packages_) {
2174 if (![package broken])
2176 pkgCache::PkgIterator pkg([package iterator]);
2178 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2179 [entry addObject:[package name]];
2180 [issues addObject:entry];
2182 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2186 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2187 pkgCache::DepIterator start;
2188 pkgCache::DepIterator end;
2189 dep.GlobOr(start, end); // ++dep
2191 if (!cache_->IsImportantDep(end))
2193 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2196 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2197 [entry addObject:failure];
2198 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2200 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2201 [failure addObject:[package name]];
2203 pkgCache::PkgIterator target(start.TargetPkg());
2204 if (target->ProvidesList != 0)
2205 [failure addObject:@"?"];
2207 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2209 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2210 else if (!cache_[target].CandidateVerIter(cache_).end())
2211 [failure addObject:@"-"];
2212 else if (target->ProvidesList == 0)
2213 [failure addObject:@"!"];
2215 [failure addObject:@"%"];
2219 if (start.TargetVer() != 0)
2220 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2231 - (void) reloadData {
2250 if (!cache_.Open(progress_, true)) {
2252 if (!_error->PopMessage(error))
2255 lprintf("cache_.Open():[%s]\n", error.c_str());
2257 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2258 [delegate_ repairWithSelector:@selector(configure)];
2259 else if (error == "The package lists or status file could not be parsed or opened.")
2260 [delegate_ repairWithSelector:@selector(update)];
2261 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2262 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2263 // else if (error == "The list of sources could not be read.")
2264 else _assert(false);
2269 now_ = [[NSDate date] retain];
2271 policy_ = new pkgDepCache::Policy();
2272 records_ = new pkgRecords(cache_);
2273 resolver_ = new pkgProblemResolver(cache_);
2274 fetcher_ = new pkgAcquire(&status_);
2277 list_ = new pkgSourceList();
2278 _assert(list_->ReadMainList());
2280 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2281 _assert(pkgApplyStatus(cache_));
2283 if (cache_->BrokenCount() != 0) {
2284 _assert(pkgFixBroken(cache_));
2285 _assert(cache_->BrokenCount() == 0);
2286 _assert(pkgMinimizeUpgrade(cache_));
2289 [sources_ removeAllObjects];
2290 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2291 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2292 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2294 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2295 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2299 [packages_ removeAllObjects];
2300 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2301 if (Package *package = [Package packageWithIterator:iterator database:self])
2302 [packages_ addObject:package];
2304 [packages_ sortUsingSelector:@selector(compareByName:)];
2307 - (void) configure {
2308 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2309 system([dpkg UTF8String]);
2317 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2318 _assert(!_error->PendingError());
2321 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2324 public pkgArchiveCleaner
2327 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2332 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2334 while (_error->PopMessage(error))
2335 lprintf("ArchiveCleaner: %s\n", error.c_str());
2340 pkgRecords records(cache_);
2342 lock_ = new FileFd();
2343 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2344 _assert(!_error->PendingError());
2347 // XXX: explain this with an error message
2348 _assert(list.ReadMainList());
2350 manager_ = (_system->CreatePM(cache_));
2351 _assert(manager_->GetArchives(fetcher_, &list, &records));
2352 _assert(!_error->PendingError());
2356 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2358 _assert(list.ReadMainList());
2359 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2360 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2363 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2368 bool failed = false;
2369 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2370 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2373 std::string uri = (*item)->DescURI();
2374 std::string error = (*item)->ErrorText;
2376 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2379 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2380 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2391 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2393 if (_error->PendingError()) {
2398 if (result == pkgPackageManager::Failed) {
2403 if (result != pkgPackageManager::Completed) {
2408 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2410 _assert(list.ReadMainList());
2411 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2412 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2415 if (![before isEqualToArray:after])
2420 _assert(pkgDistUpgrade(cache_));
2424 [self updateWithStatus:status_];
2427 - (void) updateWithStatus:(Status &)status {
2429 _assert(list.ReadMainList());
2432 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2433 _assert(!_error->PendingError());
2435 pkgAcquire fetcher(&status);
2436 _assert(list.GetIndexes(&fetcher));
2438 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2439 bool failed = false;
2440 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2441 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2442 (*item)->Finished();
2446 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2447 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2448 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2451 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2456 - (void) setDelegate:(id)delegate {
2457 delegate_ = delegate;
2458 status_.setDelegate(delegate);
2459 progress_.setDelegate(delegate);
2462 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2463 pkgIndexFile *index(NULL);
2464 list_->FindIndex(file, index);
2465 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2471 /* PopUp Windows {{{ */
2472 @interface PopUpView : UIView {
2473 _transient id delegate_;
2474 UITransitionView *transition_;
2479 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2483 @implementation PopUpView
2486 [transition_ setDelegate:nil];
2487 [transition_ release];
2493 [transition_ transition:UITransitionPushFromTop toView:nil];
2496 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2497 if (from != nil && to == nil)
2498 [self removeFromSuperview];
2501 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2502 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2503 delegate_ = delegate;
2505 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2506 [self addSubview:transition_];
2508 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2510 [view addSubview:self];
2512 [transition_ setDelegate:self];
2514 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2515 [transition_ transition:UITransitionNone toView:blank];
2516 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2523 /* Mail Composition {{{ */
2524 @interface MailToView : PopUpView {
2525 MailComposeController *controller_;
2528 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2532 @implementation MailToView
2535 [controller_ release];
2539 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2543 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2544 NSLog(@"did:%@", delivery);
2545 // [UIApp setStatusBarShowsProgress:NO];
2546 if ([controller error]){
2547 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2548 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2549 [mailAlertSheet setBodyText:[controller error]];
2550 [mailAlertSheet popupAlertAnimated:YES];
2554 - (void) showError {
2555 NSLog(@"%@", [controller_ error]);
2556 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2557 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2558 [mailAlertSheet setBodyText:[controller_ error]];
2559 [mailAlertSheet popupAlertAnimated:YES];
2562 - (void) deliverMessage { _pooled
2566 if (![controller_ deliverMessage])
2567 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2570 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2571 if ([controller_ needsDelivery])
2572 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2577 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2578 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2579 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2580 [controller_ setDelegate:self];
2581 [controller_ initializeUI];
2582 [controller_ setupForURL:url];
2584 UIView *view([controller_ view]);
2585 [overlay_ addSubview:view];
2591 /* Confirmation View {{{ */
2592 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2593 if (!iterator.end())
2594 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2595 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2597 pkgCache::PkgIterator package(dep.TargetPkg());
2600 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2607 @protocol ConfirmationViewDelegate
2612 @interface ConfirmationView : BrowserView {
2613 _transient Database *database_;
2614 UIActionSheet *essential_;
2621 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2625 @implementation ConfirmationView
2632 if (essential_ != nil)
2633 [essential_ release];
2639 [book_ popFromSuperviewAnimated:YES];
2642 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2643 NSString *context = [sheet context];
2645 if ([context isEqualToString:@"remove"])
2653 [delegate_ confirm];
2658 else if ([context isEqualToString:@"unable"])
2664 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2665 [window setValue:changes_ forKey:@"changes"];
2666 [window setValue:issues_ forKey:@"issues"];
2667 [window setValue:sizes_ forKey:@"sizes"];
2668 [super webView:sender didClearWindowObject:window forFrame:frame];
2671 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2672 if ((self = [super initWithBook:book]) != nil) {
2673 database_ = database;
2675 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2676 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2677 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2678 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2679 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2683 pkgDepCache::Policy *policy([database_ policy]);
2685 pkgCacheFile &cache([database_ cache]);
2686 NSArray *packages = [database_ packages];
2687 for (size_t i(0), e = [packages count]; i != e; ++i) {
2688 Package *package = [packages objectAtIndex:i];
2689 pkgCache::PkgIterator iterator = [package iterator];
2690 pkgDepCache::StateCache &state(cache[iterator]);
2692 NSString *name([package name]);
2694 if (state.NewInstall())
2695 [installing addObject:name];
2696 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2697 [reinstalling addObject:name];
2698 else if (state.Upgrade())
2699 [upgrading addObject:name];
2700 else if (state.Downgrade())
2701 [downgrading addObject:name];
2702 else if (state.Delete()) {
2703 if ([package essential])
2705 [removing addObject:name];
2708 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2709 substrate_ |= DepSubstrate(iterator.CurrentVer());
2714 else if (Advanced_ || true) {
2715 essential_ = [[UIActionSheet alloc]
2716 initWithTitle:@"Removing Essentials"
2717 buttons:[NSArray arrayWithObjects:
2718 @"Cancel Operation (Safe)",
2719 @"Force Removal (Unsafe)",
2721 defaultButtonIndex:0
2727 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2729 [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."];
2731 essential_ = [[UIActionSheet alloc]
2732 initWithTitle:@"Unable to Comply"
2733 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2734 defaultButtonIndex:0
2739 [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."];
2742 changes_ = [[NSArray alloc] initWithObjects:
2750 issues_ = [database_ issues];
2752 issues_ = [issues_ retain];
2754 sizes_ = [[NSArray alloc] initWithObjects:
2755 SizeString([database_ fetcher].FetchNeeded()),
2756 SizeString([database_ fetcher].PartialPresent()),
2757 SizeString([database_ cache]->UsrSize()),
2760 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2764 - (NSString *) backButtonTitle {
2768 - (NSString *) leftButtonTitle {
2772 - (NSString *) _rightButtonTitle {
2773 return issues_ == nil ? @"Confirm" : nil;
2776 - (void) _leftButtonClicked {
2780 - (void) _rightButtonClicked {
2781 if (essential_ != nil)
2782 [essential_ popupAlertAnimated:YES];
2786 [delegate_ confirm];
2793 /* Progress Data {{{ */
2794 @interface ProgressData : NSObject {
2800 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2807 @implementation ProgressData
2809 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2810 if ((self = [super init]) != nil) {
2811 selector_ = selector;
2831 /* Progress View {{{ */
2832 @interface ProgressView : UIView <
2833 ConfigurationDelegate,
2836 _transient Database *database_;
2838 UIView *background_;
2839 UITransitionView *transition_;
2841 UINavigationBar *navbar_;
2842 UIProgressBar *progress_;
2843 UITextView *output_;
2844 UITextLabel *status_;
2845 UIPushButton *close_;
2848 SHA1SumValue springlist_;
2849 SHA1SumValue sandplate_;
2851 NSTimeInterval last_;
2854 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2856 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2857 - (void) setContentView:(UIView *)view;
2860 - (void) _retachThread;
2861 - (void) _detachNewThreadData:(ProgressData *)data;
2862 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2868 @protocol ProgressViewDelegate
2869 - (void) progressViewIsComplete:(ProgressView *)sender;
2872 @implementation ProgressView
2875 [transition_ setDelegate:nil];
2876 [navbar_ setDelegate:nil];
2879 if (background_ != nil)
2880 [background_ release];
2881 [transition_ release];
2884 [progress_ release];
2891 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2892 if (bootstrap_ && from == overlay_ && to == view_)
2896 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2897 if ((self = [super initWithFrame:frame]) != nil) {
2898 database_ = database;
2899 delegate_ = delegate;
2901 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2902 [transition_ setDelegate:self];
2904 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2907 [overlay_ setBackgroundColor:[UIColor blackColor]];
2909 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2910 [background_ setBackgroundColor:[UIColor blackColor]];
2911 [self addSubview:background_];
2914 [self addSubview:transition_];
2916 CGSize navsize = [UINavigationBar defaultSize];
2917 CGRect navrect = {{0, 0}, navsize};
2919 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2920 [overlay_ addSubview:navbar_];
2922 [navbar_ setBarStyle:1];
2923 [navbar_ setDelegate:self];
2925 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2926 [navbar_ pushNavigationItem:navitem];
2928 CGRect bounds = [overlay_ bounds];
2929 CGSize prgsize = [UIProgressBar defaultSize];
2932 (bounds.size.width - prgsize.width) / 2,
2933 bounds.size.height - prgsize.height - 20
2936 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2937 [progress_ setStyle:0];
2939 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2941 bounds.size.height - prgsize.height - 50,
2942 bounds.size.width - 20,
2946 [status_ setColor:[UIColor whiteColor]];
2947 [status_ setBackgroundColor:[UIColor clearColor]];
2949 [status_ setCentersHorizontally:YES];
2950 //[status_ setFont:font];
2952 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2954 navrect.size.height + 20,
2955 bounds.size.width - 20,
2956 bounds.size.height - navsize.height - 62 - navrect.size.height
2959 //[output_ setTextFont:@"Courier New"];
2960 [output_ setTextSize:12];
2962 [output_ setTextColor:[UIColor whiteColor]];
2963 [output_ setBackgroundColor:[UIColor clearColor]];
2965 [output_ setMarginTop:0];
2966 [output_ setAllowsRubberBanding:YES];
2967 [output_ setEditable:NO];
2969 [overlay_ addSubview:output_];
2971 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2973 bounds.size.height - prgsize.height - 50,
2974 bounds.size.width - 20,
2978 [close_ setAutosizesToFit:NO];
2979 [close_ setDrawsShadow:YES];
2980 [close_ setStretchBackground:YES];
2981 [close_ setEnabled:YES];
2983 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2984 [close_ setTitleFont:bold];
2986 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2987 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2988 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2992 - (void) setContentView:(UIView *)view {
2993 view_ = [view retain];
2996 - (void) resetView {
2997 [transition_ transition:6 toView:view_];
3000 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3001 NSString *context = [sheet context];
3002 if ([context isEqualToString:@"conffile"]) {
3003 FILE *input = [database_ input];
3007 fprintf(input, "N\n");
3011 fprintf(input, "Y\n");
3022 - (void) closeButtonPushed {
3031 [delegate_ suspendWithAnimation:YES];
3035 system("launchctl stop com.apple.SpringBoard");
3039 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3048 - (void) _retachThread {
3049 UINavigationItem *item = [navbar_ topItem];
3050 [item setTitle:@"Complete"];
3052 [overlay_ addSubview:close_];
3053 [progress_ removeFromSuperview];
3054 [status_ removeFromSuperview];
3056 [delegate_ progressViewIsComplete:self];
3059 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3060 MMap mmap(file, MMap::ReadOnly);
3062 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3063 if (!(sandplate_ == sha1.Result()))
3068 FileFd file(SpringBoard_, FileFd::ReadOnly);
3069 MMap mmap(file, MMap::ReadOnly);
3071 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3072 if (!(springlist_ == sha1.Result()))
3077 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3078 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3079 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3080 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3081 case 4: [close_ setTitle:@"Reboot Device"]; break;
3084 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3086 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3087 [cache autorelease];
3089 NSFileManager *manager = [NSFileManager defaultManager];
3090 NSError *error = nil;
3092 id system = [cache objectForKey:@"System"];
3097 if (stat(Cache_, &info) == -1)
3100 [system removeAllObjects];
3102 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3103 for (NSString *app in apps)
3104 if ([app hasSuffix:@".app"]) {
3105 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3106 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3107 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3109 [info setObject:path forKey:@"Path"];
3110 [info setObject:@"System" forKey:@"ApplicationType"];
3111 [system addInfoDictionary:info];
3116 [cache writeToFile:@Cache_ atomically:YES];
3118 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3120 if (chmod(Cache_, info.st_mode) == -1)
3124 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3127 notify_post("com.apple.mobile.application_installed");
3129 [delegate_ setStatusBarShowsProgress:NO];
3132 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3133 [[data target] performSelector:[data selector] withObject:[data object]];
3136 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3139 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3140 UINavigationItem *item = [navbar_ topItem];
3141 [item setTitle:title];
3143 [status_ setText:nil];
3144 [output_ setText:@""];
3145 [progress_ setProgress:0];
3148 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3150 [close_ removeFromSuperview];
3151 [overlay_ addSubview:progress_];
3152 [overlay_ addSubview:status_];
3154 [delegate_ setStatusBarShowsProgress:YES];
3158 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3159 MMap mmap(file, MMap::ReadOnly);
3161 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3162 sandplate_ = sha1.Result();
3166 FileFd file(SpringBoard_, FileFd::ReadOnly);
3167 MMap mmap(file, MMap::ReadOnly);
3169 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3170 springlist_ = sha1.Result();
3173 [transition_ transition:6 toView:overlay_];
3176 detachNewThreadSelector:@selector(_detachNewThreadData:)
3178 withObject:[[ProgressData alloc]
3179 initWithSelector:selector
3186 - (void) repairWithSelector:(SEL)selector {
3188 detachNewThreadSelector:selector
3195 - (void) setConfigurationData:(NSString *)data {
3197 performSelectorOnMainThread:@selector(_setConfigurationData:)
3203 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3204 Package *package = id == nil ? nil : [database_ packageWithName:id];
3206 UIActionSheet *sheet = [[[UIActionSheet alloc]
3207 initWithTitle:(package == nil ? id : [package name])
3208 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3209 defaultButtonIndex:0
3214 [sheet setBodyText:error];
3215 [sheet popupAlertAnimated:YES];
3218 - (void) setProgressTitle:(NSString *)title {
3220 performSelectorOnMainThread:@selector(_setProgressTitle:)
3226 - (void) setProgressPercent:(float)percent {
3228 performSelectorOnMainThread:@selector(_setProgressPercent:)
3229 withObject:[NSNumber numberWithFloat:percent]
3234 - (void) startProgress {
3235 last_ = [NSDate timeIntervalSinceReferenceDate];
3238 - (void) addProgressOutput:(NSString *)output {
3240 performSelectorOnMainThread:@selector(_addProgressOutput:)
3246 - (bool) isCancelling:(size_t)received {
3248 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3249 if (received_ != received) {
3250 received_ = received;
3252 } else if (now - last_ > 30)
3259 - (void) _setConfigurationData:(NSString *)data {
3260 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3262 _assert(conffile_r(data));
3264 NSString *ofile = conffile_r[1];
3265 //NSString *nfile = conffile_r[2];
3267 UIActionSheet *sheet = [[[UIActionSheet alloc]
3268 initWithTitle:@"Configuration Upgrade"
3269 buttons:[NSArray arrayWithObjects:
3270 @"Keep My Old Copy",
3271 @"Accept The New Copy",
3272 // XXX: @"See What Changed",
3274 defaultButtonIndex:0
3279 [sheet setBodyText:[NSString stringWithFormat:
3280 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3283 [sheet popupAlertAnimated:YES];
3286 - (void) _setProgressTitle:(NSString *)title {
3287 [status_ setText:title];
3290 - (void) _setProgressPercent:(NSNumber *)percent {
3291 [progress_ setProgress:[percent floatValue]];
3294 - (void) _addProgressOutput:(NSString *)output {
3295 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3296 CGSize size = [output_ contentSize];
3297 CGRect rect = {{0, size.height}, {size.width, 0}};
3298 [output_ scrollRectToVisible:rect animated:YES];
3301 - (BOOL) isRunning {
3308 /* Package Cell {{{ */
3309 @interface PackageCell : UISimpleTableCell {
3312 NSString *description_;
3316 UITextLabel *status_;
3320 - (PackageCell *) init;
3321 - (void) setPackage:(Package *)package;
3323 + (int) heightForPackage:(Package *)package;
3327 @implementation PackageCell
3329 - (void) clearPackage {
3340 if (description_ != nil) {
3341 [description_ release];
3345 if (source_ != nil) {
3350 if (badge_ != nil) {
3357 [self clearPackage];
3364 - (PackageCell *) init {
3365 if ((self = [super init]) != nil) {
3367 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3368 [status_ setBackgroundColor:[UIColor clearColor]];
3369 [status_ setFont:small];
3374 - (void) setPackage:(Package *)package {
3375 [self clearPackage];
3377 Source *source = [package source];
3378 NSString *section = [package simpleSection];
3380 icon_ = [[package icon] retain];
3382 name_ = [[package name] retain];
3383 description_ = [[package tagline] retain];
3385 NSString *label = nil;
3386 bool trusted = false;
3388 if (source != nil) {
3389 label = [source label];
3390 trusted = [source trusted];
3391 } else if ([[package id] isEqualToString:@"firmware"])
3394 label = @"Unknown/Local";
3396 NSString *from = [NSString stringWithFormat:@"from %@", label];
3398 if (section != nil && ![section isEqualToString:label])
3399 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3401 source_ = [from retain];
3403 if (NSString *purpose = [package primaryPurpose])
3404 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3405 badge_ = [badge_ retain];
3408 if (NSString *mode = [package mode]) {
3409 [badge_ setImage:[UIImage applicationImageNamed:
3410 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3413 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3414 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3415 } else if ([package half]) {
3416 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3417 [status_ setText:@"Package Damaged"];
3418 [status_ setColor:[UIColor redColor]];
3420 [badge_ setImage:nil];
3421 [status_ setText:nil];
3426 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3429 rect.size = [icon_ size];
3431 rect.size.width /= 2;
3432 rect.size.height /= 2;
3434 rect.origin.x = 25 - rect.size.width / 2;
3435 rect.origin.y = 25 - rect.size.height / 2;
3437 [icon_ drawInRect:rect];
3440 if (badge_ != nil) {
3441 CGSize size = [badge_ size];
3443 [badge_ drawAtPoint:CGPointMake(
3444 36 - size.width / 2,
3445 36 - size.height / 2
3454 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3455 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3459 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3461 [super drawContentInRect:rect selected:selected];
3464 + (int) heightForPackage:(Package *)package {
3465 NSString *tagline([package tagline]);
3466 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3468 if ([package hasMode] || [package half])
3477 /* Section Cell {{{ */
3478 @interface SectionCell : UISimpleTableCell {
3483 _UISwitchSlider *switch_;
3488 - (void) setSection:(Section *)section editing:(BOOL)editing;
3492 @implementation SectionCell
3494 - (void) clearSection {
3495 if (section_ != nil) {
3505 if (count_ != nil) {
3512 [self clearSection];
3519 if ((self = [super init]) != nil) {
3520 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3522 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3523 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3527 - (void) onSwitch:(id)sender {
3528 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3529 if (metadata == nil) {
3530 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3531 [Sections_ setObject:metadata forKey:section_];
3535 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3538 - (void) setSection:(Section *)section editing:(BOOL)editing {
3539 if (editing != editing_) {
3541 [switch_ removeFromSuperview];
3543 [self addSubview:switch_];
3547 [self clearSection];
3549 if (section == nil) {
3550 name_ = [@"All Packages" retain];
3553 section_ = [section name];
3554 if (section_ != nil)
3555 section_ = [section_ retain];
3556 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3557 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3560 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3564 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3565 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3572 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3574 CGSize size = [count_ sizeWithFont:Font14_];
3578 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3580 [super drawContentInRect:rect selected:selected];
3586 /* File Table {{{ */
3587 @interface FileTable : RVPage {
3588 _transient Database *database_;
3591 NSMutableArray *files_;
3595 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3596 - (void) setPackage:(Package *)package;
3600 @implementation FileTable
3603 if (package_ != nil)
3612 - (int) numberOfRowsInTable:(UITable *)table {
3613 return files_ == nil ? 0 : [files_ count];
3616 - (float) table:(UITable *)table heightForRow:(int)row {
3620 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3621 if (reusing == nil) {
3622 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3623 UIFont *font = [UIFont systemFontOfSize:16];
3624 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3626 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3630 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3634 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3635 if ((self = [super initWithBook:book]) != nil) {
3636 database_ = database;
3638 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3640 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3641 [self addSubview:list_];
3643 UITableColumn *column = [[[UITableColumn alloc]
3644 initWithTitle:@"Name"
3646 width:[self frame].size.width
3649 [list_ setDataSource:self];
3650 [list_ setSeparatorStyle:1];
3651 [list_ addTableColumn:column];
3652 [list_ setDelegate:self];
3653 [list_ setReusesTableCells:YES];
3657 - (void) setPackage:(Package *)package {
3658 if (package_ != nil) {
3659 [package_ autorelease];
3668 [files_ removeAllObjects];
3670 if (package != nil) {
3671 package_ = [package retain];
3672 name_ = [[package id] retain];
3674 if (NSArray *files = [package files])
3675 [files_ addObjectsFromArray:files];
3677 if ([files_ count] != 0) {
3678 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3679 [files_ removeObjectAtIndex:0];
3680 [files_ sortUsingSelector:@selector(compareByPath:)];
3682 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3683 [stack addObject:@"/"];
3685 for (int i(0), e([files_ count]); i != e; ++i) {
3686 NSString *file = [files_ objectAtIndex:i];
3687 while (![file hasPrefix:[stack lastObject]])
3688 [stack removeLastObject];
3689 NSString *directory = [stack lastObject];
3690 [stack addObject:[file stringByAppendingString:@"/"]];
3691 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3692 ([stack count] - 2) * 3, "",
3693 [file substringFromIndex:[directory length]]
3702 - (void) resetViewAnimated:(BOOL)animated {
3703 [list_ resetViewAnimated:animated];
3706 - (void) reloadData {
3707 [self setPackage:[database_ packageWithName:name_]];
3708 [self reloadButtons];
3711 - (NSString *) title {
3712 return @"Installed Files";
3715 - (NSString *) backButtonTitle {
3721 /* Package View {{{ */
3722 @interface PackageView : BrowserView {
3723 _transient Database *database_;
3726 NSMutableArray *buttons_;
3729 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3730 - (void) setPackage:(Package *)package;
3734 @implementation PackageView
3737 if (package_ != nil)
3745 - (void) _clickButtonWithName:(NSString *)name {
3746 if ([name isEqualToString:@"Install"])
3747 [delegate_ installPackage:package_];
3748 else if ([name isEqualToString:@"Reinstall"])
3749 [delegate_ installPackage:package_];
3750 else if ([name isEqualToString:@"Remove"])
3751 [delegate_ removePackage:package_];
3752 else if ([name isEqualToString:@"Upgrade"])
3753 [delegate_ installPackage:package_];
3754 else _assert(false);
3757 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3758 int count = [buttons_ count];
3759 _assert(count != 0);
3760 _assert(button <= count + 1);
3762 if (count != button - 1)
3763 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3768 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3769 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3770 return [super webView:sender didFinishLoadForFrame:frame];
3773 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3774 [window setValue:package_ forKey:@"package"];
3775 [super webView:sender didClearWindowObject:window forFrame:frame];
3778 - (void) _rightButtonClicked {
3779 /*[super _rightButtonClicked];
3782 int count = [buttons_ count];
3783 _assert(count != 0);
3786 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3788 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3789 [buttons addObjectsFromArray:buttons_];
3790 [buttons addObject:@"Cancel"];
3792 [delegate_ slideUp:[[[UIActionSheet alloc]
3795 defaultButtonIndex:2
3802 - (NSString *) _rightButtonTitle {
3803 int count = [buttons_ count];
3804 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3807 - (NSString *) backButtonTitle {
3811 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3812 if ((self = [super initWithBook:book]) != nil) {
3813 database_ = database;
3814 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3818 - (void) setPackage:(Package *)package {
3819 if (package_ != nil) {
3820 [package_ autorelease];
3829 [buttons_ removeAllObjects];
3831 if (package != nil) {
3832 package_ = [package retain];
3833 name_ = [[package id] retain];
3835 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3837 if ([package_ source] == nil);
3838 else if ([package_ upgradableAndEssential:NO])
3839 [buttons_ addObject:@"Upgrade"];
3840 else if ([package_ installed] == nil)
3841 [buttons_ addObject:@"Install"];
3843 [buttons_ addObject:@"Reinstall"];
3844 if ([package_ installed] != nil)
3845 [buttons_ addObject:@"Remove"];
3849 - (void) reloadData {
3850 [self setPackage:[database_ packageWithName:name_]];
3851 [self reloadButtons];
3856 /* Package Table {{{ */
3857 @interface PackageTable : RVPage {
3858 _transient Database *database_;
3862 NSMutableArray *packages_;
3863 NSMutableArray *sections_;
3864 UISectionList *list_;
3867 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3869 - (void) setDelegate:(id)delegate;
3870 - (void) setObject:(id)object;
3872 - (void) reloadData;
3873 - (void) resetCursor;
3875 - (UISectionList *) list;
3877 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3881 @implementation PackageTable
3884 [list_ setDataSource:nil];
3889 [packages_ release];
3890 [sections_ release];
3895 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3896 return [sections_ count];
3899 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3900 return [[sections_ objectAtIndex:section] name];
3903 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3904 return [[sections_ objectAtIndex:section] row];
3907 - (int) numberOfRowsInTable:(UITable *)table {
3908 return [packages_ count];
3911 - (float) table:(UITable *)table heightForRow:(int)row {
3912 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3915 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3917 reusing = [[[PackageCell alloc] init] autorelease];
3918 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3922 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3926 - (void) tableRowSelected:(NSNotification *)notification {
3927 int row = [[notification object] selectedRow];
3931 Package *package = [packages_ objectAtIndex:row];
3932 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3933 [view setDelegate:delegate_];
3934 [view setPackage:package];
3935 [book_ pushPage:view];
3938 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3939 if ((self = [super initWithBook:book]) != nil) {
3940 database_ = database;
3941 title_ = [title retain];
3943 object_ = object == nil ? nil : [object retain];
3945 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3946 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3948 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3949 [list_ setDataSource:self];
3951 UITableColumn *column = [[[UITableColumn alloc]
3952 initWithTitle:@"Name"
3954 width:[self frame].size.width
3957 UITable *table = [list_ table];
3958 [table setSeparatorStyle:1];
3959 [table addTableColumn:column];
3960 [table setDelegate:self];
3961 [table setReusesTableCells:YES];
3963 [self addSubview:list_];
3966 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3967 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3971 - (void) setDelegate:(id)delegate {
3972 delegate_ = delegate;
3975 - (void) setObject:(id)object {
3981 object_ = [object retain];
3984 - (void) reloadData {
3985 NSArray *packages = [database_ packages];
3987 [packages_ removeAllObjects];
3988 [sections_ removeAllObjects];
3990 for (size_t i(0); i != [packages count]; ++i) {
3991 Package *package([packages objectAtIndex:i]);
3992 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3993 [packages_ addObject:package];
3996 Section *section = nil;
3998 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3999 Package *package = [packages_ objectAtIndex:offset];
4000 NSString *name = [package index];
4002 if (section == nil || ![[section name] isEqualToString:name]) {
4003 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4004 [sections_ addObject:section];
4007 [section addToCount];
4013 - (NSString *) title {
4017 - (void) resetViewAnimated:(BOOL)animated {
4018 [list_ resetViewAnimated:animated];
4021 - (void) resetCursor {
4022 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4025 - (UISectionList *) list {
4029 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4030 [list_ setShouldHideHeaderInShortLists:hide];
4036 /* Add Source View {{{ */
4037 @interface AddSourceView : RVPage {
4038 _transient Database *database_;
4041 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4045 @implementation AddSourceView
4047 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4048 if ((self = [super initWithBook:book]) != nil) {
4049 database_ = database;
4055 /* Source Cell {{{ */
4056 @interface SourceCell : UITableCell {
4059 NSString *description_;
4065 - (SourceCell *) initWithSource:(Source *)source;
4069 @implementation SourceCell
4074 [description_ release];
4079 - (SourceCell *) initWithSource:(Source *)source {
4080 if ((self = [super init]) != nil) {
4082 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4084 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4085 icon_ = [icon_ retain];
4087 origin_ = [[source name] retain];
4088 label_ = [[source uri] retain];
4089 description_ = [[source description] retain];
4093 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4095 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4102 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4106 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4110 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4112 [super drawContentInRect:rect selected:selected];
4117 /* Source Table {{{ */
4118 @interface SourceTable : RVPage {
4119 _transient Database *database_;
4120 UISectionList *list_;
4121 NSMutableArray *sources_;
4122 UIActionSheet *alert_;
4126 UIProgressHUD *hud_;
4129 //NSURLConnection *installer_;
4130 NSURLConnection *trivial_bz2_;
4131 NSURLConnection *trivial_gz_;
4132 //NSURLConnection *automatic_;
4137 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4141 @implementation SourceTable
4143 - (void) _deallocConnection:(NSURLConnection *)connection {
4144 if (connection != nil) {
4145 [connection cancel];
4146 //[connection setDelegate:nil];
4147 [connection release];
4152 [[list_ table] setDelegate:nil];
4153 [list_ setDataSource:nil];
4162 //[self _deallocConnection:installer_];
4163 [self _deallocConnection:trivial_gz_];
4164 [self _deallocConnection:trivial_bz2_];
4165 //[self _deallocConnection:automatic_];
4172 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4173 return offset_ == 0 ? 1 : 2;
4176 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4177 switch (section + (offset_ == 0 ? 1 : 0)) {
4178 case 0: return @"Entered by User";
4179 case 1: return @"Installed by Packages";
4187 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4188 switch (section + (offset_ == 0 ? 1 : 0)) {
4190 case 1: return offset_;
4198 - (int) numberOfRowsInTable:(UITable *)table {
4199 return [sources_ count];
4202 - (float) table:(UITable *)table heightForRow:(int)row {
4203 Source *source = [sources_ objectAtIndex:row];
4204 return [source description] == nil ? 56 : 73;
4207 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4208 Source *source = [sources_ objectAtIndex:row];
4209 // XXX: weird warning, stupid selectors ;P
4210 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4213 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4217 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4221 - (void) tableRowSelected:(NSNotification*)notification {
4222 UITable *table([list_ table]);
4223 int row([table selectedRow]);
4227 Source *source = [sources_ objectAtIndex:row];
4229 PackageTable *packages = [[[PackageTable alloc]
4232 title:[source label]
4233 filter:@selector(isVisibleInSource:)
4237 [packages setDelegate:delegate_];
4239 [book_ pushPage:packages];
4242 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4243 Source *source = [sources_ objectAtIndex:row];
4244 return [source record] != nil;
4247 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4248 [[list_ table] setDeleteConfirmationRow:row];
4251 - (void) table:(UITable *)table deleteRow:(int)row {
4252 Source *source = [sources_ objectAtIndex:row];
4253 [Sources_ removeObjectForKey:[source key]];
4254 [delegate_ syncData];
4257 - (void) _endConnection:(NSURLConnection *)connection {
4258 NSURLConnection **field = NULL;
4259 if (connection == trivial_bz2_)
4260 field = &trivial_bz2_;
4261 else if (connection == trivial_gz_)
4262 field = &trivial_gz_;
4263 _assert(field != NULL);
4264 [connection release];
4268 trivial_bz2_ == nil &&
4271 [delegate_ setStatusBarShowsProgress:NO];
4274 [hud_ removeFromSuperview];
4279 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4282 @"./", @"Distribution",
4283 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4285 [delegate_ syncData];
4286 } else if (error_ != nil) {
4287 UIActionSheet *sheet = [[[UIActionSheet alloc]
4288 initWithTitle:@"Verification Error"
4289 buttons:[NSArray arrayWithObjects:@"OK", nil]
4290 defaultButtonIndex:0
4295 [sheet setBodyText:[error_ localizedDescription]];
4296 [sheet popupAlertAnimated:YES];
4298 UIActionSheet *sheet = [[[UIActionSheet alloc]
4299 initWithTitle:@"Did not Find Repository"
4300 buttons:[NSArray arrayWithObjects:@"OK", nil]
4301 defaultButtonIndex:0
4306 [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."];
4307 [sheet popupAlertAnimated:YES];
4313 if (error_ != nil) {
4320 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4321 switch ([response statusCode]) {
4327 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4328 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4330 error_ = [error retain];
4331 [self _endConnection:connection];
4334 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4335 [self _endConnection:connection];
4338 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4339 NSMutableURLRequest *request = [NSMutableURLRequest
4340 requestWithURL:[NSURL URLWithString:href]
4341 cachePolicy:NSURLRequestUseProtocolCachePolicy
4342 timeoutInterval:20.0
4345 [request setHTTPMethod:method];
4347 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4350 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4351 NSString *context = [sheet context];
4352 if ([context isEqualToString:@"source"])
4355 NSString *href = [[sheet textField] text];
4357 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4359 if (![href hasSuffix:@"/"])
4360 href_ = [href stringByAppendingString:@"/"];
4363 href_ = [href_ retain];
4365 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4366 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4367 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4371 hud_ = [delegate_ addProgressHUD];
4372 [hud_ setText:@"Verifying URL"];
4385 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4386 if ((self = [super initWithBook:book]) != nil) {
4387 database_ = database;
4388 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4390 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4391 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4392 [list_ setShouldHideHeaderInShortLists:NO];
4394 [self addSubview:list_];
4395 [list_ setDataSource:self];
4397 UITableColumn *column = [[UITableColumn alloc]
4398 initWithTitle:@"Name"
4400 width:[self frame].size.width
4403 UITable *table = [list_ table];
4404 [table setSeparatorStyle:1];
4405 [table addTableColumn:column];
4406 [table setDelegate:self];
4410 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4411 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4415 - (void) reloadData {
4417 _assert(list.ReadMainList());
4419 [sources_ removeAllObjects];
4420 [sources_ addObjectsFromArray:[database_ sources]];
4422 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4425 int count = [sources_ count];
4426 for (offset_ = 0; offset_ != count; ++offset_) {
4427 Source *source = [sources_ objectAtIndex:offset_];
4428 if ([source record] == nil)
4435 - (void) resetViewAnimated:(BOOL)animated {
4436 [list_ resetViewAnimated:animated];
4439 - (void) _leftButtonClicked {
4440 /*[book_ pushPage:[[[AddSourceView alloc]
4445 UIActionSheet *sheet = [[[UIActionSheet alloc]
4446 initWithTitle:@"Enter Cydia/APT URL"
4447 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4448 defaultButtonIndex:0
4453 [sheet addTextFieldWithValue:@"http://" label:@""];
4455 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4456 [traits setAutocapitalizationType:0];
4457 [traits setKeyboardType:3];
4458 [traits setAutocorrectionType:1];
4460 [sheet popupAlertAnimated:YES];
4463 - (void) _rightButtonClicked {
4464 UITable *table = [list_ table];
4465 BOOL editing = [table isRowDeletionEnabled];
4466 [table enableRowDeletion:!editing animated:YES];
4467 [book_ reloadButtonsForPage:self];
4470 - (NSString *) title {
4474 - (NSString *) leftButtonTitle {
4475 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4478 - (NSString *) rightButtonTitle {
4479 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4482 - (UINavigationButtonStyle) rightButtonStyle {
4483 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4489 /* Installed View {{{ */
4490 @interface InstalledView : RVPage {
4491 _transient Database *database_;
4492 PackageTable *packages_;
4496 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4500 @implementation InstalledView
4503 [packages_ release];
4507 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4508 if ((self = [super initWithBook:book]) != nil) {
4509 database_ = database;
4511 packages_ = [[PackageTable alloc]
4515 filter:@selector(isInstalledAndVisible:)
4516 with:[NSNumber numberWithBool:YES]
4519 [self addSubview:packages_];
4521 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4522 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4526 - (void) resetViewAnimated:(BOOL)animated {
4527 [packages_ resetViewAnimated:animated];
4530 - (void) reloadData {
4531 [packages_ reloadData];
4534 - (void) _rightButtonClicked {
4535 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4536 [packages_ reloadData];
4538 [book_ reloadButtonsForPage:self];
4541 - (NSString *) title {
4542 return @"Installed";
4545 - (NSString *) backButtonTitle {
4549 - (NSString *) rightButtonTitle {
4550 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4553 - (UINavigationButtonStyle) rightButtonStyle {
4554 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4557 - (void) setDelegate:(id)delegate {
4558 [super setDelegate:delegate];
4559 [packages_ setDelegate:delegate];
4566 @interface HomeView : BrowserView {
4571 @implementation HomeView
4573 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4577 - (void) _leftButtonClicked {
4578 UIActionSheet *sheet = [[[UIActionSheet alloc]
4579 initWithTitle:@"About Cydia Installer"
4580 buttons:[NSArray arrayWithObjects:@"Close", nil]
4581 defaultButtonIndex:0
4587 @"Copyright (C) 2008\n"
4588 "Jay Freeman (saurik)\n"
4589 "saurik@saurik.com\n"
4590 "http://www.saurik.com/\n"
4593 "http://www.theokorigroup.com/\n"
4595 "College of Creative Studies,\n"
4596 "University of California,\n"
4598 "http://www.ccs.ucsb.edu/"
4601 [sheet popupAlertAnimated:YES];
4604 - (NSString *) leftButtonTitle {
4610 /* Manage View {{{ */
4611 @interface ManageView : BrowserView {
4616 @implementation ManageView
4618 - (NSString *) title {
4622 - (void) _leftButtonClicked {
4623 [delegate_ askForSettings];
4626 - (NSString *) leftButtonTitle {
4630 - (NSString *) _rightButtonTitle {
4637 @interface WebView (Cydia)
4638 - (void) setScriptDebugDelegate:(id)delegate;
4639 - (void) _setFormDelegate:(id)delegate;
4640 - (void) _setUIKitDelegate:(id)delegate;
4641 - (void) setWebMailDelegate:(id)delegate;
4642 - (void) _setLayoutInterval:(float)interval;
4645 /* Indirect Delegate {{{ */
4646 @interface IndirectDelegate : NSProxy {
4647 _transient volatile id delegate_;
4650 - (void) setDelegate:(id)delegate;
4651 - (id) initWithDelegate:(id)delegate;
4654 @implementation IndirectDelegate
4656 - (void) setDelegate:(id)delegate {
4657 delegate_ = delegate;
4660 - (id) initWithDelegate:(id)delegate {
4661 delegate_ = delegate;
4665 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4666 if (delegate_ != nil)
4667 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4669 // XXX: I fucking hate Apple so very very bad
4670 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4673 - (void) forwardInvocation:(NSInvocation *)inv {
4674 SEL sel = [inv selector];
4675 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4676 [inv invokeWithTarget:delegate_];
4681 /* Browser Implementation {{{ */
4682 @implementation BrowserView
4685 WebView *webview = [webview_ webView];
4686 [webview setFrameLoadDelegate:nil];
4687 [webview setResourceLoadDelegate:nil];
4688 [webview setUIDelegate:nil];
4689 [webview setScriptDebugDelegate:nil];
4690 [webview setPolicyDelegate:nil];
4692 [webview setDownloadDelegate:nil];
4694 [webview _setFormDelegate:nil];
4695 [webview _setUIKitDelegate:nil];
4696 [webview setWebMailDelegate:nil];
4697 [webview setEditingDelegate:nil];
4699 [webview_ setDelegate:nil];
4700 [webview_ setGestureDelegate:nil];
4702 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4707 [webview_ removeFromSuperview];
4708 [Documents_ addObject:[webview_ autorelease]];
4713 [indirect_ setDelegate:nil];
4714 [indirect_ release];
4716 [scroller_ setDelegate:nil];
4718 [scroller_ release];
4720 [indicator_ release];
4726 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4727 [self loadRequest:[NSURLRequest
4730 timeoutInterval:30.0
4734 - (void) loadURL:(NSURL *)url {
4735 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4738 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4739 NSMutableURLRequest *copy = [request mutableCopy];
4741 if (Machine_ != NULL)
4742 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4743 if (UniqueID_ != nil)
4744 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4747 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4752 - (void) loadRequest:(NSURLRequest *)request {
4754 [webview_ loadRequest:request];
4757 - (void) reloadURL {
4758 if ([urls_ count] == 0)
4760 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4761 [urls_ removeLastObject];
4762 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4765 - (WebView *) webView {
4766 return [webview_ webView];
4769 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4770 [scroller_ setContentSize:frame.size];
4773 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4774 [self view:sender didSetFrame:frame];
4777 - (void) pushPage:(RVPage *)page {
4778 [self setBackButtonTitle:title_];
4779 [page setDelegate:delegate_];
4780 [book_ pushPage:page];
4783 - (BOOL) getSpecial:(NSURL *)url {
4784 NSString *href([url absoluteString]);
4785 NSString *scheme([[url scheme] lowercaseString]);
4789 if ([href hasPrefix:@"apptapp://package/"])
4790 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4791 else if ([scheme isEqualToString:@"cydia"]) {
4792 page = [delegate_ pageForURL:url hasTag:NULL];
4795 } else if (![scheme isEqualToString:@"apptapp"])
4799 [self pushPage:page];
4803 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4804 [window setValue:delegate_ forKey:@"cydia"];
4807 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4808 if (NSURL *url = [request URL]) {
4809 if (![self getSpecial:url]) {
4810 NSString *scheme([[url scheme] lowercaseString]);
4811 if ([scheme isEqualToString:@"mailto"])
4812 [delegate_ openMailToURL:url];
4821 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4822 if ([WebView canShowMIMEType:type])
4825 // XXX: handle more mime types!
4827 if (frame == [webView mainFrame])
4828 [UIApp openURL:[request URL]];
4832 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4833 NSURL *url([request URL]);
4835 if (url == nil) use: {
4840 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4843 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4844 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4847 [UIApp openURL:url];
4853 int store(_not(int));
4854 if (NSURL *itms = [url itmsURL:&store]) {
4855 NSLog(@"itms#%@#%u#%@", url, store, itms);
4857 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4858 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4865 NSString *scheme([[url scheme] lowercaseString]);
4867 if ([scheme isEqualToString:@"tel"]) {
4868 // XXX: intelligence
4872 if ([scheme isEqualToString:@"mailto"]) {
4873 [delegate_ openMailToURL:url];
4877 if ([self getSpecial:url])
4879 else if ([WebView _canHandleRequest:request])
4881 else if ([url isSpringboardHandledURL])
4887 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4888 //lprintf("Status:%s\n", [text UTF8String]);
4891 - (void) _pushPage {
4895 [book_ pushPage:self];
4898 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4899 NSURL *url = [request URL];
4900 if ([self getSpecial:url])
4903 return [self _addHeadersToRequest:request];
4906 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4907 [self setBackButtonTitle:title_];
4909 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4910 [browser setDelegate:delegate_];
4913 [browser loadRequest:[self _addHeadersToRequest:request]];
4914 [book_ pushPage:browser];
4917 return [browser webView];
4920 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4921 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4924 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4925 return [self _createWebViewWithRequest:request pushed:YES];
4928 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4929 if ([frame parentFrame] != nil)
4932 title_ = [title retain];
4933 [book_ reloadTitleForPage:self];
4936 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4937 if ([frame parentFrame] != nil)
4942 [indicator_ startAnimation];
4943 [self reloadButtons];
4945 if (title_ != nil) {
4950 [book_ reloadTitleForPage:self];
4952 WebView *webview = [webview_ webView];
4953 NSString *href = [webview mainFrameURL];
4954 [urls_ addObject:[NSURL URLWithString:href]];
4956 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4958 CGRect webrect = [scroller_ bounds];
4959 webrect.size.height = 0;
4960 [webview_ setFrame:webrect];
4963 - (void) _finishLoading {
4966 [indicator_ stopAnimation];
4967 [self reloadButtons];
4971 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4972 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4975 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4976 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4979 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4980 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4983 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4984 return [webview_ webView:sender didCommitLoadForFrame:frame];
4987 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4988 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4991 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4992 if ([frame parentFrame] == nil)
4993 [self _finishLoading];
4994 return [webview_ webView:sender didFinishLoadForFrame:frame];
4997 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4998 if ([frame parentFrame] != nil)
5000 [self _finishLoading];
5002 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5003 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5004 [[error localizedDescription] stringByAddingPercentEscapes]
5008 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5010 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5014 - (id) initWithBook:(RVBook *)book {
5015 if ((self = [super initWithBook:book]) != nil) {
5018 struct CGRect bounds = [self bounds];
5020 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5021 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5022 [self addSubview:pinstripe];
5024 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5025 [self addSubview:scroller_];
5027 [scroller_ setScrollingEnabled:YES];
5028 [scroller_ setAdjustForContentSizeChange:YES];
5029 [scroller_ setClipsSubviews:YES];
5030 [scroller_ setAllowsRubberBanding:YES];
5031 [scroller_ setScrollDecelerationFactor:0.99];
5032 [scroller_ setDelegate:self];
5034 CGRect webrect = [scroller_ bounds];
5035 webrect.size.height = 0;
5040 webview_ = [Documents_ lastObject];
5041 if (webview_ != nil) {
5042 webview_ = [webview_ retain];
5043 webview = [webview_ webView];
5044 [Documents_ removeLastObject];
5045 [webview_ setFrame:webrect];
5050 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5051 webview = [webview_ webView];
5053 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5055 [webview_ setAllowsMessaging:YES];
5057 [webview_ setTilingEnabled:YES];
5058 [webview_ setDrawsGrid:NO];
5059 [webview_ setLogsTilingChanges:NO];
5060 [webview_ setTileMinificationFilter:kCAFilterNearest];
5061 [webview_ setDetectsPhoneNumbers:NO];
5062 [webview_ setAutoresizes:YES];
5064 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5065 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5066 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5068 [webview_ _setDocumentType:0x4];
5070 [webview_ setZoomsFocusedFormControl:YES];
5071 [webview_ setContentsPosition:7];
5072 [webview_ setEnabledGestures:0xa];
5073 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
5074 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
5076 [webview_ setSmoothsFonts:YES];
5078 [webview _setUsesLoaderCache:YES];
5079 [webview setGroupName:@"Cydia"];
5080 //[webview _setLayoutInterval:0.5];
5083 [webview_ setDelegate:self];
5084 [webview_ setGestureDelegate:self];
5085 [scroller_ addSubview:webview_];
5087 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5089 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5090 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5091 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5093 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5094 NSString *application = package == nil ? @"Cydia" : [NSString
5095 stringWithFormat:@"Cydia/%@",
5097 ]; [webview setApplicationNameForUserAgent:application];
5099 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5101 [webview setFrameLoadDelegate:self];
5102 [webview setResourceLoadDelegate:indirect_];
5103 [webview setUIDelegate:self];
5104 [webview setScriptDebugDelegate:self];
5105 [webview setPolicyDelegate:self];
5107 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5109 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5110 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5111 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5115 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5116 [webview_ redrawScaledDocument];
5119 - (void) _rightButtonClicked {
5124 - (NSString *) _rightButtonTitle {
5128 - (NSString *) rightButtonTitle {
5129 return loading_ ? @"" : [self _rightButtonTitle];
5132 - (NSString *) title {
5133 return title_ == nil ? @"Loading" : title_;
5136 - (NSString *) backButtonTitle {
5140 - (void) setPageActive:(BOOL)active {
5142 [indicator_ removeFromSuperview];
5144 [[book_ navigationBar] addSubview:indicator_];
5147 - (void) resetViewAnimated:(BOOL)animated {
5150 - (void) setPushed:(bool)pushed {
5157 /* Cydia Book {{{ */
5158 @interface CYBook : RVBook <
5161 _transient Database *database_;
5162 UINavigationBar *overlay_;
5163 UINavigationBar *underlay_;
5164 UIProgressIndicator *indicator_;
5165 UITextLabel *prompt_;
5166 UIProgressBar *progress_;
5167 UINavigationButton *cancel_;
5170 NSTimeInterval last_;
5173 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5179 @implementation CYBook
5183 [indicator_ release];
5185 [progress_ release];
5190 - (NSString *) getTitleForPage:(RVPage *)page {
5191 return Simplify([super getTitleForPage:page]);
5199 [UIView beginAnimations:nil context:NULL];
5201 CGRect ovrframe = [overlay_ frame];
5202 ovrframe.origin.y = 0;
5203 [overlay_ setFrame:ovrframe];
5205 CGRect barframe = [navbar_ frame];
5206 barframe.origin.y += ovrframe.size.height;
5207 [navbar_ setFrame:barframe];
5209 CGRect trnframe = [transition_ frame];
5210 trnframe.origin.y += ovrframe.size.height;
5211 trnframe.size.height -= ovrframe.size.height;
5212 [transition_ setFrame:trnframe];
5214 [UIView endAnimations];
5216 [indicator_ startAnimation];
5217 [prompt_ setText:@"Updating Database"];
5218 [progress_ setProgress:0];
5221 last_ = [NSDate timeIntervalSinceReferenceDate];
5223 [overlay_ addSubview:cancel_];
5226 detachNewThreadSelector:@selector(_update)
5235 [indicator_ stopAnimation];
5237 [UIView beginAnimations:nil context:NULL];
5239 CGRect ovrframe = [overlay_ frame];
5240 ovrframe.origin.y = -ovrframe.size.height;
5241 [overlay_ setFrame:ovrframe];
5243 CGRect barframe = [navbar_ frame];
5244 barframe.origin.y -= ovrframe.size.height;
5245 [navbar_ setFrame:barframe];
5247 CGRect trnframe = [transition_ frame];
5248 trnframe.origin.y -= ovrframe.size.height;
5249 trnframe.size.height += ovrframe.size.height;
5250 [transition_ setFrame:trnframe];
5252 [UIView commitAnimations];
5254 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5257 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5258 if ((self = [super initWithFrame:frame]) != nil) {
5259 database_ = database;
5261 CGRect ovrrect = [navbar_ bounds];
5262 ovrrect.size.height = [UINavigationBar defaultSize].height;
5263 ovrrect.origin.y = -ovrrect.size.height;
5265 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5266 [self addSubview:overlay_];
5268 ovrrect.origin.y = frame.size.height;
5269 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5270 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5271 [self addSubview:underlay_];
5273 [overlay_ setBarStyle:1];
5274 [underlay_ setBarStyle:1];
5276 int barstyle = [overlay_ _barStyle:NO];
5277 bool ugly = barstyle == 0;
5279 UIProgressIndicatorStyle style = ugly ?
5280 UIProgressIndicatorStyleMediumBrown :
5281 UIProgressIndicatorStyleMediumWhite;
5283 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5284 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5285 CGRect indrect = {{indoffset, indoffset}, indsize};
5287 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5288 [indicator_ setStyle:style];
5289 [overlay_ addSubview:indicator_];
5291 CGSize prmsize = {215, indsize.height + 4};
5294 indoffset * 2 + indsize.width,
5298 unsigned(ovrrect.size.height - prmsize.height) / 2
5301 UIFont *font = [UIFont systemFontOfSize:15];
5303 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5305 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5306 [prompt_ setBackgroundColor:[UIColor clearColor]];
5307 [prompt_ setFont:font];
5309 [overlay_ addSubview:prompt_];
5311 CGSize prgsize = {75, 100};
5314 ovrrect.size.width - prgsize.width - 10,
5315 (ovrrect.size.height - prgsize.height) / 2
5318 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5319 [progress_ setStyle:0];
5320 [overlay_ addSubview:progress_];
5322 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5323 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5325 CGRect frame = [cancel_ frame];
5326 frame.size.width = 65;
5327 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5328 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5329 [cancel_ setFrame:frame];
5331 [cancel_ setBarStyle:barstyle];
5335 - (void) _onCancel {
5337 [cancel_ removeFromSuperview];
5340 - (void) _update { _pooled
5342 status.setDelegate(self);
5344 [database_ updateWithStatus:status];
5347 performSelectorOnMainThread:@selector(_update_)
5353 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5354 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5357 - (void) setProgressTitle:(NSString *)title {
5359 performSelectorOnMainThread:@selector(_setProgressTitle:)
5365 - (void) setProgressPercent:(float)percent {
5367 performSelectorOnMainThread:@selector(_setProgressPercent:)
5368 withObject:[NSNumber numberWithFloat:percent]
5373 - (void) startProgress {
5376 - (void) addProgressOutput:(NSString *)output {
5378 performSelectorOnMainThread:@selector(_addProgressOutput:)
5384 - (bool) isCancelling:(size_t)received {
5385 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5386 if (received_ != received) {
5387 received_ = received;
5389 } else if (now - last_ > 15)
5394 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5398 - (void) _setProgressTitle:(NSString *)title {
5399 [prompt_ setText:title];
5402 - (void) _setProgressPercent:(NSNumber *)percent {
5403 [progress_ setProgress:[percent floatValue]];
5406 - (void) _addProgressOutput:(NSString *)output {
5411 /* Cydia:// Protocol {{{ */
5412 @interface CydiaURLProtocol : NSURLProtocol {
5417 @implementation CydiaURLProtocol
5419 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5420 NSURL *url([request URL]);
5423 NSString *scheme([[url scheme] lowercaseString]);
5424 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5429 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5433 - (void) startLoading {
5434 id<NSURLProtocolClient> client([self client]);
5435 NSURLRequest *request([self request]);
5437 NSURL *url([request URL]);
5438 NSString *href([url absoluteString]);
5440 NSString *path([href substringFromIndex:8]);
5441 NSRange slash([path rangeOfString:@"/"]);
5444 if (slash.location == NSNotFound) {
5448 command = [path substringToIndex:slash.location];
5449 path = [path substringFromIndex:(slash.location + 1)];
5452 Database *database([Database sharedInstance]);
5454 if ([command isEqualToString:@"package-icon"]) {
5457 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5458 Package *package([database packageWithName:path]);
5462 UIImage *icon([package icon]);
5464 NSData *data(UIImagePNGRepresentation(icon));
5466 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5467 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5468 [client URLProtocol:self didLoadData:data];
5469 [client URLProtocolDidFinishLoading:self];
5470 } else if ([command isEqualToString:@"section-icon"]) {
5473 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5474 NSString *section(Simplify(path));
5476 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5478 icon = [UIImage applicationImageNamed:@"unknown.png"];
5480 NSData *data(UIImagePNGRepresentation(icon));
5482 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5483 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5484 [client URLProtocol:self didLoadData:data];
5485 [client URLProtocolDidFinishLoading:self];
5487 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5491 - (void) stopLoading {
5497 /* Install View {{{ */
5498 @interface InstallView : RVPage {
5499 _transient Database *database_;
5500 NSMutableArray *sections_;
5501 NSMutableArray *filtered_;
5502 UITransitionView *transition_;
5508 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5509 - (void) reloadData;
5514 @implementation InstallView
5517 [list_ setDataSource:nil];
5518 [list_ setDelegate:nil];
5520 [sections_ release];
5521 [filtered_ release];
5522 [transition_ release];
5524 [accessory_ release];
5528 - (int) numberOfRowsInTable:(UITable *)table {
5529 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5532 - (float) table:(UITable *)table heightForRow:(int)row {
5536 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5538 reusing = [[[SectionCell alloc] init] autorelease];
5539 [(SectionCell *)reusing setSection:(editing_ ?
5540 [sections_ objectAtIndex:row] :
5541 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5542 ) editing:editing_];
5546 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5550 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5554 - (void) tableRowSelected:(NSNotification *)notification {
5555 int row = [[notification object] selectedRow];
5566 title = @"All Packages";
5568 section = [filtered_ objectAtIndex:(row - 1)];
5569 name = [section name];
5575 title = @"(No Section)";
5579 PackageTable *table = [[[PackageTable alloc]
5583 filter:@selector(isVisiblyUninstalledInSection:)
5587 [table setDelegate:delegate_];
5589 [book_ pushPage:table];
5592 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5593 if ((self = [super initWithBook:book]) != nil) {
5594 database_ = database;
5596 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5597 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5599 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5600 [self addSubview:transition_];
5602 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5603 [transition_ transition:0 toView:list_];
5605 UITableColumn *column = [[[UITableColumn alloc]
5606 initWithTitle:@"Name"
5608 width:[self frame].size.width
5611 [list_ setDataSource:self];
5612 [list_ setSeparatorStyle:1];
5613 [list_ addTableColumn:column];
5614 [list_ setDelegate:self];
5615 [list_ setReusesTableCells:YES];
5619 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5620 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5624 - (void) reloadData {
5625 NSArray *packages = [database_ packages];
5627 [sections_ removeAllObjects];
5628 [filtered_ removeAllObjects];
5630 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5631 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5634 for (size_t i(0); i != [packages count]; ++i) {
5635 Package *package([packages objectAtIndex:i]);
5636 NSString *name([package section]);
5639 Section *section([sections objectForKey:name]);
5640 if (section == nil) {
5641 section = [[[Section alloc] initWithName:name] autorelease];
5642 [sections setObject:section forKey:name];
5646 if ([package valid] && [package installed] == nil && [package visible])
5647 [filtered addObject:package];
5651 [sections_ addObjectsFromArray:[sections allValues]];
5652 [sections_ sortUsingSelector:@selector(compareByName:)];
5655 [filtered sortUsingSelector:@selector(compareBySection:)];
5658 Section *section = nil;
5659 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5660 Package *package = [filtered objectAtIndex:offset];
5661 NSString *name = [package section];
5663 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5664 section = name == nil ?
5665 [[[Section alloc] initWithName:nil] autorelease] :
5666 [sections objectForKey:name];
5667 [filtered_ addObject:section];
5670 [section addToCount];
5678 - (void) resetView {
5680 [self _rightButtonClicked];
5683 - (void) resetViewAnimated:(BOOL)animated {
5684 [list_ resetViewAnimated:animated];
5687 - (void) _rightButtonClicked {
5688 if ((editing_ = !editing_))
5691 [delegate_ updateData];
5692 [book_ reloadTitleForPage:self];
5693 [book_ reloadButtonsForPage:self];
5696 - (NSString *) title {
5697 return editing_ ? @"Section Visibility" : @"Install by Section";
5700 - (NSString *) backButtonTitle {
5704 - (NSString *) rightButtonTitle {
5705 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5708 - (UINavigationButtonStyle) rightButtonStyle {
5709 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5712 - (UIView *) accessoryView {
5718 /* Changes View {{{ */
5719 @interface ChangesView : RVPage {
5720 _transient Database *database_;
5721 NSMutableArray *packages_;
5722 NSMutableArray *sections_;
5723 UISectionList *list_;
5727 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5728 - (void) reloadData;
5732 @implementation ChangesView
5735 [[list_ table] setDelegate:nil];
5736 [list_ setDataSource:nil];
5738 [packages_ release];
5739 [sections_ release];
5744 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5745 return [sections_ count];
5748 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5749 return [[sections_ objectAtIndex:section] name];
5752 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5753 return [[sections_ objectAtIndex:section] row];
5756 - (int) numberOfRowsInTable:(UITable *)table {
5757 return [packages_ count];
5760 - (float) table:(UITable *)table heightForRow:(int)row {
5761 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5764 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5766 reusing = [[[PackageCell alloc] init] autorelease];
5767 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5771 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5775 - (void) tableRowSelected:(NSNotification *)notification {
5776 int row = [[notification object] selectedRow];
5779 Package *package = [packages_ objectAtIndex:row];
5780 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5781 [view setDelegate:delegate_];
5782 [view setPackage:package];
5783 [book_ pushPage:view];
5786 - (void) _leftButtonClicked {
5787 [(CYBook *)book_ update];
5788 [self reloadButtons];
5791 - (void) _rightButtonClicked {
5792 [delegate_ distUpgrade];
5795 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5796 if ((self = [super initWithBook:book]) != nil) {
5797 database_ = database;
5799 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5800 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5802 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5803 [self addSubview:list_];
5805 [list_ setShouldHideHeaderInShortLists:NO];
5806 [list_ setDataSource:self];
5807 //[list_ setSectionListStyle:1];
5809 UITableColumn *column = [[[UITableColumn alloc]
5810 initWithTitle:@"Name"
5812 width:[self frame].size.width
5815 UITable *table = [list_ table];
5816 [table setSeparatorStyle:1];
5817 [table addTableColumn:column];
5818 [table setDelegate:self];
5819 [table setReusesTableCells:YES];
5823 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5824 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5828 - (void) reloadData {
5829 NSArray *packages = [database_ packages];
5831 [packages_ removeAllObjects];
5832 [sections_ removeAllObjects];
5835 for (size_t i(0); i != [packages count]; ++i) {
5836 Package *package([packages objectAtIndex:i]);
5839 [package installed] == nil && [package valid] && [package visible] ||
5840 [package upgradableAndEssential:NO]
5842 [packages_ addObject:package];
5846 [packages_ radixUsingSelector:@selector(compareForChanges) withObject:nil];
5849 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5850 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5851 Section *section = nil;
5855 bool unseens = false;
5857 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5860 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5861 Package *package = [packages_ objectAtIndex:offset];
5863 if (![package upgradableAndEssential:YES]) {
5865 NSDate *seen = [package seen];
5867 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5870 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5871 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5872 [sections_ addObject:section];
5876 [section addToCount];
5877 } else if ([package ignored])
5878 [ignored addToCount];
5881 [upgradable addToCount];
5886 CFRelease(formatter);
5889 Section *last = [sections_ lastObject];
5890 size_t count = [last count];
5891 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5892 [sections_ removeLastObject];
5895 if ([ignored count] != 0)
5896 [sections_ insertObject:ignored atIndex:0];
5898 [sections_ insertObject:upgradable atIndex:0];
5901 [self reloadButtons];
5904 - (void) resetViewAnimated:(BOOL)animated {
5905 [list_ resetViewAnimated:animated];
5908 - (NSString *) leftButtonTitle {
5909 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5912 - (NSString *) rightButtonTitle {
5913 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5916 - (NSString *) title {
5922 /* Search View {{{ */
5923 @protocol SearchViewDelegate
5924 - (void) showKeyboard:(BOOL)show;
5927 @interface SearchView : RVPage {
5929 UISearchField *field_;
5930 UITransitionView *transition_;
5931 PackageTable *table_;
5932 UIPreferencesTable *advanced_;
5938 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5939 - (void) reloadData;
5943 @implementation SearchView
5946 [field_ setDelegate:nil];
5948 [accessory_ release];
5950 [transition_ release];
5952 [advanced_ release];
5957 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5961 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5963 case 0: return @"Advanced Search (Coming Soon!)";
5965 default: _assert(false);
5969 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5973 default: _assert(false);
5977 - (void) _showKeyboard:(BOOL)show {
5978 CGSize keysize = [UIKeyboard defaultSize];
5979 CGRect keydown = [book_ pageBounds];
5980 CGRect keyup = keydown;
5981 keyup.size.height -= keysize.height - ButtonBarHeight_;
5983 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5985 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5986 [animation setSignificantRectFields:8];
5989 [animation setStartFrame:keydown];
5990 [animation setEndFrame:keyup];
5992 [animation setStartFrame:keyup];
5993 [animation setEndFrame:keydown];
5996 UIAnimator *animator = [UIAnimator sharedAnimator];
5999 addAnimations:[NSArray arrayWithObjects:animation, nil]
6000 withDuration:(KeyboardTime_ - delay)
6005 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6007 [delegate_ showKeyboard:show];
6010 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6011 [self _showKeyboard:YES];
6014 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6015 [self _showKeyboard:NO];
6018 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6020 NSString *text([field_ text]);
6021 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6027 - (void) textFieldClearButtonPressed:(UITextField *)field {
6031 - (void) keyboardInputShouldDelete:(id)input {
6035 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6036 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6040 [field_ resignFirstResponder];
6045 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6046 if ((self = [super initWithBook:book]) != nil) {
6047 CGRect pageBounds = [book_ pageBounds];
6049 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6050 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6051 [self addSubview:pinstripe];*/
6053 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6054 [self addSubview:transition_];
6056 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6058 [advanced_ setReusesTableCells:YES];
6059 [advanced_ setDataSource:self];
6060 [advanced_ reloadData];
6062 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6063 CGColor dimmed(space_, 0, 0, 0, 0.5);
6064 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6066 table_ = [[PackageTable alloc]
6070 filter:@selector(isUnfilteredAndSearchedForBy:)
6074 [table_ setShouldHideHeaderInShortLists:NO];
6075 [transition_ transition:0 toView:table_];
6084 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6091 [self bounds].size.width - area.origin.x - 18;
6093 area.size.height = [UISearchField defaultHeight];
6095 field_ = [[UISearchField alloc] initWithFrame:area];
6097 UIFont *font = [UIFont systemFontOfSize:16];
6098 [field_ setFont:font];
6100 [field_ setPlaceholder:@"Package Names & Descriptions"];
6101 [field_ setDelegate:self];
6103 [field_ setPaddingTop:5];
6105 UITextInputTraits *traits = [field_ textInputTraits];
6106 [traits setAutocapitalizationType:0];
6107 [traits setAutocorrectionType:1];
6108 [traits setReturnKeyType:6];
6110 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6112 accessory_ = [[UIView alloc] initWithFrame:accrect];
6113 [accessory_ addSubview:field_];
6115 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6116 [configure setShowPressFeedback:YES];
6117 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6118 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6119 [accessory_ addSubview:configure];*/
6121 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6122 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6128 LKAnimation *animation = [LKTransition animation];
6129 [animation setType:@"oglFlip"];
6130 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6131 [animation setFillMode:@"extended"];
6132 [animation setTransitionFlags:3];
6133 [animation setDuration:10];
6134 [animation setSpeed:0.35];
6135 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6136 [[transition_ _layer] addAnimation:animation forKey:0];
6137 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6138 flipped_ = !flipped_;
6142 - (void) configurePushed {
6143 [field_ resignFirstResponder];
6147 - (void) resetViewAnimated:(BOOL)animated {
6150 [table_ resetViewAnimated:animated];
6153 - (void) _reloadData {
6156 - (void) reloadData {
6159 [table_ setObject:[field_ text]];
6160 [table_ reloadData];
6161 [table_ resetCursor];
6164 - (UIView *) accessoryView {
6168 - (NSString *) title {
6172 - (NSString *) backButtonTitle {
6176 - (void) setDelegate:(id)delegate {
6177 [table_ setDelegate:delegate];
6178 [super setDelegate:delegate];
6184 @interface SettingsView : RVPage {
6185 _transient Database *database_;
6188 UIPreferencesTable *table_;
6189 _UISwitchSlider *subscribedSwitch_;
6190 _UISwitchSlider *ignoredSwitch_;
6191 UIPreferencesControlTableCell *subscribedCell_;
6192 UIPreferencesControlTableCell *ignoredCell_;
6195 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6199 @implementation SettingsView
6202 [table_ setDataSource:nil];
6205 if (package_ != nil)
6208 [subscribedSwitch_ release];
6209 [ignoredSwitch_ release];
6210 [subscribedCell_ release];
6211 [ignoredCell_ release];
6215 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6216 if (package_ == nil)
6222 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6223 if (package_ == nil)
6230 default: _assert(false);
6236 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6237 if (package_ == nil)
6244 default: _assert(false);
6250 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6251 if (package_ == nil)
6258 default: _assert(false);
6264 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6265 if (package_ == nil)
6268 _UISwitchSlider *slider([cell control]);
6269 BOOL value([slider value] != 0);
6270 NSMutableDictionary *metadata([package_ metadata]);
6273 if (NSNumber *number = [metadata objectForKey:key])
6274 before = [number boolValue];
6278 if (value != before) {
6279 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6281 [delegate_ updateData];
6285 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6286 [self onSomething:cell withKey:@"IsSubscribed"];
6289 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6290 [self onSomething:cell withKey:@"IsIgnored"];
6293 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6294 if (package_ == nil)
6298 case 0: switch (row) {
6300 return subscribedCell_;
6302 return ignoredCell_;
6303 default: _assert(false);
6306 case 1: switch (row) {
6308 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6309 [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."];
6313 default: _assert(false);
6316 default: _assert(false);
6322 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6323 if ((self = [super initWithBook:book])) {
6324 database_ = database;
6325 name_ = [package retain];
6327 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6328 [self addSubview:table_];
6330 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6331 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6333 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6334 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6336 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6337 [subscribedCell_ setTitle:@"Show All Changes"];
6338 [subscribedCell_ setControl:subscribedSwitch_];
6340 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6341 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6342 [ignoredCell_ setControl:ignoredSwitch_];
6344 [table_ setDataSource:self];
6349 - (void) resetViewAnimated:(BOOL)animated {
6350 [table_ resetViewAnimated:animated];
6353 - (void) reloadData {
6354 if (package_ != nil)
6355 [package_ autorelease];
6356 package_ = [database_ packageWithName:name_];
6357 if (package_ != nil) {
6359 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6360 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6363 [table_ reloadData];
6366 - (NSString *) title {
6372 /* Signature View {{{ */
6373 @interface SignatureView : BrowserView {
6374 _transient Database *database_;
6378 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6382 @implementation SignatureView
6389 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6391 [super webView:sender didClearWindowObject:window forFrame:frame];
6394 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6395 if ((self = [super initWithBook:book]) != nil) {
6396 database_ = database;
6397 package_ = [package retain];
6402 - (void) resetViewAnimated:(BOOL)animated {
6405 - (void) reloadData {
6406 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6412 @interface Cydia : UIApplication <
6413 ConfirmationViewDelegate,
6414 ProgressViewDelegate,
6423 UIToolbar *buttonbar_;
6427 NSMutableArray *essential_;
6428 NSMutableArray *broken_;
6430 Database *database_;
6431 ProgressView *progress_;
6435 UIKeyboard *keyboard_;
6436 UIProgressHUD *hud_;
6438 InstallView *install_;
6439 ChangesView *changes_;
6440 ManageView *manage_;
6441 SearchView *search_;
6446 @implementation Cydia
6449 if ([broken_ count] != 0) {
6450 int count = [broken_ count];
6452 UIActionSheet *sheet = [[[UIActionSheet alloc]
6453 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6454 buttons:[NSArray arrayWithObjects:
6456 @"Ignore (Temporary)",
6458 defaultButtonIndex:0
6463 [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."];
6464 [sheet popupAlertAnimated:YES];
6465 } else if (!Ignored_ && [essential_ count] != 0) {
6466 int count = [essential_ count];
6468 UIActionSheet *sheet = [[[UIActionSheet alloc]
6469 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6470 buttons:[NSArray arrayWithObjects:
6471 @"Upgrade Essential",
6472 @"Upgrade Everything",
6473 @"Ignore (Temporary)",
6475 defaultButtonIndex:0
6480 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6481 [sheet popupAlertAnimated:YES];
6485 - (void) _reloadData {
6486 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6487 [hud setText:@"Reloading Data"];
6488 [overlay_ addSubview:hud];
6492 [database_ reloadData];
6497 [essential_ removeAllObjects];
6498 [broken_ removeAllObjects];
6500 NSArray *packages = [database_ packages];
6501 for (Package *package in packages) {
6503 [broken_ addObject:package];
6504 if ([package upgradableAndEssential:NO]) {
6505 if ([package essential])
6506 [essential_ addObject:package];
6512 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6513 [buttonbar_ setBadgeValue:badge forButton:3];
6514 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6515 [buttonbar_ setBadgeAnimated:YES forButton:3];
6516 [self setApplicationBadge:badge];
6518 [buttonbar_ setBadgeValue:nil forButton:3];
6519 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6520 [buttonbar_ setBadgeAnimated:NO forButton:3];
6521 [self removeApplicationBadge];
6527 if ([packages count] == 0);
6539 [hud removeFromSuperview];*/
6542 - (void) _saveConfig {
6545 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6551 - (void) updateData {
6554 /* XXX: this is just stupid */
6555 if (tag_ != 2 && install_ != nil)
6556 [install_ reloadData];
6557 if (tag_ != 3 && changes_ != nil)
6558 [changes_ reloadData];
6559 if (tag_ != 5 && search_ != nil)
6560 [search_ reloadData];
6570 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6571 _assert(file != NULL);
6573 NSArray *keys = [Sources_ allKeys];
6575 for (int i(0), e([keys count]); i != e; ++i) {
6576 NSString *key = [keys objectAtIndex:i];
6577 NSDictionary *source = [Sources_ objectForKey:key];
6579 fprintf(file, "%s %s %s\n",
6580 [[source objectForKey:@"Type"] UTF8String],
6581 [[source objectForKey:@"URI"] UTF8String],
6582 [[source objectForKey:@"Distribution"] UTF8String]
6591 detachNewThreadSelector:@selector(update_)
6594 title:@"Updating Sources"
6598 - (void) reloadData {
6599 @synchronized (self) {
6600 if (confirm_ == nil)
6606 pkgProblemResolver *resolver = [database_ resolver];
6608 resolver->InstallProtect();
6609 if (!resolver->Resolve(true))
6614 [database_ prepare];
6616 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6617 [confirm_ setDelegate:self];
6619 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6620 [page setDelegate:self];
6622 [confirm_ setPage:page];
6623 [underlay_ popSubview:confirm_];
6626 - (void) installPackage:(Package *)package {
6627 @synchronized (self) {
6634 - (void) removePackage:(Package *)package {
6635 @synchronized (self) {
6642 - (void) distUpgrade {
6643 @synchronized (self) {
6644 [database_ upgrade];
6650 @synchronized (self) {
6652 if (confirm_ != nil) {
6660 [overlay_ removeFromSuperview];
6664 detachNewThreadSelector:@selector(perform)
6671 - (void) bootstrap_ {
6673 [database_ upgrade];
6674 [database_ prepare];
6675 [database_ perform];
6678 - (void) bootstrap {
6680 detachNewThreadSelector:@selector(bootstrap_)
6683 title:@"Bootstrap Install"
6687 - (void) progressViewIsComplete:(ProgressView *)progress {
6688 if (confirm_ != nil) {
6689 [underlay_ addSubview:overlay_];
6690 [confirm_ popFromSuperviewAnimated:NO];
6696 - (void) setPage:(RVPage *)page {
6697 [page resetViewAnimated:NO];
6698 [page setDelegate:self];
6699 [book_ setPage:page];
6702 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6703 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6704 [browser loadURL:url];
6708 - (void) _setHomePage {
6709 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6712 - (void) buttonBarItemTapped:(id)sender {
6713 unsigned tag = [sender tag];
6715 [book_ resetViewAnimated:YES];
6717 } else if (tag_ == 2 && tag != 2)
6718 [install_ resetView];
6721 case 1: [self _setHomePage]; break;
6723 case 2: [self setPage:install_]; break;
6724 case 3: [self setPage:changes_]; break;
6725 case 4: [self setPage:manage_]; break;
6726 case 5: [self setPage:search_]; break;
6728 default: _assert(false);
6734 - (void) applicationWillSuspend {
6736 [super applicationWillSuspend];
6739 - (void) askForSettings {
6740 UIActionSheet *role = [[[UIActionSheet alloc]
6741 initWithTitle:@"Who Are You?"
6742 buttons:[NSArray arrayWithObjects:
6743 @"User (Graphical Only)",
6744 @"Hacker (+ Command Line)",
6745 @"Developer (No Filters)",
6747 defaultButtonIndex:-1
6752 [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."];
6753 [role popupAlertAnimated:YES];
6758 [self setStatusBarShowsProgress:NO];
6761 [hud_ removeFromSuperview];
6765 pid_t pid = ExecFork();
6767 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6768 perror("launchctl stop");
6775 [self askForSettings];
6779 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6781 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6782 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6783 0, 0, screenrect.size.width, screenrect.size.height - 48
6784 ) database:database_];
6786 [book_ setDelegate:self];
6788 [overlay_ addSubview:book_];
6790 NSArray *buttonitems = [NSArray arrayWithObjects:
6791 [NSDictionary dictionaryWithObjectsAndKeys:
6792 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6793 @"home-up.png", kUIButtonBarButtonInfo,
6794 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6795 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6796 self, kUIButtonBarButtonTarget,
6797 @"Home", kUIButtonBarButtonTitle,
6798 @"0", kUIButtonBarButtonType,
6801 [NSDictionary dictionaryWithObjectsAndKeys:
6802 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6803 @"install-up.png", kUIButtonBarButtonInfo,
6804 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6805 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6806 self, kUIButtonBarButtonTarget,
6807 @"Sections", kUIButtonBarButtonTitle,
6808 @"0", kUIButtonBarButtonType,
6811 [NSDictionary dictionaryWithObjectsAndKeys:
6812 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6813 @"changes-up.png", kUIButtonBarButtonInfo,
6814 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6815 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6816 self, kUIButtonBarButtonTarget,
6817 @"Changes", kUIButtonBarButtonTitle,
6818 @"0", kUIButtonBarButtonType,
6821 [NSDictionary dictionaryWithObjectsAndKeys:
6822 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6823 @"manage-up.png", kUIButtonBarButtonInfo,
6824 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6825 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6826 self, kUIButtonBarButtonTarget,
6827 @"Manage", kUIButtonBarButtonTitle,
6828 @"0", kUIButtonBarButtonType,
6831 [NSDictionary dictionaryWithObjectsAndKeys:
6832 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6833 @"search-up.png", kUIButtonBarButtonInfo,
6834 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6835 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6836 self, kUIButtonBarButtonTarget,
6837 @"Search", kUIButtonBarButtonTitle,
6838 @"0", kUIButtonBarButtonType,
6842 buttonbar_ = [[UIToolbar alloc]
6844 withFrame:CGRectMake(
6845 0, screenrect.size.height - ButtonBarHeight_,
6846 screenrect.size.width, ButtonBarHeight_
6848 withItemList:buttonitems
6851 [buttonbar_ setDelegate:self];
6852 [buttonbar_ setBarStyle:1];
6853 [buttonbar_ setButtonBarTrackingMode:2];
6855 int buttons[5] = {1, 2, 3, 4, 5};
6856 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6857 [buttonbar_ showButtonGroup:0 withDuration:0];
6859 for (int i = 0; i != 5; ++i)
6860 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6861 i * 64 + 2, 1, 60, ButtonBarHeight_
6864 [buttonbar_ showSelectionForButton:1];
6865 [overlay_ addSubview:buttonbar_];
6867 [UIKeyboard initImplementationNow];
6868 CGSize keysize = [UIKeyboard defaultSize];
6869 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6870 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6871 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6872 [overlay_ addSubview:keyboard_];
6875 [underlay_ addSubview:overlay_];
6879 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6880 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6881 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6883 manage_ = (ManageView *) [[self
6884 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6885 withClass:[ManageView class]
6891 [self _setHomePage];
6894 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6895 NSString *context = [sheet context];
6896 if ([context isEqualToString:@"fixhalf"])
6899 @synchronized (self) {
6900 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6901 Package *broken = [broken_ objectAtIndex:i];
6904 NSString *id = [broken id];
6905 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6906 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6907 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6908 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6917 [broken_ removeAllObjects];
6924 else if ([context isEqualToString:@"role"]) {
6926 case 1: Role_ = @"User"; break;
6927 case 2: Role_ = @"Hacker"; break;
6928 case 3: Role_ = @"Developer"; break;
6935 bool reset = Settings_ != nil;
6937 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6941 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6949 } else if ([context isEqualToString:@"upgrade"])
6952 @synchronized (self) {
6953 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6954 Package *essential = [essential_ objectAtIndex:i];
6955 [essential install];
6978 - (void) reorganize { _pooled
6979 system("/usr/libexec/cydia/free.sh");
6980 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6983 - (void) applicationSuspend:(__GSEvent *)event {
6984 if (hud_ == nil && ![progress_ isRunning])
6985 [super applicationSuspend:event];
6988 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6990 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6993 - (void) _setSuspended:(BOOL)value {
6995 [super _setSuspended:value];
6998 - (UIProgressHUD *) addProgressHUD {
6999 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7001 [underlay_ addSubview:hud];
7005 - (void) openMailToURL:(NSURL *)url {
7006 // XXX: this makes me sad
7008 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7010 [UIApp openURL:url];
7014 - (RVPage *) pageForPackage:(NSString *)name {
7015 if (Package *package = [database_ packageWithName:name]) {
7016 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7017 [view setPackage:package];
7020 UIActionSheet *sheet = [[[UIActionSheet alloc]
7021 initWithTitle:@"Cannot Locate Package"
7022 buttons:[NSArray arrayWithObjects:@"Close", nil]
7023 defaultButtonIndex:0
7028 [sheet setBodyText:[NSString stringWithFormat:
7029 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7032 [sheet popupAlertAnimated:YES];
7037 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7038 NSString *href = [url absoluteString];
7043 if ([href isEqualToString:@"cydia://add-source"])
7044 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7045 else if ([href isEqualToString:@"cydia://sources"])
7046 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7047 else if ([href isEqualToString:@"cydia://packages"])
7048 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7049 else if ([href hasPrefix:@"cydia://url/"])
7050 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7051 else if ([href hasPrefix:@"cydia://launch/"])
7052 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7053 else if ([href hasPrefix:@"cydia://package-settings/"])
7054 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7055 else if ([href hasPrefix:@"cydia://package-signature/"])
7056 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7057 else if ([href hasPrefix:@"cydia://package/"])
7058 return [self pageForPackage:[href substringFromIndex:16]];
7059 else if ([href hasPrefix:@"cydia://files/"]) {
7060 NSString *name = [href substringFromIndex:14];
7062 if (Package *package = [database_ packageWithName:name]) {
7063 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7064 [files setPackage:package];
7072 - (void) applicationOpenURL:(NSURL *)url {
7073 [super applicationOpenURL:url];
7075 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7076 [self setPage:page];
7077 [buttonbar_ showSelectionForButton:tag];
7082 - (void) applicationDidFinishLaunching:(id)unused {
7083 Font12_ = [[UIFont systemFontOfSize:12] retain];
7084 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7085 Font14_ = [[UIFont systemFontOfSize:14] retain];
7086 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7087 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7089 _assert(pkgInitConfig(*_config));
7090 _assert(pkgInitSystem(*_config, _system));
7094 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7095 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7097 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7099 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7100 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7102 [window_ orderFront:self];
7103 [window_ makeKey:self];
7104 [window_ setHidden:NO];
7106 database_ = [Database sharedInstance];
7107 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7108 [database_ setDelegate:progress_];
7109 [window_ setContentView:progress_];
7111 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7112 [progress_ setContentView:underlay_];
7114 [progress_ resetView];
7117 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7118 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7119 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7120 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7121 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7122 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7123 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7125 [self setIdleTimerDisabled:YES];
7127 hud_ = [self addProgressHUD];
7128 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
7130 [self setStatusBarShowsProgress:YES];
7133 detachNewThreadSelector:@selector(reorganize)
7141 /* Web Scripting {{{ */
7142 + (NSString *) webScriptNameForSelector:(SEL)selector {
7143 if (selector == @selector(supports:))
7148 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7149 return selector != @selector(supports:);
7152 - (BOOL) supports:(NSString *)feature {
7153 return [feature isEqualToString:@"window.open"];
7157 - (void) showKeyboard:(BOOL)show {
7158 CGSize keysize = [UIKeyboard defaultSize];
7159 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7160 CGRect keyup = keydown;
7161 keyup.origin.y -= keysize.height;
7163 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7164 [animation setSignificantRectFields:2];
7167 [animation setStartFrame:keydown];
7168 [animation setEndFrame:keyup];
7169 [keyboard_ activate];
7171 [animation setStartFrame:keyup];
7172 [animation setEndFrame:keydown];
7173 [keyboard_ deactivate];
7176 [[UIAnimator sharedAnimator]
7177 addAnimations:[NSArray arrayWithObjects:animation, nil]
7178 withDuration:KeyboardTime_
7183 - (void) slideUp:(UIActionSheet *)alert {
7185 [alert presentSheetFromButtonBar:buttonbar_];
7187 [alert presentSheetInView:overlay_];
7192 void AddPreferences(NSString *plist) { _pooled
7193 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7194 _assert(settings != NULL);
7195 NSMutableArray *items = [settings objectForKey:@"items"];
7199 for (size_t i(0); i != [items count]; ++i) {
7200 NSMutableDictionary *item([items objectAtIndex:i]);
7201 NSString *label = [item objectForKey:@"label"];
7202 if (label != nil && [label isEqualToString:@"Cydia"]) {
7209 for (size_t i(0); i != [items count]; ++i) {
7210 NSDictionary *item([items objectAtIndex:i]);
7211 NSString *label = [item objectForKey:@"label"];
7212 if (label != nil && [label isEqualToString:@"General"]) {
7213 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7214 @"CydiaSettings", @"bundle",
7215 @"PSLinkCell", @"cell",
7216 [NSNumber numberWithBool:YES], @"hasIcon",
7217 [NSNumber numberWithBool:YES], @"isController",
7219 nil] atIndex:(i + 1)];
7225 _assert([settings writeToFile:plist atomically:YES] == YES);
7230 id Alloc_(id self, SEL selector) {
7231 id object = alloc_(self, selector);
7232 lprintf("[%s]A-%p\n", self->isa->name, object);
7237 id Dealloc_(id self, SEL selector) {
7238 id object = dealloc_(self, selector);
7239 lprintf("[%s]D-%p\n", self->isa->name, object);
7243 int main(int argc, char *argv[]) { _pooled
7244 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
7246 App_ = [[NSBundle mainBundle] bundlePath];
7247 Home_ = NSHomeDirectory();
7248 Locale_ = CFLocaleCopyCurrent();
7251 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7252 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7253 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7254 Sounds_Keyboard_ = [keyboard boolValue];
7260 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7261 _assert(errno == ENOENT);
7262 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7263 _assert(errno == ENOENT);
7265 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7266 alloc_ = alloc->method_imp;
7267 alloc->method_imp = (IMP) &Alloc_;*/
7269 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7270 dealloc_ = dealloc->method_imp;
7271 dealloc->method_imp = (IMP) &Dealloc_;*/
7276 size = sizeof(maxproc);
7277 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7278 perror("sysctlbyname(\"kern.maxproc\", ?)");
7279 else if (maxproc < 64) {
7281 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7282 perror("sysctlbyname(\"kern.maxproc\", #)");
7285 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7286 char *machine = new char[size];
7287 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7288 perror("sysctlbyname(\"hw.machine\", ?)");
7292 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7294 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7295 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7297 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7298 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7300 Settings_ = [Metadata_ objectForKey:@"Settings"];
7302 Packages_ = [Metadata_ objectForKey:@"Packages"];
7303 Sections_ = [Metadata_ objectForKey:@"Sections"];
7304 Sources_ = [Metadata_ objectForKey:@"Sources"];
7307 if (Settings_ != nil)
7308 Role_ = [Settings_ objectForKey:@"Role"];
7310 if (Packages_ == nil) {
7311 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7312 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7315 if (Sections_ == nil) {
7316 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7317 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7320 if (Sources_ == nil) {
7321 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7322 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7326 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7329 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7330 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7332 if (access("/User", F_OK) != 0)
7333 system("/usr/libexec/cydia/firmware.sh");
7335 _assert([[NSFileManager defaultManager]
7336 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7337 withIntermediateDirectories:YES
7342 space_ = CGColorSpaceCreateDeviceRGB();
7344 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7345 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7346 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7347 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7348 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7349 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7351 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7353 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7355 UIApplicationUseLegacyEvents(YES);
7356 UIKeyboardDisableAutomaticAppearance();
7358 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7360 CGColorSpaceRelease(space_);