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 #include <WebKit/DOMCSSPrimitiveValue.h>
56 #include <WebKit/DOMCSSStyleDeclaration.h>
57 #include <WebKit/DOMDocument.h>
58 #include <WebKit/DOMHTMLBodyElement.h>
59 #include <WebKit/DOMNodeList.h>
60 #include <WebKit/DOMRGBColor.h>
62 #include <WebKit/WebFrame.h>
63 #include <WebKit/WebPolicyDelegate.h>
64 #include <WebKit/WebPreferences.h>
65 #include <WebKit/WebScriptObject.h>
67 #import <WebKit/WebView.h>
68 #import <WebKit/WebView-WebPrivate.h>
70 #include <WebCore/Page.h>
71 #include <WebCore/Settings.h>
73 #import <JavaScriptCore/JavaScriptCore.h>
78 #include <ext/stdio_filebuf.h>
80 #include <apt-pkg/acquire.h>
81 #include <apt-pkg/acquire-item.h>
82 #include <apt-pkg/algorithms.h>
83 #include <apt-pkg/cachefile.h>
84 #include <apt-pkg/clean.h>
85 #include <apt-pkg/configuration.h>
86 #include <apt-pkg/debmetaindex.h>
87 #include <apt-pkg/error.h>
88 #include <apt-pkg/init.h>
89 #include <apt-pkg/mmap.h>
90 #include <apt-pkg/pkgrecords.h>
91 #include <apt-pkg/sha1.h>
92 #include <apt-pkg/sourcelist.h>
93 #include <apt-pkg/sptr.h>
94 #include <apt-pkg/strutl.h>
96 #include <sys/types.h>
98 #include <sys/sysctl.h>
104 #include <mach-o/nlist.h>
114 #import "BrowserView.h"
115 #import "ResetView.h"
117 #import "substrate.h"
120 //#define _finline __attribute__((force_inline))
121 #define _finline inline
126 #define _limit(count) do { \
127 static size_t _count(0); \
128 if (++_count == count) \
132 static uint64_t profile_;
134 #define _timestamp ({ \
136 gettimeofday(&tv, NULL); \
137 tv.tv_sec * 1000000 + tv.tv_usec; \
140 /* Objective-C Handle<> {{{ */
141 template <typename Type_>
143 typedef _H<Type_> This_;
148 _finline void Retain_() {
153 _finline void Clear_() {
159 _finline _H(Type_ *value = NULL, bool mended = false) :
170 _finline This_ &operator =(Type_ *value) {
171 if (value_ != value) {
180 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
182 void NSLogPoint(const char *fix, const CGPoint &point) {
183 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
186 void NSLogRect(const char *fix, const CGRect &rect) {
187 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
190 /* NSForcedOrderingSearch doesn't work on the iPhone */
191 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
192 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
193 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
195 /* iPhoneOS 2.0 Compatibility {{{ */
197 @interface UITextView (iPhoneOS)
198 - (void) setTextSize:(float)size;
201 @implementation UITextView (iPhoneOS)
203 - (void) setTextSize:(float)size {
204 [self setFont:[[self font] fontWithSize:size]];
211 extern NSString * const kCAFilterNearest;
213 /* Information Dictionaries {{{ */
214 @interface NSMutableArray (Cydia)
215 - (void) addInfoDictionary:(NSDictionary *)info;
218 @implementation NSMutableArray (Cydia)
220 - (void) addInfoDictionary:(NSDictionary *)info {
221 [self addObject:info];
226 @interface NSMutableDictionary (Cydia)
227 - (void) addInfoDictionary:(NSDictionary *)info;
230 @implementation NSMutableDictionary (Cydia)
232 - (void) addInfoDictionary:(NSDictionary *)info {
233 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
234 [self setObject:info forKey:bundle];
239 /* Pop Transitions {{{ */
240 @interface PopTransitionView : UITransitionView {
245 @implementation PopTransitionView
247 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
248 if (from != nil && to == nil)
249 [self removeFromSuperview];
254 @interface UIView (PopUpView)
255 - (void) popFromSuperviewAnimated:(BOOL)animated;
256 - (void) popSubview:(UIView *)view;
259 @implementation UIView (PopUpView)
261 - (void) popFromSuperviewAnimated:(BOOL)animated {
262 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
265 - (void) popSubview:(UIView *)view {
266 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
267 [transition setDelegate:transition];
268 [self addSubview:transition];
270 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
271 [transition transition:UITransitionNone toView:blank];
272 [transition transition:UITransitionPushFromBottom toView:view];
278 #define lprintf(args...) fprintf(stderr, args)
281 #define ForSaurik (1 && !ForRelease)
282 #define RecycleWebViews 0
283 #define AlwaysReload (1 && !ForRelease)
286 @interface NSMutableArray (Radix)
287 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
290 @implementation NSMutableArray (Radix)
292 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
293 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
294 [invocation setSelector:selector];
295 [invocation setArgument:&object atIndex:2];
297 size_t count([self count]);
302 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
304 for (size_t i(0); i != count; ++i) {
305 RadixItem &item(lhs[i]);
308 id object([self objectAtIndex:i]);
309 [invocation setTarget:object];
312 [invocation getReturnValue:&item.key];
315 static const size_t width = 32;
316 static const size_t bits = 11;
317 static const size_t slots = 1 << bits;
318 static const size_t passes = (width + (bits - 1)) / bits;
320 size_t *hist(new size_t[slots]);
322 for (size_t pass(0); pass != passes; ++pass) {
323 memset(hist, 0, sizeof(size_t) * slots);
325 for (size_t i(0); i != count; ++i) {
326 uint32_t key(lhs[i].key);
328 key &= _not(uint32_t) >> width - bits;
333 for (size_t i(0); i != slots; ++i) {
334 size_t local(offset);
339 for (size_t i(0); i != count; ++i) {
340 uint32_t key(lhs[i].key);
342 key &= _not(uint32_t) >> width - bits;
343 rhs[hist[key]++] = lhs[i];
353 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
354 for (size_t i(0); i != count; ++i)
355 [values addObject:[self objectAtIndex:lhs[i].index]];
356 [self setArray:values];
364 /* Apple Bug Fixes {{{ */
365 @implementation UIWebDocumentView (Cydia)
367 - (void) _setScrollerOffset:(CGPoint)offset {
368 UIScroller *scroller([self _scroller]);
370 CGSize size([scroller contentSize]);
371 CGSize bounds([scroller bounds].size);
374 max.x = size.width - bounds.width;
375 max.y = size.height - bounds.height;
383 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
384 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
386 [scroller setOffset:offset];
393 kUIControlEventMouseDown = 1 << 0,
394 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
395 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
396 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
397 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
398 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
399 } UIControlEventMasks;
401 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
402 size_t length([self length] - state->state);
405 else if (length > count)
407 for (size_t i(0); i != length; ++i)
408 objects[i] = [self item:state->state++];
409 state->itemsPtr = objects;
410 state->mutationsPtr = (unsigned long *) self;
414 @interface NSString (UIKit)
415 - (NSString *) stringByAddingPercentEscapes;
416 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
419 @interface NSString (Cydia)
420 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
421 - (NSComparisonResult) compareByPath:(NSString *)other;
424 @implementation NSString (Cydia)
426 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
427 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
430 - (NSComparisonResult) compareByPath:(NSString *)other {
431 NSString *prefix = [self commonPrefixWithString:other options:0];
432 size_t length = [prefix length];
434 NSRange lrange = NSMakeRange(length, [self length] - length);
435 NSRange rrange = NSMakeRange(length, [other length] - length);
437 lrange = [self rangeOfString:@"/" options:0 range:lrange];
438 rrange = [other rangeOfString:@"/" options:0 range:rrange];
440 NSComparisonResult value;
442 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
443 value = NSOrderedSame;
444 else if (lrange.location == NSNotFound)
445 value = NSOrderedAscending;
446 else if (rrange.location == NSNotFound)
447 value = NSOrderedDescending;
449 value = NSOrderedSame;
451 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
452 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
453 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
454 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
456 NSComparisonResult result = [lpath compare:rpath];
457 return result == NSOrderedSame ? value : result;
462 /* Perl-Compatible RegEx {{{ */
472 Pcre(const char *regex) :
477 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
480 lprintf("%d:%s\n", offset, error);
484 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
485 matches_ = new int[(capture_ + 1) * 3];
493 NSString *operator [](size_t match) {
494 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
497 bool operator ()(NSString *data) {
498 // XXX: length is for characters, not for bytes
499 return operator ()([data UTF8String], [data length]);
502 bool operator ()(const char *data, size_t size) {
504 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
508 /* Mime Addresses {{{ */
509 @interface Address : NSObject {
515 - (NSString *) address;
517 + (Address *) addressWithString:(NSString *)string;
518 - (Address *) initWithString:(NSString *)string;
521 @implementation Address
530 - (NSString *) name {
534 - (NSString *) address {
538 + (Address *) addressWithString:(NSString *)string {
539 return [[[Address alloc] initWithString:string] autorelease];
542 + (NSArray *) _attributeKeys {
543 return [NSArray arrayWithObjects:@"address", @"name", nil];
546 - (NSArray *) attributeKeys {
547 return [[self class] _attributeKeys];
550 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
551 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
554 - (Address *) initWithString:(NSString *)string {
555 if ((self = [super init]) != nil) {
556 const char *data = [string UTF8String];
557 size_t size = [string length];
559 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
561 if (address_r(data, size)) {
562 name_ = [address_r[1] retain];
563 address_ = [address_r[2] retain];
565 name_ = [string retain];
573 /* CoreGraphics Primitives {{{ */
584 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
587 Set(space, red, green, blue, alpha);
592 CGColorRelease(color_);
599 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
601 float color[] = {red, green, blue, alpha};
602 color_ = CGColorCreate(space, color);
605 operator CGColorRef() {
611 extern "C" void UISetColor(CGColorRef color);
613 /* Random Global Variables {{{ */
614 static const int PulseInterval_ = 50000;
615 static const int ButtonBarHeight_ = 48;
616 static const float KeyboardTime_ = 0.3f;
618 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
619 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
621 static CGColor Blue_;
622 static CGColor Blueish_;
623 static CGColor Black_;
625 static CGColor White_;
626 static CGColor Gray_;
628 static NSString *App_;
629 static NSString *Home_;
630 static BOOL Sounds_Keyboard_;
632 static BOOL Advanced_;
636 static BOOL Ignored_;
638 static UIFont *Font12_;
639 static UIFont *Font12Bold_;
640 static UIFont *Font14_;
641 static UIFont *Font18Bold_;
642 static UIFont *Font22Bold_;
644 static const char *Machine_ = NULL;
645 static const NSString *UniqueID_ = NULL;
652 CGColorSpaceRef space_;
654 #define FW_LEAST(major, minor, bugfix) \
655 (major < Major_ || major == Major_ && \
656 (minor < Minor_ || minor == Minor_ && \
662 static NSDictionary *SectionMap_;
663 static NSMutableDictionary *Metadata_;
664 static NSMutableDictionary *Indices_;
665 static _transient NSMutableDictionary *Settings_;
666 static _transient NSString *Role_;
667 static _transient NSMutableDictionary *Packages_;
668 static _transient NSMutableDictionary *Sections_;
669 static _transient NSMutableDictionary *Sources_;
670 static bool Changed_;
674 static NSMutableArray *Documents_;
677 NSString *GetLastUpdate() {
678 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
681 return @"Never or Unknown";
683 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
684 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
686 CFRelease(formatter);
688 return [(NSString *) formatted autorelease];
691 /* Display Helpers {{{ */
692 inline float Interpolate(float begin, float end, float fraction) {
693 return (end - begin) * fraction + begin;
696 NSString *SizeString(double size) {
697 bool negative = size < 0;
702 while (size > 1024) {
707 static const char *powers_[] = {"B", "kB", "MB", "GB"};
709 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
712 NSString *StripVersion(NSString *version) {
713 NSRange colon = [version rangeOfString:@":"];
714 if (colon.location != NSNotFound)
715 version = [version substringFromIndex:(colon.location + 1)];
719 NSString *Simplify(NSString *title) {
720 const char *data = [title UTF8String];
721 size_t size = [title length];
723 static Pcre square_r("^\\[(.*)\\]$");
724 if (square_r(data, size))
725 return Simplify(square_r[1]);
727 static Pcre paren_r("^\\((.*)\\)$");
728 if (paren_r(data, size))
729 return Simplify(paren_r[1]);
731 static Pcre title_r("^(.*?) \\(.*\\)$");
732 if (title_r(data, size))
733 return Simplify(title_r[1]);
739 bool isSectionVisible(NSString *section) {
740 NSDictionary *metadata = [Sections_ objectForKey:section];
741 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
742 return hidden == nil || ![hidden boolValue];
745 /* Delegate Prototypes {{{ */
749 @interface NSObject (ProgressDelegate)
752 @implementation NSObject(ProgressDelegate)
754 - (void) _setProgressError:(NSArray *)args {
755 [self performSelector:@selector(setProgressError:forPackage:)
756 withObject:[args objectAtIndex:0]
757 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
763 @protocol ProgressDelegate
764 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
765 - (void) setProgressTitle:(NSString *)title;
766 - (void) setProgressPercent:(float)percent;
767 - (void) startProgress;
768 - (void) addProgressOutput:(NSString *)output;
769 - (bool) isCancelling:(size_t)received;
772 @protocol ConfigurationDelegate
773 - (void) repairWithSelector:(SEL)selector;
774 - (void) setConfigurationData:(NSString *)data;
777 @protocol CydiaDelegate
778 - (void) installPackage:(Package *)package;
779 - (void) removePackage:(Package *)package;
780 - (void) slideUp:(UIActionSheet *)alert;
781 - (void) distUpgrade;
784 - (void) askForSettings;
785 - (UIProgressHUD *) addProgressHUD;
786 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
787 - (RVPage *) pageForPackage:(NSString *)name;
788 - (void) openMailToURL:(NSURL *)url;
789 - (void) clearFirstResponder;
793 /* Status Delegation {{{ */
795 public pkgAcquireStatus
798 _transient NSObject<ProgressDelegate> *delegate_;
806 void setDelegate(id delegate) {
807 delegate_ = delegate;
810 virtual bool MediaChange(std::string media, std::string drive) {
814 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
817 virtual void Fetch(pkgAcquire::ItemDesc &item) {
818 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
819 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
822 virtual void Done(pkgAcquire::ItemDesc &item) {
825 virtual void Fail(pkgAcquire::ItemDesc &item) {
827 item.Owner->Status == pkgAcquire::Item::StatIdle ||
828 item.Owner->Status == pkgAcquire::Item::StatDone
832 std::string &error(item.Owner->ErrorText);
836 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
837 NSArray *fields([description componentsSeparatedByString:@" "]);
838 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
840 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
841 withObject:[NSArray arrayWithObjects:
842 [NSString stringWithUTF8String:error.c_str()],
849 virtual bool Pulse(pkgAcquire *Owner) {
850 bool value = pkgAcquireStatus::Pulse(Owner);
853 double(CurrentBytes + CurrentItems) /
854 double(TotalBytes + TotalItems)
857 [delegate_ setProgressPercent:percent];
858 return [delegate_ isCancelling:CurrentBytes] ? false : value;
861 virtual void Start() {
862 [delegate_ startProgress];
865 virtual void Stop() {
869 /* Progress Delegation {{{ */
874 _transient id<ProgressDelegate> delegate_;
877 virtual void Update() {
878 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
879 [delegate_ setProgressPercent:(Percent / 100)];
888 void setDelegate(id delegate) {
889 delegate_ = delegate;
892 virtual void Done() {
893 [delegate_ setProgressPercent:1];
898 /* Database Interface {{{ */
899 @interface Database : NSObject {
901 pkgDepCache::Policy *policy_;
902 pkgRecords *records_;
903 pkgProblemResolver *resolver_;
904 pkgAcquire *fetcher_;
906 SPtr<pkgPackageManager> manager_;
907 pkgSourceList *list_;
909 NSMutableDictionary *sources_;
910 NSMutableArray *packages_;
912 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
921 + (Database *) sharedInstance;
923 - (void) _readCydia:(NSNumber *)fd;
924 - (void) _readStatus:(NSNumber *)fd;
925 - (void) _readOutput:(NSNumber *)fd;
929 - (Package *) packageWithName:(NSString *)name;
931 - (pkgCacheFile &) cache;
932 - (pkgDepCache::Policy *) policy;
933 - (pkgRecords *) records;
934 - (pkgProblemResolver *) resolver;
935 - (pkgAcquire &) fetcher;
936 - (NSArray *) packages;
937 - (NSArray *) sources;
946 - (void) updateWithStatus:(Status &)status;
948 - (void) setDelegate:(id)delegate;
949 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
953 /* Source Class {{{ */
954 @interface Source : NSObject {
955 NSString *description_;
960 NSString *distribution_;
964 NSString *defaultIcon_;
966 NSDictionary *record_;
970 - (Source *) initWithMetaIndex:(metaIndex *)index;
972 - (NSComparisonResult) compareByNameAndType:(Source *)source;
974 - (NSDictionary *) record;
978 - (NSString *) distribution;
984 - (NSString *) description;
985 - (NSString *) label;
986 - (NSString *) origin;
987 - (NSString *) version;
989 - (NSString *) defaultIcon;
993 @implementation Source
997 [distribution_ release];
1000 if (description_ != nil)
1001 [description_ release];
1006 if (version_ != nil)
1008 if (defaultIcon_ != nil)
1009 [defaultIcon_ release];
1016 + (NSArray *) _attributeKeys {
1017 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1020 - (NSArray *) attributeKeys {
1021 return [[self class] _attributeKeys];
1024 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1025 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1028 - (Source *) initWithMetaIndex:(metaIndex *)index {
1029 if ((self = [super init]) != nil) {
1030 trusted_ = index->IsTrusted();
1032 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1033 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1034 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1036 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1037 if (dindex != NULL) {
1038 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1040 while (std::getline(release, line)) {
1041 std::string::size_type colon(line.find(':'));
1042 if (colon == std::string::npos)
1045 std::string name(line.substr(0, colon));
1046 std::string value(line.substr(colon + 1));
1047 while (!value.empty() && value[0] == ' ')
1048 value = value.substr(1);
1050 if (name == "Default-Icon")
1051 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1052 else if (name == "Description")
1053 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1054 else if (name == "Label")
1055 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1056 else if (name == "Origin")
1057 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1058 else if (name == "Version")
1059 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1063 record_ = [Sources_ objectForKey:[self key]];
1065 record_ = [record_ retain];
1069 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1070 NSDictionary *lhr = [self record];
1071 NSDictionary *rhr = [source record];
1074 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1076 NSString *lhs = [self name];
1077 NSString *rhs = [source name];
1079 if ([lhs length] != 0 && [rhs length] != 0) {
1080 unichar lhc = [lhs characterAtIndex:0];
1081 unichar rhc = [rhs characterAtIndex:0];
1083 if (isalpha(lhc) && !isalpha(rhc))
1084 return NSOrderedAscending;
1085 else if (!isalpha(lhc) && isalpha(rhc))
1086 return NSOrderedDescending;
1089 return [lhs compare:rhs options:LaxCompareOptions_];
1092 - (NSDictionary *) record {
1100 - (NSString *) uri {
1104 - (NSString *) distribution {
1105 return distribution_;
1108 - (NSString *) type {
1112 - (NSString *) key {
1113 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1116 - (NSString *) host {
1117 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1120 - (NSString *) name {
1121 return origin_ == nil ? [self host] : origin_;
1124 - (NSString *) description {
1125 return description_;
1128 - (NSString *) label {
1129 return label_ == nil ? [self host] : label_;
1132 - (NSString *) origin {
1136 - (NSString *) version {
1140 - (NSString *) defaultIcon {
1141 return defaultIcon_;
1146 /* Relationship Class {{{ */
1147 @interface Relationship : NSObject {
1152 - (NSString *) type;
1154 - (NSString *) name;
1158 @implementation Relationship
1166 - (NSString *) type {
1174 - (NSString *) name {
1181 /* Package Class {{{ */
1182 @interface Package : NSObject {
1183 pkgCache::PkgIterator iterator_;
1184 _transient Database *database_;
1185 pkgCache::VerIterator version_;
1186 pkgCache::VerFileIterator file_;
1194 NSString *installed_;
1200 NSString *depiction_;
1201 NSString *homepage_;
1207 NSArray *relationships_;
1210 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1211 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1213 - (pkgCache::PkgIterator) iterator;
1215 - (NSString *) section;
1216 - (NSString *) simpleSection;
1218 - (Address *) maintainer;
1220 - (NSString *) description;
1221 - (NSString *) index;
1223 - (NSMutableDictionary *) metadata;
1225 - (BOOL) subscribed;
1228 - (NSString *) latest;
1229 - (NSString *) installed;
1232 - (BOOL) upgradableAndEssential:(BOOL)essential;
1235 - (BOOL) unfiltered;
1239 - (BOOL) halfConfigured;
1240 - (BOOL) halfInstalled;
1242 - (NSString *) mode;
1245 - (NSString *) name;
1246 - (NSString *) tagline;
1248 - (NSString *) homepage;
1249 - (NSString *) depiction;
1250 - (Address *) author;
1252 - (NSArray *) files;
1253 - (NSArray *) relationships;
1254 - (NSArray *) warnings;
1255 - (NSArray *) applications;
1257 - (Source *) source;
1258 - (NSString *) role;
1259 - (NSString *) rating;
1261 - (BOOL) matches:(NSString *)text;
1263 - (bool) hasSupportingRole;
1264 - (BOOL) hasTag:(NSString *)tag;
1265 - (NSString *) primaryPurpose;
1266 - (NSArray *) purposes;
1268 - (NSComparisonResult) compareByName:(Package *)package;
1269 - (NSComparisonResult) compareBySection:(Package *)package;
1271 - (uint32_t) compareForChanges;
1276 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1277 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1278 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1279 - (NSNumber *) isVisibleInSource:(Source *)source;
1283 @implementation Package
1289 if (section_ != nil)
1293 if (installed_ != nil)
1294 [installed_ release];
1302 if (depiction_ != nil)
1303 [depiction_ release];
1304 if (homepage_ != nil)
1305 [homepage_ release];
1306 if (sponsor_ != nil)
1315 if (relationships_ != nil)
1316 [relationships_ release];
1321 + (NSArray *) _attributeKeys {
1322 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1325 - (NSArray *) attributeKeys {
1326 return [[self class] _attributeKeys];
1329 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1330 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1333 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1334 if ((self = [super init]) != nil) {
1335 iterator_ = iterator;
1336 database_ = database;
1338 version_ = [database_ policy]->GetCandidateVer(iterator_);
1339 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1340 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1342 pkgCache::VerIterator current = iterator_.CurrentVer();
1343 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1344 installed_ = [StripVersion(installed) retain];
1346 if (!version_.end())
1347 file_ = version_.FileList();
1349 pkgCache &cache([database_ cache]);
1350 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1353 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1356 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1358 const char *begin, *end;
1359 parser->GetRec(begin, end);
1361 NSString *website(nil);
1362 NSString *sponsor(nil);
1363 NSString *author(nil);
1372 {"depiction", &depiction_},
1373 {"homepage", &homepage_},
1374 {"website", &website},
1375 {"sponsor", &sponsor},
1376 {"author", &author},
1380 while (begin != end)
1381 if (*begin == '\n') {
1384 } else if (isblank(*begin)) next: {
1385 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1388 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1389 const char *name(begin);
1390 size_t size(colon - begin);
1392 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1395 const char *stop(begin == NULL ? end : begin);
1396 while (stop[-1] == '\r')
1398 while (++colon != stop && isblank(*colon));
1400 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1401 if (strncasecmp(names[i].name_, name, size) == 0) {
1402 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1403 *names[i].value_ = value;
1414 name_ = [name_ retain];
1415 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1417 icon_ = [icon_ retain];
1418 if (depiction_ != nil)
1419 depiction_ = [depiction_ retain];
1420 if (homepage_ == nil)
1421 homepage_ = website;
1422 if ([homepage_ isEqualToString:depiction_])
1424 if (homepage_ != nil)
1425 homepage_ = [homepage_ retain];
1427 sponsor_ = [[Address addressWithString:sponsor] retain];
1429 author_ = [[Address addressWithString:author] retain];
1431 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1435 for (int i(0), e([tags_ count]); i != e; ++i) {
1436 NSString *tag = [tags_ objectAtIndex:i];
1437 if ([tag hasPrefix:@"role::"]) {
1438 role_ = [[tag substringFromIndex:6] retain];
1443 NSString *solid(latest == nil ? installed : latest);
1444 bool changed(false);
1446 NSString *key([id_ lowercaseString]);
1448 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1449 if (metadata == nil) {
1450 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1455 [metadata setObject:solid forKey:@"LastVersion"];
1458 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1459 NSDate *last([metadata objectForKey:@"LastSeen"]);
1460 NSString *version([metadata objectForKey:@"LastVersion"]);
1463 first = last == nil ? now_ : last;
1464 [metadata setObject:first forKey:@"FirstSeen"];
1469 if (version == nil) {
1470 [metadata setObject:solid forKey:@"LastVersion"];
1472 } else if (![version isEqualToString:solid]) {
1473 [metadata setObject:solid forKey:@"LastVersion"];
1475 [metadata setObject:last forKey:@"LastSeen"];
1481 [Packages_ setObject:metadata forKey:key];
1487 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1488 return [[[Package alloc]
1489 initWithIterator:iterator
1494 - (pkgCache::PkgIterator) iterator {
1498 - (NSString *) section {
1499 if (section_ != nil)
1502 const char *section = iterator_.Section();
1503 if (section == NULL)
1506 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1509 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1510 if (NSString *rename = [value objectForKey:@"Rename"]) {
1515 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1519 - (NSString *) simpleSection {
1520 if (NSString *section = [self section])
1521 return Simplify(section);
1527 - (Address *) maintainer {
1530 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1531 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1535 return version_.end() ? 0 : version_->InstalledSize;
1538 - (NSString *) description {
1541 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1542 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1544 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1545 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1546 if ([lines count] < 2)
1549 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1550 for (size_t i(1); i != [lines count]; ++i) {
1551 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1552 [trimmed addObject:trim];
1555 return [trimmed componentsJoinedByString:@"\n"];
1558 - (NSString *) index {
1559 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1560 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1563 - (NSMutableDictionary *) metadata {
1564 return [Packages_ objectForKey:[id_ lowercaseString]];
1568 NSDictionary *metadata([self metadata]);
1569 if ([self subscribed])
1570 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1572 return [metadata objectForKey:@"FirstSeen"];
1575 - (BOOL) subscribed {
1576 NSDictionary *metadata([self metadata]);
1577 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1578 return [subscribed boolValue];
1584 NSDictionary *metadata([self metadata]);
1585 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1586 return [ignored boolValue];
1591 - (NSString *) latest {
1595 - (NSString *) installed {
1600 return !version_.end();
1603 - (BOOL) upgradableAndEssential:(BOOL)essential {
1604 pkgCache::VerIterator current = iterator_.CurrentVer();
1607 return essential && [self essential];
1609 return !version_.end() && version_ != current;
1612 - (BOOL) essential {
1613 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1617 return [database_ cache][iterator_].InstBroken();
1620 - (BOOL) unfiltered {
1621 NSString *section = [self section];
1622 return section == nil || isSectionVisible(section);
1626 return [self hasSupportingRole] && [self unfiltered];
1630 unsigned char current = iterator_->CurrentState;
1631 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1634 - (BOOL) halfConfigured {
1635 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1638 - (BOOL) halfInstalled {
1639 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1643 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1644 return state.Mode != pkgDepCache::ModeKeep;
1647 - (NSString *) mode {
1648 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1650 switch (state.Mode) {
1651 case pkgDepCache::ModeDelete:
1652 if ((state.iFlags & pkgDepCache::Purge) != 0)
1656 case pkgDepCache::ModeKeep:
1657 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1661 case pkgDepCache::ModeInstall:
1662 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1663 return @"Reinstall";
1664 else switch (state.Status) {
1666 return @"Downgrade";
1672 return @"New Install";
1685 - (NSString *) name {
1686 return name_ == nil ? id_ : name_;
1689 - (NSString *) tagline {
1693 - (UIImage *) icon {
1694 NSString *section = [self simpleSection];
1697 if (NSString *icon = icon_)
1698 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1699 if (icon == nil) if (section != nil)
1700 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1701 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1702 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1704 icon = [UIImage applicationImageNamed:@"unknown.png"];
1708 - (NSString *) homepage {
1712 - (NSString *) depiction {
1716 - (Address *) sponsor {
1720 - (Address *) author {
1724 - (NSArray *) files {
1725 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1726 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1729 fin.open([path UTF8String]);
1734 while (std::getline(fin, line))
1735 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1740 - (NSArray *) relationships {
1741 return relationships_;
1744 - (NSArray *) warnings {
1745 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1746 const char *name(iterator_.Name());
1748 size_t length(strlen(name));
1749 if (length < 2) invalid:
1750 [warnings addObject:@"illegal package identifier"];
1751 else for (size_t i(0); i != length; ++i)
1753 (name[i] < 'a' || name[i] > 'z') &&
1754 (name[i] < '0' || name[i] > '9') &&
1755 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1758 if (strcmp(name, "cydia") != 0) {
1762 if (NSArray *files = [self files])
1763 for (NSString *file in files)
1764 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1766 else if (!stash && [file isEqualToString:@"/var/stash"])
1770 [warnings addObject:@"files installed into Cydia.app"];
1772 [warnings addObject:@"files installed to /var/stash"];
1775 return [warnings count] == 0 ? nil : warnings;
1778 - (NSArray *) applications {
1779 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1781 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1783 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1784 if (NSArray *files = [self files])
1785 for (NSString *file in files)
1786 if (application_r(file)) {
1787 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1788 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1789 if ([id isEqualToString:me])
1792 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1794 display = application_r[1];
1796 NSString *bundle([file stringByDeletingLastPathComponent]);
1797 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1798 if (icon == nil || [icon length] == 0)
1800 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1802 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1803 [applications addObject:application];
1805 [application addObject:id];
1806 [application addObject:display];
1807 [application addObject:url];
1810 return [applications count] == 0 ? nil : applications;
1813 - (Source *) source {
1815 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1822 - (NSString *) role {
1826 - (NSString *) rating {
1827 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1828 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1833 - (BOOL) matches:(NSString *)text {
1839 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1840 if (range.location != NSNotFound)
1843 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1844 if (range.location != NSNotFound)
1847 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1848 if (range.location != NSNotFound)
1854 - (bool) hasSupportingRole {
1857 if ([role_ isEqualToString:@"enduser"])
1859 if ([Role_ isEqualToString:@"User"])
1861 if ([role_ isEqualToString:@"hacker"])
1863 if ([Role_ isEqualToString:@"Hacker"])
1865 if ([role_ isEqualToString:@"developer"])
1867 if ([Role_ isEqualToString:@"Developer"])
1872 - (BOOL) hasTag:(NSString *)tag {
1873 return tags_ == nil ? NO : [tags_ containsObject:tag];
1876 - (NSString *) primaryPurpose {
1877 for (NSString *tag in tags_)
1878 if ([tag hasPrefix:@"purpose::"])
1879 return [tag substringFromIndex:9];
1883 - (NSArray *) purposes {
1884 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1885 for (NSString *tag in tags_)
1886 if ([tag hasPrefix:@"purpose::"])
1887 [purposes addObject:[tag substringFromIndex:9]];
1888 return [purposes count] == 0 ? nil : purposes;
1891 - (NSComparisonResult) compareByName:(Package *)package {
1892 NSString *lhs = [self name];
1893 NSString *rhs = [package name];
1895 if ([lhs length] != 0 && [rhs length] != 0) {
1896 unichar lhc = [lhs characterAtIndex:0];
1897 unichar rhc = [rhs characterAtIndex:0];
1899 if (isalpha(lhc) && !isalpha(rhc))
1900 return NSOrderedAscending;
1901 else if (!isalpha(lhc) && isalpha(rhc))
1902 return NSOrderedDescending;
1905 return [lhs compare:rhs options:LaxCompareOptions_];
1908 - (NSComparisonResult) compareBySection:(Package *)package {
1909 NSString *lhs = [self section];
1910 NSString *rhs = [package section];
1912 if (lhs == NULL && rhs != NULL)
1913 return NSOrderedAscending;
1914 else if (lhs != NULL && rhs == NULL)
1915 return NSOrderedDescending;
1916 else if (lhs != NULL && rhs != NULL) {
1917 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1918 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1921 return NSOrderedSame;
1924 - (uint32_t) compareForChanges {
1929 uint32_t timestamp : 30;
1930 uint32_t ignored : 1;
1931 uint32_t upgradable : 1;
1935 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1937 if ([self upgradableAndEssential:YES]) {
1938 value.bits.timestamp = 0;
1939 value.bits.ignored = [self ignored] ? 0 : 1;
1940 value.bits.upgradable = 1;
1942 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1943 value.bits.ignored = 0;
1944 value.bits.upgradable = 0;
1947 return _not(uint32_t) - value.key;
1951 pkgProblemResolver *resolver = [database_ resolver];
1952 resolver->Clear(iterator_);
1953 resolver->Protect(iterator_);
1954 pkgCacheFile &cache([database_ cache]);
1955 cache->MarkInstall(iterator_, false);
1956 pkgDepCache::StateCache &state((*cache)[iterator_]);
1957 if (!state.Install())
1958 cache->SetReInstall(iterator_, true);
1962 pkgProblemResolver *resolver = [database_ resolver];
1963 resolver->Clear(iterator_);
1964 resolver->Protect(iterator_);
1965 resolver->Remove(iterator_);
1966 [database_ cache]->MarkDelete(iterator_, true);
1969 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1970 return [NSNumber numberWithBool:(
1971 [self unfiltered] && [self matches:search]
1975 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1976 return [NSNumber numberWithBool:(
1977 (![number boolValue] || [self visible]) && [self installed] != nil
1981 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1982 NSString *section = [self section];
1984 return [NSNumber numberWithBool:(
1986 [self installed] == nil && (
1988 section == nil && [name length] == 0 ||
1989 [name isEqualToString:section]
1994 - (NSNumber *) isVisibleInSource:(Source *)source {
1995 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2000 /* Section Class {{{ */
2001 @interface Section : NSObject {
2007 - (NSComparisonResult) compareByName:(Section *)section;
2008 - (Section *) initWithName:(NSString *)name;
2009 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2010 - (NSString *) name;
2013 - (void) addToCount;
2017 @implementation Section
2024 - (NSComparisonResult) compareByName:(Section *)section {
2025 NSString *lhs = [self name];
2026 NSString *rhs = [section name];
2028 if ([lhs length] != 0 && [rhs length] != 0) {
2029 unichar lhc = [lhs characterAtIndex:0];
2030 unichar rhc = [rhs characterAtIndex:0];
2032 if (isalpha(lhc) && !isalpha(rhc))
2033 return NSOrderedAscending;
2034 else if (!isalpha(lhc) && isalpha(rhc))
2035 return NSOrderedDescending;
2038 return [lhs compare:rhs options:LaxCompareOptions_];
2041 - (Section *) initWithName:(NSString *)name {
2042 return [self initWithName:name row:0];
2045 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2046 if ((self = [super init]) != nil) {
2047 name_ = [name retain];
2052 - (NSString *) name {
2064 - (void) addToCount {
2072 static NSArray *Finishes_;
2074 /* Database Implementation {{{ */
2075 @implementation Database
2077 + (Database *) sharedInstance {
2078 static Database *instance;
2079 if (instance == nil)
2080 instance = [[Database alloc] init];
2089 - (void) _readCydia:(NSNumber *)fd { _pooled
2090 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2091 std::istream is(&ib);
2094 static Pcre finish_r("^finish:([^:]*)$");
2096 while (std::getline(is, line)) {
2097 const char *data(line.c_str());
2098 size_t size = line.size();
2099 lprintf("C:%s\n", data);
2101 if (finish_r(data, size)) {
2102 NSString *finish = finish_r[1];
2103 int index = [Finishes_ indexOfObject:finish];
2104 if (index != INT_MAX && index > Finish_)
2112 - (void) _readStatus:(NSNumber *)fd { _pooled
2113 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2114 std::istream is(&ib);
2117 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2118 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2120 while (std::getline(is, line)) {
2121 const char *data(line.c_str());
2122 size_t size = line.size();
2123 lprintf("S:%s\n", data);
2125 if (conffile_r(data, size)) {
2126 [delegate_ setConfigurationData:conffile_r[1]];
2127 } else if (strncmp(data, "status: ", 8) == 0) {
2128 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2129 [delegate_ setProgressTitle:string];
2130 } else if (pmstatus_r(data, size)) {
2131 std::string type([pmstatus_r[1] UTF8String]);
2132 NSString *id = pmstatus_r[2];
2134 float percent([pmstatus_r[3] floatValue]);
2135 [delegate_ setProgressPercent:(percent / 100)];
2137 NSString *string = pmstatus_r[4];
2139 if (type == "pmerror")
2140 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2141 withObject:[NSArray arrayWithObjects:string, id, nil]
2144 else if (type == "pmstatus") {
2145 [delegate_ setProgressTitle:string];
2146 } else if (type == "pmconffile")
2147 [delegate_ setConfigurationData:string];
2148 else _assert(false);
2149 } else _assert(false);
2155 - (void) _readOutput:(NSNumber *)fd { _pooled
2156 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2157 std::istream is(&ib);
2160 while (std::getline(is, line)) {
2161 lprintf("O:%s\n", line.c_str());
2162 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2172 - (Package *) packageWithName:(NSString *)name {
2173 if (static_cast<pkgDepCache *>(cache_) == NULL)
2175 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2176 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2179 - (Database *) init {
2180 if ((self = [super init]) != nil) {
2187 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2188 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2192 _assert(pipe(fds) != -1);
2195 _config->Set("APT::Keep-Fds::", cydiafd_);
2196 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2199 detachNewThreadSelector:@selector(_readCydia:)
2201 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2204 _assert(pipe(fds) != -1);
2208 detachNewThreadSelector:@selector(_readStatus:)
2210 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2213 _assert(pipe(fds) != -1);
2214 _assert(dup2(fds[0], 0) != -1);
2215 _assert(close(fds[0]) != -1);
2217 input_ = fdopen(fds[1], "a");
2219 _assert(pipe(fds) != -1);
2220 _assert(dup2(fds[1], 1) != -1);
2221 _assert(close(fds[1]) != -1);
2224 detachNewThreadSelector:@selector(_readOutput:)
2226 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2231 - (pkgCacheFile &) cache {
2235 - (pkgDepCache::Policy *) policy {
2239 - (pkgRecords *) records {
2243 - (pkgProblemResolver *) resolver {
2247 - (pkgAcquire &) fetcher {
2251 - (NSArray *) packages {
2255 - (NSArray *) sources {
2256 return [sources_ allValues];
2259 - (NSArray *) issues {
2260 if (cache_->BrokenCount() == 0)
2263 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2265 for (Package *package in packages_) {
2266 if (![package broken])
2268 pkgCache::PkgIterator pkg([package iterator]);
2270 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2271 [entry addObject:[package name]];
2272 [issues addObject:entry];
2274 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2278 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2279 pkgCache::DepIterator start;
2280 pkgCache::DepIterator end;
2281 dep.GlobOr(start, end); // ++dep
2283 if (!cache_->IsImportantDep(end))
2285 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2288 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2289 [entry addObject:failure];
2290 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2292 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2293 [failure addObject:[package name]];
2295 pkgCache::PkgIterator target(start.TargetPkg());
2296 if (target->ProvidesList != 0)
2297 [failure addObject:@"?"];
2299 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2301 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2302 else if (!cache_[target].CandidateVerIter(cache_).end())
2303 [failure addObject:@"-"];
2304 else if (target->ProvidesList == 0)
2305 [failure addObject:@"!"];
2307 [failure addObject:@"%"];
2311 if (start.TargetVer() != 0)
2312 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2323 - (void) reloadData {
2343 if (!cache_.Open(progress_, true)) {
2345 if (!_error->PopMessage(error))
2348 lprintf("cache_.Open():[%s]\n", error.c_str());
2350 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2351 [delegate_ repairWithSelector:@selector(configure)];
2352 else if (error == "The package lists or status file could not be parsed or opened.")
2353 [delegate_ repairWithSelector:@selector(update)];
2354 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2355 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2356 // else if (error == "The list of sources could not be read.")
2357 else _assert(false);
2363 now_ = [[NSDate date] retain];
2365 policy_ = new pkgDepCache::Policy();
2366 records_ = new pkgRecords(cache_);
2367 resolver_ = new pkgProblemResolver(cache_);
2368 fetcher_ = new pkgAcquire(&status_);
2371 list_ = new pkgSourceList();
2372 _assert(list_->ReadMainList());
2374 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2375 _assert(pkgApplyStatus(cache_));
2377 if (cache_->BrokenCount() != 0) {
2378 _assert(pkgFixBroken(cache_));
2379 _assert(cache_->BrokenCount() == 0);
2380 _assert(pkgMinimizeUpgrade(cache_));
2383 [sources_ removeAllObjects];
2384 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2385 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2386 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2388 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2389 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2393 [packages_ removeAllObjects];
2396 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2397 if (Package *package = [Package packageWithIterator:iterator database:self])
2398 [packages_ addObject:package];
2400 [packages_ sortUsingSelector:@selector(compareByName:)];
2404 - (void) configure {
2405 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2406 system([dpkg UTF8String]);
2414 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2415 _assert(!_error->PendingError());
2418 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2421 public pkgArchiveCleaner
2424 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2429 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2431 while (_error->PopMessage(error))
2432 lprintf("ArchiveCleaner: %s\n", error.c_str());
2437 pkgRecords records(cache_);
2439 lock_ = new FileFd();
2440 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2441 _assert(!_error->PendingError());
2444 // XXX: explain this with an error message
2445 _assert(list.ReadMainList());
2447 manager_ = (_system->CreatePM(cache_));
2448 _assert(manager_->GetArchives(fetcher_, &list, &records));
2449 _assert(!_error->PendingError());
2453 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2455 _assert(list.ReadMainList());
2456 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2457 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2460 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2465 bool failed = false;
2466 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2467 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2470 std::string uri = (*item)->DescURI();
2471 std::string error = (*item)->ErrorText;
2473 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2476 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2477 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2488 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2490 if (_error->PendingError()) {
2495 if (result == pkgPackageManager::Failed) {
2500 if (result != pkgPackageManager::Completed) {
2505 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2507 _assert(list.ReadMainList());
2508 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2509 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2512 if (![before isEqualToArray:after])
2517 _assert(pkgDistUpgrade(cache_));
2521 [self updateWithStatus:status_];
2524 - (void) updateWithStatus:(Status &)status {
2526 _assert(list.ReadMainList());
2529 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2530 _assert(!_error->PendingError());
2532 pkgAcquire fetcher(&status);
2533 _assert(list.GetIndexes(&fetcher));
2535 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2536 bool failed = false;
2537 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2538 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2539 (*item)->Finished();
2543 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2544 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2545 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2548 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2553 - (void) setDelegate:(id)delegate {
2554 delegate_ = delegate;
2555 status_.setDelegate(delegate);
2556 progress_.setDelegate(delegate);
2559 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2560 pkgIndexFile *index(NULL);
2561 list_->FindIndex(file, index);
2562 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2568 /* PopUp Windows {{{ */
2569 @interface PopUpView : UIView {
2570 _transient id delegate_;
2571 UITransitionView *transition_;
2576 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2580 @implementation PopUpView
2583 [transition_ setDelegate:nil];
2584 [transition_ release];
2590 [transition_ transition:UITransitionPushFromTop toView:nil];
2593 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2594 if (from != nil && to == nil)
2595 [self removeFromSuperview];
2598 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2599 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2600 delegate_ = delegate;
2602 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2603 [self addSubview:transition_];
2605 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2607 [view addSubview:self];
2609 [transition_ setDelegate:self];
2611 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2612 [transition_ transition:UITransitionNone toView:blank];
2613 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2620 /* Mail Composition {{{ */
2621 @interface MailToView : PopUpView {
2622 MailComposeController *controller_;
2625 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2629 @implementation MailToView
2632 [controller_ release];
2636 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2640 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2641 NSLog(@"did:%@", delivery);
2642 // [UIApp setStatusBarShowsProgress:NO];
2643 if ([controller error]){
2644 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2645 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2646 [mailAlertSheet setBodyText:[controller error]];
2647 [mailAlertSheet popupAlertAnimated:YES];
2651 - (void) showError {
2652 NSLog(@"%@", [controller_ error]);
2653 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2654 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2655 [mailAlertSheet setBodyText:[controller_ error]];
2656 [mailAlertSheet popupAlertAnimated:YES];
2659 - (void) deliverMessage { _pooled
2663 if (![controller_ deliverMessage])
2664 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2667 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2668 if ([controller_ needsDelivery])
2669 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2674 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2675 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2676 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2677 [controller_ setDelegate:self];
2678 [controller_ initializeUI];
2679 [controller_ setupForURL:url];
2681 UIView *view([controller_ view]);
2682 [overlay_ addSubview:view];
2688 /* Confirmation View {{{ */
2689 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2690 if (!iterator.end())
2691 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2692 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2694 pkgCache::PkgIterator package(dep.TargetPkg());
2697 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2704 @protocol ConfirmationViewDelegate
2709 @interface ConfirmationView : BrowserView {
2710 _transient Database *database_;
2711 UIActionSheet *essential_;
2718 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2722 @implementation ConfirmationView
2729 if (essential_ != nil)
2730 [essential_ release];
2736 [book_ popFromSuperviewAnimated:YES];
2739 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2740 NSString *context([sheet context]);
2742 if ([context isEqualToString:@"remove"]) {
2750 [delegate_ confirm];
2757 } else if ([context isEqualToString:@"unable"]) {
2761 [super alertSheet:sheet buttonClicked:button];
2764 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2765 [window setValue:changes_ forKey:@"changes"];
2766 [window setValue:issues_ forKey:@"issues"];
2767 [window setValue:sizes_ forKey:@"sizes"];
2768 [super webView:sender didClearWindowObject:window forFrame:frame];
2771 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2772 if ((self = [super initWithBook:book]) != nil) {
2773 database_ = database;
2775 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2776 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2777 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2778 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2779 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2783 pkgDepCache::Policy *policy([database_ policy]);
2785 pkgCacheFile &cache([database_ cache]);
2786 NSArray *packages = [database_ packages];
2787 for (size_t i(0), e = [packages count]; i != e; ++i) {
2788 Package *package = [packages objectAtIndex:i];
2789 pkgCache::PkgIterator iterator = [package iterator];
2790 pkgDepCache::StateCache &state(cache[iterator]);
2792 NSString *name([package name]);
2794 if (state.NewInstall())
2795 [installing addObject:name];
2796 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2797 [reinstalling addObject:name];
2798 else if (state.Upgrade())
2799 [upgrading addObject:name];
2800 else if (state.Downgrade())
2801 [downgrading addObject:name];
2802 else if (state.Delete()) {
2803 if ([package essential])
2805 [removing addObject:name];
2808 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2809 substrate_ |= DepSubstrate(iterator.CurrentVer());
2814 else if (Advanced_ || true) {
2815 essential_ = [[UIActionSheet alloc]
2816 initWithTitle:@"Removing Essentials"
2817 buttons:[NSArray arrayWithObjects:
2818 @"Cancel Operation (Safe)",
2819 @"Force Removal (Unsafe)",
2821 defaultButtonIndex:0
2827 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2829 [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."];
2831 essential_ = [[UIActionSheet alloc]
2832 initWithTitle:@"Unable to Comply"
2833 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2834 defaultButtonIndex:0
2839 [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."];
2842 changes_ = [[NSArray alloc] initWithObjects:
2850 issues_ = [database_ issues];
2852 issues_ = [issues_ retain];
2854 sizes_ = [[NSArray alloc] initWithObjects:
2855 SizeString([database_ fetcher].FetchNeeded()),
2856 SizeString([database_ fetcher].PartialPresent()),
2857 SizeString([database_ cache]->UsrSize()),
2860 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2864 - (NSString *) backButtonTitle {
2868 - (NSString *) leftButtonTitle {
2872 - (id) _rightButtonTitle {
2873 return issues_ == nil ? @"Confirm" : nil;
2876 - (void) _leftButtonClicked {
2881 - (void) _rightButtonClicked {
2882 if (essential_ != nil)
2883 [essential_ popupAlertAnimated:YES];
2887 [delegate_ confirm];
2895 /* Progress Data {{{ */
2896 @interface ProgressData : NSObject {
2902 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2909 @implementation ProgressData
2911 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2912 if ((self = [super init]) != nil) {
2913 selector_ = selector;
2933 /* Progress View {{{ */
2934 @interface ProgressView : UIView <
2935 ConfigurationDelegate,
2938 _transient Database *database_;
2940 UIView *background_;
2941 UITransitionView *transition_;
2943 UINavigationBar *navbar_;
2944 UIProgressBar *progress_;
2945 UITextView *output_;
2946 UITextLabel *status_;
2947 UIPushButton *close_;
2950 SHA1SumValue springlist_;
2951 SHA1SumValue sandplate_;
2953 NSTimeInterval last_;
2956 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2958 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2959 - (void) setContentView:(UIView *)view;
2962 - (void) _retachThread;
2963 - (void) _detachNewThreadData:(ProgressData *)data;
2964 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2970 @protocol ProgressViewDelegate
2971 - (void) progressViewIsComplete:(ProgressView *)sender;
2974 @implementation ProgressView
2977 [transition_ setDelegate:nil];
2978 [navbar_ setDelegate:nil];
2981 if (background_ != nil)
2982 [background_ release];
2983 [transition_ release];
2986 [progress_ release];
2993 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2994 if (bootstrap_ && from == overlay_ && to == view_)
2998 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2999 if ((self = [super initWithFrame:frame]) != nil) {
3000 database_ = database;
3001 delegate_ = delegate;
3003 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3004 [transition_ setDelegate:self];
3006 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3009 [overlay_ setBackgroundColor:[UIColor blackColor]];
3011 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3012 [background_ setBackgroundColor:[UIColor blackColor]];
3013 [self addSubview:background_];
3016 [self addSubview:transition_];
3018 CGSize navsize = [UINavigationBar defaultSize];
3019 CGRect navrect = {{0, 0}, navsize};
3021 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3022 [overlay_ addSubview:navbar_];
3024 [navbar_ setBarStyle:1];
3025 [navbar_ setDelegate:self];
3027 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3028 [navbar_ pushNavigationItem:navitem];
3030 CGRect bounds = [overlay_ bounds];
3031 CGSize prgsize = [UIProgressBar defaultSize];
3034 (bounds.size.width - prgsize.width) / 2,
3035 bounds.size.height - prgsize.height - 20
3038 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3039 [progress_ setStyle:0];
3041 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3043 bounds.size.height - prgsize.height - 50,
3044 bounds.size.width - 20,
3048 [status_ setColor:[UIColor whiteColor]];
3049 [status_ setBackgroundColor:[UIColor clearColor]];
3051 [status_ setCentersHorizontally:YES];
3052 //[status_ setFont:font];
3054 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3056 navrect.size.height + 20,
3057 bounds.size.width - 20,
3058 bounds.size.height - navsize.height - 62 - navrect.size.height
3061 //[output_ setTextFont:@"Courier New"];
3062 [output_ setTextSize:12];
3064 [output_ setTextColor:[UIColor whiteColor]];
3065 [output_ setBackgroundColor:[UIColor clearColor]];
3067 [output_ setMarginTop:0];
3068 [output_ setAllowsRubberBanding:YES];
3069 [output_ setEditable:NO];
3071 [overlay_ addSubview:output_];
3073 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3075 bounds.size.height - prgsize.height - 50,
3076 bounds.size.width - 20,
3080 [close_ setAutosizesToFit:NO];
3081 [close_ setDrawsShadow:YES];
3082 [close_ setStretchBackground:YES];
3083 [close_ setEnabled:YES];
3085 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3086 [close_ setTitleFont:bold];
3088 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3089 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3090 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3094 - (void) setContentView:(UIView *)view {
3095 view_ = [view retain];
3098 - (void) resetView {
3099 [transition_ transition:6 toView:view_];
3102 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3103 NSString *context([sheet context]);
3105 if ([context isEqualToString:@"conffile"]) {
3106 FILE *input = [database_ input];
3110 fprintf(input, "N\n");
3114 fprintf(input, "Y\n");
3125 - (void) closeButtonPushed {
3134 [delegate_ suspendWithAnimation:YES];
3138 system("launchctl stop com.apple.SpringBoard");
3142 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3151 - (void) _retachThread {
3152 UINavigationItem *item = [navbar_ topItem];
3153 [item setTitle:@"Complete"];
3155 [overlay_ addSubview:close_];
3156 [progress_ removeFromSuperview];
3157 [status_ removeFromSuperview];
3159 [delegate_ progressViewIsComplete:self];
3162 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3163 MMap mmap(file, MMap::ReadOnly);
3165 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3166 if (!(sandplate_ == sha1.Result()))
3171 FileFd file(SpringBoard_, FileFd::ReadOnly);
3172 MMap mmap(file, MMap::ReadOnly);
3174 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3175 if (!(springlist_ == sha1.Result()))
3180 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3181 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3182 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3183 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3184 case 4: [close_ setTitle:@"Reboot Device"]; break;
3187 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3189 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3190 [cache autorelease];
3192 NSFileManager *manager = [NSFileManager defaultManager];
3193 NSError *error = nil;
3195 id system = [cache objectForKey:@"System"];
3200 if (stat(Cache_, &info) == -1)
3203 [system removeAllObjects];
3205 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3206 for (NSString *app in apps)
3207 if ([app hasSuffix:@".app"]) {
3208 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3209 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3210 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3212 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3213 [info setObject:path forKey:@"Path"];
3214 [info setObject:@"System" forKey:@"ApplicationType"];
3215 [system addInfoDictionary:info];
3221 [cache writeToFile:@Cache_ atomically:YES];
3223 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3225 if (chmod(Cache_, info.st_mode) == -1)
3229 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3232 notify_post("com.apple.mobile.application_installed");
3234 [delegate_ setStatusBarShowsProgress:NO];
3237 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3238 [[data target] performSelector:[data selector] withObject:[data object]];
3241 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3244 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3245 UINavigationItem *item = [navbar_ topItem];
3246 [item setTitle:title];
3248 [status_ setText:nil];
3249 [output_ setText:@""];
3250 [progress_ setProgress:0];
3253 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3255 [close_ removeFromSuperview];
3256 [overlay_ addSubview:progress_];
3257 [overlay_ addSubview:status_];
3259 [delegate_ setStatusBarShowsProgress:YES];
3263 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3264 MMap mmap(file, MMap::ReadOnly);
3266 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3267 sandplate_ = sha1.Result();
3271 FileFd file(SpringBoard_, FileFd::ReadOnly);
3272 MMap mmap(file, MMap::ReadOnly);
3274 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3275 springlist_ = sha1.Result();
3278 [transition_ transition:6 toView:overlay_];
3281 detachNewThreadSelector:@selector(_detachNewThreadData:)
3283 withObject:[[ProgressData alloc]
3284 initWithSelector:selector
3291 - (void) repairWithSelector:(SEL)selector {
3293 detachNewThreadSelector:selector
3300 - (void) setConfigurationData:(NSString *)data {
3302 performSelectorOnMainThread:@selector(_setConfigurationData:)
3308 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3309 Package *package = id == nil ? nil : [database_ packageWithName:id];
3311 UIActionSheet *sheet = [[[UIActionSheet alloc]
3312 initWithTitle:(package == nil ? id : [package name])
3313 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3314 defaultButtonIndex:0
3319 [sheet setBodyText:error];
3320 [sheet popupAlertAnimated:YES];
3323 - (void) setProgressTitle:(NSString *)title {
3325 performSelectorOnMainThread:@selector(_setProgressTitle:)
3331 - (void) setProgressPercent:(float)percent {
3333 performSelectorOnMainThread:@selector(_setProgressPercent:)
3334 withObject:[NSNumber numberWithFloat:percent]
3339 - (void) startProgress {
3340 last_ = [NSDate timeIntervalSinceReferenceDate];
3343 - (void) addProgressOutput:(NSString *)output {
3345 performSelectorOnMainThread:@selector(_addProgressOutput:)
3351 - (bool) isCancelling:(size_t)received {
3353 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3354 if (received_ != received) {
3355 received_ = received;
3357 } else if (now - last_ > 30)
3364 - (void) _setConfigurationData:(NSString *)data {
3365 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3367 _assert(conffile_r(data));
3369 NSString *ofile = conffile_r[1];
3370 //NSString *nfile = conffile_r[2];
3372 UIActionSheet *sheet = [[[UIActionSheet alloc]
3373 initWithTitle:@"Configuration Upgrade"
3374 buttons:[NSArray arrayWithObjects:
3375 @"Keep My Old Copy",
3376 @"Accept The New Copy",
3377 // XXX: @"See What Changed",
3379 defaultButtonIndex:0
3384 [sheet setBodyText:[NSString stringWithFormat:
3385 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3388 [sheet popupAlertAnimated:YES];
3391 - (void) _setProgressTitle:(NSString *)title {
3392 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3393 for (size_t i(0), e([words count]); i != e; ++i) {
3394 NSString *word([words objectAtIndex:i]);
3395 if (Package *package = [database_ packageWithName:word])
3396 [words replaceObjectAtIndex:i withObject:[package name]];
3399 [status_ setText:[words componentsJoinedByString:@" "]];
3402 - (void) _setProgressPercent:(NSNumber *)percent {
3403 [progress_ setProgress:[percent floatValue]];
3406 - (void) _addProgressOutput:(NSString *)output {
3407 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3408 CGSize size = [output_ contentSize];
3409 CGRect rect = {{0, size.height}, {size.width, 0}};
3410 [output_ scrollRectToVisible:rect animated:YES];
3413 - (BOOL) isRunning {
3420 /* Package Cell {{{ */
3421 @interface PackageCell : UISimpleTableCell {
3424 NSString *description_;
3428 UITextLabel *status_;
3432 - (PackageCell *) init;
3433 - (void) setPackage:(Package *)package;
3435 + (int) heightForPackage:(Package *)package;
3439 @implementation PackageCell
3441 - (void) clearPackage {
3452 if (description_ != nil) {
3453 [description_ release];
3457 if (source_ != nil) {
3462 if (badge_ != nil) {
3469 [self clearPackage];
3476 - (PackageCell *) init {
3477 if ((self = [super init]) != nil) {
3479 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3480 [status_ setBackgroundColor:[UIColor clearColor]];
3481 [status_ setFont:small];
3486 - (void) setPackage:(Package *)package {
3487 [self clearPackage];
3489 Source *source = [package source];
3490 NSString *section = [package simpleSection];
3492 icon_ = [[package icon] retain];
3494 name_ = [[package name] retain];
3495 description_ = [[package tagline] retain];
3497 NSString *label = nil;
3498 bool trusted = false;
3500 if (source != nil) {
3501 label = [source label];
3502 trusted = [source trusted];
3503 } else if ([[package id] isEqualToString:@"firmware"])
3506 label = @"Unknown/Local";
3508 NSString *from = [NSString stringWithFormat:@"from %@", label];
3510 if (section != nil && ![section isEqualToString:label])
3511 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3513 source_ = [from retain];
3515 if (NSString *purpose = [package primaryPurpose])
3516 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3517 badge_ = [badge_ retain];
3520 if (NSString *mode = [package mode]) {
3521 [badge_ setImage:[UIImage applicationImageNamed:
3522 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3525 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3526 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3527 } else if ([package half]) {
3528 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3529 [status_ setText:@"Package Damaged"];
3530 [status_ setColor:[UIColor redColor]];
3532 [badge_ setImage:nil];
3533 [status_ setText:nil];
3538 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3541 rect.size = [icon_ size];
3543 rect.size.width /= 2;
3544 rect.size.height /= 2;
3546 rect.origin.x = 25 - rect.size.width / 2;
3547 rect.origin.y = 25 - rect.size.height / 2;
3549 [icon_ drawInRect:rect];
3552 if (badge_ != nil) {
3553 CGSize size = [badge_ size];
3555 [badge_ drawAtPoint:CGPointMake(
3556 36 - size.width / 2,
3557 36 - size.height / 2
3566 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3567 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3571 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3573 [super drawContentInRect:rect selected:selected];
3576 + (int) heightForPackage:(Package *)package {
3577 NSString *tagline([package tagline]);
3578 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3580 if ([package hasMode] || [package half])
3589 /* Section Cell {{{ */
3590 @interface SectionCell : UISimpleTableCell {
3595 _UISwitchSlider *switch_;
3600 - (void) setSection:(Section *)section editing:(BOOL)editing;
3604 @implementation SectionCell
3606 - (void) clearSection {
3607 if (section_ != nil) {
3617 if (count_ != nil) {
3624 [self clearSection];
3631 if ((self = [super init]) != nil) {
3632 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3634 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3635 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3639 - (void) onSwitch:(id)sender {
3640 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3641 if (metadata == nil) {
3642 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3643 [Sections_ setObject:metadata forKey:section_];
3647 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3650 - (void) setSection:(Section *)section editing:(BOOL)editing {
3651 if (editing != editing_) {
3653 [switch_ removeFromSuperview];
3655 [self addSubview:switch_];
3659 [self clearSection];
3661 if (section == nil) {
3662 name_ = [@"All Packages" retain];
3665 section_ = [section name];
3666 if (section_ != nil)
3667 section_ = [section_ retain];
3668 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3669 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3672 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3676 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3677 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3684 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3686 CGSize size = [count_ sizeWithFont:Font14_];
3690 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3692 [super drawContentInRect:rect selected:selected];
3698 /* File Table {{{ */
3699 @interface FileTable : RVPage {
3700 _transient Database *database_;
3703 NSMutableArray *files_;
3707 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3708 - (void) setPackage:(Package *)package;
3712 @implementation FileTable
3715 if (package_ != nil)
3724 - (int) numberOfRowsInTable:(UITable *)table {
3725 return files_ == nil ? 0 : [files_ count];
3728 - (float) table:(UITable *)table heightForRow:(int)row {
3732 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3733 if (reusing == nil) {
3734 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3735 UIFont *font = [UIFont systemFontOfSize:16];
3736 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3738 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3742 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3746 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3747 if ((self = [super initWithBook:book]) != nil) {
3748 database_ = database;
3750 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3752 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3753 [self addSubview:list_];
3755 UITableColumn *column = [[[UITableColumn alloc]
3756 initWithTitle:@"Name"
3758 width:[self frame].size.width
3761 [list_ setDataSource:self];
3762 [list_ setSeparatorStyle:1];
3763 [list_ addTableColumn:column];
3764 [list_ setDelegate:self];
3765 [list_ setReusesTableCells:YES];
3769 - (void) setPackage:(Package *)package {
3770 if (package_ != nil) {
3771 [package_ autorelease];
3780 [files_ removeAllObjects];
3782 if (package != nil) {
3783 package_ = [package retain];
3784 name_ = [[package id] retain];
3786 if (NSArray *files = [package files])
3787 [files_ addObjectsFromArray:files];
3789 if ([files_ count] != 0) {
3790 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3791 [files_ removeObjectAtIndex:0];
3792 [files_ sortUsingSelector:@selector(compareByPath:)];
3794 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3795 [stack addObject:@"/"];
3797 for (int i(0), e([files_ count]); i != e; ++i) {
3798 NSString *file = [files_ objectAtIndex:i];
3799 while (![file hasPrefix:[stack lastObject]])
3800 [stack removeLastObject];
3801 NSString *directory = [stack lastObject];
3802 [stack addObject:[file stringByAppendingString:@"/"]];
3803 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3804 ([stack count] - 2) * 3, "",
3805 [file substringFromIndex:[directory length]]
3814 - (void) resetViewAnimated:(BOOL)animated {
3815 [list_ resetViewAnimated:animated];
3818 - (void) reloadData {
3819 [self setPackage:[database_ packageWithName:name_]];
3820 [self reloadButtons];
3823 - (NSString *) title {
3824 return @"Installed Files";
3827 - (NSString *) backButtonTitle {
3833 /* Package View {{{ */
3834 @interface PackageView : BrowserView {
3835 _transient Database *database_;
3838 NSMutableArray *buttons_;
3841 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3842 - (void) setPackage:(Package *)package;
3846 @implementation PackageView
3849 if (package_ != nil)
3857 - (void) _clickButtonWithName:(NSString *)name {
3858 if ([name isEqualToString:@"Install"])
3859 [delegate_ installPackage:package_];
3860 else if ([name isEqualToString:@"Reinstall"])
3861 [delegate_ installPackage:package_];
3862 else if ([name isEqualToString:@"Remove"])
3863 [delegate_ removePackage:package_];
3864 else if ([name isEqualToString:@"Upgrade"])
3865 [delegate_ installPackage:package_];
3866 else _assert(false);
3869 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3870 NSString *context([sheet context]);
3872 if ([context isEqualToString:@"modify"]) {
3873 int count = [buttons_ count];
3874 _assert(count != 0);
3875 _assert(button <= count + 1);
3877 if (count != button - 1)
3878 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3882 [super alertSheet:sheet buttonClicked:button];
3885 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3886 return [super webView:sender didFinishLoadForFrame:frame];
3889 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3890 [window setValue:package_ forKey:@"package"];
3891 [super webView:sender didClearWindowObject:window forFrame:frame];
3895 - (void) _rightButtonClicked {
3896 /*[super _rightButtonClicked];
3899 int count = [buttons_ count];
3900 _assert(count != 0);
3903 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3905 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3906 [buttons addObjectsFromArray:buttons_];
3907 [buttons addObject:@"Cancel"];
3909 [delegate_ slideUp:[[[UIActionSheet alloc]
3912 defaultButtonIndex:2
3920 - (id) _rightButtonTitle {
3921 int count = [buttons_ count];
3922 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3925 - (NSString *) backButtonTitle {
3929 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3930 if ((self = [super initWithBook:book]) != nil) {
3931 database_ = database;
3932 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3936 - (void) setPackage:(Package *)package {
3937 if (package_ != nil) {
3938 [package_ autorelease];
3947 [buttons_ removeAllObjects];
3949 if (package != nil) {
3950 package_ = [package retain];
3951 name_ = [[package id] retain];
3953 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3955 if ([package_ source] == nil);
3956 else if ([package_ upgradableAndEssential:NO])
3957 [buttons_ addObject:@"Upgrade"];
3958 else if ([package_ installed] == nil)
3959 [buttons_ addObject:@"Install"];
3961 [buttons_ addObject:@"Reinstall"];
3962 if ([package_ installed] != nil)
3963 [buttons_ addObject:@"Remove"];
3971 - (void) reloadData {
3972 [self setPackage:[database_ packageWithName:name_]];
3973 [self reloadButtons];
3978 /* Package Table {{{ */
3979 @interface PackageTable : RVPage {
3980 _transient Database *database_;
3984 NSMutableArray *packages_;
3985 NSMutableArray *sections_;
3986 UISectionList *list_;
3989 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3991 - (void) setDelegate:(id)delegate;
3992 - (void) setObject:(id)object;
3994 - (void) reloadData;
3995 - (void) resetCursor;
3997 - (UISectionList *) list;
3999 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4003 @implementation PackageTable
4006 [list_ setDataSource:nil];
4011 [packages_ release];
4012 [sections_ release];
4017 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4018 return [sections_ count];
4021 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4022 return [[sections_ objectAtIndex:section] name];
4025 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4026 return [[sections_ objectAtIndex:section] row];
4029 - (int) numberOfRowsInTable:(UITable *)table {
4030 return [packages_ count];
4033 - (float) table:(UITable *)table heightForRow:(int)row {
4034 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4037 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4039 reusing = [[[PackageCell alloc] init] autorelease];
4040 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4044 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4048 - (void) tableRowSelected:(NSNotification *)notification {
4049 int row = [[notification object] selectedRow];
4053 Package *package = [packages_ objectAtIndex:row];
4054 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4055 [view setDelegate:delegate_];
4056 [view setPackage:package];
4057 [book_ pushPage:view];
4060 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4061 if ((self = [super initWithBook:book]) != nil) {
4062 database_ = database;
4063 title_ = [title retain];
4065 object_ = object == nil ? nil : [object retain];
4067 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4068 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4070 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4071 [list_ setDataSource:self];
4073 UITableColumn *column = [[[UITableColumn alloc]
4074 initWithTitle:@"Name"
4076 width:[self frame].size.width
4079 UITable *table = [list_ table];
4080 [table setSeparatorStyle:1];
4081 [table addTableColumn:column];
4082 [table setDelegate:self];
4083 [table setReusesTableCells:YES];
4085 [self addSubview:list_];
4088 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4089 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4093 - (void) setDelegate:(id)delegate {
4094 delegate_ = delegate;
4097 - (void) setObject:(id)object {
4103 object_ = [object retain];
4106 - (void) reloadData {
4107 NSArray *packages = [database_ packages];
4109 [packages_ removeAllObjects];
4110 [sections_ removeAllObjects];
4112 for (size_t i(0); i != [packages count]; ++i) {
4113 Package *package([packages objectAtIndex:i]);
4114 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4115 [packages_ addObject:package];
4118 Section *section = nil;
4120 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4121 Package *package = [packages_ objectAtIndex:offset];
4122 NSString *name = [package index];
4124 if (section == nil || ![[section name] isEqualToString:name]) {
4125 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4126 [sections_ addObject:section];
4129 [section addToCount];
4135 - (NSString *) title {
4139 - (void) resetViewAnimated:(BOOL)animated {
4140 [list_ resetViewAnimated:animated];
4143 - (void) resetCursor {
4144 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4147 - (UISectionList *) list {
4151 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4152 [list_ setShouldHideHeaderInShortLists:hide];
4158 /* Add Source View {{{ */
4159 @interface AddSourceView : RVPage {
4160 _transient Database *database_;
4163 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4167 @implementation AddSourceView
4169 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4170 if ((self = [super initWithBook:book]) != nil) {
4171 database_ = database;
4177 /* Source Cell {{{ */
4178 @interface SourceCell : UITableCell {
4181 NSString *description_;
4187 - (SourceCell *) initWithSource:(Source *)source;
4191 @implementation SourceCell
4196 [description_ release];
4201 - (SourceCell *) initWithSource:(Source *)source {
4202 if ((self = [super init]) != nil) {
4204 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4206 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4207 icon_ = [icon_ retain];
4209 origin_ = [[source name] retain];
4210 label_ = [[source uri] retain];
4211 description_ = [[source description] retain];
4215 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4217 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4224 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4228 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4232 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4234 [super drawContentInRect:rect selected:selected];
4239 /* Source Table {{{ */
4240 @interface SourceTable : RVPage {
4241 _transient Database *database_;
4242 UISectionList *list_;
4243 NSMutableArray *sources_;
4244 UIActionSheet *alert_;
4248 UIProgressHUD *hud_;
4251 //NSURLConnection *installer_;
4252 NSURLConnection *trivial_bz2_;
4253 NSURLConnection *trivial_gz_;
4254 //NSURLConnection *automatic_;
4259 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4263 @implementation SourceTable
4265 - (void) _deallocConnection:(NSURLConnection *)connection {
4266 if (connection != nil) {
4267 [connection cancel];
4268 //[connection setDelegate:nil];
4269 [connection release];
4274 [[list_ table] setDelegate:nil];
4275 [list_ setDataSource:nil];
4284 //[self _deallocConnection:installer_];
4285 [self _deallocConnection:trivial_gz_];
4286 [self _deallocConnection:trivial_bz2_];
4287 //[self _deallocConnection:automatic_];
4294 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4295 return offset_ == 0 ? 1 : 2;
4298 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4299 switch (section + (offset_ == 0 ? 1 : 0)) {
4300 case 0: return @"Entered by User";
4301 case 1: return @"Installed by Packages";
4309 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4310 switch (section + (offset_ == 0 ? 1 : 0)) {
4312 case 1: return offset_;
4320 - (int) numberOfRowsInTable:(UITable *)table {
4321 return [sources_ count];
4324 - (float) table:(UITable *)table heightForRow:(int)row {
4325 Source *source = [sources_ objectAtIndex:row];
4326 return [source description] == nil ? 56 : 73;
4329 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4330 Source *source = [sources_ objectAtIndex:row];
4331 // XXX: weird warning, stupid selectors ;P
4332 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4335 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4339 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4343 - (void) tableRowSelected:(NSNotification*)notification {
4344 UITable *table([list_ table]);
4345 int row([table selectedRow]);
4349 Source *source = [sources_ objectAtIndex:row];
4351 PackageTable *packages = [[[PackageTable alloc]
4354 title:[source label]
4355 filter:@selector(isVisibleInSource:)
4359 [packages setDelegate:delegate_];
4361 [book_ pushPage:packages];
4364 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4365 Source *source = [sources_ objectAtIndex:row];
4366 return [source record] != nil;
4369 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4370 [[list_ table] setDeleteConfirmationRow:row];
4373 - (void) table:(UITable *)table deleteRow:(int)row {
4374 Source *source = [sources_ objectAtIndex:row];
4375 [Sources_ removeObjectForKey:[source key]];
4376 [delegate_ syncData];
4379 - (void) _endConnection:(NSURLConnection *)connection {
4380 NSURLConnection **field = NULL;
4381 if (connection == trivial_bz2_)
4382 field = &trivial_bz2_;
4383 else if (connection == trivial_gz_)
4384 field = &trivial_gz_;
4385 _assert(field != NULL);
4386 [connection release];
4390 trivial_bz2_ == nil &&
4393 [delegate_ setStatusBarShowsProgress:NO];
4396 [hud_ removeFromSuperview];
4401 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4404 @"./", @"Distribution",
4405 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4407 [delegate_ syncData];
4408 } else if (error_ != nil) {
4409 UIActionSheet *sheet = [[[UIActionSheet alloc]
4410 initWithTitle:@"Verification Error"
4411 buttons:[NSArray arrayWithObjects:@"OK", nil]
4412 defaultButtonIndex:0
4417 [sheet setBodyText:[error_ localizedDescription]];
4418 [sheet popupAlertAnimated:YES];
4420 UIActionSheet *sheet = [[[UIActionSheet alloc]
4421 initWithTitle:@"Did not Find Repository"
4422 buttons:[NSArray arrayWithObjects:@"OK", nil]
4423 defaultButtonIndex:0
4428 [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."];
4429 [sheet popupAlertAnimated:YES];
4435 if (error_ != nil) {
4442 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4443 switch ([response statusCode]) {
4449 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4450 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4452 error_ = [error retain];
4453 [self _endConnection:connection];
4456 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4457 [self _endConnection:connection];
4460 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4461 NSMutableURLRequest *request = [NSMutableURLRequest
4462 requestWithURL:[NSURL URLWithString:href]
4463 cachePolicy:NSURLRequestUseProtocolCachePolicy
4464 timeoutInterval:20.0
4467 [request setHTTPMethod:method];
4469 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4472 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4473 NSString *context([sheet context]);
4475 if ([context isEqualToString:@"source"]) {
4478 NSString *href = [[sheet textField] text];
4480 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4482 if (![href hasSuffix:@"/"])
4483 href_ = [href stringByAppendingString:@"/"];
4486 href_ = [href_ retain];
4488 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4489 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4490 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4494 hud_ = [delegate_ addProgressHUD];
4495 [hud_ setText:@"Verifying URL"];
4506 } else if ([context isEqualToString:@"trivial"])
4508 else if ([context isEqualToString:@"urlerror"])
4512 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4513 if ((self = [super initWithBook:book]) != nil) {
4514 database_ = database;
4515 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4517 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4518 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4519 [list_ setShouldHideHeaderInShortLists:NO];
4521 [self addSubview:list_];
4522 [list_ setDataSource:self];
4524 UITableColumn *column = [[UITableColumn alloc]
4525 initWithTitle:@"Name"
4527 width:[self frame].size.width
4530 UITable *table = [list_ table];
4531 [table setSeparatorStyle:1];
4532 [table addTableColumn:column];
4533 [table setDelegate:self];
4537 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4538 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4542 - (void) reloadData {
4544 _assert(list.ReadMainList());
4546 [sources_ removeAllObjects];
4547 [sources_ addObjectsFromArray:[database_ sources]];
4549 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4552 int count = [sources_ count];
4553 for (offset_ = 0; offset_ != count; ++offset_) {
4554 Source *source = [sources_ objectAtIndex:offset_];
4555 if ([source record] == nil)
4562 - (void) resetViewAnimated:(BOOL)animated {
4563 [list_ resetViewAnimated:animated];
4566 - (void) _leftButtonClicked {
4567 /*[book_ pushPage:[[[AddSourceView alloc]
4572 UIActionSheet *sheet = [[[UIActionSheet alloc]
4573 initWithTitle:@"Enter Cydia/APT URL"
4574 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4575 defaultButtonIndex:0
4580 [sheet setNumberOfRows:1];
4582 [sheet addTextFieldWithValue:@"http://" label:@""];
4584 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4585 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4586 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4587 [traits setKeyboardType:UIKeyboardTypeURL];
4588 // XXX: UIReturnKeyDone
4589 [traits setReturnKeyType:UIReturnKeyNext];
4591 [sheet popupAlertAnimated:YES];
4594 - (void) _rightButtonClicked {
4595 UITable *table = [list_ table];
4596 BOOL editing = [table isRowDeletionEnabled];
4597 [table enableRowDeletion:!editing animated:YES];
4598 [book_ reloadButtonsForPage:self];
4601 - (NSString *) title {
4605 - (NSString *) leftButtonTitle {
4606 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4609 - (id) rightButtonTitle {
4610 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4613 - (UINavigationButtonStyle) rightButtonStyle {
4614 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4620 /* Installed View {{{ */
4621 @interface InstalledView : RVPage {
4622 _transient Database *database_;
4623 PackageTable *packages_;
4627 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4631 @implementation InstalledView
4634 [packages_ release];
4638 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4639 if ((self = [super initWithBook:book]) != nil) {
4640 database_ = database;
4642 packages_ = [[PackageTable alloc]
4646 filter:@selector(isInstalledAndVisible:)
4647 with:[NSNumber numberWithBool:YES]
4650 [self addSubview:packages_];
4652 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4653 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4657 - (void) resetViewAnimated:(BOOL)animated {
4658 [packages_ resetViewAnimated:animated];
4661 - (void) reloadData {
4662 [packages_ reloadData];
4665 - (void) _rightButtonClicked {
4666 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4667 [packages_ reloadData];
4669 [book_ reloadButtonsForPage:self];
4672 - (NSString *) title {
4673 return @"Installed";
4676 - (NSString *) backButtonTitle {
4680 - (id) rightButtonTitle {
4681 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4684 - (UINavigationButtonStyle) rightButtonStyle {
4685 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4688 - (void) setDelegate:(id)delegate {
4689 [super setDelegate:delegate];
4690 [packages_ setDelegate:delegate];
4697 @interface HomeView : BrowserView {
4702 @implementation HomeView
4704 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4705 NSString *context([sheet context]);
4707 if ([context isEqualToString:@"about"])
4710 [super alertSheet:sheet buttonClicked:button];
4713 - (void) _leftButtonClicked {
4714 UIActionSheet *sheet = [[[UIActionSheet alloc]
4715 initWithTitle:@"About Cydia Installer"
4716 buttons:[NSArray arrayWithObjects:@"Close", nil]
4717 defaultButtonIndex:0
4723 @"Copyright (C) 2008\n"
4724 "Jay Freeman (saurik)\n"
4725 "saurik@saurik.com\n"
4726 "http://www.saurik.com/\n"
4729 "http://www.theokorigroup.com/\n"
4731 "College of Creative Studies,\n"
4732 "University of California,\n"
4734 "http://www.ccs.ucsb.edu/"
4737 [sheet popupAlertAnimated:YES];
4740 - (NSString *) leftButtonTitle {
4746 /* Manage View {{{ */
4747 @interface ManageView : BrowserView {
4752 @implementation ManageView
4754 - (NSString *) title {
4758 - (void) _leftButtonClicked {
4759 [delegate_ askForSettings];
4762 - (NSString *) leftButtonTitle {
4767 - (id) _rightButtonTitle {
4779 @interface WebView (Cydia)
4780 - (void) setScriptDebugDelegate:(id)delegate;
4781 - (void) _setFormDelegate:(id)delegate;
4782 - (void) _setUIKitDelegate:(id)delegate;
4783 - (void) setWebMailDelegate:(id)delegate;
4784 - (void) _setLayoutInterval:(float)interval;
4787 /* Indirect Delegate {{{ */
4788 @interface IndirectDelegate : NSProxy {
4789 _transient volatile id delegate_;
4792 - (void) setDelegate:(id)delegate;
4793 - (id) initWithDelegate:(id)delegate;
4796 @implementation IndirectDelegate
4798 - (void) setDelegate:(id)delegate {
4799 delegate_ = delegate;
4802 - (id) initWithDelegate:(id)delegate {
4803 delegate_ = delegate;
4807 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4808 if (delegate_ != nil)
4809 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4811 // XXX: I fucking hate Apple so very very bad
4812 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4815 - (void) forwardInvocation:(NSInvocation *)inv {
4816 SEL sel = [inv selector];
4817 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4818 [inv invokeWithTarget:delegate_];
4823 /* Browser Implementation {{{ */
4824 @implementation BrowserView
4827 #include "internals.h"
4831 if (challenge_ != nil)
4832 [challenge_ release];
4834 WebView *webview = [webview_ webView];
4835 [webview setFrameLoadDelegate:nil];
4836 [webview setResourceLoadDelegate:nil];
4837 [webview setUIDelegate:nil];
4838 [webview setScriptDebugDelegate:nil];
4839 [webview setPolicyDelegate:nil];
4841 [webview setDownloadDelegate:nil];
4843 [webview _setFormDelegate:nil];
4844 [webview _setUIKitDelegate:nil];
4845 [webview setWebMailDelegate:nil];
4846 [webview setEditingDelegate:nil];
4848 [webview_ setDelegate:nil];
4849 [webview_ setGestureDelegate:nil];
4851 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4856 [webview_ removeFromSuperview];
4857 [Documents_ addObject:[webview_ autorelease]];
4862 [indirect_ setDelegate:nil];
4863 [indirect_ release];
4865 [scroller_ setDelegate:nil];
4871 if (function_ != nil)
4872 [function_ release];
4874 [scroller_ release];
4875 [indicator_ release];
4876 if (confirm_ != nil)
4883 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4884 [self loadRequest:[NSURLRequest
4887 timeoutInterval:30.0
4891 - (void) loadURL:(NSURL *)url {
4892 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4895 - (NSMutableURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4896 NSMutableURLRequest *copy = [request mutableCopy];
4898 if (Machine_ != NULL)
4899 [copy setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4900 if (UniqueID_ != nil)
4901 [copy setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4904 [copy setValue:Role_ forHTTPHeaderField:@"X-Role"];
4909 - (void) loadRequest:(NSURLRequest *)request {
4911 [webview_ loadRequest:request];
4914 - (void) reloadURL {
4915 NSLog(@"rlu:%@", request_);
4916 if (request_ == nil)
4919 if ([request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil)
4920 [webview_ loadRequest:request_];
4922 UIActionSheet *sheet = [[[UIActionSheet alloc]
4923 initWithTitle:@"Are you sure you want to submit this form again?"
4924 buttons:[NSArray arrayWithObjects:@"Cancel", @"Submit", nil]
4925 defaultButtonIndex:0
4930 [sheet setNumberOfRows:1];
4931 [sheet popupAlertAnimated:YES];
4935 - (WebView *) webView {
4936 return [webview_ webView];
4939 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4940 [scroller_ setContentSize:frame.size];
4943 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4944 [self view:sender didSetFrame:frame];
4947 - (void) pushPage:(RVPage *)page {
4948 [self setBackButtonTitle:title_];
4949 [page setDelegate:delegate_];
4950 [book_ pushPage:page];
4953 - (BOOL) getSpecial:(NSURL *)url {
4954 NSString *href([url absoluteString]);
4955 NSString *scheme([[url scheme] lowercaseString]);
4959 if ([href hasPrefix:@"apptapp://package/"])
4960 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4961 else if ([scheme isEqualToString:@"cydia"]) {
4962 page = [delegate_ pageForURL:url hasTag:NULL];
4965 } else if (![scheme isEqualToString:@"apptapp"])
4969 [self pushPage:page];
4973 - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4974 UIActionSheet *sheet = [[[UIActionSheet alloc]
4976 buttons:[NSArray arrayWithObjects:@"OK", nil]
4977 defaultButtonIndex:0
4982 [sheet setBodyText:message];
4983 [sheet popupAlertAnimated:YES];
4986 - (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4987 UIActionSheet *sheet = [[[UIActionSheet alloc]
4989 buttons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil]
4990 defaultButtonIndex:0
4995 [sheet setNumberOfRows:1];
4996 [sheet setBodyText:message];
4997 [sheet popupAlertAnimated:YES];
4999 NSRunLoop *loop([NSRunLoop currentRunLoop]);
5000 NSDate *future([NSDate distantFuture]);
5002 while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
5004 NSNumber *confirm([confirm_ autorelease]);
5006 return [confirm boolValue];
5009 /* Web Scripting {{{ */
5010 + (NSString *) webScriptNameForSelector:(SEL)selector {
5011 if (selector == @selector(getPackageById:))
5012 return @"getPackageById";
5013 else if (selector == @selector(setButtonImage:withStyle:toFunction:))
5014 return @"setButtonImage";
5015 else if (selector == @selector(setButtonTitle:withStyle:toFunction:))
5016 return @"setButtonTitle";
5017 else if (selector == @selector(supports:))
5023 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
5024 return [self webScriptNameForSelector:selector] == nil;
5027 - (BOOL) supports:(NSString *)feature {
5028 return [feature isEqualToString:@"window.open"];
5031 - (Package *) getPackageById:(NSString *)id {
5032 return [[Database sharedInstance] packageWithName:id];
5035 - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
5037 [button_ autorelease];
5038 button_ = button == nil ? nil : [[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:button]]] retain];
5041 [style_ autorelease];
5042 style_ = style == nil ? nil : [style retain];
5044 if (function_ != nil)
5045 [function_ autorelease];
5046 function_ = function == nil ? nil : [function retain];
5049 - (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
5051 [button_ autorelease];
5052 button_ = button == nil ? nil : [button retain];
5055 [style_ autorelease];
5056 style_ = style == nil ? nil : [style retain];
5058 if (function_ != nil)
5059 [function_ autorelease];
5060 function_ = function == nil ? nil : [function retain];
5064 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
5065 [window setValue:self forKey:@"cydia"];
5068 - (void) webView:(WebView *)sender unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame {
5069 NSLog(@"err:%@", error);
5072 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
5073 if (NSURL *url = [request URL]) {
5074 if (name != nil && [name isEqualToString:@"_open"])
5075 [delegate_ openURL:url];
5077 NSLog(@"win:%@:%@", url, [action description]);
5078 if (![self getSpecial:url]) {
5079 NSString *scheme([[url scheme] lowercaseString]);
5080 if ([scheme isEqualToString:@"mailto"])
5081 [delegate_ openMailToURL:url];
5090 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
5091 if ([WebView canShowMIMEType:type])
5094 // XXX: handle more mime types!
5096 if (frame == [webView mainFrame])
5097 [UIApp openURL:[request URL]];
5101 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
5102 if (request == nil) ignore: {
5107 NSURL *url([request URL]);
5109 if (url == nil) use: {
5110 if ([frame parentFrame] == nil) {
5111 if (request_ != nil)
5112 [request_ autorelease];
5113 request_ = [request retain];
5114 NSLog(@"dpn:%@", request_);
5121 else NSLog(@"nav:%@:%@", url, [action description]);
5124 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
5127 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
5128 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
5131 [UIApp openURL:url];
5135 int store(_not(int));
5136 if (NSURL *itms = [url itmsURL:&store]) {
5137 NSLog(@"itms#%@#%u#%@", url, store, itms);
5139 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
5140 store == 2 && [capability containsObject:@"com.apple.AppStore"]
5147 NSString *scheme([[url scheme] lowercaseString]);
5149 if ([scheme isEqualToString:@"tel"]) {
5150 // XXX: intelligence
5154 if ([scheme isEqualToString:@"mailto"]) {
5155 [delegate_ openMailToURL:url];
5159 if ([self getSpecial:url])
5161 else if ([WebView _canHandleRequest:request])
5163 else if ([url isSpringboardHandledURL])
5169 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
5170 //lprintf("Status:%s\n", [text UTF8String]);
5173 - (void) _pushPage {
5177 [book_ pushPage:self];
5180 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5181 NSString *context([sheet context]);
5183 if ([context isEqualToString:@"alert"])
5185 else if ([context isEqualToString:@"confirm"]) {
5188 confirm_ = [NSNumber numberWithBool:YES];
5192 confirm_ = [NSNumber numberWithBool:NO];
5197 } else if ([context isEqualToString:@"challenge"]) {
5198 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
5202 NSString *username([[sheet textFieldAtIndex:0] text]);
5203 NSString *password([[sheet textFieldAtIndex:1] text]);
5205 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
5207 [sender useCredential:credential forAuthenticationChallenge:challenge_];
5211 [sender cancelAuthenticationChallenge:challenge_];
5218 [challenge_ release];
5222 } else if ([context isEqualToString:@"submit"]) {
5228 if (request_ != nil)
5229 [webview_ loadRequest:request_];
5240 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
5241 challenge_ = [challenge retain];
5243 NSURLProtectionSpace *space([challenge protectionSpace]);
5244 NSString *realm([space realm]);
5248 UIActionSheet *sheet = [[[UIActionSheet alloc]
5250 buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
5251 defaultButtonIndex:0
5253 context:@"challenge"
5256 [sheet setNumberOfRows:1];
5258 [sheet addTextFieldWithValue:@"" label:@"username"];
5259 [sheet addTextFieldWithValue:@"" label:@"password"];
5261 UITextField *username([sheet textFieldAtIndex:0]); {
5262 UITextInputTraits *traits([username textInputTraits]);
5263 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5264 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5265 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5266 [traits setReturnKeyType:UIReturnKeyNext];
5269 UITextField *password([sheet textFieldAtIndex:1]); {
5270 UITextInputTraits *traits([password textInputTraits]);
5271 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5272 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5273 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5274 // XXX: UIReturnKeyDone
5275 [traits setReturnKeyType:UIReturnKeyNext];
5276 [traits setSecureTextEntry:YES];
5279 [sheet popupAlertAnimated:YES];
5282 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
5283 NSURL *url = [request URL];
5284 if ([self getSpecial:url])
5287 return [self _addHeadersToRequest:request];
5290 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
5291 [self setBackButtonTitle:title_];
5293 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
5294 [browser setDelegate:delegate_];
5297 [browser loadRequest:request];
5298 [book_ pushPage:browser];
5301 return [browser webView];
5304 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
5305 return [self _createWebViewWithRequest:request pushed:(request != nil)];
5308 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
5309 return [self _createWebViewWithRequest:request pushed:YES];
5312 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
5313 if ([frame parentFrame] != nil)
5316 title_ = [title retain];
5317 [book_ reloadTitleForPage:self];
5320 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
5321 if ([frame parentFrame] != nil)
5326 [self reloadButtons];
5328 if (title_ != nil) {
5333 if (button_ != nil) {
5338 if (style_ != nil) {
5343 if (function_ != nil) {
5344 [function_ release];
5348 [book_ reloadTitleForPage:self];
5350 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
5352 CGRect webrect = [scroller_ bounds];
5353 webrect.size.height = 0;
5354 [webview_ setFrame:webrect];
5357 - (void) _finishLoading {
5360 [self reloadButtons];
5368 - (void) reloadButtons {
5369 if ([self _loading])
5370 [indicator_ startAnimation];
5372 [indicator_ stopAnimation];
5373 [super reloadButtons];
5376 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5377 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5380 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5381 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5384 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5385 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5388 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5389 return [webview_ webView:sender didCommitLoadForFrame:frame];
5392 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5393 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5396 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5397 if ([frame parentFrame] == nil) {
5398 [self _finishLoading];
5400 if (DOMDocument *document = [frame DOMDocument])
5401 if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
5402 for (DOMHTMLBodyElement *body in bodies) {
5403 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
5405 bool colored(false);
5407 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
5408 DOMRGBColor *rgb([color getRGBColorValue]);
5410 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
5411 NSLog(@"alpha:%g", alpha);
5416 [scroller_ setBackgroundColor:[UIColor
5417 colorWithRed:([[rgb red] getFloatValue:DOM_CSS_NUMBER] / 255)
5418 green:([[rgb green] getFloatValue:DOM_CSS_NUMBER] / 255)
5419 blue:([[rgb blue] getFloatValue:DOM_CSS_NUMBER] / 255)
5426 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
5431 return [webview_ webView:sender didFinishLoadForFrame:frame];
5434 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5435 if ([frame parentFrame] != nil)
5437 [self _finishLoading];
5439 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5440 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5441 [[error localizedDescription] stringByAddingPercentEscapes]
5445 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5447 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5451 - (id) initWithBook:(RVBook *)book {
5452 if ((self = [super initWithBook:book]) != nil) {
5455 struct CGRect bounds = [self bounds];
5457 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5458 [self addSubview:scroller_];
5460 [scroller_ setShowBackgroundShadow:NO];
5461 [scroller_ setFixedBackgroundPattern:YES];
5462 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
5464 [scroller_ setScrollingEnabled:YES];
5465 [scroller_ setAdjustForContentSizeChange:YES];
5466 [scroller_ setClipsSubviews:YES];
5467 [scroller_ setAllowsRubberBanding:YES];
5468 [scroller_ setScrollDecelerationFactor:0.99];
5469 [scroller_ setDelegate:self];
5471 CGRect webrect = [scroller_ bounds];
5472 webrect.size.height = 0;
5477 webview_ = [Documents_ lastObject];
5478 if (webview_ != nil) {
5479 webview_ = [webview_ retain];
5480 webview = [webview_ webView];
5481 [Documents_ removeLastObject];
5482 [webview_ setFrame:webrect];
5487 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5488 webview = [webview_ webView];
5490 // XXX: this is terribly (too?) expensive
5491 //[webview_ setDrawsBackground:NO];
5492 [webview setPreferencesIdentifier:@"Cydia"];
5494 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5496 [webview_ setAllowsMessaging:YES];
5498 [webview_ setTilingEnabled:YES];
5499 [webview_ setDrawsGrid:NO];
5500 [webview_ setLogsTilingChanges:NO];
5501 [webview_ setTileMinificationFilter:kCAFilterNearest];
5502 [webview_ setDetectsPhoneNumbers:NO];
5503 [webview_ setAutoresizes:YES];
5505 [webview_ setMinimumScale:0.25f forDocumentTypes:0x10];
5506 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10];
5507 [webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
5509 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2];
5511 [webview_ setMinimumScale:1.0f forDocumentTypes:0x8];
5512 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8];
5513 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8];
5515 [webview_ _setDocumentType:0x4];
5517 [webview_ setZoomsFocusedFormControl:YES];
5518 [webview_ setContentsPosition:7];
5519 [webview_ setEnabledGestures:0xa];
5520 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
5521 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
5523 [webview_ setSmoothsFonts:YES];
5525 [webview _setUsesLoaderCache:YES];
5526 [webview setGroupName:@"Cydia"];
5527 [webview _setLayoutInterval:0];
5530 [webview_ setDelegate:self];
5531 [webview_ setGestureDelegate:self];
5532 [scroller_ addSubview:webview_];
5534 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5536 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5537 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5538 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5540 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5541 NSString *application = package == nil ? @"Cydia" : [NSString
5542 stringWithFormat:@"Cydia/%@",
5544 ]; [webview setApplicationNameForUserAgent:application];
5546 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5548 [webview setFrameLoadDelegate:self];
5549 [webview setResourceLoadDelegate:indirect_];
5550 [webview setUIDelegate:self];
5551 [webview setScriptDebugDelegate:self];
5552 [webview setPolicyDelegate:self];
5554 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5555 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5559 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5560 [webview_ redrawScaledDocument];
5563 - (void) _rightButtonClicked {
5564 if (function_ == nil) {
5568 WebView *webview([webview_ webView]);
5569 WebFrame *frame([webview mainFrame]);
5571 id _private(MSHookIvar<id>(webview, "_private"));
5572 WebCore::Page *page(_private == nil ? NULL : MSHookIvar<WebCore::Page *>(_private, "page"));
5573 WebCore::Settings *settings(page == NULL ? NULL : page->settings());
5576 if (settings == NULL)
5579 no = settings->JavaScriptCanOpenWindowsAutomatically();
5580 settings->setJavaScriptCanOpenWindowsAutomatically(true);
5583 [delegate_ clearFirstResponder];
5584 JSObjectRef function([function_ JSObject]);
5585 JSGlobalContextRef context([frame globalContext]);
5586 JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
5588 if (settings != NULL)
5589 settings->setJavaScriptCanOpenWindowsAutomatically(no);
5593 - (id) _rightButtonTitle {
5594 return button_ != nil ? button_ : @"Reload";
5597 - (id) rightButtonTitle {
5598 return [self _loading] ? @"" : [self _rightButtonTitle];
5601 - (UINavigationButtonStyle) rightButtonStyle {
5602 if (style_ == nil) normal:
5603 return UINavigationButtonStyleNormal;
5604 else if ([style_ isEqualToString:@"Normal"])
5605 return UINavigationButtonStyleNormal;
5606 else if ([style_ isEqualToString:@"Back"])
5607 return UINavigationButtonStyleBack;
5608 else if ([style_ isEqualToString:@"Highlighted"])
5609 return UINavigationButtonStyleHighlighted;
5610 else if ([style_ isEqualToString:@"Destructive"])
5611 return UINavigationButtonStyleDestructive;
5615 - (NSString *) title {
5616 return title_ == nil ? @"Loading" : title_;
5619 - (NSString *) backButtonTitle {
5623 - (void) setPageActive:(BOOL)active {
5625 [indicator_ removeFromSuperview];
5627 [[book_ navigationBar] addSubview:indicator_];
5630 - (void) resetViewAnimated:(BOOL)animated {
5633 - (void) setPushed:(bool)pushed {
5640 /* Cydia Book {{{ */
5641 @interface CYBook : RVBook <
5644 _transient Database *database_;
5645 UINavigationBar *overlay_;
5646 UINavigationBar *underlay_;
5647 UIProgressIndicator *indicator_;
5648 UITextLabel *prompt_;
5649 UIProgressBar *progress_;
5650 UINavigationButton *cancel_;
5653 NSTimeInterval last_;
5656 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5662 @implementation CYBook
5666 [indicator_ release];
5668 [progress_ release];
5673 - (NSString *) getTitleForPage:(RVPage *)page {
5674 return Simplify([super getTitleForPage:page]);
5682 [UIView beginAnimations:nil context:NULL];
5684 CGRect ovrframe = [overlay_ frame];
5685 ovrframe.origin.y = 0;
5686 [overlay_ setFrame:ovrframe];
5688 CGRect barframe = [navbar_ frame];
5689 barframe.origin.y += ovrframe.size.height;
5690 [navbar_ setFrame:barframe];
5692 CGRect trnframe = [transition_ frame];
5693 trnframe.origin.y += ovrframe.size.height;
5694 trnframe.size.height -= ovrframe.size.height;
5695 [transition_ setFrame:trnframe];
5697 [UIView endAnimations];
5699 [indicator_ startAnimation];
5700 [prompt_ setText:@"Updating Database"];
5701 [progress_ setProgress:0];
5704 last_ = [NSDate timeIntervalSinceReferenceDate];
5706 [overlay_ addSubview:cancel_];
5709 detachNewThreadSelector:@selector(_update)
5718 [indicator_ stopAnimation];
5720 [UIView beginAnimations:nil context:NULL];
5722 CGRect ovrframe = [overlay_ frame];
5723 ovrframe.origin.y = -ovrframe.size.height;
5724 [overlay_ setFrame:ovrframe];
5726 CGRect barframe = [navbar_ frame];
5727 barframe.origin.y -= ovrframe.size.height;
5728 [navbar_ setFrame:barframe];
5730 CGRect trnframe = [transition_ frame];
5731 trnframe.origin.y -= ovrframe.size.height;
5732 trnframe.size.height += ovrframe.size.height;
5733 [transition_ setFrame:trnframe];
5735 [UIView commitAnimations];
5737 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5740 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5741 if ((self = [super initWithFrame:frame]) != nil) {
5742 database_ = database;
5744 CGRect ovrrect = [navbar_ bounds];
5745 ovrrect.size.height = [UINavigationBar defaultSize].height;
5746 ovrrect.origin.y = -ovrrect.size.height;
5748 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5749 [self addSubview:overlay_];
5751 ovrrect.origin.y = frame.size.height;
5752 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5753 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5754 [self addSubview:underlay_];
5756 [overlay_ setBarStyle:1];
5757 [underlay_ setBarStyle:1];
5759 int barstyle = [overlay_ _barStyle:NO];
5760 bool ugly = barstyle == 0;
5762 UIProgressIndicatorStyle style = ugly ?
5763 UIProgressIndicatorStyleMediumBrown :
5764 UIProgressIndicatorStyleMediumWhite;
5766 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5767 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5768 CGRect indrect = {{indoffset, indoffset}, indsize};
5770 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5771 [indicator_ setStyle:style];
5772 [overlay_ addSubview:indicator_];
5774 CGSize prmsize = {215, indsize.height + 4};
5777 indoffset * 2 + indsize.width,
5781 unsigned(ovrrect.size.height - prmsize.height) / 2
5784 UIFont *font = [UIFont systemFontOfSize:15];
5786 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5788 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5789 [prompt_ setBackgroundColor:[UIColor clearColor]];
5790 [prompt_ setFont:font];
5792 [overlay_ addSubview:prompt_];
5794 CGSize prgsize = {75, 100};
5797 ovrrect.size.width - prgsize.width - 10,
5798 (ovrrect.size.height - prgsize.height) / 2
5801 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5802 [progress_ setStyle:0];
5803 [overlay_ addSubview:progress_];
5805 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5806 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5808 CGRect frame = [cancel_ frame];
5809 frame.size.width = 65;
5810 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5811 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5812 [cancel_ setFrame:frame];
5814 [cancel_ setBarStyle:barstyle];
5818 - (void) _onCancel {
5820 [cancel_ removeFromSuperview];
5823 - (void) _update { _pooled
5825 status.setDelegate(self);
5827 [database_ updateWithStatus:status];
5830 performSelectorOnMainThread:@selector(_update_)
5836 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5837 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5840 - (void) setProgressTitle:(NSString *)title {
5842 performSelectorOnMainThread:@selector(_setProgressTitle:)
5848 - (void) setProgressPercent:(float)percent {
5850 performSelectorOnMainThread:@selector(_setProgressPercent:)
5851 withObject:[NSNumber numberWithFloat:percent]
5856 - (void) startProgress {
5859 - (void) addProgressOutput:(NSString *)output {
5861 performSelectorOnMainThread:@selector(_addProgressOutput:)
5867 - (bool) isCancelling:(size_t)received {
5868 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5869 if (received_ != received) {
5870 received_ = received;
5872 } else if (now - last_ > 15)
5877 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5881 - (void) _setProgressTitle:(NSString *)title {
5882 [prompt_ setText:title];
5885 - (void) _setProgressPercent:(NSNumber *)percent {
5886 [progress_ setProgress:[percent floatValue]];
5889 - (void) _addProgressOutput:(NSString *)output {
5894 /* Cydia:// Protocol {{{ */
5895 @interface CydiaURLProtocol : NSURLProtocol {
5900 @implementation CydiaURLProtocol
5902 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5903 NSURL *url([request URL]);
5906 NSString *scheme([[url scheme] lowercaseString]);
5907 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5912 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5916 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5917 id<NSURLProtocolClient> client([self client]);
5918 NSData *data(UIImagePNGRepresentation(icon));
5920 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5921 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5922 [client URLProtocol:self didLoadData:data];
5923 [client URLProtocolDidFinishLoading:self];
5926 - (void) startLoading {
5927 id<NSURLProtocolClient> client([self client]);
5928 NSURLRequest *request([self request]);
5930 NSURL *url([request URL]);
5931 NSString *href([url absoluteString]);
5933 NSString *path([href substringFromIndex:8]);
5934 NSRange slash([path rangeOfString:@"/"]);
5937 if (slash.location == NSNotFound) {
5941 command = [path substringToIndex:slash.location];
5942 path = [path substringFromIndex:(slash.location + 1)];
5945 Database *database([Database sharedInstance]);
5947 if ([command isEqualToString:@"package-icon"]) {
5950 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5951 Package *package([database packageWithName:path]);
5954 UIImage *icon([package icon]);
5955 [self _returnPNGWithImage:icon forRequest:request];
5956 } else if ([command isEqualToString:@"source-icon"]) {
5959 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5960 NSString *source(Simplify(path));
5961 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5963 icon = [UIImage applicationImageNamed:@"unknown.png"];
5964 [self _returnPNGWithImage:icon forRequest:request];
5965 } else if ([command isEqualToString:@"uikit-image"]) {
5968 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5969 UIImage *icon(_UIImageWithName(path));
5970 [self _returnPNGWithImage:icon forRequest:request];
5971 } else if ([command isEqualToString:@"section-icon"]) {
5974 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5975 NSString *section(Simplify(path));
5976 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5978 icon = [UIImage applicationImageNamed:@"unknown.png"];
5979 [self _returnPNGWithImage:icon forRequest:request];
5981 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5985 - (void) stopLoading {
5991 /* Install View {{{ */
5992 @interface InstallView : RVPage {
5993 _transient Database *database_;
5994 NSMutableArray *sections_;
5995 NSMutableArray *filtered_;
5996 UITransitionView *transition_;
6002 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6003 - (void) reloadData;
6008 @implementation InstallView
6011 [list_ setDataSource:nil];
6012 [list_ setDelegate:nil];
6014 [sections_ release];
6015 [filtered_ release];
6016 [transition_ release];
6018 [accessory_ release];
6022 - (int) numberOfRowsInTable:(UITable *)table {
6023 return editing_ ? [sections_ count] : [filtered_ count] + 1;
6026 - (float) table:(UITable *)table heightForRow:(int)row {
6030 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
6032 reusing = [[[SectionCell alloc] init] autorelease];
6033 [(SectionCell *)reusing setSection:(editing_ ?
6034 [sections_ objectAtIndex:row] :
6035 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
6036 ) editing:editing_];
6040 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
6044 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
6048 - (void) tableRowSelected:(NSNotification *)notification {
6049 int row = [[notification object] selectedRow];
6060 title = @"All Packages";
6062 section = [filtered_ objectAtIndex:(row - 1)];
6063 name = [section name];
6069 title = @"(No Section)";
6073 PackageTable *table = [[[PackageTable alloc]
6077 filter:@selector(isVisiblyUninstalledInSection:)
6081 [table setDelegate:delegate_];
6083 [book_ pushPage:table];
6086 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6087 if ((self = [super initWithBook:book]) != nil) {
6088 database_ = database;
6090 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6091 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
6093 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
6094 [self addSubview:transition_];
6096 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
6097 [transition_ transition:0 toView:list_];
6099 UITableColumn *column = [[[UITableColumn alloc]
6100 initWithTitle:@"Name"
6102 width:[self frame].size.width
6105 [list_ setDataSource:self];
6106 [list_ setSeparatorStyle:1];
6107 [list_ addTableColumn:column];
6108 [list_ setDelegate:self];
6109 [list_ setReusesTableCells:YES];
6113 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6114 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6118 - (void) reloadData {
6119 NSArray *packages = [database_ packages];
6121 [sections_ removeAllObjects];
6122 [filtered_ removeAllObjects];
6124 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
6125 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
6128 for (size_t i(0); i != [packages count]; ++i) {
6129 Package *package([packages objectAtIndex:i]);
6130 NSString *name([package section]);
6133 Section *section([sections objectForKey:name]);
6134 if (section == nil) {
6135 section = [[[Section alloc] initWithName:name] autorelease];
6136 [sections setObject:section forKey:name];
6140 if ([package valid] && [package installed] == nil && [package visible])
6141 [filtered addObject:package];
6145 [sections_ addObjectsFromArray:[sections allValues]];
6146 [sections_ sortUsingSelector:@selector(compareByName:)];
6149 [filtered sortUsingSelector:@selector(compareBySection:)];
6152 Section *section = nil;
6153 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
6154 Package *package = [filtered objectAtIndex:offset];
6155 NSString *name = [package section];
6157 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
6158 section = name == nil ?
6159 [[[Section alloc] initWithName:nil] autorelease] :
6160 [sections objectForKey:name];
6161 [filtered_ addObject:section];
6164 [section addToCount];
6172 - (void) resetView {
6174 [self _rightButtonClicked];
6177 - (void) resetViewAnimated:(BOOL)animated {
6178 [list_ resetViewAnimated:animated];
6181 - (void) _rightButtonClicked {
6182 if ((editing_ = !editing_))
6185 [delegate_ updateData];
6186 [book_ reloadTitleForPage:self];
6187 [book_ reloadButtonsForPage:self];
6190 - (NSString *) title {
6191 return editing_ ? @"Section Visibility" : @"Install by Section";
6194 - (NSString *) backButtonTitle {
6198 - (id) rightButtonTitle {
6199 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
6202 - (UINavigationButtonStyle) rightButtonStyle {
6203 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
6206 - (UIView *) accessoryView {
6212 /* Changes View {{{ */
6213 @interface ChangesView : RVPage {
6214 _transient Database *database_;
6215 NSMutableArray *packages_;
6216 NSMutableArray *sections_;
6217 UISectionList *list_;
6221 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6222 - (void) reloadData;
6226 @implementation ChangesView
6229 [[list_ table] setDelegate:nil];
6230 [list_ setDataSource:nil];
6232 [packages_ release];
6233 [sections_ release];
6238 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
6239 return [sections_ count];
6242 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
6243 return [[sections_ objectAtIndex:section] name];
6246 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
6247 return [[sections_ objectAtIndex:section] row];
6250 - (int) numberOfRowsInTable:(UITable *)table {
6251 return [packages_ count];
6254 - (float) table:(UITable *)table heightForRow:(int)row {
6255 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
6258 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
6260 reusing = [[[PackageCell alloc] init] autorelease];
6261 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
6265 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
6269 - (void) tableRowSelected:(NSNotification *)notification {
6270 int row = [[notification object] selectedRow];
6273 Package *package = [packages_ objectAtIndex:row];
6274 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6275 [view setDelegate:delegate_];
6276 [view setPackage:package];
6277 [book_ pushPage:view];
6280 - (void) _leftButtonClicked {
6281 [(CYBook *)book_ update];
6282 [self reloadButtons];
6285 - (void) _rightButtonClicked {
6286 [delegate_ distUpgrade];
6289 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6290 if ((self = [super initWithBook:book]) != nil) {
6291 database_ = database;
6293 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
6294 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6296 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
6297 [self addSubview:list_];
6299 [list_ setShouldHideHeaderInShortLists:NO];
6300 [list_ setDataSource:self];
6301 //[list_ setSectionListStyle:1];
6303 UITableColumn *column = [[[UITableColumn alloc]
6304 initWithTitle:@"Name"
6306 width:[self frame].size.width
6309 UITable *table = [list_ table];
6310 [table setSeparatorStyle:1];
6311 [table addTableColumn:column];
6312 [table setDelegate:self];
6313 [table setReusesTableCells:YES];
6317 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6318 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6322 - (void) reloadData {
6323 NSArray *packages = [database_ packages];
6325 [packages_ removeAllObjects];
6326 [sections_ removeAllObjects];
6329 for (size_t i(0); i != [packages count]; ++i) {
6330 Package *package([packages objectAtIndex:i]);
6333 [package installed] == nil && [package valid] && [package visible] ||
6334 [package upgradableAndEssential:NO]
6336 [packages_ addObject:package];
6340 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
6343 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
6344 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
6345 Section *section = nil;
6349 bool unseens = false;
6351 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
6354 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6355 Package *package = [packages_ objectAtIndex:offset];
6357 if (![package upgradableAndEssential:YES]) {
6359 NSDate *seen = [package seen];
6361 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6364 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
6365 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6366 [sections_ addObject:section];
6370 [section addToCount];
6371 } else if ([package ignored])
6372 [ignored addToCount];
6375 [upgradable addToCount];
6380 CFRelease(formatter);
6383 Section *last = [sections_ lastObject];
6384 size_t count = [last count];
6385 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6386 [sections_ removeLastObject];
6389 if ([ignored count] != 0)
6390 [sections_ insertObject:ignored atIndex:0];
6392 [sections_ insertObject:upgradable atIndex:0];
6395 [self reloadButtons];
6398 - (void) resetViewAnimated:(BOOL)animated {
6399 [list_ resetViewAnimated:animated];
6402 - (NSString *) leftButtonTitle {
6403 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6406 - (id) rightButtonTitle {
6407 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
6410 - (NSString *) title {
6416 /* Search View {{{ */
6417 @protocol SearchViewDelegate
6418 - (void) showKeyboard:(BOOL)show;
6421 @interface SearchView : RVPage {
6423 UISearchField *field_;
6424 UITransitionView *transition_;
6425 PackageTable *table_;
6426 UIPreferencesTable *advanced_;
6432 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6433 - (void) reloadData;
6437 @implementation SearchView
6440 [field_ setDelegate:nil];
6442 [accessory_ release];
6444 [transition_ release];
6446 [advanced_ release];
6451 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6455 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6457 case 0: return @"Advanced Search (Coming Soon!)";
6459 default: _assert(false);
6463 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6467 default: _assert(false);
6471 - (void) _showKeyboard:(BOOL)show {
6472 CGSize keysize = [UIKeyboard defaultSize];
6473 CGRect keydown = [book_ pageBounds];
6474 CGRect keyup = keydown;
6475 keyup.size.height -= keysize.height - ButtonBarHeight_;
6477 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6479 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6480 [animation setSignificantRectFields:8];
6483 [animation setStartFrame:keydown];
6484 [animation setEndFrame:keyup];
6486 [animation setStartFrame:keyup];
6487 [animation setEndFrame:keydown];
6490 UIAnimator *animator = [UIAnimator sharedAnimator];
6493 addAnimations:[NSArray arrayWithObjects:animation, nil]
6494 withDuration:(KeyboardTime_ - delay)
6499 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6501 [delegate_ showKeyboard:show];
6504 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6505 [self _showKeyboard:YES];
6508 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6509 [self _showKeyboard:NO];
6512 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6514 NSString *text([field_ text]);
6515 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6521 - (void) textFieldClearButtonPressed:(UITextField *)field {
6525 - (void) keyboardInputShouldDelete:(id)input {
6529 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6530 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6534 [field_ resignFirstResponder];
6539 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6540 if ((self = [super initWithBook:book]) != nil) {
6541 CGRect pageBounds = [book_ pageBounds];
6543 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6544 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6545 [self addSubview:pinstripe];*/
6547 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6548 [self addSubview:transition_];
6550 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6552 [advanced_ setReusesTableCells:YES];
6553 [advanced_ setDataSource:self];
6554 [advanced_ reloadData];
6556 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6557 CGColor dimmed(space_, 0, 0, 0, 0.5);
6558 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6560 table_ = [[PackageTable alloc]
6564 filter:@selector(isUnfilteredAndSearchedForBy:)
6568 [table_ setShouldHideHeaderInShortLists:NO];
6569 [transition_ transition:0 toView:table_];
6578 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6585 [self bounds].size.width - area.origin.x - 18;
6587 area.size.height = [UISearchField defaultHeight];
6589 field_ = [[UISearchField alloc] initWithFrame:area];
6591 UIFont *font = [UIFont systemFontOfSize:16];
6592 [field_ setFont:font];
6594 [field_ setPlaceholder:@"Package Names & Descriptions"];
6595 [field_ setDelegate:self];
6597 [field_ setPaddingTop:5];
6599 UITextInputTraits *traits([field_ textInputTraits]);
6600 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6601 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6602 [traits setReturnKeyType:UIReturnKeySearch];
6604 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6606 accessory_ = [[UIView alloc] initWithFrame:accrect];
6607 [accessory_ addSubview:field_];
6609 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6610 [configure setShowPressFeedback:YES];
6611 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6612 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6613 [accessory_ addSubview:configure];*/
6615 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6616 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6622 LKAnimation *animation = [LKTransition animation];
6623 [animation setType:@"oglFlip"];
6624 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6625 [animation setFillMode:@"extended"];
6626 [animation setTransitionFlags:3];
6627 [animation setDuration:10];
6628 [animation setSpeed:0.35];
6629 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6630 [[transition_ _layer] addAnimation:animation forKey:0];
6631 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6632 flipped_ = !flipped_;
6636 - (void) configurePushed {
6637 [field_ resignFirstResponder];
6641 - (void) resetViewAnimated:(BOOL)animated {
6644 [table_ resetViewAnimated:animated];
6647 - (void) _reloadData {
6650 - (void) reloadData {
6653 [table_ setObject:[field_ text]];
6654 [table_ reloadData];
6655 [table_ resetCursor];
6658 - (UIView *) accessoryView {
6662 - (NSString *) title {
6666 - (NSString *) backButtonTitle {
6670 - (void) setDelegate:(id)delegate {
6671 [table_ setDelegate:delegate];
6672 [super setDelegate:delegate];
6678 @interface SettingsView : RVPage {
6679 _transient Database *database_;
6682 UIPreferencesTable *table_;
6683 _UISwitchSlider *subscribedSwitch_;
6684 _UISwitchSlider *ignoredSwitch_;
6685 UIPreferencesControlTableCell *subscribedCell_;
6686 UIPreferencesControlTableCell *ignoredCell_;
6689 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6693 @implementation SettingsView
6696 [table_ setDataSource:nil];
6699 if (package_ != nil)
6702 [subscribedSwitch_ release];
6703 [ignoredSwitch_ release];
6704 [subscribedCell_ release];
6705 [ignoredCell_ release];
6709 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6710 if (package_ == nil)
6716 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6717 if (package_ == nil)
6724 default: _assert(false);
6730 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6731 if (package_ == nil)
6738 default: _assert(false);
6744 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6745 if (package_ == nil)
6752 default: _assert(false);
6758 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6759 if (package_ == nil)
6762 _UISwitchSlider *slider([cell control]);
6763 BOOL value([slider value] != 0);
6764 NSMutableDictionary *metadata([package_ metadata]);
6767 if (NSNumber *number = [metadata objectForKey:key])
6768 before = [number boolValue];
6772 if (value != before) {
6773 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6775 [delegate_ updateData];
6779 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6780 [self onSomething:cell withKey:@"IsSubscribed"];
6783 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6784 [self onSomething:cell withKey:@"IsIgnored"];
6787 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6788 if (package_ == nil)
6792 case 0: switch (row) {
6794 return subscribedCell_;
6796 return ignoredCell_;
6797 default: _assert(false);
6800 case 1: switch (row) {
6802 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6803 [cell setShowSelection:NO];
6804 [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."];
6808 default: _assert(false);
6811 default: _assert(false);
6817 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6818 if ((self = [super initWithBook:book])) {
6819 database_ = database;
6820 name_ = [package retain];
6822 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6823 [self addSubview:table_];
6825 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6826 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6828 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6829 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6831 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6832 [subscribedCell_ setShowSelection:NO];
6833 [subscribedCell_ setTitle:@"Show All Changes"];
6834 [subscribedCell_ setControl:subscribedSwitch_];
6836 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6837 [ignoredCell_ setShowSelection:NO];
6838 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6839 [ignoredCell_ setControl:ignoredSwitch_];
6841 [table_ setDataSource:self];
6846 - (void) resetViewAnimated:(BOOL)animated {
6847 [table_ resetViewAnimated:animated];
6850 - (void) reloadData {
6851 if (package_ != nil)
6852 [package_ autorelease];
6853 package_ = [database_ packageWithName:name_];
6854 if (package_ != nil) {
6856 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6857 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6860 [table_ reloadData];
6863 - (NSString *) title {
6869 /* Signature View {{{ */
6870 @interface SignatureView : BrowserView {
6871 _transient Database *database_;
6875 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6879 @implementation SignatureView
6886 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6888 [super webView:sender didClearWindowObject:window forFrame:frame];
6891 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6892 if ((self = [super initWithBook:book]) != nil) {
6893 database_ = database;
6894 package_ = [package retain];
6899 - (void) resetViewAnimated:(BOOL)animated {
6902 - (void) reloadData {
6903 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6909 @interface Cydia : UIApplication <
6910 ConfirmationViewDelegate,
6911 ProgressViewDelegate,
6920 UIToolbar *buttonbar_;
6924 NSMutableArray *essential_;
6925 NSMutableArray *broken_;
6927 Database *database_;
6928 ProgressView *progress_;
6932 UIKeyboard *keyboard_;
6933 UIProgressHUD *hud_;
6935 InstallView *install_;
6936 ChangesView *changes_;
6937 ManageView *manage_;
6938 SearchView *search_;
6943 @implementation Cydia
6946 if ([broken_ count] != 0) {
6947 int count = [broken_ count];
6949 UIActionSheet *sheet = [[[UIActionSheet alloc]
6950 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6951 buttons:[NSArray arrayWithObjects:
6953 @"Ignore (Temporary)",
6955 defaultButtonIndex:0
6960 [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."];
6961 [sheet popupAlertAnimated:YES];
6962 } else if (!Ignored_ && [essential_ count] != 0) {
6963 int count = [essential_ count];
6965 UIActionSheet *sheet = [[[UIActionSheet alloc]
6966 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6967 buttons:[NSArray arrayWithObjects:
6968 @"Upgrade Essential",
6969 @"Complete Upgrade",
6970 @"Ignore (Temporary)",
6972 defaultButtonIndex:0
6977 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6978 [sheet popupAlertAnimated:YES];
6982 - (void) _reloadData {
6983 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6984 [hud setText:@"Reloading Data"];
6985 [overlay_ addSubview:hud];
6988 [database_ reloadData];
6992 [essential_ removeAllObjects];
6993 [broken_ removeAllObjects];
6995 NSArray *packages = [database_ packages];
6996 for (Package *package in packages) {
6998 [broken_ addObject:package];
6999 if ([package upgradableAndEssential:NO]) {
7000 if ([package essential])
7001 [essential_ addObject:package];
7007 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
7008 [buttonbar_ setBadgeValue:badge forButton:3];
7009 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
7010 [buttonbar_ setBadgeAnimated:YES forButton:3];
7011 [self setApplicationBadge:badge];
7013 [buttonbar_ setBadgeValue:nil forButton:3];
7014 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
7015 [buttonbar_ setBadgeAnimated:NO forButton:3];
7016 [self removeApplicationBadge];
7022 if ([packages count] == 0);
7034 [hud removeFromSuperview];*/
7037 - (void) _saveConfig {
7040 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
7046 - (void) updateData {
7049 /* XXX: this is just stupid */
7050 if (tag_ != 2 && install_ != nil)
7051 [install_ reloadData];
7052 if (tag_ != 3 && changes_ != nil)
7053 [changes_ reloadData];
7054 if (tag_ != 5 && search_ != nil)
7055 [search_ reloadData];
7065 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
7066 _assert(file != NULL);
7068 NSArray *keys = [Sources_ allKeys];
7070 for (int i(0), e([keys count]); i != e; ++i) {
7071 NSString *key = [keys objectAtIndex:i];
7072 NSDictionary *source = [Sources_ objectForKey:key];
7074 fprintf(file, "%s %s %s\n",
7075 [[source objectForKey:@"Type"] UTF8String],
7076 [[source objectForKey:@"URI"] UTF8String],
7077 [[source objectForKey:@"Distribution"] UTF8String]
7086 detachNewThreadSelector:@selector(update_)
7089 title:@"Updating Sources"
7093 - (void) reloadData {
7094 @synchronized (self) {
7095 if (confirm_ == nil)
7101 pkgProblemResolver *resolver = [database_ resolver];
7103 resolver->InstallProtect();
7104 if (!resolver->Resolve(true))
7109 [database_ prepare];
7111 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
7112 [confirm_ setDelegate:self];
7114 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
7115 [page setDelegate:self];
7117 [confirm_ setPage:page];
7118 [underlay_ popSubview:confirm_];
7121 - (void) installPackage:(Package *)package {
7122 @synchronized (self) {
7129 - (void) removePackage:(Package *)package {
7130 @synchronized (self) {
7137 - (void) distUpgrade {
7138 @synchronized (self) {
7139 [database_ upgrade];
7145 @synchronized (self) {
7147 if (confirm_ != nil) {
7155 [overlay_ removeFromSuperview];
7159 detachNewThreadSelector:@selector(perform)
7166 - (void) bootstrap_ {
7168 [database_ upgrade];
7169 [database_ prepare];
7170 [database_ perform];
7173 - (void) bootstrap {
7175 detachNewThreadSelector:@selector(bootstrap_)
7178 title:@"Bootstrap Install"
7182 - (void) progressViewIsComplete:(ProgressView *)progress {
7183 if (confirm_ != nil) {
7184 [underlay_ addSubview:overlay_];
7185 [confirm_ popFromSuperviewAnimated:NO];
7191 - (void) setPage:(RVPage *)page {
7192 [page resetViewAnimated:NO];
7193 [page setDelegate:self];
7194 [book_ setPage:page];
7197 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
7198 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
7199 [browser loadURL:url];
7203 - (void) _setHomePage {
7204 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
7207 - (void) buttonBarItemTapped:(id)sender {
7208 unsigned tag = [sender tag];
7210 [book_ resetViewAnimated:YES];
7212 } else if (tag_ == 2 && tag != 2)
7213 [install_ resetView];
7216 case 1: [self _setHomePage]; break;
7218 case 2: [self setPage:install_]; break;
7219 case 3: [self setPage:changes_]; break;
7220 case 4: [self setPage:manage_]; break;
7221 case 5: [self setPage:search_]; break;
7223 default: _assert(false);
7229 - (void) applicationWillSuspend {
7231 [super applicationWillSuspend];
7234 - (void) askForSettings {
7235 UIActionSheet *role = [[[UIActionSheet alloc]
7236 initWithTitle:@"Who Are You?"
7237 buttons:[NSArray arrayWithObjects:
7238 @"User (Graphical Only)",
7239 @"Hacker (+ Command Line)",
7240 @"Developer (No Filters)",
7242 defaultButtonIndex:-1
7247 [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."];
7248 [role popupAlertAnimated:YES];
7253 [self setStatusBarShowsProgress:NO];
7256 [hud_ removeFromSuperview];
7260 pid_t pid = ExecFork();
7262 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
7263 perror("launchctl stop");
7270 [self askForSettings];
7274 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
7276 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7277 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
7278 0, 0, screenrect.size.width, screenrect.size.height - 48
7279 ) database:database_];
7281 [book_ setDelegate:self];
7283 [overlay_ addSubview:book_];
7285 NSArray *buttonitems = [NSArray arrayWithObjects:
7286 [NSDictionary dictionaryWithObjectsAndKeys:
7287 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7288 @"home-up.png", kUIButtonBarButtonInfo,
7289 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
7290 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
7291 self, kUIButtonBarButtonTarget,
7292 @"Home", kUIButtonBarButtonTitle,
7293 @"0", kUIButtonBarButtonType,
7296 [NSDictionary dictionaryWithObjectsAndKeys:
7297 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7298 @"install-up.png", kUIButtonBarButtonInfo,
7299 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
7300 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
7301 self, kUIButtonBarButtonTarget,
7302 @"Sections", kUIButtonBarButtonTitle,
7303 @"0", kUIButtonBarButtonType,
7306 [NSDictionary dictionaryWithObjectsAndKeys:
7307 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7308 @"changes-up.png", kUIButtonBarButtonInfo,
7309 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
7310 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
7311 self, kUIButtonBarButtonTarget,
7312 @"Changes", kUIButtonBarButtonTitle,
7313 @"0", kUIButtonBarButtonType,
7316 [NSDictionary dictionaryWithObjectsAndKeys:
7317 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7318 @"manage-up.png", kUIButtonBarButtonInfo,
7319 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
7320 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
7321 self, kUIButtonBarButtonTarget,
7322 @"Manage", kUIButtonBarButtonTitle,
7323 @"0", kUIButtonBarButtonType,
7326 [NSDictionary dictionaryWithObjectsAndKeys:
7327 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7328 @"search-up.png", kUIButtonBarButtonInfo,
7329 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
7330 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
7331 self, kUIButtonBarButtonTarget,
7332 @"Search", kUIButtonBarButtonTitle,
7333 @"0", kUIButtonBarButtonType,
7337 buttonbar_ = [[UIToolbar alloc]
7339 withFrame:CGRectMake(
7340 0, screenrect.size.height - ButtonBarHeight_,
7341 screenrect.size.width, ButtonBarHeight_
7343 withItemList:buttonitems
7346 [buttonbar_ setDelegate:self];
7347 [buttonbar_ setBarStyle:1];
7348 [buttonbar_ setButtonBarTrackingMode:2];
7350 int buttons[5] = {1, 2, 3, 4, 5};
7351 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7352 [buttonbar_ showButtonGroup:0 withDuration:0];
7354 for (int i = 0; i != 5; ++i)
7355 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
7356 i * 64 + 2, 1, 60, ButtonBarHeight_
7359 [buttonbar_ showSelectionForButton:1];
7360 [overlay_ addSubview:buttonbar_];
7362 [UIKeyboard initImplementationNow];
7363 CGSize keysize = [UIKeyboard defaultSize];
7364 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
7365 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
7366 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
7367 [overlay_ addSubview:keyboard_];
7370 [underlay_ addSubview:overlay_];
7374 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
7375 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
7376 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7378 manage_ = (ManageView *) [[self
7379 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7380 withClass:[ManageView class]
7386 [self _setHomePage];
7389 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7390 NSString *context([sheet context]);
7392 if ([context isEqualToString:@"fixhalf"]) {
7395 @synchronized (self) {
7396 for (int i = 0, e = [broken_ count]; i != e; ++i) {
7397 Package *broken = [broken_ objectAtIndex:i];
7400 NSString *id = [broken id];
7401 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7402 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7403 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7404 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7413 [broken_ removeAllObjects];
7422 } else if ([context isEqualToString:@"role"]) {
7424 case 1: Role_ = @"User"; break;
7425 case 2: Role_ = @"Hacker"; break;
7426 case 3: Role_ = @"Developer"; break;
7433 bool reset = Settings_ != nil;
7435 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7439 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7449 } else if ([context isEqualToString:@"upgrade"]) {
7452 @synchronized (self) {
7453 for (int i = 0, e = [essential_ count]; i != e; ++i) {
7454 Package *essential = [essential_ objectAtIndex:i];
7455 [essential install];
7479 - (void) reorganize { _pooled
7480 system("/usr/libexec/cydia/free.sh");
7481 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7484 - (void) applicationSuspend:(__GSEvent *)event {
7485 if (hud_ == nil && ![progress_ isRunning])
7486 [super applicationSuspend:event];
7489 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7491 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7494 - (void) _setSuspended:(BOOL)value {
7496 [super _setSuspended:value];
7499 - (UIProgressHUD *) addProgressHUD {
7500 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7502 [underlay_ addSubview:hud];
7506 - (void) openMailToURL:(NSURL *)url {
7507 // XXX: this makes me sad
7509 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7511 [UIApp openURL:url];// asPanel:YES];
7515 - (void) clearFirstResponder {
7516 if (id responder = [window_ firstResponder])
7517 [responder resignFirstResponder];
7520 - (RVPage *) pageForPackage:(NSString *)name {
7521 if (Package *package = [database_ packageWithName:name]) {
7522 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7523 [view setPackage:package];
7526 UIActionSheet *sheet = [[[UIActionSheet alloc]
7527 initWithTitle:@"Cannot Locate Package"
7528 buttons:[NSArray arrayWithObjects:@"Close", nil]
7529 defaultButtonIndex:0
7534 [sheet setBodyText:[NSString stringWithFormat:
7535 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7538 [sheet popupAlertAnimated:YES];
7543 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7544 NSString *href = [url absoluteString];
7549 if ([href isEqualToString:@"cydia://add-source"])
7550 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7551 else if ([href isEqualToString:@"cydia://sources"])
7552 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7553 else if ([href isEqualToString:@"cydia://packages"])
7554 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7555 else if ([href hasPrefix:@"cydia://url/"])
7556 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7557 else if ([href hasPrefix:@"cydia://launch/"])
7558 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7559 else if ([href hasPrefix:@"cydia://package-settings/"])
7560 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7561 else if ([href hasPrefix:@"cydia://package-signature/"])
7562 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7563 else if ([href hasPrefix:@"cydia://package/"])
7564 return [self pageForPackage:[href substringFromIndex:16]];
7565 else if ([href hasPrefix:@"cydia://files/"]) {
7566 NSString *name = [href substringFromIndex:14];
7568 if (Package *package = [database_ packageWithName:name]) {
7569 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7570 [files setPackage:package];
7578 - (void) applicationOpenURL:(NSURL *)url {
7579 [super applicationOpenURL:url];
7581 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7582 [self setPage:page];
7583 [buttonbar_ showSelectionForButton:tag];
7588 - (void) applicationDidFinishLaunching:(id)unused {
7589 Font12_ = [[UIFont systemFontOfSize:12] retain];
7590 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7591 Font14_ = [[UIFont systemFontOfSize:14] retain];
7592 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7593 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7595 _assert(pkgInitConfig(*_config));
7596 _assert(pkgInitSystem(*_config, _system));
7600 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7601 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7603 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7605 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7606 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7608 [window_ orderFront:self];
7609 [window_ makeKey:self];
7610 [window_ setHidden:NO];
7612 database_ = [Database sharedInstance];
7613 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7614 [database_ setDelegate:progress_];
7615 [window_ setContentView:progress_];
7617 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7618 [progress_ setContentView:underlay_];
7620 [progress_ resetView];
7623 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7624 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7625 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7626 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7627 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7628 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7629 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7631 [self setIdleTimerDisabled:YES];
7633 hud_ = [self addProgressHUD];
7634 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7636 [self setStatusBarShowsProgress:YES];
7639 detachNewThreadSelector:@selector(reorganize)
7647 - (void) showKeyboard:(BOOL)show {
7648 CGSize keysize = [UIKeyboard defaultSize];
7649 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7650 CGRect keyup = keydown;
7651 keyup.origin.y -= keysize.height;
7653 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7654 [animation setSignificantRectFields:2];
7657 [animation setStartFrame:keydown];
7658 [animation setEndFrame:keyup];
7659 [keyboard_ activate];
7661 [animation setStartFrame:keyup];
7662 [animation setEndFrame:keydown];
7663 [keyboard_ deactivate];
7666 [[UIAnimator sharedAnimator]
7667 addAnimations:[NSArray arrayWithObjects:animation, nil]
7668 withDuration:KeyboardTime_
7673 - (void) slideUp:(UIActionSheet *)alert {
7675 [alert presentSheetFromButtonBar:buttonbar_];
7677 [alert presentSheetInView:overlay_];
7682 void AddPreferences(NSString *plist) { _pooled
7683 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7684 _assert(settings != NULL);
7685 NSMutableArray *items = [settings objectForKey:@"items"];
7689 for (size_t i(0); i != [items count]; ++i) {
7690 NSMutableDictionary *item([items objectAtIndex:i]);
7691 NSString *label = [item objectForKey:@"label"];
7692 if (label != nil && [label isEqualToString:@"Cydia"]) {
7699 for (size_t i(0); i != [items count]; ++i) {
7700 NSDictionary *item([items objectAtIndex:i]);
7701 NSString *label = [item objectForKey:@"label"];
7702 if (label != nil && [label isEqualToString:@"General"]) {
7703 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7704 @"CydiaSettings", @"bundle",
7705 @"PSLinkCell", @"cell",
7706 [NSNumber numberWithBool:YES], @"hasIcon",
7707 [NSNumber numberWithBool:YES], @"isController",
7709 nil] atIndex:(i + 1)];
7715 _assert([settings writeToFile:plist atomically:YES] == YES);
7720 id Alloc_(id self, SEL selector) {
7721 id object = alloc_(self, selector);
7722 lprintf("[%s]A-%p\n", self->isa->name, object);
7727 id Dealloc_(id self, SEL selector) {
7728 id object = dealloc_(self, selector);
7729 lprintf("[%s]D-%p\n", self->isa->name, object);
7733 int main(int argc, char *argv[]) { _pooled
7734 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7736 bool substrate(false);
7742 for (int argi(1); argi != argc; ++argi)
7743 if (strcmp(argv[argi], "--") == 0) {
7745 argv[argi] = argv[0];
7751 for (int argi(1); argi != arge; ++argi)
7752 if (strcmp(args[argi], "--bootstrap") == 0)
7754 else if (strcmp(args[argi], "--substrate") == 0)
7757 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7760 App_ = [[NSBundle mainBundle] bundlePath];
7761 Home_ = NSHomeDirectory();
7762 Locale_ = CFLocaleCopyCurrent();
7765 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7766 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7767 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7768 Sounds_Keyboard_ = [keyboard boolValue];
7774 #if 1 /* XXX: this costs 1.4s of startup performance */
7775 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7776 _assert(errno == ENOENT);
7777 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7778 _assert(errno == ENOENT);
7781 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7782 alloc_ = alloc->method_imp;
7783 alloc->method_imp = (IMP) &Alloc_;*/
7785 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7786 dealloc_ = dealloc->method_imp;
7787 dealloc->method_imp = (IMP) &Dealloc_;*/
7792 size = sizeof(maxproc);
7793 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7794 perror("sysctlbyname(\"kern.maxproc\", ?)");
7795 else if (maxproc < 64) {
7797 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7798 perror("sysctlbyname(\"kern.maxproc\", #)");
7801 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7802 char *machine = new char[size];
7803 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7804 perror("sysctlbyname(\"hw.machine\", ?)");
7808 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7810 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7811 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7813 if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7814 Indices_ = [[NSMutableDictionary alloc] init];
7816 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7817 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7819 Settings_ = [Metadata_ objectForKey:@"Settings"];
7821 Packages_ = [Metadata_ objectForKey:@"Packages"];
7822 Sections_ = [Metadata_ objectForKey:@"Sections"];
7823 Sources_ = [Metadata_ objectForKey:@"Sources"];
7826 if (Settings_ != nil)
7827 Role_ = [Settings_ objectForKey:@"Role"];
7829 if (Packages_ == nil) {
7830 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7831 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7834 if (Sections_ == nil) {
7835 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7836 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7839 if (Sources_ == nil) {
7840 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7841 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7845 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7848 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7849 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7851 if (access("/User", F_OK) != 0)
7852 system("/usr/libexec/cydia/firmware.sh");
7854 _assert([[NSFileManager defaultManager]
7855 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7856 withIntermediateDirectories:YES
7861 space_ = CGColorSpaceCreateDeviceRGB();
7863 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7864 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7865 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7866 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7867 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7868 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7870 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7872 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7874 UIApplicationUseLegacyEvents(YES);
7875 UIKeyboardDisableAutomaticAppearance();
7877 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7879 CGColorSpaceRelease(space_);