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/WebScriptObject.h>
66 #import <WebKit/WebView.h>
67 #import <WebKit/WebView-WebPrivate.h>
69 #import <JavaScriptCore/JavaScriptCore.h>
74 #include <ext/stdio_filebuf.h>
76 #include <apt-pkg/acquire.h>
77 #include <apt-pkg/acquire-item.h>
78 #include <apt-pkg/algorithms.h>
79 #include <apt-pkg/cachefile.h>
80 #include <apt-pkg/clean.h>
81 #include <apt-pkg/configuration.h>
82 #include <apt-pkg/debmetaindex.h>
83 #include <apt-pkg/error.h>
84 #include <apt-pkg/init.h>
85 #include <apt-pkg/mmap.h>
86 #include <apt-pkg/pkgrecords.h>
87 #include <apt-pkg/sha1.h>
88 #include <apt-pkg/sourcelist.h>
89 #include <apt-pkg/sptr.h>
90 #include <apt-pkg/strutl.h>
92 #include <sys/types.h>
94 #include <sys/sysctl.h>
100 #include <mach-o/nlist.h>
110 #import "BrowserView.h"
111 #import "ResetView.h"
114 //#define _finline __attribute__((force_inline))
115 #define _finline inline
120 #define _limit(count) do { \
121 static size_t _count(0); \
122 if (++_count == count) \
126 static uint64_t profile_;
128 #define _timestamp ({ \
130 gettimeofday(&tv, NULL); \
131 tv.tv_sec * 1000000 + tv.tv_usec; \
134 /* Objective-C Handle<> {{{ */
135 template <typename Type_>
137 typedef _H<Type_> This_;
142 _finline void Retain_() {
147 _finline void Clear_() {
153 _finline _H(Type_ *value = NULL, bool mended = false) :
164 _finline This_ &operator =(Type_ *value) {
165 if (value_ != value) {
174 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
176 void NSLogPoint(const char *fix, const CGPoint &point) {
177 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
180 void NSLogRect(const char *fix, const CGRect &rect) {
181 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
184 /* NSForcedOrderingSearch doesn't work on the iPhone */
185 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
186 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
187 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
189 /* iPhoneOS 2.0 Compatibility {{{ */
191 @interface UITextView (iPhoneOS)
192 - (void) setTextSize:(float)size;
195 @implementation UITextView (iPhoneOS)
197 - (void) setTextSize:(float)size {
198 [self setFont:[[self font] fontWithSize:size]];
205 extern NSString * const kCAFilterNearest;
207 /* Information Dictionaries {{{ */
208 @interface NSMutableArray (Cydia)
209 - (void) addInfoDictionary:(NSDictionary *)info;
212 @implementation NSMutableArray (Cydia)
214 - (void) addInfoDictionary:(NSDictionary *)info {
215 [self addObject:info];
220 @interface NSMutableDictionary (Cydia)
221 - (void) addInfoDictionary:(NSDictionary *)info;
224 @implementation NSMutableDictionary (Cydia)
226 - (void) addInfoDictionary:(NSDictionary *)info {
227 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
228 [self setObject:info forKey:bundle];
233 /* Pop Transitions {{{ */
234 @interface PopTransitionView : UITransitionView {
239 @implementation PopTransitionView
241 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
242 if (from != nil && to == nil)
243 [self removeFromSuperview];
248 @interface UIView (PopUpView)
249 - (void) popFromSuperviewAnimated:(BOOL)animated;
250 - (void) popSubview:(UIView *)view;
253 @implementation UIView (PopUpView)
255 - (void) popFromSuperviewAnimated:(BOOL)animated {
256 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
259 - (void) popSubview:(UIView *)view {
260 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
261 [transition setDelegate:transition];
262 [self addSubview:transition];
264 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
265 [transition transition:UITransitionNone toView:blank];
266 [transition transition:UITransitionPushFromBottom toView:view];
272 #define lprintf(args...) fprintf(stderr, args)
275 #define ForSaurik (1 && !ForRelease)
276 #define RecycleWebViews 0
277 #define AlwaysReload (1 && !ForRelease)
280 @interface NSMutableArray (Radix)
281 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
284 @implementation NSMutableArray (Radix)
286 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
287 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
288 [invocation setSelector:selector];
289 [invocation setArgument:&object atIndex:2];
291 size_t count([self count]);
296 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
298 for (size_t i(0); i != count; ++i) {
299 RadixItem &item(lhs[i]);
302 id object([self objectAtIndex:i]);
303 [invocation setTarget:object];
306 [invocation getReturnValue:&item.key];
309 static const size_t width = 32;
310 static const size_t bits = 11;
311 static const size_t slots = 1 << bits;
312 static const size_t passes = (width + (bits - 1)) / bits;
314 size_t *hist(new size_t[slots]);
316 for (size_t pass(0); pass != passes; ++pass) {
317 memset(hist, 0, sizeof(size_t) * slots);
319 for (size_t i(0); i != count; ++i) {
320 uint32_t key(lhs[i].key);
322 key &= _not(uint32_t) >> width - bits;
327 for (size_t i(0); i != slots; ++i) {
328 size_t local(offset);
333 for (size_t i(0); i != count; ++i) {
334 uint32_t key(lhs[i].key);
336 key &= _not(uint32_t) >> width - bits;
337 rhs[hist[key]++] = lhs[i];
347 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
348 for (size_t i(0); i != count; ++i)
349 [values addObject:[self objectAtIndex:lhs[i].index]];
350 [self setArray:values];
358 /* Apple Bug Fixes {{{ */
359 @implementation UIWebDocumentView (Cydia)
361 - (void) _setScrollerOffset:(CGPoint)offset {
362 UIScroller *scroller([self _scroller]);
364 CGSize size([scroller contentSize]);
365 CGSize bounds([scroller bounds].size);
368 max.x = size.width - bounds.width;
369 max.y = size.height - bounds.height;
377 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
378 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
380 [scroller setOffset:offset];
387 kUIControlEventMouseDown = 1 << 0,
388 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
389 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
390 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
391 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
392 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
393 } UIControlEventMasks;
395 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
396 size_t length([self length] - state->state);
399 else if (length > count)
401 for (size_t i(0); i != length; ++i)
402 objects[i] = [self item:state->state++];
403 state->itemsPtr = objects;
404 state->mutationsPtr = (unsigned long *) self;
408 @interface NSString (UIKit)
409 - (NSString *) stringByAddingPercentEscapes;
410 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
413 @interface NSString (Cydia)
414 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
415 - (NSComparisonResult) compareByPath:(NSString *)other;
418 @implementation NSString (Cydia)
420 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
421 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
424 - (NSComparisonResult) compareByPath:(NSString *)other {
425 NSString *prefix = [self commonPrefixWithString:other options:0];
426 size_t length = [prefix length];
428 NSRange lrange = NSMakeRange(length, [self length] - length);
429 NSRange rrange = NSMakeRange(length, [other length] - length);
431 lrange = [self rangeOfString:@"/" options:0 range:lrange];
432 rrange = [other rangeOfString:@"/" options:0 range:rrange];
434 NSComparisonResult value;
436 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
437 value = NSOrderedSame;
438 else if (lrange.location == NSNotFound)
439 value = NSOrderedAscending;
440 else if (rrange.location == NSNotFound)
441 value = NSOrderedDescending;
443 value = NSOrderedSame;
445 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
446 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
447 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
448 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
450 NSComparisonResult result = [lpath compare:rpath];
451 return result == NSOrderedSame ? value : result;
456 /* Perl-Compatible RegEx {{{ */
466 Pcre(const char *regex) :
471 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
474 lprintf("%d:%s\n", offset, error);
478 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
479 matches_ = new int[(capture_ + 1) * 3];
487 NSString *operator [](size_t match) {
488 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
491 bool operator ()(NSString *data) {
492 // XXX: length is for characters, not for bytes
493 return operator ()([data UTF8String], [data length]);
496 bool operator ()(const char *data, size_t size) {
498 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
502 /* Mime Addresses {{{ */
503 @interface Address : NSObject {
509 - (NSString *) address;
511 + (Address *) addressWithString:(NSString *)string;
512 - (Address *) initWithString:(NSString *)string;
515 @implementation Address
524 - (NSString *) name {
528 - (NSString *) address {
532 + (Address *) addressWithString:(NSString *)string {
533 return [[[Address alloc] initWithString:string] autorelease];
536 + (NSArray *) _attributeKeys {
537 return [NSArray arrayWithObjects:@"address", @"name", nil];
540 - (NSArray *) attributeKeys {
541 return [[self class] _attributeKeys];
544 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
545 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
548 - (Address *) initWithString:(NSString *)string {
549 if ((self = [super init]) != nil) {
550 const char *data = [string UTF8String];
551 size_t size = [string length];
553 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
555 if (address_r(data, size)) {
556 name_ = [address_r[1] retain];
557 address_ = [address_r[2] retain];
559 name_ = [string retain];
567 /* CoreGraphics Primitives {{{ */
578 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
581 Set(space, red, green, blue, alpha);
586 CGColorRelease(color_);
593 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
595 float color[] = {red, green, blue, alpha};
596 color_ = CGColorCreate(space, color);
599 operator CGColorRef() {
605 extern "C" void UISetColor(CGColorRef color);
607 /* Random Global Variables {{{ */
608 static const int PulseInterval_ = 50000;
609 static const int ButtonBarHeight_ = 48;
610 static const float KeyboardTime_ = 0.3f;
612 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
613 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
615 static CGColor Blue_;
616 static CGColor Blueish_;
617 static CGColor Black_;
619 static CGColor White_;
620 static CGColor Gray_;
622 static NSString *App_;
623 static NSString *Home_;
624 static BOOL Sounds_Keyboard_;
626 static BOOL Advanced_;
630 static BOOL Ignored_;
632 static UIFont *Font12_;
633 static UIFont *Font12Bold_;
634 static UIFont *Font14_;
635 static UIFont *Font18Bold_;
636 static UIFont *Font22Bold_;
638 static const char *Machine_ = NULL;
639 static const NSString *UniqueID_ = NULL;
646 CGColorSpaceRef space_;
648 #define FW_LEAST(major, minor, bugfix) \
649 (major < Major_ || major == Major_ && \
650 (minor < Minor_ || minor == Minor_ && \
656 static NSDictionary *SectionMap_;
657 static NSMutableDictionary *Metadata_;
658 static NSMutableDictionary *Indices_;
659 static _transient NSMutableDictionary *Settings_;
660 static _transient NSString *Role_;
661 static _transient NSMutableDictionary *Packages_;
662 static _transient NSMutableDictionary *Sections_;
663 static _transient NSMutableDictionary *Sources_;
664 static bool Changed_;
668 static NSMutableArray *Documents_;
671 NSString *GetLastUpdate() {
672 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
675 return @"Never or Unknown";
677 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
678 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
680 CFRelease(formatter);
682 return [(NSString *) formatted autorelease];
685 /* Display Helpers {{{ */
686 inline float Interpolate(float begin, float end, float fraction) {
687 return (end - begin) * fraction + begin;
690 NSString *SizeString(double size) {
691 bool negative = size < 0;
696 while (size > 1024) {
701 static const char *powers_[] = {"B", "kB", "MB", "GB"};
703 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
706 NSString *StripVersion(NSString *version) {
707 NSRange colon = [version rangeOfString:@":"];
708 if (colon.location != NSNotFound)
709 version = [version substringFromIndex:(colon.location + 1)];
713 NSString *Simplify(NSString *title) {
714 const char *data = [title UTF8String];
715 size_t size = [title length];
717 static Pcre square_r("^\\[(.*)\\]$");
718 if (square_r(data, size))
719 return Simplify(square_r[1]);
721 static Pcre paren_r("^\\((.*)\\)$");
722 if (paren_r(data, size))
723 return Simplify(paren_r[1]);
725 static Pcre title_r("^(.*?) \\(.*\\)$");
726 if (title_r(data, size))
727 return Simplify(title_r[1]);
733 bool isSectionVisible(NSString *section) {
734 NSDictionary *metadata = [Sections_ objectForKey:section];
735 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
736 return hidden == nil || ![hidden boolValue];
739 /* Delegate Prototypes {{{ */
743 @interface NSObject (ProgressDelegate)
746 @implementation NSObject(ProgressDelegate)
748 - (void) _setProgressError:(NSArray *)args {
749 [self performSelector:@selector(setProgressError:forPackage:)
750 withObject:[args objectAtIndex:0]
751 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
757 @protocol ProgressDelegate
758 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
759 - (void) setProgressTitle:(NSString *)title;
760 - (void) setProgressPercent:(float)percent;
761 - (void) startProgress;
762 - (void) addProgressOutput:(NSString *)output;
763 - (bool) isCancelling:(size_t)received;
766 @protocol ConfigurationDelegate
767 - (void) repairWithSelector:(SEL)selector;
768 - (void) setConfigurationData:(NSString *)data;
771 @protocol CydiaDelegate
772 - (void) installPackage:(Package *)package;
773 - (void) removePackage:(Package *)package;
774 - (void) slideUp:(UIActionSheet *)alert;
775 - (void) distUpgrade;
778 - (void) askForSettings;
779 - (UIProgressHUD *) addProgressHUD;
780 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
781 - (RVPage *) pageForPackage:(NSString *)name;
782 - (void) openMailToURL:(NSURL *)url;
786 /* Status Delegation {{{ */
788 public pkgAcquireStatus
791 _transient NSObject<ProgressDelegate> *delegate_;
799 void setDelegate(id delegate) {
800 delegate_ = delegate;
803 virtual bool MediaChange(std::string media, std::string drive) {
807 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
810 virtual void Fetch(pkgAcquire::ItemDesc &item) {
811 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
812 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
815 virtual void Done(pkgAcquire::ItemDesc &item) {
818 virtual void Fail(pkgAcquire::ItemDesc &item) {
820 item.Owner->Status == pkgAcquire::Item::StatIdle ||
821 item.Owner->Status == pkgAcquire::Item::StatDone
825 std::string &error(item.Owner->ErrorText);
829 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
830 NSArray *fields([description componentsSeparatedByString:@" "]);
831 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
833 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
834 withObject:[NSArray arrayWithObjects:
835 [NSString stringWithUTF8String:error.c_str()],
842 virtual bool Pulse(pkgAcquire *Owner) {
843 bool value = pkgAcquireStatus::Pulse(Owner);
846 double(CurrentBytes + CurrentItems) /
847 double(TotalBytes + TotalItems)
850 [delegate_ setProgressPercent:percent];
851 return [delegate_ isCancelling:CurrentBytes] ? false : value;
854 virtual void Start() {
855 [delegate_ startProgress];
858 virtual void Stop() {
862 /* Progress Delegation {{{ */
867 _transient id<ProgressDelegate> delegate_;
870 virtual void Update() {
871 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
872 [delegate_ setProgressPercent:(Percent / 100)];
881 void setDelegate(id delegate) {
882 delegate_ = delegate;
885 virtual void Done() {
886 [delegate_ setProgressPercent:1];
891 /* Database Interface {{{ */
892 @interface Database : NSObject {
894 pkgDepCache::Policy *policy_;
895 pkgRecords *records_;
896 pkgProblemResolver *resolver_;
897 pkgAcquire *fetcher_;
899 SPtr<pkgPackageManager> manager_;
900 pkgSourceList *list_;
902 NSMutableDictionary *sources_;
903 NSMutableArray *packages_;
905 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
914 + (Database *) sharedInstance;
916 - (void) _readCydia:(NSNumber *)fd;
917 - (void) _readStatus:(NSNumber *)fd;
918 - (void) _readOutput:(NSNumber *)fd;
922 - (Package *) packageWithName:(NSString *)name;
924 - (pkgCacheFile &) cache;
925 - (pkgDepCache::Policy *) policy;
926 - (pkgRecords *) records;
927 - (pkgProblemResolver *) resolver;
928 - (pkgAcquire &) fetcher;
929 - (NSArray *) packages;
930 - (NSArray *) sources;
939 - (void) updateWithStatus:(Status &)status;
941 - (void) setDelegate:(id)delegate;
942 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
946 /* Source Class {{{ */
947 @interface Source : NSObject {
948 NSString *description_;
953 NSString *distribution_;
957 NSString *defaultIcon_;
959 NSDictionary *record_;
963 - (Source *) initWithMetaIndex:(metaIndex *)index;
965 - (NSComparisonResult) compareByNameAndType:(Source *)source;
967 - (NSDictionary *) record;
971 - (NSString *) distribution;
977 - (NSString *) description;
978 - (NSString *) label;
979 - (NSString *) origin;
980 - (NSString *) version;
982 - (NSString *) defaultIcon;
986 @implementation Source
990 [distribution_ release];
993 if (description_ != nil)
994 [description_ release];
1001 if (defaultIcon_ != nil)
1002 [defaultIcon_ release];
1009 + (NSArray *) _attributeKeys {
1010 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1013 - (NSArray *) attributeKeys {
1014 return [[self class] _attributeKeys];
1017 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1018 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1021 - (Source *) initWithMetaIndex:(metaIndex *)index {
1022 if ((self = [super init]) != nil) {
1023 trusted_ = index->IsTrusted();
1025 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1026 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1027 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1029 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1030 if (dindex != NULL) {
1031 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1033 while (std::getline(release, line)) {
1034 std::string::size_type colon(line.find(':'));
1035 if (colon == std::string::npos)
1038 std::string name(line.substr(0, colon));
1039 std::string value(line.substr(colon + 1));
1040 while (!value.empty() && value[0] == ' ')
1041 value = value.substr(1);
1043 if (name == "Default-Icon")
1044 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1045 else if (name == "Description")
1046 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1047 else if (name == "Label")
1048 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1049 else if (name == "Origin")
1050 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1051 else if (name == "Version")
1052 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1056 record_ = [Sources_ objectForKey:[self key]];
1058 record_ = [record_ retain];
1062 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1063 NSDictionary *lhr = [self record];
1064 NSDictionary *rhr = [source record];
1067 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1069 NSString *lhs = [self name];
1070 NSString *rhs = [source name];
1072 if ([lhs length] != 0 && [rhs length] != 0) {
1073 unichar lhc = [lhs characterAtIndex:0];
1074 unichar rhc = [rhs characterAtIndex:0];
1076 if (isalpha(lhc) && !isalpha(rhc))
1077 return NSOrderedAscending;
1078 else if (!isalpha(lhc) && isalpha(rhc))
1079 return NSOrderedDescending;
1082 return [lhs compare:rhs options:LaxCompareOptions_];
1085 - (NSDictionary *) record {
1093 - (NSString *) uri {
1097 - (NSString *) distribution {
1098 return distribution_;
1101 - (NSString *) type {
1105 - (NSString *) key {
1106 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1109 - (NSString *) host {
1110 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1113 - (NSString *) name {
1114 return origin_ == nil ? [self host] : origin_;
1117 - (NSString *) description {
1118 return description_;
1121 - (NSString *) label {
1122 return label_ == nil ? [self host] : label_;
1125 - (NSString *) origin {
1129 - (NSString *) version {
1133 - (NSString *) defaultIcon {
1134 return defaultIcon_;
1139 /* Relationship Class {{{ */
1140 @interface Relationship : NSObject {
1145 - (NSString *) type;
1147 - (NSString *) name;
1151 @implementation Relationship
1159 - (NSString *) type {
1167 - (NSString *) name {
1174 /* Package Class {{{ */
1175 @interface Package : NSObject {
1176 pkgCache::PkgIterator iterator_;
1177 _transient Database *database_;
1178 pkgCache::VerIterator version_;
1179 pkgCache::VerFileIterator file_;
1187 NSString *installed_;
1193 NSString *depiction_;
1194 NSString *homepage_;
1200 NSArray *relationships_;
1203 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1204 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1206 - (pkgCache::PkgIterator) iterator;
1208 - (NSString *) section;
1209 - (NSString *) simpleSection;
1211 - (Address *) maintainer;
1213 - (NSString *) description;
1214 - (NSString *) index;
1216 - (NSMutableDictionary *) metadata;
1218 - (BOOL) subscribed;
1221 - (NSString *) latest;
1222 - (NSString *) installed;
1225 - (BOOL) upgradableAndEssential:(BOOL)essential;
1228 - (BOOL) unfiltered;
1232 - (BOOL) halfConfigured;
1233 - (BOOL) halfInstalled;
1235 - (NSString *) mode;
1238 - (NSString *) name;
1239 - (NSString *) tagline;
1241 - (NSString *) homepage;
1242 - (NSString *) depiction;
1243 - (Address *) author;
1245 - (NSArray *) files;
1246 - (NSArray *) relationships;
1247 - (NSArray *) warnings;
1248 - (NSArray *) applications;
1250 - (Source *) source;
1251 - (NSString *) role;
1252 - (NSString *) rating;
1254 - (BOOL) matches:(NSString *)text;
1256 - (bool) hasSupportingRole;
1257 - (BOOL) hasTag:(NSString *)tag;
1258 - (NSString *) primaryPurpose;
1259 - (NSArray *) purposes;
1261 - (NSComparisonResult) compareByName:(Package *)package;
1262 - (NSComparisonResult) compareBySection:(Package *)package;
1264 - (uint32_t) compareForChanges;
1269 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1270 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1271 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1272 - (NSNumber *) isVisibleInSource:(Source *)source;
1276 @implementation Package
1282 if (section_ != nil)
1286 if (installed_ != nil)
1287 [installed_ release];
1295 if (depiction_ != nil)
1296 [depiction_ release];
1297 if (homepage_ != nil)
1298 [homepage_ release];
1299 if (sponsor_ != nil)
1308 if (relationships_ != nil)
1309 [relationships_ release];
1314 + (NSArray *) _attributeKeys {
1315 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1318 - (NSArray *) attributeKeys {
1319 return [[self class] _attributeKeys];
1322 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1323 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1326 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1327 if ((self = [super init]) != nil) {
1328 iterator_ = iterator;
1329 database_ = database;
1331 version_ = [database_ policy]->GetCandidateVer(iterator_);
1332 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1333 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1335 pkgCache::VerIterator current = iterator_.CurrentVer();
1336 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1337 installed_ = [StripVersion(installed) retain];
1339 if (!version_.end())
1340 file_ = version_.FileList();
1342 pkgCache &cache([database_ cache]);
1343 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1346 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1349 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1351 const char *begin, *end;
1352 parser->GetRec(begin, end);
1354 NSString *website(nil);
1355 NSString *sponsor(nil);
1356 NSString *author(nil);
1365 {"depiction", &depiction_},
1366 {"homepage", &homepage_},
1367 {"website", &website},
1368 {"sponsor", &sponsor},
1369 {"author", &author},
1373 while (begin != end)
1374 if (*begin == '\n') {
1377 } else if (isblank(*begin)) next: {
1378 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1381 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1382 const char *name(begin);
1383 size_t size(colon - begin);
1385 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1388 const char *stop(begin == NULL ? end : begin);
1389 while (stop[-1] == '\r')
1391 while (++colon != stop && isblank(*colon));
1393 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1394 if (strncasecmp(names[i].name_, name, size) == 0) {
1395 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1396 *names[i].value_ = value;
1407 name_ = [name_ retain];
1408 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1410 icon_ = [icon_ retain];
1411 if (depiction_ != nil)
1412 depiction_ = [depiction_ retain];
1413 if (homepage_ == nil)
1414 homepage_ = website;
1415 if ([homepage_ isEqualToString:depiction_])
1417 if (homepage_ != nil)
1418 homepage_ = [homepage_ retain];
1420 sponsor_ = [[Address addressWithString:sponsor] retain];
1422 author_ = [[Address addressWithString:author] retain];
1424 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1428 for (int i(0), e([tags_ count]); i != e; ++i) {
1429 NSString *tag = [tags_ objectAtIndex:i];
1430 if ([tag hasPrefix:@"role::"]) {
1431 role_ = [[tag substringFromIndex:6] retain];
1436 NSString *solid(latest == nil ? installed : latest);
1437 bool changed(false);
1439 NSString *key([id_ lowercaseString]);
1441 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1442 if (metadata == nil) {
1443 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1448 [metadata setObject:solid forKey:@"LastVersion"];
1451 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1452 NSDate *last([metadata objectForKey:@"LastSeen"]);
1453 NSString *version([metadata objectForKey:@"LastVersion"]);
1456 first = last == nil ? now_ : last;
1457 [metadata setObject:first forKey:@"FirstSeen"];
1462 if (version == nil) {
1463 [metadata setObject:solid forKey:@"LastVersion"];
1465 } else if (![version isEqualToString:solid]) {
1466 [metadata setObject:solid forKey:@"LastVersion"];
1468 [metadata setObject:last forKey:@"LastSeen"];
1474 [Packages_ setObject:metadata forKey:key];
1480 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1481 return [[[Package alloc]
1482 initWithIterator:iterator
1487 - (pkgCache::PkgIterator) iterator {
1491 - (NSString *) section {
1492 if (section_ != nil)
1495 const char *section = iterator_.Section();
1496 if (section == NULL)
1499 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1502 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1503 if (NSString *rename = [value objectForKey:@"Rename"]) {
1508 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1512 - (NSString *) simpleSection {
1513 if (NSString *section = [self section])
1514 return Simplify(section);
1520 - (Address *) maintainer {
1523 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1524 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1528 return version_.end() ? 0 : version_->InstalledSize;
1531 - (NSString *) description {
1534 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1535 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1537 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1538 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1539 if ([lines count] < 2)
1542 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1543 for (size_t i(1); i != [lines count]; ++i) {
1544 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1545 [trimmed addObject:trim];
1548 return [trimmed componentsJoinedByString:@"\n"];
1551 - (NSString *) index {
1552 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1553 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1556 - (NSMutableDictionary *) metadata {
1557 return [Packages_ objectForKey:[id_ lowercaseString]];
1561 NSDictionary *metadata([self metadata]);
1562 if ([self subscribed])
1563 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1565 return [metadata objectForKey:@"FirstSeen"];
1568 - (BOOL) subscribed {
1569 NSDictionary *metadata([self metadata]);
1570 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1571 return [subscribed boolValue];
1577 NSDictionary *metadata([self metadata]);
1578 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1579 return [ignored boolValue];
1584 - (NSString *) latest {
1588 - (NSString *) installed {
1593 return !version_.end();
1596 - (BOOL) upgradableAndEssential:(BOOL)essential {
1597 pkgCache::VerIterator current = iterator_.CurrentVer();
1600 return essential && [self essential];
1602 return !version_.end() && version_ != current;
1605 - (BOOL) essential {
1606 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1610 return [database_ cache][iterator_].InstBroken();
1613 - (BOOL) unfiltered {
1614 NSString *section = [self section];
1615 return section == nil || isSectionVisible(section);
1619 return [self hasSupportingRole] && [self unfiltered];
1623 unsigned char current = iterator_->CurrentState;
1624 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1627 - (BOOL) halfConfigured {
1628 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1631 - (BOOL) halfInstalled {
1632 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1636 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1637 return state.Mode != pkgDepCache::ModeKeep;
1640 - (NSString *) mode {
1641 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1643 switch (state.Mode) {
1644 case pkgDepCache::ModeDelete:
1645 if ((state.iFlags & pkgDepCache::Purge) != 0)
1649 case pkgDepCache::ModeKeep:
1650 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1654 case pkgDepCache::ModeInstall:
1655 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1656 return @"Reinstall";
1657 else switch (state.Status) {
1659 return @"Downgrade";
1665 return @"New Install";
1678 - (NSString *) name {
1679 return name_ == nil ? id_ : name_;
1682 - (NSString *) tagline {
1686 - (UIImage *) icon {
1687 NSString *section = [self simpleSection];
1690 if (NSString *icon = icon_)
1691 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1692 if (icon == nil) if (section != nil)
1693 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1694 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1695 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1697 icon = [UIImage applicationImageNamed:@"unknown.png"];
1701 - (NSString *) homepage {
1705 - (NSString *) depiction {
1709 - (Address *) sponsor {
1713 - (Address *) author {
1717 - (NSArray *) files {
1718 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1719 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1722 fin.open([path UTF8String]);
1727 while (std::getline(fin, line))
1728 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1733 - (NSArray *) relationships {
1734 return relationships_;
1737 - (NSArray *) warnings {
1738 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1739 const char *name(iterator_.Name());
1741 size_t length(strlen(name));
1742 if (length < 2) invalid:
1743 [warnings addObject:@"illegal package identifier"];
1744 else for (size_t i(0); i != length; ++i)
1746 (name[i] < 'a' || name[i] > 'z') &&
1747 (name[i] < '0' || name[i] > '9') &&
1748 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1751 if (strcmp(name, "cydia") != 0) {
1755 if (NSArray *files = [self files])
1756 for (NSString *file in files)
1757 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1759 else if (!stash && [file isEqualToString:@"/var/stash"])
1763 [warnings addObject:@"files installed into Cydia.app"];
1765 [warnings addObject:@"files installed to /var/stash"];
1768 return [warnings count] == 0 ? nil : warnings;
1771 - (NSArray *) applications {
1772 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1774 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1776 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1777 if (NSArray *files = [self files])
1778 for (NSString *file in files)
1779 if (application_r(file)) {
1780 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1781 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1782 if ([id isEqualToString:me])
1785 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1787 display = application_r[1];
1789 NSString *bundle([file stringByDeletingLastPathComponent]);
1790 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1791 if (icon == nil || [icon length] == 0)
1793 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1795 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1796 [applications addObject:application];
1798 [application addObject:id];
1799 [application addObject:display];
1800 [application addObject:url];
1803 return [applications count] == 0 ? nil : applications;
1806 - (Source *) source {
1808 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1815 - (NSString *) role {
1819 - (NSString *) rating {
1820 if (NSString *rating = [Indices_ objectForKey:@"Rating"]) {
1821 rating = [NSString stringWithFormat:@"%@?package=%@", rating,
1822 [id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1824 rating = [NSString stringWithFormat:@"%@&version=%@", rating,
1825 [latest_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1831 - (BOOL) matches:(NSString *)text {
1837 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1838 if (range.location != NSNotFound)
1841 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1842 if (range.location != NSNotFound)
1845 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1846 if (range.location != NSNotFound)
1852 - (bool) hasSupportingRole {
1855 if ([role_ isEqualToString:@"enduser"])
1857 if ([Role_ isEqualToString:@"User"])
1859 if ([role_ isEqualToString:@"hacker"])
1861 if ([Role_ isEqualToString:@"Hacker"])
1863 if ([role_ isEqualToString:@"developer"])
1865 if ([Role_ isEqualToString:@"Developer"])
1870 - (BOOL) hasTag:(NSString *)tag {
1871 return tags_ == nil ? NO : [tags_ containsObject:tag];
1874 - (NSString *) primaryPurpose {
1875 for (NSString *tag in tags_)
1876 if ([tag hasPrefix:@"purpose::"])
1877 return [tag substringFromIndex:9];
1881 - (NSArray *) purposes {
1882 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1883 for (NSString *tag in tags_)
1884 if ([tag hasPrefix:@"purpose::"])
1885 [purposes addObject:[tag substringFromIndex:9]];
1886 return [purposes count] == 0 ? nil : purposes;
1889 - (NSComparisonResult) compareByName:(Package *)package {
1890 NSString *lhs = [self name];
1891 NSString *rhs = [package name];
1893 if ([lhs length] != 0 && [rhs length] != 0) {
1894 unichar lhc = [lhs characterAtIndex:0];
1895 unichar rhc = [rhs characterAtIndex:0];
1897 if (isalpha(lhc) && !isalpha(rhc))
1898 return NSOrderedAscending;
1899 else if (!isalpha(lhc) && isalpha(rhc))
1900 return NSOrderedDescending;
1903 return [lhs compare:rhs options:LaxCompareOptions_];
1906 - (NSComparisonResult) compareBySection:(Package *)package {
1907 NSString *lhs = [self section];
1908 NSString *rhs = [package section];
1910 if (lhs == NULL && rhs != NULL)
1911 return NSOrderedAscending;
1912 else if (lhs != NULL && rhs == NULL)
1913 return NSOrderedDescending;
1914 else if (lhs != NULL && rhs != NULL) {
1915 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1916 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1919 return NSOrderedSame;
1922 - (uint32_t) compareForChanges {
1927 uint32_t timestamp : 30;
1928 uint32_t ignored : 1;
1929 uint32_t upgradable : 1;
1933 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1935 if ([self upgradableAndEssential:YES]) {
1936 value.bits.timestamp = 0;
1937 value.bits.ignored = [self ignored] ? 0 : 1;
1938 value.bits.upgradable = 1;
1940 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1941 value.bits.ignored = 0;
1942 value.bits.upgradable = 0;
1945 return _not(uint32_t) - value.key;
1949 pkgProblemResolver *resolver = [database_ resolver];
1950 resolver->Clear(iterator_);
1951 resolver->Protect(iterator_);
1952 pkgCacheFile &cache([database_ cache]);
1953 cache->MarkInstall(iterator_, false);
1954 pkgDepCache::StateCache &state((*cache)[iterator_]);
1955 if (!state.Install())
1956 cache->SetReInstall(iterator_, true);
1960 pkgProblemResolver *resolver = [database_ resolver];
1961 resolver->Clear(iterator_);
1962 resolver->Protect(iterator_);
1963 resolver->Remove(iterator_);
1964 [database_ cache]->MarkDelete(iterator_, true);
1967 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1968 return [NSNumber numberWithBool:(
1969 [self unfiltered] && [self matches:search]
1973 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1974 return [NSNumber numberWithBool:(
1975 (![number boolValue] || [self visible]) && [self installed] != nil
1979 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1980 NSString *section = [self section];
1982 return [NSNumber numberWithBool:(
1984 [self installed] == nil && (
1986 section == nil && [name length] == 0 ||
1987 [name isEqualToString:section]
1992 - (NSNumber *) isVisibleInSource:(Source *)source {
1993 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1998 /* Section Class {{{ */
1999 @interface Section : NSObject {
2005 - (NSComparisonResult) compareByName:(Section *)section;
2006 - (Section *) initWithName:(NSString *)name;
2007 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2008 - (NSString *) name;
2011 - (void) addToCount;
2015 @implementation Section
2022 - (NSComparisonResult) compareByName:(Section *)section {
2023 NSString *lhs = [self name];
2024 NSString *rhs = [section name];
2026 if ([lhs length] != 0 && [rhs length] != 0) {
2027 unichar lhc = [lhs characterAtIndex:0];
2028 unichar rhc = [rhs characterAtIndex:0];
2030 if (isalpha(lhc) && !isalpha(rhc))
2031 return NSOrderedAscending;
2032 else if (!isalpha(lhc) && isalpha(rhc))
2033 return NSOrderedDescending;
2036 return [lhs compare:rhs options:LaxCompareOptions_];
2039 - (Section *) initWithName:(NSString *)name {
2040 return [self initWithName:name row:0];
2043 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2044 if ((self = [super init]) != nil) {
2045 name_ = [name retain];
2050 - (NSString *) name {
2062 - (void) addToCount {
2070 static NSArray *Finishes_;
2072 /* Database Implementation {{{ */
2073 @implementation Database
2075 + (Database *) sharedInstance {
2076 static Database *instance;
2077 if (instance == nil)
2078 instance = [[Database alloc] init];
2087 - (void) _readCydia:(NSNumber *)fd { _pooled
2088 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2089 std::istream is(&ib);
2092 static Pcre finish_r("^finish:([^:]*)$");
2094 while (std::getline(is, line)) {
2095 const char *data(line.c_str());
2096 size_t size = line.size();
2097 lprintf("C:%s\n", data);
2099 if (finish_r(data, size)) {
2100 NSString *finish = finish_r[1];
2101 int index = [Finishes_ indexOfObject:finish];
2102 if (index != INT_MAX && index > Finish_)
2110 - (void) _readStatus:(NSNumber *)fd { _pooled
2111 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2112 std::istream is(&ib);
2115 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2116 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2118 while (std::getline(is, line)) {
2119 const char *data(line.c_str());
2120 size_t size = line.size();
2121 lprintf("S:%s\n", data);
2123 if (conffile_r(data, size)) {
2124 [delegate_ setConfigurationData:conffile_r[1]];
2125 } else if (strncmp(data, "status: ", 8) == 0) {
2126 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2127 [delegate_ setProgressTitle:string];
2128 } else if (pmstatus_r(data, size)) {
2129 std::string type([pmstatus_r[1] UTF8String]);
2130 NSString *id = pmstatus_r[2];
2132 float percent([pmstatus_r[3] floatValue]);
2133 [delegate_ setProgressPercent:(percent / 100)];
2135 NSString *string = pmstatus_r[4];
2137 if (type == "pmerror")
2138 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2139 withObject:[NSArray arrayWithObjects:string, id, nil]
2142 else if (type == "pmstatus") {
2143 [delegate_ setProgressTitle:string];
2144 } else if (type == "pmconffile")
2145 [delegate_ setConfigurationData:string];
2146 else _assert(false);
2147 } else _assert(false);
2153 - (void) _readOutput:(NSNumber *)fd { _pooled
2154 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2155 std::istream is(&ib);
2158 while (std::getline(is, line)) {
2159 lprintf("O:%s\n", line.c_str());
2160 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2170 - (Package *) packageWithName:(NSString *)name {
2171 if (static_cast<pkgDepCache *>(cache_) == NULL)
2173 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2174 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2177 - (Database *) init {
2178 if ((self = [super init]) != nil) {
2185 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2186 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2190 _assert(pipe(fds) != -1);
2193 _config->Set("APT::Keep-Fds::", cydiafd_);
2194 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2197 detachNewThreadSelector:@selector(_readCydia:)
2199 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2202 _assert(pipe(fds) != -1);
2206 detachNewThreadSelector:@selector(_readStatus:)
2208 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2211 _assert(pipe(fds) != -1);
2212 _assert(dup2(fds[0], 0) != -1);
2213 _assert(close(fds[0]) != -1);
2215 input_ = fdopen(fds[1], "a");
2217 _assert(pipe(fds) != -1);
2218 _assert(dup2(fds[1], 1) != -1);
2219 _assert(close(fds[1]) != -1);
2222 detachNewThreadSelector:@selector(_readOutput:)
2224 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2229 - (pkgCacheFile &) cache {
2233 - (pkgDepCache::Policy *) policy {
2237 - (pkgRecords *) records {
2241 - (pkgProblemResolver *) resolver {
2245 - (pkgAcquire &) fetcher {
2249 - (NSArray *) packages {
2253 - (NSArray *) sources {
2254 return [sources_ allValues];
2257 - (NSArray *) issues {
2258 if (cache_->BrokenCount() == 0)
2261 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2263 for (Package *package in packages_) {
2264 if (![package broken])
2266 pkgCache::PkgIterator pkg([package iterator]);
2268 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2269 [entry addObject:[package name]];
2270 [issues addObject:entry];
2272 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2276 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2277 pkgCache::DepIterator start;
2278 pkgCache::DepIterator end;
2279 dep.GlobOr(start, end); // ++dep
2281 if (!cache_->IsImportantDep(end))
2283 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2286 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2287 [entry addObject:failure];
2288 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2290 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2291 [failure addObject:[package name]];
2293 pkgCache::PkgIterator target(start.TargetPkg());
2294 if (target->ProvidesList != 0)
2295 [failure addObject:@"?"];
2297 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2299 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2300 else if (!cache_[target].CandidateVerIter(cache_).end())
2301 [failure addObject:@"-"];
2302 else if (target->ProvidesList == 0)
2303 [failure addObject:@"!"];
2305 [failure addObject:@"%"];
2309 if (start.TargetVer() != 0)
2310 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2321 - (void) reloadData {
2341 if (!cache_.Open(progress_, true)) {
2343 if (!_error->PopMessage(error))
2346 lprintf("cache_.Open():[%s]\n", error.c_str());
2348 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2349 [delegate_ repairWithSelector:@selector(configure)];
2350 else if (error == "The package lists or status file could not be parsed or opened.")
2351 [delegate_ repairWithSelector:@selector(update)];
2352 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2353 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2354 // else if (error == "The list of sources could not be read.")
2355 else _assert(false);
2361 now_ = [[NSDate date] retain];
2363 policy_ = new pkgDepCache::Policy();
2364 records_ = new pkgRecords(cache_);
2365 resolver_ = new pkgProblemResolver(cache_);
2366 fetcher_ = new pkgAcquire(&status_);
2369 list_ = new pkgSourceList();
2370 _assert(list_->ReadMainList());
2372 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2373 _assert(pkgApplyStatus(cache_));
2375 if (cache_->BrokenCount() != 0) {
2376 _assert(pkgFixBroken(cache_));
2377 _assert(cache_->BrokenCount() == 0);
2378 _assert(pkgMinimizeUpgrade(cache_));
2381 [sources_ removeAllObjects];
2382 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2383 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2384 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2386 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2387 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2391 [packages_ removeAllObjects];
2394 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2395 if (Package *package = [Package packageWithIterator:iterator database:self])
2396 [packages_ addObject:package];
2398 [packages_ sortUsingSelector:@selector(compareByName:)];
2402 - (void) configure {
2403 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2404 system([dpkg UTF8String]);
2412 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2413 _assert(!_error->PendingError());
2416 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2419 public pkgArchiveCleaner
2422 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2427 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2429 while (_error->PopMessage(error))
2430 lprintf("ArchiveCleaner: %s\n", error.c_str());
2435 pkgRecords records(cache_);
2437 lock_ = new FileFd();
2438 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2439 _assert(!_error->PendingError());
2442 // XXX: explain this with an error message
2443 _assert(list.ReadMainList());
2445 manager_ = (_system->CreatePM(cache_));
2446 _assert(manager_->GetArchives(fetcher_, &list, &records));
2447 _assert(!_error->PendingError());
2451 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2453 _assert(list.ReadMainList());
2454 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2455 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2458 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2463 bool failed = false;
2464 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2465 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2468 std::string uri = (*item)->DescURI();
2469 std::string error = (*item)->ErrorText;
2471 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2474 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2475 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2486 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2488 if (_error->PendingError()) {
2493 if (result == pkgPackageManager::Failed) {
2498 if (result != pkgPackageManager::Completed) {
2503 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2505 _assert(list.ReadMainList());
2506 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2507 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2510 if (![before isEqualToArray:after])
2515 _assert(pkgDistUpgrade(cache_));
2519 [self updateWithStatus:status_];
2522 - (void) updateWithStatus:(Status &)status {
2524 _assert(list.ReadMainList());
2527 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2528 _assert(!_error->PendingError());
2530 pkgAcquire fetcher(&status);
2531 _assert(list.GetIndexes(&fetcher));
2533 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2534 bool failed = false;
2535 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2536 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2537 (*item)->Finished();
2541 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2542 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2543 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2546 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2551 - (void) setDelegate:(id)delegate {
2552 delegate_ = delegate;
2553 status_.setDelegate(delegate);
2554 progress_.setDelegate(delegate);
2557 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2558 pkgIndexFile *index(NULL);
2559 list_->FindIndex(file, index);
2560 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2566 /* PopUp Windows {{{ */
2567 @interface PopUpView : UIView {
2568 _transient id delegate_;
2569 UITransitionView *transition_;
2574 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2578 @implementation PopUpView
2581 [transition_ setDelegate:nil];
2582 [transition_ release];
2588 [transition_ transition:UITransitionPushFromTop toView:nil];
2591 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2592 if (from != nil && to == nil)
2593 [self removeFromSuperview];
2596 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2597 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2598 delegate_ = delegate;
2600 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2601 [self addSubview:transition_];
2603 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2605 [view addSubview:self];
2607 [transition_ setDelegate:self];
2609 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2610 [transition_ transition:UITransitionNone toView:blank];
2611 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2618 /* Mail Composition {{{ */
2619 @interface MailToView : PopUpView {
2620 MailComposeController *controller_;
2623 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2627 @implementation MailToView
2630 [controller_ release];
2634 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2638 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2639 NSLog(@"did:%@", delivery);
2640 // [UIApp setStatusBarShowsProgress:NO];
2641 if ([controller error]){
2642 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2643 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2644 [mailAlertSheet setBodyText:[controller error]];
2645 [mailAlertSheet popupAlertAnimated:YES];
2649 - (void) showError {
2650 NSLog(@"%@", [controller_ error]);
2651 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2652 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2653 [mailAlertSheet setBodyText:[controller_ error]];
2654 [mailAlertSheet popupAlertAnimated:YES];
2657 - (void) deliverMessage { _pooled
2661 if (![controller_ deliverMessage])
2662 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2665 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2666 if ([controller_ needsDelivery])
2667 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2672 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2673 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2674 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2675 [controller_ setDelegate:self];
2676 [controller_ initializeUI];
2677 [controller_ setupForURL:url];
2679 UIView *view([controller_ view]);
2680 [overlay_ addSubview:view];
2686 /* Confirmation View {{{ */
2687 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2688 if (!iterator.end())
2689 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2690 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2692 pkgCache::PkgIterator package(dep.TargetPkg());
2695 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2702 @protocol ConfirmationViewDelegate
2707 @interface ConfirmationView : BrowserView {
2708 _transient Database *database_;
2709 UIActionSheet *essential_;
2716 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2720 @implementation ConfirmationView
2727 if (essential_ != nil)
2728 [essential_ release];
2734 [book_ popFromSuperviewAnimated:YES];
2737 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2738 NSString *context([sheet context]);
2740 if ([context isEqualToString:@"remove"]) {
2748 [delegate_ confirm];
2755 } else if ([context isEqualToString:@"unable"]) {
2759 [super alertSheet:sheet buttonClicked:button];
2762 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2763 [window setValue:changes_ forKey:@"changes"];
2764 [window setValue:issues_ forKey:@"issues"];
2765 [window setValue:sizes_ forKey:@"sizes"];
2766 [super webView:sender didClearWindowObject:window forFrame:frame];
2769 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2770 if ((self = [super initWithBook:book]) != nil) {
2771 database_ = database;
2773 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2774 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2775 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2776 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2777 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2781 pkgDepCache::Policy *policy([database_ policy]);
2783 pkgCacheFile &cache([database_ cache]);
2784 NSArray *packages = [database_ packages];
2785 for (size_t i(0), e = [packages count]; i != e; ++i) {
2786 Package *package = [packages objectAtIndex:i];
2787 pkgCache::PkgIterator iterator = [package iterator];
2788 pkgDepCache::StateCache &state(cache[iterator]);
2790 NSString *name([package name]);
2792 if (state.NewInstall())
2793 [installing addObject:name];
2794 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2795 [reinstalling addObject:name];
2796 else if (state.Upgrade())
2797 [upgrading addObject:name];
2798 else if (state.Downgrade())
2799 [downgrading addObject:name];
2800 else if (state.Delete()) {
2801 if ([package essential])
2803 [removing addObject:name];
2806 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2807 substrate_ |= DepSubstrate(iterator.CurrentVer());
2812 else if (Advanced_ || true) {
2813 essential_ = [[UIActionSheet alloc]
2814 initWithTitle:@"Removing Essentials"
2815 buttons:[NSArray arrayWithObjects:
2816 @"Cancel Operation (Safe)",
2817 @"Force Removal (Unsafe)",
2819 defaultButtonIndex:0
2825 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2827 [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."];
2829 essential_ = [[UIActionSheet alloc]
2830 initWithTitle:@"Unable to Comply"
2831 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2832 defaultButtonIndex:0
2837 [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."];
2840 changes_ = [[NSArray alloc] initWithObjects:
2848 issues_ = [database_ issues];
2850 issues_ = [issues_ retain];
2852 sizes_ = [[NSArray alloc] initWithObjects:
2853 SizeString([database_ fetcher].FetchNeeded()),
2854 SizeString([database_ fetcher].PartialPresent()),
2855 SizeString([database_ cache]->UsrSize()),
2858 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2862 - (NSString *) backButtonTitle {
2866 - (NSString *) leftButtonTitle {
2870 - (NSString *) _rightButtonTitle {
2871 return issues_ == nil ? @"Confirm" : nil;
2874 - (void) _leftButtonClicked {
2879 - (void) _rightButtonClicked {
2880 if (essential_ != nil)
2881 [essential_ popupAlertAnimated:YES];
2885 [delegate_ confirm];
2893 /* Progress Data {{{ */
2894 @interface ProgressData : NSObject {
2900 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2907 @implementation ProgressData
2909 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2910 if ((self = [super init]) != nil) {
2911 selector_ = selector;
2931 /* Progress View {{{ */
2932 @interface ProgressView : UIView <
2933 ConfigurationDelegate,
2936 _transient Database *database_;
2938 UIView *background_;
2939 UITransitionView *transition_;
2941 UINavigationBar *navbar_;
2942 UIProgressBar *progress_;
2943 UITextView *output_;
2944 UITextLabel *status_;
2945 UIPushButton *close_;
2948 SHA1SumValue springlist_;
2949 SHA1SumValue sandplate_;
2951 NSTimeInterval last_;
2954 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2956 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2957 - (void) setContentView:(UIView *)view;
2960 - (void) _retachThread;
2961 - (void) _detachNewThreadData:(ProgressData *)data;
2962 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2968 @protocol ProgressViewDelegate
2969 - (void) progressViewIsComplete:(ProgressView *)sender;
2972 @implementation ProgressView
2975 [transition_ setDelegate:nil];
2976 [navbar_ setDelegate:nil];
2979 if (background_ != nil)
2980 [background_ release];
2981 [transition_ release];
2984 [progress_ release];
2991 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2992 if (bootstrap_ && from == overlay_ && to == view_)
2996 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2997 if ((self = [super initWithFrame:frame]) != nil) {
2998 database_ = database;
2999 delegate_ = delegate;
3001 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3002 [transition_ setDelegate:self];
3004 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3007 [overlay_ setBackgroundColor:[UIColor blackColor]];
3009 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3010 [background_ setBackgroundColor:[UIColor blackColor]];
3011 [self addSubview:background_];
3014 [self addSubview:transition_];
3016 CGSize navsize = [UINavigationBar defaultSize];
3017 CGRect navrect = {{0, 0}, navsize};
3019 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3020 [overlay_ addSubview:navbar_];
3022 [navbar_ setBarStyle:1];
3023 [navbar_ setDelegate:self];
3025 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3026 [navbar_ pushNavigationItem:navitem];
3028 CGRect bounds = [overlay_ bounds];
3029 CGSize prgsize = [UIProgressBar defaultSize];
3032 (bounds.size.width - prgsize.width) / 2,
3033 bounds.size.height - prgsize.height - 20
3036 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3037 [progress_ setStyle:0];
3039 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3041 bounds.size.height - prgsize.height - 50,
3042 bounds.size.width - 20,
3046 [status_ setColor:[UIColor whiteColor]];
3047 [status_ setBackgroundColor:[UIColor clearColor]];
3049 [status_ setCentersHorizontally:YES];
3050 //[status_ setFont:font];
3052 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3054 navrect.size.height + 20,
3055 bounds.size.width - 20,
3056 bounds.size.height - navsize.height - 62 - navrect.size.height
3059 //[output_ setTextFont:@"Courier New"];
3060 [output_ setTextSize:12];
3062 [output_ setTextColor:[UIColor whiteColor]];
3063 [output_ setBackgroundColor:[UIColor clearColor]];
3065 [output_ setMarginTop:0];
3066 [output_ setAllowsRubberBanding:YES];
3067 [output_ setEditable:NO];
3069 [overlay_ addSubview:output_];
3071 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3073 bounds.size.height - prgsize.height - 50,
3074 bounds.size.width - 20,
3078 [close_ setAutosizesToFit:NO];
3079 [close_ setDrawsShadow:YES];
3080 [close_ setStretchBackground:YES];
3081 [close_ setEnabled:YES];
3083 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3084 [close_ setTitleFont:bold];
3086 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3087 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3088 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3092 - (void) setContentView:(UIView *)view {
3093 view_ = [view retain];
3096 - (void) resetView {
3097 [transition_ transition:6 toView:view_];
3100 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3101 NSString *context([sheet context]);
3103 if ([context isEqualToString:@"conffile"]) {
3104 FILE *input = [database_ input];
3108 fprintf(input, "N\n");
3112 fprintf(input, "Y\n");
3123 - (void) closeButtonPushed {
3132 [delegate_ suspendWithAnimation:YES];
3136 system("launchctl stop com.apple.SpringBoard");
3140 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3149 - (void) _retachThread {
3150 UINavigationItem *item = [navbar_ topItem];
3151 [item setTitle:@"Complete"];
3153 [overlay_ addSubview:close_];
3154 [progress_ removeFromSuperview];
3155 [status_ removeFromSuperview];
3157 [delegate_ progressViewIsComplete:self];
3160 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3161 MMap mmap(file, MMap::ReadOnly);
3163 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3164 if (!(sandplate_ == sha1.Result()))
3169 FileFd file(SpringBoard_, FileFd::ReadOnly);
3170 MMap mmap(file, MMap::ReadOnly);
3172 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3173 if (!(springlist_ == sha1.Result()))
3178 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3179 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3180 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3181 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3182 case 4: [close_ setTitle:@"Reboot Device"]; break;
3185 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3187 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3188 [cache autorelease];
3190 NSFileManager *manager = [NSFileManager defaultManager];
3191 NSError *error = nil;
3193 id system = [cache objectForKey:@"System"];
3198 if (stat(Cache_, &info) == -1)
3201 [system removeAllObjects];
3203 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3204 for (NSString *app in apps)
3205 if ([app hasSuffix:@".app"]) {
3206 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3207 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3208 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3210 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3211 [info setObject:path forKey:@"Path"];
3212 [info setObject:@"System" forKey:@"ApplicationType"];
3213 [system addInfoDictionary:info];
3219 [cache writeToFile:@Cache_ atomically:YES];
3221 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3223 if (chmod(Cache_, info.st_mode) == -1)
3227 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3230 notify_post("com.apple.mobile.application_installed");
3232 [delegate_ setStatusBarShowsProgress:NO];
3235 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3236 [[data target] performSelector:[data selector] withObject:[data object]];
3239 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3242 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3243 UINavigationItem *item = [navbar_ topItem];
3244 [item setTitle:title];
3246 [status_ setText:nil];
3247 [output_ setText:@""];
3248 [progress_ setProgress:0];
3251 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3253 [close_ removeFromSuperview];
3254 [overlay_ addSubview:progress_];
3255 [overlay_ addSubview:status_];
3257 [delegate_ setStatusBarShowsProgress:YES];
3261 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3262 MMap mmap(file, MMap::ReadOnly);
3264 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3265 sandplate_ = sha1.Result();
3269 FileFd file(SpringBoard_, FileFd::ReadOnly);
3270 MMap mmap(file, MMap::ReadOnly);
3272 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3273 springlist_ = sha1.Result();
3276 [transition_ transition:6 toView:overlay_];
3279 detachNewThreadSelector:@selector(_detachNewThreadData:)
3281 withObject:[[ProgressData alloc]
3282 initWithSelector:selector
3289 - (void) repairWithSelector:(SEL)selector {
3291 detachNewThreadSelector:selector
3298 - (void) setConfigurationData:(NSString *)data {
3300 performSelectorOnMainThread:@selector(_setConfigurationData:)
3306 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3307 Package *package = id == nil ? nil : [database_ packageWithName:id];
3309 UIActionSheet *sheet = [[[UIActionSheet alloc]
3310 initWithTitle:(package == nil ? id : [package name])
3311 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3312 defaultButtonIndex:0
3317 [sheet setBodyText:error];
3318 [sheet popupAlertAnimated:YES];
3321 - (void) setProgressTitle:(NSString *)title {
3323 performSelectorOnMainThread:@selector(_setProgressTitle:)
3329 - (void) setProgressPercent:(float)percent {
3331 performSelectorOnMainThread:@selector(_setProgressPercent:)
3332 withObject:[NSNumber numberWithFloat:percent]
3337 - (void) startProgress {
3338 last_ = [NSDate timeIntervalSinceReferenceDate];
3341 - (void) addProgressOutput:(NSString *)output {
3343 performSelectorOnMainThread:@selector(_addProgressOutput:)
3349 - (bool) isCancelling:(size_t)received {
3351 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3352 if (received_ != received) {
3353 received_ = received;
3355 } else if (now - last_ > 30)
3362 - (void) _setConfigurationData:(NSString *)data {
3363 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3365 _assert(conffile_r(data));
3367 NSString *ofile = conffile_r[1];
3368 //NSString *nfile = conffile_r[2];
3370 UIActionSheet *sheet = [[[UIActionSheet alloc]
3371 initWithTitle:@"Configuration Upgrade"
3372 buttons:[NSArray arrayWithObjects:
3373 @"Keep My Old Copy",
3374 @"Accept The New Copy",
3375 // XXX: @"See What Changed",
3377 defaultButtonIndex:0
3382 [sheet setBodyText:[NSString stringWithFormat:
3383 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3386 [sheet popupAlertAnimated:YES];
3389 - (void) _setProgressTitle:(NSString *)title {
3390 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3391 for (size_t i(0), e([words count]); i != e; ++i) {
3392 NSString *word([words objectAtIndex:i]);
3393 if (Package *package = [database_ packageWithName:word])
3394 [words replaceObjectAtIndex:i withObject:[package name]];
3397 [status_ setText:[words componentsJoinedByString:@" "]];
3400 - (void) _setProgressPercent:(NSNumber *)percent {
3401 [progress_ setProgress:[percent floatValue]];
3404 - (void) _addProgressOutput:(NSString *)output {
3405 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3406 CGSize size = [output_ contentSize];
3407 CGRect rect = {{0, size.height}, {size.width, 0}};
3408 [output_ scrollRectToVisible:rect animated:YES];
3411 - (BOOL) isRunning {
3418 /* Package Cell {{{ */
3419 @interface PackageCell : UISimpleTableCell {
3422 NSString *description_;
3426 UITextLabel *status_;
3430 - (PackageCell *) init;
3431 - (void) setPackage:(Package *)package;
3433 + (int) heightForPackage:(Package *)package;
3437 @implementation PackageCell
3439 - (void) clearPackage {
3450 if (description_ != nil) {
3451 [description_ release];
3455 if (source_ != nil) {
3460 if (badge_ != nil) {
3467 [self clearPackage];
3474 - (PackageCell *) init {
3475 if ((self = [super init]) != nil) {
3477 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3478 [status_ setBackgroundColor:[UIColor clearColor]];
3479 [status_ setFont:small];
3484 - (void) setPackage:(Package *)package {
3485 [self clearPackage];
3487 Source *source = [package source];
3488 NSString *section = [package simpleSection];
3490 icon_ = [[package icon] retain];
3492 name_ = [[package name] retain];
3493 description_ = [[package tagline] retain];
3495 NSString *label = nil;
3496 bool trusted = false;
3498 if (source != nil) {
3499 label = [source label];
3500 trusted = [source trusted];
3501 } else if ([[package id] isEqualToString:@"firmware"])
3504 label = @"Unknown/Local";
3506 NSString *from = [NSString stringWithFormat:@"from %@", label];
3508 if (section != nil && ![section isEqualToString:label])
3509 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3511 source_ = [from retain];
3513 if (NSString *purpose = [package primaryPurpose])
3514 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3515 badge_ = [badge_ retain];
3518 if (NSString *mode = [package mode]) {
3519 [badge_ setImage:[UIImage applicationImageNamed:
3520 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3523 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3524 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3525 } else if ([package half]) {
3526 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3527 [status_ setText:@"Package Damaged"];
3528 [status_ setColor:[UIColor redColor]];
3530 [badge_ setImage:nil];
3531 [status_ setText:nil];
3536 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3539 rect.size = [icon_ size];
3541 rect.size.width /= 2;
3542 rect.size.height /= 2;
3544 rect.origin.x = 25 - rect.size.width / 2;
3545 rect.origin.y = 25 - rect.size.height / 2;
3547 [icon_ drawInRect:rect];
3550 if (badge_ != nil) {
3551 CGSize size = [badge_ size];
3553 [badge_ drawAtPoint:CGPointMake(
3554 36 - size.width / 2,
3555 36 - size.height / 2
3564 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3565 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3569 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3571 [super drawContentInRect:rect selected:selected];
3574 + (int) heightForPackage:(Package *)package {
3575 NSString *tagline([package tagline]);
3576 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3578 if ([package hasMode] || [package half])
3587 /* Section Cell {{{ */
3588 @interface SectionCell : UISimpleTableCell {
3593 _UISwitchSlider *switch_;
3598 - (void) setSection:(Section *)section editing:(BOOL)editing;
3602 @implementation SectionCell
3604 - (void) clearSection {
3605 if (section_ != nil) {
3615 if (count_ != nil) {
3622 [self clearSection];
3629 if ((self = [super init]) != nil) {
3630 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3632 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3633 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3637 - (void) onSwitch:(id)sender {
3638 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3639 if (metadata == nil) {
3640 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3641 [Sections_ setObject:metadata forKey:section_];
3645 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3648 - (void) setSection:(Section *)section editing:(BOOL)editing {
3649 if (editing != editing_) {
3651 [switch_ removeFromSuperview];
3653 [self addSubview:switch_];
3657 [self clearSection];
3659 if (section == nil) {
3660 name_ = [@"All Packages" retain];
3663 section_ = [section name];
3664 if (section_ != nil)
3665 section_ = [section_ retain];
3666 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3667 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3670 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3674 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3675 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3682 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3684 CGSize size = [count_ sizeWithFont:Font14_];
3688 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3690 [super drawContentInRect:rect selected:selected];
3696 /* File Table {{{ */
3697 @interface FileTable : RVPage {
3698 _transient Database *database_;
3701 NSMutableArray *files_;
3705 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3706 - (void) setPackage:(Package *)package;
3710 @implementation FileTable
3713 if (package_ != nil)
3722 - (int) numberOfRowsInTable:(UITable *)table {
3723 return files_ == nil ? 0 : [files_ count];
3726 - (float) table:(UITable *)table heightForRow:(int)row {
3730 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3731 if (reusing == nil) {
3732 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3733 UIFont *font = [UIFont systemFontOfSize:16];
3734 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3736 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3740 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3744 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3745 if ((self = [super initWithBook:book]) != nil) {
3746 database_ = database;
3748 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3750 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3751 [self addSubview:list_];
3753 UITableColumn *column = [[[UITableColumn alloc]
3754 initWithTitle:@"Name"
3756 width:[self frame].size.width
3759 [list_ setDataSource:self];
3760 [list_ setSeparatorStyle:1];
3761 [list_ addTableColumn:column];
3762 [list_ setDelegate:self];
3763 [list_ setReusesTableCells:YES];
3767 - (void) setPackage:(Package *)package {
3768 if (package_ != nil) {
3769 [package_ autorelease];
3778 [files_ removeAllObjects];
3780 if (package != nil) {
3781 package_ = [package retain];
3782 name_ = [[package id] retain];
3784 if (NSArray *files = [package files])
3785 [files_ addObjectsFromArray:files];
3787 if ([files_ count] != 0) {
3788 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3789 [files_ removeObjectAtIndex:0];
3790 [files_ sortUsingSelector:@selector(compareByPath:)];
3792 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3793 [stack addObject:@"/"];
3795 for (int i(0), e([files_ count]); i != e; ++i) {
3796 NSString *file = [files_ objectAtIndex:i];
3797 while (![file hasPrefix:[stack lastObject]])
3798 [stack removeLastObject];
3799 NSString *directory = [stack lastObject];
3800 [stack addObject:[file stringByAppendingString:@"/"]];
3801 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3802 ([stack count] - 2) * 3, "",
3803 [file substringFromIndex:[directory length]]
3812 - (void) resetViewAnimated:(BOOL)animated {
3813 [list_ resetViewAnimated:animated];
3816 - (void) reloadData {
3817 [self setPackage:[database_ packageWithName:name_]];
3818 [self reloadButtons];
3821 - (NSString *) title {
3822 return @"Installed Files";
3825 - (NSString *) backButtonTitle {
3831 /* Package View {{{ */
3832 @interface PackageView : BrowserView {
3833 _transient Database *database_;
3836 NSMutableArray *buttons_;
3839 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3840 - (void) setPackage:(Package *)package;
3844 @implementation PackageView
3847 if (package_ != nil)
3855 - (void) _clickButtonWithName:(NSString *)name {
3856 if ([name isEqualToString:@"Install"])
3857 [delegate_ installPackage:package_];
3858 else if ([name isEqualToString:@"Reinstall"])
3859 [delegate_ installPackage:package_];
3860 else if ([name isEqualToString:@"Remove"])
3861 [delegate_ removePackage:package_];
3862 else if ([name isEqualToString:@"Upgrade"])
3863 [delegate_ installPackage:package_];
3864 else _assert(false);
3867 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3868 NSString *context([sheet context]);
3870 if ([context isEqualToString:@"modify"]) {
3871 int count = [buttons_ count];
3872 _assert(count != 0);
3873 _assert(button <= count + 1);
3875 if (count != button - 1)
3876 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3880 [super alertSheet:sheet buttonClicked:button];
3883 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3884 return [super webView:sender didFinishLoadForFrame:frame];
3887 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3888 [window setValue:package_ forKey:@"package"];
3889 [super webView:sender didClearWindowObject:window forFrame:frame];
3893 - (void) _rightButtonClicked {
3894 /*[super _rightButtonClicked];
3897 int count = [buttons_ count];
3898 _assert(count != 0);
3901 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3903 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3904 [buttons addObjectsFromArray:buttons_];
3905 [buttons addObject:@"Cancel"];
3907 [delegate_ slideUp:[[[UIActionSheet alloc]
3910 defaultButtonIndex:2
3918 - (NSString *) _rightButtonTitle {
3919 int count = [buttons_ count];
3920 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3923 - (NSString *) backButtonTitle {
3927 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3928 if ((self = [super initWithBook:book]) != nil) {
3929 database_ = database;
3930 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3934 - (void) setPackage:(Package *)package {
3935 if (package_ != nil) {
3936 [package_ autorelease];
3945 [buttons_ removeAllObjects];
3947 if (package != nil) {
3948 package_ = [package retain];
3949 name_ = [[package id] retain];
3951 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3953 if ([package_ source] == nil);
3954 else if ([package_ upgradableAndEssential:NO])
3955 [buttons_ addObject:@"Upgrade"];
3956 else if ([package_ installed] == nil)
3957 [buttons_ addObject:@"Install"];
3959 [buttons_ addObject:@"Reinstall"];
3960 if ([package_ installed] != nil)
3961 [buttons_ addObject:@"Remove"];
3969 - (void) reloadData {
3970 [self setPackage:[database_ packageWithName:name_]];
3971 [self reloadButtons];
3976 /* Package Table {{{ */
3977 @interface PackageTable : RVPage {
3978 _transient Database *database_;
3982 NSMutableArray *packages_;
3983 NSMutableArray *sections_;
3984 UISectionList *list_;
3987 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3989 - (void) setDelegate:(id)delegate;
3990 - (void) setObject:(id)object;
3992 - (void) reloadData;
3993 - (void) resetCursor;
3995 - (UISectionList *) list;
3997 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4001 @implementation PackageTable
4004 [list_ setDataSource:nil];
4009 [packages_ release];
4010 [sections_ release];
4015 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4016 return [sections_ count];
4019 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4020 return [[sections_ objectAtIndex:section] name];
4023 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4024 return [[sections_ objectAtIndex:section] row];
4027 - (int) numberOfRowsInTable:(UITable *)table {
4028 return [packages_ count];
4031 - (float) table:(UITable *)table heightForRow:(int)row {
4032 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4035 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4037 reusing = [[[PackageCell alloc] init] autorelease];
4038 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4042 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4046 - (void) tableRowSelected:(NSNotification *)notification {
4047 int row = [[notification object] selectedRow];
4051 Package *package = [packages_ objectAtIndex:row];
4052 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4053 [view setDelegate:delegate_];
4054 [view setPackage:package];
4055 [book_ pushPage:view];
4058 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4059 if ((self = [super initWithBook:book]) != nil) {
4060 database_ = database;
4061 title_ = [title retain];
4063 object_ = object == nil ? nil : [object retain];
4065 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4066 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4068 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4069 [list_ setDataSource:self];
4071 UITableColumn *column = [[[UITableColumn alloc]
4072 initWithTitle:@"Name"
4074 width:[self frame].size.width
4077 UITable *table = [list_ table];
4078 [table setSeparatorStyle:1];
4079 [table addTableColumn:column];
4080 [table setDelegate:self];
4081 [table setReusesTableCells:YES];
4083 [self addSubview:list_];
4086 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4087 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4091 - (void) setDelegate:(id)delegate {
4092 delegate_ = delegate;
4095 - (void) setObject:(id)object {
4101 object_ = [object retain];
4104 - (void) reloadData {
4105 NSArray *packages = [database_ packages];
4107 [packages_ removeAllObjects];
4108 [sections_ removeAllObjects];
4110 for (size_t i(0); i != [packages count]; ++i) {
4111 Package *package([packages objectAtIndex:i]);
4112 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4113 [packages_ addObject:package];
4116 Section *section = nil;
4118 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4119 Package *package = [packages_ objectAtIndex:offset];
4120 NSString *name = [package index];
4122 if (section == nil || ![[section name] isEqualToString:name]) {
4123 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4124 [sections_ addObject:section];
4127 [section addToCount];
4133 - (NSString *) title {
4137 - (void) resetViewAnimated:(BOOL)animated {
4138 [list_ resetViewAnimated:animated];
4141 - (void) resetCursor {
4142 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4145 - (UISectionList *) list {
4149 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4150 [list_ setShouldHideHeaderInShortLists:hide];
4156 /* Add Source View {{{ */
4157 @interface AddSourceView : RVPage {
4158 _transient Database *database_;
4161 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4165 @implementation AddSourceView
4167 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4168 if ((self = [super initWithBook:book]) != nil) {
4169 database_ = database;
4175 /* Source Cell {{{ */
4176 @interface SourceCell : UITableCell {
4179 NSString *description_;
4185 - (SourceCell *) initWithSource:(Source *)source;
4189 @implementation SourceCell
4194 [description_ release];
4199 - (SourceCell *) initWithSource:(Source *)source {
4200 if ((self = [super init]) != nil) {
4202 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4204 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4205 icon_ = [icon_ retain];
4207 origin_ = [[source name] retain];
4208 label_ = [[source uri] retain];
4209 description_ = [[source description] retain];
4213 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4215 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4222 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4226 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4230 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4232 [super drawContentInRect:rect selected:selected];
4237 /* Source Table {{{ */
4238 @interface SourceTable : RVPage {
4239 _transient Database *database_;
4240 UISectionList *list_;
4241 NSMutableArray *sources_;
4242 UIActionSheet *alert_;
4246 UIProgressHUD *hud_;
4249 //NSURLConnection *installer_;
4250 NSURLConnection *trivial_bz2_;
4251 NSURLConnection *trivial_gz_;
4252 //NSURLConnection *automatic_;
4257 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4261 @implementation SourceTable
4263 - (void) _deallocConnection:(NSURLConnection *)connection {
4264 if (connection != nil) {
4265 [connection cancel];
4266 //[connection setDelegate:nil];
4267 [connection release];
4272 [[list_ table] setDelegate:nil];
4273 [list_ setDataSource:nil];
4282 //[self _deallocConnection:installer_];
4283 [self _deallocConnection:trivial_gz_];
4284 [self _deallocConnection:trivial_bz2_];
4285 //[self _deallocConnection:automatic_];
4292 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4293 return offset_ == 0 ? 1 : 2;
4296 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4297 switch (section + (offset_ == 0 ? 1 : 0)) {
4298 case 0: return @"Entered by User";
4299 case 1: return @"Installed by Packages";
4307 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4308 switch (section + (offset_ == 0 ? 1 : 0)) {
4310 case 1: return offset_;
4318 - (int) numberOfRowsInTable:(UITable *)table {
4319 return [sources_ count];
4322 - (float) table:(UITable *)table heightForRow:(int)row {
4323 Source *source = [sources_ objectAtIndex:row];
4324 return [source description] == nil ? 56 : 73;
4327 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4328 Source *source = [sources_ objectAtIndex:row];
4329 // XXX: weird warning, stupid selectors ;P
4330 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4333 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4337 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4341 - (void) tableRowSelected:(NSNotification*)notification {
4342 UITable *table([list_ table]);
4343 int row([table selectedRow]);
4347 Source *source = [sources_ objectAtIndex:row];
4349 PackageTable *packages = [[[PackageTable alloc]
4352 title:[source label]
4353 filter:@selector(isVisibleInSource:)
4357 [packages setDelegate:delegate_];
4359 [book_ pushPage:packages];
4362 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4363 Source *source = [sources_ objectAtIndex:row];
4364 return [source record] != nil;
4367 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4368 [[list_ table] setDeleteConfirmationRow:row];
4371 - (void) table:(UITable *)table deleteRow:(int)row {
4372 Source *source = [sources_ objectAtIndex:row];
4373 [Sources_ removeObjectForKey:[source key]];
4374 [delegate_ syncData];
4377 - (void) _endConnection:(NSURLConnection *)connection {
4378 NSURLConnection **field = NULL;
4379 if (connection == trivial_bz2_)
4380 field = &trivial_bz2_;
4381 else if (connection == trivial_gz_)
4382 field = &trivial_gz_;
4383 _assert(field != NULL);
4384 [connection release];
4388 trivial_bz2_ == nil &&
4391 [delegate_ setStatusBarShowsProgress:NO];
4394 [hud_ removeFromSuperview];
4399 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4402 @"./", @"Distribution",
4403 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4405 [delegate_ syncData];
4406 } else if (error_ != nil) {
4407 UIActionSheet *sheet = [[[UIActionSheet alloc]
4408 initWithTitle:@"Verification Error"
4409 buttons:[NSArray arrayWithObjects:@"OK", nil]
4410 defaultButtonIndex:0
4415 [sheet setBodyText:[error_ localizedDescription]];
4416 [sheet popupAlertAnimated:YES];
4418 UIActionSheet *sheet = [[[UIActionSheet alloc]
4419 initWithTitle:@"Did not Find Repository"
4420 buttons:[NSArray arrayWithObjects:@"OK", nil]
4421 defaultButtonIndex:0
4426 [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."];
4427 [sheet popupAlertAnimated:YES];
4433 if (error_ != nil) {
4440 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4441 switch ([response statusCode]) {
4447 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4448 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4450 error_ = [error retain];
4451 [self _endConnection:connection];
4454 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4455 [self _endConnection:connection];
4458 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4459 NSMutableURLRequest *request = [NSMutableURLRequest
4460 requestWithURL:[NSURL URLWithString:href]
4461 cachePolicy:NSURLRequestUseProtocolCachePolicy
4462 timeoutInterval:20.0
4465 [request setHTTPMethod:method];
4467 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4470 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4471 NSString *context([sheet context]);
4473 if ([context isEqualToString:@"source"]) {
4476 NSString *href = [[sheet textField] text];
4478 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4480 if (![href hasSuffix:@"/"])
4481 href_ = [href stringByAppendingString:@"/"];
4484 href_ = [href_ retain];
4486 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4487 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4488 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4492 hud_ = [delegate_ addProgressHUD];
4493 [hud_ setText:@"Verifying URL"];
4504 } else if ([context isEqualToString:@"trivial"])
4506 else if ([context isEqualToString:@"urlerror"])
4510 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4511 if ((self = [super initWithBook:book]) != nil) {
4512 database_ = database;
4513 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4515 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4516 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4517 [list_ setShouldHideHeaderInShortLists:NO];
4519 [self addSubview:list_];
4520 [list_ setDataSource:self];
4522 UITableColumn *column = [[UITableColumn alloc]
4523 initWithTitle:@"Name"
4525 width:[self frame].size.width
4528 UITable *table = [list_ table];
4529 [table setSeparatorStyle:1];
4530 [table addTableColumn:column];
4531 [table setDelegate:self];
4535 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4536 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4540 - (void) reloadData {
4542 _assert(list.ReadMainList());
4544 [sources_ removeAllObjects];
4545 [sources_ addObjectsFromArray:[database_ sources]];
4547 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4550 int count = [sources_ count];
4551 for (offset_ = 0; offset_ != count; ++offset_) {
4552 Source *source = [sources_ objectAtIndex:offset_];
4553 if ([source record] == nil)
4560 - (void) resetViewAnimated:(BOOL)animated {
4561 [list_ resetViewAnimated:animated];
4564 - (void) _leftButtonClicked {
4565 /*[book_ pushPage:[[[AddSourceView alloc]
4570 UIActionSheet *sheet = [[[UIActionSheet alloc]
4571 initWithTitle:@"Enter Cydia/APT URL"
4572 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4573 defaultButtonIndex:0
4578 [sheet setNumberOfRows:1];
4580 [sheet addTextFieldWithValue:@"http://" label:@""];
4582 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4583 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4584 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4585 [traits setKeyboardType:UIKeyboardTypeURL];
4586 // XXX: UIReturnKeyDone
4587 [traits setReturnKeyType:UIReturnKeyNext];
4589 [sheet popupAlertAnimated:YES];
4592 - (void) _rightButtonClicked {
4593 UITable *table = [list_ table];
4594 BOOL editing = [table isRowDeletionEnabled];
4595 [table enableRowDeletion:!editing animated:YES];
4596 [book_ reloadButtonsForPage:self];
4599 - (NSString *) title {
4603 - (NSString *) leftButtonTitle {
4604 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4607 - (NSString *) rightButtonTitle {
4608 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4611 - (UINavigationButtonStyle) rightButtonStyle {
4612 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4618 /* Installed View {{{ */
4619 @interface InstalledView : RVPage {
4620 _transient Database *database_;
4621 PackageTable *packages_;
4625 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4629 @implementation InstalledView
4632 [packages_ release];
4636 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4637 if ((self = [super initWithBook:book]) != nil) {
4638 database_ = database;
4640 packages_ = [[PackageTable alloc]
4644 filter:@selector(isInstalledAndVisible:)
4645 with:[NSNumber numberWithBool:YES]
4648 [self addSubview:packages_];
4650 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4651 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4655 - (void) resetViewAnimated:(BOOL)animated {
4656 [packages_ resetViewAnimated:animated];
4659 - (void) reloadData {
4660 [packages_ reloadData];
4663 - (void) _rightButtonClicked {
4664 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4665 [packages_ reloadData];
4667 [book_ reloadButtonsForPage:self];
4670 - (NSString *) title {
4671 return @"Installed";
4674 - (NSString *) backButtonTitle {
4678 - (NSString *) rightButtonTitle {
4679 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4682 - (UINavigationButtonStyle) rightButtonStyle {
4683 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4686 - (void) setDelegate:(id)delegate {
4687 [super setDelegate:delegate];
4688 [packages_ setDelegate:delegate];
4695 @interface HomeView : BrowserView {
4700 @implementation HomeView
4702 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4703 NSString *context([sheet context]);
4705 if ([context isEqualToString:@"about"])
4708 [super alertSheet:sheet buttonClicked:button];
4711 - (void) _leftButtonClicked {
4712 UIActionSheet *sheet = [[[UIActionSheet alloc]
4713 initWithTitle:@"About Cydia Installer"
4714 buttons:[NSArray arrayWithObjects:@"Close", nil]
4715 defaultButtonIndex:0
4721 @"Copyright (C) 2008\n"
4722 "Jay Freeman (saurik)\n"
4723 "saurik@saurik.com\n"
4724 "http://www.saurik.com/\n"
4727 "http://www.theokorigroup.com/\n"
4729 "College of Creative Studies,\n"
4730 "University of California,\n"
4732 "http://www.ccs.ucsb.edu/"
4735 [sheet popupAlertAnimated:YES];
4738 - (NSString *) leftButtonTitle {
4744 /* Manage View {{{ */
4745 @interface ManageView : BrowserView {
4750 @implementation ManageView
4752 - (NSString *) title {
4756 - (void) _leftButtonClicked {
4757 [delegate_ askForSettings];
4760 - (NSString *) leftButtonTitle {
4765 - (NSString *) _rightButtonTitle {
4777 @interface WebView (Cydia)
4778 - (void) setScriptDebugDelegate:(id)delegate;
4779 - (void) _setFormDelegate:(id)delegate;
4780 - (void) _setUIKitDelegate:(id)delegate;
4781 - (void) setWebMailDelegate:(id)delegate;
4782 - (void) _setLayoutInterval:(float)interval;
4785 /* Indirect Delegate {{{ */
4786 @interface IndirectDelegate : NSProxy {
4787 _transient volatile id delegate_;
4790 - (void) setDelegate:(id)delegate;
4791 - (id) initWithDelegate:(id)delegate;
4794 @implementation IndirectDelegate
4796 - (void) setDelegate:(id)delegate {
4797 delegate_ = delegate;
4800 - (id) initWithDelegate:(id)delegate {
4801 delegate_ = delegate;
4805 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4806 if (delegate_ != nil)
4807 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4809 // XXX: I fucking hate Apple so very very bad
4810 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4813 - (void) forwardInvocation:(NSInvocation *)inv {
4814 SEL sel = [inv selector];
4815 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4816 [inv invokeWithTarget:delegate_];
4821 /* Browser Implementation {{{ */
4822 @implementation BrowserView
4823 #include "internals.h"
4826 if (challenge_ != nil)
4827 [challenge_ release];
4829 WebView *webview = [webview_ webView];
4830 [webview setFrameLoadDelegate:nil];
4831 [webview setResourceLoadDelegate:nil];
4832 [webview setUIDelegate:nil];
4833 [webview setScriptDebugDelegate:nil];
4834 [webview setPolicyDelegate:nil];
4836 [webview setDownloadDelegate:nil];
4838 [webview _setFormDelegate:nil];
4839 [webview _setUIKitDelegate:nil];
4840 [webview setWebMailDelegate:nil];
4841 [webview setEditingDelegate:nil];
4843 [webview_ setDelegate:nil];
4844 [webview_ setGestureDelegate:nil];
4846 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4851 [webview_ removeFromSuperview];
4852 [Documents_ addObject:[webview_ autorelease]];
4857 [indirect_ setDelegate:nil];
4858 [indirect_ release];
4860 [scroller_ setDelegate:nil];
4866 if (function_ != nil)
4867 [function_ release];
4869 [scroller_ release];
4871 [indicator_ release];
4872 if (confirm_ != nil)
4879 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4880 [self loadRequest:[NSURLRequest
4883 timeoutInterval:30.0
4887 - (void) loadURL:(NSURL *)url {
4888 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4891 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4892 NSMutableURLRequest *copy = [request mutableCopy];
4894 if (Machine_ != NULL)
4895 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4896 if (UniqueID_ != nil)
4897 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4900 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4905 - (void) loadRequest:(NSURLRequest *)request {
4907 [webview_ loadRequest:request];
4910 - (void) reloadURL {
4911 if ([urls_ count] == 0)
4913 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4914 [urls_ removeLastObject];
4915 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4918 - (WebView *) webView {
4919 return [webview_ webView];
4922 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4923 [scroller_ setContentSize:frame.size];
4926 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4927 [self view:sender didSetFrame:frame];
4930 - (void) pushPage:(RVPage *)page {
4931 [self setBackButtonTitle:title_];
4932 [page setDelegate:delegate_];
4933 [book_ pushPage:page];
4936 - (BOOL) getSpecial:(NSURL *)url {
4937 NSString *href([url absoluteString]);
4938 NSString *scheme([[url scheme] lowercaseString]);
4942 if ([href hasPrefix:@"apptapp://package/"])
4943 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4944 else if ([scheme isEqualToString:@"cydia"]) {
4945 page = [delegate_ pageForURL:url hasTag:NULL];
4948 } else if (![scheme isEqualToString:@"apptapp"])
4952 [self pushPage:page];
4956 - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4957 UIActionSheet *sheet = [[[UIActionSheet alloc]
4958 initWithTitle:@"JavaScript Alert"
4959 buttons:[NSArray arrayWithObjects:@"OK", nil]
4960 defaultButtonIndex:0
4965 [sheet setBodyText:message];
4966 [sheet popupAlertAnimated:YES];
4969 - (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4970 UIActionSheet *sheet = [[[UIActionSheet alloc]
4971 initWithTitle:@"JavaScript Confirm"
4972 buttons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil]
4973 defaultButtonIndex:0
4978 [sheet setNumberOfRows:1];
4979 [sheet setBodyText:message];
4980 [sheet popupAlertAnimated:YES];
4982 NSRunLoop *loop([NSRunLoop currentRunLoop]);
4983 NSDate *future([NSDate distantFuture]);
4985 while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
4987 NSNumber *confirm([confirm_ autorelease]);
4989 return [confirm boolValue];
4992 /* Web Scripting {{{ */
4993 + (NSString *) webScriptNameForSelector:(SEL)selector {
4994 if (selector == @selector(getPackageById:))
4995 return @"getPackageById";
4996 else if (selector == @selector(setButton:withStyle:toFunction:))
4997 return @"setButton";
4998 else if (selector == @selector(supports:))
5004 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
5005 return [self webScriptNameForSelector:selector] == nil;
5008 - (BOOL) supports:(NSString *)feature {
5009 return [feature isEqualToString:@"window.open"];
5012 - (Package *) getPackageById:(NSString *)id {
5013 return [[Database sharedInstance] packageWithName:id];
5016 - (void) setButton:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
5017 NSLog(@"b:%@", button);
5018 NSLog(@"f:%@", [function class]);
5021 [button_ autorelease];
5022 button_ = button == nil ? nil : [button retain];
5025 [style_ autorelease];
5026 style_ = style == nil ? nil : [style retain];
5028 if (function_ != nil)
5029 [function_ autorelease];
5030 function_ = function == nil ? nil : [function retain];
5034 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
5035 [window setValue:self forKey:@"cydia"];
5038 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
5039 if (NSURL *url = [request URL]) {
5040 if (name != nil && [name isEqualToString:@"_open"])
5041 [delegate_ openURL:url];
5043 NSLog(@"win:%@:%@", url, [action description]);
5044 if (![self getSpecial:url]) {
5045 NSString *scheme([[url scheme] lowercaseString]);
5046 if ([scheme isEqualToString:@"mailto"])
5047 [delegate_ openMailToURL:url];
5056 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
5057 if ([WebView canShowMIMEType:type])
5060 // XXX: handle more mime types!
5062 if (frame == [webView mainFrame])
5063 [UIApp openURL:[request URL]];
5067 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
5068 NSURL *url([request URL]);
5070 if (url == nil) use: {
5074 else NSLog(@"nav:%@:%@", url, [action description]);
5076 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
5079 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
5080 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
5083 [UIApp openURL:url];
5089 int store(_not(int));
5090 if (NSURL *itms = [url itmsURL:&store]) {
5091 NSLog(@"itms#%@#%u#%@", url, store, itms);
5093 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
5094 store == 2 && [capability containsObject:@"com.apple.AppStore"]
5101 NSString *scheme([[url scheme] lowercaseString]);
5103 if ([scheme isEqualToString:@"tel"]) {
5104 // XXX: intelligence
5108 if ([scheme isEqualToString:@"mailto"]) {
5109 [delegate_ openMailToURL:url];
5113 if ([self getSpecial:url])
5115 else if ([WebView _canHandleRequest:request])
5117 else if ([url isSpringboardHandledURL])
5123 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
5124 //lprintf("Status:%s\n", [text UTF8String]);
5127 - (void) _pushPage {
5131 [book_ pushPage:self];
5134 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5135 NSString *context([sheet context]);
5137 if ([context isEqualToString:@"alert"])
5139 else if ([context isEqualToString:@"confirm"]) {
5142 confirm_ = [NSNumber numberWithBool:YES];
5146 confirm_ = [NSNumber numberWithBool:NO];
5151 } else if ([context isEqualToString:@"challenge"]) {
5152 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
5156 NSString *username([[sheet textFieldAtIndex:0] text]);
5157 NSString *password([[sheet textFieldAtIndex:1] text]);
5159 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
5161 [sender useCredential:credential forAuthenticationChallenge:challenge_];
5165 [sender cancelAuthenticationChallenge:challenge_];
5172 [challenge_ release];
5179 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
5180 challenge_ = [challenge retain];
5182 NSURLProtectionSpace *space([challenge protectionSpace]);
5183 NSString *realm([space realm]);
5187 UIActionSheet *sheet = [[[UIActionSheet alloc]
5189 buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
5190 defaultButtonIndex:0
5192 context:@"challenge"
5195 [sheet setNumberOfRows:1];
5197 [sheet addTextFieldWithValue:@"" label:@"username"];
5198 [sheet addTextFieldWithValue:@"" label:@"password"];
5200 UITextField *username([sheet textFieldAtIndex:0]); {
5201 UITextInputTraits *traits([username textInputTraits]);
5202 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5203 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5204 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5205 [traits setReturnKeyType:UIReturnKeyNext];
5208 UITextField *password([sheet textFieldAtIndex:1]); {
5209 UITextInputTraits *traits([password textInputTraits]);
5210 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5211 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5212 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5213 // XXX: UIReturnKeyDone
5214 [traits setReturnKeyType:UIReturnKeyNext];
5215 [traits setSecureTextEntry:YES];
5218 [sheet popupAlertAnimated:YES];
5221 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
5222 NSURL *url = [request URL];
5223 if ([self getSpecial:url])
5226 return [self _addHeadersToRequest:request];
5229 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
5230 [self setBackButtonTitle:title_];
5232 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
5233 [browser setDelegate:delegate_];
5236 [browser loadRequest:[self _addHeadersToRequest:request]];
5237 [book_ pushPage:browser];
5240 return [browser webView];
5243 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
5244 return [self _createWebViewWithRequest:request pushed:(request != nil)];
5247 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
5248 return [self _createWebViewWithRequest:request pushed:YES];
5251 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
5252 if ([frame parentFrame] != nil)
5255 title_ = [title retain];
5256 [book_ reloadTitleForPage:self];
5259 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
5260 if ([frame parentFrame] != nil)
5265 [self reloadButtons];
5267 if (title_ != nil) {
5272 if (button_ != nil) {
5277 if (style_ != nil) {
5282 if (function_ != nil) {
5283 [function_ release];
5287 [book_ reloadTitleForPage:self];
5289 WebView *webview = [webview_ webView];
5290 NSString *href = [webview mainFrameURL];
5291 [urls_ addObject:[NSURL URLWithString:href]];
5293 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
5295 CGRect webrect = [scroller_ bounds];
5296 webrect.size.height = 0;
5297 [webview_ setFrame:webrect];
5300 - (void) _finishLoading {
5303 [self reloadButtons];
5311 - (void) reloadButtons {
5312 if ([self _loading])
5313 [indicator_ startAnimation];
5315 [indicator_ stopAnimation];
5316 [super reloadButtons];
5319 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5320 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5323 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5324 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5327 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5328 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5331 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5332 return [webview_ webView:sender didCommitLoadForFrame:frame];
5335 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5336 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5339 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5340 if ([frame parentFrame] == nil) {
5341 [self _finishLoading];
5343 if (DOMDocument *document = [frame DOMDocument])
5344 if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
5345 for (DOMHTMLBodyElement *body in bodies) {
5346 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
5348 bool colored(false);
5350 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
5351 DOMRGBColor *rgb([color getRGBColorValue]);
5353 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
5354 NSLog(@"alpha:%g", alpha);
5359 [scroller_ setBackgroundColor:[UIColor
5360 colorWithRed:([[rgb red] getFloatValue:DOM_CSS_NUMBER] / 255)
5361 green:([[rgb green] getFloatValue:DOM_CSS_NUMBER] / 255)
5362 blue:([[rgb blue] getFloatValue:DOM_CSS_NUMBER] / 255)
5369 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
5374 return [webview_ webView:sender didFinishLoadForFrame:frame];
5377 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5378 if ([frame parentFrame] != nil)
5380 [self _finishLoading];
5382 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5383 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5384 [[error localizedDescription] stringByAddingPercentEscapes]
5388 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5390 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5394 - (id) initWithBook:(RVBook *)book {
5395 if ((self = [super initWithBook:book]) != nil) {
5398 struct CGRect bounds = [self bounds];
5400 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5401 [self addSubview:scroller_];
5403 [scroller_ setShowBackgroundShadow:NO];
5404 [scroller_ setFixedBackgroundPattern:YES];
5405 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
5407 [scroller_ setScrollingEnabled:YES];
5408 [scroller_ setAdjustForContentSizeChange:YES];
5409 [scroller_ setClipsSubviews:YES];
5410 [scroller_ setAllowsRubberBanding:YES];
5411 [scroller_ setScrollDecelerationFactor:0.99];
5412 [scroller_ setDelegate:self];
5414 CGRect webrect = [scroller_ bounds];
5415 webrect.size.height = 0;
5420 webview_ = [Documents_ lastObject];
5421 if (webview_ != nil) {
5422 webview_ = [webview_ retain];
5423 webview = [webview_ webView];
5424 [Documents_ removeLastObject];
5425 [webview_ setFrame:webrect];
5430 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5431 webview = [webview_ webView];
5433 // XXX: this is terribly (too?) expensive
5434 [webview_ setDrawsBackground:NO];
5436 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5438 [webview_ setAllowsMessaging:YES];
5440 [webview_ setTilingEnabled:YES];
5441 [webview_ setDrawsGrid:NO];
5442 [webview_ setLogsTilingChanges:NO];
5443 [webview_ setTileMinificationFilter:kCAFilterNearest];
5444 [webview_ setDetectsPhoneNumbers:NO];
5445 [webview_ setAutoresizes:YES];
5447 [webview_ setMinimumScale:0.25f forDocumentTypes:0x10];
5448 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10];
5449 [webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
5451 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2];
5453 [webview_ setMinimumScale:1.0f forDocumentTypes:0x8];
5454 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8];
5455 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8];
5457 [webview_ _setDocumentType:0x4];
5459 [webview_ setZoomsFocusedFormControl:YES];
5460 [webview_ setContentsPosition:7];
5461 [webview_ setEnabledGestures:0xa];
5462 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
5463 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
5465 [webview_ setSmoothsFonts:YES];
5467 [webview _setUsesLoaderCache:YES];
5468 [webview setGroupName:@"Cydia"];
5469 [webview _setLayoutInterval:0];
5472 [webview_ setDelegate:self];
5473 [webview_ setGestureDelegate:self];
5474 [scroller_ addSubview:webview_];
5476 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5478 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5479 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5480 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5482 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5483 NSString *application = package == nil ? @"Cydia" : [NSString
5484 stringWithFormat:@"Cydia/%@",
5486 ]; [webview setApplicationNameForUserAgent:application];
5488 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5490 [webview setFrameLoadDelegate:self];
5491 [webview setResourceLoadDelegate:indirect_];
5492 [webview setUIDelegate:self];
5493 [webview setScriptDebugDelegate:self];
5494 [webview setPolicyDelegate:self];
5496 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5498 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5499 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5503 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5504 [webview_ redrawScaledDocument];
5507 - (void) _rightButtonClicked {
5508 if (function_ == nil) {
5512 JSObjectRef function([function_ JSObject]);
5513 JSGlobalContextRef context([[[webview_ webView] mainFrame] globalContext]);
5514 JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
5518 - (NSString *) _rightButtonTitle {
5519 return button_ != nil ? button_ : @"Reload";
5522 - (NSString *) rightButtonTitle {
5523 return [self _loading] ? @"" : [self _rightButtonTitle];
5526 - (UINavigationButtonStyle) rightButtonStyle {
5527 if (style_ == nil) normal:
5528 return UINavigationButtonStyleNormal;
5529 else if ([style_ isEqualToString:@"Normal"])
5530 return UINavigationButtonStyleNormal;
5531 else if ([style_ isEqualToString:@"Back"])
5532 return UINavigationButtonStyleBack;
5533 else if ([style_ isEqualToString:@"Highlighted"])
5534 return UINavigationButtonStyleHighlighted;
5535 else if ([style_ isEqualToString:@"Destructive"])
5536 return UINavigationButtonStyleDestructive;
5540 - (NSString *) title {
5541 return title_ == nil ? @"Loading" : title_;
5544 - (NSString *) backButtonTitle {
5548 - (void) setPageActive:(BOOL)active {
5550 [indicator_ removeFromSuperview];
5552 [[book_ navigationBar] addSubview:indicator_];
5555 - (void) resetViewAnimated:(BOOL)animated {
5558 - (void) setPushed:(bool)pushed {
5565 /* Cydia Book {{{ */
5566 @interface CYBook : RVBook <
5569 _transient Database *database_;
5570 UINavigationBar *overlay_;
5571 UINavigationBar *underlay_;
5572 UIProgressIndicator *indicator_;
5573 UITextLabel *prompt_;
5574 UIProgressBar *progress_;
5575 UINavigationButton *cancel_;
5578 NSTimeInterval last_;
5581 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5587 @implementation CYBook
5591 [indicator_ release];
5593 [progress_ release];
5598 - (NSString *) getTitleForPage:(RVPage *)page {
5599 return Simplify([super getTitleForPage:page]);
5607 [UIView beginAnimations:nil context:NULL];
5609 CGRect ovrframe = [overlay_ frame];
5610 ovrframe.origin.y = 0;
5611 [overlay_ setFrame:ovrframe];
5613 CGRect barframe = [navbar_ frame];
5614 barframe.origin.y += ovrframe.size.height;
5615 [navbar_ setFrame:barframe];
5617 CGRect trnframe = [transition_ frame];
5618 trnframe.origin.y += ovrframe.size.height;
5619 trnframe.size.height -= ovrframe.size.height;
5620 [transition_ setFrame:trnframe];
5622 [UIView endAnimations];
5624 [indicator_ startAnimation];
5625 [prompt_ setText:@"Updating Database"];
5626 [progress_ setProgress:0];
5629 last_ = [NSDate timeIntervalSinceReferenceDate];
5631 [overlay_ addSubview:cancel_];
5634 detachNewThreadSelector:@selector(_update)
5643 [indicator_ stopAnimation];
5645 [UIView beginAnimations:nil context:NULL];
5647 CGRect ovrframe = [overlay_ frame];
5648 ovrframe.origin.y = -ovrframe.size.height;
5649 [overlay_ setFrame:ovrframe];
5651 CGRect barframe = [navbar_ frame];
5652 barframe.origin.y -= ovrframe.size.height;
5653 [navbar_ setFrame:barframe];
5655 CGRect trnframe = [transition_ frame];
5656 trnframe.origin.y -= ovrframe.size.height;
5657 trnframe.size.height += ovrframe.size.height;
5658 [transition_ setFrame:trnframe];
5660 [UIView commitAnimations];
5662 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5665 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5666 if ((self = [super initWithFrame:frame]) != nil) {
5667 database_ = database;
5669 CGRect ovrrect = [navbar_ bounds];
5670 ovrrect.size.height = [UINavigationBar defaultSize].height;
5671 ovrrect.origin.y = -ovrrect.size.height;
5673 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5674 [self addSubview:overlay_];
5676 ovrrect.origin.y = frame.size.height;
5677 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5678 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5679 [self addSubview:underlay_];
5681 [overlay_ setBarStyle:1];
5682 [underlay_ setBarStyle:1];
5684 int barstyle = [overlay_ _barStyle:NO];
5685 bool ugly = barstyle == 0;
5687 UIProgressIndicatorStyle style = ugly ?
5688 UIProgressIndicatorStyleMediumBrown :
5689 UIProgressIndicatorStyleMediumWhite;
5691 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5692 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5693 CGRect indrect = {{indoffset, indoffset}, indsize};
5695 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5696 [indicator_ setStyle:style];
5697 [overlay_ addSubview:indicator_];
5699 CGSize prmsize = {215, indsize.height + 4};
5702 indoffset * 2 + indsize.width,
5706 unsigned(ovrrect.size.height - prmsize.height) / 2
5709 UIFont *font = [UIFont systemFontOfSize:15];
5711 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5713 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5714 [prompt_ setBackgroundColor:[UIColor clearColor]];
5715 [prompt_ setFont:font];
5717 [overlay_ addSubview:prompt_];
5719 CGSize prgsize = {75, 100};
5722 ovrrect.size.width - prgsize.width - 10,
5723 (ovrrect.size.height - prgsize.height) / 2
5726 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5727 [progress_ setStyle:0];
5728 [overlay_ addSubview:progress_];
5730 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5731 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5733 CGRect frame = [cancel_ frame];
5734 frame.size.width = 65;
5735 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5736 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5737 [cancel_ setFrame:frame];
5739 [cancel_ setBarStyle:barstyle];
5743 - (void) _onCancel {
5745 [cancel_ removeFromSuperview];
5748 - (void) _update { _pooled
5750 status.setDelegate(self);
5752 [database_ updateWithStatus:status];
5755 performSelectorOnMainThread:@selector(_update_)
5761 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5762 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5765 - (void) setProgressTitle:(NSString *)title {
5767 performSelectorOnMainThread:@selector(_setProgressTitle:)
5773 - (void) setProgressPercent:(float)percent {
5775 performSelectorOnMainThread:@selector(_setProgressPercent:)
5776 withObject:[NSNumber numberWithFloat:percent]
5781 - (void) startProgress {
5784 - (void) addProgressOutput:(NSString *)output {
5786 performSelectorOnMainThread:@selector(_addProgressOutput:)
5792 - (bool) isCancelling:(size_t)received {
5793 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5794 if (received_ != received) {
5795 received_ = received;
5797 } else if (now - last_ > 15)
5802 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5806 - (void) _setProgressTitle:(NSString *)title {
5807 [prompt_ setText:title];
5810 - (void) _setProgressPercent:(NSNumber *)percent {
5811 [progress_ setProgress:[percent floatValue]];
5814 - (void) _addProgressOutput:(NSString *)output {
5819 /* Cydia:// Protocol {{{ */
5820 @interface CydiaURLProtocol : NSURLProtocol {
5825 @implementation CydiaURLProtocol
5827 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5828 NSURL *url([request URL]);
5831 NSString *scheme([[url scheme] lowercaseString]);
5832 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5837 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5841 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5842 id<NSURLProtocolClient> client([self client]);
5843 NSData *data(UIImagePNGRepresentation(icon));
5845 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5846 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5847 [client URLProtocol:self didLoadData:data];
5848 [client URLProtocolDidFinishLoading:self];
5851 - (void) startLoading {
5852 id<NSURLProtocolClient> client([self client]);
5853 NSURLRequest *request([self request]);
5855 NSURL *url([request URL]);
5856 NSString *href([url absoluteString]);
5858 NSString *path([href substringFromIndex:8]);
5859 NSRange slash([path rangeOfString:@"/"]);
5862 if (slash.location == NSNotFound) {
5866 command = [path substringToIndex:slash.location];
5867 path = [path substringFromIndex:(slash.location + 1)];
5870 Database *database([Database sharedInstance]);
5872 if ([command isEqualToString:@"package-icon"]) {
5875 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5876 Package *package([database packageWithName:path]);
5879 UIImage *icon([package icon]);
5880 [self _returnPNGWithImage:icon forRequest:request];
5881 } else if ([command isEqualToString:@"source-icon"]) {
5884 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5885 NSString *source(Simplify(path));
5886 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5888 icon = [UIImage applicationImageNamed:@"unknown.png"];
5889 [self _returnPNGWithImage:icon forRequest:request];
5890 } else if ([command isEqualToString:@"uikit-image"]) {
5893 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5894 UIImage *icon(_UIImageWithName(path));
5895 [self _returnPNGWithImage:icon forRequest:request];
5896 } else if ([command isEqualToString:@"section-icon"]) {
5899 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5900 NSString *section(Simplify(path));
5901 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5903 icon = [UIImage applicationImageNamed:@"unknown.png"];
5904 [self _returnPNGWithImage:icon forRequest:request];
5906 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5910 - (void) stopLoading {
5916 /* Install View {{{ */
5917 @interface InstallView : RVPage {
5918 _transient Database *database_;
5919 NSMutableArray *sections_;
5920 NSMutableArray *filtered_;
5921 UITransitionView *transition_;
5927 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5928 - (void) reloadData;
5933 @implementation InstallView
5936 [list_ setDataSource:nil];
5937 [list_ setDelegate:nil];
5939 [sections_ release];
5940 [filtered_ release];
5941 [transition_ release];
5943 [accessory_ release];
5947 - (int) numberOfRowsInTable:(UITable *)table {
5948 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5951 - (float) table:(UITable *)table heightForRow:(int)row {
5955 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5957 reusing = [[[SectionCell alloc] init] autorelease];
5958 [(SectionCell *)reusing setSection:(editing_ ?
5959 [sections_ objectAtIndex:row] :
5960 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5961 ) editing:editing_];
5965 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5969 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5973 - (void) tableRowSelected:(NSNotification *)notification {
5974 int row = [[notification object] selectedRow];
5985 title = @"All Packages";
5987 section = [filtered_ objectAtIndex:(row - 1)];
5988 name = [section name];
5994 title = @"(No Section)";
5998 PackageTable *table = [[[PackageTable alloc]
6002 filter:@selector(isVisiblyUninstalledInSection:)
6006 [table setDelegate:delegate_];
6008 [book_ pushPage:table];
6011 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6012 if ((self = [super initWithBook:book]) != nil) {
6013 database_ = database;
6015 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6016 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
6018 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
6019 [self addSubview:transition_];
6021 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
6022 [transition_ transition:0 toView:list_];
6024 UITableColumn *column = [[[UITableColumn alloc]
6025 initWithTitle:@"Name"
6027 width:[self frame].size.width
6030 [list_ setDataSource:self];
6031 [list_ setSeparatorStyle:1];
6032 [list_ addTableColumn:column];
6033 [list_ setDelegate:self];
6034 [list_ setReusesTableCells:YES];
6038 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6039 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6043 - (void) reloadData {
6044 NSArray *packages = [database_ packages];
6046 [sections_ removeAllObjects];
6047 [filtered_ removeAllObjects];
6049 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
6050 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
6053 for (size_t i(0); i != [packages count]; ++i) {
6054 Package *package([packages objectAtIndex:i]);
6055 NSString *name([package section]);
6058 Section *section([sections objectForKey:name]);
6059 if (section == nil) {
6060 section = [[[Section alloc] initWithName:name] autorelease];
6061 [sections setObject:section forKey:name];
6065 if ([package valid] && [package installed] == nil && [package visible])
6066 [filtered addObject:package];
6070 [sections_ addObjectsFromArray:[sections allValues]];
6071 [sections_ sortUsingSelector:@selector(compareByName:)];
6074 [filtered sortUsingSelector:@selector(compareBySection:)];
6077 Section *section = nil;
6078 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
6079 Package *package = [filtered objectAtIndex:offset];
6080 NSString *name = [package section];
6082 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
6083 section = name == nil ?
6084 [[[Section alloc] initWithName:nil] autorelease] :
6085 [sections objectForKey:name];
6086 [filtered_ addObject:section];
6089 [section addToCount];
6097 - (void) resetView {
6099 [self _rightButtonClicked];
6102 - (void) resetViewAnimated:(BOOL)animated {
6103 [list_ resetViewAnimated:animated];
6106 - (void) _rightButtonClicked {
6107 if ((editing_ = !editing_))
6110 [delegate_ updateData];
6111 [book_ reloadTitleForPage:self];
6112 [book_ reloadButtonsForPage:self];
6115 - (NSString *) title {
6116 return editing_ ? @"Section Visibility" : @"Install by Section";
6119 - (NSString *) backButtonTitle {
6123 - (NSString *) rightButtonTitle {
6124 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
6127 - (UINavigationButtonStyle) rightButtonStyle {
6128 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
6131 - (UIView *) accessoryView {
6137 /* Changes View {{{ */
6138 @interface ChangesView : RVPage {
6139 _transient Database *database_;
6140 NSMutableArray *packages_;
6141 NSMutableArray *sections_;
6142 UISectionList *list_;
6146 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6147 - (void) reloadData;
6151 @implementation ChangesView
6154 [[list_ table] setDelegate:nil];
6155 [list_ setDataSource:nil];
6157 [packages_ release];
6158 [sections_ release];
6163 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
6164 return [sections_ count];
6167 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
6168 return [[sections_ objectAtIndex:section] name];
6171 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
6172 return [[sections_ objectAtIndex:section] row];
6175 - (int) numberOfRowsInTable:(UITable *)table {
6176 return [packages_ count];
6179 - (float) table:(UITable *)table heightForRow:(int)row {
6180 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
6183 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
6185 reusing = [[[PackageCell alloc] init] autorelease];
6186 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
6190 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
6194 - (void) tableRowSelected:(NSNotification *)notification {
6195 int row = [[notification object] selectedRow];
6198 Package *package = [packages_ objectAtIndex:row];
6199 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6200 [view setDelegate:delegate_];
6201 [view setPackage:package];
6202 [book_ pushPage:view];
6205 - (void) _leftButtonClicked {
6206 [(CYBook *)book_ update];
6207 [self reloadButtons];
6210 - (void) _rightButtonClicked {
6211 [delegate_ distUpgrade];
6214 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6215 if ((self = [super initWithBook:book]) != nil) {
6216 database_ = database;
6218 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
6219 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6221 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
6222 [self addSubview:list_];
6224 [list_ setShouldHideHeaderInShortLists:NO];
6225 [list_ setDataSource:self];
6226 //[list_ setSectionListStyle:1];
6228 UITableColumn *column = [[[UITableColumn alloc]
6229 initWithTitle:@"Name"
6231 width:[self frame].size.width
6234 UITable *table = [list_ table];
6235 [table setSeparatorStyle:1];
6236 [table addTableColumn:column];
6237 [table setDelegate:self];
6238 [table setReusesTableCells:YES];
6242 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6243 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6247 - (void) reloadData {
6248 NSArray *packages = [database_ packages];
6250 [packages_ removeAllObjects];
6251 [sections_ removeAllObjects];
6254 for (size_t i(0); i != [packages count]; ++i) {
6255 Package *package([packages objectAtIndex:i]);
6258 [package installed] == nil && [package valid] && [package visible] ||
6259 [package upgradableAndEssential:NO]
6261 [packages_ addObject:package];
6265 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
6268 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
6269 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
6270 Section *section = nil;
6274 bool unseens = false;
6276 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
6279 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6280 Package *package = [packages_ objectAtIndex:offset];
6282 if (![package upgradableAndEssential:YES]) {
6284 NSDate *seen = [package seen];
6286 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6289 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
6290 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6291 [sections_ addObject:section];
6295 [section addToCount];
6296 } else if ([package ignored])
6297 [ignored addToCount];
6300 [upgradable addToCount];
6305 CFRelease(formatter);
6308 Section *last = [sections_ lastObject];
6309 size_t count = [last count];
6310 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6311 [sections_ removeLastObject];
6314 if ([ignored count] != 0)
6315 [sections_ insertObject:ignored atIndex:0];
6317 [sections_ insertObject:upgradable atIndex:0];
6320 [self reloadButtons];
6323 - (void) resetViewAnimated:(BOOL)animated {
6324 [list_ resetViewAnimated:animated];
6327 - (NSString *) leftButtonTitle {
6328 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6331 - (NSString *) rightButtonTitle {
6332 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
6335 - (NSString *) title {
6341 /* Search View {{{ */
6342 @protocol SearchViewDelegate
6343 - (void) showKeyboard:(BOOL)show;
6346 @interface SearchView : RVPage {
6348 UISearchField *field_;
6349 UITransitionView *transition_;
6350 PackageTable *table_;
6351 UIPreferencesTable *advanced_;
6357 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6358 - (void) reloadData;
6362 @implementation SearchView
6365 [field_ setDelegate:nil];
6367 [accessory_ release];
6369 [transition_ release];
6371 [advanced_ release];
6376 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6380 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6382 case 0: return @"Advanced Search (Coming Soon!)";
6384 default: _assert(false);
6388 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6392 default: _assert(false);
6396 - (void) _showKeyboard:(BOOL)show {
6397 CGSize keysize = [UIKeyboard defaultSize];
6398 CGRect keydown = [book_ pageBounds];
6399 CGRect keyup = keydown;
6400 keyup.size.height -= keysize.height - ButtonBarHeight_;
6402 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6404 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6405 [animation setSignificantRectFields:8];
6408 [animation setStartFrame:keydown];
6409 [animation setEndFrame:keyup];
6411 [animation setStartFrame:keyup];
6412 [animation setEndFrame:keydown];
6415 UIAnimator *animator = [UIAnimator sharedAnimator];
6418 addAnimations:[NSArray arrayWithObjects:animation, nil]
6419 withDuration:(KeyboardTime_ - delay)
6424 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6426 [delegate_ showKeyboard:show];
6429 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6430 [self _showKeyboard:YES];
6433 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6434 [self _showKeyboard:NO];
6437 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6439 NSString *text([field_ text]);
6440 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6446 - (void) textFieldClearButtonPressed:(UITextField *)field {
6450 - (void) keyboardInputShouldDelete:(id)input {
6454 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6455 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6459 [field_ resignFirstResponder];
6464 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6465 if ((self = [super initWithBook:book]) != nil) {
6466 CGRect pageBounds = [book_ pageBounds];
6468 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6469 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6470 [self addSubview:pinstripe];*/
6472 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6473 [self addSubview:transition_];
6475 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6477 [advanced_ setReusesTableCells:YES];
6478 [advanced_ setDataSource:self];
6479 [advanced_ reloadData];
6481 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6482 CGColor dimmed(space_, 0, 0, 0, 0.5);
6483 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6485 table_ = [[PackageTable alloc]
6489 filter:@selector(isUnfilteredAndSearchedForBy:)
6493 [table_ setShouldHideHeaderInShortLists:NO];
6494 [transition_ transition:0 toView:table_];
6503 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6510 [self bounds].size.width - area.origin.x - 18;
6512 area.size.height = [UISearchField defaultHeight];
6514 field_ = [[UISearchField alloc] initWithFrame:area];
6516 UIFont *font = [UIFont systemFontOfSize:16];
6517 [field_ setFont:font];
6519 [field_ setPlaceholder:@"Package Names & Descriptions"];
6520 [field_ setDelegate:self];
6522 [field_ setPaddingTop:5];
6524 UITextInputTraits *traits([field_ textInputTraits]);
6525 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6526 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6527 [traits setReturnKeyType:UIReturnKeySearch];
6529 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6531 accessory_ = [[UIView alloc] initWithFrame:accrect];
6532 [accessory_ addSubview:field_];
6534 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6535 [configure setShowPressFeedback:YES];
6536 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6537 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6538 [accessory_ addSubview:configure];*/
6540 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6541 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6547 LKAnimation *animation = [LKTransition animation];
6548 [animation setType:@"oglFlip"];
6549 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6550 [animation setFillMode:@"extended"];
6551 [animation setTransitionFlags:3];
6552 [animation setDuration:10];
6553 [animation setSpeed:0.35];
6554 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6555 [[transition_ _layer] addAnimation:animation forKey:0];
6556 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6557 flipped_ = !flipped_;
6561 - (void) configurePushed {
6562 [field_ resignFirstResponder];
6566 - (void) resetViewAnimated:(BOOL)animated {
6569 [table_ resetViewAnimated:animated];
6572 - (void) _reloadData {
6575 - (void) reloadData {
6578 [table_ setObject:[field_ text]];
6579 [table_ reloadData];
6580 [table_ resetCursor];
6583 - (UIView *) accessoryView {
6587 - (NSString *) title {
6591 - (NSString *) backButtonTitle {
6595 - (void) setDelegate:(id)delegate {
6596 [table_ setDelegate:delegate];
6597 [super setDelegate:delegate];
6603 @interface SettingsView : RVPage {
6604 _transient Database *database_;
6607 UIPreferencesTable *table_;
6608 _UISwitchSlider *subscribedSwitch_;
6609 _UISwitchSlider *ignoredSwitch_;
6610 UIPreferencesControlTableCell *subscribedCell_;
6611 UIPreferencesControlTableCell *ignoredCell_;
6614 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6618 @implementation SettingsView
6621 [table_ setDataSource:nil];
6624 if (package_ != nil)
6627 [subscribedSwitch_ release];
6628 [ignoredSwitch_ release];
6629 [subscribedCell_ release];
6630 [ignoredCell_ release];
6634 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6635 if (package_ == nil)
6641 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6642 if (package_ == nil)
6649 default: _assert(false);
6655 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6656 if (package_ == nil)
6663 default: _assert(false);
6669 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6670 if (package_ == nil)
6677 default: _assert(false);
6683 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6684 if (package_ == nil)
6687 _UISwitchSlider *slider([cell control]);
6688 BOOL value([slider value] != 0);
6689 NSMutableDictionary *metadata([package_ metadata]);
6692 if (NSNumber *number = [metadata objectForKey:key])
6693 before = [number boolValue];
6697 if (value != before) {
6698 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6700 [delegate_ updateData];
6704 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6705 [self onSomething:cell withKey:@"IsSubscribed"];
6708 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6709 [self onSomething:cell withKey:@"IsIgnored"];
6712 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6713 if (package_ == nil)
6717 case 0: switch (row) {
6719 return subscribedCell_;
6721 return ignoredCell_;
6722 default: _assert(false);
6725 case 1: switch (row) {
6727 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6728 [cell setShowSelection:NO];
6729 [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."];
6733 default: _assert(false);
6736 default: _assert(false);
6742 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6743 if ((self = [super initWithBook:book])) {
6744 database_ = database;
6745 name_ = [package retain];
6747 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6748 [self addSubview:table_];
6750 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6751 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6753 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6754 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6756 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6757 [subscribedCell_ setShowSelection:NO];
6758 [subscribedCell_ setTitle:@"Show All Changes"];
6759 [subscribedCell_ setControl:subscribedSwitch_];
6761 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6762 [ignoredCell_ setShowSelection:NO];
6763 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6764 [ignoredCell_ setControl:ignoredSwitch_];
6766 [table_ setDataSource:self];
6771 - (void) resetViewAnimated:(BOOL)animated {
6772 [table_ resetViewAnimated:animated];
6775 - (void) reloadData {
6776 if (package_ != nil)
6777 [package_ autorelease];
6778 package_ = [database_ packageWithName:name_];
6779 if (package_ != nil) {
6781 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6782 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6785 [table_ reloadData];
6788 - (NSString *) title {
6794 /* Signature View {{{ */
6795 @interface SignatureView : BrowserView {
6796 _transient Database *database_;
6800 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6804 @implementation SignatureView
6811 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6813 [super webView:sender didClearWindowObject:window forFrame:frame];
6816 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6817 if ((self = [super initWithBook:book]) != nil) {
6818 database_ = database;
6819 package_ = [package retain];
6824 - (void) resetViewAnimated:(BOOL)animated {
6827 - (void) reloadData {
6828 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6834 @interface Cydia : UIApplication <
6835 ConfirmationViewDelegate,
6836 ProgressViewDelegate,
6845 UIToolbar *buttonbar_;
6849 NSMutableArray *essential_;
6850 NSMutableArray *broken_;
6852 Database *database_;
6853 ProgressView *progress_;
6857 UIKeyboard *keyboard_;
6858 UIProgressHUD *hud_;
6860 InstallView *install_;
6861 ChangesView *changes_;
6862 ManageView *manage_;
6863 SearchView *search_;
6868 @implementation Cydia
6871 if ([broken_ count] != 0) {
6872 int count = [broken_ count];
6874 UIActionSheet *sheet = [[[UIActionSheet alloc]
6875 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6876 buttons:[NSArray arrayWithObjects:
6878 @"Ignore (Temporary)",
6880 defaultButtonIndex:0
6885 [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."];
6886 [sheet popupAlertAnimated:YES];
6887 } else if (!Ignored_ && [essential_ count] != 0) {
6888 int count = [essential_ count];
6890 UIActionSheet *sheet = [[[UIActionSheet alloc]
6891 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6892 buttons:[NSArray arrayWithObjects:
6893 @"Upgrade Essential",
6894 @"Complete Upgrade",
6895 @"Ignore (Temporary)",
6897 defaultButtonIndex:0
6902 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6903 [sheet popupAlertAnimated:YES];
6907 - (void) _reloadData {
6908 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6909 [hud setText:@"Reloading Data"];
6910 [overlay_ addSubview:hud];
6913 [database_ reloadData];
6917 [essential_ removeAllObjects];
6918 [broken_ removeAllObjects];
6920 NSArray *packages = [database_ packages];
6921 for (Package *package in packages) {
6923 [broken_ addObject:package];
6924 if ([package upgradableAndEssential:NO]) {
6925 if ([package essential])
6926 [essential_ addObject:package];
6932 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6933 [buttonbar_ setBadgeValue:badge forButton:3];
6934 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6935 [buttonbar_ setBadgeAnimated:YES forButton:3];
6936 [self setApplicationBadge:badge];
6938 [buttonbar_ setBadgeValue:nil forButton:3];
6939 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6940 [buttonbar_ setBadgeAnimated:NO forButton:3];
6941 [self removeApplicationBadge];
6947 if ([packages count] == 0);
6959 [hud removeFromSuperview];*/
6962 - (void) _saveConfig {
6965 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6971 - (void) updateData {
6974 /* XXX: this is just stupid */
6975 if (tag_ != 2 && install_ != nil)
6976 [install_ reloadData];
6977 if (tag_ != 3 && changes_ != nil)
6978 [changes_ reloadData];
6979 if (tag_ != 5 && search_ != nil)
6980 [search_ reloadData];
6990 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6991 _assert(file != NULL);
6993 NSArray *keys = [Sources_ allKeys];
6995 for (int i(0), e([keys count]); i != e; ++i) {
6996 NSString *key = [keys objectAtIndex:i];
6997 NSDictionary *source = [Sources_ objectForKey:key];
6999 fprintf(file, "%s %s %s\n",
7000 [[source objectForKey:@"Type"] UTF8String],
7001 [[source objectForKey:@"URI"] UTF8String],
7002 [[source objectForKey:@"Distribution"] UTF8String]
7011 detachNewThreadSelector:@selector(update_)
7014 title:@"Updating Sources"
7018 - (void) reloadData {
7019 @synchronized (self) {
7020 if (confirm_ == nil)
7026 pkgProblemResolver *resolver = [database_ resolver];
7028 resolver->InstallProtect();
7029 if (!resolver->Resolve(true))
7034 [database_ prepare];
7036 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
7037 [confirm_ setDelegate:self];
7039 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
7040 [page setDelegate:self];
7042 [confirm_ setPage:page];
7043 [underlay_ popSubview:confirm_];
7046 - (void) installPackage:(Package *)package {
7047 @synchronized (self) {
7054 - (void) removePackage:(Package *)package {
7055 @synchronized (self) {
7062 - (void) distUpgrade {
7063 @synchronized (self) {
7064 [database_ upgrade];
7070 @synchronized (self) {
7072 if (confirm_ != nil) {
7080 [overlay_ removeFromSuperview];
7084 detachNewThreadSelector:@selector(perform)
7091 - (void) bootstrap_ {
7093 [database_ upgrade];
7094 [database_ prepare];
7095 [database_ perform];
7098 - (void) bootstrap {
7100 detachNewThreadSelector:@selector(bootstrap_)
7103 title:@"Bootstrap Install"
7107 - (void) progressViewIsComplete:(ProgressView *)progress {
7108 if (confirm_ != nil) {
7109 [underlay_ addSubview:overlay_];
7110 [confirm_ popFromSuperviewAnimated:NO];
7116 - (void) setPage:(RVPage *)page {
7117 [page resetViewAnimated:NO];
7118 [page setDelegate:self];
7119 [book_ setPage:page];
7122 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
7123 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
7124 [browser loadURL:url];
7128 - (void) _setHomePage {
7129 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
7132 - (void) buttonBarItemTapped:(id)sender {
7133 unsigned tag = [sender tag];
7135 [book_ resetViewAnimated:YES];
7137 } else if (tag_ == 2 && tag != 2)
7138 [install_ resetView];
7141 case 1: [self _setHomePage]; break;
7143 case 2: [self setPage:install_]; break;
7144 case 3: [self setPage:changes_]; break;
7145 case 4: [self setPage:manage_]; break;
7146 case 5: [self setPage:search_]; break;
7148 default: _assert(false);
7154 - (void) applicationWillSuspend {
7156 [super applicationWillSuspend];
7159 - (void) askForSettings {
7160 UIActionSheet *role = [[[UIActionSheet alloc]
7161 initWithTitle:@"Who Are You?"
7162 buttons:[NSArray arrayWithObjects:
7163 @"User (Graphical Only)",
7164 @"Hacker (+ Command Line)",
7165 @"Developer (No Filters)",
7167 defaultButtonIndex:-1
7172 [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."];
7173 [role popupAlertAnimated:YES];
7178 [self setStatusBarShowsProgress:NO];
7181 [hud_ removeFromSuperview];
7185 pid_t pid = ExecFork();
7187 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
7188 perror("launchctl stop");
7195 [self askForSettings];
7199 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
7201 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7202 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
7203 0, 0, screenrect.size.width, screenrect.size.height - 48
7204 ) database:database_];
7206 [book_ setDelegate:self];
7208 [overlay_ addSubview:book_];
7210 NSArray *buttonitems = [NSArray arrayWithObjects:
7211 [NSDictionary dictionaryWithObjectsAndKeys:
7212 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7213 @"home-up.png", kUIButtonBarButtonInfo,
7214 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
7215 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
7216 self, kUIButtonBarButtonTarget,
7217 @"Home", kUIButtonBarButtonTitle,
7218 @"0", kUIButtonBarButtonType,
7221 [NSDictionary dictionaryWithObjectsAndKeys:
7222 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7223 @"install-up.png", kUIButtonBarButtonInfo,
7224 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
7225 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
7226 self, kUIButtonBarButtonTarget,
7227 @"Sections", kUIButtonBarButtonTitle,
7228 @"0", kUIButtonBarButtonType,
7231 [NSDictionary dictionaryWithObjectsAndKeys:
7232 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7233 @"changes-up.png", kUIButtonBarButtonInfo,
7234 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
7235 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
7236 self, kUIButtonBarButtonTarget,
7237 @"Changes", kUIButtonBarButtonTitle,
7238 @"0", kUIButtonBarButtonType,
7241 [NSDictionary dictionaryWithObjectsAndKeys:
7242 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7243 @"manage-up.png", kUIButtonBarButtonInfo,
7244 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
7245 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
7246 self, kUIButtonBarButtonTarget,
7247 @"Manage", kUIButtonBarButtonTitle,
7248 @"0", kUIButtonBarButtonType,
7251 [NSDictionary dictionaryWithObjectsAndKeys:
7252 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7253 @"search-up.png", kUIButtonBarButtonInfo,
7254 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
7255 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
7256 self, kUIButtonBarButtonTarget,
7257 @"Search", kUIButtonBarButtonTitle,
7258 @"0", kUIButtonBarButtonType,
7262 buttonbar_ = [[UIToolbar alloc]
7264 withFrame:CGRectMake(
7265 0, screenrect.size.height - ButtonBarHeight_,
7266 screenrect.size.width, ButtonBarHeight_
7268 withItemList:buttonitems
7271 [buttonbar_ setDelegate:self];
7272 [buttonbar_ setBarStyle:1];
7273 [buttonbar_ setButtonBarTrackingMode:2];
7275 int buttons[5] = {1, 2, 3, 4, 5};
7276 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7277 [buttonbar_ showButtonGroup:0 withDuration:0];
7279 for (int i = 0; i != 5; ++i)
7280 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
7281 i * 64 + 2, 1, 60, ButtonBarHeight_
7284 [buttonbar_ showSelectionForButton:1];
7285 [overlay_ addSubview:buttonbar_];
7287 [UIKeyboard initImplementationNow];
7288 CGSize keysize = [UIKeyboard defaultSize];
7289 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
7290 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
7291 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
7292 [overlay_ addSubview:keyboard_];
7295 [underlay_ addSubview:overlay_];
7299 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
7300 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
7301 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7303 manage_ = (ManageView *) [[self
7304 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7305 withClass:[ManageView class]
7311 [self _setHomePage];
7314 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7315 NSString *context([sheet context]);
7317 if ([context isEqualToString:@"fixhalf"]) {
7320 @synchronized (self) {
7321 for (int i = 0, e = [broken_ count]; i != e; ++i) {
7322 Package *broken = [broken_ objectAtIndex:i];
7325 NSString *id = [broken id];
7326 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7327 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7328 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7329 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7338 [broken_ removeAllObjects];
7347 } else if ([context isEqualToString:@"role"]) {
7349 case 1: Role_ = @"User"; break;
7350 case 2: Role_ = @"Hacker"; break;
7351 case 3: Role_ = @"Developer"; break;
7358 bool reset = Settings_ != nil;
7360 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7364 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7374 } else if ([context isEqualToString:@"upgrade"]) {
7377 @synchronized (self) {
7378 for (int i = 0, e = [essential_ count]; i != e; ++i) {
7379 Package *essential = [essential_ objectAtIndex:i];
7380 [essential install];
7404 - (void) reorganize { _pooled
7405 system("/usr/libexec/cydia/free.sh");
7406 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7409 - (void) applicationSuspend:(__GSEvent *)event {
7410 if (hud_ == nil && ![progress_ isRunning])
7411 [super applicationSuspend:event];
7414 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7416 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7419 - (void) _setSuspended:(BOOL)value {
7421 [super _setSuspended:value];
7424 - (UIProgressHUD *) addProgressHUD {
7425 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7427 [underlay_ addSubview:hud];
7431 - (void) openMailToURL:(NSURL *)url {
7432 // XXX: this makes me sad
7434 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7436 [UIApp openURL:url];// asPanel:YES];
7440 - (RVPage *) pageForPackage:(NSString *)name {
7441 if (Package *package = [database_ packageWithName:name]) {
7442 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7443 [view setPackage:package];
7446 UIActionSheet *sheet = [[[UIActionSheet alloc]
7447 initWithTitle:@"Cannot Locate Package"
7448 buttons:[NSArray arrayWithObjects:@"Close", nil]
7449 defaultButtonIndex:0
7454 [sheet setBodyText:[NSString stringWithFormat:
7455 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7458 [sheet popupAlertAnimated:YES];
7463 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7464 NSString *href = [url absoluteString];
7469 if ([href isEqualToString:@"cydia://add-source"])
7470 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7471 else if ([href isEqualToString:@"cydia://sources"])
7472 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7473 else if ([href isEqualToString:@"cydia://packages"])
7474 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7475 else if ([href hasPrefix:@"cydia://url/"])
7476 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7477 else if ([href hasPrefix:@"cydia://launch/"])
7478 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7479 else if ([href hasPrefix:@"cydia://package-settings/"])
7480 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7481 else if ([href hasPrefix:@"cydia://package-signature/"])
7482 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7483 else if ([href hasPrefix:@"cydia://package/"])
7484 return [self pageForPackage:[href substringFromIndex:16]];
7485 else if ([href hasPrefix:@"cydia://files/"]) {
7486 NSString *name = [href substringFromIndex:14];
7488 if (Package *package = [database_ packageWithName:name]) {
7489 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7490 [files setPackage:package];
7498 - (void) applicationOpenURL:(NSURL *)url {
7499 [super applicationOpenURL:url];
7501 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7502 [self setPage:page];
7503 [buttonbar_ showSelectionForButton:tag];
7508 - (void) applicationDidFinishLaunching:(id)unused {
7509 Font12_ = [[UIFont systemFontOfSize:12] retain];
7510 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7511 Font14_ = [[UIFont systemFontOfSize:14] retain];
7512 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7513 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7515 _assert(pkgInitConfig(*_config));
7516 _assert(pkgInitSystem(*_config, _system));
7520 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7521 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7523 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7525 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7526 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7528 [window_ orderFront:self];
7529 [window_ makeKey:self];
7530 [window_ setHidden:NO];
7532 database_ = [Database sharedInstance];
7533 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7534 [database_ setDelegate:progress_];
7535 [window_ setContentView:progress_];
7537 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7538 [progress_ setContentView:underlay_];
7540 [progress_ resetView];
7543 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7544 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7545 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7546 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7547 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7548 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7549 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7551 [self setIdleTimerDisabled:YES];
7553 hud_ = [self addProgressHUD];
7554 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7556 [self setStatusBarShowsProgress:YES];
7559 detachNewThreadSelector:@selector(reorganize)
7567 - (void) showKeyboard:(BOOL)show {
7568 CGSize keysize = [UIKeyboard defaultSize];
7569 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7570 CGRect keyup = keydown;
7571 keyup.origin.y -= keysize.height;
7573 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7574 [animation setSignificantRectFields:2];
7577 [animation setStartFrame:keydown];
7578 [animation setEndFrame:keyup];
7579 [keyboard_ activate];
7581 [animation setStartFrame:keyup];
7582 [animation setEndFrame:keydown];
7583 [keyboard_ deactivate];
7586 [[UIAnimator sharedAnimator]
7587 addAnimations:[NSArray arrayWithObjects:animation, nil]
7588 withDuration:KeyboardTime_
7593 - (void) slideUp:(UIActionSheet *)alert {
7595 [alert presentSheetFromButtonBar:buttonbar_];
7597 [alert presentSheetInView:overlay_];
7602 void AddPreferences(NSString *plist) { _pooled
7603 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7604 _assert(settings != NULL);
7605 NSMutableArray *items = [settings objectForKey:@"items"];
7609 for (size_t i(0); i != [items count]; ++i) {
7610 NSMutableDictionary *item([items objectAtIndex:i]);
7611 NSString *label = [item objectForKey:@"label"];
7612 if (label != nil && [label isEqualToString:@"Cydia"]) {
7619 for (size_t i(0); i != [items count]; ++i) {
7620 NSDictionary *item([items objectAtIndex:i]);
7621 NSString *label = [item objectForKey:@"label"];
7622 if (label != nil && [label isEqualToString:@"General"]) {
7623 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7624 @"CydiaSettings", @"bundle",
7625 @"PSLinkCell", @"cell",
7626 [NSNumber numberWithBool:YES], @"hasIcon",
7627 [NSNumber numberWithBool:YES], @"isController",
7629 nil] atIndex:(i + 1)];
7635 _assert([settings writeToFile:plist atomically:YES] == YES);
7640 id Alloc_(id self, SEL selector) {
7641 id object = alloc_(self, selector);
7642 lprintf("[%s]A-%p\n", self->isa->name, object);
7647 id Dealloc_(id self, SEL selector) {
7648 id object = dealloc_(self, selector);
7649 lprintf("[%s]D-%p\n", self->isa->name, object);
7653 int main(int argc, char *argv[]) { _pooled
7654 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7656 bool substrate(false);
7662 for (int argi(1); argi != argc; ++argi)
7663 if (strcmp(argv[argi], "--") == 0) {
7665 argv[argi] = argv[0];
7671 for (int argi(1); argi != arge; ++argi)
7672 if (strcmp(args[argi], "--bootstrap") == 0)
7674 else if (strcmp(args[argi], "--substrate") == 0)
7677 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7680 App_ = [[NSBundle mainBundle] bundlePath];
7681 Home_ = NSHomeDirectory();
7682 Locale_ = CFLocaleCopyCurrent();
7685 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7686 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7687 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7688 Sounds_Keyboard_ = [keyboard boolValue];
7694 #if 1 /* XXX: this costs 1.4s of startup performance */
7695 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7696 _assert(errno == ENOENT);
7697 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7698 _assert(errno == ENOENT);
7701 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7702 alloc_ = alloc->method_imp;
7703 alloc->method_imp = (IMP) &Alloc_;*/
7705 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7706 dealloc_ = dealloc->method_imp;
7707 dealloc->method_imp = (IMP) &Dealloc_;*/
7712 size = sizeof(maxproc);
7713 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7714 perror("sysctlbyname(\"kern.maxproc\", ?)");
7715 else if (maxproc < 64) {
7717 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7718 perror("sysctlbyname(\"kern.maxproc\", #)");
7721 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7722 char *machine = new char[size];
7723 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7724 perror("sysctlbyname(\"hw.machine\", ?)");
7728 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7730 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7731 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7733 if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7734 Indices_ = [[NSMutableDictionary alloc] init];
7736 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7737 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7739 Settings_ = [Metadata_ objectForKey:@"Settings"];
7741 Packages_ = [Metadata_ objectForKey:@"Packages"];
7742 Sections_ = [Metadata_ objectForKey:@"Sections"];
7743 Sources_ = [Metadata_ objectForKey:@"Sources"];
7746 if (Settings_ != nil)
7747 Role_ = [Settings_ objectForKey:@"Role"];
7749 if (Packages_ == nil) {
7750 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7751 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7754 if (Sections_ == nil) {
7755 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7756 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7759 if (Sources_ == nil) {
7760 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7761 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7765 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7768 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7769 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7771 if (access("/User", F_OK) != 0)
7772 system("/usr/libexec/cydia/firmware.sh");
7774 _assert([[NSFileManager defaultManager]
7775 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7776 withIntermediateDirectories:YES
7781 space_ = CGColorSpaceCreateDeviceRGB();
7783 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7784 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7785 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7786 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7787 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7788 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7790 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7792 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7794 UIApplicationUseLegacyEvents(YES);
7795 UIKeyboardDisableAutomaticAppearance();
7797 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7799 CGColorSpaceRelease(space_);