1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #import "UICaboodle.h"
41 #include <objc/objc.h>
42 #include <objc/runtime.h>
44 #include <CoreGraphics/CoreGraphics.h>
45 #include <GraphicsServices/GraphicsServices.h>
46 #include <Foundation/Foundation.h>
48 #import <QuartzCore/CALayer.h>
50 #import <UIKit/UIKit.h>
53 #import <MessageUI/MailComposeController.h>
55 #import <WebCore/WebScriptObject.h>
56 //#include <WebCore/DOMHTML.h>
58 #include <WebKit/WebFrame.h>
59 #include <WebKit/WebPolicyDelegate.h>
60 #include <WebKit/WebView.h>
62 #import <WebKit/WebView-WebPrivate.h>
67 #include <ext/stdio_filebuf.h>
69 #include <apt-pkg/acquire.h>
70 #include <apt-pkg/acquire-item.h>
71 #include <apt-pkg/algorithms.h>
72 #include <apt-pkg/cachefile.h>
73 #include <apt-pkg/clean.h>
74 #include <apt-pkg/configuration.h>
75 #include <apt-pkg/debmetaindex.h>
76 #include <apt-pkg/error.h>
77 #include <apt-pkg/init.h>
78 #include <apt-pkg/mmap.h>
79 #include <apt-pkg/pkgrecords.h>
80 #include <apt-pkg/sha1.h>
81 #include <apt-pkg/sourcelist.h>
82 #include <apt-pkg/sptr.h>
83 #include <apt-pkg/strutl.h>
85 #include <sys/types.h>
87 #include <sys/sysctl.h>
93 #include <mach-o/nlist.h>
103 #import "BrowserView.h"
104 #import "ResetView.h"
107 //#define _finline __attribute__((force_inline))
108 #define _finline inline
113 #define _limit(count) do { \
114 static size_t _count(0); \
115 if (++_count == count) \
119 static uint64_t profile_;
121 #define _timestamp ({ \
123 gettimeofday(&tv, NULL); \
124 tv.tv_sec * 1000000 + tv.tv_usec; \
127 /* Objective-C Handle<> {{{ */
128 template <typename Type_>
130 typedef _H<Type_> This_;
135 _finline void Retain_() {
140 _finline void Clear_() {
146 _finline _H(Type_ *value = NULL, bool mended = false) :
157 _finline This_ &operator =(Type_ *value) {
158 if (value_ != value) {
167 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
169 void NSLogPoint(const char *fix, const CGPoint &point) {
170 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
173 void NSLogRect(const char *fix, const CGRect &rect) {
174 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
177 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
179 /* iPhoneOS 2.0 Compatibility {{{ */
181 @interface UITextView (iPhoneOS)
182 - (void) setTextSize:(float)size;
185 @implementation UITextView (iPhoneOS)
187 - (void) setTextSize:(float)size {
188 [self setFont:[[self font] fontWithSize:size]];
195 extern NSString * const kCAFilterNearest;
197 /* Information Dictionaries {{{ */
198 @interface NSMutableArray (Cydia)
199 - (void) addInfoDictionary:(NSDictionary *)info;
202 @implementation NSMutableArray (Cydia)
204 - (void) addInfoDictionary:(NSDictionary *)info {
205 [self addObject:info];
210 @interface NSMutableDictionary (Cydia)
211 - (void) addInfoDictionary:(NSDictionary *)info;
214 @implementation NSMutableDictionary (Cydia)
216 - (void) addInfoDictionary:(NSDictionary *)info {
217 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
218 [self setObject:info forKey:bundle];
223 /* Pop Transitions {{{ */
224 @interface PopTransitionView : UITransitionView {
229 @implementation PopTransitionView
231 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
232 if (from != nil && to == nil)
233 [self removeFromSuperview];
238 @interface UIView (PopUpView)
239 - (void) popFromSuperviewAnimated:(BOOL)animated;
240 - (void) popSubview:(UIView *)view;
243 @implementation UIView (PopUpView)
245 - (void) popFromSuperviewAnimated:(BOOL)animated {
246 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
249 - (void) popSubview:(UIView *)view {
250 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
251 [transition setDelegate:transition];
252 [self addSubview:transition];
254 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
255 [transition transition:UITransitionNone toView:blank];
256 [transition transition:UITransitionPushFromBottom toView:view];
262 #define lprintf(args...) fprintf(stderr, args)
265 #define ForSaurik 1 && !ForRelease
266 #define RecycleWebViews 0
267 #define AlwaysReload 1 && !ForRelease
270 @interface NSMutableArray (Radix)
271 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
274 @implementation NSMutableArray (Radix)
276 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
277 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
278 [invocation setSelector:selector];
279 [invocation setArgument:&object atIndex:2];
281 size_t count([self count]);
286 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
288 for (size_t i(0); i != count; ++i) {
289 RadixItem &item(lhs[i]);
292 id object([self objectAtIndex:i]);
293 [invocation setTarget:object];
296 [invocation getReturnValue:&item.key];
299 static const size_t width = 32;
300 static const size_t bits = 11;
301 static const size_t slots = 1 << bits;
302 static const size_t passes = (width + (bits - 1)) / bits;
304 size_t *hist(new size_t[slots]);
306 for (size_t pass(0); pass != passes; ++pass) {
307 memset(hist, 0, sizeof(size_t) * slots);
309 for (size_t i(0); i != count; ++i) {
310 uint32_t key(lhs[i].key);
312 key &= _not(uint32_t) >> width - bits;
317 for (size_t i(0); i != slots; ++i) {
318 size_t local(offset);
323 for (size_t i(0); i != count; ++i) {
324 uint32_t key(lhs[i].key);
326 key &= _not(uint32_t) >> width - bits;
327 rhs[hist[key]++] = lhs[i];
337 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
338 for (size_t i(0); i != count; ++i)
339 [values addObject:[self objectAtIndex:lhs[i].index]];
340 [self setArray:values];
348 /* Apple Bug Fixes {{{ */
349 @implementation UIWebDocumentView (Cydia)
351 - (void) _setScrollerOffset:(CGPoint)offset {
352 UIScroller *scroller([self _scroller]);
354 CGSize size([scroller contentSize]);
355 CGSize bounds([scroller bounds].size);
358 max.x = size.width - bounds.width;
359 max.y = size.height - bounds.height;
367 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
368 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
370 [scroller setOffset:offset];
377 kUIControlEventMouseDown = 1 << 0,
378 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
379 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
380 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
381 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
382 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
383 } UIControlEventMasks;
385 @interface NSString (UIKit)
386 - (NSString *) stringByAddingPercentEscapes;
387 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
390 @interface NSString (Cydia)
391 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
392 - (NSComparisonResult) compareByPath:(NSString *)other;
395 @implementation NSString (Cydia)
397 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
398 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
401 - (NSComparisonResult) compareByPath:(NSString *)other {
402 NSString *prefix = [self commonPrefixWithString:other options:0];
403 size_t length = [prefix length];
405 NSRange lrange = NSMakeRange(length, [self length] - length);
406 NSRange rrange = NSMakeRange(length, [other length] - length);
408 lrange = [self rangeOfString:@"/" options:0 range:lrange];
409 rrange = [other rangeOfString:@"/" options:0 range:rrange];
411 NSComparisonResult value;
413 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
414 value = NSOrderedSame;
415 else if (lrange.location == NSNotFound)
416 value = NSOrderedAscending;
417 else if (rrange.location == NSNotFound)
418 value = NSOrderedDescending;
420 value = NSOrderedSame;
422 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
423 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
424 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
425 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
427 NSComparisonResult result = [lpath compare:rpath];
428 return result == NSOrderedSame ? value : result;
433 /* Perl-Compatible RegEx {{{ */
443 Pcre(const char *regex) :
448 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
451 lprintf("%d:%s\n", offset, error);
455 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
456 matches_ = new int[(capture_ + 1) * 3];
464 NSString *operator [](size_t match) {
465 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
468 bool operator ()(NSString *data) {
469 // XXX: length is for characters, not for bytes
470 return operator ()([data UTF8String], [data length]);
473 bool operator ()(const char *data, size_t size) {
475 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
479 /* Mime Addresses {{{ */
480 @interface Address : NSObject {
486 - (NSString *) address;
488 + (Address *) addressWithString:(NSString *)string;
489 - (Address *) initWithString:(NSString *)string;
492 @implementation Address
501 - (NSString *) name {
505 - (NSString *) address {
509 + (Address *) addressWithString:(NSString *)string {
510 return [[[Address alloc] initWithString:string] autorelease];
513 + (NSArray *) _attributeKeys {
514 return [NSArray arrayWithObjects:@"address", @"name", nil];
517 - (NSArray *) attributeKeys {
518 return [[self class] _attributeKeys];
521 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
522 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
525 - (Address *) initWithString:(NSString *)string {
526 if ((self = [super init]) != nil) {
527 const char *data = [string UTF8String];
528 size_t size = [string length];
530 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
532 if (address_r(data, size)) {
533 name_ = [address_r[1] retain];
534 address_ = [address_r[2] retain];
536 name_ = [string retain];
544 /* CoreGraphics Primitives {{{ */
555 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
558 Set(space, red, green, blue, alpha);
563 CGColorRelease(color_);
570 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
572 float color[] = {red, green, blue, alpha};
573 color_ = CGColorCreate(space, color);
576 operator CGColorRef() {
582 extern "C" void UISetColor(CGColorRef color);
584 /* Random Global Variables {{{ */
585 static const int PulseInterval_ = 50000;
586 static const int ButtonBarHeight_ = 48;
587 static const float KeyboardTime_ = 0.3f;
589 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
590 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
592 static CGColor Blue_;
593 static CGColor Blueish_;
594 static CGColor Black_;
596 static CGColor White_;
597 static CGColor Gray_;
599 static NSString *App_;
600 static NSString *Home_;
601 static BOOL Sounds_Keyboard_;
603 static BOOL Advanced_;
607 static BOOL Ignored_;
609 static UIFont *Font12_;
610 static UIFont *Font12Bold_;
611 static UIFont *Font14_;
612 static UIFont *Font18Bold_;
613 static UIFont *Font22Bold_;
615 static const char *Machine_ = NULL;
616 static const NSString *UniqueID_ = NULL;
623 CGColorSpaceRef space_;
625 #define FW_LEAST(major, minor, bugfix) \
626 (major < Major_ || major == Major_ && \
627 (minor < Minor_ || minor == Minor_ && \
633 static NSDictionary *SectionMap_;
634 static NSMutableDictionary *Metadata_;
635 static _transient NSMutableDictionary *Settings_;
636 static _transient NSString *Role_;
637 static _transient NSMutableDictionary *Packages_;
638 static _transient NSMutableDictionary *Sections_;
639 static _transient NSMutableDictionary *Sources_;
640 static bool Changed_;
644 static NSMutableArray *Documents_;
647 NSString *GetLastUpdate() {
648 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
651 return @"Never or Unknown";
653 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
654 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
656 CFRelease(formatter);
658 return [(NSString *) formatted autorelease];
661 /* Display Helpers {{{ */
662 inline float Interpolate(float begin, float end, float fraction) {
663 return (end - begin) * fraction + begin;
666 NSString *SizeString(double size) {
667 bool negative = size < 0;
672 while (size > 1024) {
677 static const char *powers_[] = {"B", "kB", "MB", "GB"};
679 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
682 NSString *StripVersion(NSString *version) {
683 NSRange colon = [version rangeOfString:@":"];
684 if (colon.location != NSNotFound)
685 version = [version substringFromIndex:(colon.location + 1)];
689 NSString *Simplify(NSString *title) {
690 const char *data = [title UTF8String];
691 size_t size = [title length];
693 static Pcre square_r("^\\[(.*)\\]$");
694 if (square_r(data, size))
695 return Simplify(square_r[1]);
697 static Pcre paren_r("^\\((.*)\\)$");
698 if (paren_r(data, size))
699 return Simplify(paren_r[1]);
701 static Pcre title_r("^(.*?) \\(.*\\)$");
702 if (title_r(data, size))
703 return Simplify(title_r[1]);
709 bool isSectionVisible(NSString *section) {
710 NSDictionary *metadata = [Sections_ objectForKey:section];
711 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
712 return hidden == nil || ![hidden boolValue];
715 /* Delegate Prototypes {{{ */
719 @interface NSObject (ProgressDelegate)
722 @implementation NSObject(ProgressDelegate)
724 - (void) _setProgressError:(NSArray *)args {
725 [self performSelector:@selector(setProgressError:forPackage:)
726 withObject:[args objectAtIndex:0]
727 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
733 @protocol ProgressDelegate
734 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
735 - (void) setProgressTitle:(NSString *)title;
736 - (void) setProgressPercent:(float)percent;
737 - (void) startProgress;
738 - (void) addProgressOutput:(NSString *)output;
739 - (bool) isCancelling:(size_t)received;
742 @protocol ConfigurationDelegate
743 - (void) repairWithSelector:(SEL)selector;
744 - (void) setConfigurationData:(NSString *)data;
747 @protocol CydiaDelegate
748 - (void) installPackage:(Package *)package;
749 - (void) removePackage:(Package *)package;
750 - (void) slideUp:(UIActionSheet *)alert;
751 - (void) distUpgrade;
754 - (void) askForSettings;
755 - (UIProgressHUD *) addProgressHUD;
756 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
757 - (RVPage *) pageForPackage:(NSString *)name;
758 - (void) openMailToURL:(NSURL *)url;
762 /* Status Delegation {{{ */
764 public pkgAcquireStatus
767 _transient NSObject<ProgressDelegate> *delegate_;
775 void setDelegate(id delegate) {
776 delegate_ = delegate;
779 virtual bool MediaChange(std::string media, std::string drive) {
783 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
786 virtual void Fetch(pkgAcquire::ItemDesc &item) {
787 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
788 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
791 virtual void Done(pkgAcquire::ItemDesc &item) {
794 virtual void Fail(pkgAcquire::ItemDesc &item) {
796 item.Owner->Status == pkgAcquire::Item::StatIdle ||
797 item.Owner->Status == pkgAcquire::Item::StatDone
801 std::string &error(item.Owner->ErrorText);
805 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
806 NSArray *fields([description componentsSeparatedByString:@" "]);
807 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
809 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
810 withObject:[NSArray arrayWithObjects:
811 [NSString stringWithUTF8String:error.c_str()],
818 virtual bool Pulse(pkgAcquire *Owner) {
819 bool value = pkgAcquireStatus::Pulse(Owner);
822 double(CurrentBytes + CurrentItems) /
823 double(TotalBytes + TotalItems)
826 [delegate_ setProgressPercent:percent];
827 return [delegate_ isCancelling:CurrentBytes] ? false : value;
830 virtual void Start() {
831 [delegate_ startProgress];
834 virtual void Stop() {
838 /* Progress Delegation {{{ */
843 _transient id<ProgressDelegate> delegate_;
846 virtual void Update() {
847 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
848 [delegate_ setProgressPercent:(Percent / 100)];
857 void setDelegate(id delegate) {
858 delegate_ = delegate;
861 virtual void Done() {
862 [delegate_ setProgressPercent:1];
867 /* Database Interface {{{ */
868 @interface Database : NSObject {
870 pkgDepCache::Policy *policy_;
871 pkgRecords *records_;
872 pkgProblemResolver *resolver_;
873 pkgAcquire *fetcher_;
875 SPtr<pkgPackageManager> manager_;
876 pkgSourceList *list_;
878 NSMutableDictionary *sources_;
879 NSMutableArray *packages_;
881 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
890 + (Database *) sharedInstance;
892 - (void) _readCydia:(NSNumber *)fd;
893 - (void) _readStatus:(NSNumber *)fd;
894 - (void) _readOutput:(NSNumber *)fd;
898 - (Package *) packageWithName:(NSString *)name;
900 - (pkgCacheFile &) cache;
901 - (pkgDepCache::Policy *) policy;
902 - (pkgRecords *) records;
903 - (pkgProblemResolver *) resolver;
904 - (pkgAcquire &) fetcher;
905 - (NSArray *) packages;
906 - (NSArray *) sources;
915 - (void) updateWithStatus:(Status &)status;
917 - (void) setDelegate:(id)delegate;
918 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
922 /* Source Class {{{ */
923 @interface Source : NSObject {
924 NSString *description_;
929 NSString *distribution_;
933 NSString *defaultIcon_;
935 NSDictionary *record_;
939 - (Source *) initWithMetaIndex:(metaIndex *)index;
941 - (NSComparisonResult) compareByNameAndType:(Source *)source;
943 - (NSDictionary *) record;
947 - (NSString *) distribution;
953 - (NSString *) description;
954 - (NSString *) label;
955 - (NSString *) origin;
956 - (NSString *) version;
958 - (NSString *) defaultIcon;
962 @implementation Source
966 [distribution_ release];
969 if (description_ != nil)
970 [description_ release];
977 if (defaultIcon_ != nil)
978 [defaultIcon_ release];
985 + (NSArray *) _attributeKeys {
986 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
989 - (NSArray *) attributeKeys {
990 return [[self class] _attributeKeys];
993 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
994 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
997 - (Source *) initWithMetaIndex:(metaIndex *)index {
998 if ((self = [super init]) != nil) {
999 trusted_ = index->IsTrusted();
1001 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1002 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1003 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1005 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1006 if (dindex != NULL) {
1007 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1009 while (std::getline(release, line)) {
1010 std::string::size_type colon(line.find(':'));
1011 if (colon == std::string::npos)
1014 std::string name(line.substr(0, colon));
1015 std::string value(line.substr(colon + 1));
1016 while (!value.empty() && value[0] == ' ')
1017 value = value.substr(1);
1019 if (name == "Default-Icon")
1020 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1021 else if (name == "Description")
1022 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1023 else if (name == "Label")
1024 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1025 else if (name == "Origin")
1026 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1027 else if (name == "Version")
1028 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1032 record_ = [Sources_ objectForKey:[self key]];
1034 record_ = [record_ retain];
1038 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1039 NSDictionary *lhr = [self record];
1040 NSDictionary *rhr = [source record];
1043 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1045 NSString *lhs = [self name];
1046 NSString *rhs = [source name];
1048 if ([lhs length] != 0 && [rhs length] != 0) {
1049 unichar lhc = [lhs characterAtIndex:0];
1050 unichar rhc = [rhs characterAtIndex:0];
1052 if (isalpha(lhc) && !isalpha(rhc))
1053 return NSOrderedAscending;
1054 else if (!isalpha(lhc) && isalpha(rhc))
1055 return NSOrderedDescending;
1058 return [lhs compare:rhs options:CompareOptions_];
1061 - (NSDictionary *) record {
1069 - (NSString *) uri {
1073 - (NSString *) distribution {
1074 return distribution_;
1077 - (NSString *) type {
1081 - (NSString *) key {
1082 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1085 - (NSString *) host {
1086 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1089 - (NSString *) name {
1090 return origin_ == nil ? [self host] : origin_;
1093 - (NSString *) description {
1094 return description_;
1097 - (NSString *) label {
1098 return label_ == nil ? [self host] : label_;
1101 - (NSString *) origin {
1105 - (NSString *) version {
1109 - (NSString *) defaultIcon {
1110 return defaultIcon_;
1115 /* Relationship Class {{{ */
1116 @interface Relationship : NSObject {
1121 - (NSString *) type;
1123 - (NSString *) name;
1127 @implementation Relationship
1135 - (NSString *) type {
1143 - (NSString *) name {
1150 /* Package Class {{{ */
1151 @interface Package : NSObject {
1152 pkgCache::PkgIterator iterator_;
1153 _transient Database *database_;
1154 pkgCache::VerIterator version_;
1155 pkgCache::VerFileIterator file_;
1163 NSString *installed_;
1169 NSString *depiction_;
1170 NSString *homepage_;
1176 NSArray *relationships_;
1179 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1180 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1182 - (pkgCache::PkgIterator) iterator;
1184 - (NSString *) section;
1185 - (NSString *) simpleSection;
1187 - (Address *) maintainer;
1189 - (NSString *) description;
1190 - (NSString *) index;
1192 - (NSMutableDictionary *) metadata;
1194 - (BOOL) subscribed;
1197 - (NSString *) latest;
1198 - (NSString *) installed;
1201 - (BOOL) upgradableAndEssential:(BOOL)essential;
1204 - (BOOL) unfiltered;
1208 - (BOOL) halfConfigured;
1209 - (BOOL) halfInstalled;
1211 - (NSString *) mode;
1214 - (NSString *) name;
1215 - (NSString *) tagline;
1217 - (NSString *) homepage;
1218 - (NSString *) depiction;
1219 - (Address *) author;
1221 - (NSArray *) files;
1222 - (NSArray *) relationships;
1223 - (NSArray *) warnings;
1224 - (NSArray *) applications;
1226 - (Source *) source;
1227 - (NSString *) role;
1229 - (BOOL) matches:(NSString *)text;
1231 - (bool) hasSupportingRole;
1232 - (BOOL) hasTag:(NSString *)tag;
1233 - (NSString *) primaryPurpose;
1234 - (NSArray *) purposes;
1236 - (NSComparisonResult) compareByName:(Package *)package;
1237 - (NSComparisonResult) compareBySection:(Package *)package;
1239 - (uint32_t) compareForChanges;
1244 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1245 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1246 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1247 - (NSNumber *) isVisibleInSource:(Source *)source;
1251 @implementation Package
1257 if (section_ != nil)
1261 if (installed_ != nil)
1262 [installed_ release];
1270 if (depiction_ != nil)
1271 [depiction_ release];
1272 if (homepage_ != nil)
1273 [homepage_ release];
1274 if (sponsor_ != nil)
1283 if (relationships_ != nil)
1284 [relationships_ release];
1289 + (NSArray *) _attributeKeys {
1290 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1293 - (NSArray *) attributeKeys {
1294 return [[self class] _attributeKeys];
1297 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1298 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1301 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1302 if ((self = [super init]) != nil) {
1303 iterator_ = iterator;
1304 database_ = database;
1306 version_ = [database_ policy]->GetCandidateVer(iterator_);
1307 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1308 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1310 pkgCache::VerIterator current = iterator_.CurrentVer();
1311 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1312 installed_ = [StripVersion(installed) retain];
1314 if (!version_.end())
1315 file_ = version_.FileList();
1317 pkgCache &cache([database_ cache]);
1318 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1321 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1324 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1326 const char *begin, *end;
1327 parser->GetRec(begin, end);
1329 NSString *website(nil);
1330 NSString *sponsor(nil);
1331 NSString *author(nil);
1340 {"depiction", &depiction_},
1341 {"homepage", &homepage_},
1342 {"website", &website},
1343 {"sponsor", &sponsor},
1344 {"author", &author},
1348 while (begin != end)
1349 if (*begin == '\n') {
1352 } else if (isblank(*begin)) next: {
1353 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1356 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1357 const char *name(begin);
1358 size_t size(colon - begin);
1360 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1363 const char *stop(begin == NULL ? end : begin);
1364 while (stop[-1] == '\r')
1366 while (++colon != stop && isblank(*colon));
1368 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1369 if (strncasecmp(names[i].name_, name, size) == 0) {
1370 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1371 *names[i].value_ = value;
1382 name_ = [name_ retain];
1383 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1385 icon_ = [icon_ retain];
1386 if (depiction_ != nil)
1387 depiction_ = [depiction_ retain];
1388 if (homepage_ == nil)
1389 homepage_ = website;
1390 if ([homepage_ isEqualToString:depiction_])
1392 if (homepage_ != nil)
1393 homepage_ = [homepage_ retain];
1395 sponsor_ = [[Address addressWithString:sponsor] retain];
1397 author_ = [[Address addressWithString:author] retain];
1399 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1403 for (int i(0), e([tags_ count]); i != e; ++i) {
1404 NSString *tag = [tags_ objectAtIndex:i];
1405 if ([tag hasPrefix:@"role::"]) {
1406 role_ = [[tag substringFromIndex:6] retain];
1411 NSString *solid(latest == nil ? installed : latest);
1412 bool changed(false);
1414 NSString *key([id_ lowercaseString]);
1416 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1417 if (metadata == nil) {
1418 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1423 [metadata setObject:solid forKey:@"LastVersion"];
1426 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1427 NSDate *last([metadata objectForKey:@"LastSeen"]);
1428 NSString *version([metadata objectForKey:@"LastVersion"]);
1431 first = last == nil ? now_ : last;
1432 [metadata setObject:first forKey:@"FirstSeen"];
1437 if (version == nil) {
1438 [metadata setObject:solid forKey:@"LastVersion"];
1440 } else if (![version isEqualToString:solid]) {
1441 [metadata setObject:solid forKey:@"LastVersion"];
1443 [metadata setObject:last forKey:@"LastSeen"];
1449 [Packages_ setObject:metadata forKey:key];
1455 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1456 return [[[Package alloc]
1457 initWithIterator:iterator
1462 - (pkgCache::PkgIterator) iterator {
1466 - (NSString *) section {
1467 if (section_ != nil)
1470 const char *section = iterator_.Section();
1471 if (section == NULL)
1474 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1477 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1478 if (NSString *rename = [value objectForKey:@"Rename"]) {
1483 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1487 - (NSString *) simpleSection {
1488 if (NSString *section = [self section])
1489 return Simplify(section);
1495 - (Address *) maintainer {
1498 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1499 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1503 return version_.end() ? 0 : version_->InstalledSize;
1506 - (NSString *) description {
1509 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1510 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1512 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1513 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1514 if ([lines count] < 2)
1517 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1518 for (size_t i(1); i != [lines count]; ++i) {
1519 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1520 [trimmed addObject:trim];
1523 return [trimmed componentsJoinedByString:@"\n"];
1526 - (NSString *) index {
1527 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1528 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1531 - (NSMutableDictionary *) metadata {
1532 return [Packages_ objectForKey:[id_ lowercaseString]];
1536 NSDictionary *metadata([self metadata]);
1537 if ([self subscribed])
1538 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1540 return [metadata objectForKey:@"FirstSeen"];
1543 - (BOOL) subscribed {
1544 NSDictionary *metadata([self metadata]);
1545 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1546 return [subscribed boolValue];
1552 NSDictionary *metadata([self metadata]);
1553 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1554 return [ignored boolValue];
1559 - (NSString *) latest {
1563 - (NSString *) installed {
1568 return !version_.end();
1571 - (BOOL) upgradableAndEssential:(BOOL)essential {
1572 pkgCache::VerIterator current = iterator_.CurrentVer();
1575 return essential && [self essential];
1577 return !version_.end() && version_ != current;
1580 - (BOOL) essential {
1581 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1585 return [database_ cache][iterator_].InstBroken();
1588 - (BOOL) unfiltered {
1589 NSString *section = [self section];
1590 return section == nil || isSectionVisible(section);
1594 return [self hasSupportingRole] && [self unfiltered];
1598 unsigned char current = iterator_->CurrentState;
1599 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1602 - (BOOL) halfConfigured {
1603 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1606 - (BOOL) halfInstalled {
1607 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1611 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1612 return state.Mode != pkgDepCache::ModeKeep;
1615 - (NSString *) mode {
1616 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1618 switch (state.Mode) {
1619 case pkgDepCache::ModeDelete:
1620 if ((state.iFlags & pkgDepCache::Purge) != 0)
1624 case pkgDepCache::ModeKeep:
1625 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1629 case pkgDepCache::ModeInstall:
1630 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1631 return @"Reinstall";
1632 else switch (state.Status) {
1634 return @"Downgrade";
1640 return @"New Install";
1653 - (NSString *) name {
1654 return name_ == nil ? id_ : name_;
1657 - (NSString *) tagline {
1661 - (UIImage *) icon {
1662 NSString *section = [self simpleSection];
1665 if (NSString *icon = icon_)
1666 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1667 if (icon == nil) if (section != nil)
1668 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1669 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1670 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1672 icon = [UIImage applicationImageNamed:@"unknown.png"];
1676 - (NSString *) homepage {
1680 - (NSString *) depiction {
1684 - (Address *) sponsor {
1688 - (Address *) author {
1692 - (NSArray *) files {
1693 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1694 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1697 fin.open([path UTF8String]);
1702 while (std::getline(fin, line))
1703 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1708 - (NSArray *) relationships {
1709 return relationships_;
1712 - (NSArray *) warnings {
1713 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1714 const char *name(iterator_.Name());
1716 size_t length(strlen(name));
1717 if (length < 2) invalid:
1718 [warnings addObject:@"illegal package identifier"];
1719 else for (size_t i(0); i != length; ++i)
1721 (name[i] < 'a' || name[i] > 'z') &&
1722 (name[i] < '0' || name[i] > '9') &&
1723 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1726 if (strcmp(name, "cydia") != 0) {
1730 if (NSArray *files = [self files])
1731 for (NSString *file in files)
1732 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1734 else if (!stash && [file isEqualToString:@"/var/stash"])
1738 [warnings addObject:@"files installed into Cydia.app"];
1740 [warnings addObject:@"files installed to /var/stash"];
1743 return [warnings count] == 0 ? nil : warnings;
1746 - (NSArray *) applications {
1747 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1749 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1751 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1752 if (NSArray *files = [self files])
1753 for (NSString *file in files)
1754 if (application_r(file)) {
1755 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1756 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1757 if ([id isEqualToString:me])
1760 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1762 display = application_r[1];
1764 NSString *bundle([file stringByDeletingLastPathComponent]);
1765 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1766 if (icon == nil || [icon length] == 0)
1768 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1770 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1771 [applications addObject:application];
1773 [application addObject:id];
1774 [application addObject:display];
1775 [application addObject:url];
1778 return [applications count] == 0 ? nil : applications;
1781 - (Source *) source {
1783 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1790 - (NSString *) role {
1794 - (BOOL) matches:(NSString *)text {
1800 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1801 if (range.location != NSNotFound)
1804 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1805 if (range.location != NSNotFound)
1808 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1809 if (range.location != NSNotFound)
1815 - (bool) hasSupportingRole {
1818 if ([role_ isEqualToString:@"enduser"])
1820 if ([Role_ isEqualToString:@"User"])
1822 if ([role_ isEqualToString:@"hacker"])
1824 if ([Role_ isEqualToString:@"Hacker"])
1826 if ([role_ isEqualToString:@"developer"])
1828 if ([Role_ isEqualToString:@"Developer"])
1833 - (BOOL) hasTag:(NSString *)tag {
1834 return tags_ == nil ? NO : [tags_ containsObject:tag];
1837 - (NSString *) primaryPurpose {
1838 for (NSString *tag in tags_)
1839 if ([tag hasPrefix:@"purpose::"])
1840 return [tag substringFromIndex:9];
1844 - (NSArray *) purposes {
1845 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1846 for (NSString *tag in tags_)
1847 if ([tag hasPrefix:@"purpose::"])
1848 [purposes addObject:[tag substringFromIndex:9]];
1849 return [purposes count] == 0 ? nil : purposes;
1852 - (NSComparisonResult) compareByName:(Package *)package {
1853 NSString *lhs = [self name];
1854 NSString *rhs = [package name];
1856 if ([lhs length] != 0 && [rhs length] != 0) {
1857 unichar lhc = [lhs characterAtIndex:0];
1858 unichar rhc = [rhs characterAtIndex:0];
1860 if (isalpha(lhc) && !isalpha(rhc))
1861 return NSOrderedAscending;
1862 else if (!isalpha(lhc) && isalpha(rhc))
1863 return NSOrderedDescending;
1866 return [lhs compare:rhs options:CompareOptions_];
1869 - (NSComparisonResult) compareBySection:(Package *)package {
1870 NSString *lhs = [self section];
1871 NSString *rhs = [package section];
1873 if (lhs == NULL && rhs != NULL)
1874 return NSOrderedAscending;
1875 else if (lhs != NULL && rhs == NULL)
1876 return NSOrderedDescending;
1877 else if (lhs != NULL && rhs != NULL) {
1878 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1879 if (result != NSOrderedSame)
1883 return NSOrderedSame;
1886 - (uint32_t) compareForChanges {
1891 uint32_t timestamp : 30;
1892 uint32_t ignored : 1;
1893 uint32_t upgradable : 1;
1897 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1899 if ([self upgradableAndEssential:YES]) {
1900 value.bits.timestamp = 0;
1901 value.bits.ignored = [self ignored] ? 0 : 1;
1902 value.bits.upgradable = 1;
1904 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1905 value.bits.ignored = 0;
1906 value.bits.upgradable = 0;
1909 return _not(uint32_t) - value.key;
1913 pkgProblemResolver *resolver = [database_ resolver];
1914 resolver->Clear(iterator_);
1915 resolver->Protect(iterator_);
1916 pkgCacheFile &cache([database_ cache]);
1917 cache->MarkInstall(iterator_, false);
1918 pkgDepCache::StateCache &state((*cache)[iterator_]);
1919 if (!state.Install())
1920 cache->SetReInstall(iterator_, true);
1924 pkgProblemResolver *resolver = [database_ resolver];
1925 resolver->Clear(iterator_);
1926 resolver->Protect(iterator_);
1927 resolver->Remove(iterator_);
1928 [database_ cache]->MarkDelete(iterator_, true);
1931 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1932 return [NSNumber numberWithBool:(
1933 [self unfiltered] && [self matches:search]
1937 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1938 return [NSNumber numberWithBool:(
1939 (![number boolValue] || [self visible]) && [self installed] != nil
1943 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1944 NSString *section = [self section];
1946 return [NSNumber numberWithBool:(
1948 [self installed] == nil && (
1950 section == nil && [name length] == 0 ||
1951 [name isEqualToString:section]
1956 - (NSNumber *) isVisibleInSource:(Source *)source {
1957 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1962 /* Section Class {{{ */
1963 @interface Section : NSObject {
1969 - (NSComparisonResult) compareByName:(Section *)section;
1970 - (Section *) initWithName:(NSString *)name;
1971 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1972 - (NSString *) name;
1975 - (void) addToCount;
1979 @implementation Section
1986 - (NSComparisonResult) compareByName:(Section *)section {
1987 NSString *lhs = [self name];
1988 NSString *rhs = [section name];
1990 if ([lhs length] != 0 && [rhs length] != 0) {
1991 unichar lhc = [lhs characterAtIndex:0];
1992 unichar rhc = [rhs characterAtIndex:0];
1994 if (isalpha(lhc) && !isalpha(rhc))
1995 return NSOrderedAscending;
1996 else if (!isalpha(lhc) && isalpha(rhc))
1997 return NSOrderedDescending;
2000 return [lhs compare:rhs options:CompareOptions_];
2003 - (Section *) initWithName:(NSString *)name {
2004 return [self initWithName:name row:0];
2007 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2008 if ((self = [super init]) != nil) {
2009 name_ = [name retain];
2014 - (NSString *) name {
2026 - (void) addToCount {
2034 static NSArray *Finishes_;
2036 /* Database Implementation {{{ */
2037 @implementation Database
2039 + (Database *) sharedInstance {
2040 static Database *instance;
2041 if (instance == nil)
2042 instance = [[Database alloc] init];
2051 - (void) _readCydia:(NSNumber *)fd { _pooled
2052 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2053 std::istream is(&ib);
2056 static Pcre finish_r("^finish:([^:]*)$");
2058 while (std::getline(is, line)) {
2059 const char *data(line.c_str());
2060 size_t size = line.size();
2061 lprintf("C:%s\n", data);
2063 if (finish_r(data, size)) {
2064 NSString *finish = finish_r[1];
2065 int index = [Finishes_ indexOfObject:finish];
2066 if (index != INT_MAX && index > Finish_)
2074 - (void) _readStatus:(NSNumber *)fd { _pooled
2075 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2076 std::istream is(&ib);
2079 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2080 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2082 while (std::getline(is, line)) {
2083 const char *data(line.c_str());
2084 size_t size = line.size();
2085 lprintf("S:%s\n", data);
2087 if (conffile_r(data, size)) {
2088 [delegate_ setConfigurationData:conffile_r[1]];
2089 } else if (strncmp(data, "status: ", 8) == 0) {
2090 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2091 [delegate_ setProgressTitle:string];
2092 } else if (pmstatus_r(data, size)) {
2093 std::string type([pmstatus_r[1] UTF8String]);
2094 NSString *id = pmstatus_r[2];
2096 float percent([pmstatus_r[3] floatValue]);
2097 [delegate_ setProgressPercent:(percent / 100)];
2099 NSString *string = pmstatus_r[4];
2101 if (type == "pmerror")
2102 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2103 withObject:[NSArray arrayWithObjects:string, id, nil]
2106 else if (type == "pmstatus") {
2107 [delegate_ setProgressTitle:string];
2108 } else if (type == "pmconffile")
2109 [delegate_ setConfigurationData:string];
2110 else _assert(false);
2111 } else _assert(false);
2117 - (void) _readOutput:(NSNumber *)fd { _pooled
2118 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2119 std::istream is(&ib);
2122 while (std::getline(is, line)) {
2123 lprintf("O:%s\n", line.c_str());
2124 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2134 - (Package *) packageWithName:(NSString *)name {
2135 if (static_cast<pkgDepCache *>(cache_) == NULL)
2137 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2138 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2141 - (Database *) init {
2142 if ((self = [super init]) != nil) {
2149 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2150 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2154 _assert(pipe(fds) != -1);
2157 _config->Set("APT::Keep-Fds::", cydiafd_);
2158 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2161 detachNewThreadSelector:@selector(_readCydia:)
2163 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2166 _assert(pipe(fds) != -1);
2170 detachNewThreadSelector:@selector(_readStatus:)
2172 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2175 _assert(pipe(fds) != -1);
2176 _assert(dup2(fds[0], 0) != -1);
2177 _assert(close(fds[0]) != -1);
2179 input_ = fdopen(fds[1], "a");
2181 _assert(pipe(fds) != -1);
2182 _assert(dup2(fds[1], 1) != -1);
2183 _assert(close(fds[1]) != -1);
2186 detachNewThreadSelector:@selector(_readOutput:)
2188 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2193 - (pkgCacheFile &) cache {
2197 - (pkgDepCache::Policy *) policy {
2201 - (pkgRecords *) records {
2205 - (pkgProblemResolver *) resolver {
2209 - (pkgAcquire &) fetcher {
2213 - (NSArray *) packages {
2217 - (NSArray *) sources {
2218 return [sources_ allValues];
2221 - (NSArray *) issues {
2222 if (cache_->BrokenCount() == 0)
2225 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2227 for (Package *package in packages_) {
2228 if (![package broken])
2230 pkgCache::PkgIterator pkg([package iterator]);
2232 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2233 [entry addObject:[package name]];
2234 [issues addObject:entry];
2236 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2240 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2241 pkgCache::DepIterator start;
2242 pkgCache::DepIterator end;
2243 dep.GlobOr(start, end); // ++dep
2245 if (!cache_->IsImportantDep(end))
2247 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2250 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2251 [entry addObject:failure];
2252 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2254 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2255 [failure addObject:[package name]];
2257 pkgCache::PkgIterator target(start.TargetPkg());
2258 if (target->ProvidesList != 0)
2259 [failure addObject:@"?"];
2261 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2263 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2264 else if (!cache_[target].CandidateVerIter(cache_).end())
2265 [failure addObject:@"-"];
2266 else if (target->ProvidesList == 0)
2267 [failure addObject:@"!"];
2269 [failure addObject:@"%"];
2273 if (start.TargetVer() != 0)
2274 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2285 - (void) reloadData {
2305 if (!cache_.Open(progress_, true)) {
2307 if (!_error->PopMessage(error))
2310 lprintf("cache_.Open():[%s]\n", error.c_str());
2312 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2313 [delegate_ repairWithSelector:@selector(configure)];
2314 else if (error == "The package lists or status file could not be parsed or opened.")
2315 [delegate_ repairWithSelector:@selector(update)];
2316 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2317 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2318 // else if (error == "The list of sources could not be read.")
2319 else _assert(false);
2325 now_ = [[NSDate date] retain];
2327 policy_ = new pkgDepCache::Policy();
2328 records_ = new pkgRecords(cache_);
2329 resolver_ = new pkgProblemResolver(cache_);
2330 fetcher_ = new pkgAcquire(&status_);
2333 list_ = new pkgSourceList();
2334 _assert(list_->ReadMainList());
2336 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2337 _assert(pkgApplyStatus(cache_));
2339 if (cache_->BrokenCount() != 0) {
2340 _assert(pkgFixBroken(cache_));
2341 _assert(cache_->BrokenCount() == 0);
2342 _assert(pkgMinimizeUpgrade(cache_));
2345 [sources_ removeAllObjects];
2346 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2347 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2348 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2350 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2351 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2355 [packages_ removeAllObjects];
2358 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2359 if (Package *package = [Package packageWithIterator:iterator database:self])
2360 [packages_ addObject:package];
2362 [packages_ sortUsingSelector:@selector(compareByName:)];
2366 - (void) configure {
2367 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2368 system([dpkg UTF8String]);
2376 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2377 _assert(!_error->PendingError());
2380 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2383 public pkgArchiveCleaner
2386 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2391 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2393 while (_error->PopMessage(error))
2394 lprintf("ArchiveCleaner: %s\n", error.c_str());
2399 pkgRecords records(cache_);
2401 lock_ = new FileFd();
2402 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2403 _assert(!_error->PendingError());
2406 // XXX: explain this with an error message
2407 _assert(list.ReadMainList());
2409 manager_ = (_system->CreatePM(cache_));
2410 _assert(manager_->GetArchives(fetcher_, &list, &records));
2411 _assert(!_error->PendingError());
2415 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2417 _assert(list.ReadMainList());
2418 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2419 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2422 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2427 bool failed = false;
2428 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2429 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2432 std::string uri = (*item)->DescURI();
2433 std::string error = (*item)->ErrorText;
2435 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2438 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2439 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2450 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2452 if (_error->PendingError()) {
2457 if (result == pkgPackageManager::Failed) {
2462 if (result != pkgPackageManager::Completed) {
2467 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2469 _assert(list.ReadMainList());
2470 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2471 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2474 if (![before isEqualToArray:after])
2479 _assert(pkgDistUpgrade(cache_));
2483 [self updateWithStatus:status_];
2486 - (void) updateWithStatus:(Status &)status {
2488 _assert(list.ReadMainList());
2491 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2492 _assert(!_error->PendingError());
2494 pkgAcquire fetcher(&status);
2495 _assert(list.GetIndexes(&fetcher));
2497 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2498 bool failed = false;
2499 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2500 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2501 (*item)->Finished();
2505 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2506 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2507 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2510 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2515 - (void) setDelegate:(id)delegate {
2516 delegate_ = delegate;
2517 status_.setDelegate(delegate);
2518 progress_.setDelegate(delegate);
2521 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2522 pkgIndexFile *index(NULL);
2523 list_->FindIndex(file, index);
2524 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2530 /* PopUp Windows {{{ */
2531 @interface PopUpView : UIView {
2532 _transient id delegate_;
2533 UITransitionView *transition_;
2538 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2542 @implementation PopUpView
2545 [transition_ setDelegate:nil];
2546 [transition_ release];
2552 [transition_ transition:UITransitionPushFromTop toView:nil];
2555 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2556 if (from != nil && to == nil)
2557 [self removeFromSuperview];
2560 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2561 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2562 delegate_ = delegate;
2564 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2565 [self addSubview:transition_];
2567 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2569 [view addSubview:self];
2571 [transition_ setDelegate:self];
2573 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2574 [transition_ transition:UITransitionNone toView:blank];
2575 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2582 /* Mail Composition {{{ */
2583 @interface MailToView : PopUpView {
2584 MailComposeController *controller_;
2587 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2591 @implementation MailToView
2594 [controller_ release];
2598 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2602 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2603 NSLog(@"did:%@", delivery);
2604 // [UIApp setStatusBarShowsProgress:NO];
2605 if ([controller error]){
2606 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2607 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2608 [mailAlertSheet setBodyText:[controller error]];
2609 [mailAlertSheet popupAlertAnimated:YES];
2613 - (void) showError {
2614 NSLog(@"%@", [controller_ error]);
2615 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2616 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2617 [mailAlertSheet setBodyText:[controller_ error]];
2618 [mailAlertSheet popupAlertAnimated:YES];
2621 - (void) deliverMessage { _pooled
2625 if (![controller_ deliverMessage])
2626 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2629 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2630 if ([controller_ needsDelivery])
2631 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2636 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2637 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2638 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2639 [controller_ setDelegate:self];
2640 [controller_ initializeUI];
2641 [controller_ setupForURL:url];
2643 UIView *view([controller_ view]);
2644 [overlay_ addSubview:view];
2650 /* Confirmation View {{{ */
2651 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2652 if (!iterator.end())
2653 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2654 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2656 pkgCache::PkgIterator package(dep.TargetPkg());
2659 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2666 @protocol ConfirmationViewDelegate
2671 @interface ConfirmationView : BrowserView {
2672 _transient Database *database_;
2673 UIActionSheet *essential_;
2680 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2684 @implementation ConfirmationView
2691 if (essential_ != nil)
2692 [essential_ release];
2698 [book_ popFromSuperviewAnimated:YES];
2701 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2702 NSString *context([sheet context]);
2704 if ([context isEqualToString:@"remove"]) {
2712 [delegate_ confirm];
2719 } else if ([context isEqualToString:@"unable"]) {
2723 [super alertSheet:sheet buttonClicked:button];
2726 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2727 [window setValue:changes_ forKey:@"changes"];
2728 [window setValue:issues_ forKey:@"issues"];
2729 [window setValue:sizes_ forKey:@"sizes"];
2730 [super webView:sender didClearWindowObject:window forFrame:frame];
2733 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2734 if ((self = [super initWithBook:book]) != nil) {
2735 database_ = database;
2737 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2738 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2739 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2740 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2741 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2745 pkgDepCache::Policy *policy([database_ policy]);
2747 pkgCacheFile &cache([database_ cache]);
2748 NSArray *packages = [database_ packages];
2749 for (size_t i(0), e = [packages count]; i != e; ++i) {
2750 Package *package = [packages objectAtIndex:i];
2751 pkgCache::PkgIterator iterator = [package iterator];
2752 pkgDepCache::StateCache &state(cache[iterator]);
2754 NSString *name([package name]);
2756 if (state.NewInstall())
2757 [installing addObject:name];
2758 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2759 [reinstalling addObject:name];
2760 else if (state.Upgrade())
2761 [upgrading addObject:name];
2762 else if (state.Downgrade())
2763 [downgrading addObject:name];
2764 else if (state.Delete()) {
2765 if ([package essential])
2767 [removing addObject:name];
2770 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2771 substrate_ |= DepSubstrate(iterator.CurrentVer());
2776 else if (Advanced_ || true) {
2777 essential_ = [[UIActionSheet alloc]
2778 initWithTitle:@"Removing Essentials"
2779 buttons:[NSArray arrayWithObjects:
2780 @"Cancel Operation (Safe)",
2781 @"Force Removal (Unsafe)",
2783 defaultButtonIndex:0
2789 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2791 [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."];
2793 essential_ = [[UIActionSheet alloc]
2794 initWithTitle:@"Unable to Comply"
2795 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2796 defaultButtonIndex:0
2801 [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."];
2804 changes_ = [[NSArray alloc] initWithObjects:
2812 issues_ = [database_ issues];
2814 issues_ = [issues_ retain];
2816 sizes_ = [[NSArray alloc] initWithObjects:
2817 SizeString([database_ fetcher].FetchNeeded()),
2818 SizeString([database_ fetcher].PartialPresent()),
2819 SizeString([database_ cache]->UsrSize()),
2822 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2826 - (NSString *) backButtonTitle {
2830 - (NSString *) leftButtonTitle {
2834 - (NSString *) _rightButtonTitle {
2835 return issues_ == nil ? @"Confirm" : nil;
2838 - (void) _leftButtonClicked {
2843 - (void) _rightButtonClicked {
2844 if (essential_ != nil)
2845 [essential_ popupAlertAnimated:YES];
2849 [delegate_ confirm];
2857 /* Progress Data {{{ */
2858 @interface ProgressData : NSObject {
2864 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2871 @implementation ProgressData
2873 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2874 if ((self = [super init]) != nil) {
2875 selector_ = selector;
2895 /* Progress View {{{ */
2896 @interface ProgressView : UIView <
2897 ConfigurationDelegate,
2900 _transient Database *database_;
2902 UIView *background_;
2903 UITransitionView *transition_;
2905 UINavigationBar *navbar_;
2906 UIProgressBar *progress_;
2907 UITextView *output_;
2908 UITextLabel *status_;
2909 UIPushButton *close_;
2912 SHA1SumValue springlist_;
2913 SHA1SumValue sandplate_;
2915 NSTimeInterval last_;
2918 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2920 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2921 - (void) setContentView:(UIView *)view;
2924 - (void) _retachThread;
2925 - (void) _detachNewThreadData:(ProgressData *)data;
2926 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2932 @protocol ProgressViewDelegate
2933 - (void) progressViewIsComplete:(ProgressView *)sender;
2936 @implementation ProgressView
2939 [transition_ setDelegate:nil];
2940 [navbar_ setDelegate:nil];
2943 if (background_ != nil)
2944 [background_ release];
2945 [transition_ release];
2948 [progress_ release];
2955 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2956 if (bootstrap_ && from == overlay_ && to == view_)
2960 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2961 if ((self = [super initWithFrame:frame]) != nil) {
2962 database_ = database;
2963 delegate_ = delegate;
2965 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2966 [transition_ setDelegate:self];
2968 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2971 [overlay_ setBackgroundColor:[UIColor blackColor]];
2973 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2974 [background_ setBackgroundColor:[UIColor blackColor]];
2975 [self addSubview:background_];
2978 [self addSubview:transition_];
2980 CGSize navsize = [UINavigationBar defaultSize];
2981 CGRect navrect = {{0, 0}, navsize};
2983 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2984 [overlay_ addSubview:navbar_];
2986 [navbar_ setBarStyle:1];
2987 [navbar_ setDelegate:self];
2989 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2990 [navbar_ pushNavigationItem:navitem];
2992 CGRect bounds = [overlay_ bounds];
2993 CGSize prgsize = [UIProgressBar defaultSize];
2996 (bounds.size.width - prgsize.width) / 2,
2997 bounds.size.height - prgsize.height - 20
3000 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3001 [progress_ setStyle:0];
3003 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3005 bounds.size.height - prgsize.height - 50,
3006 bounds.size.width - 20,
3010 [status_ setColor:[UIColor whiteColor]];
3011 [status_ setBackgroundColor:[UIColor clearColor]];
3013 [status_ setCentersHorizontally:YES];
3014 //[status_ setFont:font];
3016 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3018 navrect.size.height + 20,
3019 bounds.size.width - 20,
3020 bounds.size.height - navsize.height - 62 - navrect.size.height
3023 //[output_ setTextFont:@"Courier New"];
3024 [output_ setTextSize:12];
3026 [output_ setTextColor:[UIColor whiteColor]];
3027 [output_ setBackgroundColor:[UIColor clearColor]];
3029 [output_ setMarginTop:0];
3030 [output_ setAllowsRubberBanding:YES];
3031 [output_ setEditable:NO];
3033 [overlay_ addSubview:output_];
3035 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3037 bounds.size.height - prgsize.height - 50,
3038 bounds.size.width - 20,
3042 [close_ setAutosizesToFit:NO];
3043 [close_ setDrawsShadow:YES];
3044 [close_ setStretchBackground:YES];
3045 [close_ setEnabled:YES];
3047 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3048 [close_ setTitleFont:bold];
3050 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3051 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3052 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3056 - (void) setContentView:(UIView *)view {
3057 view_ = [view retain];
3060 - (void) resetView {
3061 [transition_ transition:6 toView:view_];
3064 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3065 NSString *context([sheet context]);
3067 if ([context isEqualToString:@"conffile"]) {
3068 FILE *input = [database_ input];
3072 fprintf(input, "N\n");
3076 fprintf(input, "Y\n");
3087 - (void) closeButtonPushed {
3096 [delegate_ suspendWithAnimation:YES];
3100 system("launchctl stop com.apple.SpringBoard");
3104 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3113 - (void) _retachThread {
3114 UINavigationItem *item = [navbar_ topItem];
3115 [item setTitle:@"Complete"];
3117 [overlay_ addSubview:close_];
3118 [progress_ removeFromSuperview];
3119 [status_ removeFromSuperview];
3121 [delegate_ progressViewIsComplete:self];
3124 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3125 MMap mmap(file, MMap::ReadOnly);
3127 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3128 if (!(sandplate_ == sha1.Result()))
3133 FileFd file(SpringBoard_, FileFd::ReadOnly);
3134 MMap mmap(file, MMap::ReadOnly);
3136 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3137 if (!(springlist_ == sha1.Result()))
3142 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3143 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3144 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3145 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3146 case 4: [close_ setTitle:@"Reboot Device"]; break;
3149 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3151 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3152 [cache autorelease];
3154 NSFileManager *manager = [NSFileManager defaultManager];
3155 NSError *error = nil;
3157 id system = [cache objectForKey:@"System"];
3162 if (stat(Cache_, &info) == -1)
3165 [system removeAllObjects];
3167 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3168 for (NSString *app in apps)
3169 if ([app hasSuffix:@".app"]) {
3170 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3171 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3172 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3174 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3175 [info setObject:path forKey:@"Path"];
3176 [info setObject:@"System" forKey:@"ApplicationType"];
3177 [system addInfoDictionary:info];
3183 [cache writeToFile:@Cache_ atomically:YES];
3185 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3187 if (chmod(Cache_, info.st_mode) == -1)
3191 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3194 notify_post("com.apple.mobile.application_installed");
3196 [delegate_ setStatusBarShowsProgress:NO];
3199 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3200 [[data target] performSelector:[data selector] withObject:[data object]];
3203 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3206 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3207 UINavigationItem *item = [navbar_ topItem];
3208 [item setTitle:title];
3210 [status_ setText:nil];
3211 [output_ setText:@""];
3212 [progress_ setProgress:0];
3215 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3217 [close_ removeFromSuperview];
3218 [overlay_ addSubview:progress_];
3219 [overlay_ addSubview:status_];
3221 [delegate_ setStatusBarShowsProgress:YES];
3225 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3226 MMap mmap(file, MMap::ReadOnly);
3228 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3229 sandplate_ = sha1.Result();
3233 FileFd file(SpringBoard_, FileFd::ReadOnly);
3234 MMap mmap(file, MMap::ReadOnly);
3236 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3237 springlist_ = sha1.Result();
3240 [transition_ transition:6 toView:overlay_];
3243 detachNewThreadSelector:@selector(_detachNewThreadData:)
3245 withObject:[[ProgressData alloc]
3246 initWithSelector:selector
3253 - (void) repairWithSelector:(SEL)selector {
3255 detachNewThreadSelector:selector
3262 - (void) setConfigurationData:(NSString *)data {
3264 performSelectorOnMainThread:@selector(_setConfigurationData:)
3270 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3271 Package *package = id == nil ? nil : [database_ packageWithName:id];
3273 UIActionSheet *sheet = [[[UIActionSheet alloc]
3274 initWithTitle:(package == nil ? id : [package name])
3275 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3276 defaultButtonIndex:0
3281 [sheet setBodyText:error];
3282 [sheet popupAlertAnimated:YES];
3285 - (void) setProgressTitle:(NSString *)title {
3287 performSelectorOnMainThread:@selector(_setProgressTitle:)
3293 - (void) setProgressPercent:(float)percent {
3295 performSelectorOnMainThread:@selector(_setProgressPercent:)
3296 withObject:[NSNumber numberWithFloat:percent]
3301 - (void) startProgress {
3302 last_ = [NSDate timeIntervalSinceReferenceDate];
3305 - (void) addProgressOutput:(NSString *)output {
3307 performSelectorOnMainThread:@selector(_addProgressOutput:)
3313 - (bool) isCancelling:(size_t)received {
3315 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3316 if (received_ != received) {
3317 received_ = received;
3319 } else if (now - last_ > 30)
3326 - (void) _setConfigurationData:(NSString *)data {
3327 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3329 _assert(conffile_r(data));
3331 NSString *ofile = conffile_r[1];
3332 //NSString *nfile = conffile_r[2];
3334 UIActionSheet *sheet = [[[UIActionSheet alloc]
3335 initWithTitle:@"Configuration Upgrade"
3336 buttons:[NSArray arrayWithObjects:
3337 @"Keep My Old Copy",
3338 @"Accept The New Copy",
3339 // XXX: @"See What Changed",
3341 defaultButtonIndex:0
3346 [sheet setBodyText:[NSString stringWithFormat:
3347 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3350 [sheet popupAlertAnimated:YES];
3353 - (void) _setProgressTitle:(NSString *)title {
3354 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3355 for (size_t i(0), e([words count]); i != e; ++i) {
3356 NSString *word([words objectAtIndex:i]);
3357 if (Package *package = [database_ packageWithName:word])
3358 [words replaceObjectAtIndex:i withObject:[package name]];
3361 [status_ setText:[words componentsJoinedByString:@" "]];
3364 - (void) _setProgressPercent:(NSNumber *)percent {
3365 [progress_ setProgress:[percent floatValue]];
3368 - (void) _addProgressOutput:(NSString *)output {
3369 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3370 CGSize size = [output_ contentSize];
3371 CGRect rect = {{0, size.height}, {size.width, 0}};
3372 [output_ scrollRectToVisible:rect animated:YES];
3375 - (BOOL) isRunning {
3382 /* Package Cell {{{ */
3383 @interface PackageCell : UISimpleTableCell {
3386 NSString *description_;
3390 UITextLabel *status_;
3394 - (PackageCell *) init;
3395 - (void) setPackage:(Package *)package;
3397 + (int) heightForPackage:(Package *)package;
3401 @implementation PackageCell
3403 - (void) clearPackage {
3414 if (description_ != nil) {
3415 [description_ release];
3419 if (source_ != nil) {
3424 if (badge_ != nil) {
3431 [self clearPackage];
3438 - (PackageCell *) init {
3439 if ((self = [super init]) != nil) {
3441 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3442 [status_ setBackgroundColor:[UIColor clearColor]];
3443 [status_ setFont:small];
3448 - (void) setPackage:(Package *)package {
3449 [self clearPackage];
3451 Source *source = [package source];
3452 NSString *section = [package simpleSection];
3454 icon_ = [[package icon] retain];
3456 name_ = [[package name] retain];
3457 description_ = [[package tagline] retain];
3459 NSString *label = nil;
3460 bool trusted = false;
3462 if (source != nil) {
3463 label = [source label];
3464 trusted = [source trusted];
3465 } else if ([[package id] isEqualToString:@"firmware"])
3468 label = @"Unknown/Local";
3470 NSString *from = [NSString stringWithFormat:@"from %@", label];
3472 if (section != nil && ![section isEqualToString:label])
3473 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3475 source_ = [from retain];
3477 if (NSString *purpose = [package primaryPurpose])
3478 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3479 badge_ = [badge_ retain];
3482 if (NSString *mode = [package mode]) {
3483 [badge_ setImage:[UIImage applicationImageNamed:
3484 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3487 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3488 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3489 } else if ([package half]) {
3490 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3491 [status_ setText:@"Package Damaged"];
3492 [status_ setColor:[UIColor redColor]];
3494 [badge_ setImage:nil];
3495 [status_ setText:nil];
3500 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3503 rect.size = [icon_ size];
3505 rect.size.width /= 2;
3506 rect.size.height /= 2;
3508 rect.origin.x = 25 - rect.size.width / 2;
3509 rect.origin.y = 25 - rect.size.height / 2;
3511 [icon_ drawInRect:rect];
3514 if (badge_ != nil) {
3515 CGSize size = [badge_ size];
3517 [badge_ drawAtPoint:CGPointMake(
3518 36 - size.width / 2,
3519 36 - size.height / 2
3528 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3529 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3533 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3535 [super drawContentInRect:rect selected:selected];
3538 + (int) heightForPackage:(Package *)package {
3539 NSString *tagline([package tagline]);
3540 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3542 if ([package hasMode] || [package half])
3551 /* Section Cell {{{ */
3552 @interface SectionCell : UISimpleTableCell {
3557 _UISwitchSlider *switch_;
3562 - (void) setSection:(Section *)section editing:(BOOL)editing;
3566 @implementation SectionCell
3568 - (void) clearSection {
3569 if (section_ != nil) {
3579 if (count_ != nil) {
3586 [self clearSection];
3593 if ((self = [super init]) != nil) {
3594 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3596 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3597 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3601 - (void) onSwitch:(id)sender {
3602 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3603 if (metadata == nil) {
3604 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3605 [Sections_ setObject:metadata forKey:section_];
3609 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3612 - (void) setSection:(Section *)section editing:(BOOL)editing {
3613 if (editing != editing_) {
3615 [switch_ removeFromSuperview];
3617 [self addSubview:switch_];
3621 [self clearSection];
3623 if (section == nil) {
3624 name_ = [@"All Packages" retain];
3627 section_ = [section name];
3628 if (section_ != nil)
3629 section_ = [section_ retain];
3630 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3631 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3634 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3638 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3639 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3646 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3648 CGSize size = [count_ sizeWithFont:Font14_];
3652 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3654 [super drawContentInRect:rect selected:selected];
3660 /* File Table {{{ */
3661 @interface FileTable : RVPage {
3662 _transient Database *database_;
3665 NSMutableArray *files_;
3669 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3670 - (void) setPackage:(Package *)package;
3674 @implementation FileTable
3677 if (package_ != nil)
3686 - (int) numberOfRowsInTable:(UITable *)table {
3687 return files_ == nil ? 0 : [files_ count];
3690 - (float) table:(UITable *)table heightForRow:(int)row {
3694 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3695 if (reusing == nil) {
3696 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3697 UIFont *font = [UIFont systemFontOfSize:16];
3698 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3700 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3704 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3708 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3709 if ((self = [super initWithBook:book]) != nil) {
3710 database_ = database;
3712 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3714 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3715 [self addSubview:list_];
3717 UITableColumn *column = [[[UITableColumn alloc]
3718 initWithTitle:@"Name"
3720 width:[self frame].size.width
3723 [list_ setDataSource:self];
3724 [list_ setSeparatorStyle:1];
3725 [list_ addTableColumn:column];
3726 [list_ setDelegate:self];
3727 [list_ setReusesTableCells:YES];
3731 - (void) setPackage:(Package *)package {
3732 if (package_ != nil) {
3733 [package_ autorelease];
3742 [files_ removeAllObjects];
3744 if (package != nil) {
3745 package_ = [package retain];
3746 name_ = [[package id] retain];
3748 if (NSArray *files = [package files])
3749 [files_ addObjectsFromArray:files];
3751 if ([files_ count] != 0) {
3752 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3753 [files_ removeObjectAtIndex:0];
3754 [files_ sortUsingSelector:@selector(compareByPath:)];
3756 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3757 [stack addObject:@"/"];
3759 for (int i(0), e([files_ count]); i != e; ++i) {
3760 NSString *file = [files_ objectAtIndex:i];
3761 while (![file hasPrefix:[stack lastObject]])
3762 [stack removeLastObject];
3763 NSString *directory = [stack lastObject];
3764 [stack addObject:[file stringByAppendingString:@"/"]];
3765 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3766 ([stack count] - 2) * 3, "",
3767 [file substringFromIndex:[directory length]]
3776 - (void) resetViewAnimated:(BOOL)animated {
3777 [list_ resetViewAnimated:animated];
3780 - (void) reloadData {
3781 [self setPackage:[database_ packageWithName:name_]];
3782 [self reloadButtons];
3785 - (NSString *) title {
3786 return @"Installed Files";
3789 - (NSString *) backButtonTitle {
3795 /* Package View {{{ */
3796 @interface PackageView : BrowserView {
3797 _transient Database *database_;
3800 NSMutableArray *buttons_;
3803 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3804 - (void) setPackage:(Package *)package;
3808 @implementation PackageView
3811 if (package_ != nil)
3819 - (void) _clickButtonWithName:(NSString *)name {
3820 if ([name isEqualToString:@"Install"])
3821 [delegate_ installPackage:package_];
3822 else if ([name isEqualToString:@"Reinstall"])
3823 [delegate_ installPackage:package_];
3824 else if ([name isEqualToString:@"Remove"])
3825 [delegate_ removePackage:package_];
3826 else if ([name isEqualToString:@"Upgrade"])
3827 [delegate_ installPackage:package_];
3828 else _assert(false);
3831 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3832 NSString *context([sheet context]);
3834 if ([context isEqualToString:@"modify"]) {
3835 int count = [buttons_ count];
3836 _assert(count != 0);
3837 _assert(button <= count + 1);
3839 if (count != button - 1)
3840 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3844 [super alertSheet:sheet buttonClicked:button];
3847 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3848 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3849 return [super webView:sender didFinishLoadForFrame:frame];
3852 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3853 [window setValue:package_ forKey:@"package"];
3854 [super webView:sender didClearWindowObject:window forFrame:frame];
3858 - (void) _rightButtonClicked {
3859 /*[super _rightButtonClicked];
3862 int count = [buttons_ count];
3863 _assert(count != 0);
3866 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3868 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3869 [buttons addObjectsFromArray:buttons_];
3870 [buttons addObject:@"Cancel"];
3872 [delegate_ slideUp:[[[UIActionSheet alloc]
3875 defaultButtonIndex:2
3883 - (NSString *) _rightButtonTitle {
3884 int count = [buttons_ count];
3885 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3888 - (NSString *) backButtonTitle {
3892 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3893 if ((self = [super initWithBook:book]) != nil) {
3894 database_ = database;
3895 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3899 - (void) setPackage:(Package *)package {
3900 if (package_ != nil) {
3901 [package_ autorelease];
3910 [buttons_ removeAllObjects];
3912 if (package != nil) {
3913 package_ = [package retain];
3914 name_ = [[package id] retain];
3916 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3918 if ([package_ source] == nil);
3919 else if ([package_ upgradableAndEssential:NO])
3920 [buttons_ addObject:@"Upgrade"];
3921 else if ([package_ installed] == nil)
3922 [buttons_ addObject:@"Install"];
3924 [buttons_ addObject:@"Reinstall"];
3925 if ([package_ installed] != nil)
3926 [buttons_ addObject:@"Remove"];
3930 - (void) reloadData {
3931 [self setPackage:[database_ packageWithName:name_]];
3932 [self reloadButtons];
3937 /* Package Table {{{ */
3938 @interface PackageTable : RVPage {
3939 _transient Database *database_;
3943 NSMutableArray *packages_;
3944 NSMutableArray *sections_;
3945 UISectionList *list_;
3948 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3950 - (void) setDelegate:(id)delegate;
3951 - (void) setObject:(id)object;
3953 - (void) reloadData;
3954 - (void) resetCursor;
3956 - (UISectionList *) list;
3958 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3962 @implementation PackageTable
3965 [list_ setDataSource:nil];
3970 [packages_ release];
3971 [sections_ release];
3976 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3977 return [sections_ count];
3980 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3981 return [[sections_ objectAtIndex:section] name];
3984 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3985 return [[sections_ objectAtIndex:section] row];
3988 - (int) numberOfRowsInTable:(UITable *)table {
3989 return [packages_ count];
3992 - (float) table:(UITable *)table heightForRow:(int)row {
3993 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3996 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3998 reusing = [[[PackageCell alloc] init] autorelease];
3999 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4003 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4007 - (void) tableRowSelected:(NSNotification *)notification {
4008 int row = [[notification object] selectedRow];
4012 Package *package = [packages_ objectAtIndex:row];
4013 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4014 [view setDelegate:delegate_];
4015 [view setPackage:package];
4016 [book_ pushPage:view];
4019 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4020 if ((self = [super initWithBook:book]) != nil) {
4021 database_ = database;
4022 title_ = [title retain];
4024 object_ = object == nil ? nil : [object retain];
4026 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4027 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4029 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4030 [list_ setDataSource:self];
4032 UITableColumn *column = [[[UITableColumn alloc]
4033 initWithTitle:@"Name"
4035 width:[self frame].size.width
4038 UITable *table = [list_ table];
4039 [table setSeparatorStyle:1];
4040 [table addTableColumn:column];
4041 [table setDelegate:self];
4042 [table setReusesTableCells:YES];
4044 [self addSubview:list_];
4047 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4048 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4052 - (void) setDelegate:(id)delegate {
4053 delegate_ = delegate;
4056 - (void) setObject:(id)object {
4062 object_ = [object retain];
4065 - (void) reloadData {
4066 NSArray *packages = [database_ packages];
4068 [packages_ removeAllObjects];
4069 [sections_ removeAllObjects];
4071 for (size_t i(0); i != [packages count]; ++i) {
4072 Package *package([packages objectAtIndex:i]);
4073 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4074 [packages_ addObject:package];
4077 Section *section = nil;
4079 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4080 Package *package = [packages_ objectAtIndex:offset];
4081 NSString *name = [package index];
4083 if (section == nil || ![[section name] isEqualToString:name]) {
4084 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4085 [sections_ addObject:section];
4088 [section addToCount];
4094 - (NSString *) title {
4098 - (void) resetViewAnimated:(BOOL)animated {
4099 [list_ resetViewAnimated:animated];
4102 - (void) resetCursor {
4103 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4106 - (UISectionList *) list {
4110 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4111 [list_ setShouldHideHeaderInShortLists:hide];
4117 /* Add Source View {{{ */
4118 @interface AddSourceView : RVPage {
4119 _transient Database *database_;
4122 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4126 @implementation AddSourceView
4128 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4129 if ((self = [super initWithBook:book]) != nil) {
4130 database_ = database;
4136 /* Source Cell {{{ */
4137 @interface SourceCell : UITableCell {
4140 NSString *description_;
4146 - (SourceCell *) initWithSource:(Source *)source;
4150 @implementation SourceCell
4155 [description_ release];
4160 - (SourceCell *) initWithSource:(Source *)source {
4161 if ((self = [super init]) != nil) {
4163 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4165 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4166 icon_ = [icon_ retain];
4168 origin_ = [[source name] retain];
4169 label_ = [[source uri] retain];
4170 description_ = [[source description] retain];
4174 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4176 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4183 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4187 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4191 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4193 [super drawContentInRect:rect selected:selected];
4198 /* Source Table {{{ */
4199 @interface SourceTable : RVPage {
4200 _transient Database *database_;
4201 UISectionList *list_;
4202 NSMutableArray *sources_;
4203 UIActionSheet *alert_;
4207 UIProgressHUD *hud_;
4210 //NSURLConnection *installer_;
4211 NSURLConnection *trivial_bz2_;
4212 NSURLConnection *trivial_gz_;
4213 //NSURLConnection *automatic_;
4218 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4222 @implementation SourceTable
4224 - (void) _deallocConnection:(NSURLConnection *)connection {
4225 if (connection != nil) {
4226 [connection cancel];
4227 //[connection setDelegate:nil];
4228 [connection release];
4233 [[list_ table] setDelegate:nil];
4234 [list_ setDataSource:nil];
4243 //[self _deallocConnection:installer_];
4244 [self _deallocConnection:trivial_gz_];
4245 [self _deallocConnection:trivial_bz2_];
4246 //[self _deallocConnection:automatic_];
4253 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4254 return offset_ == 0 ? 1 : 2;
4257 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4258 switch (section + (offset_ == 0 ? 1 : 0)) {
4259 case 0: return @"Entered by User";
4260 case 1: return @"Installed by Packages";
4268 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4269 switch (section + (offset_ == 0 ? 1 : 0)) {
4271 case 1: return offset_;
4279 - (int) numberOfRowsInTable:(UITable *)table {
4280 return [sources_ count];
4283 - (float) table:(UITable *)table heightForRow:(int)row {
4284 Source *source = [sources_ objectAtIndex:row];
4285 return [source description] == nil ? 56 : 73;
4288 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4289 Source *source = [sources_ objectAtIndex:row];
4290 // XXX: weird warning, stupid selectors ;P
4291 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4294 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4298 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4302 - (void) tableRowSelected:(NSNotification*)notification {
4303 UITable *table([list_ table]);
4304 int row([table selectedRow]);
4308 Source *source = [sources_ objectAtIndex:row];
4310 PackageTable *packages = [[[PackageTable alloc]
4313 title:[source label]
4314 filter:@selector(isVisibleInSource:)
4318 [packages setDelegate:delegate_];
4320 [book_ pushPage:packages];
4323 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4324 Source *source = [sources_ objectAtIndex:row];
4325 return [source record] != nil;
4328 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4329 [[list_ table] setDeleteConfirmationRow:row];
4332 - (void) table:(UITable *)table deleteRow:(int)row {
4333 Source *source = [sources_ objectAtIndex:row];
4334 [Sources_ removeObjectForKey:[source key]];
4335 [delegate_ syncData];
4338 - (void) _endConnection:(NSURLConnection *)connection {
4339 NSURLConnection **field = NULL;
4340 if (connection == trivial_bz2_)
4341 field = &trivial_bz2_;
4342 else if (connection == trivial_gz_)
4343 field = &trivial_gz_;
4344 _assert(field != NULL);
4345 [connection release];
4349 trivial_bz2_ == nil &&
4352 [delegate_ setStatusBarShowsProgress:NO];
4355 [hud_ removeFromSuperview];
4360 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4363 @"./", @"Distribution",
4364 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4366 [delegate_ syncData];
4367 } else if (error_ != nil) {
4368 UIActionSheet *sheet = [[[UIActionSheet alloc]
4369 initWithTitle:@"Verification Error"
4370 buttons:[NSArray arrayWithObjects:@"OK", nil]
4371 defaultButtonIndex:0
4376 [sheet setBodyText:[error_ localizedDescription]];
4377 [sheet popupAlertAnimated:YES];
4379 UIActionSheet *sheet = [[[UIActionSheet alloc]
4380 initWithTitle:@"Did not Find Repository"
4381 buttons:[NSArray arrayWithObjects:@"OK", nil]
4382 defaultButtonIndex:0
4387 [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."];
4388 [sheet popupAlertAnimated:YES];
4394 if (error_ != nil) {
4401 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4402 switch ([response statusCode]) {
4408 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4409 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4411 error_ = [error retain];
4412 [self _endConnection:connection];
4415 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4416 [self _endConnection:connection];
4419 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4420 NSMutableURLRequest *request = [NSMutableURLRequest
4421 requestWithURL:[NSURL URLWithString:href]
4422 cachePolicy:NSURLRequestUseProtocolCachePolicy
4423 timeoutInterval:20.0
4426 [request setHTTPMethod:method];
4428 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4431 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4432 NSString *context([sheet context]);
4434 if ([context isEqualToString:@"source"]) {
4437 NSString *href = [[sheet textField] text];
4439 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4441 if (![href hasSuffix:@"/"])
4442 href_ = [href stringByAppendingString:@"/"];
4445 href_ = [href_ retain];
4447 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4448 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4449 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4453 hud_ = [delegate_ addProgressHUD];
4454 [hud_ setText:@"Verifying URL"];
4465 } else if ([context isEqualToString:@"trivial"])
4467 else if ([context isEqualToString:@"urlerror"])
4471 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4472 if ((self = [super initWithBook:book]) != nil) {
4473 database_ = database;
4474 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4476 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4477 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4478 [list_ setShouldHideHeaderInShortLists:NO];
4480 [self addSubview:list_];
4481 [list_ setDataSource:self];
4483 UITableColumn *column = [[UITableColumn alloc]
4484 initWithTitle:@"Name"
4486 width:[self frame].size.width
4489 UITable *table = [list_ table];
4490 [table setSeparatorStyle:1];
4491 [table addTableColumn:column];
4492 [table setDelegate:self];
4496 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4497 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4501 - (void) reloadData {
4503 _assert(list.ReadMainList());
4505 [sources_ removeAllObjects];
4506 [sources_ addObjectsFromArray:[database_ sources]];
4508 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4511 int count = [sources_ count];
4512 for (offset_ = 0; offset_ != count; ++offset_) {
4513 Source *source = [sources_ objectAtIndex:offset_];
4514 if ([source record] == nil)
4521 - (void) resetViewAnimated:(BOOL)animated {
4522 [list_ resetViewAnimated:animated];
4525 - (void) _leftButtonClicked {
4526 /*[book_ pushPage:[[[AddSourceView alloc]
4531 UIActionSheet *sheet = [[[UIActionSheet alloc]
4532 initWithTitle:@"Enter Cydia/APT URL"
4533 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4534 defaultButtonIndex:0
4539 [sheet setNumberOfRows:1];
4541 [sheet addTextFieldWithValue:@"http://" label:@""];
4543 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4544 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4545 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4546 [traits setKeyboardType:UIKeyboardTypeURL];
4547 // XXX: UIReturnKeyDone
4548 [traits setReturnKeyType:UIReturnKeyNext];
4550 [sheet popupAlertAnimated:YES];
4553 - (void) _rightButtonClicked {
4554 UITable *table = [list_ table];
4555 BOOL editing = [table isRowDeletionEnabled];
4556 [table enableRowDeletion:!editing animated:YES];
4557 [book_ reloadButtonsForPage:self];
4560 - (NSString *) title {
4564 - (NSString *) leftButtonTitle {
4565 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4568 - (NSString *) rightButtonTitle {
4569 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4572 - (UINavigationButtonStyle) rightButtonStyle {
4573 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4579 /* Installed View {{{ */
4580 @interface InstalledView : RVPage {
4581 _transient Database *database_;
4582 PackageTable *packages_;
4586 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4590 @implementation InstalledView
4593 [packages_ release];
4597 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4598 if ((self = [super initWithBook:book]) != nil) {
4599 database_ = database;
4601 packages_ = [[PackageTable alloc]
4605 filter:@selector(isInstalledAndVisible:)
4606 with:[NSNumber numberWithBool:YES]
4609 [self addSubview:packages_];
4611 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4612 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4616 - (void) resetViewAnimated:(BOOL)animated {
4617 [packages_ resetViewAnimated:animated];
4620 - (void) reloadData {
4621 [packages_ reloadData];
4624 - (void) _rightButtonClicked {
4625 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4626 [packages_ reloadData];
4628 [book_ reloadButtonsForPage:self];
4631 - (NSString *) title {
4632 return @"Installed";
4635 - (NSString *) backButtonTitle {
4639 - (NSString *) rightButtonTitle {
4640 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4643 - (UINavigationButtonStyle) rightButtonStyle {
4644 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4647 - (void) setDelegate:(id)delegate {
4648 [super setDelegate:delegate];
4649 [packages_ setDelegate:delegate];
4656 @interface HomeView : BrowserView {
4661 @implementation HomeView
4663 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4664 NSString *context([sheet context]);
4666 if ([context isEqualToString:@"about"])
4669 [super alertSheet:sheet buttonClicked:button];
4672 - (void) _leftButtonClicked {
4673 UIActionSheet *sheet = [[[UIActionSheet alloc]
4674 initWithTitle:@"About Cydia Installer"
4675 buttons:[NSArray arrayWithObjects:@"Close", nil]
4676 defaultButtonIndex:0
4682 @"Copyright (C) 2008\n"
4683 "Jay Freeman (saurik)\n"
4684 "saurik@saurik.com\n"
4685 "http://www.saurik.com/\n"
4688 "http://www.theokorigroup.com/\n"
4690 "College of Creative Studies,\n"
4691 "University of California,\n"
4693 "http://www.ccs.ucsb.edu/"
4696 [sheet popupAlertAnimated:YES];
4699 - (NSString *) leftButtonTitle {
4705 /* Manage View {{{ */
4706 @interface ManageView : BrowserView {
4711 @implementation ManageView
4713 - (NSString *) title {
4717 - (void) _leftButtonClicked {
4718 [delegate_ askForSettings];
4721 - (NSString *) leftButtonTitle {
4726 - (NSString *) _rightButtonTitle {
4734 @interface WebView (Cydia)
4735 - (void) setScriptDebugDelegate:(id)delegate;
4736 - (void) _setFormDelegate:(id)delegate;
4737 - (void) _setUIKitDelegate:(id)delegate;
4738 - (void) setWebMailDelegate:(id)delegate;
4739 - (void) _setLayoutInterval:(float)interval;
4742 /* Indirect Delegate {{{ */
4743 @interface IndirectDelegate : NSProxy {
4744 _transient volatile id delegate_;
4747 - (void) setDelegate:(id)delegate;
4748 - (id) initWithDelegate:(id)delegate;
4751 @implementation IndirectDelegate
4753 - (void) setDelegate:(id)delegate {
4754 delegate_ = delegate;
4757 - (id) initWithDelegate:(id)delegate {
4758 delegate_ = delegate;
4762 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4763 if (delegate_ != nil)
4764 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4766 // XXX: I fucking hate Apple so very very bad
4767 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4770 - (void) forwardInvocation:(NSInvocation *)inv {
4771 SEL sel = [inv selector];
4772 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4773 [inv invokeWithTarget:delegate_];
4778 /* Browser Implementation {{{ */
4779 @implementation BrowserView
4782 if (challenge_ != nil)
4783 [challenge_ release];
4785 WebView *webview = [webview_ webView];
4786 [webview setFrameLoadDelegate:nil];
4787 [webview setResourceLoadDelegate:nil];
4788 [webview setUIDelegate:nil];
4789 [webview setScriptDebugDelegate:nil];
4790 [webview setPolicyDelegate:nil];
4792 [webview setDownloadDelegate:nil];
4794 [webview _setFormDelegate:nil];
4795 [webview _setUIKitDelegate:nil];
4796 [webview setWebMailDelegate:nil];
4797 [webview setEditingDelegate:nil];
4799 [webview_ setDelegate:nil];
4800 [webview_ setGestureDelegate:nil];
4802 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4807 [webview_ removeFromSuperview];
4808 [Documents_ addObject:[webview_ autorelease]];
4813 [indirect_ setDelegate:nil];
4814 [indirect_ release];
4816 [scroller_ setDelegate:nil];
4818 [scroller_ release];
4820 [indicator_ release];
4826 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4827 [self loadRequest:[NSURLRequest
4830 timeoutInterval:30.0
4834 - (void) loadURL:(NSURL *)url {
4835 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4838 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4839 NSMutableURLRequest *copy = [request mutableCopy];
4841 if (Machine_ != NULL)
4842 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4843 if (UniqueID_ != nil)
4844 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4847 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4852 - (void) loadRequest:(NSURLRequest *)request {
4854 [webview_ loadRequest:request];
4857 - (void) reloadURL {
4858 if ([urls_ count] == 0)
4860 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4861 [urls_ removeLastObject];
4862 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4865 - (WebView *) webView {
4866 return [webview_ webView];
4869 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4870 [scroller_ setContentSize:frame.size];
4873 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4874 [self view:sender didSetFrame:frame];
4877 - (void) pushPage:(RVPage *)page {
4878 [self setBackButtonTitle:title_];
4879 [page setDelegate:delegate_];
4880 [book_ pushPage:page];
4883 - (BOOL) getSpecial:(NSURL *)url {
4884 NSString *href([url absoluteString]);
4885 NSString *scheme([[url scheme] lowercaseString]);
4889 if ([href hasPrefix:@"apptapp://package/"])
4890 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4891 else if ([scheme isEqualToString:@"cydia"]) {
4892 page = [delegate_ pageForURL:url hasTag:NULL];
4895 } else if (![scheme isEqualToString:@"apptapp"])
4899 [self pushPage:page];
4903 - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4904 UIActionSheet *sheet = [[[UIActionSheet alloc]
4905 initWithTitle:@"JavaScript Alert"
4906 buttons:[NSArray arrayWithObjects:@"OK", nil]
4907 defaultButtonIndex:0
4912 [sheet setBodyText:message];
4913 [sheet popupAlertAnimated:YES];
4916 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4917 [window setValue:delegate_ forKey:@"cydia"];
4920 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4921 if (NSURL *url = [request URL]) {
4922 if (![self getSpecial:url]) {
4923 NSString *scheme([[url scheme] lowercaseString]);
4924 if ([scheme isEqualToString:@"mailto"])
4925 [delegate_ openMailToURL:url];
4934 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4935 if ([WebView canShowMIMEType:type])
4938 // XXX: handle more mime types!
4940 if (frame == [webView mainFrame])
4941 [UIApp openURL:[request URL]];
4945 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4946 NSURL *url([request URL]);
4948 if (url == nil) use: {
4953 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4956 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4957 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4960 [UIApp openURL:url];
4966 int store(_not(int));
4967 if (NSURL *itms = [url itmsURL:&store]) {
4968 NSLog(@"itms#%@#%u#%@", url, store, itms);
4970 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4971 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4978 NSString *scheme([[url scheme] lowercaseString]);
4980 if ([scheme isEqualToString:@"tel"]) {
4981 // XXX: intelligence
4985 if ([scheme isEqualToString:@"mailto"]) {
4986 [delegate_ openMailToURL:url];
4990 if ([self getSpecial:url])
4992 else if ([WebView _canHandleRequest:request])
4994 else if ([url isSpringboardHandledURL])
5000 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
5001 //lprintf("Status:%s\n", [text UTF8String]);
5004 - (void) _pushPage {
5008 [book_ pushPage:self];
5011 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5012 NSString *context([sheet context]);
5014 if ([context isEqualToString:@"alert"])
5016 else if ([context isEqualToString:@"challenge"]) {
5017 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
5021 NSString *username([[sheet textFieldAtIndex:0] text]);
5022 NSString *password([[sheet textFieldAtIndex:1] text]);
5024 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
5026 [sender useCredential:credential forAuthenticationChallenge:challenge_];
5030 [sender cancelAuthenticationChallenge:challenge_];
5037 [challenge_ release];
5044 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
5045 challenge_ = [challenge retain];
5047 NSURLProtectionSpace *space([challenge protectionSpace]);
5048 NSString *realm([space realm]);
5052 UIActionSheet *sheet = [[[UIActionSheet alloc]
5054 buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
5055 defaultButtonIndex:0
5057 context:@"challenge"
5060 [sheet setNumberOfRows:1];
5062 [sheet addTextFieldWithValue:@"" label:@"username"];
5063 [sheet addTextFieldWithValue:@"" label:@"password"];
5065 UITextField *username([sheet textFieldAtIndex:0]); {
5066 UITextInputTraits *traits([username textInputTraits]);
5067 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5068 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5069 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5070 [traits setReturnKeyType:UIReturnKeyNext];
5073 UITextField *password([sheet textFieldAtIndex:1]); {
5074 UITextInputTraits *traits([password textInputTraits]);
5075 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5076 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5077 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5078 // XXX: UIReturnKeyDone
5079 [traits setReturnKeyType:UIReturnKeyNext];
5080 [traits setSecureTextEntry:YES];
5083 [sheet popupAlertAnimated:YES];
5086 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
5087 NSURL *url = [request URL];
5088 if ([self getSpecial:url])
5091 return [self _addHeadersToRequest:request];
5094 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
5095 [self setBackButtonTitle:title_];
5097 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
5098 [browser setDelegate:delegate_];
5101 [browser loadRequest:[self _addHeadersToRequest:request]];
5102 [book_ pushPage:browser];
5105 return [browser webView];
5108 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
5109 return [self _createWebViewWithRequest:request pushed:(request != nil)];
5112 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
5113 return [self _createWebViewWithRequest:request pushed:YES];
5116 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
5117 if ([frame parentFrame] != nil)
5120 title_ = [title retain];
5121 [book_ reloadTitleForPage:self];
5124 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
5125 if ([frame parentFrame] != nil)
5130 [indicator_ startAnimation];
5131 [self reloadButtons];
5133 if (title_ != nil) {
5138 [book_ reloadTitleForPage:self];
5140 WebView *webview = [webview_ webView];
5141 NSString *href = [webview mainFrameURL];
5142 [urls_ addObject:[NSURL URLWithString:href]];
5144 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
5146 CGRect webrect = [scroller_ bounds];
5147 webrect.size.height = 0;
5148 [webview_ setFrame:webrect];
5151 - (void) _finishLoading {
5154 [indicator_ stopAnimation];
5155 [self reloadButtons];
5159 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5160 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5163 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5164 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5167 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5168 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5171 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5172 return [webview_ webView:sender didCommitLoadForFrame:frame];
5175 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5176 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5179 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5180 if ([frame parentFrame] == nil)
5181 [self _finishLoading];
5182 return [webview_ webView:sender didFinishLoadForFrame:frame];
5185 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5186 if ([frame parentFrame] != nil)
5188 [self _finishLoading];
5190 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5191 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5192 [[error localizedDescription] stringByAddingPercentEscapes]
5196 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5198 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5202 - (id) initWithBook:(RVBook *)book {
5203 if ((self = [super initWithBook:book]) != nil) {
5206 struct CGRect bounds = [self bounds];
5208 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5209 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5210 [self addSubview:pinstripe];
5212 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5213 [self addSubview:scroller_];
5215 [scroller_ setScrollingEnabled:YES];
5216 [scroller_ setAdjustForContentSizeChange:YES];
5217 [scroller_ setClipsSubviews:YES];
5218 [scroller_ setAllowsRubberBanding:YES];
5219 [scroller_ setScrollDecelerationFactor:0.99];
5220 [scroller_ setDelegate:self];
5222 CGRect webrect = [scroller_ bounds];
5223 webrect.size.height = 0;
5228 webview_ = [Documents_ lastObject];
5229 if (webview_ != nil) {
5230 webview_ = [webview_ retain];
5231 webview = [webview_ webView];
5232 [Documents_ removeLastObject];
5233 [webview_ setFrame:webrect];
5238 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5239 webview = [webview_ webView];
5241 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5243 [webview_ setAllowsMessaging:YES];
5245 [webview_ setTilingEnabled:YES];
5246 [webview_ setDrawsGrid:NO];
5247 [webview_ setLogsTilingChanges:NO];
5248 [webview_ setTileMinificationFilter:kCAFilterNearest];
5249 [webview_ setDetectsPhoneNumbers:NO];
5250 [webview_ setAutoresizes:YES];
5252 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5253 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5254 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5256 [webview_ _setDocumentType:0x4];
5258 [webview_ setZoomsFocusedFormControl:YES];
5259 [webview_ setContentsPosition:7];
5260 [webview_ setEnabledGestures:0xa];
5261 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
5262 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
5264 [webview_ setSmoothsFonts:YES];
5266 [webview _setUsesLoaderCache:YES];
5267 [webview setGroupName:@"Cydia"];
5268 //[webview _setLayoutInterval:0.5];
5271 [webview_ setDelegate:self];
5272 [webview_ setGestureDelegate:self];
5273 [scroller_ addSubview:webview_];
5275 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5277 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5278 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5279 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5281 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5282 NSString *application = package == nil ? @"Cydia" : [NSString
5283 stringWithFormat:@"Cydia/%@",
5285 ]; [webview setApplicationNameForUserAgent:application];
5287 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5289 [webview setFrameLoadDelegate:self];
5290 [webview setResourceLoadDelegate:indirect_];
5291 [webview setUIDelegate:self];
5292 [webview setScriptDebugDelegate:self];
5293 [webview setPolicyDelegate:self];
5295 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5297 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5298 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5299 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5303 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5304 [webview_ redrawScaledDocument];
5307 - (void) _rightButtonClicked {
5312 - (NSString *) _rightButtonTitle {
5316 - (NSString *) rightButtonTitle {
5317 return loading_ ? @"" : [self _rightButtonTitle];
5320 - (NSString *) title {
5321 return title_ == nil ? @"Loading" : title_;
5324 - (NSString *) backButtonTitle {
5328 - (void) setPageActive:(BOOL)active {
5330 [indicator_ removeFromSuperview];
5332 [[book_ navigationBar] addSubview:indicator_];
5335 - (void) resetViewAnimated:(BOOL)animated {
5338 - (void) setPushed:(bool)pushed {
5345 /* Cydia Book {{{ */
5346 @interface CYBook : RVBook <
5349 _transient Database *database_;
5350 UINavigationBar *overlay_;
5351 UINavigationBar *underlay_;
5352 UIProgressIndicator *indicator_;
5353 UITextLabel *prompt_;
5354 UIProgressBar *progress_;
5355 UINavigationButton *cancel_;
5358 NSTimeInterval last_;
5361 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5367 @implementation CYBook
5371 [indicator_ release];
5373 [progress_ release];
5378 - (NSString *) getTitleForPage:(RVPage *)page {
5379 return Simplify([super getTitleForPage:page]);
5387 [UIView beginAnimations:nil context:NULL];
5389 CGRect ovrframe = [overlay_ frame];
5390 ovrframe.origin.y = 0;
5391 [overlay_ setFrame:ovrframe];
5393 CGRect barframe = [navbar_ frame];
5394 barframe.origin.y += ovrframe.size.height;
5395 [navbar_ setFrame:barframe];
5397 CGRect trnframe = [transition_ frame];
5398 trnframe.origin.y += ovrframe.size.height;
5399 trnframe.size.height -= ovrframe.size.height;
5400 [transition_ setFrame:trnframe];
5402 [UIView endAnimations];
5404 [indicator_ startAnimation];
5405 [prompt_ setText:@"Updating Database"];
5406 [progress_ setProgress:0];
5409 last_ = [NSDate timeIntervalSinceReferenceDate];
5411 [overlay_ addSubview:cancel_];
5414 detachNewThreadSelector:@selector(_update)
5423 [indicator_ stopAnimation];
5425 [UIView beginAnimations:nil context:NULL];
5427 CGRect ovrframe = [overlay_ frame];
5428 ovrframe.origin.y = -ovrframe.size.height;
5429 [overlay_ setFrame:ovrframe];
5431 CGRect barframe = [navbar_ frame];
5432 barframe.origin.y -= ovrframe.size.height;
5433 [navbar_ setFrame:barframe];
5435 CGRect trnframe = [transition_ frame];
5436 trnframe.origin.y -= ovrframe.size.height;
5437 trnframe.size.height += ovrframe.size.height;
5438 [transition_ setFrame:trnframe];
5440 [UIView commitAnimations];
5442 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5445 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5446 if ((self = [super initWithFrame:frame]) != nil) {
5447 database_ = database;
5449 CGRect ovrrect = [navbar_ bounds];
5450 ovrrect.size.height = [UINavigationBar defaultSize].height;
5451 ovrrect.origin.y = -ovrrect.size.height;
5453 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5454 [self addSubview:overlay_];
5456 ovrrect.origin.y = frame.size.height;
5457 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5458 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5459 [self addSubview:underlay_];
5461 [overlay_ setBarStyle:1];
5462 [underlay_ setBarStyle:1];
5464 int barstyle = [overlay_ _barStyle:NO];
5465 bool ugly = barstyle == 0;
5467 UIProgressIndicatorStyle style = ugly ?
5468 UIProgressIndicatorStyleMediumBrown :
5469 UIProgressIndicatorStyleMediumWhite;
5471 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5472 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5473 CGRect indrect = {{indoffset, indoffset}, indsize};
5475 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5476 [indicator_ setStyle:style];
5477 [overlay_ addSubview:indicator_];
5479 CGSize prmsize = {215, indsize.height + 4};
5482 indoffset * 2 + indsize.width,
5486 unsigned(ovrrect.size.height - prmsize.height) / 2
5489 UIFont *font = [UIFont systemFontOfSize:15];
5491 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5493 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5494 [prompt_ setBackgroundColor:[UIColor clearColor]];
5495 [prompt_ setFont:font];
5497 [overlay_ addSubview:prompt_];
5499 CGSize prgsize = {75, 100};
5502 ovrrect.size.width - prgsize.width - 10,
5503 (ovrrect.size.height - prgsize.height) / 2
5506 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5507 [progress_ setStyle:0];
5508 [overlay_ addSubview:progress_];
5510 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5511 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5513 CGRect frame = [cancel_ frame];
5514 frame.size.width = 65;
5515 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5516 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5517 [cancel_ setFrame:frame];
5519 [cancel_ setBarStyle:barstyle];
5523 - (void) _onCancel {
5525 [cancel_ removeFromSuperview];
5528 - (void) _update { _pooled
5530 status.setDelegate(self);
5532 [database_ updateWithStatus:status];
5535 performSelectorOnMainThread:@selector(_update_)
5541 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5542 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5545 - (void) setProgressTitle:(NSString *)title {
5547 performSelectorOnMainThread:@selector(_setProgressTitle:)
5553 - (void) setProgressPercent:(float)percent {
5555 performSelectorOnMainThread:@selector(_setProgressPercent:)
5556 withObject:[NSNumber numberWithFloat:percent]
5561 - (void) startProgress {
5564 - (void) addProgressOutput:(NSString *)output {
5566 performSelectorOnMainThread:@selector(_addProgressOutput:)
5572 - (bool) isCancelling:(size_t)received {
5573 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5574 if (received_ != received) {
5575 received_ = received;
5577 } else if (now - last_ > 15)
5582 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5586 - (void) _setProgressTitle:(NSString *)title {
5587 [prompt_ setText:title];
5590 - (void) _setProgressPercent:(NSNumber *)percent {
5591 [progress_ setProgress:[percent floatValue]];
5594 - (void) _addProgressOutput:(NSString *)output {
5599 /* Cydia:// Protocol {{{ */
5600 @interface CydiaURLProtocol : NSURLProtocol {
5605 @implementation CydiaURLProtocol
5607 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5608 NSURL *url([request URL]);
5611 NSString *scheme([[url scheme] lowercaseString]);
5612 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5617 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5621 - (void) startLoading {
5622 id<NSURLProtocolClient> client([self client]);
5623 NSURLRequest *request([self request]);
5625 NSURL *url([request URL]);
5626 NSString *href([url absoluteString]);
5628 NSString *path([href substringFromIndex:8]);
5629 NSRange slash([path rangeOfString:@"/"]);
5632 if (slash.location == NSNotFound) {
5636 command = [path substringToIndex:slash.location];
5637 path = [path substringFromIndex:(slash.location + 1)];
5640 Database *database([Database sharedInstance]);
5642 if ([command isEqualToString:@"package-icon"]) {
5645 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5646 Package *package([database packageWithName:path]);
5650 UIImage *icon([package icon]);
5652 NSData *data(UIImagePNGRepresentation(icon));
5654 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5655 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5656 [client URLProtocol:self didLoadData:data];
5657 [client URLProtocolDidFinishLoading:self];
5658 } else if ([command isEqualToString:@"source-icon"]) {
5661 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5662 NSString *source(Simplify(path));
5664 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5666 icon = [UIImage applicationImageNamed:@"unknown.png"];
5668 NSData *data(UIImagePNGRepresentation(icon));
5670 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5671 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5672 [client URLProtocol:self didLoadData:data];
5673 [client URLProtocolDidFinishLoading:self];
5674 } else if ([command isEqualToString:@"section-icon"]) {
5677 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5678 NSString *section(Simplify(path));
5680 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5682 icon = [UIImage applicationImageNamed:@"unknown.png"];
5684 NSData *data(UIImagePNGRepresentation(icon));
5686 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5687 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5688 [client URLProtocol:self didLoadData:data];
5689 [client URLProtocolDidFinishLoading:self];
5691 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5695 - (void) stopLoading {
5701 /* Install View {{{ */
5702 @interface InstallView : RVPage {
5703 _transient Database *database_;
5704 NSMutableArray *sections_;
5705 NSMutableArray *filtered_;
5706 UITransitionView *transition_;
5712 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5713 - (void) reloadData;
5718 @implementation InstallView
5721 [list_ setDataSource:nil];
5722 [list_ setDelegate:nil];
5724 [sections_ release];
5725 [filtered_ release];
5726 [transition_ release];
5728 [accessory_ release];
5732 - (int) numberOfRowsInTable:(UITable *)table {
5733 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5736 - (float) table:(UITable *)table heightForRow:(int)row {
5740 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5742 reusing = [[[SectionCell alloc] init] autorelease];
5743 [(SectionCell *)reusing setSection:(editing_ ?
5744 [sections_ objectAtIndex:row] :
5745 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5746 ) editing:editing_];
5750 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5754 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5758 - (void) tableRowSelected:(NSNotification *)notification {
5759 int row = [[notification object] selectedRow];
5770 title = @"All Packages";
5772 section = [filtered_ objectAtIndex:(row - 1)];
5773 name = [section name];
5779 title = @"(No Section)";
5783 PackageTable *table = [[[PackageTable alloc]
5787 filter:@selector(isVisiblyUninstalledInSection:)
5791 [table setDelegate:delegate_];
5793 [book_ pushPage:table];
5796 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5797 if ((self = [super initWithBook:book]) != nil) {
5798 database_ = database;
5800 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5801 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5803 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5804 [self addSubview:transition_];
5806 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5807 [transition_ transition:0 toView:list_];
5809 UITableColumn *column = [[[UITableColumn alloc]
5810 initWithTitle:@"Name"
5812 width:[self frame].size.width
5815 [list_ setDataSource:self];
5816 [list_ setSeparatorStyle:1];
5817 [list_ addTableColumn:column];
5818 [list_ setDelegate:self];
5819 [list_ setReusesTableCells:YES];
5823 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5824 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5828 - (void) reloadData {
5829 NSArray *packages = [database_ packages];
5831 [sections_ removeAllObjects];
5832 [filtered_ removeAllObjects];
5834 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5835 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5838 for (size_t i(0); i != [packages count]; ++i) {
5839 Package *package([packages objectAtIndex:i]);
5840 NSString *name([package section]);
5843 Section *section([sections objectForKey:name]);
5844 if (section == nil) {
5845 section = [[[Section alloc] initWithName:name] autorelease];
5846 [sections setObject:section forKey:name];
5850 if ([package valid] && [package installed] == nil && [package visible])
5851 [filtered addObject:package];
5855 [sections_ addObjectsFromArray:[sections allValues]];
5856 [sections_ sortUsingSelector:@selector(compareByName:)];
5859 [filtered sortUsingSelector:@selector(compareBySection:)];
5862 Section *section = nil;
5863 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5864 Package *package = [filtered objectAtIndex:offset];
5865 NSString *name = [package section];
5867 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5868 section = name == nil ?
5869 [[[Section alloc] initWithName:nil] autorelease] :
5870 [sections objectForKey:name];
5871 [filtered_ addObject:section];
5874 [section addToCount];
5882 - (void) resetView {
5884 [self _rightButtonClicked];
5887 - (void) resetViewAnimated:(BOOL)animated {
5888 [list_ resetViewAnimated:animated];
5891 - (void) _rightButtonClicked {
5892 if ((editing_ = !editing_))
5895 [delegate_ updateData];
5896 [book_ reloadTitleForPage:self];
5897 [book_ reloadButtonsForPage:self];
5900 - (NSString *) title {
5901 return editing_ ? @"Section Visibility" : @"Install by Section";
5904 - (NSString *) backButtonTitle {
5908 - (NSString *) rightButtonTitle {
5909 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5912 - (UINavigationButtonStyle) rightButtonStyle {
5913 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5916 - (UIView *) accessoryView {
5922 /* Changes View {{{ */
5923 @interface ChangesView : RVPage {
5924 _transient Database *database_;
5925 NSMutableArray *packages_;
5926 NSMutableArray *sections_;
5927 UISectionList *list_;
5931 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5932 - (void) reloadData;
5936 @implementation ChangesView
5939 [[list_ table] setDelegate:nil];
5940 [list_ setDataSource:nil];
5942 [packages_ release];
5943 [sections_ release];
5948 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5949 return [sections_ count];
5952 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5953 return [[sections_ objectAtIndex:section] name];
5956 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5957 return [[sections_ objectAtIndex:section] row];
5960 - (int) numberOfRowsInTable:(UITable *)table {
5961 return [packages_ count];
5964 - (float) table:(UITable *)table heightForRow:(int)row {
5965 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5968 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5970 reusing = [[[PackageCell alloc] init] autorelease];
5971 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5975 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5979 - (void) tableRowSelected:(NSNotification *)notification {
5980 int row = [[notification object] selectedRow];
5983 Package *package = [packages_ objectAtIndex:row];
5984 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5985 [view setDelegate:delegate_];
5986 [view setPackage:package];
5987 [book_ pushPage:view];
5990 - (void) _leftButtonClicked {
5991 [(CYBook *)book_ update];
5992 [self reloadButtons];
5995 - (void) _rightButtonClicked {
5996 [delegate_ distUpgrade];
5999 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6000 if ((self = [super initWithBook:book]) != nil) {
6001 database_ = database;
6003 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
6004 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6006 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
6007 [self addSubview:list_];
6009 [list_ setShouldHideHeaderInShortLists:NO];
6010 [list_ setDataSource:self];
6011 //[list_ setSectionListStyle:1];
6013 UITableColumn *column = [[[UITableColumn alloc]
6014 initWithTitle:@"Name"
6016 width:[self frame].size.width
6019 UITable *table = [list_ table];
6020 [table setSeparatorStyle:1];
6021 [table addTableColumn:column];
6022 [table setDelegate:self];
6023 [table setReusesTableCells:YES];
6027 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6028 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6032 - (void) reloadData {
6033 NSArray *packages = [database_ packages];
6035 [packages_ removeAllObjects];
6036 [sections_ removeAllObjects];
6039 for (size_t i(0); i != [packages count]; ++i) {
6040 Package *package([packages objectAtIndex:i]);
6043 [package installed] == nil && [package valid] && [package visible] ||
6044 [package upgradableAndEssential:NO]
6046 [packages_ addObject:package];
6050 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
6053 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
6054 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
6055 Section *section = nil;
6059 bool unseens = false;
6061 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
6064 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6065 Package *package = [packages_ objectAtIndex:offset];
6067 if (![package upgradableAndEssential:YES]) {
6069 NSDate *seen = [package seen];
6071 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6074 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
6075 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6076 [sections_ addObject:section];
6080 [section addToCount];
6081 } else if ([package ignored])
6082 [ignored addToCount];
6085 [upgradable addToCount];
6090 CFRelease(formatter);
6093 Section *last = [sections_ lastObject];
6094 size_t count = [last count];
6095 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6096 [sections_ removeLastObject];
6099 if ([ignored count] != 0)
6100 [sections_ insertObject:ignored atIndex:0];
6102 [sections_ insertObject:upgradable atIndex:0];
6105 [self reloadButtons];
6108 - (void) resetViewAnimated:(BOOL)animated {
6109 [list_ resetViewAnimated:animated];
6112 - (NSString *) leftButtonTitle {
6113 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6116 - (NSString *) rightButtonTitle {
6117 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
6120 - (NSString *) title {
6126 /* Search View {{{ */
6127 @protocol SearchViewDelegate
6128 - (void) showKeyboard:(BOOL)show;
6131 @interface SearchView : RVPage {
6133 UISearchField *field_;
6134 UITransitionView *transition_;
6135 PackageTable *table_;
6136 UIPreferencesTable *advanced_;
6142 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6143 - (void) reloadData;
6147 @implementation SearchView
6150 [field_ setDelegate:nil];
6152 [accessory_ release];
6154 [transition_ release];
6156 [advanced_ release];
6161 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6165 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6167 case 0: return @"Advanced Search (Coming Soon!)";
6169 default: _assert(false);
6173 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6177 default: _assert(false);
6181 - (void) _showKeyboard:(BOOL)show {
6182 CGSize keysize = [UIKeyboard defaultSize];
6183 CGRect keydown = [book_ pageBounds];
6184 CGRect keyup = keydown;
6185 keyup.size.height -= keysize.height - ButtonBarHeight_;
6187 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6189 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6190 [animation setSignificantRectFields:8];
6193 [animation setStartFrame:keydown];
6194 [animation setEndFrame:keyup];
6196 [animation setStartFrame:keyup];
6197 [animation setEndFrame:keydown];
6200 UIAnimator *animator = [UIAnimator sharedAnimator];
6203 addAnimations:[NSArray arrayWithObjects:animation, nil]
6204 withDuration:(KeyboardTime_ - delay)
6209 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6211 [delegate_ showKeyboard:show];
6214 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6215 [self _showKeyboard:YES];
6218 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6219 [self _showKeyboard:NO];
6222 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6224 NSString *text([field_ text]);
6225 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6231 - (void) textFieldClearButtonPressed:(UITextField *)field {
6235 - (void) keyboardInputShouldDelete:(id)input {
6239 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6240 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6244 [field_ resignFirstResponder];
6249 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6250 if ((self = [super initWithBook:book]) != nil) {
6251 CGRect pageBounds = [book_ pageBounds];
6253 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6254 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6255 [self addSubview:pinstripe];*/
6257 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6258 [self addSubview:transition_];
6260 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6262 [advanced_ setReusesTableCells:YES];
6263 [advanced_ setDataSource:self];
6264 [advanced_ reloadData];
6266 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6267 CGColor dimmed(space_, 0, 0, 0, 0.5);
6268 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6270 table_ = [[PackageTable alloc]
6274 filter:@selector(isUnfilteredAndSearchedForBy:)
6278 [table_ setShouldHideHeaderInShortLists:NO];
6279 [transition_ transition:0 toView:table_];
6288 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6295 [self bounds].size.width - area.origin.x - 18;
6297 area.size.height = [UISearchField defaultHeight];
6299 field_ = [[UISearchField alloc] initWithFrame:area];
6301 UIFont *font = [UIFont systemFontOfSize:16];
6302 [field_ setFont:font];
6304 [field_ setPlaceholder:@"Package Names & Descriptions"];
6305 [field_ setDelegate:self];
6307 [field_ setPaddingTop:5];
6309 UITextInputTraits *traits([field_ textInputTraits]);
6310 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6311 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6312 [traits setReturnKeyType:UIReturnKeySearch];
6314 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6316 accessory_ = [[UIView alloc] initWithFrame:accrect];
6317 [accessory_ addSubview:field_];
6319 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6320 [configure setShowPressFeedback:YES];
6321 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6322 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6323 [accessory_ addSubview:configure];*/
6325 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6326 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6332 LKAnimation *animation = [LKTransition animation];
6333 [animation setType:@"oglFlip"];
6334 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6335 [animation setFillMode:@"extended"];
6336 [animation setTransitionFlags:3];
6337 [animation setDuration:10];
6338 [animation setSpeed:0.35];
6339 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6340 [[transition_ _layer] addAnimation:animation forKey:0];
6341 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6342 flipped_ = !flipped_;
6346 - (void) configurePushed {
6347 [field_ resignFirstResponder];
6351 - (void) resetViewAnimated:(BOOL)animated {
6354 [table_ resetViewAnimated:animated];
6357 - (void) _reloadData {
6360 - (void) reloadData {
6363 [table_ setObject:[field_ text]];
6364 [table_ reloadData];
6365 [table_ resetCursor];
6368 - (UIView *) accessoryView {
6372 - (NSString *) title {
6376 - (NSString *) backButtonTitle {
6380 - (void) setDelegate:(id)delegate {
6381 [table_ setDelegate:delegate];
6382 [super setDelegate:delegate];
6388 @interface SettingsView : RVPage {
6389 _transient Database *database_;
6392 UIPreferencesTable *table_;
6393 _UISwitchSlider *subscribedSwitch_;
6394 _UISwitchSlider *ignoredSwitch_;
6395 UIPreferencesControlTableCell *subscribedCell_;
6396 UIPreferencesControlTableCell *ignoredCell_;
6399 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6403 @implementation SettingsView
6406 [table_ setDataSource:nil];
6409 if (package_ != nil)
6412 [subscribedSwitch_ release];
6413 [ignoredSwitch_ release];
6414 [subscribedCell_ release];
6415 [ignoredCell_ release];
6419 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6420 if (package_ == nil)
6426 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6427 if (package_ == nil)
6434 default: _assert(false);
6440 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6441 if (package_ == nil)
6448 default: _assert(false);
6454 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6455 if (package_ == nil)
6462 default: _assert(false);
6468 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6469 if (package_ == nil)
6472 _UISwitchSlider *slider([cell control]);
6473 BOOL value([slider value] != 0);
6474 NSMutableDictionary *metadata([package_ metadata]);
6477 if (NSNumber *number = [metadata objectForKey:key])
6478 before = [number boolValue];
6482 if (value != before) {
6483 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6485 [delegate_ updateData];
6489 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6490 [self onSomething:cell withKey:@"IsSubscribed"];
6493 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6494 [self onSomething:cell withKey:@"IsIgnored"];
6497 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6498 if (package_ == nil)
6502 case 0: switch (row) {
6504 return subscribedCell_;
6506 return ignoredCell_;
6507 default: _assert(false);
6510 case 1: switch (row) {
6512 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6513 [cell setShowSelection:NO];
6514 [cell setTitle:@"Changes only shows upgrades to installed packages so as to minimize spam from packagers. Activate this to see upgrades to this package even when it is not installed."];
6518 default: _assert(false);
6521 default: _assert(false);
6527 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6528 if ((self = [super initWithBook:book])) {
6529 database_ = database;
6530 name_ = [package retain];
6532 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6533 [self addSubview:table_];
6535 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6536 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6538 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6539 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6541 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6542 [subscribedCell_ setShowSelection:NO];
6543 [subscribedCell_ setTitle:@"Show All Changes"];
6544 [subscribedCell_ setControl:subscribedSwitch_];
6546 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6547 [ignoredCell_ setShowSelection:NO];
6548 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6549 [ignoredCell_ setControl:ignoredSwitch_];
6551 [table_ setDataSource:self];
6556 - (void) resetViewAnimated:(BOOL)animated {
6557 [table_ resetViewAnimated:animated];
6560 - (void) reloadData {
6561 if (package_ != nil)
6562 [package_ autorelease];
6563 package_ = [database_ packageWithName:name_];
6564 if (package_ != nil) {
6566 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6567 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6570 [table_ reloadData];
6573 - (NSString *) title {
6579 /* Signature View {{{ */
6580 @interface SignatureView : BrowserView {
6581 _transient Database *database_;
6585 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6589 @implementation SignatureView
6596 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6598 [super webView:sender didClearWindowObject:window forFrame:frame];
6601 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6602 if ((self = [super initWithBook:book]) != nil) {
6603 database_ = database;
6604 package_ = [package retain];
6609 - (void) resetViewAnimated:(BOOL)animated {
6612 - (void) reloadData {
6613 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6619 @interface Cydia : UIApplication <
6620 ConfirmationViewDelegate,
6621 ProgressViewDelegate,
6630 UIToolbar *buttonbar_;
6634 NSMutableArray *essential_;
6635 NSMutableArray *broken_;
6637 Database *database_;
6638 ProgressView *progress_;
6642 UIKeyboard *keyboard_;
6643 UIProgressHUD *hud_;
6645 InstallView *install_;
6646 ChangesView *changes_;
6647 ManageView *manage_;
6648 SearchView *search_;
6653 @implementation Cydia
6656 if ([broken_ count] != 0) {
6657 int count = [broken_ count];
6659 UIActionSheet *sheet = [[[UIActionSheet alloc]
6660 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6661 buttons:[NSArray arrayWithObjects:
6663 @"Ignore (Temporary)",
6665 defaultButtonIndex:0
6670 [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."];
6671 [sheet popupAlertAnimated:YES];
6672 } else if (!Ignored_ && [essential_ count] != 0) {
6673 int count = [essential_ count];
6675 UIActionSheet *sheet = [[[UIActionSheet alloc]
6676 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6677 buttons:[NSArray arrayWithObjects:
6678 @"Upgrade Essential",
6679 @"Complete Upgrade",
6680 @"Ignore (Temporary)",
6682 defaultButtonIndex:0
6687 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6688 [sheet popupAlertAnimated:YES];
6692 - (void) _reloadData {
6693 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6694 [hud setText:@"Reloading Data"];
6695 [overlay_ addSubview:hud];
6698 [database_ reloadData];
6702 [essential_ removeAllObjects];
6703 [broken_ removeAllObjects];
6705 NSArray *packages = [database_ packages];
6706 for (Package *package in packages) {
6708 [broken_ addObject:package];
6709 if ([package upgradableAndEssential:NO]) {
6710 if ([package essential])
6711 [essential_ addObject:package];
6717 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6718 [buttonbar_ setBadgeValue:badge forButton:3];
6719 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6720 [buttonbar_ setBadgeAnimated:YES forButton:3];
6721 [self setApplicationBadge:badge];
6723 [buttonbar_ setBadgeValue:nil forButton:3];
6724 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6725 [buttonbar_ setBadgeAnimated:NO forButton:3];
6726 [self removeApplicationBadge];
6732 if ([packages count] == 0);
6744 [hud removeFromSuperview];*/
6747 - (void) _saveConfig {
6750 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6756 - (void) updateData {
6759 /* XXX: this is just stupid */
6760 if (tag_ != 2 && install_ != nil)
6761 [install_ reloadData];
6762 if (tag_ != 3 && changes_ != nil)
6763 [changes_ reloadData];
6764 if (tag_ != 5 && search_ != nil)
6765 [search_ reloadData];
6775 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6776 _assert(file != NULL);
6778 NSArray *keys = [Sources_ allKeys];
6780 for (int i(0), e([keys count]); i != e; ++i) {
6781 NSString *key = [keys objectAtIndex:i];
6782 NSDictionary *source = [Sources_ objectForKey:key];
6784 fprintf(file, "%s %s %s\n",
6785 [[source objectForKey:@"Type"] UTF8String],
6786 [[source objectForKey:@"URI"] UTF8String],
6787 [[source objectForKey:@"Distribution"] UTF8String]
6796 detachNewThreadSelector:@selector(update_)
6799 title:@"Updating Sources"
6803 - (void) reloadData {
6804 @synchronized (self) {
6805 if (confirm_ == nil)
6811 pkgProblemResolver *resolver = [database_ resolver];
6813 resolver->InstallProtect();
6814 if (!resolver->Resolve(true))
6819 [database_ prepare];
6821 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6822 [confirm_ setDelegate:self];
6824 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6825 [page setDelegate:self];
6827 [confirm_ setPage:page];
6828 [underlay_ popSubview:confirm_];
6831 - (void) installPackage:(Package *)package {
6832 @synchronized (self) {
6839 - (void) removePackage:(Package *)package {
6840 @synchronized (self) {
6847 - (void) distUpgrade {
6848 @synchronized (self) {
6849 [database_ upgrade];
6855 @synchronized (self) {
6857 if (confirm_ != nil) {
6865 [overlay_ removeFromSuperview];
6869 detachNewThreadSelector:@selector(perform)
6876 - (void) bootstrap_ {
6878 [database_ upgrade];
6879 [database_ prepare];
6880 [database_ perform];
6883 - (void) bootstrap {
6885 detachNewThreadSelector:@selector(bootstrap_)
6888 title:@"Bootstrap Install"
6892 - (void) progressViewIsComplete:(ProgressView *)progress {
6893 if (confirm_ != nil) {
6894 [underlay_ addSubview:overlay_];
6895 [confirm_ popFromSuperviewAnimated:NO];
6901 - (void) setPage:(RVPage *)page {
6902 [page resetViewAnimated:NO];
6903 [page setDelegate:self];
6904 [book_ setPage:page];
6907 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6908 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6909 [browser loadURL:url];
6913 - (void) _setHomePage {
6914 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6917 - (void) buttonBarItemTapped:(id)sender {
6918 unsigned tag = [sender tag];
6920 [book_ resetViewAnimated:YES];
6922 } else if (tag_ == 2 && tag != 2)
6923 [install_ resetView];
6926 case 1: [self _setHomePage]; break;
6928 case 2: [self setPage:install_]; break;
6929 case 3: [self setPage:changes_]; break;
6930 case 4: [self setPage:manage_]; break;
6931 case 5: [self setPage:search_]; break;
6933 default: _assert(false);
6939 - (void) applicationWillSuspend {
6941 [super applicationWillSuspend];
6944 - (void) askForSettings {
6945 UIActionSheet *role = [[[UIActionSheet alloc]
6946 initWithTitle:@"Who Are You?"
6947 buttons:[NSArray arrayWithObjects:
6948 @"User (Graphical Only)",
6949 @"Hacker (+ Command Line)",
6950 @"Developer (No Filters)",
6952 defaultButtonIndex:-1
6957 [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."];
6958 [role popupAlertAnimated:YES];
6963 [self setStatusBarShowsProgress:NO];
6966 [hud_ removeFromSuperview];
6970 pid_t pid = ExecFork();
6972 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6973 perror("launchctl stop");
6980 [self askForSettings];
6984 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6986 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6987 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6988 0, 0, screenrect.size.width, screenrect.size.height - 48
6989 ) database:database_];
6991 [book_ setDelegate:self];
6993 [overlay_ addSubview:book_];
6995 NSArray *buttonitems = [NSArray arrayWithObjects:
6996 [NSDictionary dictionaryWithObjectsAndKeys:
6997 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6998 @"home-up.png", kUIButtonBarButtonInfo,
6999 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
7000 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
7001 self, kUIButtonBarButtonTarget,
7002 @"Home", kUIButtonBarButtonTitle,
7003 @"0", kUIButtonBarButtonType,
7006 [NSDictionary dictionaryWithObjectsAndKeys:
7007 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7008 @"install-up.png", kUIButtonBarButtonInfo,
7009 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
7010 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
7011 self, kUIButtonBarButtonTarget,
7012 @"Sections", kUIButtonBarButtonTitle,
7013 @"0", kUIButtonBarButtonType,
7016 [NSDictionary dictionaryWithObjectsAndKeys:
7017 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7018 @"changes-up.png", kUIButtonBarButtonInfo,
7019 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
7020 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
7021 self, kUIButtonBarButtonTarget,
7022 @"Changes", kUIButtonBarButtonTitle,
7023 @"0", kUIButtonBarButtonType,
7026 [NSDictionary dictionaryWithObjectsAndKeys:
7027 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7028 @"manage-up.png", kUIButtonBarButtonInfo,
7029 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
7030 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
7031 self, kUIButtonBarButtonTarget,
7032 @"Manage", kUIButtonBarButtonTitle,
7033 @"0", kUIButtonBarButtonType,
7036 [NSDictionary dictionaryWithObjectsAndKeys:
7037 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7038 @"search-up.png", kUIButtonBarButtonInfo,
7039 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
7040 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
7041 self, kUIButtonBarButtonTarget,
7042 @"Search", kUIButtonBarButtonTitle,
7043 @"0", kUIButtonBarButtonType,
7047 buttonbar_ = [[UIToolbar alloc]
7049 withFrame:CGRectMake(
7050 0, screenrect.size.height - ButtonBarHeight_,
7051 screenrect.size.width, ButtonBarHeight_
7053 withItemList:buttonitems
7056 [buttonbar_ setDelegate:self];
7057 [buttonbar_ setBarStyle:1];
7058 [buttonbar_ setButtonBarTrackingMode:2];
7060 int buttons[5] = {1, 2, 3, 4, 5};
7061 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7062 [buttonbar_ showButtonGroup:0 withDuration:0];
7064 for (int i = 0; i != 5; ++i)
7065 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
7066 i * 64 + 2, 1, 60, ButtonBarHeight_
7069 [buttonbar_ showSelectionForButton:1];
7070 [overlay_ addSubview:buttonbar_];
7072 [UIKeyboard initImplementationNow];
7073 CGSize keysize = [UIKeyboard defaultSize];
7074 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
7075 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
7076 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
7077 [overlay_ addSubview:keyboard_];
7080 [underlay_ addSubview:overlay_];
7084 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
7085 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
7086 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7088 manage_ = (ManageView *) [[self
7089 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7090 withClass:[ManageView class]
7096 [self _setHomePage];
7099 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7100 NSString *context([sheet context]);
7102 if ([context isEqualToString:@"fixhalf"]) {
7105 @synchronized (self) {
7106 for (int i = 0, e = [broken_ count]; i != e; ++i) {
7107 Package *broken = [broken_ objectAtIndex:i];
7110 NSString *id = [broken id];
7111 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7112 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7113 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7114 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7123 [broken_ removeAllObjects];
7132 } else if ([context isEqualToString:@"role"]) {
7134 case 1: Role_ = @"User"; break;
7135 case 2: Role_ = @"Hacker"; break;
7136 case 3: Role_ = @"Developer"; break;
7143 bool reset = Settings_ != nil;
7145 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7149 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7159 } else if ([context isEqualToString:@"upgrade"]) {
7162 @synchronized (self) {
7163 for (int i = 0, e = [essential_ count]; i != e; ++i) {
7164 Package *essential = [essential_ objectAtIndex:i];
7165 [essential install];
7189 - (void) reorganize { _pooled
7190 system("/usr/libexec/cydia/free.sh");
7191 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7194 - (void) applicationSuspend:(__GSEvent *)event {
7195 if (hud_ == nil && ![progress_ isRunning])
7196 [super applicationSuspend:event];
7199 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7201 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7204 - (void) _setSuspended:(BOOL)value {
7206 [super _setSuspended:value];
7209 - (UIProgressHUD *) addProgressHUD {
7210 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7212 [underlay_ addSubview:hud];
7216 - (void) openMailToURL:(NSURL *)url {
7217 // XXX: this makes me sad
7219 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7221 [UIApp openURL:url];
7225 - (RVPage *) pageForPackage:(NSString *)name {
7226 if (Package *package = [database_ packageWithName:name]) {
7227 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7228 [view setPackage:package];
7231 UIActionSheet *sheet = [[[UIActionSheet alloc]
7232 initWithTitle:@"Cannot Locate Package"
7233 buttons:[NSArray arrayWithObjects:@"Close", nil]
7234 defaultButtonIndex:0
7239 [sheet setBodyText:[NSString stringWithFormat:
7240 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7243 [sheet popupAlertAnimated:YES];
7248 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7249 NSString *href = [url absoluteString];
7254 if ([href isEqualToString:@"cydia://add-source"])
7255 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7256 else if ([href isEqualToString:@"cydia://sources"])
7257 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7258 else if ([href isEqualToString:@"cydia://packages"])
7259 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7260 else if ([href hasPrefix:@"cydia://url/"])
7261 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7262 else if ([href hasPrefix:@"cydia://launch/"])
7263 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7264 else if ([href hasPrefix:@"cydia://package-settings/"])
7265 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7266 else if ([href hasPrefix:@"cydia://package-signature/"])
7267 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7268 else if ([href hasPrefix:@"cydia://package/"])
7269 return [self pageForPackage:[href substringFromIndex:16]];
7270 else if ([href hasPrefix:@"cydia://files/"]) {
7271 NSString *name = [href substringFromIndex:14];
7273 if (Package *package = [database_ packageWithName:name]) {
7274 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7275 [files setPackage:package];
7283 - (void) applicationOpenURL:(NSURL *)url {
7284 [super applicationOpenURL:url];
7286 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7287 [self setPage:page];
7288 [buttonbar_ showSelectionForButton:tag];
7293 - (void) applicationDidFinishLaunching:(id)unused {
7294 Font12_ = [[UIFont systemFontOfSize:12] retain];
7295 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7296 Font14_ = [[UIFont systemFontOfSize:14] retain];
7297 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7298 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7300 _assert(pkgInitConfig(*_config));
7301 _assert(pkgInitSystem(*_config, _system));
7305 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7306 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7308 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7310 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7311 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7313 [window_ orderFront:self];
7314 [window_ makeKey:self];
7315 [window_ setHidden:NO];
7317 database_ = [Database sharedInstance];
7318 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7319 [database_ setDelegate:progress_];
7320 [window_ setContentView:progress_];
7322 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7323 [progress_ setContentView:underlay_];
7325 [progress_ resetView];
7328 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7329 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7330 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7331 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7332 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7333 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7334 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7336 [self setIdleTimerDisabled:YES];
7338 hud_ = [self addProgressHUD];
7339 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7341 [self setStatusBarShowsProgress:YES];
7344 detachNewThreadSelector:@selector(reorganize)
7352 /* Web Scripting {{{ */
7353 + (NSString *) webScriptNameForSelector:(SEL)selector {
7354 if (selector == @selector(supports:))
7359 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7360 return selector != @selector(supports:);
7363 - (BOOL) supports:(NSString *)feature {
7364 return [feature isEqualToString:@"window.open"];
7368 - (void) showKeyboard:(BOOL)show {
7369 CGSize keysize = [UIKeyboard defaultSize];
7370 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7371 CGRect keyup = keydown;
7372 keyup.origin.y -= keysize.height;
7374 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7375 [animation setSignificantRectFields:2];
7378 [animation setStartFrame:keydown];
7379 [animation setEndFrame:keyup];
7380 [keyboard_ activate];
7382 [animation setStartFrame:keyup];
7383 [animation setEndFrame:keydown];
7384 [keyboard_ deactivate];
7387 [[UIAnimator sharedAnimator]
7388 addAnimations:[NSArray arrayWithObjects:animation, nil]
7389 withDuration:KeyboardTime_
7394 - (void) slideUp:(UIActionSheet *)alert {
7396 [alert presentSheetFromButtonBar:buttonbar_];
7398 [alert presentSheetInView:overlay_];
7403 void AddPreferences(NSString *plist) { _pooled
7404 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7405 _assert(settings != NULL);
7406 NSMutableArray *items = [settings objectForKey:@"items"];
7410 for (size_t i(0); i != [items count]; ++i) {
7411 NSMutableDictionary *item([items objectAtIndex:i]);
7412 NSString *label = [item objectForKey:@"label"];
7413 if (label != nil && [label isEqualToString:@"Cydia"]) {
7420 for (size_t i(0); i != [items count]; ++i) {
7421 NSDictionary *item([items objectAtIndex:i]);
7422 NSString *label = [item objectForKey:@"label"];
7423 if (label != nil && [label isEqualToString:@"General"]) {
7424 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7425 @"CydiaSettings", @"bundle",
7426 @"PSLinkCell", @"cell",
7427 [NSNumber numberWithBool:YES], @"hasIcon",
7428 [NSNumber numberWithBool:YES], @"isController",
7430 nil] atIndex:(i + 1)];
7436 _assert([settings writeToFile:plist atomically:YES] == YES);
7441 id Alloc_(id self, SEL selector) {
7442 id object = alloc_(self, selector);
7443 lprintf("[%s]A-%p\n", self->isa->name, object);
7448 id Dealloc_(id self, SEL selector) {
7449 id object = dealloc_(self, selector);
7450 lprintf("[%s]D-%p\n", self->isa->name, object);
7454 int main(int argc, char *argv[]) { _pooled
7455 bool substrate(false);
7461 for (int argi(1); argi != argc; ++argi)
7462 if (strcmp(argv[argi], "--") == 0) {
7464 argv[argi] = argv[0];
7470 for (int argi(1); argi != arge; ++argi)
7471 if (strcmp(args[argi], "--bootstrap") == 0)
7473 else if (strcmp(args[argi], "--substrate") == 0)
7476 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7479 App_ = [[NSBundle mainBundle] bundlePath];
7480 Home_ = NSHomeDirectory();
7481 Locale_ = CFLocaleCopyCurrent();
7484 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7485 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7486 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7487 Sounds_Keyboard_ = [keyboard boolValue];
7493 #if 1 /* XXX: this costs 1.4s of startup performance */
7494 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7495 _assert(errno == ENOENT);
7496 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7497 _assert(errno == ENOENT);
7500 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7501 alloc_ = alloc->method_imp;
7502 alloc->method_imp = (IMP) &Alloc_;*/
7504 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7505 dealloc_ = dealloc->method_imp;
7506 dealloc->method_imp = (IMP) &Dealloc_;*/
7511 size = sizeof(maxproc);
7512 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7513 perror("sysctlbyname(\"kern.maxproc\", ?)");
7514 else if (maxproc < 64) {
7516 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7517 perror("sysctlbyname(\"kern.maxproc\", #)");
7520 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7521 char *machine = new char[size];
7522 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7523 perror("sysctlbyname(\"hw.machine\", ?)");
7527 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7529 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7530 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7532 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7533 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7535 Settings_ = [Metadata_ objectForKey:@"Settings"];
7537 Packages_ = [Metadata_ objectForKey:@"Packages"];
7538 Sections_ = [Metadata_ objectForKey:@"Sections"];
7539 Sources_ = [Metadata_ objectForKey:@"Sources"];
7542 if (Settings_ != nil)
7543 Role_ = [Settings_ objectForKey:@"Role"];
7545 if (Packages_ == nil) {
7546 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7547 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7550 if (Sections_ == nil) {
7551 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7552 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7555 if (Sources_ == nil) {
7556 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7557 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7561 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7564 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7565 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7567 if (access("/User", F_OK) != 0)
7568 system("/usr/libexec/cydia/firmware.sh");
7570 _assert([[NSFileManager defaultManager]
7571 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7572 withIntermediateDirectories:YES
7577 space_ = CGColorSpaceCreateDeviceRGB();
7579 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7580 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7581 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7582 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7583 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7584 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7586 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7588 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7590 UIApplicationUseLegacyEvents(YES);
7591 UIKeyboardDisableAutomaticAppearance();
7593 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7595 CGColorSpaceRelease(space_);