1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #import "UICaboodle.h"
41 #include <objc/objc.h>
42 #include <objc/runtime.h>
44 #include <CoreGraphics/CoreGraphics.h>
45 #include <GraphicsServices/GraphicsServices.h>
46 #include <Foundation/Foundation.h>
48 #import <QuartzCore/CALayer.h>
50 #import <UIKit/UIKit.h>
53 #import <MessageUI/MailComposeController.h>
55 #import <WebCore/WebScriptObject.h>
56 //#include <WebCore/DOMHTML.h>
58 #include <WebKit/WebFrame.h>
59 #include <WebKit/WebPolicyDelegate.h>
60 #include <WebKit/WebView.h>
62 #import <WebKit/WebView-WebPrivate.h>
67 #include <ext/stdio_filebuf.h>
69 #include <apt-pkg/acquire.h>
70 #include <apt-pkg/acquire-item.h>
71 #include <apt-pkg/algorithms.h>
72 #include <apt-pkg/cachefile.h>
73 #include <apt-pkg/clean.h>
74 #include <apt-pkg/configuration.h>
75 #include <apt-pkg/debmetaindex.h>
76 #include <apt-pkg/error.h>
77 #include <apt-pkg/init.h>
78 #include <apt-pkg/mmap.h>
79 #include <apt-pkg/pkgrecords.h>
80 #include <apt-pkg/sha1.h>
81 #include <apt-pkg/sourcelist.h>
82 #include <apt-pkg/sptr.h>
83 #include <apt-pkg/strutl.h>
85 #include <sys/types.h>
87 #include <sys/sysctl.h>
93 #include <mach-o/nlist.h>
103 #import "BrowserView.h"
104 #import "ResetView.h"
107 //#define _finline __attribute__((force_inline))
108 #define _finline inline
113 #define _limit(count) do { \
114 static size_t _count(0); \
115 if (++_count == count) \
119 /* Objective-C Handle<> {{{ */
120 template <typename Type_>
122 typedef _H<Type_> This_;
127 _finline void Retain_() {
132 _finline void Clear_() {
138 _finline _H(Type_ *value = NULL, bool mended = false) :
149 _finline This_ &operator =(Type_ *value) {
150 if (value_ != value) {
159 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
161 void NSLogRect(const char *fix, const CGRect &rect) {
162 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
165 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
167 /* iPhoneOS 2.0 Compatibility {{{ */
169 @interface UITextView (iPhoneOS)
170 - (void) setTextSize:(float)size;
173 @implementation UITextView (iPhoneOS)
175 - (void) setTextSize:(float)size {
176 [self setFont:[[self font] fontWithSize:size]];
183 extern NSString * const kCAFilterNearest;
185 /* Information Dictionaries {{{ */
186 @interface NSMutableArray (Cydia)
187 - (void) addInfoDictionary:(NSDictionary *)info;
190 @implementation NSMutableArray (Cydia)
192 - (void) addInfoDictionary:(NSDictionary *)info {
193 [self addObject:info];
198 @interface NSMutableDictionary (Cydia)
199 - (void) addInfoDictionary:(NSDictionary *)info;
202 @implementation NSMutableDictionary (Cydia)
204 - (void) addInfoDictionary:(NSDictionary *)info {
205 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
206 [self setObject:info forKey:bundle];
211 /* Pop Transitions {{{ */
212 @interface PopTransitionView : UITransitionView {
217 @implementation PopTransitionView
219 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
220 if (from != nil && to == nil)
221 [self removeFromSuperview];
226 @interface UIView (PopUpView)
227 - (void) popFromSuperviewAnimated:(BOOL)animated;
228 - (void) popSubview:(UIView *)view;
231 @implementation UIView (PopUpView)
233 - (void) popFromSuperviewAnimated:(BOOL)animated {
234 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
237 - (void) popSubview:(UIView *)view {
238 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
239 [transition setDelegate:transition];
240 [self addSubview:transition];
242 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
243 [transition transition:UITransitionNone toView:blank];
244 [transition transition:UITransitionPushFromBottom toView:view];
250 #define lprintf(args...) fprintf(stderr, args)
253 #define RecycleWebViews 0
256 @interface NSMutableArray (Radix)
257 - (void) radixUsingSelector:(SEL)selector withObject:(id)object;
260 @implementation NSMutableArray (Radix)
262 - (void) radixUsingSelector:(SEL)selector withObject:(id)object {
263 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
264 [invocation setSelector:selector];
265 [invocation setArgument:&object atIndex:2];
267 size_t count([self count]);
272 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
274 for (size_t i(0); i != count; ++i) {
275 RadixItem &item(lhs[i]);
278 id object([self objectAtIndex:i]);
279 [invocation setTarget:object];
282 [invocation getReturnValue:&item.key];
285 static const size_t bits = 11;
286 static const size_t slots = 1 << bits;
287 static const size_t passes = (32 + (bits - 1)) / bits;
289 size_t *hist(new size_t[slots]);
291 for (size_t pass(0); pass != passes; ++pass) {
292 memset(hist, 0, sizeof(size_t) * slots);
294 for (size_t i(0); i != count; ++i) {
295 uint32_t key(lhs[i].key);
297 key &= _not(uint32_t) >> 32 - bits;
302 for (size_t i(0); i != slots; ++i) {
303 size_t local(offset);
308 for (size_t i(0); i != count; ++i) {
309 uint32_t key(lhs[i].key);
311 key &= _not(uint32_t) >> 32 - bits;
312 rhs[hist[key]++] = lhs[i];
322 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
323 for (size_t i(0); i != count; ++i)
324 [values addObject:[self objectAtIndex:lhs[i].index]];
325 [self setArray:values];
334 kUIControlEventMouseDown = 1 << 0,
335 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
336 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
337 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
338 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
339 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
340 } UIControlEventMasks;
342 @interface NSString (UIKit)
343 - (NSString *) stringByAddingPercentEscapes;
344 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
347 @interface NSString (Cydia)
348 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
349 - (NSComparisonResult) compareByPath:(NSString *)other;
352 @implementation NSString (Cydia)
354 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
355 char data[length + 1];
356 memcpy(data, bytes, length);
358 return [NSString stringWithUTF8String:data];
361 - (NSComparisonResult) compareByPath:(NSString *)other {
362 NSString *prefix = [self commonPrefixWithString:other options:0];
363 size_t length = [prefix length];
365 NSRange lrange = NSMakeRange(length, [self length] - length);
366 NSRange rrange = NSMakeRange(length, [other length] - length);
368 lrange = [self rangeOfString:@"/" options:0 range:lrange];
369 rrange = [other rangeOfString:@"/" options:0 range:rrange];
371 NSComparisonResult value;
373 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
374 value = NSOrderedSame;
375 else if (lrange.location == NSNotFound)
376 value = NSOrderedAscending;
377 else if (rrange.location == NSNotFound)
378 value = NSOrderedDescending;
380 value = NSOrderedSame;
382 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
383 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
384 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
385 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
387 NSComparisonResult result = [lpath compare:rpath];
388 return result == NSOrderedSame ? value : result;
393 /* Perl-Compatible RegEx {{{ */
403 Pcre(const char *regex) :
408 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
411 lprintf("%d:%s\n", offset, error);
415 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
416 matches_ = new int[(capture_ + 1) * 3];
424 NSString *operator [](size_t match) {
425 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
428 bool operator ()(NSString *data) {
429 // XXX: length is for characters, not for bytes
430 return operator ()([data UTF8String], [data length]);
433 bool operator ()(const char *data, size_t size) {
435 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
439 /* Mime Addresses {{{ */
440 @interface Address : NSObject {
446 - (NSString *) address;
448 + (Address *) addressWithString:(NSString *)string;
449 - (Address *) initWithString:(NSString *)string;
452 @implementation Address
461 - (NSString *) name {
465 - (NSString *) address {
469 + (Address *) addressWithString:(NSString *)string {
470 return [[[Address alloc] initWithString:string] autorelease];
473 + (NSArray *) _attributeKeys {
474 return [NSArray arrayWithObjects:@"address", @"name", nil];
477 - (NSArray *) attributeKeys {
478 return [[self class] _attributeKeys];
481 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
482 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
485 - (Address *) initWithString:(NSString *)string {
486 if ((self = [super init]) != nil) {
487 const char *data = [string UTF8String];
488 size_t size = [string length];
490 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
492 if (address_r(data, size)) {
493 name_ = [address_r[1] retain];
494 address_ = [address_r[2] retain];
496 name_ = [string retain];
504 /* CoreGraphics Primitives {{{ */
515 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
518 Set(space, red, green, blue, alpha);
523 CGColorRelease(color_);
530 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
532 float color[] = {red, green, blue, alpha};
533 color_ = CGColorCreate(space, color);
536 operator CGColorRef() {
542 extern "C" void UISetColor(CGColorRef color);
544 /* Random Global Variables {{{ */
545 static const int PulseInterval_ = 50000;
546 static const int ButtonBarHeight_ = 48;
547 static const float KeyboardTime_ = 0.3f;
549 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
550 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
552 static CGColor Blue_;
553 static CGColor Blueish_;
554 static CGColor Black_;
556 static CGColor White_;
557 static CGColor Gray_;
559 static NSString *App_;
560 static NSString *Home_;
561 static BOOL Sounds_Keyboard_;
563 static BOOL Advanced_;
567 static BOOL Ignored_;
569 static UIFont *Font12_;
570 static UIFont *Font12Bold_;
571 static UIFont *Font14_;
572 static UIFont *Font18Bold_;
573 static UIFont *Font22Bold_;
575 static const char *Machine_ = NULL;
576 static const NSString *UniqueID_ = NULL;
583 CGColorSpaceRef space_;
585 #define FW_LEAST(major, minor, bugfix) \
586 (major < Major_ || major == Major_ && \
587 (minor < Minor_ || minor == Minor_ && \
593 static NSDictionary *SectionMap_;
594 static NSMutableDictionary *Metadata_;
595 static _transient NSMutableDictionary *Settings_;
596 static _transient NSString *Role_;
597 static _transient NSMutableDictionary *Packages_;
598 static _transient NSMutableDictionary *Sections_;
599 static _transient NSMutableDictionary *Sources_;
600 static bool Changed_;
604 static NSMutableArray *Documents_;
607 NSString *GetLastUpdate() {
608 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
611 return @"Never or Unknown";
613 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
614 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
616 CFRelease(formatter);
618 return [(NSString *) formatted autorelease];
621 /* Display Helpers {{{ */
622 inline float Interpolate(float begin, float end, float fraction) {
623 return (end - begin) * fraction + begin;
626 NSString *SizeString(double size) {
627 bool negative = size < 0;
632 while (size > 1024) {
637 static const char *powers_[] = {"B", "kB", "MB", "GB"};
639 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
642 NSString *StripVersion(NSString *version) {
643 NSRange colon = [version rangeOfString:@":"];
644 if (colon.location != NSNotFound)
645 version = [version substringFromIndex:(colon.location + 1)];
649 NSString *Simplify(NSString *title) {
650 const char *data = [title UTF8String];
651 size_t size = [title length];
653 static Pcre square_r("^\\[(.*)\\]$");
654 if (square_r(data, size))
655 return Simplify(square_r[1]);
657 static Pcre paren_r("^\\((.*)\\)$");
658 if (paren_r(data, size))
659 return Simplify(paren_r[1]);
661 static Pcre title_r("^(.*?) \\(.*\\)$");
662 if (title_r(data, size))
663 return Simplify(title_r[1]);
669 bool isSectionVisible(NSString *section) {
670 NSDictionary *metadata = [Sections_ objectForKey:section];
671 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
672 return hidden == nil || ![hidden boolValue];
675 /* Delegate Prototypes {{{ */
679 @interface NSObject (ProgressDelegate)
682 @implementation NSObject(ProgressDelegate)
684 - (void) _setProgressError:(NSArray *)args {
685 [self performSelector:@selector(setProgressError:forPackage:)
686 withObject:[args objectAtIndex:0]
687 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
693 @protocol ProgressDelegate
694 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
695 - (void) setProgressTitle:(NSString *)title;
696 - (void) setProgressPercent:(float)percent;
697 - (void) startProgress;
698 - (void) addProgressOutput:(NSString *)output;
699 - (bool) isCancelling:(size_t)received;
702 @protocol ConfigurationDelegate
703 - (void) repairWithSelector:(SEL)selector;
704 - (void) setConfigurationData:(NSString *)data;
707 @protocol CydiaDelegate
708 - (void) installPackage:(Package *)package;
709 - (void) removePackage:(Package *)package;
710 - (void) slideUp:(UIActionSheet *)alert;
711 - (void) distUpgrade;
714 - (void) askForSettings;
715 - (UIProgressHUD *) addProgressHUD;
716 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
717 - (RVPage *) pageForPackage:(NSString *)name;
718 - (void) openMailToURL:(NSURL *)url;
722 /* Status Delegation {{{ */
724 public pkgAcquireStatus
727 _transient NSObject<ProgressDelegate> *delegate_;
735 void setDelegate(id delegate) {
736 delegate_ = delegate;
739 virtual bool MediaChange(std::string media, std::string drive) {
743 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
746 virtual void Fetch(pkgAcquire::ItemDesc &item) {
747 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
750 virtual void Done(pkgAcquire::ItemDesc &item) {
753 virtual void Fail(pkgAcquire::ItemDesc &item) {
755 item.Owner->Status == pkgAcquire::Item::StatIdle ||
756 item.Owner->Status == pkgAcquire::Item::StatDone
760 std::string &error(item.Owner->ErrorText);
764 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
765 NSArray *fields([description componentsSeparatedByString:@" "]);
766 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
768 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
769 withObject:[NSArray arrayWithObjects:
770 [NSString stringWithUTF8String:error.c_str()],
777 virtual bool Pulse(pkgAcquire *Owner) {
778 bool value = pkgAcquireStatus::Pulse(Owner);
781 double(CurrentBytes + CurrentItems) /
782 double(TotalBytes + TotalItems)
785 [delegate_ setProgressPercent:percent];
786 return [delegate_ isCancelling:CurrentBytes] ? false : value;
789 virtual void Start() {
790 [delegate_ startProgress];
793 virtual void Stop() {
797 /* Progress Delegation {{{ */
802 _transient id<ProgressDelegate> delegate_;
805 virtual void Update() {
806 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
807 [delegate_ setProgressPercent:(Percent / 100)];
816 void setDelegate(id delegate) {
817 delegate_ = delegate;
820 virtual void Done() {
821 [delegate_ setProgressPercent:1];
826 /* Database Interface {{{ */
827 @interface Database : NSObject {
829 pkgDepCache::Policy *policy_;
830 pkgRecords *records_;
831 pkgProblemResolver *resolver_;
832 pkgAcquire *fetcher_;
834 SPtr<pkgPackageManager> manager_;
835 pkgSourceList *list_;
837 NSMutableDictionary *sources_;
838 NSMutableArray *packages_;
840 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
849 + (Database *) sharedInstance;
851 - (void) _readCydia:(NSNumber *)fd;
852 - (void) _readStatus:(NSNumber *)fd;
853 - (void) _readOutput:(NSNumber *)fd;
857 - (Package *) packageWithName:(NSString *)name;
859 - (pkgCacheFile &) cache;
860 - (pkgDepCache::Policy *) policy;
861 - (pkgRecords *) records;
862 - (pkgProblemResolver *) resolver;
863 - (pkgAcquire &) fetcher;
864 - (NSArray *) packages;
865 - (NSArray *) sources;
874 - (void) updateWithStatus:(Status &)status;
876 - (void) setDelegate:(id)delegate;
877 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
881 /* Source Class {{{ */
882 @interface Source : NSObject {
883 NSString *description_;
888 NSString *distribution_;
892 NSString *defaultIcon_;
894 NSDictionary *record_;
898 - (Source *) initWithMetaIndex:(metaIndex *)index;
900 - (NSComparisonResult) compareByNameAndType:(Source *)source;
902 - (NSDictionary *) record;
906 - (NSString *) distribution;
912 - (NSString *) description;
913 - (NSString *) label;
914 - (NSString *) origin;
915 - (NSString *) version;
917 - (NSString *) defaultIcon;
921 @implementation Source
925 [distribution_ release];
928 if (description_ != nil)
929 [description_ release];
936 if (defaultIcon_ != nil)
937 [defaultIcon_ release];
944 + (NSArray *) _attributeKeys {
945 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
948 - (NSArray *) attributeKeys {
949 return [[self class] _attributeKeys];
952 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
953 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
956 - (Source *) initWithMetaIndex:(metaIndex *)index {
957 if ((self = [super init]) != nil) {
958 trusted_ = index->IsTrusted();
960 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
961 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
962 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
964 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
965 if (dindex != NULL) {
966 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
968 while (std::getline(release, line)) {
969 std::string::size_type colon(line.find(':'));
970 if (colon == std::string::npos)
973 std::string name(line.substr(0, colon));
974 std::string value(line.substr(colon + 1));
975 while (!value.empty() && value[0] == ' ')
976 value = value.substr(1);
978 if (name == "Default-Icon")
979 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
980 else if (name == "Description")
981 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
982 else if (name == "Label")
983 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
984 else if (name == "Origin")
985 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
986 else if (name == "Version")
987 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
991 record_ = [Sources_ objectForKey:[self key]];
993 record_ = [record_ retain];
997 - (NSComparisonResult) compareByNameAndType:(Source *)source {
998 NSDictionary *lhr = [self record];
999 NSDictionary *rhr = [source record];
1002 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1004 NSString *lhs = [self name];
1005 NSString *rhs = [source name];
1007 if ([lhs length] != 0 && [rhs length] != 0) {
1008 unichar lhc = [lhs characterAtIndex:0];
1009 unichar rhc = [rhs characterAtIndex:0];
1011 if (isalpha(lhc) && !isalpha(rhc))
1012 return NSOrderedAscending;
1013 else if (!isalpha(lhc) && isalpha(rhc))
1014 return NSOrderedDescending;
1017 return [lhs compare:rhs options:CompareOptions_];
1020 - (NSDictionary *) record {
1028 - (NSString *) uri {
1032 - (NSString *) distribution {
1033 return distribution_;
1036 - (NSString *) type {
1040 - (NSString *) key {
1041 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1044 - (NSString *) host {
1045 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1048 - (NSString *) name {
1049 return origin_ == nil ? [self host] : origin_;
1052 - (NSString *) description {
1053 return description_;
1056 - (NSString *) label {
1057 return label_ == nil ? [self host] : label_;
1060 - (NSString *) origin {
1064 - (NSString *) version {
1068 - (NSString *) defaultIcon {
1069 return defaultIcon_;
1074 /* Relationship Class {{{ */
1075 @interface Relationship : NSObject {
1080 - (NSString *) type;
1082 - (NSString *) name;
1086 @implementation Relationship
1094 - (NSString *) type {
1102 - (NSString *) name {
1109 /* Package Class {{{ */
1110 NSString *Scour(const char *field, const char *begin, const char *end) {
1111 size_t i(0), l(strlen(field));
1114 const char *name = begin + i;
1115 const char *colon = name + l;
1116 const char *value = colon + 1;
1121 strncasecmp(name, field, l) == 0
1123 while (value != end && value[0] == ' ')
1125 const char *line = std::find(value, end, '\n');
1126 while (line != value && line[-1] == ' ')
1129 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1131 begin = std::find(begin, end, '\n');
1139 @interface Package : NSObject {
1140 pkgCache::PkgIterator iterator_;
1141 _transient Database *database_;
1142 pkgCache::VerIterator version_;
1143 pkgCache::VerFileIterator file_;
1151 NSString *installed_;
1157 NSString *depiction_;
1158 NSString *homepage_;
1164 NSArray *relationships_;
1167 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1168 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1170 - (pkgCache::PkgIterator) iterator;
1172 - (NSString *) section;
1173 - (Address *) maintainer;
1175 - (NSString *) description;
1176 - (NSString *) index;
1178 - (NSMutableDictionary *) metadata;
1180 - (BOOL) subscribed;
1183 - (NSString *) latest;
1184 - (NSString *) installed;
1187 - (BOOL) upgradableAndEssential:(BOOL)essential;
1190 - (BOOL) unfiltered;
1194 - (BOOL) halfConfigured;
1195 - (BOOL) halfInstalled;
1197 - (NSString *) mode;
1200 - (NSString *) name;
1201 - (NSString *) tagline;
1203 - (NSString *) homepage;
1204 - (NSString *) depiction;
1205 - (Address *) author;
1207 - (NSArray *) files;
1208 - (NSArray *) relationships;
1209 - (NSArray *) warnings;
1210 - (NSArray *) applications;
1212 - (Source *) source;
1213 - (NSString *) role;
1215 - (BOOL) matches:(NSString *)text;
1217 - (bool) hasSupportingRole;
1218 - (BOOL) hasTag:(NSString *)tag;
1219 - (NSString *) primaryPurpose;
1220 - (NSArray *) purposes;
1222 - (NSComparisonResult) compareByName:(Package *)package;
1223 - (NSComparisonResult) compareBySection:(Package *)package;
1225 - (uint32_t) compareForChanges;
1230 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1231 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1232 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1233 - (NSNumber *) isVisibleInSource:(Source *)source;
1237 @implementation Package
1243 if (section_ != nil)
1247 if (installed_ != nil)
1248 [installed_ release];
1256 if (depiction_ != nil)
1257 [depiction_ release];
1258 if (homepage_ != nil)
1259 [homepage_ release];
1260 if (sponsor_ != nil)
1269 if (relationships_ != nil)
1270 [relationships_ release];
1275 + (NSArray *) _attributeKeys {
1276 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1279 - (NSArray *) attributeKeys {
1280 return [[self class] _attributeKeys];
1283 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1284 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1287 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1288 if ((self = [super init]) != nil) {
1289 iterator_ = iterator;
1290 database_ = database;
1292 version_ = [database_ policy]->GetCandidateVer(iterator_);
1293 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1294 latest_ = [StripVersion(latest) retain];
1296 pkgCache::VerIterator current = iterator_.CurrentVer();
1297 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1298 installed_ = [StripVersion(installed) retain];
1300 if (!version_.end())
1301 file_ = version_.FileList();
1303 pkgCache &cache([database_ cache]);
1304 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1307 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1310 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1312 const char *begin, *end;
1313 parser->GetRec(begin, end);
1315 name_ = Scour("name", begin, end);
1317 name_ = [name_ retain];
1318 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1319 icon_ = Scour("icon", begin, end);
1321 icon_ = [icon_ retain];
1322 depiction_ = Scour("depiction", begin, end);
1323 if (depiction_ != nil)
1324 depiction_ = [depiction_ retain];
1325 homepage_ = Scour("homepage", begin, end);
1326 if (homepage_ == nil)
1327 homepage_ = Scour("website", begin, end);
1328 if ([homepage_ isEqualToString:depiction_])
1330 if (homepage_ != nil)
1331 homepage_ = [homepage_ retain];
1332 NSString *sponsor = Scour("sponsor", begin, end);
1334 sponsor_ = [[Address addressWithString:sponsor] retain];
1335 NSString *author = Scour("author", begin, end);
1337 author_ = [[Address addressWithString:author] retain];
1338 NSString *tags = Scour("tag", begin, end);
1340 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1344 for (int i(0), e([tags_ count]); i != e; ++i) {
1345 NSString *tag = [tags_ objectAtIndex:i];
1346 if ([tag hasPrefix:@"role::"]) {
1347 role_ = [[tag substringFromIndex:6] retain];
1352 NSString *solid(latest == nil ? installed : latest);
1353 bool changed(false);
1355 NSString *key([id_ lowercaseString]);
1357 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1358 if (metadata == nil) {
1359 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1364 [metadata setObject:solid forKey:@"LastVersion"];
1367 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1368 NSDate *last([metadata objectForKey:@"LastSeen"]);
1369 NSString *version([metadata objectForKey:@"LastVersion"]);
1372 first = last == nil ? now_ : last;
1373 [metadata setObject:first forKey:@"FirstSeen"];
1378 if (version == nil) {
1379 [metadata setObject:solid forKey:@"LastVersion"];
1381 } else if (![version isEqualToString:solid]) {
1382 [metadata setObject:solid forKey:@"LastVersion"];
1384 [metadata setObject:last forKey:@"LastSeen"];
1390 [Packages_ setObject:metadata forKey:key];
1396 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1397 return [[[Package alloc]
1398 initWithIterator:iterator
1403 - (pkgCache::PkgIterator) iterator {
1407 - (NSString *) section {
1408 if (section_ != nil)
1411 const char *section = iterator_.Section();
1412 if (section == NULL)
1415 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1418 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1419 if (NSString *rename = [value objectForKey:@"Rename"]) {
1424 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1428 - (Address *) maintainer {
1431 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1432 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1436 return version_.end() ? 0 : version_->InstalledSize;
1439 - (NSString *) description {
1442 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1443 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1445 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1446 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1447 if ([lines count] < 2)
1450 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1451 for (size_t i(1); i != [lines count]; ++i) {
1452 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1453 [trimmed addObject:trim];
1456 return [trimmed componentsJoinedByString:@"\n"];
1459 - (NSString *) index {
1460 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1461 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1464 - (NSMutableDictionary *) metadata {
1465 return [Packages_ objectForKey:[id_ lowercaseString]];
1469 NSDictionary *metadata([self metadata]);
1470 if ([self subscribed])
1471 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1473 return [metadata objectForKey:@"FirstSeen"];
1476 - (BOOL) subscribed {
1477 NSDictionary *metadata([self metadata]);
1478 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1479 return [subscribed boolValue];
1485 NSDictionary *metadata([self metadata]);
1486 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1487 return [ignored boolValue];
1492 - (NSString *) latest {
1496 - (NSString *) installed {
1501 return !version_.end();
1504 - (BOOL) upgradableAndEssential:(BOOL)essential {
1505 pkgCache::VerIterator current = iterator_.CurrentVer();
1508 return essential && [self essential];
1510 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1511 return !candidate.end() && candidate != current;
1515 - (BOOL) essential {
1516 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1520 return [database_ cache][iterator_].InstBroken();
1523 - (BOOL) unfiltered {
1524 NSString *section = [self section];
1525 return section == nil || isSectionVisible(section);
1529 return [self hasSupportingRole] && [self unfiltered];
1533 unsigned char current = iterator_->CurrentState;
1534 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1537 - (BOOL) halfConfigured {
1538 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1541 - (BOOL) halfInstalled {
1542 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1546 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1547 return state.Mode != pkgDepCache::ModeKeep;
1550 - (NSString *) mode {
1551 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1553 switch (state.Mode) {
1554 case pkgDepCache::ModeDelete:
1555 if ((state.iFlags & pkgDepCache::Purge) != 0)
1559 case pkgDepCache::ModeKeep:
1560 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1564 case pkgDepCache::ModeInstall:
1565 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1566 return @"Reinstall";
1567 else switch (state.Status) {
1569 return @"Downgrade";
1575 return @"New Install";
1588 - (NSString *) name {
1589 return name_ == nil ? id_ : name_;
1592 - (NSString *) tagline {
1596 - (UIImage *) icon {
1597 NSString *section = [self section];
1599 section = Simplify(section);
1602 if (NSString *icon = icon_)
1603 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1604 if (icon == nil) if (section != nil)
1605 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1606 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1607 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1609 icon = [UIImage applicationImageNamed:@"unknown.png"];
1613 - (NSString *) homepage {
1617 - (NSString *) depiction {
1621 - (Address *) sponsor {
1625 - (Address *) author {
1629 - (NSArray *) files {
1630 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1631 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1634 fin.open([path UTF8String]);
1639 while (std::getline(fin, line))
1640 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1645 - (NSArray *) relationships {
1646 return relationships_;
1649 - (NSArray *) warnings {
1650 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1651 const char *name(iterator_.Name());
1653 size_t length(strlen(name));
1654 if (length < 2) invalid:
1655 [warnings addObject:@"illegal package identifier"];
1656 else for (size_t i(0); i != length; ++i)
1658 (name[i] < 'a' || name[i] > 'z') &&
1659 (name[i] < '0' || name[i] > '9') &&
1660 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1663 if (strcmp(name, "cydia") != 0) {
1667 if (NSArray *files = [self files])
1668 for (NSString *file in files)
1669 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1671 else if (!stash && [file isEqualToString:@"/var/stash"])
1675 [warnings addObject:@"files installed into Cydia.app"];
1677 [warnings addObject:@"files installed to /var/stash"];
1680 return [warnings count] == 0 ? nil : warnings;
1683 - (NSArray *) applications {
1684 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1686 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1688 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1689 if (NSArray *files = [self files])
1690 for (NSString *file in files)
1691 if (application_r(file)) {
1692 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1693 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1694 if ([id isEqualToString:me])
1697 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1699 display = application_r[1];
1701 NSString *bundle([file stringByDeletingLastPathComponent]);
1702 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1703 if (icon == nil || [icon length] == 0)
1705 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1707 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1708 [applications addObject:application];
1710 [application addObject:id];
1711 [application addObject:display];
1712 [application addObject:url];
1715 return [applications count] == 0 ? nil : applications;
1718 - (Source *) source {
1720 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1727 - (NSString *) role {
1731 - (BOOL) matches:(NSString *)text {
1737 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1738 if (range.location != NSNotFound)
1741 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1742 if (range.location != NSNotFound)
1745 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1746 if (range.location != NSNotFound)
1752 - (bool) hasSupportingRole {
1755 if ([role_ isEqualToString:@"enduser"])
1757 if ([Role_ isEqualToString:@"User"])
1759 if ([role_ isEqualToString:@"hacker"])
1761 if ([Role_ isEqualToString:@"Hacker"])
1763 if ([role_ isEqualToString:@"developer"])
1765 if ([Role_ isEqualToString:@"Developer"])
1770 - (BOOL) hasTag:(NSString *)tag {
1771 return tags_ == nil ? NO : [tags_ containsObject:tag];
1774 - (NSString *) primaryPurpose {
1775 for (NSString *tag in tags_)
1776 if ([tag hasPrefix:@"purpose::"])
1777 return [tag substringFromIndex:9];
1781 - (NSArray *) purposes {
1782 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1783 for (NSString *tag in tags_)
1784 if ([tag hasPrefix:@"purpose::"])
1785 [purposes addObject:[tag substringFromIndex:9]];
1786 return [purposes count] == 0 ? nil : purposes;
1789 - (NSComparisonResult) compareByName:(Package *)package {
1790 NSString *lhs = [self name];
1791 NSString *rhs = [package name];
1793 if ([lhs length] != 0 && [rhs length] != 0) {
1794 unichar lhc = [lhs characterAtIndex:0];
1795 unichar rhc = [rhs characterAtIndex:0];
1797 if (isalpha(lhc) && !isalpha(rhc))
1798 return NSOrderedAscending;
1799 else if (!isalpha(lhc) && isalpha(rhc))
1800 return NSOrderedDescending;
1803 return [lhs compare:rhs options:CompareOptions_];
1806 - (NSComparisonResult) compareBySection:(Package *)package {
1807 NSString *lhs = [self section];
1808 NSString *rhs = [package section];
1810 if (lhs == NULL && rhs != NULL)
1811 return NSOrderedAscending;
1812 else if (lhs != NULL && rhs == NULL)
1813 return NSOrderedDescending;
1814 else if (lhs != NULL && rhs != NULL) {
1815 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1816 if (result != NSOrderedSame)
1820 return NSOrderedSame;
1823 - (uint32_t) compareForChanges {
1828 uint32_t timestamp : 30;
1829 uint32_t ignored : 1;
1830 uint32_t upgradable : 1;
1834 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1836 if ([self upgradableAndEssential:YES]) {
1837 value.bits.timestamp = 0;
1838 value.bits.ignored = [self ignored] ? 0 : 1;
1839 value.bits.upgradable = 1;
1841 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1842 value.bits.ignored = 0;
1843 value.bits.upgradable = 0;
1846 return _not(uint32_t) - value.key;
1850 pkgProblemResolver *resolver = [database_ resolver];
1851 resolver->Clear(iterator_);
1852 resolver->Protect(iterator_);
1853 pkgCacheFile &cache([database_ cache]);
1854 cache->MarkInstall(iterator_, false);
1855 pkgDepCache::StateCache &state((*cache)[iterator_]);
1856 if (!state.Install())
1857 cache->SetReInstall(iterator_, true);
1861 pkgProblemResolver *resolver = [database_ resolver];
1862 resolver->Clear(iterator_);
1863 resolver->Protect(iterator_);
1864 resolver->Remove(iterator_);
1865 [database_ cache]->MarkDelete(iterator_, true);
1868 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1869 return [NSNumber numberWithBool:(
1870 [self unfiltered] && [self matches:search]
1874 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1875 return [NSNumber numberWithBool:(
1876 (![number boolValue] || [self visible]) && [self installed] != nil
1880 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1881 NSString *section = [self section];
1883 return [NSNumber numberWithBool:(
1885 [self installed] == nil && (
1887 section == nil && [name length] == 0 ||
1888 [name isEqualToString:section]
1893 - (NSNumber *) isVisibleInSource:(Source *)source {
1894 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1899 /* Section Class {{{ */
1900 @interface Section : NSObject {
1906 - (NSComparisonResult) compareByName:(Section *)section;
1907 - (Section *) initWithName:(NSString *)name;
1908 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1909 - (NSString *) name;
1912 - (void) addToCount;
1916 @implementation Section
1923 - (NSComparisonResult) compareByName:(Section *)section {
1924 NSString *lhs = [self name];
1925 NSString *rhs = [section name];
1927 if ([lhs length] != 0 && [rhs length] != 0) {
1928 unichar lhc = [lhs characterAtIndex:0];
1929 unichar rhc = [rhs characterAtIndex:0];
1931 if (isalpha(lhc) && !isalpha(rhc))
1932 return NSOrderedAscending;
1933 else if (!isalpha(lhc) && isalpha(rhc))
1934 return NSOrderedDescending;
1937 return [lhs compare:rhs options:CompareOptions_];
1940 - (Section *) initWithName:(NSString *)name {
1941 return [self initWithName:name row:0];
1944 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1945 if ((self = [super init]) != nil) {
1946 name_ = [name retain];
1951 - (NSString *) name {
1963 - (void) addToCount {
1971 static NSArray *Finishes_;
1973 /* Database Implementation {{{ */
1974 @implementation Database
1976 + (Database *) sharedInstance {
1977 static Database *instance;
1978 if (instance == nil)
1979 instance = [[Database alloc] init];
1988 - (void) _readCydia:(NSNumber *)fd { _pooled
1989 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1990 std::istream is(&ib);
1993 static Pcre finish_r("^finish:([^:]*)$");
1995 while (std::getline(is, line)) {
1996 const char *data(line.c_str());
1997 size_t size = line.size();
1998 lprintf("C:%s\n", data);
2000 if (finish_r(data, size)) {
2001 NSString *finish = finish_r[1];
2002 int index = [Finishes_ indexOfObject:finish];
2003 if (index != INT_MAX && index > Finish_)
2011 - (void) _readStatus:(NSNumber *)fd { _pooled
2012 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2013 std::istream is(&ib);
2016 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2017 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2019 while (std::getline(is, line)) {
2020 const char *data(line.c_str());
2021 size_t size = line.size();
2022 lprintf("S:%s\n", data);
2024 if (conffile_r(data, size)) {
2025 [delegate_ setConfigurationData:conffile_r[1]];
2026 } else if (strncmp(data, "status: ", 8) == 0) {
2027 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2028 [delegate_ setProgressTitle:string];
2029 } else if (pmstatus_r(data, size)) {
2030 std::string type([pmstatus_r[1] UTF8String]);
2031 NSString *id = pmstatus_r[2];
2033 float percent([pmstatus_r[3] floatValue]);
2034 [delegate_ setProgressPercent:(percent / 100)];
2036 NSString *string = pmstatus_r[4];
2038 if (type == "pmerror")
2039 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2040 withObject:[NSArray arrayWithObjects:string, id, nil]
2043 else if (type == "pmstatus")
2044 [delegate_ setProgressTitle:string];
2045 else if (type == "pmconffile")
2046 [delegate_ setConfigurationData:string];
2047 else _assert(false);
2048 } else _assert(false);
2054 - (void) _readOutput:(NSNumber *)fd { _pooled
2055 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2056 std::istream is(&ib);
2059 while (std::getline(is, line)) {
2060 lprintf("O:%s\n", line.c_str());
2061 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2071 - (Package *) packageWithName:(NSString *)name {
2072 if (static_cast<pkgDepCache *>(cache_) == NULL)
2074 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2075 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2078 - (Database *) init {
2079 if ((self = [super init]) != nil) {
2086 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2087 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2091 _assert(pipe(fds) != -1);
2094 _config->Set("APT::Keep-Fds::", cydiafd_);
2095 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2098 detachNewThreadSelector:@selector(_readCydia:)
2100 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2103 _assert(pipe(fds) != -1);
2107 detachNewThreadSelector:@selector(_readStatus:)
2109 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2112 _assert(pipe(fds) != -1);
2113 _assert(dup2(fds[0], 0) != -1);
2114 _assert(close(fds[0]) != -1);
2116 input_ = fdopen(fds[1], "a");
2118 _assert(pipe(fds) != -1);
2119 _assert(dup2(fds[1], 1) != -1);
2120 _assert(close(fds[1]) != -1);
2123 detachNewThreadSelector:@selector(_readOutput:)
2125 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2130 - (pkgCacheFile &) cache {
2134 - (pkgDepCache::Policy *) policy {
2138 - (pkgRecords *) records {
2142 - (pkgProblemResolver *) resolver {
2146 - (pkgAcquire &) fetcher {
2150 - (NSArray *) packages {
2154 - (NSArray *) sources {
2155 return [sources_ allValues];
2158 - (NSArray *) issues {
2159 if (cache_->BrokenCount() == 0)
2162 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2164 for (Package *package in packages_) {
2165 if (![package broken])
2167 pkgCache::PkgIterator pkg([package iterator]);
2169 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2170 [entry addObject:[package name]];
2171 [issues addObject:entry];
2173 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2177 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2178 pkgCache::DepIterator start;
2179 pkgCache::DepIterator end;
2180 dep.GlobOr(start, end); // ++dep
2182 if (!cache_->IsImportantDep(end))
2184 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2187 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2188 [entry addObject:failure];
2189 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2191 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2192 [failure addObject:[package name]];
2194 pkgCache::PkgIterator target(start.TargetPkg());
2195 if (target->ProvidesList != 0)
2196 [failure addObject:@"?"];
2198 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2200 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2201 else if (!cache_[target].CandidateVerIter(cache_).end())
2202 [failure addObject:@"-"];
2203 else if (target->ProvidesList == 0)
2204 [failure addObject:@"!"];
2206 [failure addObject:@"%"];
2210 if (start.TargetVer() != 0)
2211 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2222 - (void) reloadData {
2241 if (!cache_.Open(progress_, true)) {
2243 if (!_error->PopMessage(error))
2246 lprintf("cache_.Open():[%s]\n", error.c_str());
2248 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2249 [delegate_ repairWithSelector:@selector(configure)];
2250 else if (error == "The package lists or status file could not be parsed or opened.")
2251 [delegate_ repairWithSelector:@selector(update)];
2252 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2253 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2254 // else if (error == "The list of sources could not be read.")
2255 else _assert(false);
2260 now_ = [[NSDate date] retain];
2262 policy_ = new pkgDepCache::Policy();
2263 records_ = new pkgRecords(cache_);
2264 resolver_ = new pkgProblemResolver(cache_);
2265 fetcher_ = new pkgAcquire(&status_);
2268 list_ = new pkgSourceList();
2269 _assert(list_->ReadMainList());
2271 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2272 _assert(pkgApplyStatus(cache_));
2274 if (cache_->BrokenCount() != 0) {
2275 _assert(pkgFixBroken(cache_));
2276 _assert(cache_->BrokenCount() == 0);
2277 _assert(pkgMinimizeUpgrade(cache_));
2280 [sources_ removeAllObjects];
2281 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2282 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2283 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2285 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2286 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2290 [packages_ removeAllObjects];
2291 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2292 if (Package *package = [Package packageWithIterator:iterator database:self])
2293 [packages_ addObject:package];
2295 [packages_ sortUsingSelector:@selector(compareByName:)];
2298 - (void) configure {
2299 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2300 system([dpkg UTF8String]);
2308 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2309 _assert(!_error->PendingError());
2312 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2315 public pkgArchiveCleaner
2318 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2323 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2325 while (_error->PopMessage(error))
2326 lprintf("ArchiveCleaner: %s\n", error.c_str());
2331 pkgRecords records(cache_);
2333 lock_ = new FileFd();
2334 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2335 _assert(!_error->PendingError());
2338 // XXX: explain this with an error message
2339 _assert(list.ReadMainList());
2341 manager_ = (_system->CreatePM(cache_));
2342 _assert(manager_->GetArchives(fetcher_, &list, &records));
2343 _assert(!_error->PendingError());
2347 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2349 _assert(list.ReadMainList());
2350 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2351 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2354 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2359 bool failed = false;
2360 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2361 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2364 std::string uri = (*item)->DescURI();
2365 std::string error = (*item)->ErrorText;
2367 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2370 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2371 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2382 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2384 if (_error->PendingError()) {
2389 if (result == pkgPackageManager::Failed) {
2394 if (result != pkgPackageManager::Completed) {
2399 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2401 _assert(list.ReadMainList());
2402 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2403 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2406 if (![before isEqualToArray:after])
2411 _assert(pkgDistUpgrade(cache_));
2415 [self updateWithStatus:status_];
2418 - (void) updateWithStatus:(Status &)status {
2420 _assert(list.ReadMainList());
2423 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2424 _assert(!_error->PendingError());
2426 pkgAcquire fetcher(&status);
2427 _assert(list.GetIndexes(&fetcher));
2429 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2430 bool failed = false;
2431 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2432 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2433 (*item)->Finished();
2437 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2438 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2439 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2442 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2447 - (void) setDelegate:(id)delegate {
2448 delegate_ = delegate;
2449 status_.setDelegate(delegate);
2450 progress_.setDelegate(delegate);
2453 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2454 pkgIndexFile *index(NULL);
2455 list_->FindIndex(file, index);
2456 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2462 /* PopUp Windows {{{ */
2463 @interface PopUpView : UIView {
2464 _transient id delegate_;
2465 UITransitionView *transition_;
2470 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2474 @implementation PopUpView
2477 [transition_ setDelegate:nil];
2478 [transition_ release];
2484 [transition_ transition:UITransitionPushFromTop toView:nil];
2487 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2488 if (from != nil && to == nil)
2489 [self removeFromSuperview];
2492 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2493 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2494 delegate_ = delegate;
2496 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2497 [self addSubview:transition_];
2499 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2501 [view addSubview:self];
2503 [transition_ setDelegate:self];
2505 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2506 [transition_ transition:UITransitionNone toView:blank];
2507 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2514 /* Mail Composition {{{ */
2515 @interface MailToView : PopUpView {
2516 MailComposeController *controller_;
2519 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2523 @implementation MailToView
2526 [controller_ release];
2530 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2534 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2535 NSLog(@"did:%@", delivery);
2536 // [UIApp setStatusBarShowsProgress:NO];
2537 if ([controller error]){
2538 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2539 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2540 [mailAlertSheet setBodyText:[controller error]];
2541 [mailAlertSheet popupAlertAnimated:YES];
2545 - (void) showError {
2546 NSLog(@"%@", [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];
2553 - (void) deliverMessage { _pooled
2557 if (![controller_ deliverMessage])
2558 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2561 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2562 if ([controller_ needsDelivery])
2563 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2568 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2569 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2570 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2571 [controller_ setDelegate:self];
2572 [controller_ initializeUI];
2573 [controller_ setupForURL:url];
2575 UIView *view([controller_ view]);
2576 [overlay_ addSubview:view];
2582 /* Confirmation View {{{ */
2583 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2584 if (!iterator.end())
2585 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2586 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2588 pkgCache::PkgIterator package(dep.TargetPkg());
2591 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2598 @protocol ConfirmationViewDelegate
2603 @interface ConfirmationView : BrowserView {
2604 _transient Database *database_;
2605 UIActionSheet *essential_;
2612 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2616 @implementation ConfirmationView
2623 if (essential_ != nil)
2624 [essential_ release];
2630 [book_ popFromSuperviewAnimated:YES];
2633 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2634 NSString *context = [sheet context];
2636 if ([context isEqualToString:@"remove"])
2644 [delegate_ confirm];
2649 else if ([context isEqualToString:@"unable"])
2655 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2656 [window setValue:changes_ forKey:@"changes"];
2657 [window setValue:issues_ forKey:@"issues"];
2658 [window setValue:sizes_ forKey:@"sizes"];
2659 [super webView:sender didClearWindowObject:window forFrame:frame];
2662 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2663 if ((self = [super initWithBook:book]) != nil) {
2664 database_ = database;
2666 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2667 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2668 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2669 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2670 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2674 pkgDepCache::Policy *policy([database_ policy]);
2676 pkgCacheFile &cache([database_ cache]);
2677 NSArray *packages = [database_ packages];
2678 for (size_t i(0), e = [packages count]; i != e; ++i) {
2679 Package *package = [packages objectAtIndex:i];
2680 pkgCache::PkgIterator iterator = [package iterator];
2681 pkgDepCache::StateCache &state(cache[iterator]);
2683 NSString *name([package name]);
2685 if (state.NewInstall())
2686 [installing addObject:name];
2687 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2688 [reinstalling addObject:name];
2689 else if (state.Upgrade())
2690 [upgrading addObject:name];
2691 else if (state.Downgrade())
2692 [downgrading addObject:name];
2693 else if (state.Delete()) {
2694 if ([package essential])
2696 [removing addObject:name];
2699 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2700 substrate_ |= DepSubstrate(iterator.CurrentVer());
2705 else if (Advanced_ || true) {
2706 essential_ = [[UIActionSheet alloc]
2707 initWithTitle:@"Removing Essentials"
2708 buttons:[NSArray arrayWithObjects:
2709 @"Cancel Operation (Safe)",
2710 @"Force Removal (Unsafe)",
2712 defaultButtonIndex:0
2718 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2720 [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."];
2722 essential_ = [[UIActionSheet alloc]
2723 initWithTitle:@"Unable to Comply"
2724 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2725 defaultButtonIndex:0
2730 [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."];
2733 changes_ = [[NSArray alloc] initWithObjects:
2741 issues_ = [database_ issues];
2743 issues_ = [issues_ retain];
2745 sizes_ = [[NSArray alloc] initWithObjects:
2746 SizeString([database_ fetcher].FetchNeeded()),
2747 SizeString([database_ fetcher].PartialPresent()),
2748 SizeString([database_ cache]->UsrSize()),
2751 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2755 - (NSString *) backButtonTitle {
2759 - (NSString *) leftButtonTitle {
2763 - (NSString *) _rightButtonTitle {
2764 return issues_ == nil ? @"Confirm" : nil;
2767 - (void) _leftButtonClicked {
2771 - (void) _rightButtonClicked {
2772 if (essential_ != nil)
2773 [essential_ popupAlertAnimated:YES];
2777 [delegate_ confirm];
2784 /* Progress Data {{{ */
2785 @interface ProgressData : NSObject {
2791 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2798 @implementation ProgressData
2800 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2801 if ((self = [super init]) != nil) {
2802 selector_ = selector;
2822 /* Progress View {{{ */
2823 @interface ProgressView : UIView <
2824 ConfigurationDelegate,
2827 _transient Database *database_;
2829 UIView *background_;
2830 UITransitionView *transition_;
2832 UINavigationBar *navbar_;
2833 UIProgressBar *progress_;
2834 UITextView *output_;
2835 UITextLabel *status_;
2836 UIPushButton *close_;
2839 SHA1SumValue springlist_;
2840 SHA1SumValue sandplate_;
2842 NSTimeInterval last_;
2845 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2847 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2848 - (void) setContentView:(UIView *)view;
2851 - (void) _retachThread;
2852 - (void) _detachNewThreadData:(ProgressData *)data;
2853 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2859 @protocol ProgressViewDelegate
2860 - (void) progressViewIsComplete:(ProgressView *)sender;
2863 @implementation ProgressView
2866 [transition_ setDelegate:nil];
2867 [navbar_ setDelegate:nil];
2870 if (background_ != nil)
2871 [background_ release];
2872 [transition_ release];
2875 [progress_ release];
2882 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2883 if (bootstrap_ && from == overlay_ && to == view_)
2887 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2888 if ((self = [super initWithFrame:frame]) != nil) {
2889 database_ = database;
2890 delegate_ = delegate;
2892 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2893 [transition_ setDelegate:self];
2895 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2898 [overlay_ setBackgroundColor:[UIColor blackColor]];
2900 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2901 [background_ setBackgroundColor:[UIColor blackColor]];
2902 [self addSubview:background_];
2905 [self addSubview:transition_];
2907 CGSize navsize = [UINavigationBar defaultSize];
2908 CGRect navrect = {{0, 0}, navsize};
2910 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2911 [overlay_ addSubview:navbar_];
2913 [navbar_ setBarStyle:1];
2914 [navbar_ setDelegate:self];
2916 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2917 [navbar_ pushNavigationItem:navitem];
2919 CGRect bounds = [overlay_ bounds];
2920 CGSize prgsize = [UIProgressBar defaultSize];
2923 (bounds.size.width - prgsize.width) / 2,
2924 bounds.size.height - prgsize.height - 20
2927 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2928 [progress_ setStyle:0];
2930 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2932 bounds.size.height - prgsize.height - 50,
2933 bounds.size.width - 20,
2937 [status_ setColor:[UIColor whiteColor]];
2938 [status_ setBackgroundColor:[UIColor clearColor]];
2940 [status_ setCentersHorizontally:YES];
2941 //[status_ setFont:font];
2943 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2945 navrect.size.height + 20,
2946 bounds.size.width - 20,
2947 bounds.size.height - navsize.height - 62 - navrect.size.height
2950 //[output_ setTextFont:@"Courier New"];
2951 [output_ setTextSize:12];
2953 [output_ setTextColor:[UIColor whiteColor]];
2954 [output_ setBackgroundColor:[UIColor clearColor]];
2956 [output_ setMarginTop:0];
2957 [output_ setAllowsRubberBanding:YES];
2958 [output_ setEditable:NO];
2960 [overlay_ addSubview:output_];
2962 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2964 bounds.size.height - prgsize.height - 50,
2965 bounds.size.width - 20,
2969 [close_ setAutosizesToFit:NO];
2970 [close_ setDrawsShadow:YES];
2971 [close_ setStretchBackground:YES];
2972 [close_ setEnabled:YES];
2974 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2975 [close_ setTitleFont:bold];
2977 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2978 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2979 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2983 - (void) setContentView:(UIView *)view {
2984 view_ = [view retain];
2987 - (void) resetView {
2988 [transition_ transition:6 toView:view_];
2991 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2992 NSString *context = [sheet context];
2993 if ([context isEqualToString:@"conffile"]) {
2994 FILE *input = [database_ input];
2998 fprintf(input, "N\n");
3002 fprintf(input, "Y\n");
3013 - (void) closeButtonPushed {
3022 [delegate_ suspendWithAnimation:YES];
3026 system("launchctl stop com.apple.SpringBoard");
3030 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3039 - (void) _retachThread {
3040 UINavigationItem *item = [navbar_ topItem];
3041 [item setTitle:@"Complete"];
3043 [overlay_ addSubview:close_];
3044 [progress_ removeFromSuperview];
3045 [status_ removeFromSuperview];
3047 [delegate_ progressViewIsComplete:self];
3050 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3051 MMap mmap(file, MMap::ReadOnly);
3053 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3054 if (!(sandplate_ == sha1.Result()))
3059 FileFd file(SpringBoard_, FileFd::ReadOnly);
3060 MMap mmap(file, MMap::ReadOnly);
3062 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3063 if (!(springlist_ == sha1.Result()))
3068 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3069 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3070 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3071 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3072 case 4: [close_ setTitle:@"Reboot Device"]; break;
3075 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3077 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3078 [cache autorelease];
3080 NSFileManager *manager = [NSFileManager defaultManager];
3081 NSError *error = nil;
3083 id system = [cache objectForKey:@"System"];
3088 if (stat(Cache_, &info) == -1)
3091 [system removeAllObjects];
3093 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3094 for (NSString *app in apps)
3095 if ([app hasSuffix:@".app"]) {
3096 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3097 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3098 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3100 [info setObject:path forKey:@"Path"];
3101 [info setObject:@"System" forKey:@"ApplicationType"];
3102 [system addInfoDictionary:info];
3107 [cache writeToFile:@Cache_ atomically:YES];
3109 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3111 if (chmod(Cache_, info.st_mode) == -1)
3115 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3118 notify_post("com.apple.mobile.application_installed");
3120 [delegate_ setStatusBarShowsProgress:NO];
3123 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3124 [[data target] performSelector:[data selector] withObject:[data object]];
3127 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3130 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3131 UINavigationItem *item = [navbar_ topItem];
3132 [item setTitle:title];
3134 [status_ setText:nil];
3135 [output_ setText:@""];
3136 [progress_ setProgress:0];
3139 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3141 [close_ removeFromSuperview];
3142 [overlay_ addSubview:progress_];
3143 [overlay_ addSubview:status_];
3145 [delegate_ setStatusBarShowsProgress:YES];
3149 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3150 MMap mmap(file, MMap::ReadOnly);
3152 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3153 sandplate_ = sha1.Result();
3157 FileFd file(SpringBoard_, FileFd::ReadOnly);
3158 MMap mmap(file, MMap::ReadOnly);
3160 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3161 springlist_ = sha1.Result();
3164 [transition_ transition:6 toView:overlay_];
3167 detachNewThreadSelector:@selector(_detachNewThreadData:)
3169 withObject:[[ProgressData alloc]
3170 initWithSelector:selector
3177 - (void) repairWithSelector:(SEL)selector {
3179 detachNewThreadSelector:selector
3186 - (void) setConfigurationData:(NSString *)data {
3188 performSelectorOnMainThread:@selector(_setConfigurationData:)
3194 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3195 Package *package = id == nil ? nil : [database_ packageWithName:id];
3197 UIActionSheet *sheet = [[[UIActionSheet alloc]
3198 initWithTitle:(package == nil ? id : [package name])
3199 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3200 defaultButtonIndex:0
3205 [sheet setBodyText:error];
3206 [sheet popupAlertAnimated:YES];
3209 - (void) setProgressTitle:(NSString *)title {
3211 performSelectorOnMainThread:@selector(_setProgressTitle:)
3217 - (void) setProgressPercent:(float)percent {
3219 performSelectorOnMainThread:@selector(_setProgressPercent:)
3220 withObject:[NSNumber numberWithFloat:percent]
3225 - (void) startProgress {
3226 last_ = [NSDate timeIntervalSinceReferenceDate];
3229 - (void) addProgressOutput:(NSString *)output {
3231 performSelectorOnMainThread:@selector(_addProgressOutput:)
3237 - (bool) isCancelling:(size_t)received {
3239 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3240 if (received_ != received) {
3241 received_ = received;
3243 } else if (now - last_ > 30)
3250 - (void) _setConfigurationData:(NSString *)data {
3251 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3253 _assert(conffile_r(data));
3255 NSString *ofile = conffile_r[1];
3256 //NSString *nfile = conffile_r[2];
3258 UIActionSheet *sheet = [[[UIActionSheet alloc]
3259 initWithTitle:@"Configuration Upgrade"
3260 buttons:[NSArray arrayWithObjects:
3261 @"Keep My Old Copy",
3262 @"Accept The New Copy",
3263 // XXX: @"See What Changed",
3265 defaultButtonIndex:0
3270 [sheet setBodyText:[NSString stringWithFormat:
3271 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3274 [sheet popupAlertAnimated:YES];
3277 - (void) _setProgressTitle:(NSString *)title {
3278 [status_ setText:title];
3281 - (void) _setProgressPercent:(NSNumber *)percent {
3282 [progress_ setProgress:[percent floatValue]];
3285 - (void) _addProgressOutput:(NSString *)output {
3286 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3287 CGSize size = [output_ contentSize];
3288 CGRect rect = {{0, size.height}, {size.width, 0}};
3289 [output_ scrollRectToVisible:rect animated:YES];
3292 - (BOOL) isRunning {
3299 /* Package Cell {{{ */
3300 @interface PackageCell : UISimpleTableCell {
3303 NSString *description_;
3307 UITextLabel *status_;
3311 - (PackageCell *) init;
3312 - (void) setPackage:(Package *)package;
3314 + (int) heightForPackage:(Package *)package;
3318 @implementation PackageCell
3320 - (void) clearPackage {
3331 if (description_ != nil) {
3332 [description_ release];
3336 if (source_ != nil) {
3341 if (badge_ != nil) {
3348 [self clearPackage];
3355 - (PackageCell *) init {
3356 if ((self = [super init]) != nil) {
3358 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3359 [status_ setBackgroundColor:[UIColor clearColor]];
3360 [status_ setFont:small];
3365 - (void) setPackage:(Package *)package {
3366 [self clearPackage];
3368 Source *source = [package source];
3369 NSString *section = [package section];
3371 section = Simplify(section);
3373 icon_ = [[package icon] retain];
3375 name_ = [[package name] retain];
3376 description_ = [[package tagline] retain];
3378 NSString *label = nil;
3379 bool trusted = false;
3381 if (source != nil) {
3382 label = [source label];
3383 trusted = [source trusted];
3384 } else if ([[package id] isEqualToString:@"firmware"])
3387 label = @"Unknown/Local";
3389 NSString *from = [NSString stringWithFormat:@"from %@", label];
3391 if (section != nil && ![section isEqualToString:label])
3392 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3394 source_ = [from retain];
3396 if (NSString *purpose = [package primaryPurpose])
3397 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3398 badge_ = [badge_ retain];
3401 if (NSString *mode = [package mode]) {
3402 [badge_ setImage:[UIImage applicationImageNamed:
3403 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3406 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3407 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3408 } else if ([package half]) {
3409 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3410 [status_ setText:@"Package Damaged"];
3411 [status_ setColor:[UIColor redColor]];
3413 [badge_ setImage:nil];
3414 [status_ setText:nil];
3419 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3422 rect.size = [icon_ size];
3424 rect.size.width /= 2;
3425 rect.size.height /= 2;
3427 rect.origin.x = 25 - rect.size.width / 2;
3428 rect.origin.y = 25 - rect.size.height / 2;
3430 [icon_ drawInRect:rect];
3433 if (badge_ != nil) {
3434 CGSize size = [badge_ size];
3436 [badge_ drawAtPoint:CGPointMake(
3437 36 - size.width / 2,
3438 36 - size.height / 2
3447 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3448 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3452 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3454 [super drawContentInRect:rect selected:selected];
3457 + (int) heightForPackage:(Package *)package {
3458 NSString *tagline([package tagline]);
3459 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3461 if ([package hasMode] || [package half])
3470 /* Section Cell {{{ */
3471 @interface SectionCell : UISimpleTableCell {
3476 _UISwitchSlider *switch_;
3481 - (void) setSection:(Section *)section editing:(BOOL)editing;
3485 @implementation SectionCell
3487 - (void) clearSection {
3488 if (section_ != nil) {
3498 if (count_ != nil) {
3505 [self clearSection];
3512 if ((self = [super init]) != nil) {
3513 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3515 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3516 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3520 - (void) onSwitch:(id)sender {
3521 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3522 if (metadata == nil) {
3523 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3524 [Sections_ setObject:metadata forKey:section_];
3528 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3531 - (void) setSection:(Section *)section editing:(BOOL)editing {
3532 if (editing != editing_) {
3534 [switch_ removeFromSuperview];
3536 [self addSubview:switch_];
3540 [self clearSection];
3542 if (section == nil) {
3543 name_ = [@"All Packages" retain];
3546 section_ = [section name];
3547 if (section_ != nil)
3548 section_ = [section_ retain];
3549 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3550 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3553 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3557 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3558 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3565 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3567 CGSize size = [count_ sizeWithFont:Font14_];
3571 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3573 [super drawContentInRect:rect selected:selected];
3579 /* File Table {{{ */
3580 @interface FileTable : RVPage {
3581 _transient Database *database_;
3584 NSMutableArray *files_;
3588 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3589 - (void) setPackage:(Package *)package;
3593 @implementation FileTable
3596 if (package_ != nil)
3605 - (int) numberOfRowsInTable:(UITable *)table {
3606 return files_ == nil ? 0 : [files_ count];
3609 - (float) table:(UITable *)table heightForRow:(int)row {
3613 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3614 if (reusing == nil) {
3615 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3616 UIFont *font = [UIFont systemFontOfSize:16];
3617 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3619 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3623 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3627 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3628 if ((self = [super initWithBook:book]) != nil) {
3629 database_ = database;
3631 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3633 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3634 [self addSubview:list_];
3636 UITableColumn *column = [[[UITableColumn alloc]
3637 initWithTitle:@"Name"
3639 width:[self frame].size.width
3642 [list_ setDataSource:self];
3643 [list_ setSeparatorStyle:1];
3644 [list_ addTableColumn:column];
3645 [list_ setDelegate:self];
3646 [list_ setReusesTableCells:YES];
3650 - (void) setPackage:(Package *)package {
3651 if (package_ != nil) {
3652 [package_ autorelease];
3661 [files_ removeAllObjects];
3663 if (package != nil) {
3664 package_ = [package retain];
3665 name_ = [[package id] retain];
3667 if (NSArray *files = [package files])
3668 [files_ addObjectsFromArray:files];
3670 if ([files_ count] != 0) {
3671 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3672 [files_ removeObjectAtIndex:0];
3673 [files_ sortUsingSelector:@selector(compareByPath:)];
3675 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3676 [stack addObject:@"/"];
3678 for (int i(0), e([files_ count]); i != e; ++i) {
3679 NSString *file = [files_ objectAtIndex:i];
3680 while (![file hasPrefix:[stack lastObject]])
3681 [stack removeLastObject];
3682 NSString *directory = [stack lastObject];
3683 [stack addObject:[file stringByAppendingString:@"/"]];
3684 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3685 ([stack count] - 2) * 3, "",
3686 [file substringFromIndex:[directory length]]
3695 - (void) resetViewAnimated:(BOOL)animated {
3696 [list_ resetViewAnimated:animated];
3699 - (void) reloadData {
3700 [self setPackage:[database_ packageWithName:name_]];
3701 [self reloadButtons];
3704 - (NSString *) title {
3705 return @"Installed Files";
3708 - (NSString *) backButtonTitle {
3714 /* Package View {{{ */
3715 @interface PackageView : BrowserView {
3716 _transient Database *database_;
3719 NSMutableArray *buttons_;
3722 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3723 - (void) setPackage:(Package *)package;
3727 @implementation PackageView
3730 if (package_ != nil)
3738 - (void) _clickButtonWithName:(NSString *)name {
3739 if ([name isEqualToString:@"Install"])
3740 [delegate_ installPackage:package_];
3741 else if ([name isEqualToString:@"Reinstall"])
3742 [delegate_ installPackage:package_];
3743 else if ([name isEqualToString:@"Remove"])
3744 [delegate_ removePackage:package_];
3745 else if ([name isEqualToString:@"Upgrade"])
3746 [delegate_ installPackage:package_];
3747 else _assert(false);
3750 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3751 int count = [buttons_ count];
3752 _assert(count != 0);
3753 _assert(button <= count + 1);
3755 if (count != button - 1)
3756 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3761 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3762 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3763 return [super webView:sender didFinishLoadForFrame:frame];
3766 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3767 [window setValue:package_ forKey:@"package"];
3768 [super webView:sender didClearWindowObject:window forFrame:frame];
3771 - (void) _rightButtonClicked {
3772 /*[super _rightButtonClicked];
3775 int count = [buttons_ count];
3776 _assert(count != 0);
3779 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3781 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3782 [buttons addObjectsFromArray:buttons_];
3783 [buttons addObject:@"Cancel"];
3785 [delegate_ slideUp:[[[UIActionSheet alloc]
3788 defaultButtonIndex:2
3795 - (NSString *) _rightButtonTitle {
3796 int count = [buttons_ count];
3797 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3800 - (NSString *) backButtonTitle {
3804 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3805 if ((self = [super initWithBook:book]) != nil) {
3806 database_ = database;
3807 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3811 - (void) setPackage:(Package *)package {
3812 if (package_ != nil) {
3813 [package_ autorelease];
3822 [buttons_ removeAllObjects];
3824 if (package != nil) {
3825 package_ = [package retain];
3826 name_ = [[package id] retain];
3828 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3830 if ([package_ source] == nil);
3831 else if ([package_ upgradableAndEssential:NO])
3832 [buttons_ addObject:@"Upgrade"];
3833 else if ([package_ installed] == nil)
3834 [buttons_ addObject:@"Install"];
3836 [buttons_ addObject:@"Reinstall"];
3837 if ([package_ installed] != nil)
3838 [buttons_ addObject:@"Remove"];
3842 - (void) reloadData {
3843 [self setPackage:[database_ packageWithName:name_]];
3844 [self reloadButtons];
3849 /* Package Table {{{ */
3850 @interface PackageTable : RVPage {
3851 _transient Database *database_;
3855 NSMutableArray *packages_;
3856 NSMutableArray *sections_;
3857 UISectionList *list_;
3860 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3862 - (void) setDelegate:(id)delegate;
3863 - (void) setObject:(id)object;
3865 - (void) reloadData;
3866 - (void) resetCursor;
3868 - (UISectionList *) list;
3870 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3874 @implementation PackageTable
3877 [list_ setDataSource:nil];
3882 [packages_ release];
3883 [sections_ release];
3888 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3889 return [sections_ count];
3892 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3893 return [[sections_ objectAtIndex:section] name];
3896 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3897 return [[sections_ objectAtIndex:section] row];
3900 - (int) numberOfRowsInTable:(UITable *)table {
3901 return [packages_ count];
3904 - (float) table:(UITable *)table heightForRow:(int)row {
3905 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3908 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3910 reusing = [[[PackageCell alloc] init] autorelease];
3911 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3915 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3919 - (void) tableRowSelected:(NSNotification *)notification {
3920 int row = [[notification object] selectedRow];
3924 Package *package = [packages_ objectAtIndex:row];
3925 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3926 [view setDelegate:delegate_];
3927 [view setPackage:package];
3928 [book_ pushPage:view];
3931 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3932 if ((self = [super initWithBook:book]) != nil) {
3933 database_ = database;
3934 title_ = [title retain];
3936 object_ = object == nil ? nil : [object retain];
3938 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3939 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3941 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3942 [list_ setDataSource:self];
3944 UITableColumn *column = [[[UITableColumn alloc]
3945 initWithTitle:@"Name"
3947 width:[self frame].size.width
3950 UITable *table = [list_ table];
3951 [table setSeparatorStyle:1];
3952 [table addTableColumn:column];
3953 [table setDelegate:self];
3954 [table setReusesTableCells:YES];
3956 [self addSubview:list_];
3959 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3960 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3964 - (void) setDelegate:(id)delegate {
3965 delegate_ = delegate;
3968 - (void) setObject:(id)object {
3974 object_ = [object retain];
3977 - (void) reloadData {
3978 NSArray *packages = [database_ packages];
3980 [packages_ removeAllObjects];
3981 [sections_ removeAllObjects];
3983 for (size_t i(0); i != [packages count]; ++i) {
3984 Package *package([packages objectAtIndex:i]);
3985 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3986 [packages_ addObject:package];
3989 Section *section = nil;
3991 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3992 Package *package = [packages_ objectAtIndex:offset];
3993 NSString *name = [package index];
3995 if (section == nil || ![[section name] isEqualToString:name]) {
3996 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3997 [sections_ addObject:section];
4000 [section addToCount];
4006 - (NSString *) title {
4010 - (void) resetViewAnimated:(BOOL)animated {
4011 [list_ resetViewAnimated:animated];
4014 - (void) resetCursor {
4015 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4018 - (UISectionList *) list {
4022 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4023 [list_ setShouldHideHeaderInShortLists:hide];
4029 /* Add Source View {{{ */
4030 @interface AddSourceView : RVPage {
4031 _transient Database *database_;
4034 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4038 @implementation AddSourceView
4040 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4041 if ((self = [super initWithBook:book]) != nil) {
4042 database_ = database;
4048 /* Source Cell {{{ */
4049 @interface SourceCell : UITableCell {
4052 NSString *description_;
4058 - (SourceCell *) initWithSource:(Source *)source;
4062 @implementation SourceCell
4067 [description_ release];
4072 - (SourceCell *) initWithSource:(Source *)source {
4073 if ((self = [super init]) != nil) {
4075 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4077 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4078 icon_ = [icon_ retain];
4080 origin_ = [[source name] retain];
4081 label_ = [[source uri] retain];
4082 description_ = [[source description] retain];
4086 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4088 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4095 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4099 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4103 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4105 [super drawContentInRect:rect selected:selected];
4110 /* Source Table {{{ */
4111 @interface SourceTable : RVPage {
4112 _transient Database *database_;
4113 UISectionList *list_;
4114 NSMutableArray *sources_;
4115 UIActionSheet *alert_;
4119 UIProgressHUD *hud_;
4122 //NSURLConnection *installer_;
4123 NSURLConnection *trivial_bz2_;
4124 NSURLConnection *trivial_gz_;
4125 //NSURLConnection *automatic_;
4130 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4134 @implementation SourceTable
4136 - (void) _deallocConnection:(NSURLConnection *)connection {
4137 if (connection != nil) {
4138 [connection cancel];
4139 //[connection setDelegate:nil];
4140 [connection release];
4145 [[list_ table] setDelegate:nil];
4146 [list_ setDataSource:nil];
4155 //[self _deallocConnection:installer_];
4156 [self _deallocConnection:trivial_gz_];
4157 [self _deallocConnection:trivial_bz2_];
4158 //[self _deallocConnection:automatic_];
4165 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4166 return offset_ == 0 ? 1 : 2;
4169 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4170 switch (section + (offset_ == 0 ? 1 : 0)) {
4171 case 0: return @"Entered by User";
4172 case 1: return @"Installed by Packages";
4180 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4181 switch (section + (offset_ == 0 ? 1 : 0)) {
4183 case 1: return offset_;
4191 - (int) numberOfRowsInTable:(UITable *)table {
4192 return [sources_ count];
4195 - (float) table:(UITable *)table heightForRow:(int)row {
4196 Source *source = [sources_ objectAtIndex:row];
4197 return [source description] == nil ? 56 : 73;
4200 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4201 Source *source = [sources_ objectAtIndex:row];
4202 // XXX: weird warning, stupid selectors ;P
4203 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4206 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4210 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4214 - (void) tableRowSelected:(NSNotification*)notification {
4215 UITable *table([list_ table]);
4216 int row([table selectedRow]);
4220 Source *source = [sources_ objectAtIndex:row];
4222 PackageTable *packages = [[[PackageTable alloc]
4225 title:[source label]
4226 filter:@selector(isVisibleInSource:)
4230 [packages setDelegate:delegate_];
4232 [book_ pushPage:packages];
4235 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4236 Source *source = [sources_ objectAtIndex:row];
4237 return [source record] != nil;
4240 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4241 [[list_ table] setDeleteConfirmationRow:row];
4244 - (void) table:(UITable *)table deleteRow:(int)row {
4245 Source *source = [sources_ objectAtIndex:row];
4246 [Sources_ removeObjectForKey:[source key]];
4247 [delegate_ syncData];
4250 - (void) _endConnection:(NSURLConnection *)connection {
4251 NSURLConnection **field = NULL;
4252 if (connection == trivial_bz2_)
4253 field = &trivial_bz2_;
4254 else if (connection == trivial_gz_)
4255 field = &trivial_gz_;
4256 _assert(field != NULL);
4257 [connection release];
4261 trivial_bz2_ == nil &&
4264 [delegate_ setStatusBarShowsProgress:NO];
4267 [hud_ removeFromSuperview];
4272 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4275 @"./", @"Distribution",
4276 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4278 [delegate_ syncData];
4279 } else if (error_ != nil) {
4280 UIActionSheet *sheet = [[[UIActionSheet alloc]
4281 initWithTitle:@"Verification Error"
4282 buttons:[NSArray arrayWithObjects:@"OK", nil]
4283 defaultButtonIndex:0
4288 [sheet setBodyText:[error_ localizedDescription]];
4289 [sheet popupAlertAnimated:YES];
4291 UIActionSheet *sheet = [[[UIActionSheet alloc]
4292 initWithTitle:@"Did not Find Repository"
4293 buttons:[NSArray arrayWithObjects:@"OK", nil]
4294 defaultButtonIndex:0
4299 [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."];
4300 [sheet popupAlertAnimated:YES];
4306 if (error_ != nil) {
4313 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4314 switch ([response statusCode]) {
4320 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4321 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4323 error_ = [error retain];
4324 [self _endConnection:connection];
4327 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4328 [self _endConnection:connection];
4331 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4332 NSMutableURLRequest *request = [NSMutableURLRequest
4333 requestWithURL:[NSURL URLWithString:href]
4334 cachePolicy:NSURLRequestUseProtocolCachePolicy
4335 timeoutInterval:20.0
4338 [request setHTTPMethod:method];
4340 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4343 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4344 NSString *context = [sheet context];
4345 if ([context isEqualToString:@"source"])
4348 NSString *href = [[sheet textField] text];
4350 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4352 if (![href hasSuffix:@"/"])
4353 href_ = [href stringByAppendingString:@"/"];
4356 href_ = [href_ retain];
4358 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4359 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4360 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4364 hud_ = [delegate_ addProgressHUD];
4365 [hud_ setText:@"Verifying URL"];
4378 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4379 if ((self = [super initWithBook:book]) != nil) {
4380 database_ = database;
4381 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4383 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4384 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4385 [list_ setShouldHideHeaderInShortLists:NO];
4387 [self addSubview:list_];
4388 [list_ setDataSource:self];
4390 UITableColumn *column = [[UITableColumn alloc]
4391 initWithTitle:@"Name"
4393 width:[self frame].size.width
4396 UITable *table = [list_ table];
4397 [table setSeparatorStyle:1];
4398 [table addTableColumn:column];
4399 [table setDelegate:self];
4403 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4404 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4408 - (void) reloadData {
4410 _assert(list.ReadMainList());
4412 [sources_ removeAllObjects];
4413 [sources_ addObjectsFromArray:[database_ sources]];
4415 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4418 int count = [sources_ count];
4419 for (offset_ = 0; offset_ != count; ++offset_) {
4420 Source *source = [sources_ objectAtIndex:offset_];
4421 if ([source record] == nil)
4428 - (void) resetViewAnimated:(BOOL)animated {
4429 [list_ resetViewAnimated:animated];
4432 - (void) _leftButtonClicked {
4433 /*[book_ pushPage:[[[AddSourceView alloc]
4438 UIActionSheet *sheet = [[[UIActionSheet alloc]
4439 initWithTitle:@"Enter Cydia/APT URL"
4440 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4441 defaultButtonIndex:0
4446 [sheet addTextFieldWithValue:@"http://" label:@""];
4448 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4449 [traits setAutocapitalizationType:0];
4450 [traits setKeyboardType:3];
4451 [traits setAutocorrectionType:1];
4453 [sheet popupAlertAnimated:YES];
4456 - (void) _rightButtonClicked {
4457 UITable *table = [list_ table];
4458 BOOL editing = [table isRowDeletionEnabled];
4459 [table enableRowDeletion:!editing animated:YES];
4460 [book_ reloadButtonsForPage:self];
4463 - (NSString *) title {
4467 - (NSString *) leftButtonTitle {
4468 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4471 - (NSString *) rightButtonTitle {
4472 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4475 - (UINavigationButtonStyle) rightButtonStyle {
4476 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4482 /* Installed View {{{ */
4483 @interface InstalledView : RVPage {
4484 _transient Database *database_;
4485 PackageTable *packages_;
4489 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4493 @implementation InstalledView
4496 [packages_ release];
4500 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4501 if ((self = [super initWithBook:book]) != nil) {
4502 database_ = database;
4504 packages_ = [[PackageTable alloc]
4508 filter:@selector(isInstalledAndVisible:)
4509 with:[NSNumber numberWithBool:YES]
4512 [self addSubview:packages_];
4514 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4515 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4519 - (void) resetViewAnimated:(BOOL)animated {
4520 [packages_ resetViewAnimated:animated];
4523 - (void) reloadData {
4524 [packages_ reloadData];
4527 - (void) _rightButtonClicked {
4528 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4529 [packages_ reloadData];
4531 [book_ reloadButtonsForPage:self];
4534 - (NSString *) title {
4535 return @"Installed";
4538 - (NSString *) backButtonTitle {
4542 - (NSString *) rightButtonTitle {
4543 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4546 - (UINavigationButtonStyle) rightButtonStyle {
4547 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4550 - (void) setDelegate:(id)delegate {
4551 [super setDelegate:delegate];
4552 [packages_ setDelegate:delegate];
4559 @interface HomeView : BrowserView {
4564 @implementation HomeView
4566 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4570 - (void) _leftButtonClicked {
4571 UIActionSheet *sheet = [[[UIActionSheet alloc]
4572 initWithTitle:@"About Cydia Installer"
4573 buttons:[NSArray arrayWithObjects:@"Close", nil]
4574 defaultButtonIndex:0
4580 @"Copyright (C) 2008\n"
4581 "Jay Freeman (saurik)\n"
4582 "saurik@saurik.com\n"
4583 "http://www.saurik.com/\n"
4586 "http://www.theokorigroup.com/\n"
4588 "College of Creative Studies,\n"
4589 "University of California,\n"
4591 "http://www.ccs.ucsb.edu/"
4594 [sheet popupAlertAnimated:YES];
4597 - (NSString *) leftButtonTitle {
4603 /* Manage View {{{ */
4604 @interface ManageView : BrowserView {
4609 @implementation ManageView
4611 - (NSString *) title {
4615 - (void) _leftButtonClicked {
4616 [delegate_ askForSettings];
4619 - (NSString *) leftButtonTitle {
4623 - (NSString *) _rightButtonTitle {
4630 @interface WebView (Cydia)
4631 - (void) setScriptDebugDelegate:(id)delegate;
4632 - (void) _setFormDelegate:(id)delegate;
4633 - (void) _setUIKitDelegate:(id)delegate;
4634 - (void) setWebMailDelegate:(id)delegate;
4635 - (void) _setLayoutInterval:(float)interval;
4638 /* Indirect Delegate {{{ */
4639 @interface IndirectDelegate : NSProxy {
4640 _transient volatile id delegate_;
4643 - (void) setDelegate:(id)delegate;
4644 - (id) initWithDelegate:(id)delegate;
4647 @implementation IndirectDelegate
4649 - (void) setDelegate:(id)delegate {
4650 delegate_ = delegate;
4653 - (id) initWithDelegate:(id)delegate {
4654 delegate_ = delegate;
4658 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4659 if (delegate_ != nil)
4660 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4662 // XXX: I fucking hate Apple so very very bad
4663 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4666 - (void) forwardInvocation:(NSInvocation *)inv {
4667 SEL sel = [inv selector];
4668 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4669 [inv invokeWithTarget:delegate_];
4674 /* Browser Implementation {{{ */
4675 @implementation BrowserView
4678 WebView *webview = [webview_ webView];
4679 [webview setFrameLoadDelegate:nil];
4680 [webview setResourceLoadDelegate:nil];
4681 [webview setUIDelegate:nil];
4682 [webview setScriptDebugDelegate:nil];
4683 [webview setPolicyDelegate:nil];
4685 [webview setDownloadDelegate:nil];
4687 [webview _setFormDelegate:nil];
4688 [webview _setUIKitDelegate:nil];
4689 [webview setWebMailDelegate:nil];
4690 [webview setEditingDelegate:nil];
4692 [webview_ setDelegate:nil];
4693 [webview_ setGestureDelegate:nil];
4695 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4700 [webview_ removeFromSuperview];
4701 [Documents_ addObject:[webview_ autorelease]];
4706 [indirect_ setDelegate:nil];
4707 [indirect_ release];
4709 [scroller_ setDelegate:nil];
4711 [scroller_ release];
4713 [indicator_ release];
4719 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4720 [self loadRequest:[NSURLRequest
4723 timeoutInterval:30.0
4727 - (void) loadURL:(NSURL *)url {
4728 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4731 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4732 NSMutableURLRequest *copy = [request mutableCopy];
4734 if (Machine_ != NULL)
4735 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4736 if (UniqueID_ != nil)
4737 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4740 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4745 - (void) loadRequest:(NSURLRequest *)request {
4747 [webview_ loadRequest:request];
4750 - (void) reloadURL {
4751 if ([urls_ count] == 0)
4753 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4754 [urls_ removeLastObject];
4755 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4758 - (WebView *) webView {
4759 return [webview_ webView];
4762 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4763 [scroller_ setContentSize:frame.size];
4766 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4767 [self view:sender didSetFrame:frame];
4770 - (void) pushPage:(RVPage *)page {
4771 [self setBackButtonTitle:title_];
4772 [page setDelegate:delegate_];
4773 [book_ pushPage:page];
4776 - (BOOL) getSpecial:(NSURL *)url {
4777 NSString *href([url absoluteString]);
4778 NSString *scheme([[url scheme] lowercaseString]);
4782 if ([href hasPrefix:@"apptapp://package/"])
4783 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4784 else if ([scheme isEqualToString:@"cydia"]) {
4785 page = [delegate_ pageForURL:url hasTag:NULL];
4788 } else if (![scheme isEqualToString:@"apptapp"])
4792 [self pushPage:page];
4796 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4797 [window setValue:delegate_ forKey:@"cydia"];
4800 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4801 if (NSURL *url = [request URL]) {
4802 if (![self getSpecial:url]) {
4803 NSString *scheme([[url scheme] lowercaseString]);
4804 if ([scheme isEqualToString:@"mailto"])
4805 [delegate_ openMailToURL:url];
4814 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4815 if ([WebView canShowMIMEType:type])
4818 // XXX: handle more mime types!
4820 if (frame == [webView mainFrame])
4821 [UIApp openURL:[request URL]];
4825 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4826 NSURL *url([request URL]);
4828 if (url == nil) use: {
4833 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4836 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4837 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4840 [UIApp openURL:url];
4846 int store(_not(int));
4847 if (NSURL *itms = [url itmsURL:&store]) {
4848 NSLog(@"itms#%@#%u#%@", url, store, itms);
4850 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4851 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4858 NSString *scheme([[url scheme] lowercaseString]);
4860 if ([scheme isEqualToString:@"tel"]) {
4861 // XXX: intelligence
4865 if ([scheme isEqualToString:@"mailto"]) {
4866 [delegate_ openMailToURL:url];
4870 if ([self getSpecial:url])
4872 else if ([WebView _canHandleRequest:request])
4874 else if ([url isSpringboardHandledURL])
4880 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4881 //lprintf("Status:%s\n", [text UTF8String]);
4884 - (void) _pushPage {
4888 [book_ pushPage:self];
4891 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4892 NSURL *url = [request URL];
4893 if ([self getSpecial:url])
4896 return [self _addHeadersToRequest:request];
4899 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4900 [self setBackButtonTitle:title_];
4902 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4903 [browser setDelegate:delegate_];
4906 [browser loadRequest:[self _addHeadersToRequest:request]];
4907 [book_ pushPage:browser];
4910 return [browser webView];
4913 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4914 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4917 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4918 return [self _createWebViewWithRequest:request pushed:YES];
4921 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4922 if ([frame parentFrame] != nil)
4925 title_ = [title retain];
4926 [book_ reloadTitleForPage:self];
4929 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4930 if ([frame parentFrame] != nil)
4935 [indicator_ startAnimation];
4936 [self reloadButtons];
4938 if (title_ != nil) {
4943 [book_ reloadTitleForPage:self];
4945 WebView *webview = [webview_ webView];
4946 NSString *href = [webview mainFrameURL];
4947 [urls_ addObject:[NSURL URLWithString:href]];
4949 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4951 CGRect webrect = [scroller_ bounds];
4952 webrect.size.height = 0;
4953 [webview_ setFrame:webrect];
4956 - (void) _finishLoading {
4959 [indicator_ stopAnimation];
4960 [self reloadButtons];
4964 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4965 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4968 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4969 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4972 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4973 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4976 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4977 return [webview_ webView:sender didCommitLoadForFrame:frame];
4980 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4981 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4984 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4985 if ([frame parentFrame] == nil)
4986 [self _finishLoading];
4987 return [webview_ webView:sender didFinishLoadForFrame:frame];
4990 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4991 if ([frame parentFrame] != nil)
4993 [self _finishLoading];
4995 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4996 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4997 [[error localizedDescription] stringByAddingPercentEscapes]
5001 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5003 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5007 - (id) initWithBook:(RVBook *)book {
5008 if ((self = [super initWithBook:book]) != nil) {
5011 struct CGRect bounds = [self bounds];
5013 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5014 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5015 [self addSubview:pinstripe];
5017 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5018 [self addSubview:scroller_];
5020 [scroller_ setScrollingEnabled:YES];
5021 [scroller_ setAdjustForContentSizeChange:YES];
5022 [scroller_ setClipsSubviews:YES];
5023 [scroller_ setAllowsRubberBanding:YES];
5024 [scroller_ setScrollDecelerationFactor:0.99];
5025 [scroller_ setDelegate:self];
5027 CGRect webrect = [scroller_ bounds];
5028 webrect.size.height = 0;
5033 webview_ = [Documents_ lastObject];
5034 if (webview_ != nil) {
5035 webview_ = [webview_ retain];
5036 webview = [webview_ webView];
5037 [Documents_ removeLastObject];
5038 [webview_ setFrame:webrect];
5043 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5044 webview = [webview_ webView];
5046 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5048 [webview_ setAllowsMessaging:YES];
5050 [webview_ setTilingEnabled:YES];
5051 [webview_ setDrawsGrid:NO];
5052 [webview_ setLogsTilingChanges:NO];
5053 [webview_ setTileMinificationFilter:kCAFilterNearest];
5054 [webview_ setDetectsPhoneNumbers:NO];
5055 [webview_ setAutoresizes:YES];
5057 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5058 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5059 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5061 [webview_ _setDocumentType:0x4];
5063 [webview_ setZoomsFocusedFormControl:YES];
5064 [webview_ setContentsPosition:7];
5065 [webview_ setEnabledGestures:0xa];
5066 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
5067 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
5069 [webview_ setSmoothsFonts:YES];
5071 [webview _setUsesLoaderCache:YES];
5072 [webview setGroupName:@"Cydia"];
5073 //[webview _setLayoutInterval:0.5];
5076 [webview_ setDelegate:self];
5077 [webview_ setGestureDelegate:self];
5078 [scroller_ addSubview:webview_];
5080 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5082 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5083 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5084 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5086 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5087 NSString *application = package == nil ? @"Cydia" : [NSString
5088 stringWithFormat:@"Cydia/%@",
5090 ]; [webview setApplicationNameForUserAgent:application];
5092 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5094 [webview setFrameLoadDelegate:self];
5095 [webview setResourceLoadDelegate:indirect_];
5096 [webview setUIDelegate:self];
5097 [webview setScriptDebugDelegate:self];
5098 [webview setPolicyDelegate:self];
5100 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5102 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5103 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5104 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5108 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5109 [webview_ redrawScaledDocument];
5112 - (void) _rightButtonClicked {
5117 - (NSString *) _rightButtonTitle {
5121 - (NSString *) rightButtonTitle {
5122 return loading_ ? @"" : [self _rightButtonTitle];
5125 - (NSString *) title {
5126 return title_ == nil ? @"Loading" : title_;
5129 - (NSString *) backButtonTitle {
5133 - (void) setPageActive:(BOOL)active {
5135 [indicator_ removeFromSuperview];
5137 [[book_ navigationBar] addSubview:indicator_];
5140 - (void) resetViewAnimated:(BOOL)animated {
5143 - (void) setPushed:(bool)pushed {
5150 /* Cydia Book {{{ */
5151 @interface CYBook : RVBook <
5154 _transient Database *database_;
5155 UINavigationBar *overlay_;
5156 UINavigationBar *underlay_;
5157 UIProgressIndicator *indicator_;
5158 UITextLabel *prompt_;
5159 UIProgressBar *progress_;
5160 UINavigationButton *cancel_;
5163 NSTimeInterval last_;
5166 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5172 @implementation CYBook
5176 [indicator_ release];
5178 [progress_ release];
5183 - (NSString *) getTitleForPage:(RVPage *)page {
5184 return Simplify([super getTitleForPage:page]);
5192 [UIView beginAnimations:nil context:NULL];
5194 CGRect ovrframe = [overlay_ frame];
5195 ovrframe.origin.y = 0;
5196 [overlay_ setFrame:ovrframe];
5198 CGRect barframe = [navbar_ frame];
5199 barframe.origin.y += ovrframe.size.height;
5200 [navbar_ setFrame:barframe];
5202 CGRect trnframe = [transition_ frame];
5203 trnframe.origin.y += ovrframe.size.height;
5204 trnframe.size.height -= ovrframe.size.height;
5205 [transition_ setFrame:trnframe];
5207 [UIView endAnimations];
5209 [indicator_ startAnimation];
5210 [prompt_ setText:@"Updating Database"];
5211 [progress_ setProgress:0];
5214 last_ = [NSDate timeIntervalSinceReferenceDate];
5216 [overlay_ addSubview:cancel_];
5219 detachNewThreadSelector:@selector(_update)
5228 [indicator_ stopAnimation];
5230 [UIView beginAnimations:nil context:NULL];
5232 CGRect ovrframe = [overlay_ frame];
5233 ovrframe.origin.y = -ovrframe.size.height;
5234 [overlay_ setFrame:ovrframe];
5236 CGRect barframe = [navbar_ frame];
5237 barframe.origin.y -= ovrframe.size.height;
5238 [navbar_ setFrame:barframe];
5240 CGRect trnframe = [transition_ frame];
5241 trnframe.origin.y -= ovrframe.size.height;
5242 trnframe.size.height += ovrframe.size.height;
5243 [transition_ setFrame:trnframe];
5245 [UIView commitAnimations];
5247 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5250 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5251 if ((self = [super initWithFrame:frame]) != nil) {
5252 database_ = database;
5254 CGRect ovrrect = [navbar_ bounds];
5255 ovrrect.size.height = [UINavigationBar defaultSize].height;
5256 ovrrect.origin.y = -ovrrect.size.height;
5258 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5259 [self addSubview:overlay_];
5261 ovrrect.origin.y = frame.size.height;
5262 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5263 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5264 [self addSubview:underlay_];
5266 [overlay_ setBarStyle:1];
5267 [underlay_ setBarStyle:1];
5269 int barstyle = [overlay_ _barStyle:NO];
5270 bool ugly = barstyle == 0;
5272 UIProgressIndicatorStyle style = ugly ?
5273 UIProgressIndicatorStyleMediumBrown :
5274 UIProgressIndicatorStyleMediumWhite;
5276 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5277 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5278 CGRect indrect = {{indoffset, indoffset}, indsize};
5280 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5281 [indicator_ setStyle:style];
5282 [overlay_ addSubview:indicator_];
5284 CGSize prmsize = {215, indsize.height + 4};
5287 indoffset * 2 + indsize.width,
5291 unsigned(ovrrect.size.height - prmsize.height) / 2
5294 UIFont *font = [UIFont systemFontOfSize:15];
5296 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5298 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5299 [prompt_ setBackgroundColor:[UIColor clearColor]];
5300 [prompt_ setFont:font];
5302 [overlay_ addSubview:prompt_];
5304 CGSize prgsize = {75, 100};
5307 ovrrect.size.width - prgsize.width - 10,
5308 (ovrrect.size.height - prgsize.height) / 2
5311 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5312 [progress_ setStyle:0];
5313 [overlay_ addSubview:progress_];
5315 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5316 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5318 CGRect frame = [cancel_ frame];
5319 frame.size.width = 65;
5320 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5321 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5322 [cancel_ setFrame:frame];
5324 [cancel_ setBarStyle:barstyle];
5328 - (void) _onCancel {
5330 [cancel_ removeFromSuperview];
5333 - (void) _update { _pooled
5335 status.setDelegate(self);
5337 [database_ updateWithStatus:status];
5340 performSelectorOnMainThread:@selector(_update_)
5346 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5347 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5350 - (void) setProgressTitle:(NSString *)title {
5352 performSelectorOnMainThread:@selector(_setProgressTitle:)
5358 - (void) setProgressPercent:(float)percent {
5360 performSelectorOnMainThread:@selector(_setProgressPercent:)
5361 withObject:[NSNumber numberWithFloat:percent]
5366 - (void) startProgress {
5369 - (void) addProgressOutput:(NSString *)output {
5371 performSelectorOnMainThread:@selector(_addProgressOutput:)
5377 - (bool) isCancelling:(size_t)received {
5378 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5379 if (received_ != received) {
5380 received_ = received;
5382 } else if (now - last_ > 15)
5387 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5391 - (void) _setProgressTitle:(NSString *)title {
5392 [prompt_ setText:title];
5395 - (void) _setProgressPercent:(NSNumber *)percent {
5396 [progress_ setProgress:[percent floatValue]];
5399 - (void) _addProgressOutput:(NSString *)output {
5404 /* Cydia:// Protocol {{{ */
5405 @interface CydiaURLProtocol : NSURLProtocol {
5410 @implementation CydiaURLProtocol
5412 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5413 NSURL *url([request URL]);
5416 NSString *scheme([[url scheme] lowercaseString]);
5417 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5422 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5426 - (void) startLoading {
5427 id<NSURLProtocolClient> client([self client]);
5428 NSURLRequest *request([self request]);
5430 NSURL *url([request URL]);
5431 NSString *href([url absoluteString]);
5433 NSString *path([href substringFromIndex:8]);
5434 NSRange slash([path rangeOfString:@"/"]);
5437 if (slash.location == NSNotFound) {
5441 command = [path substringToIndex:slash.location];
5442 path = [path substringFromIndex:(slash.location + 1)];
5445 Database *database([Database sharedInstance]);
5447 if ([command isEqualToString:@"package-icon"]) {
5450 Package *package([database packageWithName:path]);
5454 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5456 UIImage *icon([package icon]);
5457 NSData *data(UIImagePNGRepresentation(icon));
5459 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5460 [client URLProtocol:self didLoadData:data];
5461 [client URLProtocolDidFinishLoading:self];
5463 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5467 - (void) stopLoading {
5473 /* Install View {{{ */
5474 @interface InstallView : RVPage {
5475 _transient Database *database_;
5476 NSMutableArray *sections_;
5477 NSMutableArray *filtered_;
5478 UITransitionView *transition_;
5484 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5485 - (void) reloadData;
5490 @implementation InstallView
5493 [list_ setDataSource:nil];
5494 [list_ setDelegate:nil];
5496 [sections_ release];
5497 [filtered_ release];
5498 [transition_ release];
5500 [accessory_ release];
5504 - (int) numberOfRowsInTable:(UITable *)table {
5505 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5508 - (float) table:(UITable *)table heightForRow:(int)row {
5512 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5514 reusing = [[[SectionCell alloc] init] autorelease];
5515 [(SectionCell *)reusing setSection:(editing_ ?
5516 [sections_ objectAtIndex:row] :
5517 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5518 ) editing:editing_];
5522 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5526 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5530 - (void) tableRowSelected:(NSNotification *)notification {
5531 int row = [[notification object] selectedRow];
5542 title = @"All Packages";
5544 section = [filtered_ objectAtIndex:(row - 1)];
5545 name = [section name];
5551 title = @"(No Section)";
5555 PackageTable *table = [[[PackageTable alloc]
5559 filter:@selector(isVisiblyUninstalledInSection:)
5563 [table setDelegate:delegate_];
5565 [book_ pushPage:table];
5568 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5569 if ((self = [super initWithBook:book]) != nil) {
5570 database_ = database;
5572 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5573 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5575 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5576 [self addSubview:transition_];
5578 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5579 [transition_ transition:0 toView:list_];
5581 UITableColumn *column = [[[UITableColumn alloc]
5582 initWithTitle:@"Name"
5584 width:[self frame].size.width
5587 [list_ setDataSource:self];
5588 [list_ setSeparatorStyle:1];
5589 [list_ addTableColumn:column];
5590 [list_ setDelegate:self];
5591 [list_ setReusesTableCells:YES];
5595 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5596 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5600 - (void) reloadData {
5601 NSArray *packages = [database_ packages];
5603 [sections_ removeAllObjects];
5604 [filtered_ removeAllObjects];
5606 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5607 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5610 for (size_t i(0); i != [packages count]; ++i) {
5611 Package *package([packages objectAtIndex:i]);
5612 NSString *name([package section]);
5615 Section *section([sections objectForKey:name]);
5616 if (section == nil) {
5617 section = [[[Section alloc] initWithName:name] autorelease];
5618 [sections setObject:section forKey:name];
5622 if ([package valid] && [package installed] == nil && [package visible])
5623 [filtered addObject:package];
5627 [sections_ addObjectsFromArray:[sections allValues]];
5628 [sections_ sortUsingSelector:@selector(compareByName:)];
5631 [filtered sortUsingSelector:@selector(compareBySection:)];
5634 Section *section = nil;
5635 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5636 Package *package = [filtered objectAtIndex:offset];
5637 NSString *name = [package section];
5639 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5640 section = name == nil ?
5641 [[[Section alloc] initWithName:nil] autorelease] :
5642 [sections objectForKey:name];
5643 [filtered_ addObject:section];
5646 [section addToCount];
5654 - (void) resetView {
5656 [self _rightButtonClicked];
5659 - (void) resetViewAnimated:(BOOL)animated {
5660 [list_ resetViewAnimated:animated];
5663 - (void) _rightButtonClicked {
5664 if ((editing_ = !editing_))
5667 [delegate_ updateData];
5668 [book_ reloadTitleForPage:self];
5669 [book_ reloadButtonsForPage:self];
5672 - (NSString *) title {
5673 return editing_ ? @"Section Visibility" : @"Install by Section";
5676 - (NSString *) backButtonTitle {
5680 - (NSString *) rightButtonTitle {
5681 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5684 - (UINavigationButtonStyle) rightButtonStyle {
5685 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5688 - (UIView *) accessoryView {
5694 /* Changes View {{{ */
5695 @interface ChangesView : RVPage {
5696 _transient Database *database_;
5697 NSMutableArray *packages_;
5698 NSMutableArray *sections_;
5699 UISectionList *list_;
5703 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5704 - (void) reloadData;
5708 @implementation ChangesView
5711 [[list_ table] setDelegate:nil];
5712 [list_ setDataSource:nil];
5714 [packages_ release];
5715 [sections_ release];
5720 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5721 return [sections_ count];
5724 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5725 return [[sections_ objectAtIndex:section] name];
5728 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5729 return [[sections_ objectAtIndex:section] row];
5732 - (int) numberOfRowsInTable:(UITable *)table {
5733 return [packages_ count];
5736 - (float) table:(UITable *)table heightForRow:(int)row {
5737 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5740 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5742 reusing = [[[PackageCell alloc] init] autorelease];
5743 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5747 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5751 - (void) tableRowSelected:(NSNotification *)notification {
5752 int row = [[notification object] selectedRow];
5755 Package *package = [packages_ objectAtIndex:row];
5756 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5757 [view setDelegate:delegate_];
5758 [view setPackage:package];
5759 [book_ pushPage:view];
5762 - (void) _leftButtonClicked {
5763 [(CYBook *)book_ update];
5764 [self reloadButtons];
5767 - (void) _rightButtonClicked {
5768 [delegate_ distUpgrade];
5771 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5772 if ((self = [super initWithBook:book]) != nil) {
5773 database_ = database;
5775 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5776 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5778 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5779 [self addSubview:list_];
5781 [list_ setShouldHideHeaderInShortLists:NO];
5782 [list_ setDataSource:self];
5783 //[list_ setSectionListStyle:1];
5785 UITableColumn *column = [[[UITableColumn alloc]
5786 initWithTitle:@"Name"
5788 width:[self frame].size.width
5791 UITable *table = [list_ table];
5792 [table setSeparatorStyle:1];
5793 [table addTableColumn:column];
5794 [table setDelegate:self];
5795 [table setReusesTableCells:YES];
5799 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5800 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5804 - (void) reloadData {
5805 NSArray *packages = [database_ packages];
5807 [packages_ removeAllObjects];
5808 [sections_ removeAllObjects];
5811 for (size_t i(0); i != [packages count]; ++i) {
5812 Package *package([packages objectAtIndex:i]);
5815 [package installed] == nil && [package valid] && [package visible] ||
5816 [package upgradableAndEssential:NO]
5818 [packages_ addObject:package];
5822 [packages_ radixUsingSelector:@selector(compareForChanges) withObject:nil];
5825 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5826 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5827 Section *section = nil;
5831 bool unseens = false;
5833 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5836 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5837 Package *package = [packages_ objectAtIndex:offset];
5839 if (![package upgradableAndEssential:YES]) {
5841 NSDate *seen = [package seen];
5843 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5846 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5847 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5848 [sections_ addObject:section];
5852 [section addToCount];
5853 } else if ([package ignored])
5854 [ignored addToCount];
5857 [upgradable addToCount];
5862 CFRelease(formatter);
5865 Section *last = [sections_ lastObject];
5866 size_t count = [last count];
5867 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5868 [sections_ removeLastObject];
5871 if ([ignored count] != 0)
5872 [sections_ insertObject:ignored atIndex:0];
5874 [sections_ insertObject:upgradable atIndex:0];
5877 [self reloadButtons];
5880 - (void) resetViewAnimated:(BOOL)animated {
5881 [list_ resetViewAnimated:animated];
5884 - (NSString *) leftButtonTitle {
5885 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5888 - (NSString *) rightButtonTitle {
5889 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5892 - (NSString *) title {
5898 /* Search View {{{ */
5899 @protocol SearchViewDelegate
5900 - (void) showKeyboard:(BOOL)show;
5903 @interface SearchView : RVPage {
5905 UISearchField *field_;
5906 UITransitionView *transition_;
5907 PackageTable *table_;
5908 UIPreferencesTable *advanced_;
5914 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5915 - (void) reloadData;
5919 @implementation SearchView
5922 [field_ setDelegate:nil];
5924 [accessory_ release];
5926 [transition_ release];
5928 [advanced_ release];
5933 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5937 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5939 case 0: return @"Advanced Search (Coming Soon!)";
5941 default: _assert(false);
5945 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5949 default: _assert(false);
5953 - (void) _showKeyboard:(BOOL)show {
5954 CGSize keysize = [UIKeyboard defaultSize];
5955 CGRect keydown = [book_ pageBounds];
5956 CGRect keyup = keydown;
5957 keyup.size.height -= keysize.height - ButtonBarHeight_;
5959 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5961 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5962 [animation setSignificantRectFields:8];
5965 [animation setStartFrame:keydown];
5966 [animation setEndFrame:keyup];
5968 [animation setStartFrame:keyup];
5969 [animation setEndFrame:keydown];
5972 UIAnimator *animator = [UIAnimator sharedAnimator];
5975 addAnimations:[NSArray arrayWithObjects:animation, nil]
5976 withDuration:(KeyboardTime_ - delay)
5981 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5983 [delegate_ showKeyboard:show];
5986 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5987 [self _showKeyboard:YES];
5990 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5991 [self _showKeyboard:NO];
5994 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5996 NSString *text([field_ text]);
5997 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6003 - (void) textFieldClearButtonPressed:(UITextField *)field {
6007 - (void) keyboardInputShouldDelete:(id)input {
6011 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6012 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6016 [field_ resignFirstResponder];
6021 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6022 if ((self = [super initWithBook:book]) != nil) {
6023 CGRect pageBounds = [book_ pageBounds];
6025 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6026 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6027 [self addSubview:pinstripe];*/
6029 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6030 [self addSubview:transition_];
6032 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6034 [advanced_ setReusesTableCells:YES];
6035 [advanced_ setDataSource:self];
6036 [advanced_ reloadData];
6038 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6039 CGColor dimmed(space_, 0, 0, 0, 0.5);
6040 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6042 table_ = [[PackageTable alloc]
6046 filter:@selector(isUnfilteredAndSearchedForBy:)
6050 [table_ setShouldHideHeaderInShortLists:NO];
6051 [transition_ transition:0 toView:table_];
6060 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6067 [self bounds].size.width - area.origin.x - 18;
6069 area.size.height = [UISearchField defaultHeight];
6071 field_ = [[UISearchField alloc] initWithFrame:area];
6073 UIFont *font = [UIFont systemFontOfSize:16];
6074 [field_ setFont:font];
6076 [field_ setPlaceholder:@"Package Names & Descriptions"];
6077 [field_ setDelegate:self];
6079 [field_ setPaddingTop:5];
6081 UITextInputTraits *traits = [field_ textInputTraits];
6082 [traits setAutocapitalizationType:0];
6083 [traits setAutocorrectionType:1];
6084 [traits setReturnKeyType:6];
6086 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6088 accessory_ = [[UIView alloc] initWithFrame:accrect];
6089 [accessory_ addSubview:field_];
6091 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6092 [configure setShowPressFeedback:YES];
6093 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6094 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6095 [accessory_ addSubview:configure];*/
6097 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6098 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6104 LKAnimation *animation = [LKTransition animation];
6105 [animation setType:@"oglFlip"];
6106 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6107 [animation setFillMode:@"extended"];
6108 [animation setTransitionFlags:3];
6109 [animation setDuration:10];
6110 [animation setSpeed:0.35];
6111 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6112 [[transition_ _layer] addAnimation:animation forKey:0];
6113 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6114 flipped_ = !flipped_;
6118 - (void) configurePushed {
6119 [field_ resignFirstResponder];
6123 - (void) resetViewAnimated:(BOOL)animated {
6126 [table_ resetViewAnimated:animated];
6129 - (void) _reloadData {
6132 - (void) reloadData {
6135 [table_ setObject:[field_ text]];
6136 [table_ reloadData];
6137 [table_ resetCursor];
6140 - (UIView *) accessoryView {
6144 - (NSString *) title {
6148 - (NSString *) backButtonTitle {
6152 - (void) setDelegate:(id)delegate {
6153 [table_ setDelegate:delegate];
6154 [super setDelegate:delegate];
6160 @interface SettingsView : RVPage {
6161 _transient Database *database_;
6164 UIPreferencesTable *table_;
6165 _UISwitchSlider *subscribedSwitch_;
6166 _UISwitchSlider *ignoredSwitch_;
6167 UIPreferencesControlTableCell *subscribedCell_;
6168 UIPreferencesControlTableCell *ignoredCell_;
6171 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6175 @implementation SettingsView
6178 [table_ setDataSource:nil];
6181 if (package_ != nil)
6184 [subscribedSwitch_ release];
6185 [ignoredSwitch_ release];
6186 [subscribedCell_ release];
6187 [ignoredCell_ release];
6191 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6192 if (package_ == nil)
6198 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6199 if (package_ == nil)
6206 default: _assert(false);
6212 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6213 if (package_ == nil)
6220 default: _assert(false);
6226 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6227 if (package_ == nil)
6234 default: _assert(false);
6240 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6241 if (package_ == nil)
6244 _UISwitchSlider *slider([cell control]);
6245 // XXX: this is just weird
6246 BOOL value([slider value] != 0);
6247 NSMutableDictionary *metadata([package_ metadata]);
6250 if (NSNumber *number = [metadata objectForKey:key])
6251 before = [number boolValue];
6254 NSLog(@"%@:%@ %@:%@ : %u:%u", cell, slider, name_, key, value, before);
6256 if (value != before) {
6257 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6259 //[delegate_ performSelector:@selector(updateData) withObject:nil afterDelay:0];
6260 [delegate_ updateData];
6264 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6265 [self onSomething:cell withKey:@"IsSubscribed"];
6268 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6269 [self onSomething:cell withKey:@"IsIgnored"];
6272 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6273 if (package_ == nil)
6277 case 0: switch (row) {
6279 return subscribedCell_;
6281 return ignoredCell_;
6282 default: _assert(false);
6285 case 1: switch (row) {
6287 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6288 [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."];
6292 default: _assert(false);
6295 default: _assert(false);
6301 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6302 if ((self = [super initWithBook:book])) {
6303 database_ = database;
6304 name_ = [package retain];
6306 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6307 [self addSubview:table_];
6309 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6310 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6312 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6313 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6315 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6316 [subscribedCell_ setTitle:@"Show All Changes"];
6317 [subscribedCell_ setControl:subscribedSwitch_];
6319 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6320 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6321 [ignoredCell_ setControl:ignoredSwitch_];
6323 [table_ setDataSource:self];
6328 - (void) resetViewAnimated:(BOOL)animated {
6329 [table_ resetViewAnimated:animated];
6332 - (void) reloadData {
6333 if (package_ != nil)
6334 [package_ autorelease];
6335 package_ = [database_ packageWithName:name_];
6336 if (package_ != nil) {
6338 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6339 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6342 [table_ reloadData];
6345 - (NSString *) title {
6351 /* Signature View {{{ */
6352 @interface SignatureView : BrowserView {
6353 _transient Database *database_;
6357 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6361 @implementation SignatureView
6368 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6370 [super webView:sender didClearWindowObject:window forFrame:frame];
6373 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6374 if ((self = [super initWithBook:book]) != nil) {
6375 database_ = database;
6376 package_ = [package retain];
6381 - (void) resetViewAnimated:(BOOL)animated {
6384 - (void) reloadData {
6385 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6391 @interface Cydia : UIApplication <
6392 ConfirmationViewDelegate,
6393 ProgressViewDelegate,
6402 UIToolbar *buttonbar_;
6406 NSMutableArray *essential_;
6407 NSMutableArray *broken_;
6409 Database *database_;
6410 ProgressView *progress_;
6414 UIKeyboard *keyboard_;
6415 UIProgressHUD *hud_;
6417 InstallView *install_;
6418 ChangesView *changes_;
6419 ManageView *manage_;
6420 SearchView *search_;
6425 @implementation Cydia
6428 if ([broken_ count] != 0) {
6429 int count = [broken_ count];
6431 UIActionSheet *sheet = [[[UIActionSheet alloc]
6432 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6433 buttons:[NSArray arrayWithObjects:
6435 @"Ignore (Temporary)",
6437 defaultButtonIndex:0
6442 [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."];
6443 [sheet popupAlertAnimated:YES];
6444 } else if (!Ignored_ && [essential_ count] != 0) {
6445 int count = [essential_ count];
6447 UIActionSheet *sheet = [[[UIActionSheet alloc]
6448 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6449 buttons:[NSArray arrayWithObjects:
6450 @"Upgrade Essential",
6451 @"Upgrade Everything",
6452 @"Ignore (Temporary)",
6454 defaultButtonIndex:0
6459 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6460 [sheet popupAlertAnimated:YES];
6464 - (void) _reloadData {
6465 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6466 [hud setText:@"Reloading Data"];
6467 [overlay_ addSubview:hud];
6471 [database_ reloadData];
6476 [essential_ removeAllObjects];
6477 [broken_ removeAllObjects];
6479 NSArray *packages = [database_ packages];
6480 for (Package *package in packages) {
6482 [broken_ addObject:package];
6483 if ([package upgradableAndEssential:NO]) {
6484 if ([package essential])
6485 [essential_ addObject:package];
6491 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6492 [buttonbar_ setBadgeValue:badge forButton:3];
6493 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6494 [buttonbar_ setBadgeAnimated:YES forButton:3];
6495 [self setApplicationBadge:badge];
6497 [buttonbar_ setBadgeValue:nil forButton:3];
6498 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6499 [buttonbar_ setBadgeAnimated:NO forButton:3];
6500 [self removeApplicationBadge];
6506 if ([packages count] == 0);
6518 [hud removeFromSuperview];*/
6521 - (void) _saveConfig {
6524 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6530 - (void) updateData {
6533 /* XXX: this is just stupid */
6534 if (tag_ != 2 && install_ != nil)
6535 [install_ reloadData];
6536 if (tag_ != 3 && changes_ != nil)
6537 [changes_ reloadData];
6538 if (tag_ != 5 && search_ != nil)
6539 [search_ reloadData];
6549 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6550 _assert(file != NULL);
6552 NSArray *keys = [Sources_ allKeys];
6554 for (int i(0), e([keys count]); i != e; ++i) {
6555 NSString *key = [keys objectAtIndex:i];
6556 NSDictionary *source = [Sources_ objectForKey:key];
6558 fprintf(file, "%s %s %s\n",
6559 [[source objectForKey:@"Type"] UTF8String],
6560 [[source objectForKey:@"URI"] UTF8String],
6561 [[source objectForKey:@"Distribution"] UTF8String]
6570 detachNewThreadSelector:@selector(update_)
6573 title:@"Updating Sources"
6577 - (void) reloadData {
6578 @synchronized (self) {
6579 if (confirm_ == nil)
6585 pkgProblemResolver *resolver = [database_ resolver];
6587 resolver->InstallProtect();
6588 if (!resolver->Resolve(true))
6593 [database_ prepare];
6595 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6596 [confirm_ setDelegate:self];
6598 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6599 [page setDelegate:self];
6601 [confirm_ setPage:page];
6602 [underlay_ popSubview:confirm_];
6605 - (void) installPackage:(Package *)package {
6606 @synchronized (self) {
6613 - (void) removePackage:(Package *)package {
6614 @synchronized (self) {
6621 - (void) distUpgrade {
6622 @synchronized (self) {
6623 [database_ upgrade];
6629 @synchronized (self) {
6631 if (confirm_ != nil) {
6639 [overlay_ removeFromSuperview];
6643 detachNewThreadSelector:@selector(perform)
6650 - (void) bootstrap_ {
6652 [database_ upgrade];
6653 [database_ prepare];
6654 [database_ perform];
6657 - (void) bootstrap {
6659 detachNewThreadSelector:@selector(bootstrap_)
6662 title:@"Bootstrap Install"
6666 - (void) progressViewIsComplete:(ProgressView *)progress {
6667 if (confirm_ != nil) {
6668 [underlay_ addSubview:overlay_];
6669 [confirm_ popFromSuperviewAnimated:NO];
6675 - (void) setPage:(RVPage *)page {
6676 [page resetViewAnimated:NO];
6677 [page setDelegate:self];
6678 [book_ setPage:page];
6681 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6682 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6683 [browser loadURL:url];
6687 - (void) _setHomePage {
6688 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6691 - (void) buttonBarItemTapped:(id)sender {
6692 unsigned tag = [sender tag];
6694 [book_ resetViewAnimated:YES];
6696 } else if (tag_ == 2 && tag != 2)
6697 [install_ resetView];
6700 case 1: [self _setHomePage]; break;
6702 case 2: [self setPage:install_]; break;
6703 case 3: [self setPage:changes_]; break;
6704 case 4: [self setPage:manage_]; break;
6705 case 5: [self setPage:search_]; break;
6707 default: _assert(false);
6713 - (void) applicationWillSuspend {
6715 [super applicationWillSuspend];
6718 - (void) askForSettings {
6719 UIActionSheet *role = [[[UIActionSheet alloc]
6720 initWithTitle:@"Who Are You?"
6721 buttons:[NSArray arrayWithObjects:
6722 @"User (Graphical Only)",
6723 @"Hacker (+ Command Line)",
6724 @"Developer (No Filters)",
6726 defaultButtonIndex:-1
6731 [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."];
6732 [role popupAlertAnimated:YES];
6737 [self setStatusBarShowsProgress:NO];
6740 [hud_ removeFromSuperview];
6744 pid_t pid = ExecFork();
6746 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6747 perror("launchctl stop");
6754 [self askForSettings];
6758 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6760 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6761 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6762 0, 0, screenrect.size.width, screenrect.size.height - 48
6763 ) database:database_];
6765 [book_ setDelegate:self];
6767 [overlay_ addSubview:book_];
6769 NSArray *buttonitems = [NSArray arrayWithObjects:
6770 [NSDictionary dictionaryWithObjectsAndKeys:
6771 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6772 @"home-up.png", kUIButtonBarButtonInfo,
6773 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6774 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6775 self, kUIButtonBarButtonTarget,
6776 @"Home", kUIButtonBarButtonTitle,
6777 @"0", kUIButtonBarButtonType,
6780 [NSDictionary dictionaryWithObjectsAndKeys:
6781 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6782 @"install-up.png", kUIButtonBarButtonInfo,
6783 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6784 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6785 self, kUIButtonBarButtonTarget,
6786 @"Sections", kUIButtonBarButtonTitle,
6787 @"0", kUIButtonBarButtonType,
6790 [NSDictionary dictionaryWithObjectsAndKeys:
6791 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6792 @"changes-up.png", kUIButtonBarButtonInfo,
6793 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6794 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6795 self, kUIButtonBarButtonTarget,
6796 @"Changes", kUIButtonBarButtonTitle,
6797 @"0", kUIButtonBarButtonType,
6800 [NSDictionary dictionaryWithObjectsAndKeys:
6801 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6802 @"manage-up.png", kUIButtonBarButtonInfo,
6803 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6804 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6805 self, kUIButtonBarButtonTarget,
6806 @"Manage", kUIButtonBarButtonTitle,
6807 @"0", kUIButtonBarButtonType,
6810 [NSDictionary dictionaryWithObjectsAndKeys:
6811 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6812 @"search-up.png", kUIButtonBarButtonInfo,
6813 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6814 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6815 self, kUIButtonBarButtonTarget,
6816 @"Search", kUIButtonBarButtonTitle,
6817 @"0", kUIButtonBarButtonType,
6821 buttonbar_ = [[UIToolbar alloc]
6823 withFrame:CGRectMake(
6824 0, screenrect.size.height - ButtonBarHeight_,
6825 screenrect.size.width, ButtonBarHeight_
6827 withItemList:buttonitems
6830 [buttonbar_ setDelegate:self];
6831 [buttonbar_ setBarStyle:1];
6832 [buttonbar_ setButtonBarTrackingMode:2];
6834 int buttons[5] = {1, 2, 3, 4, 5};
6835 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6836 [buttonbar_ showButtonGroup:0 withDuration:0];
6838 for (int i = 0; i != 5; ++i)
6839 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6840 i * 64 + 2, 1, 60, ButtonBarHeight_
6843 [buttonbar_ showSelectionForButton:1];
6844 [overlay_ addSubview:buttonbar_];
6846 [UIKeyboard initImplementationNow];
6847 CGSize keysize = [UIKeyboard defaultSize];
6848 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6849 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6850 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6851 [overlay_ addSubview:keyboard_];
6854 [underlay_ addSubview:overlay_];
6858 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6859 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6860 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6862 manage_ = (ManageView *) [[self
6863 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6864 withClass:[ManageView class]
6870 [self _setHomePage];
6873 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6874 NSString *context = [sheet context];
6875 if ([context isEqualToString:@"fixhalf"])
6878 @synchronized (self) {
6879 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6880 Package *broken = [broken_ objectAtIndex:i];
6883 NSString *id = [broken id];
6884 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6885 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6886 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6887 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6896 [broken_ removeAllObjects];
6903 else if ([context isEqualToString:@"role"]) {
6905 case 1: Role_ = @"User"; break;
6906 case 2: Role_ = @"Hacker"; break;
6907 case 3: Role_ = @"Developer"; break;
6914 bool reset = Settings_ != nil;
6916 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6920 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6928 } else if ([context isEqualToString:@"upgrade"])
6931 @synchronized (self) {
6932 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6933 Package *essential = [essential_ objectAtIndex:i];
6934 [essential install];
6957 - (void) reorganize { _pooled
6958 system("/usr/libexec/cydia/free.sh");
6959 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6962 - (void) applicationSuspend:(__GSEvent *)event {
6963 if (hud_ == nil && ![progress_ isRunning])
6964 [super applicationSuspend:event];
6967 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6969 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6972 - (void) _setSuspended:(BOOL)value {
6974 [super _setSuspended:value];
6977 - (UIProgressHUD *) addProgressHUD {
6978 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6980 [underlay_ addSubview:hud];
6984 - (void) openMailToURL:(NSURL *)url {
6985 // XXX: this makes me sad
6987 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6989 [UIApp openURL:url];
6993 - (RVPage *) pageForPackage:(NSString *)name {
6994 if (Package *package = [database_ packageWithName:name]) {
6995 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6996 [view setPackage:package];
6999 UIActionSheet *sheet = [[[UIActionSheet alloc]
7000 initWithTitle:@"Cannot Locate Package"
7001 buttons:[NSArray arrayWithObjects:@"Close", nil]
7002 defaultButtonIndex:0
7007 [sheet setBodyText:[NSString stringWithFormat:
7008 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7011 [sheet popupAlertAnimated:YES];
7016 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7017 NSString *href = [url absoluteString];
7022 if ([href isEqualToString:@"cydia://add-source"])
7023 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7024 else if ([href isEqualToString:@"cydia://sources"])
7025 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7026 else if ([href isEqualToString:@"cydia://packages"])
7027 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7028 else if ([href hasPrefix:@"cydia://url/"])
7029 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7030 else if ([href hasPrefix:@"cydia://launch/"])
7031 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7032 else if ([href hasPrefix:@"cydia://package-settings/"])
7033 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7034 else if ([href hasPrefix:@"cydia://package-signature/"])
7035 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7036 else if ([href hasPrefix:@"cydia://package/"])
7037 return [self pageForPackage:[href substringFromIndex:16]];
7038 else if ([href hasPrefix:@"cydia://files/"]) {
7039 NSString *name = [href substringFromIndex:14];
7041 if (Package *package = [database_ packageWithName:name]) {
7042 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7043 [files setPackage:package];
7051 - (void) applicationOpenURL:(NSURL *)url {
7052 [super applicationOpenURL:url];
7054 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7055 [self setPage:page];
7056 [buttonbar_ showSelectionForButton:tag];
7061 - (void) applicationDidFinishLaunching:(id)unused {
7062 Font12_ = [[UIFont systemFontOfSize:12] retain];
7063 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7064 Font14_ = [[UIFont systemFontOfSize:14] retain];
7065 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7066 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7068 _assert(pkgInitConfig(*_config));
7069 _assert(pkgInitSystem(*_config, _system));
7073 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7074 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7076 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7078 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7079 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7081 [window_ orderFront:self];
7082 [window_ makeKey:self];
7083 [window_ setHidden:NO];
7085 database_ = [Database sharedInstance];
7086 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7087 [database_ setDelegate:progress_];
7088 [window_ setContentView:progress_];
7090 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7091 [progress_ setContentView:underlay_];
7093 [progress_ resetView];
7096 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7097 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7098 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7099 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7100 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7101 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7102 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7104 [self setIdleTimerDisabled:YES];
7106 hud_ = [self addProgressHUD];
7107 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
7109 [self setStatusBarShowsProgress:YES];
7112 detachNewThreadSelector:@selector(reorganize)
7120 /* Web Scripting {{{ */
7121 + (NSString *) webScriptNameForSelector:(SEL)selector {
7122 if (selector == @selector(supports:))
7127 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7128 return selector != @selector(supports:);
7131 - (BOOL) supports:(NSString *)feature {
7132 return [feature isEqualToString:@"window.open"];
7136 - (void) showKeyboard:(BOOL)show {
7137 CGSize keysize = [UIKeyboard defaultSize];
7138 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7139 CGRect keyup = keydown;
7140 keyup.origin.y -= keysize.height;
7142 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7143 [animation setSignificantRectFields:2];
7146 [animation setStartFrame:keydown];
7147 [animation setEndFrame:keyup];
7148 [keyboard_ activate];
7150 [animation setStartFrame:keyup];
7151 [animation setEndFrame:keydown];
7152 [keyboard_ deactivate];
7155 [[UIAnimator sharedAnimator]
7156 addAnimations:[NSArray arrayWithObjects:animation, nil]
7157 withDuration:KeyboardTime_
7162 - (void) slideUp:(UIActionSheet *)alert {
7164 [alert presentSheetFromButtonBar:buttonbar_];
7166 [alert presentSheetInView:overlay_];
7171 void AddPreferences(NSString *plist) { _pooled
7172 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7173 _assert(settings != NULL);
7174 NSMutableArray *items = [settings objectForKey:@"items"];
7178 for (size_t i(0); i != [items count]; ++i) {
7179 NSMutableDictionary *item([items objectAtIndex:i]);
7180 NSString *label = [item objectForKey:@"label"];
7181 if (label != nil && [label isEqualToString:@"Cydia"]) {
7188 for (size_t i(0); i != [items count]; ++i) {
7189 NSDictionary *item([items objectAtIndex:i]);
7190 NSString *label = [item objectForKey:@"label"];
7191 if (label != nil && [label isEqualToString:@"General"]) {
7192 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7193 @"CydiaSettings", @"bundle",
7194 @"PSLinkCell", @"cell",
7195 [NSNumber numberWithBool:YES], @"hasIcon",
7196 [NSNumber numberWithBool:YES], @"isController",
7198 nil] atIndex:(i + 1)];
7204 _assert([settings writeToFile:plist atomically:YES] == YES);
7209 id Alloc_(id self, SEL selector) {
7210 id object = alloc_(self, selector);
7211 lprintf("[%s]A-%p\n", self->isa->name, object);
7216 id Dealloc_(id self, SEL selector) {
7217 id object = dealloc_(self, selector);
7218 lprintf("[%s]D-%p\n", self->isa->name, object);
7222 int main(int argc, char *argv[]) { _pooled
7223 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
7225 App_ = [[NSBundle mainBundle] bundlePath];
7226 Home_ = NSHomeDirectory();
7227 Locale_ = CFLocaleCopyCurrent();
7230 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7231 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7232 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7233 Sounds_Keyboard_ = [keyboard boolValue];
7239 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7240 _assert(errno == ENOENT);
7241 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7242 _assert(errno == ENOENT);
7244 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7245 alloc_ = alloc->method_imp;
7246 alloc->method_imp = (IMP) &Alloc_;*/
7248 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7249 dealloc_ = dealloc->method_imp;
7250 dealloc->method_imp = (IMP) &Dealloc_;*/
7255 size = sizeof(maxproc);
7256 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7257 perror("sysctlbyname(\"kern.maxproc\", ?)");
7258 else if (maxproc < 64) {
7260 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7261 perror("sysctlbyname(\"kern.maxproc\", #)");
7264 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7265 char *machine = new char[size];
7266 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7267 perror("sysctlbyname(\"hw.machine\", ?)");
7271 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7273 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7274 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7276 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7277 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7279 Settings_ = [Metadata_ objectForKey:@"Settings"];
7281 Packages_ = [Metadata_ objectForKey:@"Packages"];
7282 Sections_ = [Metadata_ objectForKey:@"Sections"];
7283 Sources_ = [Metadata_ objectForKey:@"Sources"];
7286 if (Settings_ != nil)
7287 Role_ = [Settings_ objectForKey:@"Role"];
7289 if (Packages_ == nil) {
7290 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7291 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7294 if (Sections_ == nil) {
7295 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7296 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7299 if (Sources_ == nil) {
7300 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7301 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7305 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7308 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7309 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7311 if (access("/User", F_OK) != 0)
7312 system("/usr/libexec/cydia/firmware.sh");
7314 _assert([[NSFileManager defaultManager]
7315 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7316 withIntermediateDirectories:YES
7321 space_ = CGColorSpaceCreateDeviceRGB();
7323 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7324 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7325 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7326 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7327 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7328 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7330 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7332 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7334 UIApplicationUseLegacyEvents(YES);
7335 UIKeyboardDisableAutomaticAppearance();
7337 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7339 CGColorSpaceRelease(space_);