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>
72 #include <ext/stdio_filebuf.h>
74 #include <apt-pkg/acquire.h>
75 #include <apt-pkg/acquire-item.h>
76 #include <apt-pkg/algorithms.h>
77 #include <apt-pkg/cachefile.h>
78 #include <apt-pkg/clean.h>
79 #include <apt-pkg/configuration.h>
80 #include <apt-pkg/debmetaindex.h>
81 #include <apt-pkg/error.h>
82 #include <apt-pkg/init.h>
83 #include <apt-pkg/mmap.h>
84 #include <apt-pkg/pkgrecords.h>
85 #include <apt-pkg/sha1.h>
86 #include <apt-pkg/sourcelist.h>
87 #include <apt-pkg/sptr.h>
88 #include <apt-pkg/strutl.h>
90 #include <sys/types.h>
92 #include <sys/sysctl.h>
98 #include <mach-o/nlist.h>
108 #import "BrowserView.h"
109 #import "ResetView.h"
112 //#define _finline __attribute__((force_inline))
113 #define _finline inline
118 #define _limit(count) do { \
119 static size_t _count(0); \
120 if (++_count == count) \
124 static uint64_t profile_;
126 #define _timestamp ({ \
128 gettimeofday(&tv, NULL); \
129 tv.tv_sec * 1000000 + tv.tv_usec; \
132 /* Objective-C Handle<> {{{ */
133 template <typename Type_>
135 typedef _H<Type_> This_;
140 _finline void Retain_() {
145 _finline void Clear_() {
151 _finline _H(Type_ *value = NULL, bool mended = false) :
162 _finline This_ &operator =(Type_ *value) {
163 if (value_ != value) {
172 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
174 void NSLogPoint(const char *fix, const CGPoint &point) {
175 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
178 void NSLogRect(const char *fix, const CGRect &rect) {
179 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
182 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
184 /* iPhoneOS 2.0 Compatibility {{{ */
186 @interface UITextView (iPhoneOS)
187 - (void) setTextSize:(float)size;
190 @implementation UITextView (iPhoneOS)
192 - (void) setTextSize:(float)size {
193 [self setFont:[[self font] fontWithSize:size]];
200 extern NSString * const kCAFilterNearest;
202 /* Information Dictionaries {{{ */
203 @interface NSMutableArray (Cydia)
204 - (void) addInfoDictionary:(NSDictionary *)info;
207 @implementation NSMutableArray (Cydia)
209 - (void) addInfoDictionary:(NSDictionary *)info {
210 [self addObject:info];
215 @interface NSMutableDictionary (Cydia)
216 - (void) addInfoDictionary:(NSDictionary *)info;
219 @implementation NSMutableDictionary (Cydia)
221 - (void) addInfoDictionary:(NSDictionary *)info {
222 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
223 [self setObject:info forKey:bundle];
228 /* Pop Transitions {{{ */
229 @interface PopTransitionView : UITransitionView {
234 @implementation PopTransitionView
236 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
237 if (from != nil && to == nil)
238 [self removeFromSuperview];
243 @interface UIView (PopUpView)
244 - (void) popFromSuperviewAnimated:(BOOL)animated;
245 - (void) popSubview:(UIView *)view;
248 @implementation UIView (PopUpView)
250 - (void) popFromSuperviewAnimated:(BOOL)animated {
251 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
254 - (void) popSubview:(UIView *)view {
255 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
256 [transition setDelegate:transition];
257 [self addSubview:transition];
259 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
260 [transition transition:UITransitionNone toView:blank];
261 [transition transition:UITransitionPushFromBottom toView:view];
267 #define lprintf(args...) fprintf(stderr, args)
270 #define ForSaurik (1 && !ForRelease)
271 #define RecycleWebViews 0
272 #define AlwaysReload (1 && !ForRelease)
275 @interface NSMutableArray (Radix)
276 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
279 @implementation NSMutableArray (Radix)
281 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
282 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
283 [invocation setSelector:selector];
284 [invocation setArgument:&object atIndex:2];
286 size_t count([self count]);
291 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
293 for (size_t i(0); i != count; ++i) {
294 RadixItem &item(lhs[i]);
297 id object([self objectAtIndex:i]);
298 [invocation setTarget:object];
301 [invocation getReturnValue:&item.key];
304 static const size_t width = 32;
305 static const size_t bits = 11;
306 static const size_t slots = 1 << bits;
307 static const size_t passes = (width + (bits - 1)) / bits;
309 size_t *hist(new size_t[slots]);
311 for (size_t pass(0); pass != passes; ++pass) {
312 memset(hist, 0, sizeof(size_t) * slots);
314 for (size_t i(0); i != count; ++i) {
315 uint32_t key(lhs[i].key);
317 key &= _not(uint32_t) >> width - bits;
322 for (size_t i(0); i != slots; ++i) {
323 size_t local(offset);
328 for (size_t i(0); i != count; ++i) {
329 uint32_t key(lhs[i].key);
331 key &= _not(uint32_t) >> width - bits;
332 rhs[hist[key]++] = lhs[i];
342 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
343 for (size_t i(0); i != count; ++i)
344 [values addObject:[self objectAtIndex:lhs[i].index]];
345 [self setArray:values];
353 /* Apple Bug Fixes {{{ */
354 @implementation UIWebDocumentView (Cydia)
356 - (void) _setScrollerOffset:(CGPoint)offset {
357 UIScroller *scroller([self _scroller]);
359 CGSize size([scroller contentSize]);
360 CGSize bounds([scroller bounds].size);
363 max.x = size.width - bounds.width;
364 max.y = size.height - bounds.height;
372 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
373 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
375 [scroller setOffset:offset];
382 kUIControlEventMouseDown = 1 << 0,
383 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
384 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
385 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
386 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
387 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
388 } UIControlEventMasks;
390 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
391 size_t length([self length] - state->state);
394 else if (length > count)
396 for (size_t i(0); i != length; ++i)
397 objects[i] = [self item:state->state++];
398 state->itemsPtr = objects;
399 state->mutationsPtr = (unsigned long *) self;
403 @interface NSString (UIKit)
404 - (NSString *) stringByAddingPercentEscapes;
405 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
408 @interface NSString (Cydia)
409 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
410 - (NSComparisonResult) compareByPath:(NSString *)other;
413 @implementation NSString (Cydia)
415 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
416 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
419 - (NSComparisonResult) compareByPath:(NSString *)other {
420 NSString *prefix = [self commonPrefixWithString:other options:0];
421 size_t length = [prefix length];
423 NSRange lrange = NSMakeRange(length, [self length] - length);
424 NSRange rrange = NSMakeRange(length, [other length] - length);
426 lrange = [self rangeOfString:@"/" options:0 range:lrange];
427 rrange = [other rangeOfString:@"/" options:0 range:rrange];
429 NSComparisonResult value;
431 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
432 value = NSOrderedSame;
433 else if (lrange.location == NSNotFound)
434 value = NSOrderedAscending;
435 else if (rrange.location == NSNotFound)
436 value = NSOrderedDescending;
438 value = NSOrderedSame;
440 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
441 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
442 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
443 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
445 NSComparisonResult result = [lpath compare:rpath];
446 return result == NSOrderedSame ? value : result;
451 /* Perl-Compatible RegEx {{{ */
461 Pcre(const char *regex) :
466 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
469 lprintf("%d:%s\n", offset, error);
473 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
474 matches_ = new int[(capture_ + 1) * 3];
482 NSString *operator [](size_t match) {
483 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
486 bool operator ()(NSString *data) {
487 // XXX: length is for characters, not for bytes
488 return operator ()([data UTF8String], [data length]);
491 bool operator ()(const char *data, size_t size) {
493 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
497 /* Mime Addresses {{{ */
498 @interface Address : NSObject {
504 - (NSString *) address;
506 + (Address *) addressWithString:(NSString *)string;
507 - (Address *) initWithString:(NSString *)string;
510 @implementation Address
519 - (NSString *) name {
523 - (NSString *) address {
527 + (Address *) addressWithString:(NSString *)string {
528 return [[[Address alloc] initWithString:string] autorelease];
531 + (NSArray *) _attributeKeys {
532 return [NSArray arrayWithObjects:@"address", @"name", nil];
535 - (NSArray *) attributeKeys {
536 return [[self class] _attributeKeys];
539 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
540 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
543 - (Address *) initWithString:(NSString *)string {
544 if ((self = [super init]) != nil) {
545 const char *data = [string UTF8String];
546 size_t size = [string length];
548 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
550 if (address_r(data, size)) {
551 name_ = [address_r[1] retain];
552 address_ = [address_r[2] retain];
554 name_ = [string retain];
562 /* CoreGraphics Primitives {{{ */
573 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
576 Set(space, red, green, blue, alpha);
581 CGColorRelease(color_);
588 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
590 float color[] = {red, green, blue, alpha};
591 color_ = CGColorCreate(space, color);
594 operator CGColorRef() {
600 extern "C" void UISetColor(CGColorRef color);
602 /* Random Global Variables {{{ */
603 static const int PulseInterval_ = 50000;
604 static const int ButtonBarHeight_ = 48;
605 static const float KeyboardTime_ = 0.3f;
607 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
608 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
610 static CGColor Blue_;
611 static CGColor Blueish_;
612 static CGColor Black_;
614 static CGColor White_;
615 static CGColor Gray_;
617 static NSString *App_;
618 static NSString *Home_;
619 static BOOL Sounds_Keyboard_;
621 static BOOL Advanced_;
625 static BOOL Ignored_;
627 static UIFont *Font12_;
628 static UIFont *Font12Bold_;
629 static UIFont *Font14_;
630 static UIFont *Font18Bold_;
631 static UIFont *Font22Bold_;
633 static const char *Machine_ = NULL;
634 static const NSString *UniqueID_ = NULL;
641 CGColorSpaceRef space_;
643 #define FW_LEAST(major, minor, bugfix) \
644 (major < Major_ || major == Major_ && \
645 (minor < Minor_ || minor == Minor_ && \
651 static NSDictionary *SectionMap_;
652 static NSMutableDictionary *Metadata_;
653 static NSMutableDictionary *Indices_;
654 static _transient NSMutableDictionary *Settings_;
655 static _transient NSString *Role_;
656 static _transient NSMutableDictionary *Packages_;
657 static _transient NSMutableDictionary *Sections_;
658 static _transient NSMutableDictionary *Sources_;
659 static bool Changed_;
663 static NSMutableArray *Documents_;
666 NSString *GetLastUpdate() {
667 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
670 return @"Never or Unknown";
672 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
673 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
675 CFRelease(formatter);
677 return [(NSString *) formatted autorelease];
680 /* Display Helpers {{{ */
681 inline float Interpolate(float begin, float end, float fraction) {
682 return (end - begin) * fraction + begin;
685 NSString *SizeString(double size) {
686 bool negative = size < 0;
691 while (size > 1024) {
696 static const char *powers_[] = {"B", "kB", "MB", "GB"};
698 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
701 NSString *StripVersion(NSString *version) {
702 NSRange colon = [version rangeOfString:@":"];
703 if (colon.location != NSNotFound)
704 version = [version substringFromIndex:(colon.location + 1)];
708 NSString *Simplify(NSString *title) {
709 const char *data = [title UTF8String];
710 size_t size = [title length];
712 static Pcre square_r("^\\[(.*)\\]$");
713 if (square_r(data, size))
714 return Simplify(square_r[1]);
716 static Pcre paren_r("^\\((.*)\\)$");
717 if (paren_r(data, size))
718 return Simplify(paren_r[1]);
720 static Pcre title_r("^(.*?) \\(.*\\)$");
721 if (title_r(data, size))
722 return Simplify(title_r[1]);
728 bool isSectionVisible(NSString *section) {
729 NSDictionary *metadata = [Sections_ objectForKey:section];
730 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
731 return hidden == nil || ![hidden boolValue];
734 /* Delegate Prototypes {{{ */
738 @interface NSObject (ProgressDelegate)
741 @implementation NSObject(ProgressDelegate)
743 - (void) _setProgressError:(NSArray *)args {
744 [self performSelector:@selector(setProgressError:forPackage:)
745 withObject:[args objectAtIndex:0]
746 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
752 @protocol ProgressDelegate
753 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
754 - (void) setProgressTitle:(NSString *)title;
755 - (void) setProgressPercent:(float)percent;
756 - (void) startProgress;
757 - (void) addProgressOutput:(NSString *)output;
758 - (bool) isCancelling:(size_t)received;
761 @protocol ConfigurationDelegate
762 - (void) repairWithSelector:(SEL)selector;
763 - (void) setConfigurationData:(NSString *)data;
766 @protocol CydiaDelegate
767 - (void) installPackage:(Package *)package;
768 - (void) removePackage:(Package *)package;
769 - (void) slideUp:(UIActionSheet *)alert;
770 - (void) distUpgrade;
773 - (void) askForSettings;
774 - (UIProgressHUD *) addProgressHUD;
775 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
776 - (RVPage *) pageForPackage:(NSString *)name;
777 - (void) openMailToURL:(NSURL *)url;
781 /* Status Delegation {{{ */
783 public pkgAcquireStatus
786 _transient NSObject<ProgressDelegate> *delegate_;
794 void setDelegate(id delegate) {
795 delegate_ = delegate;
798 virtual bool MediaChange(std::string media, std::string drive) {
802 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
805 virtual void Fetch(pkgAcquire::ItemDesc &item) {
806 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
807 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
810 virtual void Done(pkgAcquire::ItemDesc &item) {
813 virtual void Fail(pkgAcquire::ItemDesc &item) {
815 item.Owner->Status == pkgAcquire::Item::StatIdle ||
816 item.Owner->Status == pkgAcquire::Item::StatDone
820 std::string &error(item.Owner->ErrorText);
824 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
825 NSArray *fields([description componentsSeparatedByString:@" "]);
826 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
828 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
829 withObject:[NSArray arrayWithObjects:
830 [NSString stringWithUTF8String:error.c_str()],
837 virtual bool Pulse(pkgAcquire *Owner) {
838 bool value = pkgAcquireStatus::Pulse(Owner);
841 double(CurrentBytes + CurrentItems) /
842 double(TotalBytes + TotalItems)
845 [delegate_ setProgressPercent:percent];
846 return [delegate_ isCancelling:CurrentBytes] ? false : value;
849 virtual void Start() {
850 [delegate_ startProgress];
853 virtual void Stop() {
857 /* Progress Delegation {{{ */
862 _transient id<ProgressDelegate> delegate_;
865 virtual void Update() {
866 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
867 [delegate_ setProgressPercent:(Percent / 100)];
876 void setDelegate(id delegate) {
877 delegate_ = delegate;
880 virtual void Done() {
881 [delegate_ setProgressPercent:1];
886 /* Database Interface {{{ */
887 @interface Database : NSObject {
889 pkgDepCache::Policy *policy_;
890 pkgRecords *records_;
891 pkgProblemResolver *resolver_;
892 pkgAcquire *fetcher_;
894 SPtr<pkgPackageManager> manager_;
895 pkgSourceList *list_;
897 NSMutableDictionary *sources_;
898 NSMutableArray *packages_;
900 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
909 + (Database *) sharedInstance;
911 - (void) _readCydia:(NSNumber *)fd;
912 - (void) _readStatus:(NSNumber *)fd;
913 - (void) _readOutput:(NSNumber *)fd;
917 - (Package *) packageWithName:(NSString *)name;
919 - (pkgCacheFile &) cache;
920 - (pkgDepCache::Policy *) policy;
921 - (pkgRecords *) records;
922 - (pkgProblemResolver *) resolver;
923 - (pkgAcquire &) fetcher;
924 - (NSArray *) packages;
925 - (NSArray *) sources;
934 - (void) updateWithStatus:(Status &)status;
936 - (void) setDelegate:(id)delegate;
937 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
941 /* Source Class {{{ */
942 @interface Source : NSObject {
943 NSString *description_;
948 NSString *distribution_;
952 NSString *defaultIcon_;
954 NSDictionary *record_;
958 - (Source *) initWithMetaIndex:(metaIndex *)index;
960 - (NSComparisonResult) compareByNameAndType:(Source *)source;
962 - (NSDictionary *) record;
966 - (NSString *) distribution;
972 - (NSString *) description;
973 - (NSString *) label;
974 - (NSString *) origin;
975 - (NSString *) version;
977 - (NSString *) defaultIcon;
981 @implementation Source
985 [distribution_ release];
988 if (description_ != nil)
989 [description_ release];
996 if (defaultIcon_ != nil)
997 [defaultIcon_ release];
1004 + (NSArray *) _attributeKeys {
1005 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1008 - (NSArray *) attributeKeys {
1009 return [[self class] _attributeKeys];
1012 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1013 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1016 - (Source *) initWithMetaIndex:(metaIndex *)index {
1017 if ((self = [super init]) != nil) {
1018 trusted_ = index->IsTrusted();
1020 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1021 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1022 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1024 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1025 if (dindex != NULL) {
1026 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1028 while (std::getline(release, line)) {
1029 std::string::size_type colon(line.find(':'));
1030 if (colon == std::string::npos)
1033 std::string name(line.substr(0, colon));
1034 std::string value(line.substr(colon + 1));
1035 while (!value.empty() && value[0] == ' ')
1036 value = value.substr(1);
1038 if (name == "Default-Icon")
1039 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1040 else if (name == "Description")
1041 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1042 else if (name == "Label")
1043 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1044 else if (name == "Origin")
1045 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1046 else if (name == "Version")
1047 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1051 record_ = [Sources_ objectForKey:[self key]];
1053 record_ = [record_ retain];
1057 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1058 NSDictionary *lhr = [self record];
1059 NSDictionary *rhr = [source record];
1062 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1064 NSString *lhs = [self name];
1065 NSString *rhs = [source name];
1067 if ([lhs length] != 0 && [rhs length] != 0) {
1068 unichar lhc = [lhs characterAtIndex:0];
1069 unichar rhc = [rhs characterAtIndex:0];
1071 if (isalpha(lhc) && !isalpha(rhc))
1072 return NSOrderedAscending;
1073 else if (!isalpha(lhc) && isalpha(rhc))
1074 return NSOrderedDescending;
1077 return [lhs compare:rhs options:CompareOptions_];
1080 - (NSDictionary *) record {
1088 - (NSString *) uri {
1092 - (NSString *) distribution {
1093 return distribution_;
1096 - (NSString *) type {
1100 - (NSString *) key {
1101 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1104 - (NSString *) host {
1105 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1108 - (NSString *) name {
1109 return origin_ == nil ? [self host] : origin_;
1112 - (NSString *) description {
1113 return description_;
1116 - (NSString *) label {
1117 return label_ == nil ? [self host] : label_;
1120 - (NSString *) origin {
1124 - (NSString *) version {
1128 - (NSString *) defaultIcon {
1129 return defaultIcon_;
1134 /* Relationship Class {{{ */
1135 @interface Relationship : NSObject {
1140 - (NSString *) type;
1142 - (NSString *) name;
1146 @implementation Relationship
1154 - (NSString *) type {
1162 - (NSString *) name {
1169 /* Package Class {{{ */
1170 @interface Package : NSObject {
1171 pkgCache::PkgIterator iterator_;
1172 _transient Database *database_;
1173 pkgCache::VerIterator version_;
1174 pkgCache::VerFileIterator file_;
1182 NSString *installed_;
1188 NSString *depiction_;
1189 NSString *homepage_;
1195 NSArray *relationships_;
1198 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1199 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1201 - (pkgCache::PkgIterator) iterator;
1203 - (NSString *) section;
1204 - (NSString *) simpleSection;
1206 - (Address *) maintainer;
1208 - (NSString *) description;
1209 - (NSString *) index;
1211 - (NSMutableDictionary *) metadata;
1213 - (BOOL) subscribed;
1216 - (NSString *) latest;
1217 - (NSString *) installed;
1220 - (BOOL) upgradableAndEssential:(BOOL)essential;
1223 - (BOOL) unfiltered;
1227 - (BOOL) halfConfigured;
1228 - (BOOL) halfInstalled;
1230 - (NSString *) mode;
1233 - (NSString *) name;
1234 - (NSString *) tagline;
1236 - (NSString *) homepage;
1237 - (NSString *) depiction;
1238 - (Address *) author;
1240 - (NSArray *) files;
1241 - (NSArray *) relationships;
1242 - (NSArray *) warnings;
1243 - (NSArray *) applications;
1245 - (Source *) source;
1246 - (NSString *) role;
1247 - (NSString *) rating;
1249 - (BOOL) matches:(NSString *)text;
1251 - (bool) hasSupportingRole;
1252 - (BOOL) hasTag:(NSString *)tag;
1253 - (NSString *) primaryPurpose;
1254 - (NSArray *) purposes;
1256 - (NSComparisonResult) compareByName:(Package *)package;
1257 - (NSComparisonResult) compareBySection:(Package *)package;
1259 - (uint32_t) compareForChanges;
1264 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1265 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1266 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1267 - (NSNumber *) isVisibleInSource:(Source *)source;
1271 @implementation Package
1277 if (section_ != nil)
1281 if (installed_ != nil)
1282 [installed_ release];
1290 if (depiction_ != nil)
1291 [depiction_ release];
1292 if (homepage_ != nil)
1293 [homepage_ release];
1294 if (sponsor_ != nil)
1303 if (relationships_ != nil)
1304 [relationships_ release];
1309 + (NSArray *) _attributeKeys {
1310 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1313 - (NSArray *) attributeKeys {
1314 return [[self class] _attributeKeys];
1317 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1318 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1321 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1322 if ((self = [super init]) != nil) {
1323 iterator_ = iterator;
1324 database_ = database;
1326 version_ = [database_ policy]->GetCandidateVer(iterator_);
1327 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1328 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1330 pkgCache::VerIterator current = iterator_.CurrentVer();
1331 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1332 installed_ = [StripVersion(installed) retain];
1334 if (!version_.end())
1335 file_ = version_.FileList();
1337 pkgCache &cache([database_ cache]);
1338 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1341 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1344 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1346 const char *begin, *end;
1347 parser->GetRec(begin, end);
1349 NSString *website(nil);
1350 NSString *sponsor(nil);
1351 NSString *author(nil);
1360 {"depiction", &depiction_},
1361 {"homepage", &homepage_},
1362 {"website", &website},
1363 {"sponsor", &sponsor},
1364 {"author", &author},
1368 while (begin != end)
1369 if (*begin == '\n') {
1372 } else if (isblank(*begin)) next: {
1373 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1376 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1377 const char *name(begin);
1378 size_t size(colon - begin);
1380 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1383 const char *stop(begin == NULL ? end : begin);
1384 while (stop[-1] == '\r')
1386 while (++colon != stop && isblank(*colon));
1388 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1389 if (strncasecmp(names[i].name_, name, size) == 0) {
1390 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1391 *names[i].value_ = value;
1402 name_ = [name_ retain];
1403 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1405 icon_ = [icon_ retain];
1406 if (depiction_ != nil)
1407 depiction_ = [depiction_ retain];
1408 if (homepage_ == nil)
1409 homepage_ = website;
1410 if ([homepage_ isEqualToString:depiction_])
1412 if (homepage_ != nil)
1413 homepage_ = [homepage_ retain];
1415 sponsor_ = [[Address addressWithString:sponsor] retain];
1417 author_ = [[Address addressWithString:author] retain];
1419 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1423 for (int i(0), e([tags_ count]); i != e; ++i) {
1424 NSString *tag = [tags_ objectAtIndex:i];
1425 if ([tag hasPrefix:@"role::"]) {
1426 role_ = [[tag substringFromIndex:6] retain];
1431 NSString *solid(latest == nil ? installed : latest);
1432 bool changed(false);
1434 NSString *key([id_ lowercaseString]);
1436 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1437 if (metadata == nil) {
1438 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1443 [metadata setObject:solid forKey:@"LastVersion"];
1446 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1447 NSDate *last([metadata objectForKey:@"LastSeen"]);
1448 NSString *version([metadata objectForKey:@"LastVersion"]);
1451 first = last == nil ? now_ : last;
1452 [metadata setObject:first forKey:@"FirstSeen"];
1457 if (version == nil) {
1458 [metadata setObject:solid forKey:@"LastVersion"];
1460 } else if (![version isEqualToString:solid]) {
1461 [metadata setObject:solid forKey:@"LastVersion"];
1463 [metadata setObject:last forKey:@"LastSeen"];
1469 [Packages_ setObject:metadata forKey:key];
1475 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1476 return [[[Package alloc]
1477 initWithIterator:iterator
1482 - (pkgCache::PkgIterator) iterator {
1486 - (NSString *) section {
1487 if (section_ != nil)
1490 const char *section = iterator_.Section();
1491 if (section == NULL)
1494 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1497 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1498 if (NSString *rename = [value objectForKey:@"Rename"]) {
1503 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1507 - (NSString *) simpleSection {
1508 if (NSString *section = [self section])
1509 return Simplify(section);
1515 - (Address *) maintainer {
1518 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1519 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1523 return version_.end() ? 0 : version_->InstalledSize;
1526 - (NSString *) description {
1529 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1530 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1532 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1533 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1534 if ([lines count] < 2)
1537 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1538 for (size_t i(1); i != [lines count]; ++i) {
1539 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1540 [trimmed addObject:trim];
1543 return [trimmed componentsJoinedByString:@"\n"];
1546 - (NSString *) index {
1547 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1548 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1551 - (NSMutableDictionary *) metadata {
1552 return [Packages_ objectForKey:[id_ lowercaseString]];
1556 NSDictionary *metadata([self metadata]);
1557 if ([self subscribed])
1558 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1560 return [metadata objectForKey:@"FirstSeen"];
1563 - (BOOL) subscribed {
1564 NSDictionary *metadata([self metadata]);
1565 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1566 return [subscribed boolValue];
1572 NSDictionary *metadata([self metadata]);
1573 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1574 return [ignored boolValue];
1579 - (NSString *) latest {
1583 - (NSString *) installed {
1588 return !version_.end();
1591 - (BOOL) upgradableAndEssential:(BOOL)essential {
1592 pkgCache::VerIterator current = iterator_.CurrentVer();
1595 return essential && [self essential];
1597 return !version_.end() && version_ != current;
1600 - (BOOL) essential {
1601 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1605 return [database_ cache][iterator_].InstBroken();
1608 - (BOOL) unfiltered {
1609 NSString *section = [self section];
1610 return section == nil || isSectionVisible(section);
1614 return [self hasSupportingRole] && [self unfiltered];
1618 unsigned char current = iterator_->CurrentState;
1619 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1622 - (BOOL) halfConfigured {
1623 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1626 - (BOOL) halfInstalled {
1627 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1631 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1632 return state.Mode != pkgDepCache::ModeKeep;
1635 - (NSString *) mode {
1636 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1638 switch (state.Mode) {
1639 case pkgDepCache::ModeDelete:
1640 if ((state.iFlags & pkgDepCache::Purge) != 0)
1644 case pkgDepCache::ModeKeep:
1645 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1649 case pkgDepCache::ModeInstall:
1650 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1651 return @"Reinstall";
1652 else switch (state.Status) {
1654 return @"Downgrade";
1660 return @"New Install";
1673 - (NSString *) name {
1674 return name_ == nil ? id_ : name_;
1677 - (NSString *) tagline {
1681 - (UIImage *) icon {
1682 NSString *section = [self simpleSection];
1685 if (NSString *icon = icon_)
1686 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1687 if (icon == nil) if (section != nil)
1688 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1689 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1690 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1692 icon = [UIImage applicationImageNamed:@"unknown.png"];
1696 - (NSString *) homepage {
1700 - (NSString *) depiction {
1704 - (Address *) sponsor {
1708 - (Address *) author {
1712 - (NSArray *) files {
1713 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1714 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1717 fin.open([path UTF8String]);
1722 while (std::getline(fin, line))
1723 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1728 - (NSArray *) relationships {
1729 return relationships_;
1732 - (NSArray *) warnings {
1733 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1734 const char *name(iterator_.Name());
1736 size_t length(strlen(name));
1737 if (length < 2) invalid:
1738 [warnings addObject:@"illegal package identifier"];
1739 else for (size_t i(0); i != length; ++i)
1741 (name[i] < 'a' || name[i] > 'z') &&
1742 (name[i] < '0' || name[i] > '9') &&
1743 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1746 if (strcmp(name, "cydia") != 0) {
1750 if (NSArray *files = [self files])
1751 for (NSString *file in files)
1752 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1754 else if (!stash && [file isEqualToString:@"/var/stash"])
1758 [warnings addObject:@"files installed into Cydia.app"];
1760 [warnings addObject:@"files installed to /var/stash"];
1763 return [warnings count] == 0 ? nil : warnings;
1766 - (NSArray *) applications {
1767 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1769 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1771 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1772 if (NSArray *files = [self files])
1773 for (NSString *file in files)
1774 if (application_r(file)) {
1775 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1776 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1777 if ([id isEqualToString:me])
1780 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1782 display = application_r[1];
1784 NSString *bundle([file stringByDeletingLastPathComponent]);
1785 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1786 if (icon == nil || [icon length] == 0)
1788 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1790 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1791 [applications addObject:application];
1793 [application addObject:id];
1794 [application addObject:display];
1795 [application addObject:url];
1798 return [applications count] == 0 ? nil : applications;
1801 - (Source *) source {
1803 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1810 - (NSString *) role {
1814 - (NSString *) rating {
1815 if (NSString *pattern = [Indices_ objectForKey:@"Rating"])
1816 return [pattern stringByReplacingOccurrencesOfString:@"%@" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1821 - (BOOL) matches:(NSString *)text {
1827 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1828 if (range.location != NSNotFound)
1831 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1832 if (range.location != NSNotFound)
1835 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1836 if (range.location != NSNotFound)
1842 - (bool) hasSupportingRole {
1845 if ([role_ isEqualToString:@"enduser"])
1847 if ([Role_ isEqualToString:@"User"])
1849 if ([role_ isEqualToString:@"hacker"])
1851 if ([Role_ isEqualToString:@"Hacker"])
1853 if ([role_ isEqualToString:@"developer"])
1855 if ([Role_ isEqualToString:@"Developer"])
1860 - (BOOL) hasTag:(NSString *)tag {
1861 return tags_ == nil ? NO : [tags_ containsObject:tag];
1864 - (NSString *) primaryPurpose {
1865 for (NSString *tag in tags_)
1866 if ([tag hasPrefix:@"purpose::"])
1867 return [tag substringFromIndex:9];
1871 - (NSArray *) purposes {
1872 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1873 for (NSString *tag in tags_)
1874 if ([tag hasPrefix:@"purpose::"])
1875 [purposes addObject:[tag substringFromIndex:9]];
1876 return [purposes count] == 0 ? nil : purposes;
1879 - (NSComparisonResult) compareByName:(Package *)package {
1880 NSString *lhs = [self name];
1881 NSString *rhs = [package name];
1883 if ([lhs length] != 0 && [rhs length] != 0) {
1884 unichar lhc = [lhs characterAtIndex:0];
1885 unichar rhc = [rhs characterAtIndex:0];
1887 if (isalpha(lhc) && !isalpha(rhc))
1888 return NSOrderedAscending;
1889 else if (!isalpha(lhc) && isalpha(rhc))
1890 return NSOrderedDescending;
1893 return [lhs compare:rhs options:CompareOptions_];
1896 - (NSComparisonResult) compareBySection:(Package *)package {
1897 NSString *lhs = [self section];
1898 NSString *rhs = [package section];
1900 if (lhs == NULL && rhs != NULL)
1901 return NSOrderedAscending;
1902 else if (lhs != NULL && rhs == NULL)
1903 return NSOrderedDescending;
1904 else if (lhs != NULL && rhs != NULL) {
1905 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1906 if (result != NSOrderedSame)
1910 return NSOrderedSame;
1913 - (uint32_t) compareForChanges {
1918 uint32_t timestamp : 30;
1919 uint32_t ignored : 1;
1920 uint32_t upgradable : 1;
1924 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1926 if ([self upgradableAndEssential:YES]) {
1927 value.bits.timestamp = 0;
1928 value.bits.ignored = [self ignored] ? 0 : 1;
1929 value.bits.upgradable = 1;
1931 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1932 value.bits.ignored = 0;
1933 value.bits.upgradable = 0;
1936 return _not(uint32_t) - value.key;
1940 pkgProblemResolver *resolver = [database_ resolver];
1941 resolver->Clear(iterator_);
1942 resolver->Protect(iterator_);
1943 pkgCacheFile &cache([database_ cache]);
1944 cache->MarkInstall(iterator_, false);
1945 pkgDepCache::StateCache &state((*cache)[iterator_]);
1946 if (!state.Install())
1947 cache->SetReInstall(iterator_, true);
1951 pkgProblemResolver *resolver = [database_ resolver];
1952 resolver->Clear(iterator_);
1953 resolver->Protect(iterator_);
1954 resolver->Remove(iterator_);
1955 [database_ cache]->MarkDelete(iterator_, true);
1958 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1959 return [NSNumber numberWithBool:(
1960 [self unfiltered] && [self matches:search]
1964 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1965 return [NSNumber numberWithBool:(
1966 (![number boolValue] || [self visible]) && [self installed] != nil
1970 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1971 NSString *section = [self section];
1973 return [NSNumber numberWithBool:(
1975 [self installed] == nil && (
1977 section == nil && [name length] == 0 ||
1978 [name isEqualToString:section]
1983 - (NSNumber *) isVisibleInSource:(Source *)source {
1984 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1989 /* Section Class {{{ */
1990 @interface Section : NSObject {
1996 - (NSComparisonResult) compareByName:(Section *)section;
1997 - (Section *) initWithName:(NSString *)name;
1998 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1999 - (NSString *) name;
2002 - (void) addToCount;
2006 @implementation Section
2013 - (NSComparisonResult) compareByName:(Section *)section {
2014 NSString *lhs = [self name];
2015 NSString *rhs = [section name];
2017 if ([lhs length] != 0 && [rhs length] != 0) {
2018 unichar lhc = [lhs characterAtIndex:0];
2019 unichar rhc = [rhs characterAtIndex:0];
2021 if (isalpha(lhc) && !isalpha(rhc))
2022 return NSOrderedAscending;
2023 else if (!isalpha(lhc) && isalpha(rhc))
2024 return NSOrderedDescending;
2027 return [lhs compare:rhs options:CompareOptions_];
2030 - (Section *) initWithName:(NSString *)name {
2031 return [self initWithName:name row:0];
2034 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2035 if ((self = [super init]) != nil) {
2036 name_ = [name retain];
2041 - (NSString *) name {
2053 - (void) addToCount {
2061 static NSArray *Finishes_;
2063 /* Database Implementation {{{ */
2064 @implementation Database
2066 + (Database *) sharedInstance {
2067 static Database *instance;
2068 if (instance == nil)
2069 instance = [[Database alloc] init];
2078 - (void) _readCydia:(NSNumber *)fd { _pooled
2079 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2080 std::istream is(&ib);
2083 static Pcre finish_r("^finish:([^:]*)$");
2085 while (std::getline(is, line)) {
2086 const char *data(line.c_str());
2087 size_t size = line.size();
2088 lprintf("C:%s\n", data);
2090 if (finish_r(data, size)) {
2091 NSString *finish = finish_r[1];
2092 int index = [Finishes_ indexOfObject:finish];
2093 if (index != INT_MAX && index > Finish_)
2101 - (void) _readStatus:(NSNumber *)fd { _pooled
2102 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2103 std::istream is(&ib);
2106 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2107 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2109 while (std::getline(is, line)) {
2110 const char *data(line.c_str());
2111 size_t size = line.size();
2112 lprintf("S:%s\n", data);
2114 if (conffile_r(data, size)) {
2115 [delegate_ setConfigurationData:conffile_r[1]];
2116 } else if (strncmp(data, "status: ", 8) == 0) {
2117 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2118 [delegate_ setProgressTitle:string];
2119 } else if (pmstatus_r(data, size)) {
2120 std::string type([pmstatus_r[1] UTF8String]);
2121 NSString *id = pmstatus_r[2];
2123 float percent([pmstatus_r[3] floatValue]);
2124 [delegate_ setProgressPercent:(percent / 100)];
2126 NSString *string = pmstatus_r[4];
2128 if (type == "pmerror")
2129 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2130 withObject:[NSArray arrayWithObjects:string, id, nil]
2133 else if (type == "pmstatus") {
2134 [delegate_ setProgressTitle:string];
2135 } else if (type == "pmconffile")
2136 [delegate_ setConfigurationData:string];
2137 else _assert(false);
2138 } else _assert(false);
2144 - (void) _readOutput:(NSNumber *)fd { _pooled
2145 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2146 std::istream is(&ib);
2149 while (std::getline(is, line)) {
2150 lprintf("O:%s\n", line.c_str());
2151 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2161 - (Package *) packageWithName:(NSString *)name {
2162 if (static_cast<pkgDepCache *>(cache_) == NULL)
2164 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2165 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2168 - (Database *) init {
2169 if ((self = [super init]) != nil) {
2176 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2177 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2181 _assert(pipe(fds) != -1);
2184 _config->Set("APT::Keep-Fds::", cydiafd_);
2185 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2188 detachNewThreadSelector:@selector(_readCydia:)
2190 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2193 _assert(pipe(fds) != -1);
2197 detachNewThreadSelector:@selector(_readStatus:)
2199 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2202 _assert(pipe(fds) != -1);
2203 _assert(dup2(fds[0], 0) != -1);
2204 _assert(close(fds[0]) != -1);
2206 input_ = fdopen(fds[1], "a");
2208 _assert(pipe(fds) != -1);
2209 _assert(dup2(fds[1], 1) != -1);
2210 _assert(close(fds[1]) != -1);
2213 detachNewThreadSelector:@selector(_readOutput:)
2215 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2220 - (pkgCacheFile &) cache {
2224 - (pkgDepCache::Policy *) policy {
2228 - (pkgRecords *) records {
2232 - (pkgProblemResolver *) resolver {
2236 - (pkgAcquire &) fetcher {
2240 - (NSArray *) packages {
2244 - (NSArray *) sources {
2245 return [sources_ allValues];
2248 - (NSArray *) issues {
2249 if (cache_->BrokenCount() == 0)
2252 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2254 for (Package *package in packages_) {
2255 if (![package broken])
2257 pkgCache::PkgIterator pkg([package iterator]);
2259 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2260 [entry addObject:[package name]];
2261 [issues addObject:entry];
2263 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2267 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2268 pkgCache::DepIterator start;
2269 pkgCache::DepIterator end;
2270 dep.GlobOr(start, end); // ++dep
2272 if (!cache_->IsImportantDep(end))
2274 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2277 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2278 [entry addObject:failure];
2279 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2281 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2282 [failure addObject:[package name]];
2284 pkgCache::PkgIterator target(start.TargetPkg());
2285 if (target->ProvidesList != 0)
2286 [failure addObject:@"?"];
2288 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2290 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2291 else if (!cache_[target].CandidateVerIter(cache_).end())
2292 [failure addObject:@"-"];
2293 else if (target->ProvidesList == 0)
2294 [failure addObject:@"!"];
2296 [failure addObject:@"%"];
2300 if (start.TargetVer() != 0)
2301 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2312 - (void) reloadData {
2332 if (!cache_.Open(progress_, true)) {
2334 if (!_error->PopMessage(error))
2337 lprintf("cache_.Open():[%s]\n", error.c_str());
2339 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2340 [delegate_ repairWithSelector:@selector(configure)];
2341 else if (error == "The package lists or status file could not be parsed or opened.")
2342 [delegate_ repairWithSelector:@selector(update)];
2343 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2344 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2345 // else if (error == "The list of sources could not be read.")
2346 else _assert(false);
2352 now_ = [[NSDate date] retain];
2354 policy_ = new pkgDepCache::Policy();
2355 records_ = new pkgRecords(cache_);
2356 resolver_ = new pkgProblemResolver(cache_);
2357 fetcher_ = new pkgAcquire(&status_);
2360 list_ = new pkgSourceList();
2361 _assert(list_->ReadMainList());
2363 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2364 _assert(pkgApplyStatus(cache_));
2366 if (cache_->BrokenCount() != 0) {
2367 _assert(pkgFixBroken(cache_));
2368 _assert(cache_->BrokenCount() == 0);
2369 _assert(pkgMinimizeUpgrade(cache_));
2372 [sources_ removeAllObjects];
2373 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2374 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2375 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2377 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2378 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2382 [packages_ removeAllObjects];
2385 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2386 if (Package *package = [Package packageWithIterator:iterator database:self])
2387 [packages_ addObject:package];
2389 [packages_ sortUsingSelector:@selector(compareByName:)];
2393 - (void) configure {
2394 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2395 system([dpkg UTF8String]);
2403 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2404 _assert(!_error->PendingError());
2407 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2410 public pkgArchiveCleaner
2413 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2418 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2420 while (_error->PopMessage(error))
2421 lprintf("ArchiveCleaner: %s\n", error.c_str());
2426 pkgRecords records(cache_);
2428 lock_ = new FileFd();
2429 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2430 _assert(!_error->PendingError());
2433 // XXX: explain this with an error message
2434 _assert(list.ReadMainList());
2436 manager_ = (_system->CreatePM(cache_));
2437 _assert(manager_->GetArchives(fetcher_, &list, &records));
2438 _assert(!_error->PendingError());
2442 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2444 _assert(list.ReadMainList());
2445 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2446 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2449 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2454 bool failed = false;
2455 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2456 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2459 std::string uri = (*item)->DescURI();
2460 std::string error = (*item)->ErrorText;
2462 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2465 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2466 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2477 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2479 if (_error->PendingError()) {
2484 if (result == pkgPackageManager::Failed) {
2489 if (result != pkgPackageManager::Completed) {
2494 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2496 _assert(list.ReadMainList());
2497 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2498 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2501 if (![before isEqualToArray:after])
2506 _assert(pkgDistUpgrade(cache_));
2510 [self updateWithStatus:status_];
2513 - (void) updateWithStatus:(Status &)status {
2515 _assert(list.ReadMainList());
2518 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2519 _assert(!_error->PendingError());
2521 pkgAcquire fetcher(&status);
2522 _assert(list.GetIndexes(&fetcher));
2524 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2525 bool failed = false;
2526 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2527 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2528 (*item)->Finished();
2532 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2533 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2534 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2537 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2542 - (void) setDelegate:(id)delegate {
2543 delegate_ = delegate;
2544 status_.setDelegate(delegate);
2545 progress_.setDelegate(delegate);
2548 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2549 pkgIndexFile *index(NULL);
2550 list_->FindIndex(file, index);
2551 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2557 /* PopUp Windows {{{ */
2558 @interface PopUpView : UIView {
2559 _transient id delegate_;
2560 UITransitionView *transition_;
2565 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2569 @implementation PopUpView
2572 [transition_ setDelegate:nil];
2573 [transition_ release];
2579 [transition_ transition:UITransitionPushFromTop toView:nil];
2582 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2583 if (from != nil && to == nil)
2584 [self removeFromSuperview];
2587 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2588 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2589 delegate_ = delegate;
2591 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2592 [self addSubview:transition_];
2594 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2596 [view addSubview:self];
2598 [transition_ setDelegate:self];
2600 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2601 [transition_ transition:UITransitionNone toView:blank];
2602 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2609 /* Mail Composition {{{ */
2610 @interface MailToView : PopUpView {
2611 MailComposeController *controller_;
2614 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2618 @implementation MailToView
2621 [controller_ release];
2625 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2629 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2630 NSLog(@"did:%@", delivery);
2631 // [UIApp setStatusBarShowsProgress:NO];
2632 if ([controller error]){
2633 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2634 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2635 [mailAlertSheet setBodyText:[controller error]];
2636 [mailAlertSheet popupAlertAnimated:YES];
2640 - (void) showError {
2641 NSLog(@"%@", [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];
2648 - (void) deliverMessage { _pooled
2652 if (![controller_ deliverMessage])
2653 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2656 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2657 if ([controller_ needsDelivery])
2658 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2663 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2664 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2665 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2666 [controller_ setDelegate:self];
2667 [controller_ initializeUI];
2668 [controller_ setupForURL:url];
2670 UIView *view([controller_ view]);
2671 [overlay_ addSubview:view];
2677 /* Confirmation View {{{ */
2678 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2679 if (!iterator.end())
2680 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2681 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2683 pkgCache::PkgIterator package(dep.TargetPkg());
2686 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2693 @protocol ConfirmationViewDelegate
2698 @interface ConfirmationView : BrowserView {
2699 _transient Database *database_;
2700 UIActionSheet *essential_;
2707 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2711 @implementation ConfirmationView
2718 if (essential_ != nil)
2719 [essential_ release];
2725 [book_ popFromSuperviewAnimated:YES];
2728 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2729 NSString *context([sheet context]);
2731 if ([context isEqualToString:@"remove"]) {
2739 [delegate_ confirm];
2746 } else if ([context isEqualToString:@"unable"]) {
2750 [super alertSheet:sheet buttonClicked:button];
2753 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2754 [window setValue:changes_ forKey:@"changes"];
2755 [window setValue:issues_ forKey:@"issues"];
2756 [window setValue:sizes_ forKey:@"sizes"];
2757 [super webView:sender didClearWindowObject:window forFrame:frame];
2760 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2761 if ((self = [super initWithBook:book]) != nil) {
2762 database_ = database;
2764 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2765 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2766 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2767 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2768 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2772 pkgDepCache::Policy *policy([database_ policy]);
2774 pkgCacheFile &cache([database_ cache]);
2775 NSArray *packages = [database_ packages];
2776 for (size_t i(0), e = [packages count]; i != e; ++i) {
2777 Package *package = [packages objectAtIndex:i];
2778 pkgCache::PkgIterator iterator = [package iterator];
2779 pkgDepCache::StateCache &state(cache[iterator]);
2781 NSString *name([package name]);
2783 if (state.NewInstall())
2784 [installing addObject:name];
2785 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2786 [reinstalling addObject:name];
2787 else if (state.Upgrade())
2788 [upgrading addObject:name];
2789 else if (state.Downgrade())
2790 [downgrading addObject:name];
2791 else if (state.Delete()) {
2792 if ([package essential])
2794 [removing addObject:name];
2797 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2798 substrate_ |= DepSubstrate(iterator.CurrentVer());
2803 else if (Advanced_ || true) {
2804 essential_ = [[UIActionSheet alloc]
2805 initWithTitle:@"Removing Essentials"
2806 buttons:[NSArray arrayWithObjects:
2807 @"Cancel Operation (Safe)",
2808 @"Force Removal (Unsafe)",
2810 defaultButtonIndex:0
2816 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2818 [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."];
2820 essential_ = [[UIActionSheet alloc]
2821 initWithTitle:@"Unable to Comply"
2822 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2823 defaultButtonIndex:0
2828 [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."];
2831 changes_ = [[NSArray alloc] initWithObjects:
2839 issues_ = [database_ issues];
2841 issues_ = [issues_ retain];
2843 sizes_ = [[NSArray alloc] initWithObjects:
2844 SizeString([database_ fetcher].FetchNeeded()),
2845 SizeString([database_ fetcher].PartialPresent()),
2846 SizeString([database_ cache]->UsrSize()),
2849 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2853 - (NSString *) backButtonTitle {
2857 - (NSString *) leftButtonTitle {
2861 - (NSString *) _rightButtonTitle {
2862 return issues_ == nil ? @"Confirm" : nil;
2865 - (void) _leftButtonClicked {
2870 - (void) _rightButtonClicked {
2871 if (essential_ != nil)
2872 [essential_ popupAlertAnimated:YES];
2876 [delegate_ confirm];
2884 /* Progress Data {{{ */
2885 @interface ProgressData : NSObject {
2891 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2898 @implementation ProgressData
2900 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2901 if ((self = [super init]) != nil) {
2902 selector_ = selector;
2922 /* Progress View {{{ */
2923 @interface ProgressView : UIView <
2924 ConfigurationDelegate,
2927 _transient Database *database_;
2929 UIView *background_;
2930 UITransitionView *transition_;
2932 UINavigationBar *navbar_;
2933 UIProgressBar *progress_;
2934 UITextView *output_;
2935 UITextLabel *status_;
2936 UIPushButton *close_;
2939 SHA1SumValue springlist_;
2940 SHA1SumValue sandplate_;
2942 NSTimeInterval last_;
2945 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2947 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2948 - (void) setContentView:(UIView *)view;
2951 - (void) _retachThread;
2952 - (void) _detachNewThreadData:(ProgressData *)data;
2953 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2959 @protocol ProgressViewDelegate
2960 - (void) progressViewIsComplete:(ProgressView *)sender;
2963 @implementation ProgressView
2966 [transition_ setDelegate:nil];
2967 [navbar_ setDelegate:nil];
2970 if (background_ != nil)
2971 [background_ release];
2972 [transition_ release];
2975 [progress_ release];
2982 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2983 if (bootstrap_ && from == overlay_ && to == view_)
2987 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2988 if ((self = [super initWithFrame:frame]) != nil) {
2989 database_ = database;
2990 delegate_ = delegate;
2992 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2993 [transition_ setDelegate:self];
2995 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2998 [overlay_ setBackgroundColor:[UIColor blackColor]];
3000 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3001 [background_ setBackgroundColor:[UIColor blackColor]];
3002 [self addSubview:background_];
3005 [self addSubview:transition_];
3007 CGSize navsize = [UINavigationBar defaultSize];
3008 CGRect navrect = {{0, 0}, navsize};
3010 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3011 [overlay_ addSubview:navbar_];
3013 [navbar_ setBarStyle:1];
3014 [navbar_ setDelegate:self];
3016 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3017 [navbar_ pushNavigationItem:navitem];
3019 CGRect bounds = [overlay_ bounds];
3020 CGSize prgsize = [UIProgressBar defaultSize];
3023 (bounds.size.width - prgsize.width) / 2,
3024 bounds.size.height - prgsize.height - 20
3027 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3028 [progress_ setStyle:0];
3030 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3032 bounds.size.height - prgsize.height - 50,
3033 bounds.size.width - 20,
3037 [status_ setColor:[UIColor whiteColor]];
3038 [status_ setBackgroundColor:[UIColor clearColor]];
3040 [status_ setCentersHorizontally:YES];
3041 //[status_ setFont:font];
3043 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3045 navrect.size.height + 20,
3046 bounds.size.width - 20,
3047 bounds.size.height - navsize.height - 62 - navrect.size.height
3050 //[output_ setTextFont:@"Courier New"];
3051 [output_ setTextSize:12];
3053 [output_ setTextColor:[UIColor whiteColor]];
3054 [output_ setBackgroundColor:[UIColor clearColor]];
3056 [output_ setMarginTop:0];
3057 [output_ setAllowsRubberBanding:YES];
3058 [output_ setEditable:NO];
3060 [overlay_ addSubview:output_];
3062 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3064 bounds.size.height - prgsize.height - 50,
3065 bounds.size.width - 20,
3069 [close_ setAutosizesToFit:NO];
3070 [close_ setDrawsShadow:YES];
3071 [close_ setStretchBackground:YES];
3072 [close_ setEnabled:YES];
3074 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3075 [close_ setTitleFont:bold];
3077 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3078 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3079 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3083 - (void) setContentView:(UIView *)view {
3084 view_ = [view retain];
3087 - (void) resetView {
3088 [transition_ transition:6 toView:view_];
3091 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3092 NSString *context([sheet context]);
3094 if ([context isEqualToString:@"conffile"]) {
3095 FILE *input = [database_ input];
3099 fprintf(input, "N\n");
3103 fprintf(input, "Y\n");
3114 - (void) closeButtonPushed {
3123 [delegate_ suspendWithAnimation:YES];
3127 system("launchctl stop com.apple.SpringBoard");
3131 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3140 - (void) _retachThread {
3141 UINavigationItem *item = [navbar_ topItem];
3142 [item setTitle:@"Complete"];
3144 [overlay_ addSubview:close_];
3145 [progress_ removeFromSuperview];
3146 [status_ removeFromSuperview];
3148 [delegate_ progressViewIsComplete:self];
3151 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3152 MMap mmap(file, MMap::ReadOnly);
3154 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3155 if (!(sandplate_ == sha1.Result()))
3160 FileFd file(SpringBoard_, FileFd::ReadOnly);
3161 MMap mmap(file, MMap::ReadOnly);
3163 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3164 if (!(springlist_ == sha1.Result()))
3169 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3170 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3171 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3172 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3173 case 4: [close_ setTitle:@"Reboot Device"]; break;
3176 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3178 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3179 [cache autorelease];
3181 NSFileManager *manager = [NSFileManager defaultManager];
3182 NSError *error = nil;
3184 id system = [cache objectForKey:@"System"];
3189 if (stat(Cache_, &info) == -1)
3192 [system removeAllObjects];
3194 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3195 for (NSString *app in apps)
3196 if ([app hasSuffix:@".app"]) {
3197 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3198 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3199 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3201 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3202 [info setObject:path forKey:@"Path"];
3203 [info setObject:@"System" forKey:@"ApplicationType"];
3204 [system addInfoDictionary:info];
3210 [cache writeToFile:@Cache_ atomically:YES];
3212 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3214 if (chmod(Cache_, info.st_mode) == -1)
3218 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3221 notify_post("com.apple.mobile.application_installed");
3223 [delegate_ setStatusBarShowsProgress:NO];
3226 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3227 [[data target] performSelector:[data selector] withObject:[data object]];
3230 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3233 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3234 UINavigationItem *item = [navbar_ topItem];
3235 [item setTitle:title];
3237 [status_ setText:nil];
3238 [output_ setText:@""];
3239 [progress_ setProgress:0];
3242 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3244 [close_ removeFromSuperview];
3245 [overlay_ addSubview:progress_];
3246 [overlay_ addSubview:status_];
3248 [delegate_ setStatusBarShowsProgress:YES];
3252 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3253 MMap mmap(file, MMap::ReadOnly);
3255 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3256 sandplate_ = sha1.Result();
3260 FileFd file(SpringBoard_, FileFd::ReadOnly);
3261 MMap mmap(file, MMap::ReadOnly);
3263 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3264 springlist_ = sha1.Result();
3267 [transition_ transition:6 toView:overlay_];
3270 detachNewThreadSelector:@selector(_detachNewThreadData:)
3272 withObject:[[ProgressData alloc]
3273 initWithSelector:selector
3280 - (void) repairWithSelector:(SEL)selector {
3282 detachNewThreadSelector:selector
3289 - (void) setConfigurationData:(NSString *)data {
3291 performSelectorOnMainThread:@selector(_setConfigurationData:)
3297 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3298 Package *package = id == nil ? nil : [database_ packageWithName:id];
3300 UIActionSheet *sheet = [[[UIActionSheet alloc]
3301 initWithTitle:(package == nil ? id : [package name])
3302 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3303 defaultButtonIndex:0
3308 [sheet setBodyText:error];
3309 [sheet popupAlertAnimated:YES];
3312 - (void) setProgressTitle:(NSString *)title {
3314 performSelectorOnMainThread:@selector(_setProgressTitle:)
3320 - (void) setProgressPercent:(float)percent {
3322 performSelectorOnMainThread:@selector(_setProgressPercent:)
3323 withObject:[NSNumber numberWithFloat:percent]
3328 - (void) startProgress {
3329 last_ = [NSDate timeIntervalSinceReferenceDate];
3332 - (void) addProgressOutput:(NSString *)output {
3334 performSelectorOnMainThread:@selector(_addProgressOutput:)
3340 - (bool) isCancelling:(size_t)received {
3342 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3343 if (received_ != received) {
3344 received_ = received;
3346 } else if (now - last_ > 30)
3353 - (void) _setConfigurationData:(NSString *)data {
3354 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3356 _assert(conffile_r(data));
3358 NSString *ofile = conffile_r[1];
3359 //NSString *nfile = conffile_r[2];
3361 UIActionSheet *sheet = [[[UIActionSheet alloc]
3362 initWithTitle:@"Configuration Upgrade"
3363 buttons:[NSArray arrayWithObjects:
3364 @"Keep My Old Copy",
3365 @"Accept The New Copy",
3366 // XXX: @"See What Changed",
3368 defaultButtonIndex:0
3373 [sheet setBodyText:[NSString stringWithFormat:
3374 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3377 [sheet popupAlertAnimated:YES];
3380 - (void) _setProgressTitle:(NSString *)title {
3381 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3382 for (size_t i(0), e([words count]); i != e; ++i) {
3383 NSString *word([words objectAtIndex:i]);
3384 if (Package *package = [database_ packageWithName:word])
3385 [words replaceObjectAtIndex:i withObject:[package name]];
3388 [status_ setText:[words componentsJoinedByString:@" "]];
3391 - (void) _setProgressPercent:(NSNumber *)percent {
3392 [progress_ setProgress:[percent floatValue]];
3395 - (void) _addProgressOutput:(NSString *)output {
3396 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3397 CGSize size = [output_ contentSize];
3398 CGRect rect = {{0, size.height}, {size.width, 0}};
3399 [output_ scrollRectToVisible:rect animated:YES];
3402 - (BOOL) isRunning {
3409 /* Package Cell {{{ */
3410 @interface PackageCell : UISimpleTableCell {
3413 NSString *description_;
3417 UITextLabel *status_;
3421 - (PackageCell *) init;
3422 - (void) setPackage:(Package *)package;
3424 + (int) heightForPackage:(Package *)package;
3428 @implementation PackageCell
3430 - (void) clearPackage {
3441 if (description_ != nil) {
3442 [description_ release];
3446 if (source_ != nil) {
3451 if (badge_ != nil) {
3458 [self clearPackage];
3465 - (PackageCell *) init {
3466 if ((self = [super init]) != nil) {
3468 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3469 [status_ setBackgroundColor:[UIColor clearColor]];
3470 [status_ setFont:small];
3475 - (void) setPackage:(Package *)package {
3476 [self clearPackage];
3478 Source *source = [package source];
3479 NSString *section = [package simpleSection];
3481 icon_ = [[package icon] retain];
3483 name_ = [[package name] retain];
3484 description_ = [[package tagline] retain];
3486 NSString *label = nil;
3487 bool trusted = false;
3489 if (source != nil) {
3490 label = [source label];
3491 trusted = [source trusted];
3492 } else if ([[package id] isEqualToString:@"firmware"])
3495 label = @"Unknown/Local";
3497 NSString *from = [NSString stringWithFormat:@"from %@", label];
3499 if (section != nil && ![section isEqualToString:label])
3500 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3502 source_ = [from retain];
3504 if (NSString *purpose = [package primaryPurpose])
3505 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3506 badge_ = [badge_ retain];
3509 if (NSString *mode = [package mode]) {
3510 [badge_ setImage:[UIImage applicationImageNamed:
3511 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3514 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3515 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3516 } else if ([package half]) {
3517 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3518 [status_ setText:@"Package Damaged"];
3519 [status_ setColor:[UIColor redColor]];
3521 [badge_ setImage:nil];
3522 [status_ setText:nil];
3527 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3530 rect.size = [icon_ size];
3532 rect.size.width /= 2;
3533 rect.size.height /= 2;
3535 rect.origin.x = 25 - rect.size.width / 2;
3536 rect.origin.y = 25 - rect.size.height / 2;
3538 [icon_ drawInRect:rect];
3541 if (badge_ != nil) {
3542 CGSize size = [badge_ size];
3544 [badge_ drawAtPoint:CGPointMake(
3545 36 - size.width / 2,
3546 36 - size.height / 2
3555 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3556 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3560 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3562 [super drawContentInRect:rect selected:selected];
3565 + (int) heightForPackage:(Package *)package {
3566 NSString *tagline([package tagline]);
3567 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3569 if ([package hasMode] || [package half])
3578 /* Section Cell {{{ */
3579 @interface SectionCell : UISimpleTableCell {
3584 _UISwitchSlider *switch_;
3589 - (void) setSection:(Section *)section editing:(BOOL)editing;
3593 @implementation SectionCell
3595 - (void) clearSection {
3596 if (section_ != nil) {
3606 if (count_ != nil) {
3613 [self clearSection];
3620 if ((self = [super init]) != nil) {
3621 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3623 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3624 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3628 - (void) onSwitch:(id)sender {
3629 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3630 if (metadata == nil) {
3631 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3632 [Sections_ setObject:metadata forKey:section_];
3636 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3639 - (void) setSection:(Section *)section editing:(BOOL)editing {
3640 if (editing != editing_) {
3642 [switch_ removeFromSuperview];
3644 [self addSubview:switch_];
3648 [self clearSection];
3650 if (section == nil) {
3651 name_ = [@"All Packages" retain];
3654 section_ = [section name];
3655 if (section_ != nil)
3656 section_ = [section_ retain];
3657 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3658 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3661 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3665 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3666 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3673 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3675 CGSize size = [count_ sizeWithFont:Font14_];
3679 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3681 [super drawContentInRect:rect selected:selected];
3687 /* File Table {{{ */
3688 @interface FileTable : RVPage {
3689 _transient Database *database_;
3692 NSMutableArray *files_;
3696 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3697 - (void) setPackage:(Package *)package;
3701 @implementation FileTable
3704 if (package_ != nil)
3713 - (int) numberOfRowsInTable:(UITable *)table {
3714 return files_ == nil ? 0 : [files_ count];
3717 - (float) table:(UITable *)table heightForRow:(int)row {
3721 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3722 if (reusing == nil) {
3723 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3724 UIFont *font = [UIFont systemFontOfSize:16];
3725 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3727 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3731 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3735 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3736 if ((self = [super initWithBook:book]) != nil) {
3737 database_ = database;
3739 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3741 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3742 [self addSubview:list_];
3744 UITableColumn *column = [[[UITableColumn alloc]
3745 initWithTitle:@"Name"
3747 width:[self frame].size.width
3750 [list_ setDataSource:self];
3751 [list_ setSeparatorStyle:1];
3752 [list_ addTableColumn:column];
3753 [list_ setDelegate:self];
3754 [list_ setReusesTableCells:YES];
3758 - (void) setPackage:(Package *)package {
3759 if (package_ != nil) {
3760 [package_ autorelease];
3769 [files_ removeAllObjects];
3771 if (package != nil) {
3772 package_ = [package retain];
3773 name_ = [[package id] retain];
3775 if (NSArray *files = [package files])
3776 [files_ addObjectsFromArray:files];
3778 if ([files_ count] != 0) {
3779 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3780 [files_ removeObjectAtIndex:0];
3781 [files_ sortUsingSelector:@selector(compareByPath:)];
3783 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3784 [stack addObject:@"/"];
3786 for (int i(0), e([files_ count]); i != e; ++i) {
3787 NSString *file = [files_ objectAtIndex:i];
3788 while (![file hasPrefix:[stack lastObject]])
3789 [stack removeLastObject];
3790 NSString *directory = [stack lastObject];
3791 [stack addObject:[file stringByAppendingString:@"/"]];
3792 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3793 ([stack count] - 2) * 3, "",
3794 [file substringFromIndex:[directory length]]
3803 - (void) resetViewAnimated:(BOOL)animated {
3804 [list_ resetViewAnimated:animated];
3807 - (void) reloadData {
3808 [self setPackage:[database_ packageWithName:name_]];
3809 [self reloadButtons];
3812 - (NSString *) title {
3813 return @"Installed Files";
3816 - (NSString *) backButtonTitle {
3822 /* Package View {{{ */
3823 @interface PackageView : BrowserView {
3824 _transient Database *database_;
3827 NSMutableArray *buttons_;
3830 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3831 - (void) setPackage:(Package *)package;
3835 @implementation PackageView
3838 if (package_ != nil)
3846 - (void) _clickButtonWithName:(NSString *)name {
3847 if ([name isEqualToString:@"Install"])
3848 [delegate_ installPackage:package_];
3849 else if ([name isEqualToString:@"Reinstall"])
3850 [delegate_ installPackage:package_];
3851 else if ([name isEqualToString:@"Remove"])
3852 [delegate_ removePackage:package_];
3853 else if ([name isEqualToString:@"Upgrade"])
3854 [delegate_ installPackage:package_];
3855 else _assert(false);
3858 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3859 NSString *context([sheet context]);
3861 if ([context isEqualToString:@"modify"]) {
3862 int count = [buttons_ count];
3863 _assert(count != 0);
3864 _assert(button <= count + 1);
3866 if (count != button - 1)
3867 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3871 [super alertSheet:sheet buttonClicked:button];
3874 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3875 return [super webView:sender didFinishLoadForFrame:frame];
3878 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3879 [window setValue:package_ forKey:@"package"];
3880 [super webView:sender didClearWindowObject:window forFrame:frame];
3884 - (void) _rightButtonClicked {
3885 /*[super _rightButtonClicked];
3888 int count = [buttons_ count];
3889 _assert(count != 0);
3892 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3894 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3895 [buttons addObjectsFromArray:buttons_];
3896 [buttons addObject:@"Cancel"];
3898 [delegate_ slideUp:[[[UIActionSheet alloc]
3901 defaultButtonIndex:2
3909 - (NSString *) _rightButtonTitle {
3910 int count = [buttons_ count];
3911 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3914 - (NSString *) backButtonTitle {
3918 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3919 if ((self = [super initWithBook:book]) != nil) {
3920 database_ = database;
3921 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3925 - (void) setPackage:(Package *)package {
3926 if (package_ != nil) {
3927 [package_ autorelease];
3936 [buttons_ removeAllObjects];
3938 if (package != nil) {
3939 package_ = [package retain];
3940 name_ = [[package id] retain];
3942 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3944 if ([package_ source] == nil);
3945 else if ([package_ upgradableAndEssential:NO])
3946 [buttons_ addObject:@"Upgrade"];
3947 else if ([package_ installed] == nil)
3948 [buttons_ addObject:@"Install"];
3950 [buttons_ addObject:@"Reinstall"];
3951 if ([package_ installed] != nil)
3952 [buttons_ addObject:@"Remove"];
3956 - (void) reloadData {
3957 [self setPackage:[database_ packageWithName:name_]];
3958 [self reloadButtons];
3963 /* Package Table {{{ */
3964 @interface PackageTable : RVPage {
3965 _transient Database *database_;
3969 NSMutableArray *packages_;
3970 NSMutableArray *sections_;
3971 UISectionList *list_;
3974 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3976 - (void) setDelegate:(id)delegate;
3977 - (void) setObject:(id)object;
3979 - (void) reloadData;
3980 - (void) resetCursor;
3982 - (UISectionList *) list;
3984 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3988 @implementation PackageTable
3991 [list_ setDataSource:nil];
3996 [packages_ release];
3997 [sections_ release];
4002 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4003 return [sections_ count];
4006 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4007 return [[sections_ objectAtIndex:section] name];
4010 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4011 return [[sections_ objectAtIndex:section] row];
4014 - (int) numberOfRowsInTable:(UITable *)table {
4015 return [packages_ count];
4018 - (float) table:(UITable *)table heightForRow:(int)row {
4019 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4022 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4024 reusing = [[[PackageCell alloc] init] autorelease];
4025 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4029 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4033 - (void) tableRowSelected:(NSNotification *)notification {
4034 int row = [[notification object] selectedRow];
4038 Package *package = [packages_ objectAtIndex:row];
4039 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4040 [view setDelegate:delegate_];
4041 [view setPackage:package];
4042 [book_ pushPage:view];
4045 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4046 if ((self = [super initWithBook:book]) != nil) {
4047 database_ = database;
4048 title_ = [title retain];
4050 object_ = object == nil ? nil : [object retain];
4052 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4053 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4055 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4056 [list_ setDataSource:self];
4058 UITableColumn *column = [[[UITableColumn alloc]
4059 initWithTitle:@"Name"
4061 width:[self frame].size.width
4064 UITable *table = [list_ table];
4065 [table setSeparatorStyle:1];
4066 [table addTableColumn:column];
4067 [table setDelegate:self];
4068 [table setReusesTableCells:YES];
4070 [self addSubview:list_];
4073 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4074 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4078 - (void) setDelegate:(id)delegate {
4079 delegate_ = delegate;
4082 - (void) setObject:(id)object {
4088 object_ = [object retain];
4091 - (void) reloadData {
4092 NSArray *packages = [database_ packages];
4094 [packages_ removeAllObjects];
4095 [sections_ removeAllObjects];
4097 for (size_t i(0); i != [packages count]; ++i) {
4098 Package *package([packages objectAtIndex:i]);
4099 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4100 [packages_ addObject:package];
4103 Section *section = nil;
4105 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4106 Package *package = [packages_ objectAtIndex:offset];
4107 NSString *name = [package index];
4109 if (section == nil || ![[section name] isEqualToString:name]) {
4110 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4111 [sections_ addObject:section];
4114 [section addToCount];
4120 - (NSString *) title {
4124 - (void) resetViewAnimated:(BOOL)animated {
4125 [list_ resetViewAnimated:animated];
4128 - (void) resetCursor {
4129 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4132 - (UISectionList *) list {
4136 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4137 [list_ setShouldHideHeaderInShortLists:hide];
4143 /* Add Source View {{{ */
4144 @interface AddSourceView : RVPage {
4145 _transient Database *database_;
4148 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4152 @implementation AddSourceView
4154 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4155 if ((self = [super initWithBook:book]) != nil) {
4156 database_ = database;
4162 /* Source Cell {{{ */
4163 @interface SourceCell : UITableCell {
4166 NSString *description_;
4172 - (SourceCell *) initWithSource:(Source *)source;
4176 @implementation SourceCell
4181 [description_ release];
4186 - (SourceCell *) initWithSource:(Source *)source {
4187 if ((self = [super init]) != nil) {
4189 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4191 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4192 icon_ = [icon_ retain];
4194 origin_ = [[source name] retain];
4195 label_ = [[source uri] retain];
4196 description_ = [[source description] retain];
4200 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4202 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4209 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4213 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4217 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4219 [super drawContentInRect:rect selected:selected];
4224 /* Source Table {{{ */
4225 @interface SourceTable : RVPage {
4226 _transient Database *database_;
4227 UISectionList *list_;
4228 NSMutableArray *sources_;
4229 UIActionSheet *alert_;
4233 UIProgressHUD *hud_;
4236 //NSURLConnection *installer_;
4237 NSURLConnection *trivial_bz2_;
4238 NSURLConnection *trivial_gz_;
4239 //NSURLConnection *automatic_;
4244 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4248 @implementation SourceTable
4250 - (void) _deallocConnection:(NSURLConnection *)connection {
4251 if (connection != nil) {
4252 [connection cancel];
4253 //[connection setDelegate:nil];
4254 [connection release];
4259 [[list_ table] setDelegate:nil];
4260 [list_ setDataSource:nil];
4269 //[self _deallocConnection:installer_];
4270 [self _deallocConnection:trivial_gz_];
4271 [self _deallocConnection:trivial_bz2_];
4272 //[self _deallocConnection:automatic_];
4279 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4280 return offset_ == 0 ? 1 : 2;
4283 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4284 switch (section + (offset_ == 0 ? 1 : 0)) {
4285 case 0: return @"Entered by User";
4286 case 1: return @"Installed by Packages";
4294 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4295 switch (section + (offset_ == 0 ? 1 : 0)) {
4297 case 1: return offset_;
4305 - (int) numberOfRowsInTable:(UITable *)table {
4306 return [sources_ count];
4309 - (float) table:(UITable *)table heightForRow:(int)row {
4310 Source *source = [sources_ objectAtIndex:row];
4311 return [source description] == nil ? 56 : 73;
4314 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4315 Source *source = [sources_ objectAtIndex:row];
4316 // XXX: weird warning, stupid selectors ;P
4317 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4320 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4324 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4328 - (void) tableRowSelected:(NSNotification*)notification {
4329 UITable *table([list_ table]);
4330 int row([table selectedRow]);
4334 Source *source = [sources_ objectAtIndex:row];
4336 PackageTable *packages = [[[PackageTable alloc]
4339 title:[source label]
4340 filter:@selector(isVisibleInSource:)
4344 [packages setDelegate:delegate_];
4346 [book_ pushPage:packages];
4349 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4350 Source *source = [sources_ objectAtIndex:row];
4351 return [source record] != nil;
4354 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4355 [[list_ table] setDeleteConfirmationRow:row];
4358 - (void) table:(UITable *)table deleteRow:(int)row {
4359 Source *source = [sources_ objectAtIndex:row];
4360 [Sources_ removeObjectForKey:[source key]];
4361 [delegate_ syncData];
4364 - (void) _endConnection:(NSURLConnection *)connection {
4365 NSURLConnection **field = NULL;
4366 if (connection == trivial_bz2_)
4367 field = &trivial_bz2_;
4368 else if (connection == trivial_gz_)
4369 field = &trivial_gz_;
4370 _assert(field != NULL);
4371 [connection release];
4375 trivial_bz2_ == nil &&
4378 [delegate_ setStatusBarShowsProgress:NO];
4381 [hud_ removeFromSuperview];
4386 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4389 @"./", @"Distribution",
4390 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4392 [delegate_ syncData];
4393 } else if (error_ != nil) {
4394 UIActionSheet *sheet = [[[UIActionSheet alloc]
4395 initWithTitle:@"Verification Error"
4396 buttons:[NSArray arrayWithObjects:@"OK", nil]
4397 defaultButtonIndex:0
4402 [sheet setBodyText:[error_ localizedDescription]];
4403 [sheet popupAlertAnimated:YES];
4405 UIActionSheet *sheet = [[[UIActionSheet alloc]
4406 initWithTitle:@"Did not Find Repository"
4407 buttons:[NSArray arrayWithObjects:@"OK", nil]
4408 defaultButtonIndex:0
4413 [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."];
4414 [sheet popupAlertAnimated:YES];
4420 if (error_ != nil) {
4427 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4428 switch ([response statusCode]) {
4434 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4435 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4437 error_ = [error retain];
4438 [self _endConnection:connection];
4441 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4442 [self _endConnection:connection];
4445 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4446 NSMutableURLRequest *request = [NSMutableURLRequest
4447 requestWithURL:[NSURL URLWithString:href]
4448 cachePolicy:NSURLRequestUseProtocolCachePolicy
4449 timeoutInterval:20.0
4452 [request setHTTPMethod:method];
4454 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4457 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4458 NSString *context([sheet context]);
4460 if ([context isEqualToString:@"source"]) {
4463 NSString *href = [[sheet textField] text];
4465 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4467 if (![href hasSuffix:@"/"])
4468 href_ = [href stringByAppendingString:@"/"];
4471 href_ = [href_ retain];
4473 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4474 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4475 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4479 hud_ = [delegate_ addProgressHUD];
4480 [hud_ setText:@"Verifying URL"];
4491 } else if ([context isEqualToString:@"trivial"])
4493 else if ([context isEqualToString:@"urlerror"])
4497 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4498 if ((self = [super initWithBook:book]) != nil) {
4499 database_ = database;
4500 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4502 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4503 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4504 [list_ setShouldHideHeaderInShortLists:NO];
4506 [self addSubview:list_];
4507 [list_ setDataSource:self];
4509 UITableColumn *column = [[UITableColumn alloc]
4510 initWithTitle:@"Name"
4512 width:[self frame].size.width
4515 UITable *table = [list_ table];
4516 [table setSeparatorStyle:1];
4517 [table addTableColumn:column];
4518 [table setDelegate:self];
4522 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4523 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4527 - (void) reloadData {
4529 _assert(list.ReadMainList());
4531 [sources_ removeAllObjects];
4532 [sources_ addObjectsFromArray:[database_ sources]];
4534 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4537 int count = [sources_ count];
4538 for (offset_ = 0; offset_ != count; ++offset_) {
4539 Source *source = [sources_ objectAtIndex:offset_];
4540 if ([source record] == nil)
4547 - (void) resetViewAnimated:(BOOL)animated {
4548 [list_ resetViewAnimated:animated];
4551 - (void) _leftButtonClicked {
4552 /*[book_ pushPage:[[[AddSourceView alloc]
4557 UIActionSheet *sheet = [[[UIActionSheet alloc]
4558 initWithTitle:@"Enter Cydia/APT URL"
4559 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4560 defaultButtonIndex:0
4565 [sheet setNumberOfRows:1];
4567 [sheet addTextFieldWithValue:@"http://" label:@""];
4569 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4570 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4571 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4572 [traits setKeyboardType:UIKeyboardTypeURL];
4573 // XXX: UIReturnKeyDone
4574 [traits setReturnKeyType:UIReturnKeyNext];
4576 [sheet popupAlertAnimated:YES];
4579 - (void) _rightButtonClicked {
4580 UITable *table = [list_ table];
4581 BOOL editing = [table isRowDeletionEnabled];
4582 [table enableRowDeletion:!editing animated:YES];
4583 [book_ reloadButtonsForPage:self];
4586 - (NSString *) title {
4590 - (NSString *) leftButtonTitle {
4591 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4594 - (NSString *) rightButtonTitle {
4595 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4598 - (UINavigationButtonStyle) rightButtonStyle {
4599 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4605 /* Installed View {{{ */
4606 @interface InstalledView : RVPage {
4607 _transient Database *database_;
4608 PackageTable *packages_;
4612 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4616 @implementation InstalledView
4619 [packages_ release];
4623 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4624 if ((self = [super initWithBook:book]) != nil) {
4625 database_ = database;
4627 packages_ = [[PackageTable alloc]
4631 filter:@selector(isInstalledAndVisible:)
4632 with:[NSNumber numberWithBool:YES]
4635 [self addSubview:packages_];
4637 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4638 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4642 - (void) resetViewAnimated:(BOOL)animated {
4643 [packages_ resetViewAnimated:animated];
4646 - (void) reloadData {
4647 [packages_ reloadData];
4650 - (void) _rightButtonClicked {
4651 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4652 [packages_ reloadData];
4654 [book_ reloadButtonsForPage:self];
4657 - (NSString *) title {
4658 return @"Installed";
4661 - (NSString *) backButtonTitle {
4665 - (NSString *) rightButtonTitle {
4666 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4669 - (UINavigationButtonStyle) rightButtonStyle {
4670 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4673 - (void) setDelegate:(id)delegate {
4674 [super setDelegate:delegate];
4675 [packages_ setDelegate:delegate];
4682 @interface HomeView : BrowserView {
4687 @implementation HomeView
4689 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4690 NSString *context([sheet context]);
4692 if ([context isEqualToString:@"about"])
4695 [super alertSheet:sheet buttonClicked:button];
4698 - (void) _leftButtonClicked {
4699 UIActionSheet *sheet = [[[UIActionSheet alloc]
4700 initWithTitle:@"About Cydia Installer"
4701 buttons:[NSArray arrayWithObjects:@"Close", nil]
4702 defaultButtonIndex:0
4708 @"Copyright (C) 2008\n"
4709 "Jay Freeman (saurik)\n"
4710 "saurik@saurik.com\n"
4711 "http://www.saurik.com/\n"
4714 "http://www.theokorigroup.com/\n"
4716 "College of Creative Studies,\n"
4717 "University of California,\n"
4719 "http://www.ccs.ucsb.edu/"
4722 [sheet popupAlertAnimated:YES];
4725 - (NSString *) leftButtonTitle {
4731 /* Manage View {{{ */
4732 @interface ManageView : BrowserView {
4737 @implementation ManageView
4739 - (NSString *) title {
4743 - (void) _leftButtonClicked {
4744 [delegate_ askForSettings];
4747 - (NSString *) leftButtonTitle {
4752 - (NSString *) _rightButtonTitle {
4760 @interface WebView (Cydia)
4761 - (void) setScriptDebugDelegate:(id)delegate;
4762 - (void) _setFormDelegate:(id)delegate;
4763 - (void) _setUIKitDelegate:(id)delegate;
4764 - (void) setWebMailDelegate:(id)delegate;
4765 - (void) _setLayoutInterval:(float)interval;
4768 /* Indirect Delegate {{{ */
4769 @interface IndirectDelegate : NSProxy {
4770 _transient volatile id delegate_;
4773 - (void) setDelegate:(id)delegate;
4774 - (id) initWithDelegate:(id)delegate;
4777 @implementation IndirectDelegate
4779 - (void) setDelegate:(id)delegate {
4780 delegate_ = delegate;
4783 - (id) initWithDelegate:(id)delegate {
4784 delegate_ = delegate;
4788 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4789 if (delegate_ != nil)
4790 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4792 // XXX: I fucking hate Apple so very very bad
4793 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4796 - (void) forwardInvocation:(NSInvocation *)inv {
4797 SEL sel = [inv selector];
4798 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4799 [inv invokeWithTarget:delegate_];
4804 /* Browser Implementation {{{ */
4805 @implementation BrowserView
4806 #include "internals.h"
4809 if (challenge_ != nil)
4810 [challenge_ release];
4812 WebView *webview = [webview_ webView];
4813 [webview setFrameLoadDelegate:nil];
4814 [webview setResourceLoadDelegate:nil];
4815 [webview setUIDelegate:nil];
4816 [webview setScriptDebugDelegate:nil];
4817 [webview setPolicyDelegate:nil];
4819 [webview setDownloadDelegate:nil];
4821 [webview _setFormDelegate:nil];
4822 [webview _setUIKitDelegate:nil];
4823 [webview setWebMailDelegate:nil];
4824 [webview setEditingDelegate:nil];
4826 [webview_ setDelegate:nil];
4827 [webview_ setGestureDelegate:nil];
4829 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4834 [webview_ removeFromSuperview];
4835 [Documents_ addObject:[webview_ autorelease]];
4840 [indirect_ setDelegate:nil];
4841 [indirect_ release];
4843 [scroller_ setDelegate:nil];
4845 [background_ release];
4846 [scroller_ release];
4848 [indicator_ release];
4854 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4855 [self loadRequest:[NSURLRequest
4858 timeoutInterval:30.0
4862 - (void) loadURL:(NSURL *)url {
4863 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4866 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4867 NSMutableURLRequest *copy = [request mutableCopy];
4869 if (Machine_ != NULL)
4870 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4871 if (UniqueID_ != nil)
4872 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4875 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4880 - (void) loadRequest:(NSURLRequest *)request {
4882 [webview_ loadRequest:request];
4885 - (void) reloadURL {
4886 if ([urls_ count] == 0)
4888 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4889 [urls_ removeLastObject];
4890 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4893 - (WebView *) webView {
4894 return [webview_ webView];
4897 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4898 [scroller_ setContentSize:frame.size];
4901 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4902 [self view:sender didSetFrame:frame];
4905 - (void) pushPage:(RVPage *)page {
4906 [self setBackButtonTitle:title_];
4907 [page setDelegate:delegate_];
4908 [book_ pushPage:page];
4911 - (BOOL) getSpecial:(NSURL *)url {
4912 NSString *href([url absoluteString]);
4913 NSString *scheme([[url scheme] lowercaseString]);
4917 if ([href hasPrefix:@"apptapp://package/"])
4918 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4919 else if ([scheme isEqualToString:@"cydia"]) {
4920 page = [delegate_ pageForURL:url hasTag:NULL];
4923 } else if (![scheme isEqualToString:@"apptapp"])
4927 [self pushPage:page];
4931 - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4932 UIActionSheet *sheet = [[[UIActionSheet alloc]
4933 initWithTitle:@"JavaScript Alert"
4934 buttons:[NSArray arrayWithObjects:@"OK", nil]
4935 defaultButtonIndex:0
4940 [sheet setBodyText:message];
4941 [sheet popupAlertAnimated:YES];
4944 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4945 [window setValue:delegate_ forKey:@"cydia"];
4948 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4949 if (NSURL *url = [request URL]) {
4950 NSLog(@"win:%@:%@", url, [action description]);
4951 if (![self getSpecial:url]) {
4952 NSString *scheme([[url scheme] lowercaseString]);
4953 if ([scheme isEqualToString:@"mailto"])
4954 [delegate_ openMailToURL:url];
4963 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4964 if ([WebView canShowMIMEType:type])
4967 // XXX: handle more mime types!
4969 if (frame == [webView mainFrame])
4970 [UIApp openURL:[request URL]];
4974 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4975 NSURL *url([request URL]);
4977 if (url == nil) use: {
4981 else NSLog(@"nav:%@:%@", url, [action description]);
4983 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4986 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4987 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4990 [UIApp openURL:url];
4996 int store(_not(int));
4997 if (NSURL *itms = [url itmsURL:&store]) {
4998 NSLog(@"itms#%@#%u#%@", url, store, itms);
5000 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
5001 store == 2 && [capability containsObject:@"com.apple.AppStore"]
5008 NSString *scheme([[url scheme] lowercaseString]);
5010 if ([scheme isEqualToString:@"tel"]) {
5011 // XXX: intelligence
5015 if ([scheme isEqualToString:@"mailto"]) {
5016 [delegate_ openMailToURL:url];
5020 if ([self getSpecial:url])
5022 else if ([WebView _canHandleRequest:request])
5024 else if ([url isSpringboardHandledURL])
5030 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
5031 //lprintf("Status:%s\n", [text UTF8String]);
5034 - (void) _pushPage {
5038 [book_ pushPage:self];
5041 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5042 NSString *context([sheet context]);
5044 if ([context isEqualToString:@"alert"])
5046 else if ([context isEqualToString:@"challenge"]) {
5047 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
5051 NSString *username([[sheet textFieldAtIndex:0] text]);
5052 NSString *password([[sheet textFieldAtIndex:1] text]);
5054 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
5056 [sender useCredential:credential forAuthenticationChallenge:challenge_];
5060 [sender cancelAuthenticationChallenge:challenge_];
5067 [challenge_ release];
5074 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
5075 challenge_ = [challenge retain];
5077 NSURLProtectionSpace *space([challenge protectionSpace]);
5078 NSString *realm([space realm]);
5082 UIActionSheet *sheet = [[[UIActionSheet alloc]
5084 buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
5085 defaultButtonIndex:0
5087 context:@"challenge"
5090 [sheet setNumberOfRows:1];
5092 [sheet addTextFieldWithValue:@"" label:@"username"];
5093 [sheet addTextFieldWithValue:@"" label:@"password"];
5095 UITextField *username([sheet textFieldAtIndex:0]); {
5096 UITextInputTraits *traits([username textInputTraits]);
5097 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5098 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5099 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5100 [traits setReturnKeyType:UIReturnKeyNext];
5103 UITextField *password([sheet textFieldAtIndex:1]); {
5104 UITextInputTraits *traits([password textInputTraits]);
5105 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5106 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5107 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5108 // XXX: UIReturnKeyDone
5109 [traits setReturnKeyType:UIReturnKeyNext];
5110 [traits setSecureTextEntry:YES];
5113 [sheet popupAlertAnimated:YES];
5116 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
5117 NSURL *url = [request URL];
5118 if ([self getSpecial:url])
5121 return [self _addHeadersToRequest:request];
5124 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
5125 [self setBackButtonTitle:title_];
5127 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
5128 [browser setDelegate:delegate_];
5131 [browser loadRequest:[self _addHeadersToRequest:request]];
5132 [book_ pushPage:browser];
5135 return [browser webView];
5138 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
5139 return [self _createWebViewWithRequest:request pushed:(request != nil)];
5142 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
5143 return [self _createWebViewWithRequest:request pushed:YES];
5146 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
5147 if ([frame parentFrame] != nil)
5150 title_ = [title retain];
5151 [book_ reloadTitleForPage:self];
5154 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
5155 if ([frame parentFrame] != nil)
5160 [indicator_ startAnimation];
5161 [self reloadButtons];
5163 if (title_ != nil) {
5168 [book_ reloadTitleForPage:self];
5170 WebView *webview = [webview_ webView];
5171 NSString *href = [webview mainFrameURL];
5172 [urls_ addObject:[NSURL URLWithString:href]];
5174 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
5176 CGRect webrect = [scroller_ bounds];
5177 webrect.size.height = 0;
5178 [webview_ setFrame:webrect];
5181 - (void) _finishLoading {
5184 [indicator_ stopAnimation];
5185 [self reloadButtons];
5189 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5190 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5193 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5194 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5197 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5198 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5201 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5202 return [webview_ webView:sender didCommitLoadForFrame:frame];
5205 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5206 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5209 - (void) _clearBackground {
5210 [background_ setBackgroundColor:[UIColor pinStripeColor]];
5211 [background_ setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5212 [scroller_ setShowBackgroundShadow:NO];
5215 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5216 if ([frame parentFrame] == nil) {
5217 [self _finishLoading];
5219 [self _clearBackground];
5221 if (DOMDocument *document = [frame DOMDocument])
5222 if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
5223 for (DOMHTMLBodyElement *body in bodies) {
5224 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
5226 bool colored(false);
5228 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
5229 DOMRGBColor *rgb([color getRGBColorValue]);
5231 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
5235 [background_ setBackgroundColor:[UIColor
5236 colorWithRed:([[rgb red] getFloatValue:DOM_CSS_NUMBER] / 255)
5237 green:([[rgb green] getFloatValue:DOM_CSS_NUMBER] / 255)
5238 blue:([[rgb blue] getFloatValue:DOM_CSS_NUMBER] / 255)
5244 if (DOMCSSPrimitiveValue *image = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-image"])) {
5245 NSString *src([image getStringValue]);
5246 if ([src isEqualToString:@"none"])
5248 NSLog(@"img:%@", [image getStringValue]);
5249 } else none: if (colored)
5250 [background_ setImage:nil];
5256 return [webview_ webView:sender didFinishLoadForFrame:frame];
5259 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5260 if ([frame parentFrame] != nil)
5262 [self _finishLoading];
5264 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5265 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5266 [[error localizedDescription] stringByAddingPercentEscapes]
5270 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5272 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5276 - (id) initWithBook:(RVBook *)book {
5277 if ((self = [super initWithBook:book]) != nil) {
5280 struct CGRect bounds = [self bounds];
5282 background_ = [[UIImageView alloc] initWithFrame:bounds];
5283 [self _clearBackground];
5284 [self addSubview:background_];
5286 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5287 [self addSubview:scroller_];
5289 [scroller_ setScrollingEnabled:YES];
5290 [scroller_ setAdjustForContentSizeChange:YES];
5291 [scroller_ setClipsSubviews:YES];
5292 [scroller_ setAllowsRubberBanding:YES];
5293 [scroller_ setScrollDecelerationFactor:0.99];
5294 [scroller_ setDelegate:self];
5296 CGRect webrect = [scroller_ bounds];
5297 webrect.size.height = 0;
5302 webview_ = [Documents_ lastObject];
5303 if (webview_ != nil) {
5304 webview_ = [webview_ retain];
5305 webview = [webview_ webView];
5306 [Documents_ removeLastObject];
5307 [webview_ setFrame:webrect];
5312 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5313 webview = [webview_ webView];
5314 [webview_ setDrawsBackground:NO];
5316 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5318 [webview_ setAllowsMessaging:YES];
5320 [webview_ setTilingEnabled:YES];
5321 [webview_ setDrawsGrid:NO];
5322 [webview_ setLogsTilingChanges:NO];
5323 [webview_ setTileMinificationFilter:kCAFilterNearest];
5324 [webview_ setDetectsPhoneNumbers:NO];
5325 [webview_ setAutoresizes:YES];
5327 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5328 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5329 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5331 [webview_ _setDocumentType:0x4];
5333 [webview_ setZoomsFocusedFormControl:YES];
5334 [webview_ setContentsPosition:7];
5335 [webview_ setEnabledGestures:0xa];
5336 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
5337 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
5339 [webview_ setSmoothsFonts:YES];
5341 [webview _setUsesLoaderCache:YES];
5342 [webview setGroupName:@"Cydia"];
5343 //[webview _setLayoutInterval:0.5];
5346 [webview_ setDelegate:self];
5347 [webview_ setGestureDelegate:self];
5348 [scroller_ addSubview:webview_];
5350 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5352 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5353 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5354 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5356 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5357 NSString *application = package == nil ? @"Cydia" : [NSString
5358 stringWithFormat:@"Cydia/%@",
5360 ]; [webview setApplicationNameForUserAgent:application];
5362 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5364 [webview setFrameLoadDelegate:self];
5365 [webview setResourceLoadDelegate:indirect_];
5366 [webview setUIDelegate:self];
5367 [webview setScriptDebugDelegate:self];
5368 [webview setPolicyDelegate:self];
5370 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5372 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5373 [background_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5374 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5378 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5379 [webview_ redrawScaledDocument];
5382 - (void) _rightButtonClicked {
5387 - (NSString *) _rightButtonTitle {
5391 - (NSString *) rightButtonTitle {
5392 return loading_ ? @"" : [self _rightButtonTitle];
5395 - (NSString *) title {
5396 return title_ == nil ? @"Loading" : title_;
5399 - (NSString *) backButtonTitle {
5403 - (void) setPageActive:(BOOL)active {
5405 [indicator_ removeFromSuperview];
5407 [[book_ navigationBar] addSubview:indicator_];
5410 - (void) resetViewAnimated:(BOOL)animated {
5413 - (void) setPushed:(bool)pushed {
5420 /* Cydia Book {{{ */
5421 @interface CYBook : RVBook <
5424 _transient Database *database_;
5425 UINavigationBar *overlay_;
5426 UINavigationBar *underlay_;
5427 UIProgressIndicator *indicator_;
5428 UITextLabel *prompt_;
5429 UIProgressBar *progress_;
5430 UINavigationButton *cancel_;
5433 NSTimeInterval last_;
5436 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5442 @implementation CYBook
5446 [indicator_ release];
5448 [progress_ release];
5453 - (NSString *) getTitleForPage:(RVPage *)page {
5454 return Simplify([super getTitleForPage:page]);
5462 [UIView beginAnimations:nil context:NULL];
5464 CGRect ovrframe = [overlay_ frame];
5465 ovrframe.origin.y = 0;
5466 [overlay_ setFrame:ovrframe];
5468 CGRect barframe = [navbar_ frame];
5469 barframe.origin.y += ovrframe.size.height;
5470 [navbar_ setFrame:barframe];
5472 CGRect trnframe = [transition_ frame];
5473 trnframe.origin.y += ovrframe.size.height;
5474 trnframe.size.height -= ovrframe.size.height;
5475 [transition_ setFrame:trnframe];
5477 [UIView endAnimations];
5479 [indicator_ startAnimation];
5480 [prompt_ setText:@"Updating Database"];
5481 [progress_ setProgress:0];
5484 last_ = [NSDate timeIntervalSinceReferenceDate];
5486 [overlay_ addSubview:cancel_];
5489 detachNewThreadSelector:@selector(_update)
5498 [indicator_ stopAnimation];
5500 [UIView beginAnimations:nil context:NULL];
5502 CGRect ovrframe = [overlay_ frame];
5503 ovrframe.origin.y = -ovrframe.size.height;
5504 [overlay_ setFrame:ovrframe];
5506 CGRect barframe = [navbar_ frame];
5507 barframe.origin.y -= ovrframe.size.height;
5508 [navbar_ setFrame:barframe];
5510 CGRect trnframe = [transition_ frame];
5511 trnframe.origin.y -= ovrframe.size.height;
5512 trnframe.size.height += ovrframe.size.height;
5513 [transition_ setFrame:trnframe];
5515 [UIView commitAnimations];
5517 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5520 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5521 if ((self = [super initWithFrame:frame]) != nil) {
5522 database_ = database;
5524 CGRect ovrrect = [navbar_ bounds];
5525 ovrrect.size.height = [UINavigationBar defaultSize].height;
5526 ovrrect.origin.y = -ovrrect.size.height;
5528 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5529 [self addSubview:overlay_];
5531 ovrrect.origin.y = frame.size.height;
5532 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5533 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5534 [self addSubview:underlay_];
5536 [overlay_ setBarStyle:1];
5537 [underlay_ setBarStyle:1];
5539 int barstyle = [overlay_ _barStyle:NO];
5540 bool ugly = barstyle == 0;
5542 UIProgressIndicatorStyle style = ugly ?
5543 UIProgressIndicatorStyleMediumBrown :
5544 UIProgressIndicatorStyleMediumWhite;
5546 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5547 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5548 CGRect indrect = {{indoffset, indoffset}, indsize};
5550 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5551 [indicator_ setStyle:style];
5552 [overlay_ addSubview:indicator_];
5554 CGSize prmsize = {215, indsize.height + 4};
5557 indoffset * 2 + indsize.width,
5561 unsigned(ovrrect.size.height - prmsize.height) / 2
5564 UIFont *font = [UIFont systemFontOfSize:15];
5566 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5568 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5569 [prompt_ setBackgroundColor:[UIColor clearColor]];
5570 [prompt_ setFont:font];
5572 [overlay_ addSubview:prompt_];
5574 CGSize prgsize = {75, 100};
5577 ovrrect.size.width - prgsize.width - 10,
5578 (ovrrect.size.height - prgsize.height) / 2
5581 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5582 [progress_ setStyle:0];
5583 [overlay_ addSubview:progress_];
5585 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5586 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5588 CGRect frame = [cancel_ frame];
5589 frame.size.width = 65;
5590 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5591 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5592 [cancel_ setFrame:frame];
5594 [cancel_ setBarStyle:barstyle];
5598 - (void) _onCancel {
5600 [cancel_ removeFromSuperview];
5603 - (void) _update { _pooled
5605 status.setDelegate(self);
5607 [database_ updateWithStatus:status];
5610 performSelectorOnMainThread:@selector(_update_)
5616 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5617 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5620 - (void) setProgressTitle:(NSString *)title {
5622 performSelectorOnMainThread:@selector(_setProgressTitle:)
5628 - (void) setProgressPercent:(float)percent {
5630 performSelectorOnMainThread:@selector(_setProgressPercent:)
5631 withObject:[NSNumber numberWithFloat:percent]
5636 - (void) startProgress {
5639 - (void) addProgressOutput:(NSString *)output {
5641 performSelectorOnMainThread:@selector(_addProgressOutput:)
5647 - (bool) isCancelling:(size_t)received {
5648 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5649 if (received_ != received) {
5650 received_ = received;
5652 } else if (now - last_ > 15)
5657 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5661 - (void) _setProgressTitle:(NSString *)title {
5662 [prompt_ setText:title];
5665 - (void) _setProgressPercent:(NSNumber *)percent {
5666 [progress_ setProgress:[percent floatValue]];
5669 - (void) _addProgressOutput:(NSString *)output {
5674 /* Cydia:// Protocol {{{ */
5675 @interface CydiaURLProtocol : NSURLProtocol {
5680 @implementation CydiaURLProtocol
5682 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5683 NSURL *url([request URL]);
5686 NSString *scheme([[url scheme] lowercaseString]);
5687 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5692 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5696 - (void) startLoading {
5697 id<NSURLProtocolClient> client([self client]);
5698 NSURLRequest *request([self request]);
5700 NSURL *url([request URL]);
5701 NSString *href([url absoluteString]);
5703 NSString *path([href substringFromIndex:8]);
5704 NSRange slash([path rangeOfString:@"/"]);
5707 if (slash.location == NSNotFound) {
5711 command = [path substringToIndex:slash.location];
5712 path = [path substringFromIndex:(slash.location + 1)];
5715 Database *database([Database sharedInstance]);
5717 if ([command isEqualToString:@"package-icon"]) {
5720 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5721 Package *package([database packageWithName:path]);
5725 UIImage *icon([package icon]);
5727 NSData *data(UIImagePNGRepresentation(icon));
5729 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5730 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5731 [client URLProtocol:self didLoadData:data];
5732 [client URLProtocolDidFinishLoading:self];
5733 } else if ([command isEqualToString:@"source-icon"]) {
5736 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5737 NSString *source(Simplify(path));
5739 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5741 icon = [UIImage applicationImageNamed:@"unknown.png"];
5743 NSData *data(UIImagePNGRepresentation(icon));
5745 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5746 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5747 [client URLProtocol:self didLoadData:data];
5748 [client URLProtocolDidFinishLoading:self];
5749 } else if ([command isEqualToString:@"section-icon"]) {
5752 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5753 NSString *section(Simplify(path));
5755 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5757 icon = [UIImage applicationImageNamed:@"unknown.png"];
5759 NSData *data(UIImagePNGRepresentation(icon));
5761 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5762 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5763 [client URLProtocol:self didLoadData:data];
5764 [client URLProtocolDidFinishLoading:self];
5766 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5770 - (void) stopLoading {
5776 /* Install View {{{ */
5777 @interface InstallView : RVPage {
5778 _transient Database *database_;
5779 NSMutableArray *sections_;
5780 NSMutableArray *filtered_;
5781 UITransitionView *transition_;
5787 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5788 - (void) reloadData;
5793 @implementation InstallView
5796 [list_ setDataSource:nil];
5797 [list_ setDelegate:nil];
5799 [sections_ release];
5800 [filtered_ release];
5801 [transition_ release];
5803 [accessory_ release];
5807 - (int) numberOfRowsInTable:(UITable *)table {
5808 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5811 - (float) table:(UITable *)table heightForRow:(int)row {
5815 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5817 reusing = [[[SectionCell alloc] init] autorelease];
5818 [(SectionCell *)reusing setSection:(editing_ ?
5819 [sections_ objectAtIndex:row] :
5820 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5821 ) editing:editing_];
5825 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5829 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5833 - (void) tableRowSelected:(NSNotification *)notification {
5834 int row = [[notification object] selectedRow];
5845 title = @"All Packages";
5847 section = [filtered_ objectAtIndex:(row - 1)];
5848 name = [section name];
5854 title = @"(No Section)";
5858 PackageTable *table = [[[PackageTable alloc]
5862 filter:@selector(isVisiblyUninstalledInSection:)
5866 [table setDelegate:delegate_];
5868 [book_ pushPage:table];
5871 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5872 if ((self = [super initWithBook:book]) != nil) {
5873 database_ = database;
5875 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5876 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5878 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5879 [self addSubview:transition_];
5881 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5882 [transition_ transition:0 toView:list_];
5884 UITableColumn *column = [[[UITableColumn alloc]
5885 initWithTitle:@"Name"
5887 width:[self frame].size.width
5890 [list_ setDataSource:self];
5891 [list_ setSeparatorStyle:1];
5892 [list_ addTableColumn:column];
5893 [list_ setDelegate:self];
5894 [list_ setReusesTableCells:YES];
5898 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5899 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5903 - (void) reloadData {
5904 NSArray *packages = [database_ packages];
5906 [sections_ removeAllObjects];
5907 [filtered_ removeAllObjects];
5909 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5910 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5913 for (size_t i(0); i != [packages count]; ++i) {
5914 Package *package([packages objectAtIndex:i]);
5915 NSString *name([package section]);
5918 Section *section([sections objectForKey:name]);
5919 if (section == nil) {
5920 section = [[[Section alloc] initWithName:name] autorelease];
5921 [sections setObject:section forKey:name];
5925 if ([package valid] && [package installed] == nil && [package visible])
5926 [filtered addObject:package];
5930 [sections_ addObjectsFromArray:[sections allValues]];
5931 [sections_ sortUsingSelector:@selector(compareByName:)];
5934 [filtered sortUsingSelector:@selector(compareBySection:)];
5937 Section *section = nil;
5938 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5939 Package *package = [filtered objectAtIndex:offset];
5940 NSString *name = [package section];
5942 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5943 section = name == nil ?
5944 [[[Section alloc] initWithName:nil] autorelease] :
5945 [sections objectForKey:name];
5946 [filtered_ addObject:section];
5949 [section addToCount];
5957 - (void) resetView {
5959 [self _rightButtonClicked];
5962 - (void) resetViewAnimated:(BOOL)animated {
5963 [list_ resetViewAnimated:animated];
5966 - (void) _rightButtonClicked {
5967 if ((editing_ = !editing_))
5970 [delegate_ updateData];
5971 [book_ reloadTitleForPage:self];
5972 [book_ reloadButtonsForPage:self];
5975 - (NSString *) title {
5976 return editing_ ? @"Section Visibility" : @"Install by Section";
5979 - (NSString *) backButtonTitle {
5983 - (NSString *) rightButtonTitle {
5984 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5987 - (UINavigationButtonStyle) rightButtonStyle {
5988 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5991 - (UIView *) accessoryView {
5997 /* Changes View {{{ */
5998 @interface ChangesView : RVPage {
5999 _transient Database *database_;
6000 NSMutableArray *packages_;
6001 NSMutableArray *sections_;
6002 UISectionList *list_;
6006 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6007 - (void) reloadData;
6011 @implementation ChangesView
6014 [[list_ table] setDelegate:nil];
6015 [list_ setDataSource:nil];
6017 [packages_ release];
6018 [sections_ release];
6023 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
6024 return [sections_ count];
6027 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
6028 return [[sections_ objectAtIndex:section] name];
6031 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
6032 return [[sections_ objectAtIndex:section] row];
6035 - (int) numberOfRowsInTable:(UITable *)table {
6036 return [packages_ count];
6039 - (float) table:(UITable *)table heightForRow:(int)row {
6040 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
6043 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
6045 reusing = [[[PackageCell alloc] init] autorelease];
6046 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
6050 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
6054 - (void) tableRowSelected:(NSNotification *)notification {
6055 int row = [[notification object] selectedRow];
6058 Package *package = [packages_ objectAtIndex:row];
6059 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6060 [view setDelegate:delegate_];
6061 [view setPackage:package];
6062 [book_ pushPage:view];
6065 - (void) _leftButtonClicked {
6066 [(CYBook *)book_ update];
6067 [self reloadButtons];
6070 - (void) _rightButtonClicked {
6071 [delegate_ distUpgrade];
6074 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6075 if ((self = [super initWithBook:book]) != nil) {
6076 database_ = database;
6078 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
6079 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6081 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
6082 [self addSubview:list_];
6084 [list_ setShouldHideHeaderInShortLists:NO];
6085 [list_ setDataSource:self];
6086 //[list_ setSectionListStyle:1];
6088 UITableColumn *column = [[[UITableColumn alloc]
6089 initWithTitle:@"Name"
6091 width:[self frame].size.width
6094 UITable *table = [list_ table];
6095 [table setSeparatorStyle:1];
6096 [table addTableColumn:column];
6097 [table setDelegate:self];
6098 [table setReusesTableCells:YES];
6102 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6103 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6107 - (void) reloadData {
6108 NSArray *packages = [database_ packages];
6110 [packages_ removeAllObjects];
6111 [sections_ removeAllObjects];
6114 for (size_t i(0); i != [packages count]; ++i) {
6115 Package *package([packages objectAtIndex:i]);
6118 [package installed] == nil && [package valid] && [package visible] ||
6119 [package upgradableAndEssential:NO]
6121 [packages_ addObject:package];
6125 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
6128 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
6129 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
6130 Section *section = nil;
6134 bool unseens = false;
6136 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
6139 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6140 Package *package = [packages_ objectAtIndex:offset];
6142 if (![package upgradableAndEssential:YES]) {
6144 NSDate *seen = [package seen];
6146 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6149 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
6150 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6151 [sections_ addObject:section];
6155 [section addToCount];
6156 } else if ([package ignored])
6157 [ignored addToCount];
6160 [upgradable addToCount];
6165 CFRelease(formatter);
6168 Section *last = [sections_ lastObject];
6169 size_t count = [last count];
6170 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6171 [sections_ removeLastObject];
6174 if ([ignored count] != 0)
6175 [sections_ insertObject:ignored atIndex:0];
6177 [sections_ insertObject:upgradable atIndex:0];
6180 [self reloadButtons];
6183 - (void) resetViewAnimated:(BOOL)animated {
6184 [list_ resetViewAnimated:animated];
6187 - (NSString *) leftButtonTitle {
6188 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6191 - (NSString *) rightButtonTitle {
6192 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
6195 - (NSString *) title {
6201 /* Search View {{{ */
6202 @protocol SearchViewDelegate
6203 - (void) showKeyboard:(BOOL)show;
6206 @interface SearchView : RVPage {
6208 UISearchField *field_;
6209 UITransitionView *transition_;
6210 PackageTable *table_;
6211 UIPreferencesTable *advanced_;
6217 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6218 - (void) reloadData;
6222 @implementation SearchView
6225 [field_ setDelegate:nil];
6227 [accessory_ release];
6229 [transition_ release];
6231 [advanced_ release];
6236 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6240 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6242 case 0: return @"Advanced Search (Coming Soon!)";
6244 default: _assert(false);
6248 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6252 default: _assert(false);
6256 - (void) _showKeyboard:(BOOL)show {
6257 CGSize keysize = [UIKeyboard defaultSize];
6258 CGRect keydown = [book_ pageBounds];
6259 CGRect keyup = keydown;
6260 keyup.size.height -= keysize.height - ButtonBarHeight_;
6262 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6264 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6265 [animation setSignificantRectFields:8];
6268 [animation setStartFrame:keydown];
6269 [animation setEndFrame:keyup];
6271 [animation setStartFrame:keyup];
6272 [animation setEndFrame:keydown];
6275 UIAnimator *animator = [UIAnimator sharedAnimator];
6278 addAnimations:[NSArray arrayWithObjects:animation, nil]
6279 withDuration:(KeyboardTime_ - delay)
6284 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6286 [delegate_ showKeyboard:show];
6289 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6290 [self _showKeyboard:YES];
6293 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6294 [self _showKeyboard:NO];
6297 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6299 NSString *text([field_ text]);
6300 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6306 - (void) textFieldClearButtonPressed:(UITextField *)field {
6310 - (void) keyboardInputShouldDelete:(id)input {
6314 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6315 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6319 [field_ resignFirstResponder];
6324 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6325 if ((self = [super initWithBook:book]) != nil) {
6326 CGRect pageBounds = [book_ pageBounds];
6328 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6329 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6330 [self addSubview:pinstripe];*/
6332 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6333 [self addSubview:transition_];
6335 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6337 [advanced_ setReusesTableCells:YES];
6338 [advanced_ setDataSource:self];
6339 [advanced_ reloadData];
6341 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6342 CGColor dimmed(space_, 0, 0, 0, 0.5);
6343 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6345 table_ = [[PackageTable alloc]
6349 filter:@selector(isUnfilteredAndSearchedForBy:)
6353 [table_ setShouldHideHeaderInShortLists:NO];
6354 [transition_ transition:0 toView:table_];
6363 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6370 [self bounds].size.width - area.origin.x - 18;
6372 area.size.height = [UISearchField defaultHeight];
6374 field_ = [[UISearchField alloc] initWithFrame:area];
6376 UIFont *font = [UIFont systemFontOfSize:16];
6377 [field_ setFont:font];
6379 [field_ setPlaceholder:@"Package Names & Descriptions"];
6380 [field_ setDelegate:self];
6382 [field_ setPaddingTop:5];
6384 UITextInputTraits *traits([field_ textInputTraits]);
6385 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6386 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6387 [traits setReturnKeyType:UIReturnKeySearch];
6389 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6391 accessory_ = [[UIView alloc] initWithFrame:accrect];
6392 [accessory_ addSubview:field_];
6394 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6395 [configure setShowPressFeedback:YES];
6396 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6397 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6398 [accessory_ addSubview:configure];*/
6400 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6401 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6407 LKAnimation *animation = [LKTransition animation];
6408 [animation setType:@"oglFlip"];
6409 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6410 [animation setFillMode:@"extended"];
6411 [animation setTransitionFlags:3];
6412 [animation setDuration:10];
6413 [animation setSpeed:0.35];
6414 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6415 [[transition_ _layer] addAnimation:animation forKey:0];
6416 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6417 flipped_ = !flipped_;
6421 - (void) configurePushed {
6422 [field_ resignFirstResponder];
6426 - (void) resetViewAnimated:(BOOL)animated {
6429 [table_ resetViewAnimated:animated];
6432 - (void) _reloadData {
6435 - (void) reloadData {
6438 [table_ setObject:[field_ text]];
6439 [table_ reloadData];
6440 [table_ resetCursor];
6443 - (UIView *) accessoryView {
6447 - (NSString *) title {
6451 - (NSString *) backButtonTitle {
6455 - (void) setDelegate:(id)delegate {
6456 [table_ setDelegate:delegate];
6457 [super setDelegate:delegate];
6463 @interface SettingsView : RVPage {
6464 _transient Database *database_;
6467 UIPreferencesTable *table_;
6468 _UISwitchSlider *subscribedSwitch_;
6469 _UISwitchSlider *ignoredSwitch_;
6470 UIPreferencesControlTableCell *subscribedCell_;
6471 UIPreferencesControlTableCell *ignoredCell_;
6474 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6478 @implementation SettingsView
6481 [table_ setDataSource:nil];
6484 if (package_ != nil)
6487 [subscribedSwitch_ release];
6488 [ignoredSwitch_ release];
6489 [subscribedCell_ release];
6490 [ignoredCell_ release];
6494 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6495 if (package_ == nil)
6501 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6502 if (package_ == nil)
6509 default: _assert(false);
6515 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6516 if (package_ == nil)
6523 default: _assert(false);
6529 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6530 if (package_ == nil)
6537 default: _assert(false);
6543 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6544 if (package_ == nil)
6547 _UISwitchSlider *slider([cell control]);
6548 BOOL value([slider value] != 0);
6549 NSMutableDictionary *metadata([package_ metadata]);
6552 if (NSNumber *number = [metadata objectForKey:key])
6553 before = [number boolValue];
6557 if (value != before) {
6558 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6560 [delegate_ updateData];
6564 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6565 [self onSomething:cell withKey:@"IsSubscribed"];
6568 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6569 [self onSomething:cell withKey:@"IsIgnored"];
6572 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6573 if (package_ == nil)
6577 case 0: switch (row) {
6579 return subscribedCell_;
6581 return ignoredCell_;
6582 default: _assert(false);
6585 case 1: switch (row) {
6587 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6588 [cell setShowSelection:NO];
6589 [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."];
6593 default: _assert(false);
6596 default: _assert(false);
6602 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6603 if ((self = [super initWithBook:book])) {
6604 database_ = database;
6605 name_ = [package retain];
6607 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6608 [self addSubview:table_];
6610 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6611 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6613 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6614 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6616 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6617 [subscribedCell_ setShowSelection:NO];
6618 [subscribedCell_ setTitle:@"Show All Changes"];
6619 [subscribedCell_ setControl:subscribedSwitch_];
6621 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6622 [ignoredCell_ setShowSelection:NO];
6623 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6624 [ignoredCell_ setControl:ignoredSwitch_];
6626 [table_ setDataSource:self];
6631 - (void) resetViewAnimated:(BOOL)animated {
6632 [table_ resetViewAnimated:animated];
6635 - (void) reloadData {
6636 if (package_ != nil)
6637 [package_ autorelease];
6638 package_ = [database_ packageWithName:name_];
6639 if (package_ != nil) {
6641 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6642 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6645 [table_ reloadData];
6648 - (NSString *) title {
6654 /* Signature View {{{ */
6655 @interface SignatureView : BrowserView {
6656 _transient Database *database_;
6660 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6664 @implementation SignatureView
6671 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6673 [super webView:sender didClearWindowObject:window forFrame:frame];
6676 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6677 if ((self = [super initWithBook:book]) != nil) {
6678 database_ = database;
6679 package_ = [package retain];
6684 - (void) resetViewAnimated:(BOOL)animated {
6687 - (void) reloadData {
6688 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6694 @interface Cydia : UIApplication <
6695 ConfirmationViewDelegate,
6696 ProgressViewDelegate,
6705 UIToolbar *buttonbar_;
6709 NSMutableArray *essential_;
6710 NSMutableArray *broken_;
6712 Database *database_;
6713 ProgressView *progress_;
6717 UIKeyboard *keyboard_;
6718 UIProgressHUD *hud_;
6720 InstallView *install_;
6721 ChangesView *changes_;
6722 ManageView *manage_;
6723 SearchView *search_;
6728 @implementation Cydia
6731 if ([broken_ count] != 0) {
6732 int count = [broken_ count];
6734 UIActionSheet *sheet = [[[UIActionSheet alloc]
6735 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6736 buttons:[NSArray arrayWithObjects:
6738 @"Ignore (Temporary)",
6740 defaultButtonIndex:0
6745 [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."];
6746 [sheet popupAlertAnimated:YES];
6747 } else if (!Ignored_ && [essential_ count] != 0) {
6748 int count = [essential_ count];
6750 UIActionSheet *sheet = [[[UIActionSheet alloc]
6751 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6752 buttons:[NSArray arrayWithObjects:
6753 @"Upgrade Essential",
6754 @"Complete Upgrade",
6755 @"Ignore (Temporary)",
6757 defaultButtonIndex:0
6762 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6763 [sheet popupAlertAnimated:YES];
6767 - (void) _reloadData {
6768 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6769 [hud setText:@"Reloading Data"];
6770 [overlay_ addSubview:hud];
6773 [database_ reloadData];
6777 [essential_ removeAllObjects];
6778 [broken_ removeAllObjects];
6780 NSArray *packages = [database_ packages];
6781 for (Package *package in packages) {
6783 [broken_ addObject:package];
6784 if ([package upgradableAndEssential:NO]) {
6785 if ([package essential])
6786 [essential_ addObject:package];
6792 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6793 [buttonbar_ setBadgeValue:badge forButton:3];
6794 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6795 [buttonbar_ setBadgeAnimated:YES forButton:3];
6796 [self setApplicationBadge:badge];
6798 [buttonbar_ setBadgeValue:nil forButton:3];
6799 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6800 [buttonbar_ setBadgeAnimated:NO forButton:3];
6801 [self removeApplicationBadge];
6807 if ([packages count] == 0);
6819 [hud removeFromSuperview];*/
6822 - (void) _saveConfig {
6825 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6831 - (void) updateData {
6834 /* XXX: this is just stupid */
6835 if (tag_ != 2 && install_ != nil)
6836 [install_ reloadData];
6837 if (tag_ != 3 && changes_ != nil)
6838 [changes_ reloadData];
6839 if (tag_ != 5 && search_ != nil)
6840 [search_ reloadData];
6850 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6851 _assert(file != NULL);
6853 NSArray *keys = [Sources_ allKeys];
6855 for (int i(0), e([keys count]); i != e; ++i) {
6856 NSString *key = [keys objectAtIndex:i];
6857 NSDictionary *source = [Sources_ objectForKey:key];
6859 fprintf(file, "%s %s %s\n",
6860 [[source objectForKey:@"Type"] UTF8String],
6861 [[source objectForKey:@"URI"] UTF8String],
6862 [[source objectForKey:@"Distribution"] UTF8String]
6871 detachNewThreadSelector:@selector(update_)
6874 title:@"Updating Sources"
6878 - (void) reloadData {
6879 @synchronized (self) {
6880 if (confirm_ == nil)
6886 pkgProblemResolver *resolver = [database_ resolver];
6888 resolver->InstallProtect();
6889 if (!resolver->Resolve(true))
6894 [database_ prepare];
6896 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6897 [confirm_ setDelegate:self];
6899 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6900 [page setDelegate:self];
6902 [confirm_ setPage:page];
6903 [underlay_ popSubview:confirm_];
6906 - (void) installPackage:(Package *)package {
6907 @synchronized (self) {
6914 - (void) removePackage:(Package *)package {
6915 @synchronized (self) {
6922 - (void) distUpgrade {
6923 @synchronized (self) {
6924 [database_ upgrade];
6930 @synchronized (self) {
6932 if (confirm_ != nil) {
6940 [overlay_ removeFromSuperview];
6944 detachNewThreadSelector:@selector(perform)
6951 - (void) bootstrap_ {
6953 [database_ upgrade];
6954 [database_ prepare];
6955 [database_ perform];
6958 - (void) bootstrap {
6960 detachNewThreadSelector:@selector(bootstrap_)
6963 title:@"Bootstrap Install"
6967 - (void) progressViewIsComplete:(ProgressView *)progress {
6968 if (confirm_ != nil) {
6969 [underlay_ addSubview:overlay_];
6970 [confirm_ popFromSuperviewAnimated:NO];
6976 - (void) setPage:(RVPage *)page {
6977 [page resetViewAnimated:NO];
6978 [page setDelegate:self];
6979 [book_ setPage:page];
6982 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6983 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6984 [browser loadURL:url];
6988 - (void) _setHomePage {
6989 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6992 - (void) buttonBarItemTapped:(id)sender {
6993 unsigned tag = [sender tag];
6995 [book_ resetViewAnimated:YES];
6997 } else if (tag_ == 2 && tag != 2)
6998 [install_ resetView];
7001 case 1: [self _setHomePage]; break;
7003 case 2: [self setPage:install_]; break;
7004 case 3: [self setPage:changes_]; break;
7005 case 4: [self setPage:manage_]; break;
7006 case 5: [self setPage:search_]; break;
7008 default: _assert(false);
7014 - (void) applicationWillSuspend {
7016 [super applicationWillSuspend];
7019 - (void) askForSettings {
7020 UIActionSheet *role = [[[UIActionSheet alloc]
7021 initWithTitle:@"Who Are You?"
7022 buttons:[NSArray arrayWithObjects:
7023 @"User (Graphical Only)",
7024 @"Hacker (+ Command Line)",
7025 @"Developer (No Filters)",
7027 defaultButtonIndex:-1
7032 [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."];
7033 [role popupAlertAnimated:YES];
7038 [self setStatusBarShowsProgress:NO];
7041 [hud_ removeFromSuperview];
7045 pid_t pid = ExecFork();
7047 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
7048 perror("launchctl stop");
7055 [self askForSettings];
7059 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
7061 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7062 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
7063 0, 0, screenrect.size.width, screenrect.size.height - 48
7064 ) database:database_];
7066 [book_ setDelegate:self];
7068 [overlay_ addSubview:book_];
7070 NSArray *buttonitems = [NSArray arrayWithObjects:
7071 [NSDictionary dictionaryWithObjectsAndKeys:
7072 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7073 @"home-up.png", kUIButtonBarButtonInfo,
7074 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
7075 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
7076 self, kUIButtonBarButtonTarget,
7077 @"Home", kUIButtonBarButtonTitle,
7078 @"0", kUIButtonBarButtonType,
7081 [NSDictionary dictionaryWithObjectsAndKeys:
7082 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7083 @"install-up.png", kUIButtonBarButtonInfo,
7084 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
7085 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
7086 self, kUIButtonBarButtonTarget,
7087 @"Sections", kUIButtonBarButtonTitle,
7088 @"0", kUIButtonBarButtonType,
7091 [NSDictionary dictionaryWithObjectsAndKeys:
7092 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7093 @"changes-up.png", kUIButtonBarButtonInfo,
7094 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
7095 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
7096 self, kUIButtonBarButtonTarget,
7097 @"Changes", kUIButtonBarButtonTitle,
7098 @"0", kUIButtonBarButtonType,
7101 [NSDictionary dictionaryWithObjectsAndKeys:
7102 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7103 @"manage-up.png", kUIButtonBarButtonInfo,
7104 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
7105 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
7106 self, kUIButtonBarButtonTarget,
7107 @"Manage", kUIButtonBarButtonTitle,
7108 @"0", kUIButtonBarButtonType,
7111 [NSDictionary dictionaryWithObjectsAndKeys:
7112 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7113 @"search-up.png", kUIButtonBarButtonInfo,
7114 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
7115 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
7116 self, kUIButtonBarButtonTarget,
7117 @"Search", kUIButtonBarButtonTitle,
7118 @"0", kUIButtonBarButtonType,
7122 buttonbar_ = [[UIToolbar alloc]
7124 withFrame:CGRectMake(
7125 0, screenrect.size.height - ButtonBarHeight_,
7126 screenrect.size.width, ButtonBarHeight_
7128 withItemList:buttonitems
7131 [buttonbar_ setDelegate:self];
7132 [buttonbar_ setBarStyle:1];
7133 [buttonbar_ setButtonBarTrackingMode:2];
7135 int buttons[5] = {1, 2, 3, 4, 5};
7136 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7137 [buttonbar_ showButtonGroup:0 withDuration:0];
7139 for (int i = 0; i != 5; ++i)
7140 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
7141 i * 64 + 2, 1, 60, ButtonBarHeight_
7144 [buttonbar_ showSelectionForButton:1];
7145 [overlay_ addSubview:buttonbar_];
7147 [UIKeyboard initImplementationNow];
7148 CGSize keysize = [UIKeyboard defaultSize];
7149 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
7150 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
7151 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
7152 [overlay_ addSubview:keyboard_];
7155 [underlay_ addSubview:overlay_];
7159 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
7160 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
7161 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7163 manage_ = (ManageView *) [[self
7164 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7165 withClass:[ManageView class]
7171 [self _setHomePage];
7174 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7175 NSString *context([sheet context]);
7177 if ([context isEqualToString:@"fixhalf"]) {
7180 @synchronized (self) {
7181 for (int i = 0, e = [broken_ count]; i != e; ++i) {
7182 Package *broken = [broken_ objectAtIndex:i];
7185 NSString *id = [broken id];
7186 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7187 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7188 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7189 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7198 [broken_ removeAllObjects];
7207 } else if ([context isEqualToString:@"role"]) {
7209 case 1: Role_ = @"User"; break;
7210 case 2: Role_ = @"Hacker"; break;
7211 case 3: Role_ = @"Developer"; break;
7218 bool reset = Settings_ != nil;
7220 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7224 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7234 } else if ([context isEqualToString:@"upgrade"]) {
7237 @synchronized (self) {
7238 for (int i = 0, e = [essential_ count]; i != e; ++i) {
7239 Package *essential = [essential_ objectAtIndex:i];
7240 [essential install];
7264 - (void) reorganize { _pooled
7265 system("/usr/libexec/cydia/free.sh");
7266 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7269 - (void) applicationSuspend:(__GSEvent *)event {
7270 if (hud_ == nil && ![progress_ isRunning])
7271 [super applicationSuspend:event];
7274 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7276 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7279 - (void) _setSuspended:(BOOL)value {
7281 [super _setSuspended:value];
7284 - (UIProgressHUD *) addProgressHUD {
7285 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7287 [underlay_ addSubview:hud];
7291 - (void) openMailToURL:(NSURL *)url {
7292 // XXX: this makes me sad
7294 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7296 [UIApp openURL:url];
7300 - (RVPage *) pageForPackage:(NSString *)name {
7301 if (Package *package = [database_ packageWithName:name]) {
7302 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7303 [view setPackage:package];
7306 UIActionSheet *sheet = [[[UIActionSheet alloc]
7307 initWithTitle:@"Cannot Locate Package"
7308 buttons:[NSArray arrayWithObjects:@"Close", nil]
7309 defaultButtonIndex:0
7314 [sheet setBodyText:[NSString stringWithFormat:
7315 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7318 [sheet popupAlertAnimated:YES];
7323 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7324 NSString *href = [url absoluteString];
7329 if ([href isEqualToString:@"cydia://add-source"])
7330 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7331 else if ([href isEqualToString:@"cydia://sources"])
7332 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7333 else if ([href isEqualToString:@"cydia://packages"])
7334 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7335 else if ([href hasPrefix:@"cydia://url/"])
7336 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7337 else if ([href hasPrefix:@"cydia://launch/"])
7338 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7339 else if ([href hasPrefix:@"cydia://package-settings/"])
7340 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7341 else if ([href hasPrefix:@"cydia://package-signature/"])
7342 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7343 else if ([href hasPrefix:@"cydia://package/"])
7344 return [self pageForPackage:[href substringFromIndex:16]];
7345 else if ([href hasPrefix:@"cydia://files/"]) {
7346 NSString *name = [href substringFromIndex:14];
7348 if (Package *package = [database_ packageWithName:name]) {
7349 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7350 [files setPackage:package];
7358 - (void) applicationOpenURL:(NSURL *)url {
7359 [super applicationOpenURL:url];
7361 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7362 [self setPage:page];
7363 [buttonbar_ showSelectionForButton:tag];
7368 - (void) applicationDidFinishLaunching:(id)unused {
7369 Font12_ = [[UIFont systemFontOfSize:12] retain];
7370 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7371 Font14_ = [[UIFont systemFontOfSize:14] retain];
7372 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7373 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7375 _assert(pkgInitConfig(*_config));
7376 _assert(pkgInitSystem(*_config, _system));
7380 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7381 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7383 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7385 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7386 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7388 [window_ orderFront:self];
7389 [window_ makeKey:self];
7390 [window_ setHidden:NO];
7392 database_ = [Database sharedInstance];
7393 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7394 [database_ setDelegate:progress_];
7395 [window_ setContentView:progress_];
7397 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7398 [progress_ setContentView:underlay_];
7400 [progress_ resetView];
7403 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7404 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7405 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7406 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7407 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7408 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7409 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7411 [self setIdleTimerDisabled:YES];
7413 hud_ = [self addProgressHUD];
7414 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7416 [self setStatusBarShowsProgress:YES];
7419 detachNewThreadSelector:@selector(reorganize)
7427 /* Web Scripting {{{ */
7428 + (NSString *) webScriptNameForSelector:(SEL)selector {
7429 if (selector == @selector(supports:))
7434 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7435 return selector != @selector(supports:);
7438 - (BOOL) supports:(NSString *)feature {
7439 return [feature isEqualToString:@"window.open"];
7443 - (void) showKeyboard:(BOOL)show {
7444 CGSize keysize = [UIKeyboard defaultSize];
7445 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7446 CGRect keyup = keydown;
7447 keyup.origin.y -= keysize.height;
7449 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7450 [animation setSignificantRectFields:2];
7453 [animation setStartFrame:keydown];
7454 [animation setEndFrame:keyup];
7455 [keyboard_ activate];
7457 [animation setStartFrame:keyup];
7458 [animation setEndFrame:keydown];
7459 [keyboard_ deactivate];
7462 [[UIAnimator sharedAnimator]
7463 addAnimations:[NSArray arrayWithObjects:animation, nil]
7464 withDuration:KeyboardTime_
7469 - (void) slideUp:(UIActionSheet *)alert {
7471 [alert presentSheetFromButtonBar:buttonbar_];
7473 [alert presentSheetInView:overlay_];
7478 void AddPreferences(NSString *plist) { _pooled
7479 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7480 _assert(settings != NULL);
7481 NSMutableArray *items = [settings objectForKey:@"items"];
7485 for (size_t i(0); i != [items count]; ++i) {
7486 NSMutableDictionary *item([items objectAtIndex:i]);
7487 NSString *label = [item objectForKey:@"label"];
7488 if (label != nil && [label isEqualToString:@"Cydia"]) {
7495 for (size_t i(0); i != [items count]; ++i) {
7496 NSDictionary *item([items objectAtIndex:i]);
7497 NSString *label = [item objectForKey:@"label"];
7498 if (label != nil && [label isEqualToString:@"General"]) {
7499 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7500 @"CydiaSettings", @"bundle",
7501 @"PSLinkCell", @"cell",
7502 [NSNumber numberWithBool:YES], @"hasIcon",
7503 [NSNumber numberWithBool:YES], @"isController",
7505 nil] atIndex:(i + 1)];
7511 _assert([settings writeToFile:plist atomically:YES] == YES);
7516 id Alloc_(id self, SEL selector) {
7517 id object = alloc_(self, selector);
7518 lprintf("[%s]A-%p\n", self->isa->name, object);
7523 id Dealloc_(id self, SEL selector) {
7524 id object = dealloc_(self, selector);
7525 lprintf("[%s]D-%p\n", self->isa->name, object);
7529 int main(int argc, char *argv[]) { _pooled
7530 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7532 bool substrate(false);
7538 for (int argi(1); argi != argc; ++argi)
7539 if (strcmp(argv[argi], "--") == 0) {
7541 argv[argi] = argv[0];
7547 for (int argi(1); argi != arge; ++argi)
7548 if (strcmp(args[argi], "--bootstrap") == 0)
7550 else if (strcmp(args[argi], "--substrate") == 0)
7553 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7556 App_ = [[NSBundle mainBundle] bundlePath];
7557 Home_ = NSHomeDirectory();
7558 Locale_ = CFLocaleCopyCurrent();
7561 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7562 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7563 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7564 Sounds_Keyboard_ = [keyboard boolValue];
7570 #if 1 /* XXX: this costs 1.4s of startup performance */
7571 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7572 _assert(errno == ENOENT);
7573 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7574 _assert(errno == ENOENT);
7577 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7578 alloc_ = alloc->method_imp;
7579 alloc->method_imp = (IMP) &Alloc_;*/
7581 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7582 dealloc_ = dealloc->method_imp;
7583 dealloc->method_imp = (IMP) &Dealloc_;*/
7588 size = sizeof(maxproc);
7589 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7590 perror("sysctlbyname(\"kern.maxproc\", ?)");
7591 else if (maxproc < 64) {
7593 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7594 perror("sysctlbyname(\"kern.maxproc\", #)");
7597 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7598 char *machine = new char[size];
7599 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7600 perror("sysctlbyname(\"hw.machine\", ?)");
7604 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7606 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7607 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7609 if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7610 Indices_ = [[NSMutableDictionary alloc] init];
7612 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7613 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7615 Settings_ = [Metadata_ objectForKey:@"Settings"];
7617 Packages_ = [Metadata_ objectForKey:@"Packages"];
7618 Sections_ = [Metadata_ objectForKey:@"Sections"];
7619 Sources_ = [Metadata_ objectForKey:@"Sources"];
7622 if (Settings_ != nil)
7623 Role_ = [Settings_ objectForKey:@"Role"];
7625 if (Packages_ == nil) {
7626 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7627 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7630 if (Sections_ == nil) {
7631 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7632 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7635 if (Sources_ == nil) {
7636 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7637 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7641 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7644 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7645 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7647 if (access("/User", F_OK) != 0)
7648 system("/usr/libexec/cydia/firmware.sh");
7650 _assert([[NSFileManager defaultManager]
7651 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7652 withIntermediateDirectories:YES
7657 space_ = CGColorSpaceCreateDeviceRGB();
7659 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7660 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7661 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7662 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7663 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7664 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7666 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7668 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7670 UIApplicationUseLegacyEvents(YES);
7671 UIKeyboardDisableAutomaticAppearance();
7673 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7675 CGColorSpaceRelease(space_);