1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #import "UICaboodle.h"
41 #include <objc/objc.h>
42 #include <objc/runtime.h>
44 #include <CoreGraphics/CoreGraphics.h>
45 #include <GraphicsServices/GraphicsServices.h>
46 #include <Foundation/Foundation.h>
48 #import <QuartzCore/CALayer.h>
50 #import <UIKit/UIKit.h>
53 #import <MessageUI/MailComposeController.h>
55 #include <WebKit/DOMCSSPrimitiveValue.h>
56 #include <WebKit/DOMCSSStyleDeclaration.h>
57 #include <WebKit/DOMDocument.h>
58 #include <WebKit/DOMHTMLBodyElement.h>
59 #include <WebKit/DOMNodeList.h>
60 #include <WebKit/DOMRGBColor.h>
62 #include <WebKit/WebFrame.h>
63 #include <WebKit/WebPolicyDelegate.h>
64 #include <WebKit/WebScriptObject.h>
66 #import <WebKit/WebView.h>
67 #import <WebKit/WebView-WebPrivate.h>
69 #import <JavaScriptCore/JavaScriptCore.h>
74 #include <ext/stdio_filebuf.h>
76 #include <apt-pkg/acquire.h>
77 #include <apt-pkg/acquire-item.h>
78 #include <apt-pkg/algorithms.h>
79 #include <apt-pkg/cachefile.h>
80 #include <apt-pkg/clean.h>
81 #include <apt-pkg/configuration.h>
82 #include <apt-pkg/debmetaindex.h>
83 #include <apt-pkg/error.h>
84 #include <apt-pkg/init.h>
85 #include <apt-pkg/mmap.h>
86 #include <apt-pkg/pkgrecords.h>
87 #include <apt-pkg/sha1.h>
88 #include <apt-pkg/sourcelist.h>
89 #include <apt-pkg/sptr.h>
90 #include <apt-pkg/strutl.h>
92 #include <sys/types.h>
94 #include <sys/sysctl.h>
100 #include <mach-o/nlist.h>
110 #import "BrowserView.h"
111 #import "ResetView.h"
114 //#define _finline __attribute__((force_inline))
115 #define _finline inline
120 #define _limit(count) do { \
121 static size_t _count(0); \
122 if (++_count == count) \
126 static uint64_t profile_;
128 #define _timestamp ({ \
130 gettimeofday(&tv, NULL); \
131 tv.tv_sec * 1000000 + tv.tv_usec; \
134 /* Objective-C Handle<> {{{ */
135 template <typename Type_>
137 typedef _H<Type_> This_;
142 _finline void Retain_() {
147 _finline void Clear_() {
153 _finline _H(Type_ *value = NULL, bool mended = false) :
164 _finline This_ &operator =(Type_ *value) {
165 if (value_ != value) {
174 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
176 void NSLogPoint(const char *fix, const CGPoint &point) {
177 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
180 void NSLogRect(const char *fix, const CGRect &rect) {
181 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
184 /* NSForcedOrderingSearch doesn't work on the iPhone */
185 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
186 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
187 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
189 /* iPhoneOS 2.0 Compatibility {{{ */
191 @interface UITextView (iPhoneOS)
192 - (void) setTextSize:(float)size;
195 @implementation UITextView (iPhoneOS)
197 - (void) setTextSize:(float)size {
198 [self setFont:[[self font] fontWithSize:size]];
205 extern NSString * const kCAFilterNearest;
207 /* Information Dictionaries {{{ */
208 @interface NSMutableArray (Cydia)
209 - (void) addInfoDictionary:(NSDictionary *)info;
212 @implementation NSMutableArray (Cydia)
214 - (void) addInfoDictionary:(NSDictionary *)info {
215 [self addObject:info];
220 @interface NSMutableDictionary (Cydia)
221 - (void) addInfoDictionary:(NSDictionary *)info;
224 @implementation NSMutableDictionary (Cydia)
226 - (void) addInfoDictionary:(NSDictionary *)info {
227 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
228 [self setObject:info forKey:bundle];
233 /* Pop Transitions {{{ */
234 @interface PopTransitionView : UITransitionView {
239 @implementation PopTransitionView
241 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
242 if (from != nil && to == nil)
243 [self removeFromSuperview];
248 @interface UIView (PopUpView)
249 - (void) popFromSuperviewAnimated:(BOOL)animated;
250 - (void) popSubview:(UIView *)view;
253 @implementation UIView (PopUpView)
255 - (void) popFromSuperviewAnimated:(BOOL)animated {
256 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
259 - (void) popSubview:(UIView *)view {
260 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
261 [transition setDelegate:transition];
262 [self addSubview:transition];
264 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
265 [transition transition:UITransitionNone toView:blank];
266 [transition transition:UITransitionPushFromBottom toView:view];
272 #define lprintf(args...) fprintf(stderr, args)
275 #define ForSaurik (1 && !ForRelease)
276 #define RecycleWebViews 0
277 #define AlwaysReload (1 && !ForRelease)
280 @interface NSMutableArray (Radix)
281 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
284 @implementation NSMutableArray (Radix)
286 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
287 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
288 [invocation setSelector:selector];
289 [invocation setArgument:&object atIndex:2];
291 size_t count([self count]);
296 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
298 for (size_t i(0); i != count; ++i) {
299 RadixItem &item(lhs[i]);
302 id object([self objectAtIndex:i]);
303 [invocation setTarget:object];
306 [invocation getReturnValue:&item.key];
309 static const size_t width = 32;
310 static const size_t bits = 11;
311 static const size_t slots = 1 << bits;
312 static const size_t passes = (width + (bits - 1)) / bits;
314 size_t *hist(new size_t[slots]);
316 for (size_t pass(0); pass != passes; ++pass) {
317 memset(hist, 0, sizeof(size_t) * slots);
319 for (size_t i(0); i != count; ++i) {
320 uint32_t key(lhs[i].key);
322 key &= _not(uint32_t) >> width - bits;
327 for (size_t i(0); i != slots; ++i) {
328 size_t local(offset);
333 for (size_t i(0); i != count; ++i) {
334 uint32_t key(lhs[i].key);
336 key &= _not(uint32_t) >> width - bits;
337 rhs[hist[key]++] = lhs[i];
347 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
348 for (size_t i(0); i != count; ++i)
349 [values addObject:[self objectAtIndex:lhs[i].index]];
350 [self setArray:values];
358 /* Apple Bug Fixes {{{ */
359 @implementation UIWebDocumentView (Cydia)
361 - (void) _setScrollerOffset:(CGPoint)offset {
362 UIScroller *scroller([self _scroller]);
364 CGSize size([scroller contentSize]);
365 CGSize bounds([scroller bounds].size);
368 max.x = size.width - bounds.width;
369 max.y = size.height - bounds.height;
377 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
378 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
380 [scroller setOffset:offset];
387 kUIControlEventMouseDown = 1 << 0,
388 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
389 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
390 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
391 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
392 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
393 } UIControlEventMasks;
395 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
396 size_t length([self length] - state->state);
399 else if (length > count)
401 for (size_t i(0); i != length; ++i)
402 objects[i] = [self item:state->state++];
403 state->itemsPtr = objects;
404 state->mutationsPtr = (unsigned long *) self;
408 @interface NSString (UIKit)
409 - (NSString *) stringByAddingPercentEscapes;
410 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
413 @interface NSString (Cydia)
414 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
415 - (NSComparisonResult) compareByPath:(NSString *)other;
418 @implementation NSString (Cydia)
420 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
421 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
424 - (NSComparisonResult) compareByPath:(NSString *)other {
425 NSString *prefix = [self commonPrefixWithString:other options:0];
426 size_t length = [prefix length];
428 NSRange lrange = NSMakeRange(length, [self length] - length);
429 NSRange rrange = NSMakeRange(length, [other length] - length);
431 lrange = [self rangeOfString:@"/" options:0 range:lrange];
432 rrange = [other rangeOfString:@"/" options:0 range:rrange];
434 NSComparisonResult value;
436 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
437 value = NSOrderedSame;
438 else if (lrange.location == NSNotFound)
439 value = NSOrderedAscending;
440 else if (rrange.location == NSNotFound)
441 value = NSOrderedDescending;
443 value = NSOrderedSame;
445 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
446 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
447 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
448 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
450 NSComparisonResult result = [lpath compare:rpath];
451 return result == NSOrderedSame ? value : result;
456 /* Perl-Compatible RegEx {{{ */
466 Pcre(const char *regex) :
471 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
474 lprintf("%d:%s\n", offset, error);
478 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
479 matches_ = new int[(capture_ + 1) * 3];
487 NSString *operator [](size_t match) {
488 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
491 bool operator ()(NSString *data) {
492 // XXX: length is for characters, not for bytes
493 return operator ()([data UTF8String], [data length]);
496 bool operator ()(const char *data, size_t size) {
498 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
502 /* Mime Addresses {{{ */
503 @interface Address : NSObject {
509 - (NSString *) address;
511 + (Address *) addressWithString:(NSString *)string;
512 - (Address *) initWithString:(NSString *)string;
515 @implementation Address
524 - (NSString *) name {
528 - (NSString *) address {
532 + (Address *) addressWithString:(NSString *)string {
533 return [[[Address alloc] initWithString:string] autorelease];
536 + (NSArray *) _attributeKeys {
537 return [NSArray arrayWithObjects:@"address", @"name", nil];
540 - (NSArray *) attributeKeys {
541 return [[self class] _attributeKeys];
544 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
545 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
548 - (Address *) initWithString:(NSString *)string {
549 if ((self = [super init]) != nil) {
550 const char *data = [string UTF8String];
551 size_t size = [string length];
553 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
555 if (address_r(data, size)) {
556 name_ = [address_r[1] retain];
557 address_ = [address_r[2] retain];
559 name_ = [string retain];
567 /* CoreGraphics Primitives {{{ */
578 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
581 Set(space, red, green, blue, alpha);
586 CGColorRelease(color_);
593 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
595 float color[] = {red, green, blue, alpha};
596 color_ = CGColorCreate(space, color);
599 operator CGColorRef() {
605 extern "C" void UISetColor(CGColorRef color);
607 /* Random Global Variables {{{ */
608 static const int PulseInterval_ = 50000;
609 static const int ButtonBarHeight_ = 48;
610 static const float KeyboardTime_ = 0.3f;
612 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
613 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
615 static CGColor Blue_;
616 static CGColor Blueish_;
617 static CGColor Black_;
619 static CGColor White_;
620 static CGColor Gray_;
622 static NSString *App_;
623 static NSString *Home_;
624 static BOOL Sounds_Keyboard_;
626 static BOOL Advanced_;
630 static BOOL Ignored_;
632 static UIFont *Font12_;
633 static UIFont *Font12Bold_;
634 static UIFont *Font14_;
635 static UIFont *Font18Bold_;
636 static UIFont *Font22Bold_;
638 static const char *Machine_ = NULL;
639 static const NSString *UniqueID_ = NULL;
646 CGColorSpaceRef space_;
648 #define FW_LEAST(major, minor, bugfix) \
649 (major < Major_ || major == Major_ && \
650 (minor < Minor_ || minor == Minor_ && \
656 static NSDictionary *SectionMap_;
657 static NSMutableDictionary *Metadata_;
658 static NSMutableDictionary *Indices_;
659 static _transient NSMutableDictionary *Settings_;
660 static _transient NSString *Role_;
661 static _transient NSMutableDictionary *Packages_;
662 static _transient NSMutableDictionary *Sections_;
663 static _transient NSMutableDictionary *Sources_;
664 static bool Changed_;
668 static NSMutableArray *Documents_;
671 NSString *GetLastUpdate() {
672 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
675 return @"Never or Unknown";
677 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
678 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
680 CFRelease(formatter);
682 return [(NSString *) formatted autorelease];
685 /* Display Helpers {{{ */
686 inline float Interpolate(float begin, float end, float fraction) {
687 return (end - begin) * fraction + begin;
690 NSString *SizeString(double size) {
691 bool negative = size < 0;
696 while (size > 1024) {
701 static const char *powers_[] = {"B", "kB", "MB", "GB"};
703 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
706 NSString *StripVersion(NSString *version) {
707 NSRange colon = [version rangeOfString:@":"];
708 if (colon.location != NSNotFound)
709 version = [version substringFromIndex:(colon.location + 1)];
713 NSString *Simplify(NSString *title) {
714 const char *data = [title UTF8String];
715 size_t size = [title length];
717 static Pcre square_r("^\\[(.*)\\]$");
718 if (square_r(data, size))
719 return Simplify(square_r[1]);
721 static Pcre paren_r("^\\((.*)\\)$");
722 if (paren_r(data, size))
723 return Simplify(paren_r[1]);
725 static Pcre title_r("^(.*?) \\(.*\\)$");
726 if (title_r(data, size))
727 return Simplify(title_r[1]);
733 bool isSectionVisible(NSString *section) {
734 NSDictionary *metadata = [Sections_ objectForKey:section];
735 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
736 return hidden == nil || ![hidden boolValue];
739 /* Delegate Prototypes {{{ */
743 @interface NSObject (ProgressDelegate)
746 @implementation NSObject(ProgressDelegate)
748 - (void) _setProgressError:(NSArray *)args {
749 [self performSelector:@selector(setProgressError:forPackage:)
750 withObject:[args objectAtIndex:0]
751 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
757 @protocol ProgressDelegate
758 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
759 - (void) setProgressTitle:(NSString *)title;
760 - (void) setProgressPercent:(float)percent;
761 - (void) startProgress;
762 - (void) addProgressOutput:(NSString *)output;
763 - (bool) isCancelling:(size_t)received;
766 @protocol ConfigurationDelegate
767 - (void) repairWithSelector:(SEL)selector;
768 - (void) setConfigurationData:(NSString *)data;
771 @protocol CydiaDelegate
772 - (void) installPackage:(Package *)package;
773 - (void) removePackage:(Package *)package;
774 - (void) slideUp:(UIActionSheet *)alert;
775 - (void) distUpgrade;
778 - (void) askForSettings;
779 - (UIProgressHUD *) addProgressHUD;
780 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
781 - (RVPage *) pageForPackage:(NSString *)name;
782 - (void) openMailToURL:(NSURL *)url;
783 - (void) clearFirstResponder;
787 /* Status Delegation {{{ */
789 public pkgAcquireStatus
792 _transient NSObject<ProgressDelegate> *delegate_;
800 void setDelegate(id delegate) {
801 delegate_ = delegate;
804 virtual bool MediaChange(std::string media, std::string drive) {
808 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
811 virtual void Fetch(pkgAcquire::ItemDesc &item) {
812 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
813 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
816 virtual void Done(pkgAcquire::ItemDesc &item) {
819 virtual void Fail(pkgAcquire::ItemDesc &item) {
821 item.Owner->Status == pkgAcquire::Item::StatIdle ||
822 item.Owner->Status == pkgAcquire::Item::StatDone
826 std::string &error(item.Owner->ErrorText);
830 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
831 NSArray *fields([description componentsSeparatedByString:@" "]);
832 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
834 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
835 withObject:[NSArray arrayWithObjects:
836 [NSString stringWithUTF8String:error.c_str()],
843 virtual bool Pulse(pkgAcquire *Owner) {
844 bool value = pkgAcquireStatus::Pulse(Owner);
847 double(CurrentBytes + CurrentItems) /
848 double(TotalBytes + TotalItems)
851 [delegate_ setProgressPercent:percent];
852 return [delegate_ isCancelling:CurrentBytes] ? false : value;
855 virtual void Start() {
856 [delegate_ startProgress];
859 virtual void Stop() {
863 /* Progress Delegation {{{ */
868 _transient id<ProgressDelegate> delegate_;
871 virtual void Update() {
872 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
873 [delegate_ setProgressPercent:(Percent / 100)];
882 void setDelegate(id delegate) {
883 delegate_ = delegate;
886 virtual void Done() {
887 [delegate_ setProgressPercent:1];
892 /* Database Interface {{{ */
893 @interface Database : NSObject {
895 pkgDepCache::Policy *policy_;
896 pkgRecords *records_;
897 pkgProblemResolver *resolver_;
898 pkgAcquire *fetcher_;
900 SPtr<pkgPackageManager> manager_;
901 pkgSourceList *list_;
903 NSMutableDictionary *sources_;
904 NSMutableArray *packages_;
906 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
915 + (Database *) sharedInstance;
917 - (void) _readCydia:(NSNumber *)fd;
918 - (void) _readStatus:(NSNumber *)fd;
919 - (void) _readOutput:(NSNumber *)fd;
923 - (Package *) packageWithName:(NSString *)name;
925 - (pkgCacheFile &) cache;
926 - (pkgDepCache::Policy *) policy;
927 - (pkgRecords *) records;
928 - (pkgProblemResolver *) resolver;
929 - (pkgAcquire &) fetcher;
930 - (NSArray *) packages;
931 - (NSArray *) sources;
940 - (void) updateWithStatus:(Status &)status;
942 - (void) setDelegate:(id)delegate;
943 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
947 /* Source Class {{{ */
948 @interface Source : NSObject {
949 NSString *description_;
954 NSString *distribution_;
958 NSString *defaultIcon_;
960 NSDictionary *record_;
964 - (Source *) initWithMetaIndex:(metaIndex *)index;
966 - (NSComparisonResult) compareByNameAndType:(Source *)source;
968 - (NSDictionary *) record;
972 - (NSString *) distribution;
978 - (NSString *) description;
979 - (NSString *) label;
980 - (NSString *) origin;
981 - (NSString *) version;
983 - (NSString *) defaultIcon;
987 @implementation Source
991 [distribution_ release];
994 if (description_ != nil)
995 [description_ release];
1000 if (version_ != nil)
1002 if (defaultIcon_ != nil)
1003 [defaultIcon_ release];
1010 + (NSArray *) _attributeKeys {
1011 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1014 - (NSArray *) attributeKeys {
1015 return [[self class] _attributeKeys];
1018 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1019 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1022 - (Source *) initWithMetaIndex:(metaIndex *)index {
1023 if ((self = [super init]) != nil) {
1024 trusted_ = index->IsTrusted();
1026 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1027 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1028 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1030 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1031 if (dindex != NULL) {
1032 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1034 while (std::getline(release, line)) {
1035 std::string::size_type colon(line.find(':'));
1036 if (colon == std::string::npos)
1039 std::string name(line.substr(0, colon));
1040 std::string value(line.substr(colon + 1));
1041 while (!value.empty() && value[0] == ' ')
1042 value = value.substr(1);
1044 if (name == "Default-Icon")
1045 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1046 else if (name == "Description")
1047 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1048 else if (name == "Label")
1049 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1050 else if (name == "Origin")
1051 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1052 else if (name == "Version")
1053 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1057 record_ = [Sources_ objectForKey:[self key]];
1059 record_ = [record_ retain];
1063 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1064 NSDictionary *lhr = [self record];
1065 NSDictionary *rhr = [source record];
1068 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1070 NSString *lhs = [self name];
1071 NSString *rhs = [source name];
1073 if ([lhs length] != 0 && [rhs length] != 0) {
1074 unichar lhc = [lhs characterAtIndex:0];
1075 unichar rhc = [rhs characterAtIndex:0];
1077 if (isalpha(lhc) && !isalpha(rhc))
1078 return NSOrderedAscending;
1079 else if (!isalpha(lhc) && isalpha(rhc))
1080 return NSOrderedDescending;
1083 return [lhs compare:rhs options:LaxCompareOptions_];
1086 - (NSDictionary *) record {
1094 - (NSString *) uri {
1098 - (NSString *) distribution {
1099 return distribution_;
1102 - (NSString *) type {
1106 - (NSString *) key {
1107 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1110 - (NSString *) host {
1111 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1114 - (NSString *) name {
1115 return origin_ == nil ? [self host] : origin_;
1118 - (NSString *) description {
1119 return description_;
1122 - (NSString *) label {
1123 return label_ == nil ? [self host] : label_;
1126 - (NSString *) origin {
1130 - (NSString *) version {
1134 - (NSString *) defaultIcon {
1135 return defaultIcon_;
1140 /* Relationship Class {{{ */
1141 @interface Relationship : NSObject {
1146 - (NSString *) type;
1148 - (NSString *) name;
1152 @implementation Relationship
1160 - (NSString *) type {
1168 - (NSString *) name {
1175 /* Package Class {{{ */
1176 @interface Package : NSObject {
1177 pkgCache::PkgIterator iterator_;
1178 _transient Database *database_;
1179 pkgCache::VerIterator version_;
1180 pkgCache::VerFileIterator file_;
1188 NSString *installed_;
1194 NSString *depiction_;
1195 NSString *homepage_;
1201 NSArray *relationships_;
1204 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1205 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1207 - (pkgCache::PkgIterator) iterator;
1209 - (NSString *) section;
1210 - (NSString *) simpleSection;
1212 - (Address *) maintainer;
1214 - (NSString *) description;
1215 - (NSString *) index;
1217 - (NSMutableDictionary *) metadata;
1219 - (BOOL) subscribed;
1222 - (NSString *) latest;
1223 - (NSString *) installed;
1226 - (BOOL) upgradableAndEssential:(BOOL)essential;
1229 - (BOOL) unfiltered;
1233 - (BOOL) halfConfigured;
1234 - (BOOL) halfInstalled;
1236 - (NSString *) mode;
1239 - (NSString *) name;
1240 - (NSString *) tagline;
1242 - (NSString *) homepage;
1243 - (NSString *) depiction;
1244 - (Address *) author;
1246 - (NSArray *) files;
1247 - (NSArray *) relationships;
1248 - (NSArray *) warnings;
1249 - (NSArray *) applications;
1251 - (Source *) source;
1252 - (NSString *) role;
1253 - (NSString *) rating;
1255 - (BOOL) matches:(NSString *)text;
1257 - (bool) hasSupportingRole;
1258 - (BOOL) hasTag:(NSString *)tag;
1259 - (NSString *) primaryPurpose;
1260 - (NSArray *) purposes;
1262 - (NSComparisonResult) compareByName:(Package *)package;
1263 - (NSComparisonResult) compareBySection:(Package *)package;
1265 - (uint32_t) compareForChanges;
1270 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1271 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1272 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1273 - (NSNumber *) isVisibleInSource:(Source *)source;
1277 @implementation Package
1283 if (section_ != nil)
1287 if (installed_ != nil)
1288 [installed_ release];
1296 if (depiction_ != nil)
1297 [depiction_ release];
1298 if (homepage_ != nil)
1299 [homepage_ release];
1300 if (sponsor_ != nil)
1309 if (relationships_ != nil)
1310 [relationships_ release];
1315 + (NSArray *) _attributeKeys {
1316 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1319 - (NSArray *) attributeKeys {
1320 return [[self class] _attributeKeys];
1323 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1324 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1327 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1328 if ((self = [super init]) != nil) {
1329 iterator_ = iterator;
1330 database_ = database;
1332 version_ = [database_ policy]->GetCandidateVer(iterator_);
1333 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1334 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1336 pkgCache::VerIterator current = iterator_.CurrentVer();
1337 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1338 installed_ = [StripVersion(installed) retain];
1340 if (!version_.end())
1341 file_ = version_.FileList();
1343 pkgCache &cache([database_ cache]);
1344 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1347 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1350 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1352 const char *begin, *end;
1353 parser->GetRec(begin, end);
1355 NSString *website(nil);
1356 NSString *sponsor(nil);
1357 NSString *author(nil);
1366 {"depiction", &depiction_},
1367 {"homepage", &homepage_},
1368 {"website", &website},
1369 {"sponsor", &sponsor},
1370 {"author", &author},
1374 while (begin != end)
1375 if (*begin == '\n') {
1378 } else if (isblank(*begin)) next: {
1379 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1382 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1383 const char *name(begin);
1384 size_t size(colon - begin);
1386 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1389 const char *stop(begin == NULL ? end : begin);
1390 while (stop[-1] == '\r')
1392 while (++colon != stop && isblank(*colon));
1394 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1395 if (strncasecmp(names[i].name_, name, size) == 0) {
1396 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1397 *names[i].value_ = value;
1408 name_ = [name_ retain];
1409 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1411 icon_ = [icon_ retain];
1412 if (depiction_ != nil)
1413 depiction_ = [depiction_ retain];
1414 if (homepage_ == nil)
1415 homepage_ = website;
1416 if ([homepage_ isEqualToString:depiction_])
1418 if (homepage_ != nil)
1419 homepage_ = [homepage_ retain];
1421 sponsor_ = [[Address addressWithString:sponsor] retain];
1423 author_ = [[Address addressWithString:author] retain];
1425 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1429 for (int i(0), e([tags_ count]); i != e; ++i) {
1430 NSString *tag = [tags_ objectAtIndex:i];
1431 if ([tag hasPrefix:@"role::"]) {
1432 role_ = [[tag substringFromIndex:6] retain];
1437 NSString *solid(latest == nil ? installed : latest);
1438 bool changed(false);
1440 NSString *key([id_ lowercaseString]);
1442 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1443 if (metadata == nil) {
1444 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1449 [metadata setObject:solid forKey:@"LastVersion"];
1452 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1453 NSDate *last([metadata objectForKey:@"LastSeen"]);
1454 NSString *version([metadata objectForKey:@"LastVersion"]);
1457 first = last == nil ? now_ : last;
1458 [metadata setObject:first forKey:@"FirstSeen"];
1463 if (version == nil) {
1464 [metadata setObject:solid forKey:@"LastVersion"];
1466 } else if (![version isEqualToString:solid]) {
1467 [metadata setObject:solid forKey:@"LastVersion"];
1469 [metadata setObject:last forKey:@"LastSeen"];
1475 [Packages_ setObject:metadata forKey:key];
1481 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1482 return [[[Package alloc]
1483 initWithIterator:iterator
1488 - (pkgCache::PkgIterator) iterator {
1492 - (NSString *) section {
1493 if (section_ != nil)
1496 const char *section = iterator_.Section();
1497 if (section == NULL)
1500 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1503 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1504 if (NSString *rename = [value objectForKey:@"Rename"]) {
1509 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1513 - (NSString *) simpleSection {
1514 if (NSString *section = [self section])
1515 return Simplify(section);
1521 - (Address *) maintainer {
1524 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1525 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1529 return version_.end() ? 0 : version_->InstalledSize;
1532 - (NSString *) description {
1535 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1536 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1538 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1539 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1540 if ([lines count] < 2)
1543 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1544 for (size_t i(1); i != [lines count]; ++i) {
1545 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1546 [trimmed addObject:trim];
1549 return [trimmed componentsJoinedByString:@"\n"];
1552 - (NSString *) index {
1553 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1554 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1557 - (NSMutableDictionary *) metadata {
1558 return [Packages_ objectForKey:[id_ lowercaseString]];
1562 NSDictionary *metadata([self metadata]);
1563 if ([self subscribed])
1564 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1566 return [metadata objectForKey:@"FirstSeen"];
1569 - (BOOL) subscribed {
1570 NSDictionary *metadata([self metadata]);
1571 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1572 return [subscribed boolValue];
1578 NSDictionary *metadata([self metadata]);
1579 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1580 return [ignored boolValue];
1585 - (NSString *) latest {
1589 - (NSString *) installed {
1594 return !version_.end();
1597 - (BOOL) upgradableAndEssential:(BOOL)essential {
1598 pkgCache::VerIterator current = iterator_.CurrentVer();
1601 return essential && [self essential];
1603 return !version_.end() && version_ != current;
1606 - (BOOL) essential {
1607 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1611 return [database_ cache][iterator_].InstBroken();
1614 - (BOOL) unfiltered {
1615 NSString *section = [self section];
1616 return section == nil || isSectionVisible(section);
1620 return [self hasSupportingRole] && [self unfiltered];
1624 unsigned char current = iterator_->CurrentState;
1625 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1628 - (BOOL) halfConfigured {
1629 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1632 - (BOOL) halfInstalled {
1633 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1637 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1638 return state.Mode != pkgDepCache::ModeKeep;
1641 - (NSString *) mode {
1642 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1644 switch (state.Mode) {
1645 case pkgDepCache::ModeDelete:
1646 if ((state.iFlags & pkgDepCache::Purge) != 0)
1650 case pkgDepCache::ModeKeep:
1651 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1655 case pkgDepCache::ModeInstall:
1656 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1657 return @"Reinstall";
1658 else switch (state.Status) {
1660 return @"Downgrade";
1666 return @"New Install";
1679 - (NSString *) name {
1680 return name_ == nil ? id_ : name_;
1683 - (NSString *) tagline {
1687 - (UIImage *) icon {
1688 NSString *section = [self simpleSection];
1691 if (NSString *icon = icon_)
1692 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1693 if (icon == nil) if (section != nil)
1694 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1695 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1696 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1698 icon = [UIImage applicationImageNamed:@"unknown.png"];
1702 - (NSString *) homepage {
1706 - (NSString *) depiction {
1710 - (Address *) sponsor {
1714 - (Address *) author {
1718 - (NSArray *) files {
1719 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1720 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1723 fin.open([path UTF8String]);
1728 while (std::getline(fin, line))
1729 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1734 - (NSArray *) relationships {
1735 return relationships_;
1738 - (NSArray *) warnings {
1739 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1740 const char *name(iterator_.Name());
1742 size_t length(strlen(name));
1743 if (length < 2) invalid:
1744 [warnings addObject:@"illegal package identifier"];
1745 else for (size_t i(0); i != length; ++i)
1747 (name[i] < 'a' || name[i] > 'z') &&
1748 (name[i] < '0' || name[i] > '9') &&
1749 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1752 if (strcmp(name, "cydia") != 0) {
1756 if (NSArray *files = [self files])
1757 for (NSString *file in files)
1758 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1760 else if (!stash && [file isEqualToString:@"/var/stash"])
1764 [warnings addObject:@"files installed into Cydia.app"];
1766 [warnings addObject:@"files installed to /var/stash"];
1769 return [warnings count] == 0 ? nil : warnings;
1772 - (NSArray *) applications {
1773 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1775 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1777 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1778 if (NSArray *files = [self files])
1779 for (NSString *file in files)
1780 if (application_r(file)) {
1781 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1782 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1783 if ([id isEqualToString:me])
1786 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1788 display = application_r[1];
1790 NSString *bundle([file stringByDeletingLastPathComponent]);
1791 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1792 if (icon == nil || [icon length] == 0)
1794 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1796 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1797 [applications addObject:application];
1799 [application addObject:id];
1800 [application addObject:display];
1801 [application addObject:url];
1804 return [applications count] == 0 ? nil : applications;
1807 - (Source *) source {
1809 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1816 - (NSString *) role {
1820 - (NSString *) rating {
1821 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1822 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1827 - (BOOL) matches:(NSString *)text {
1833 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1834 if (range.location != NSNotFound)
1837 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1838 if (range.location != NSNotFound)
1841 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1842 if (range.location != NSNotFound)
1848 - (bool) hasSupportingRole {
1851 if ([role_ isEqualToString:@"enduser"])
1853 if ([Role_ isEqualToString:@"User"])
1855 if ([role_ isEqualToString:@"hacker"])
1857 if ([Role_ isEqualToString:@"Hacker"])
1859 if ([role_ isEqualToString:@"developer"])
1861 if ([Role_ isEqualToString:@"Developer"])
1866 - (BOOL) hasTag:(NSString *)tag {
1867 return tags_ == nil ? NO : [tags_ containsObject:tag];
1870 - (NSString *) primaryPurpose {
1871 for (NSString *tag in tags_)
1872 if ([tag hasPrefix:@"purpose::"])
1873 return [tag substringFromIndex:9];
1877 - (NSArray *) purposes {
1878 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1879 for (NSString *tag in tags_)
1880 if ([tag hasPrefix:@"purpose::"])
1881 [purposes addObject:[tag substringFromIndex:9]];
1882 return [purposes count] == 0 ? nil : purposes;
1885 - (NSComparisonResult) compareByName:(Package *)package {
1886 NSString *lhs = [self name];
1887 NSString *rhs = [package name];
1889 if ([lhs length] != 0 && [rhs length] != 0) {
1890 unichar lhc = [lhs characterAtIndex:0];
1891 unichar rhc = [rhs characterAtIndex:0];
1893 if (isalpha(lhc) && !isalpha(rhc))
1894 return NSOrderedAscending;
1895 else if (!isalpha(lhc) && isalpha(rhc))
1896 return NSOrderedDescending;
1899 return [lhs compare:rhs options:LaxCompareOptions_];
1902 - (NSComparisonResult) compareBySection:(Package *)package {
1903 NSString *lhs = [self section];
1904 NSString *rhs = [package section];
1906 if (lhs == NULL && rhs != NULL)
1907 return NSOrderedAscending;
1908 else if (lhs != NULL && rhs == NULL)
1909 return NSOrderedDescending;
1910 else if (lhs != NULL && rhs != NULL) {
1911 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1912 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1915 return NSOrderedSame;
1918 - (uint32_t) compareForChanges {
1923 uint32_t timestamp : 30;
1924 uint32_t ignored : 1;
1925 uint32_t upgradable : 1;
1929 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1931 if ([self upgradableAndEssential:YES]) {
1932 value.bits.timestamp = 0;
1933 value.bits.ignored = [self ignored] ? 0 : 1;
1934 value.bits.upgradable = 1;
1936 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1937 value.bits.ignored = 0;
1938 value.bits.upgradable = 0;
1941 return _not(uint32_t) - value.key;
1945 pkgProblemResolver *resolver = [database_ resolver];
1946 resolver->Clear(iterator_);
1947 resolver->Protect(iterator_);
1948 pkgCacheFile &cache([database_ cache]);
1949 cache->MarkInstall(iterator_, false);
1950 pkgDepCache::StateCache &state((*cache)[iterator_]);
1951 if (!state.Install())
1952 cache->SetReInstall(iterator_, true);
1956 pkgProblemResolver *resolver = [database_ resolver];
1957 resolver->Clear(iterator_);
1958 resolver->Protect(iterator_);
1959 resolver->Remove(iterator_);
1960 [database_ cache]->MarkDelete(iterator_, true);
1963 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1964 return [NSNumber numberWithBool:(
1965 [self unfiltered] && [self matches:search]
1969 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1970 return [NSNumber numberWithBool:(
1971 (![number boolValue] || [self visible]) && [self installed] != nil
1975 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1976 NSString *section = [self section];
1978 return [NSNumber numberWithBool:(
1980 [self installed] == nil && (
1982 section == nil && [name length] == 0 ||
1983 [name isEqualToString:section]
1988 - (NSNumber *) isVisibleInSource:(Source *)source {
1989 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1994 /* Section Class {{{ */
1995 @interface Section : NSObject {
2001 - (NSComparisonResult) compareByName:(Section *)section;
2002 - (Section *) initWithName:(NSString *)name;
2003 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2004 - (NSString *) name;
2007 - (void) addToCount;
2011 @implementation Section
2018 - (NSComparisonResult) compareByName:(Section *)section {
2019 NSString *lhs = [self name];
2020 NSString *rhs = [section name];
2022 if ([lhs length] != 0 && [rhs length] != 0) {
2023 unichar lhc = [lhs characterAtIndex:0];
2024 unichar rhc = [rhs characterAtIndex:0];
2026 if (isalpha(lhc) && !isalpha(rhc))
2027 return NSOrderedAscending;
2028 else if (!isalpha(lhc) && isalpha(rhc))
2029 return NSOrderedDescending;
2032 return [lhs compare:rhs options:LaxCompareOptions_];
2035 - (Section *) initWithName:(NSString *)name {
2036 return [self initWithName:name row:0];
2039 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2040 if ((self = [super init]) != nil) {
2041 name_ = [name retain];
2046 - (NSString *) name {
2058 - (void) addToCount {
2066 static NSArray *Finishes_;
2068 /* Database Implementation {{{ */
2069 @implementation Database
2071 + (Database *) sharedInstance {
2072 static Database *instance;
2073 if (instance == nil)
2074 instance = [[Database alloc] init];
2083 - (void) _readCydia:(NSNumber *)fd { _pooled
2084 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2085 std::istream is(&ib);
2088 static Pcre finish_r("^finish:([^:]*)$");
2090 while (std::getline(is, line)) {
2091 const char *data(line.c_str());
2092 size_t size = line.size();
2093 lprintf("C:%s\n", data);
2095 if (finish_r(data, size)) {
2096 NSString *finish = finish_r[1];
2097 int index = [Finishes_ indexOfObject:finish];
2098 if (index != INT_MAX && index > Finish_)
2106 - (void) _readStatus:(NSNumber *)fd { _pooled
2107 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2108 std::istream is(&ib);
2111 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2112 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2114 while (std::getline(is, line)) {
2115 const char *data(line.c_str());
2116 size_t size = line.size();
2117 lprintf("S:%s\n", data);
2119 if (conffile_r(data, size)) {
2120 [delegate_ setConfigurationData:conffile_r[1]];
2121 } else if (strncmp(data, "status: ", 8) == 0) {
2122 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2123 [delegate_ setProgressTitle:string];
2124 } else if (pmstatus_r(data, size)) {
2125 std::string type([pmstatus_r[1] UTF8String]);
2126 NSString *id = pmstatus_r[2];
2128 float percent([pmstatus_r[3] floatValue]);
2129 [delegate_ setProgressPercent:(percent / 100)];
2131 NSString *string = pmstatus_r[4];
2133 if (type == "pmerror")
2134 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2135 withObject:[NSArray arrayWithObjects:string, id, nil]
2138 else if (type == "pmstatus") {
2139 [delegate_ setProgressTitle:string];
2140 } else if (type == "pmconffile")
2141 [delegate_ setConfigurationData:string];
2142 else _assert(false);
2143 } else _assert(false);
2149 - (void) _readOutput:(NSNumber *)fd { _pooled
2150 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2151 std::istream is(&ib);
2154 while (std::getline(is, line)) {
2155 lprintf("O:%s\n", line.c_str());
2156 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2166 - (Package *) packageWithName:(NSString *)name {
2167 if (static_cast<pkgDepCache *>(cache_) == NULL)
2169 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2170 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2173 - (Database *) init {
2174 if ((self = [super init]) != nil) {
2181 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2182 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2186 _assert(pipe(fds) != -1);
2189 _config->Set("APT::Keep-Fds::", cydiafd_);
2190 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2193 detachNewThreadSelector:@selector(_readCydia:)
2195 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2198 _assert(pipe(fds) != -1);
2202 detachNewThreadSelector:@selector(_readStatus:)
2204 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2207 _assert(pipe(fds) != -1);
2208 _assert(dup2(fds[0], 0) != -1);
2209 _assert(close(fds[0]) != -1);
2211 input_ = fdopen(fds[1], "a");
2213 _assert(pipe(fds) != -1);
2214 _assert(dup2(fds[1], 1) != -1);
2215 _assert(close(fds[1]) != -1);
2218 detachNewThreadSelector:@selector(_readOutput:)
2220 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2225 - (pkgCacheFile &) cache {
2229 - (pkgDepCache::Policy *) policy {
2233 - (pkgRecords *) records {
2237 - (pkgProblemResolver *) resolver {
2241 - (pkgAcquire &) fetcher {
2245 - (NSArray *) packages {
2249 - (NSArray *) sources {
2250 return [sources_ allValues];
2253 - (NSArray *) issues {
2254 if (cache_->BrokenCount() == 0)
2257 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2259 for (Package *package in packages_) {
2260 if (![package broken])
2262 pkgCache::PkgIterator pkg([package iterator]);
2264 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2265 [entry addObject:[package name]];
2266 [issues addObject:entry];
2268 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2272 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2273 pkgCache::DepIterator start;
2274 pkgCache::DepIterator end;
2275 dep.GlobOr(start, end); // ++dep
2277 if (!cache_->IsImportantDep(end))
2279 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2282 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2283 [entry addObject:failure];
2284 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2286 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2287 [failure addObject:[package name]];
2289 pkgCache::PkgIterator target(start.TargetPkg());
2290 if (target->ProvidesList != 0)
2291 [failure addObject:@"?"];
2293 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2295 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2296 else if (!cache_[target].CandidateVerIter(cache_).end())
2297 [failure addObject:@"-"];
2298 else if (target->ProvidesList == 0)
2299 [failure addObject:@"!"];
2301 [failure addObject:@"%"];
2305 if (start.TargetVer() != 0)
2306 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2317 - (void) reloadData {
2337 if (!cache_.Open(progress_, true)) {
2339 if (!_error->PopMessage(error))
2342 lprintf("cache_.Open():[%s]\n", error.c_str());
2344 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2345 [delegate_ repairWithSelector:@selector(configure)];
2346 else if (error == "The package lists or status file could not be parsed or opened.")
2347 [delegate_ repairWithSelector:@selector(update)];
2348 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2349 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2350 // else if (error == "The list of sources could not be read.")
2351 else _assert(false);
2357 now_ = [[NSDate date] retain];
2359 policy_ = new pkgDepCache::Policy();
2360 records_ = new pkgRecords(cache_);
2361 resolver_ = new pkgProblemResolver(cache_);
2362 fetcher_ = new pkgAcquire(&status_);
2365 list_ = new pkgSourceList();
2366 _assert(list_->ReadMainList());
2368 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2369 _assert(pkgApplyStatus(cache_));
2371 if (cache_->BrokenCount() != 0) {
2372 _assert(pkgFixBroken(cache_));
2373 _assert(cache_->BrokenCount() == 0);
2374 _assert(pkgMinimizeUpgrade(cache_));
2377 [sources_ removeAllObjects];
2378 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2379 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2380 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2382 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2383 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2387 [packages_ removeAllObjects];
2390 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2391 if (Package *package = [Package packageWithIterator:iterator database:self])
2392 [packages_ addObject:package];
2394 [packages_ sortUsingSelector:@selector(compareByName:)];
2398 - (void) configure {
2399 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2400 system([dpkg UTF8String]);
2408 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2409 _assert(!_error->PendingError());
2412 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2415 public pkgArchiveCleaner
2418 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2423 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2425 while (_error->PopMessage(error))
2426 lprintf("ArchiveCleaner: %s\n", error.c_str());
2431 pkgRecords records(cache_);
2433 lock_ = new FileFd();
2434 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2435 _assert(!_error->PendingError());
2438 // XXX: explain this with an error message
2439 _assert(list.ReadMainList());
2441 manager_ = (_system->CreatePM(cache_));
2442 _assert(manager_->GetArchives(fetcher_, &list, &records));
2443 _assert(!_error->PendingError());
2447 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2449 _assert(list.ReadMainList());
2450 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2451 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2454 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2459 bool failed = false;
2460 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2461 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2464 std::string uri = (*item)->DescURI();
2465 std::string error = (*item)->ErrorText;
2467 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2470 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2471 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2482 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2484 if (_error->PendingError()) {
2489 if (result == pkgPackageManager::Failed) {
2494 if (result != pkgPackageManager::Completed) {
2499 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2501 _assert(list.ReadMainList());
2502 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2503 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2506 if (![before isEqualToArray:after])
2511 _assert(pkgDistUpgrade(cache_));
2515 [self updateWithStatus:status_];
2518 - (void) updateWithStatus:(Status &)status {
2520 _assert(list.ReadMainList());
2523 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2524 _assert(!_error->PendingError());
2526 pkgAcquire fetcher(&status);
2527 _assert(list.GetIndexes(&fetcher));
2529 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2530 bool failed = false;
2531 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2532 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2533 (*item)->Finished();
2537 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2538 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2539 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2542 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2547 - (void) setDelegate:(id)delegate {
2548 delegate_ = delegate;
2549 status_.setDelegate(delegate);
2550 progress_.setDelegate(delegate);
2553 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2554 pkgIndexFile *index(NULL);
2555 list_->FindIndex(file, index);
2556 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2562 /* PopUp Windows {{{ */
2563 @interface PopUpView : UIView {
2564 _transient id delegate_;
2565 UITransitionView *transition_;
2570 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2574 @implementation PopUpView
2577 [transition_ setDelegate:nil];
2578 [transition_ release];
2584 [transition_ transition:UITransitionPushFromTop toView:nil];
2587 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2588 if (from != nil && to == nil)
2589 [self removeFromSuperview];
2592 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2593 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2594 delegate_ = delegate;
2596 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2597 [self addSubview:transition_];
2599 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2601 [view addSubview:self];
2603 [transition_ setDelegate:self];
2605 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2606 [transition_ transition:UITransitionNone toView:blank];
2607 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2614 /* Mail Composition {{{ */
2615 @interface MailToView : PopUpView {
2616 MailComposeController *controller_;
2619 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2623 @implementation MailToView
2626 [controller_ release];
2630 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2634 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2635 NSLog(@"did:%@", delivery);
2636 // [UIApp setStatusBarShowsProgress:NO];
2637 if ([controller error]){
2638 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2639 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2640 [mailAlertSheet setBodyText:[controller error]];
2641 [mailAlertSheet popupAlertAnimated:YES];
2645 - (void) showError {
2646 NSLog(@"%@", [controller_ error]);
2647 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2648 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2649 [mailAlertSheet setBodyText:[controller_ error]];
2650 [mailAlertSheet popupAlertAnimated:YES];
2653 - (void) deliverMessage { _pooled
2657 if (![controller_ deliverMessage])
2658 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2661 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2662 if ([controller_ needsDelivery])
2663 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2668 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2669 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2670 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2671 [controller_ setDelegate:self];
2672 [controller_ initializeUI];
2673 [controller_ setupForURL:url];
2675 UIView *view([controller_ view]);
2676 [overlay_ addSubview:view];
2682 /* Confirmation View {{{ */
2683 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2684 if (!iterator.end())
2685 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2686 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2688 pkgCache::PkgIterator package(dep.TargetPkg());
2691 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2698 @protocol ConfirmationViewDelegate
2703 @interface ConfirmationView : BrowserView {
2704 _transient Database *database_;
2705 UIActionSheet *essential_;
2712 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2716 @implementation ConfirmationView
2723 if (essential_ != nil)
2724 [essential_ release];
2730 [book_ popFromSuperviewAnimated:YES];
2733 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2734 NSString *context([sheet context]);
2736 if ([context isEqualToString:@"remove"]) {
2744 [delegate_ confirm];
2751 } else if ([context isEqualToString:@"unable"]) {
2755 [super alertSheet:sheet buttonClicked:button];
2758 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2759 [window setValue:changes_ forKey:@"changes"];
2760 [window setValue:issues_ forKey:@"issues"];
2761 [window setValue:sizes_ forKey:@"sizes"];
2762 [super webView:sender didClearWindowObject:window forFrame:frame];
2765 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2766 if ((self = [super initWithBook:book]) != nil) {
2767 database_ = database;
2769 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2770 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2771 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2772 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2773 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2777 pkgDepCache::Policy *policy([database_ policy]);
2779 pkgCacheFile &cache([database_ cache]);
2780 NSArray *packages = [database_ packages];
2781 for (size_t i(0), e = [packages count]; i != e; ++i) {
2782 Package *package = [packages objectAtIndex:i];
2783 pkgCache::PkgIterator iterator = [package iterator];
2784 pkgDepCache::StateCache &state(cache[iterator]);
2786 NSString *name([package name]);
2788 if (state.NewInstall())
2789 [installing addObject:name];
2790 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2791 [reinstalling addObject:name];
2792 else if (state.Upgrade())
2793 [upgrading addObject:name];
2794 else if (state.Downgrade())
2795 [downgrading addObject:name];
2796 else if (state.Delete()) {
2797 if ([package essential])
2799 [removing addObject:name];
2802 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2803 substrate_ |= DepSubstrate(iterator.CurrentVer());
2808 else if (Advanced_ || true) {
2809 essential_ = [[UIActionSheet alloc]
2810 initWithTitle:@"Removing Essentials"
2811 buttons:[NSArray arrayWithObjects:
2812 @"Cancel Operation (Safe)",
2813 @"Force Removal (Unsafe)",
2815 defaultButtonIndex:0
2821 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2823 [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."];
2825 essential_ = [[UIActionSheet alloc]
2826 initWithTitle:@"Unable to Comply"
2827 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2828 defaultButtonIndex:0
2833 [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."];
2836 changes_ = [[NSArray alloc] initWithObjects:
2844 issues_ = [database_ issues];
2846 issues_ = [issues_ retain];
2848 sizes_ = [[NSArray alloc] initWithObjects:
2849 SizeString([database_ fetcher].FetchNeeded()),
2850 SizeString([database_ fetcher].PartialPresent()),
2851 SizeString([database_ cache]->UsrSize()),
2854 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2858 - (NSString *) backButtonTitle {
2862 - (NSString *) leftButtonTitle {
2866 - (NSString *) _rightButtonTitle {
2867 return issues_ == nil ? @"Confirm" : nil;
2870 - (void) _leftButtonClicked {
2875 - (void) _rightButtonClicked {
2876 if (essential_ != nil)
2877 [essential_ popupAlertAnimated:YES];
2881 [delegate_ confirm];
2889 /* Progress Data {{{ */
2890 @interface ProgressData : NSObject {
2896 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2903 @implementation ProgressData
2905 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2906 if ((self = [super init]) != nil) {
2907 selector_ = selector;
2927 /* Progress View {{{ */
2928 @interface ProgressView : UIView <
2929 ConfigurationDelegate,
2932 _transient Database *database_;
2934 UIView *background_;
2935 UITransitionView *transition_;
2937 UINavigationBar *navbar_;
2938 UIProgressBar *progress_;
2939 UITextView *output_;
2940 UITextLabel *status_;
2941 UIPushButton *close_;
2944 SHA1SumValue springlist_;
2945 SHA1SumValue sandplate_;
2947 NSTimeInterval last_;
2950 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2952 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2953 - (void) setContentView:(UIView *)view;
2956 - (void) _retachThread;
2957 - (void) _detachNewThreadData:(ProgressData *)data;
2958 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2964 @protocol ProgressViewDelegate
2965 - (void) progressViewIsComplete:(ProgressView *)sender;
2968 @implementation ProgressView
2971 [transition_ setDelegate:nil];
2972 [navbar_ setDelegate:nil];
2975 if (background_ != nil)
2976 [background_ release];
2977 [transition_ release];
2980 [progress_ release];
2987 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2988 if (bootstrap_ && from == overlay_ && to == view_)
2992 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2993 if ((self = [super initWithFrame:frame]) != nil) {
2994 database_ = database;
2995 delegate_ = delegate;
2997 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2998 [transition_ setDelegate:self];
3000 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3003 [overlay_ setBackgroundColor:[UIColor blackColor]];
3005 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3006 [background_ setBackgroundColor:[UIColor blackColor]];
3007 [self addSubview:background_];
3010 [self addSubview:transition_];
3012 CGSize navsize = [UINavigationBar defaultSize];
3013 CGRect navrect = {{0, 0}, navsize};
3015 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3016 [overlay_ addSubview:navbar_];
3018 [navbar_ setBarStyle:1];
3019 [navbar_ setDelegate:self];
3021 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3022 [navbar_ pushNavigationItem:navitem];
3024 CGRect bounds = [overlay_ bounds];
3025 CGSize prgsize = [UIProgressBar defaultSize];
3028 (bounds.size.width - prgsize.width) / 2,
3029 bounds.size.height - prgsize.height - 20
3032 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3033 [progress_ setStyle:0];
3035 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3037 bounds.size.height - prgsize.height - 50,
3038 bounds.size.width - 20,
3042 [status_ setColor:[UIColor whiteColor]];
3043 [status_ setBackgroundColor:[UIColor clearColor]];
3045 [status_ setCentersHorizontally:YES];
3046 //[status_ setFont:font];
3048 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3050 navrect.size.height + 20,
3051 bounds.size.width - 20,
3052 bounds.size.height - navsize.height - 62 - navrect.size.height
3055 //[output_ setTextFont:@"Courier New"];
3056 [output_ setTextSize:12];
3058 [output_ setTextColor:[UIColor whiteColor]];
3059 [output_ setBackgroundColor:[UIColor clearColor]];
3061 [output_ setMarginTop:0];
3062 [output_ setAllowsRubberBanding:YES];
3063 [output_ setEditable:NO];
3065 [overlay_ addSubview:output_];
3067 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3069 bounds.size.height - prgsize.height - 50,
3070 bounds.size.width - 20,
3074 [close_ setAutosizesToFit:NO];
3075 [close_ setDrawsShadow:YES];
3076 [close_ setStretchBackground:YES];
3077 [close_ setEnabled:YES];
3079 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3080 [close_ setTitleFont:bold];
3082 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3083 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3084 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3088 - (void) setContentView:(UIView *)view {
3089 view_ = [view retain];
3092 - (void) resetView {
3093 [transition_ transition:6 toView:view_];
3096 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3097 NSString *context([sheet context]);
3099 if ([context isEqualToString:@"conffile"]) {
3100 FILE *input = [database_ input];
3104 fprintf(input, "N\n");
3108 fprintf(input, "Y\n");
3119 - (void) closeButtonPushed {
3128 [delegate_ suspendWithAnimation:YES];
3132 system("launchctl stop com.apple.SpringBoard");
3136 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3145 - (void) _retachThread {
3146 UINavigationItem *item = [navbar_ topItem];
3147 [item setTitle:@"Complete"];
3149 [overlay_ addSubview:close_];
3150 [progress_ removeFromSuperview];
3151 [status_ removeFromSuperview];
3153 [delegate_ progressViewIsComplete:self];
3156 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3157 MMap mmap(file, MMap::ReadOnly);
3159 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3160 if (!(sandplate_ == sha1.Result()))
3165 FileFd file(SpringBoard_, FileFd::ReadOnly);
3166 MMap mmap(file, MMap::ReadOnly);
3168 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3169 if (!(springlist_ == sha1.Result()))
3174 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3175 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3176 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3177 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3178 case 4: [close_ setTitle:@"Reboot Device"]; break;
3181 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3183 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3184 [cache autorelease];
3186 NSFileManager *manager = [NSFileManager defaultManager];
3187 NSError *error = nil;
3189 id system = [cache objectForKey:@"System"];
3194 if (stat(Cache_, &info) == -1)
3197 [system removeAllObjects];
3199 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3200 for (NSString *app in apps)
3201 if ([app hasSuffix:@".app"]) {
3202 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3203 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3204 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3206 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3207 [info setObject:path forKey:@"Path"];
3208 [info setObject:@"System" forKey:@"ApplicationType"];
3209 [system addInfoDictionary:info];
3215 [cache writeToFile:@Cache_ atomically:YES];
3217 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3219 if (chmod(Cache_, info.st_mode) == -1)
3223 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3226 notify_post("com.apple.mobile.application_installed");
3228 [delegate_ setStatusBarShowsProgress:NO];
3231 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3232 [[data target] performSelector:[data selector] withObject:[data object]];
3235 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3238 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3239 UINavigationItem *item = [navbar_ topItem];
3240 [item setTitle:title];
3242 [status_ setText:nil];
3243 [output_ setText:@""];
3244 [progress_ setProgress:0];
3247 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3249 [close_ removeFromSuperview];
3250 [overlay_ addSubview:progress_];
3251 [overlay_ addSubview:status_];
3253 [delegate_ setStatusBarShowsProgress:YES];
3257 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3258 MMap mmap(file, MMap::ReadOnly);
3260 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3261 sandplate_ = sha1.Result();
3265 FileFd file(SpringBoard_, FileFd::ReadOnly);
3266 MMap mmap(file, MMap::ReadOnly);
3268 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3269 springlist_ = sha1.Result();
3272 [transition_ transition:6 toView:overlay_];
3275 detachNewThreadSelector:@selector(_detachNewThreadData:)
3277 withObject:[[ProgressData alloc]
3278 initWithSelector:selector
3285 - (void) repairWithSelector:(SEL)selector {
3287 detachNewThreadSelector:selector
3294 - (void) setConfigurationData:(NSString *)data {
3296 performSelectorOnMainThread:@selector(_setConfigurationData:)
3302 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3303 Package *package = id == nil ? nil : [database_ packageWithName:id];
3305 UIActionSheet *sheet = [[[UIActionSheet alloc]
3306 initWithTitle:(package == nil ? id : [package name])
3307 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3308 defaultButtonIndex:0
3313 [sheet setBodyText:error];
3314 [sheet popupAlertAnimated:YES];
3317 - (void) setProgressTitle:(NSString *)title {
3319 performSelectorOnMainThread:@selector(_setProgressTitle:)
3325 - (void) setProgressPercent:(float)percent {
3327 performSelectorOnMainThread:@selector(_setProgressPercent:)
3328 withObject:[NSNumber numberWithFloat:percent]
3333 - (void) startProgress {
3334 last_ = [NSDate timeIntervalSinceReferenceDate];
3337 - (void) addProgressOutput:(NSString *)output {
3339 performSelectorOnMainThread:@selector(_addProgressOutput:)
3345 - (bool) isCancelling:(size_t)received {
3347 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3348 if (received_ != received) {
3349 received_ = received;
3351 } else if (now - last_ > 30)
3358 - (void) _setConfigurationData:(NSString *)data {
3359 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3361 _assert(conffile_r(data));
3363 NSString *ofile = conffile_r[1];
3364 //NSString *nfile = conffile_r[2];
3366 UIActionSheet *sheet = [[[UIActionSheet alloc]
3367 initWithTitle:@"Configuration Upgrade"
3368 buttons:[NSArray arrayWithObjects:
3369 @"Keep My Old Copy",
3370 @"Accept The New Copy",
3371 // XXX: @"See What Changed",
3373 defaultButtonIndex:0
3378 [sheet setBodyText:[NSString stringWithFormat:
3379 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3382 [sheet popupAlertAnimated:YES];
3385 - (void) _setProgressTitle:(NSString *)title {
3386 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3387 for (size_t i(0), e([words count]); i != e; ++i) {
3388 NSString *word([words objectAtIndex:i]);
3389 if (Package *package = [database_ packageWithName:word])
3390 [words replaceObjectAtIndex:i withObject:[package name]];
3393 [status_ setText:[words componentsJoinedByString:@" "]];
3396 - (void) _setProgressPercent:(NSNumber *)percent {
3397 [progress_ setProgress:[percent floatValue]];
3400 - (void) _addProgressOutput:(NSString *)output {
3401 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3402 CGSize size = [output_ contentSize];
3403 CGRect rect = {{0, size.height}, {size.width, 0}};
3404 [output_ scrollRectToVisible:rect animated:YES];
3407 - (BOOL) isRunning {
3414 /* Package Cell {{{ */
3415 @interface PackageCell : UISimpleTableCell {
3418 NSString *description_;
3422 UITextLabel *status_;
3426 - (PackageCell *) init;
3427 - (void) setPackage:(Package *)package;
3429 + (int) heightForPackage:(Package *)package;
3433 @implementation PackageCell
3435 - (void) clearPackage {
3446 if (description_ != nil) {
3447 [description_ release];
3451 if (source_ != nil) {
3456 if (badge_ != nil) {
3463 [self clearPackage];
3470 - (PackageCell *) init {
3471 if ((self = [super init]) != nil) {
3473 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3474 [status_ setBackgroundColor:[UIColor clearColor]];
3475 [status_ setFont:small];
3480 - (void) setPackage:(Package *)package {
3481 [self clearPackage];
3483 Source *source = [package source];
3484 NSString *section = [package simpleSection];
3486 icon_ = [[package icon] retain];
3488 name_ = [[package name] retain];
3489 description_ = [[package tagline] retain];
3491 NSString *label = nil;
3492 bool trusted = false;
3494 if (source != nil) {
3495 label = [source label];
3496 trusted = [source trusted];
3497 } else if ([[package id] isEqualToString:@"firmware"])
3500 label = @"Unknown/Local";
3502 NSString *from = [NSString stringWithFormat:@"from %@", label];
3504 if (section != nil && ![section isEqualToString:label])
3505 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3507 source_ = [from retain];
3509 if (NSString *purpose = [package primaryPurpose])
3510 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3511 badge_ = [badge_ retain];
3514 if (NSString *mode = [package mode]) {
3515 [badge_ setImage:[UIImage applicationImageNamed:
3516 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3519 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3520 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3521 } else if ([package half]) {
3522 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3523 [status_ setText:@"Package Damaged"];
3524 [status_ setColor:[UIColor redColor]];
3526 [badge_ setImage:nil];
3527 [status_ setText:nil];
3532 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3535 rect.size = [icon_ size];
3537 rect.size.width /= 2;
3538 rect.size.height /= 2;
3540 rect.origin.x = 25 - rect.size.width / 2;
3541 rect.origin.y = 25 - rect.size.height / 2;
3543 [icon_ drawInRect:rect];
3546 if (badge_ != nil) {
3547 CGSize size = [badge_ size];
3549 [badge_ drawAtPoint:CGPointMake(
3550 36 - size.width / 2,
3551 36 - size.height / 2
3560 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3561 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3565 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3567 [super drawContentInRect:rect selected:selected];
3570 + (int) heightForPackage:(Package *)package {
3571 NSString *tagline([package tagline]);
3572 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3574 if ([package hasMode] || [package half])
3583 /* Section Cell {{{ */
3584 @interface SectionCell : UISimpleTableCell {
3589 _UISwitchSlider *switch_;
3594 - (void) setSection:(Section *)section editing:(BOOL)editing;
3598 @implementation SectionCell
3600 - (void) clearSection {
3601 if (section_ != nil) {
3611 if (count_ != nil) {
3618 [self clearSection];
3625 if ((self = [super init]) != nil) {
3626 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3628 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3629 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3633 - (void) onSwitch:(id)sender {
3634 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3635 if (metadata == nil) {
3636 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3637 [Sections_ setObject:metadata forKey:section_];
3641 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3644 - (void) setSection:(Section *)section editing:(BOOL)editing {
3645 if (editing != editing_) {
3647 [switch_ removeFromSuperview];
3649 [self addSubview:switch_];
3653 [self clearSection];
3655 if (section == nil) {
3656 name_ = [@"All Packages" retain];
3659 section_ = [section name];
3660 if (section_ != nil)
3661 section_ = [section_ retain];
3662 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3663 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3666 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3670 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3671 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3678 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3680 CGSize size = [count_ sizeWithFont:Font14_];
3684 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3686 [super drawContentInRect:rect selected:selected];
3692 /* File Table {{{ */
3693 @interface FileTable : RVPage {
3694 _transient Database *database_;
3697 NSMutableArray *files_;
3701 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3702 - (void) setPackage:(Package *)package;
3706 @implementation FileTable
3709 if (package_ != nil)
3718 - (int) numberOfRowsInTable:(UITable *)table {
3719 return files_ == nil ? 0 : [files_ count];
3722 - (float) table:(UITable *)table heightForRow:(int)row {
3726 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3727 if (reusing == nil) {
3728 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3729 UIFont *font = [UIFont systemFontOfSize:16];
3730 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3732 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3736 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3740 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3741 if ((self = [super initWithBook:book]) != nil) {
3742 database_ = database;
3744 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3746 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3747 [self addSubview:list_];
3749 UITableColumn *column = [[[UITableColumn alloc]
3750 initWithTitle:@"Name"
3752 width:[self frame].size.width
3755 [list_ setDataSource:self];
3756 [list_ setSeparatorStyle:1];
3757 [list_ addTableColumn:column];
3758 [list_ setDelegate:self];
3759 [list_ setReusesTableCells:YES];
3763 - (void) setPackage:(Package *)package {
3764 if (package_ != nil) {
3765 [package_ autorelease];
3774 [files_ removeAllObjects];
3776 if (package != nil) {
3777 package_ = [package retain];
3778 name_ = [[package id] retain];
3780 if (NSArray *files = [package files])
3781 [files_ addObjectsFromArray:files];
3783 if ([files_ count] != 0) {
3784 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3785 [files_ removeObjectAtIndex:0];
3786 [files_ sortUsingSelector:@selector(compareByPath:)];
3788 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3789 [stack addObject:@"/"];
3791 for (int i(0), e([files_ count]); i != e; ++i) {
3792 NSString *file = [files_ objectAtIndex:i];
3793 while (![file hasPrefix:[stack lastObject]])
3794 [stack removeLastObject];
3795 NSString *directory = [stack lastObject];
3796 [stack addObject:[file stringByAppendingString:@"/"]];
3797 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3798 ([stack count] - 2) * 3, "",
3799 [file substringFromIndex:[directory length]]
3808 - (void) resetViewAnimated:(BOOL)animated {
3809 [list_ resetViewAnimated:animated];
3812 - (void) reloadData {
3813 [self setPackage:[database_ packageWithName:name_]];
3814 [self reloadButtons];
3817 - (NSString *) title {
3818 return @"Installed Files";
3821 - (NSString *) backButtonTitle {
3827 /* Package View {{{ */
3828 @interface PackageView : BrowserView {
3829 _transient Database *database_;
3832 NSMutableArray *buttons_;
3835 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3836 - (void) setPackage:(Package *)package;
3840 @implementation PackageView
3843 if (package_ != nil)
3851 - (void) _clickButtonWithName:(NSString *)name {
3852 if ([name isEqualToString:@"Install"])
3853 [delegate_ installPackage:package_];
3854 else if ([name isEqualToString:@"Reinstall"])
3855 [delegate_ installPackage:package_];
3856 else if ([name isEqualToString:@"Remove"])
3857 [delegate_ removePackage:package_];
3858 else if ([name isEqualToString:@"Upgrade"])
3859 [delegate_ installPackage:package_];
3860 else _assert(false);
3863 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3864 NSString *context([sheet context]);
3866 if ([context isEqualToString:@"modify"]) {
3867 int count = [buttons_ count];
3868 _assert(count != 0);
3869 _assert(button <= count + 1);
3871 if (count != button - 1)
3872 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3876 [super alertSheet:sheet buttonClicked:button];
3879 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3880 return [super webView:sender didFinishLoadForFrame:frame];
3883 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3884 [window setValue:package_ forKey:@"package"];
3885 [super webView:sender didClearWindowObject:window forFrame:frame];
3889 - (void) _rightButtonClicked {
3890 /*[super _rightButtonClicked];
3893 int count = [buttons_ count];
3894 _assert(count != 0);
3897 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3899 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3900 [buttons addObjectsFromArray:buttons_];
3901 [buttons addObject:@"Cancel"];
3903 [delegate_ slideUp:[[[UIActionSheet alloc]
3906 defaultButtonIndex:2
3914 - (NSString *) _rightButtonTitle {
3915 int count = [buttons_ count];
3916 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3919 - (NSString *) backButtonTitle {
3923 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3924 if ((self = [super initWithBook:book]) != nil) {
3925 database_ = database;
3926 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3930 - (void) setPackage:(Package *)package {
3931 if (package_ != nil) {
3932 [package_ autorelease];
3941 [buttons_ removeAllObjects];
3943 if (package != nil) {
3944 package_ = [package retain];
3945 name_ = [[package id] retain];
3947 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3949 if ([package_ source] == nil);
3950 else if ([package_ upgradableAndEssential:NO])
3951 [buttons_ addObject:@"Upgrade"];
3952 else if ([package_ installed] == nil)
3953 [buttons_ addObject:@"Install"];
3955 [buttons_ addObject:@"Reinstall"];
3956 if ([package_ installed] != nil)
3957 [buttons_ addObject:@"Remove"];
3965 - (void) reloadData {
3966 [self setPackage:[database_ packageWithName:name_]];
3967 [self reloadButtons];
3972 /* Package Table {{{ */
3973 @interface PackageTable : RVPage {
3974 _transient Database *database_;
3978 NSMutableArray *packages_;
3979 NSMutableArray *sections_;
3980 UISectionList *list_;
3983 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3985 - (void) setDelegate:(id)delegate;
3986 - (void) setObject:(id)object;
3988 - (void) reloadData;
3989 - (void) resetCursor;
3991 - (UISectionList *) list;
3993 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3997 @implementation PackageTable
4000 [list_ setDataSource:nil];
4005 [packages_ release];
4006 [sections_ release];
4011 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4012 return [sections_ count];
4015 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4016 return [[sections_ objectAtIndex:section] name];
4019 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4020 return [[sections_ objectAtIndex:section] row];
4023 - (int) numberOfRowsInTable:(UITable *)table {
4024 return [packages_ count];
4027 - (float) table:(UITable *)table heightForRow:(int)row {
4028 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4031 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4033 reusing = [[[PackageCell alloc] init] autorelease];
4034 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4038 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4042 - (void) tableRowSelected:(NSNotification *)notification {
4043 int row = [[notification object] selectedRow];
4047 Package *package = [packages_ objectAtIndex:row];
4048 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4049 [view setDelegate:delegate_];
4050 [view setPackage:package];
4051 [book_ pushPage:view];
4054 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4055 if ((self = [super initWithBook:book]) != nil) {
4056 database_ = database;
4057 title_ = [title retain];
4059 object_ = object == nil ? nil : [object retain];
4061 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4062 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4064 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4065 [list_ setDataSource:self];
4067 UITableColumn *column = [[[UITableColumn alloc]
4068 initWithTitle:@"Name"
4070 width:[self frame].size.width
4073 UITable *table = [list_ table];
4074 [table setSeparatorStyle:1];
4075 [table addTableColumn:column];
4076 [table setDelegate:self];
4077 [table setReusesTableCells:YES];
4079 [self addSubview:list_];
4082 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4083 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4087 - (void) setDelegate:(id)delegate {
4088 delegate_ = delegate;
4091 - (void) setObject:(id)object {
4097 object_ = [object retain];
4100 - (void) reloadData {
4101 NSArray *packages = [database_ packages];
4103 [packages_ removeAllObjects];
4104 [sections_ removeAllObjects];
4106 for (size_t i(0); i != [packages count]; ++i) {
4107 Package *package([packages objectAtIndex:i]);
4108 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4109 [packages_ addObject:package];
4112 Section *section = nil;
4114 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4115 Package *package = [packages_ objectAtIndex:offset];
4116 NSString *name = [package index];
4118 if (section == nil || ![[section name] isEqualToString:name]) {
4119 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4120 [sections_ addObject:section];
4123 [section addToCount];
4129 - (NSString *) title {
4133 - (void) resetViewAnimated:(BOOL)animated {
4134 [list_ resetViewAnimated:animated];
4137 - (void) resetCursor {
4138 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4141 - (UISectionList *) list {
4145 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4146 [list_ setShouldHideHeaderInShortLists:hide];
4152 /* Add Source View {{{ */
4153 @interface AddSourceView : RVPage {
4154 _transient Database *database_;
4157 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4161 @implementation AddSourceView
4163 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4164 if ((self = [super initWithBook:book]) != nil) {
4165 database_ = database;
4171 /* Source Cell {{{ */
4172 @interface SourceCell : UITableCell {
4175 NSString *description_;
4181 - (SourceCell *) initWithSource:(Source *)source;
4185 @implementation SourceCell
4190 [description_ release];
4195 - (SourceCell *) initWithSource:(Source *)source {
4196 if ((self = [super init]) != nil) {
4198 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4200 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4201 icon_ = [icon_ retain];
4203 origin_ = [[source name] retain];
4204 label_ = [[source uri] retain];
4205 description_ = [[source description] retain];
4209 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4211 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4218 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4222 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4226 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4228 [super drawContentInRect:rect selected:selected];
4233 /* Source Table {{{ */
4234 @interface SourceTable : RVPage {
4235 _transient Database *database_;
4236 UISectionList *list_;
4237 NSMutableArray *sources_;
4238 UIActionSheet *alert_;
4242 UIProgressHUD *hud_;
4245 //NSURLConnection *installer_;
4246 NSURLConnection *trivial_bz2_;
4247 NSURLConnection *trivial_gz_;
4248 //NSURLConnection *automatic_;
4253 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4257 @implementation SourceTable
4259 - (void) _deallocConnection:(NSURLConnection *)connection {
4260 if (connection != nil) {
4261 [connection cancel];
4262 //[connection setDelegate:nil];
4263 [connection release];
4268 [[list_ table] setDelegate:nil];
4269 [list_ setDataSource:nil];
4278 //[self _deallocConnection:installer_];
4279 [self _deallocConnection:trivial_gz_];
4280 [self _deallocConnection:trivial_bz2_];
4281 //[self _deallocConnection:automatic_];
4288 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4289 return offset_ == 0 ? 1 : 2;
4292 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4293 switch (section + (offset_ == 0 ? 1 : 0)) {
4294 case 0: return @"Entered by User";
4295 case 1: return @"Installed by Packages";
4303 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4304 switch (section + (offset_ == 0 ? 1 : 0)) {
4306 case 1: return offset_;
4314 - (int) numberOfRowsInTable:(UITable *)table {
4315 return [sources_ count];
4318 - (float) table:(UITable *)table heightForRow:(int)row {
4319 Source *source = [sources_ objectAtIndex:row];
4320 return [source description] == nil ? 56 : 73;
4323 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4324 Source *source = [sources_ objectAtIndex:row];
4325 // XXX: weird warning, stupid selectors ;P
4326 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4329 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4333 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4337 - (void) tableRowSelected:(NSNotification*)notification {
4338 UITable *table([list_ table]);
4339 int row([table selectedRow]);
4343 Source *source = [sources_ objectAtIndex:row];
4345 PackageTable *packages = [[[PackageTable alloc]
4348 title:[source label]
4349 filter:@selector(isVisibleInSource:)
4353 [packages setDelegate:delegate_];
4355 [book_ pushPage:packages];
4358 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4359 Source *source = [sources_ objectAtIndex:row];
4360 return [source record] != nil;
4363 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4364 [[list_ table] setDeleteConfirmationRow:row];
4367 - (void) table:(UITable *)table deleteRow:(int)row {
4368 Source *source = [sources_ objectAtIndex:row];
4369 [Sources_ removeObjectForKey:[source key]];
4370 [delegate_ syncData];
4373 - (void) _endConnection:(NSURLConnection *)connection {
4374 NSURLConnection **field = NULL;
4375 if (connection == trivial_bz2_)
4376 field = &trivial_bz2_;
4377 else if (connection == trivial_gz_)
4378 field = &trivial_gz_;
4379 _assert(field != NULL);
4380 [connection release];
4384 trivial_bz2_ == nil &&
4387 [delegate_ setStatusBarShowsProgress:NO];
4390 [hud_ removeFromSuperview];
4395 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4398 @"./", @"Distribution",
4399 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4401 [delegate_ syncData];
4402 } else if (error_ != nil) {
4403 UIActionSheet *sheet = [[[UIActionSheet alloc]
4404 initWithTitle:@"Verification Error"
4405 buttons:[NSArray arrayWithObjects:@"OK", nil]
4406 defaultButtonIndex:0
4411 [sheet setBodyText:[error_ localizedDescription]];
4412 [sheet popupAlertAnimated:YES];
4414 UIActionSheet *sheet = [[[UIActionSheet alloc]
4415 initWithTitle:@"Did not Find Repository"
4416 buttons:[NSArray arrayWithObjects:@"OK", nil]
4417 defaultButtonIndex:0
4422 [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."];
4423 [sheet popupAlertAnimated:YES];
4429 if (error_ != nil) {
4436 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4437 switch ([response statusCode]) {
4443 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4444 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4446 error_ = [error retain];
4447 [self _endConnection:connection];
4450 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4451 [self _endConnection:connection];
4454 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4455 NSMutableURLRequest *request = [NSMutableURLRequest
4456 requestWithURL:[NSURL URLWithString:href]
4457 cachePolicy:NSURLRequestUseProtocolCachePolicy
4458 timeoutInterval:20.0
4461 [request setHTTPMethod:method];
4463 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4466 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4467 NSString *context([sheet context]);
4469 if ([context isEqualToString:@"source"]) {
4472 NSString *href = [[sheet textField] text];
4474 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4476 if (![href hasSuffix:@"/"])
4477 href_ = [href stringByAppendingString:@"/"];
4480 href_ = [href_ retain];
4482 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4483 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4484 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4488 hud_ = [delegate_ addProgressHUD];
4489 [hud_ setText:@"Verifying URL"];
4500 } else if ([context isEqualToString:@"trivial"])
4502 else if ([context isEqualToString:@"urlerror"])
4506 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4507 if ((self = [super initWithBook:book]) != nil) {
4508 database_ = database;
4509 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4511 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4512 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4513 [list_ setShouldHideHeaderInShortLists:NO];
4515 [self addSubview:list_];
4516 [list_ setDataSource:self];
4518 UITableColumn *column = [[UITableColumn alloc]
4519 initWithTitle:@"Name"
4521 width:[self frame].size.width
4524 UITable *table = [list_ table];
4525 [table setSeparatorStyle:1];
4526 [table addTableColumn:column];
4527 [table setDelegate:self];
4531 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4532 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4536 - (void) reloadData {
4538 _assert(list.ReadMainList());
4540 [sources_ removeAllObjects];
4541 [sources_ addObjectsFromArray:[database_ sources]];
4543 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4546 int count = [sources_ count];
4547 for (offset_ = 0; offset_ != count; ++offset_) {
4548 Source *source = [sources_ objectAtIndex:offset_];
4549 if ([source record] == nil)
4556 - (void) resetViewAnimated:(BOOL)animated {
4557 [list_ resetViewAnimated:animated];
4560 - (void) _leftButtonClicked {
4561 /*[book_ pushPage:[[[AddSourceView alloc]
4566 UIActionSheet *sheet = [[[UIActionSheet alloc]
4567 initWithTitle:@"Enter Cydia/APT URL"
4568 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4569 defaultButtonIndex:0
4574 [sheet setNumberOfRows:1];
4576 [sheet addTextFieldWithValue:@"http://" label:@""];
4578 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4579 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4580 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4581 [traits setKeyboardType:UIKeyboardTypeURL];
4582 // XXX: UIReturnKeyDone
4583 [traits setReturnKeyType:UIReturnKeyNext];
4585 [sheet popupAlertAnimated:YES];
4588 - (void) _rightButtonClicked {
4589 UITable *table = [list_ table];
4590 BOOL editing = [table isRowDeletionEnabled];
4591 [table enableRowDeletion:!editing animated:YES];
4592 [book_ reloadButtonsForPage:self];
4595 - (NSString *) title {
4599 - (NSString *) leftButtonTitle {
4600 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4603 - (NSString *) rightButtonTitle {
4604 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4607 - (UINavigationButtonStyle) rightButtonStyle {
4608 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4614 /* Installed View {{{ */
4615 @interface InstalledView : RVPage {
4616 _transient Database *database_;
4617 PackageTable *packages_;
4621 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4625 @implementation InstalledView
4628 [packages_ release];
4632 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4633 if ((self = [super initWithBook:book]) != nil) {
4634 database_ = database;
4636 packages_ = [[PackageTable alloc]
4640 filter:@selector(isInstalledAndVisible:)
4641 with:[NSNumber numberWithBool:YES]
4644 [self addSubview:packages_];
4646 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4647 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4651 - (void) resetViewAnimated:(BOOL)animated {
4652 [packages_ resetViewAnimated:animated];
4655 - (void) reloadData {
4656 [packages_ reloadData];
4659 - (void) _rightButtonClicked {
4660 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4661 [packages_ reloadData];
4663 [book_ reloadButtonsForPage:self];
4666 - (NSString *) title {
4667 return @"Installed";
4670 - (NSString *) backButtonTitle {
4674 - (NSString *) rightButtonTitle {
4675 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4678 - (UINavigationButtonStyle) rightButtonStyle {
4679 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4682 - (void) setDelegate:(id)delegate {
4683 [super setDelegate:delegate];
4684 [packages_ setDelegate:delegate];
4691 @interface HomeView : BrowserView {
4696 @implementation HomeView
4698 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4699 NSString *context([sheet context]);
4701 if ([context isEqualToString:@"about"])
4704 [super alertSheet:sheet buttonClicked:button];
4707 - (void) _leftButtonClicked {
4708 UIActionSheet *sheet = [[[UIActionSheet alloc]
4709 initWithTitle:@"About Cydia Installer"
4710 buttons:[NSArray arrayWithObjects:@"Close", nil]
4711 defaultButtonIndex:0
4717 @"Copyright (C) 2008\n"
4718 "Jay Freeman (saurik)\n"
4719 "saurik@saurik.com\n"
4720 "http://www.saurik.com/\n"
4723 "http://www.theokorigroup.com/\n"
4725 "College of Creative Studies,\n"
4726 "University of California,\n"
4728 "http://www.ccs.ucsb.edu/"
4731 [sheet popupAlertAnimated:YES];
4734 - (NSString *) leftButtonTitle {
4740 /* Manage View {{{ */
4741 @interface ManageView : BrowserView {
4746 @implementation ManageView
4748 - (NSString *) title {
4752 - (void) _leftButtonClicked {
4753 [delegate_ askForSettings];
4756 - (NSString *) leftButtonTitle {
4761 - (NSString *) _rightButtonTitle {
4773 @interface WebView (Cydia)
4774 - (void) setScriptDebugDelegate:(id)delegate;
4775 - (void) _setFormDelegate:(id)delegate;
4776 - (void) _setUIKitDelegate:(id)delegate;
4777 - (void) setWebMailDelegate:(id)delegate;
4778 - (void) _setLayoutInterval:(float)interval;
4781 /* Indirect Delegate {{{ */
4782 @interface IndirectDelegate : NSProxy {
4783 _transient volatile id delegate_;
4786 - (void) setDelegate:(id)delegate;
4787 - (id) initWithDelegate:(id)delegate;
4790 @implementation IndirectDelegate
4792 - (void) setDelegate:(id)delegate {
4793 delegate_ = delegate;
4796 - (id) initWithDelegate:(id)delegate {
4797 delegate_ = delegate;
4801 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4802 if (delegate_ != nil)
4803 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4805 // XXX: I fucking hate Apple so very very bad
4806 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4809 - (void) forwardInvocation:(NSInvocation *)inv {
4810 SEL sel = [inv selector];
4811 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4812 [inv invokeWithTarget:delegate_];
4817 /* Browser Implementation {{{ */
4818 @implementation BrowserView
4821 #include "internals.h"
4825 if (challenge_ != nil)
4826 [challenge_ release];
4828 WebView *webview = [webview_ webView];
4829 [webview setFrameLoadDelegate:nil];
4830 [webview setResourceLoadDelegate:nil];
4831 [webview setUIDelegate:nil];
4832 [webview setScriptDebugDelegate:nil];
4833 [webview setPolicyDelegate:nil];
4835 [webview setDownloadDelegate:nil];
4837 [webview _setFormDelegate:nil];
4838 [webview _setUIKitDelegate:nil];
4839 [webview setWebMailDelegate:nil];
4840 [webview setEditingDelegate:nil];
4842 [webview_ setDelegate:nil];
4843 [webview_ setGestureDelegate:nil];
4845 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4850 [webview_ removeFromSuperview];
4851 [Documents_ addObject:[webview_ autorelease]];
4856 [indirect_ setDelegate:nil];
4857 [indirect_ release];
4859 [scroller_ setDelegate:nil];
4865 if (function_ != nil)
4866 [function_ release];
4868 [scroller_ release];
4869 [indicator_ release];
4870 if (confirm_ != nil)
4877 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4878 [self loadRequest:[NSURLRequest
4881 timeoutInterval:30.0
4885 - (void) loadURL:(NSURL *)url {
4886 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4889 - (NSMutableURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4890 NSMutableURLRequest *copy = [request mutableCopy];
4892 if (Machine_ != NULL)
4893 [copy setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4894 if (UniqueID_ != nil)
4895 [copy setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4898 [copy setValue:Role_ forHTTPHeaderField:@"X-Role"];
4903 - (void) loadRequest:(NSURLRequest *)request {
4905 [webview_ loadRequest:request];
4908 - (void) reloadURL {
4909 NSLog(@"rlu:%@", request_);
4910 if (request_ == nil)
4913 if ([request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil)
4914 [webview_ loadRequest:request_];
4916 UIActionSheet *sheet = [[[UIActionSheet alloc]
4917 initWithTitle:@"Are you sure you want to submit this form again?"
4918 buttons:[NSArray arrayWithObjects:@"Cancel", @"Submit", nil]
4919 defaultButtonIndex:0
4924 [sheet setNumberOfRows:1];
4925 [sheet popupAlertAnimated:YES];
4929 - (WebView *) webView {
4930 return [webview_ webView];
4933 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4934 [scroller_ setContentSize:frame.size];
4937 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4938 [self view:sender didSetFrame:frame];
4941 - (void) pushPage:(RVPage *)page {
4942 [self setBackButtonTitle:title_];
4943 [page setDelegate:delegate_];
4944 [book_ pushPage:page];
4947 - (BOOL) getSpecial:(NSURL *)url {
4948 NSString *href([url absoluteString]);
4949 NSString *scheme([[url scheme] lowercaseString]);
4953 if ([href hasPrefix:@"apptapp://package/"])
4954 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4955 else if ([scheme isEqualToString:@"cydia"]) {
4956 page = [delegate_ pageForURL:url hasTag:NULL];
4959 } else if (![scheme isEqualToString:@"apptapp"])
4963 [self pushPage:page];
4967 - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4968 UIActionSheet *sheet = [[[UIActionSheet alloc]
4970 buttons:[NSArray arrayWithObjects:@"OK", nil]
4971 defaultButtonIndex:0
4976 [sheet setBodyText:message];
4977 [sheet popupAlertAnimated:YES];
4980 - (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4981 UIActionSheet *sheet = [[[UIActionSheet alloc]
4983 buttons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil]
4984 defaultButtonIndex:0
4989 [sheet setNumberOfRows:1];
4990 [sheet setBodyText:message];
4991 [sheet popupAlertAnimated:YES];
4993 NSRunLoop *loop([NSRunLoop currentRunLoop]);
4994 NSDate *future([NSDate distantFuture]);
4996 while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
4998 NSNumber *confirm([confirm_ autorelease]);
5000 return [confirm boolValue];
5003 /* Web Scripting {{{ */
5004 + (NSString *) webScriptNameForSelector:(SEL)selector {
5005 if (selector == @selector(getPackageById:))
5006 return @"getPackageById";
5007 else if (selector == @selector(setButtonTitle:withStyle:toFunction:))
5008 return @"setButtonTitle";
5009 else if (selector == @selector(supports:))
5015 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
5016 return [self webScriptNameForSelector:selector] == nil;
5019 - (BOOL) supports:(NSString *)feature {
5020 return [feature isEqualToString:@"window.open"];
5023 - (Package *) getPackageById:(NSString *)id {
5024 return [[Database sharedInstance] packageWithName:id];
5027 - (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
5029 [button_ autorelease];
5030 button_ = button == nil ? nil : [button retain];
5033 [style_ autorelease];
5034 style_ = style == nil ? nil : [style retain];
5036 if (function_ != nil)
5037 [function_ autorelease];
5038 function_ = function == nil ? nil : [function retain];
5042 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
5043 [window setValue:self forKey:@"cydia"];
5046 - (void) webView:(WebView *)sender unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame {
5047 NSLog(@"err:%@", error);
5050 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
5051 if (NSURL *url = [request URL]) {
5052 if (name != nil && [name isEqualToString:@"_open"])
5053 [delegate_ openURL:url];
5055 NSLog(@"win:%@:%@", url, [action description]);
5056 if (![self getSpecial:url]) {
5057 NSString *scheme([[url scheme] lowercaseString]);
5058 if ([scheme isEqualToString:@"mailto"])
5059 [delegate_ openMailToURL:url];
5068 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
5069 if ([WebView canShowMIMEType:type])
5072 // XXX: handle more mime types!
5074 if (frame == [webView mainFrame])
5075 [UIApp openURL:[request URL]];
5079 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
5080 if (request == nil) ignore: {
5085 NSURL *url([request URL]);
5087 if (url == nil) use: {
5088 if ([frame parentFrame] == nil) {
5089 if (request_ != nil)
5090 [request_ autorelease];
5091 request_ = [request retain];
5092 NSLog(@"dpn:%@", request_);
5099 else NSLog(@"nav:%@:%@", url, [action description]);
5102 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
5105 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
5106 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
5109 [UIApp openURL:url];
5113 int store(_not(int));
5114 if (NSURL *itms = [url itmsURL:&store]) {
5115 NSLog(@"itms#%@#%u#%@", url, store, itms);
5117 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
5118 store == 2 && [capability containsObject:@"com.apple.AppStore"]
5125 NSString *scheme([[url scheme] lowercaseString]);
5127 if ([scheme isEqualToString:@"tel"]) {
5128 // XXX: intelligence
5132 if ([scheme isEqualToString:@"mailto"]) {
5133 [delegate_ openMailToURL:url];
5137 if ([self getSpecial:url])
5139 else if ([WebView _canHandleRequest:request])
5141 else if ([url isSpringboardHandledURL])
5147 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
5148 //lprintf("Status:%s\n", [text UTF8String]);
5151 - (void) _pushPage {
5155 [book_ pushPage:self];
5158 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5159 NSString *context([sheet context]);
5161 if ([context isEqualToString:@"alert"])
5163 else if ([context isEqualToString:@"confirm"]) {
5166 confirm_ = [NSNumber numberWithBool:YES];
5170 confirm_ = [NSNumber numberWithBool:NO];
5175 } else if ([context isEqualToString:@"challenge"]) {
5176 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
5180 NSString *username([[sheet textFieldAtIndex:0] text]);
5181 NSString *password([[sheet textFieldAtIndex:1] text]);
5183 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
5185 [sender useCredential:credential forAuthenticationChallenge:challenge_];
5189 [sender cancelAuthenticationChallenge:challenge_];
5196 [challenge_ release];
5200 } else if ([context isEqualToString:@"submit"]) {
5206 if (request_ != nil)
5207 [webview_ loadRequest:request_];
5218 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
5219 challenge_ = [challenge retain];
5221 NSURLProtectionSpace *space([challenge protectionSpace]);
5222 NSString *realm([space realm]);
5226 UIActionSheet *sheet = [[[UIActionSheet alloc]
5228 buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
5229 defaultButtonIndex:0
5231 context:@"challenge"
5234 [sheet setNumberOfRows:1];
5236 [sheet addTextFieldWithValue:@"" label:@"username"];
5237 [sheet addTextFieldWithValue:@"" label:@"password"];
5239 UITextField *username([sheet textFieldAtIndex:0]); {
5240 UITextInputTraits *traits([username textInputTraits]);
5241 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5242 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5243 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5244 [traits setReturnKeyType:UIReturnKeyNext];
5247 UITextField *password([sheet textFieldAtIndex:1]); {
5248 UITextInputTraits *traits([password textInputTraits]);
5249 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5250 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5251 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5252 // XXX: UIReturnKeyDone
5253 [traits setReturnKeyType:UIReturnKeyNext];
5254 [traits setSecureTextEntry:YES];
5257 [sheet popupAlertAnimated:YES];
5260 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
5261 NSURL *url = [request URL];
5262 if ([self getSpecial:url])
5265 return [self _addHeadersToRequest:request];
5268 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
5269 [self setBackButtonTitle:title_];
5271 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
5272 [browser setDelegate:delegate_];
5275 [browser loadRequest:request];
5276 [book_ pushPage:browser];
5279 return [browser webView];
5282 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
5283 return [self _createWebViewWithRequest:request pushed:(request != nil)];
5286 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
5287 return [self _createWebViewWithRequest:request pushed:YES];
5290 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
5291 if ([frame parentFrame] != nil)
5294 title_ = [title retain];
5295 [book_ reloadTitleForPage:self];
5298 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
5299 if ([frame parentFrame] != nil)
5304 [self reloadButtons];
5306 if (title_ != nil) {
5311 if (button_ != nil) {
5316 if (style_ != nil) {
5321 if (function_ != nil) {
5322 [function_ release];
5326 [book_ reloadTitleForPage:self];
5328 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
5330 CGRect webrect = [scroller_ bounds];
5331 webrect.size.height = 0;
5332 [webview_ setFrame:webrect];
5335 - (void) _finishLoading {
5338 [self reloadButtons];
5346 - (void) reloadButtons {
5347 if ([self _loading])
5348 [indicator_ startAnimation];
5350 [indicator_ stopAnimation];
5351 [super reloadButtons];
5354 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5355 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5358 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5359 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5362 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5363 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5366 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5367 return [webview_ webView:sender didCommitLoadForFrame:frame];
5370 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5371 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5374 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5375 if ([frame parentFrame] == nil) {
5376 [self _finishLoading];
5378 if (DOMDocument *document = [frame DOMDocument])
5379 if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
5380 for (DOMHTMLBodyElement *body in bodies) {
5381 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
5383 bool colored(false);
5385 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
5386 DOMRGBColor *rgb([color getRGBColorValue]);
5388 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
5389 NSLog(@"alpha:%g", alpha);
5394 [scroller_ setBackgroundColor:[UIColor
5395 colorWithRed:([[rgb red] getFloatValue:DOM_CSS_NUMBER] / 255)
5396 green:([[rgb green] getFloatValue:DOM_CSS_NUMBER] / 255)
5397 blue:([[rgb blue] getFloatValue:DOM_CSS_NUMBER] / 255)
5404 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
5409 return [webview_ webView:sender didFinishLoadForFrame:frame];
5412 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5413 if ([frame parentFrame] != nil)
5415 [self _finishLoading];
5417 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5418 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5419 [[error localizedDescription] stringByAddingPercentEscapes]
5423 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5425 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5429 - (id) initWithBook:(RVBook *)book {
5430 if ((self = [super initWithBook:book]) != nil) {
5433 struct CGRect bounds = [self bounds];
5435 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5436 [self addSubview:scroller_];
5438 [scroller_ setShowBackgroundShadow:NO];
5439 [scroller_ setFixedBackgroundPattern:YES];
5440 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
5442 [scroller_ setScrollingEnabled:YES];
5443 [scroller_ setAdjustForContentSizeChange:YES];
5444 [scroller_ setClipsSubviews:YES];
5445 [scroller_ setAllowsRubberBanding:YES];
5446 [scroller_ setScrollDecelerationFactor:0.99];
5447 [scroller_ setDelegate:self];
5449 CGRect webrect = [scroller_ bounds];
5450 webrect.size.height = 0;
5455 webview_ = [Documents_ lastObject];
5456 if (webview_ != nil) {
5457 webview_ = [webview_ retain];
5458 webview = [webview_ webView];
5459 [Documents_ removeLastObject];
5460 [webview_ setFrame:webrect];
5465 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5466 webview = [webview_ webView];
5468 // XXX: this is terribly (too?) expensive
5469 [webview_ setDrawsBackground:NO];
5471 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5473 [webview_ setAllowsMessaging:YES];
5475 [webview_ setTilingEnabled:YES];
5476 [webview_ setDrawsGrid:NO];
5477 [webview_ setLogsTilingChanges:NO];
5478 [webview_ setTileMinificationFilter:kCAFilterNearest];
5479 [webview_ setDetectsPhoneNumbers:NO];
5480 [webview_ setAutoresizes:YES];
5482 [webview_ setMinimumScale:0.25f forDocumentTypes:0x10];
5483 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10];
5484 [webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
5486 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2];
5488 [webview_ setMinimumScale:1.0f forDocumentTypes:0x8];
5489 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8];
5490 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8];
5492 [webview_ _setDocumentType:0x4];
5494 [webview_ setZoomsFocusedFormControl:YES];
5495 [webview_ setContentsPosition:7];
5496 [webview_ setEnabledGestures:0xa];
5497 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
5498 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
5500 [webview_ setSmoothsFonts:YES];
5502 [webview _setUsesLoaderCache:YES];
5503 [webview setGroupName:@"Cydia"];
5504 [webview _setLayoutInterval:0];
5507 [webview_ setDelegate:self];
5508 [webview_ setGestureDelegate:self];
5509 [scroller_ addSubview:webview_];
5511 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5513 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5514 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5515 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5517 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5518 NSString *application = package == nil ? @"Cydia" : [NSString
5519 stringWithFormat:@"Cydia/%@",
5521 ]; [webview setApplicationNameForUserAgent:application];
5523 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5525 [webview setFrameLoadDelegate:self];
5526 [webview setResourceLoadDelegate:indirect_];
5527 [webview setUIDelegate:self];
5528 [webview setScriptDebugDelegate:self];
5529 [webview setPolicyDelegate:self];
5531 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5532 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5536 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5537 [webview_ redrawScaledDocument];
5540 - (void) _rightButtonClicked {
5541 if (function_ == nil) {
5545 [delegate_ clearFirstResponder];
5546 JSObjectRef function([function_ JSObject]);
5547 JSGlobalContextRef context([[[webview_ webView] mainFrame] globalContext]);
5548 JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
5552 - (NSString *) _rightButtonTitle {
5553 return button_ != nil ? button_ : @"Reload";
5556 - (NSString *) rightButtonTitle {
5557 return [self _loading] ? @"" : [self _rightButtonTitle];
5560 - (UINavigationButtonStyle) rightButtonStyle {
5561 if (style_ == nil) normal:
5562 return UINavigationButtonStyleNormal;
5563 else if ([style_ isEqualToString:@"Normal"])
5564 return UINavigationButtonStyleNormal;
5565 else if ([style_ isEqualToString:@"Back"])
5566 return UINavigationButtonStyleBack;
5567 else if ([style_ isEqualToString:@"Highlighted"])
5568 return UINavigationButtonStyleHighlighted;
5569 else if ([style_ isEqualToString:@"Destructive"])
5570 return UINavigationButtonStyleDestructive;
5574 - (NSString *) title {
5575 return title_ == nil ? @"Loading" : title_;
5578 - (NSString *) backButtonTitle {
5582 - (void) setPageActive:(BOOL)active {
5584 [indicator_ removeFromSuperview];
5586 [[book_ navigationBar] addSubview:indicator_];
5589 - (void) resetViewAnimated:(BOOL)animated {
5592 - (void) setPushed:(bool)pushed {
5599 /* Cydia Book {{{ */
5600 @interface CYBook : RVBook <
5603 _transient Database *database_;
5604 UINavigationBar *overlay_;
5605 UINavigationBar *underlay_;
5606 UIProgressIndicator *indicator_;
5607 UITextLabel *prompt_;
5608 UIProgressBar *progress_;
5609 UINavigationButton *cancel_;
5612 NSTimeInterval last_;
5615 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5621 @implementation CYBook
5625 [indicator_ release];
5627 [progress_ release];
5632 - (NSString *) getTitleForPage:(RVPage *)page {
5633 return Simplify([super getTitleForPage:page]);
5641 [UIView beginAnimations:nil context:NULL];
5643 CGRect ovrframe = [overlay_ frame];
5644 ovrframe.origin.y = 0;
5645 [overlay_ setFrame:ovrframe];
5647 CGRect barframe = [navbar_ frame];
5648 barframe.origin.y += ovrframe.size.height;
5649 [navbar_ setFrame:barframe];
5651 CGRect trnframe = [transition_ frame];
5652 trnframe.origin.y += ovrframe.size.height;
5653 trnframe.size.height -= ovrframe.size.height;
5654 [transition_ setFrame:trnframe];
5656 [UIView endAnimations];
5658 [indicator_ startAnimation];
5659 [prompt_ setText:@"Updating Database"];
5660 [progress_ setProgress:0];
5663 last_ = [NSDate timeIntervalSinceReferenceDate];
5665 [overlay_ addSubview:cancel_];
5668 detachNewThreadSelector:@selector(_update)
5677 [indicator_ stopAnimation];
5679 [UIView beginAnimations:nil context:NULL];
5681 CGRect ovrframe = [overlay_ frame];
5682 ovrframe.origin.y = -ovrframe.size.height;
5683 [overlay_ setFrame:ovrframe];
5685 CGRect barframe = [navbar_ frame];
5686 barframe.origin.y -= ovrframe.size.height;
5687 [navbar_ setFrame:barframe];
5689 CGRect trnframe = [transition_ frame];
5690 trnframe.origin.y -= ovrframe.size.height;
5691 trnframe.size.height += ovrframe.size.height;
5692 [transition_ setFrame:trnframe];
5694 [UIView commitAnimations];
5696 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5699 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5700 if ((self = [super initWithFrame:frame]) != nil) {
5701 database_ = database;
5703 CGRect ovrrect = [navbar_ bounds];
5704 ovrrect.size.height = [UINavigationBar defaultSize].height;
5705 ovrrect.origin.y = -ovrrect.size.height;
5707 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5708 [self addSubview:overlay_];
5710 ovrrect.origin.y = frame.size.height;
5711 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5712 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5713 [self addSubview:underlay_];
5715 [overlay_ setBarStyle:1];
5716 [underlay_ setBarStyle:1];
5718 int barstyle = [overlay_ _barStyle:NO];
5719 bool ugly = barstyle == 0;
5721 UIProgressIndicatorStyle style = ugly ?
5722 UIProgressIndicatorStyleMediumBrown :
5723 UIProgressIndicatorStyleMediumWhite;
5725 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5726 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5727 CGRect indrect = {{indoffset, indoffset}, indsize};
5729 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5730 [indicator_ setStyle:style];
5731 [overlay_ addSubview:indicator_];
5733 CGSize prmsize = {215, indsize.height + 4};
5736 indoffset * 2 + indsize.width,
5740 unsigned(ovrrect.size.height - prmsize.height) / 2
5743 UIFont *font = [UIFont systemFontOfSize:15];
5745 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5747 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5748 [prompt_ setBackgroundColor:[UIColor clearColor]];
5749 [prompt_ setFont:font];
5751 [overlay_ addSubview:prompt_];
5753 CGSize prgsize = {75, 100};
5756 ovrrect.size.width - prgsize.width - 10,
5757 (ovrrect.size.height - prgsize.height) / 2
5760 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5761 [progress_ setStyle:0];
5762 [overlay_ addSubview:progress_];
5764 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5765 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5767 CGRect frame = [cancel_ frame];
5768 frame.size.width = 65;
5769 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5770 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5771 [cancel_ setFrame:frame];
5773 [cancel_ setBarStyle:barstyle];
5777 - (void) _onCancel {
5779 [cancel_ removeFromSuperview];
5782 - (void) _update { _pooled
5784 status.setDelegate(self);
5786 [database_ updateWithStatus:status];
5789 performSelectorOnMainThread:@selector(_update_)
5795 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5796 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5799 - (void) setProgressTitle:(NSString *)title {
5801 performSelectorOnMainThread:@selector(_setProgressTitle:)
5807 - (void) setProgressPercent:(float)percent {
5809 performSelectorOnMainThread:@selector(_setProgressPercent:)
5810 withObject:[NSNumber numberWithFloat:percent]
5815 - (void) startProgress {
5818 - (void) addProgressOutput:(NSString *)output {
5820 performSelectorOnMainThread:@selector(_addProgressOutput:)
5826 - (bool) isCancelling:(size_t)received {
5827 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5828 if (received_ != received) {
5829 received_ = received;
5831 } else if (now - last_ > 15)
5836 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5840 - (void) _setProgressTitle:(NSString *)title {
5841 [prompt_ setText:title];
5844 - (void) _setProgressPercent:(NSNumber *)percent {
5845 [progress_ setProgress:[percent floatValue]];
5848 - (void) _addProgressOutput:(NSString *)output {
5853 /* Cydia:// Protocol {{{ */
5854 @interface CydiaURLProtocol : NSURLProtocol {
5859 @implementation CydiaURLProtocol
5861 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5862 NSURL *url([request URL]);
5865 NSString *scheme([[url scheme] lowercaseString]);
5866 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5871 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5875 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5876 id<NSURLProtocolClient> client([self client]);
5877 NSData *data(UIImagePNGRepresentation(icon));
5879 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5880 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5881 [client URLProtocol:self didLoadData:data];
5882 [client URLProtocolDidFinishLoading:self];
5885 - (void) startLoading {
5886 id<NSURLProtocolClient> client([self client]);
5887 NSURLRequest *request([self request]);
5889 NSURL *url([request URL]);
5890 NSString *href([url absoluteString]);
5892 NSString *path([href substringFromIndex:8]);
5893 NSRange slash([path rangeOfString:@"/"]);
5896 if (slash.location == NSNotFound) {
5900 command = [path substringToIndex:slash.location];
5901 path = [path substringFromIndex:(slash.location + 1)];
5904 Database *database([Database sharedInstance]);
5906 if ([command isEqualToString:@"package-icon"]) {
5909 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5910 Package *package([database packageWithName:path]);
5913 UIImage *icon([package icon]);
5914 [self _returnPNGWithImage:icon forRequest:request];
5915 } else if ([command isEqualToString:@"source-icon"]) {
5918 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5919 NSString *source(Simplify(path));
5920 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5922 icon = [UIImage applicationImageNamed:@"unknown.png"];
5923 [self _returnPNGWithImage:icon forRequest:request];
5924 } else if ([command isEqualToString:@"uikit-image"]) {
5927 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5928 UIImage *icon(_UIImageWithName(path));
5929 [self _returnPNGWithImage:icon forRequest:request];
5930 } else if ([command isEqualToString:@"section-icon"]) {
5933 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5934 NSString *section(Simplify(path));
5935 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5937 icon = [UIImage applicationImageNamed:@"unknown.png"];
5938 [self _returnPNGWithImage:icon forRequest:request];
5940 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5944 - (void) stopLoading {
5950 /* Install View {{{ */
5951 @interface InstallView : RVPage {
5952 _transient Database *database_;
5953 NSMutableArray *sections_;
5954 NSMutableArray *filtered_;
5955 UITransitionView *transition_;
5961 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5962 - (void) reloadData;
5967 @implementation InstallView
5970 [list_ setDataSource:nil];
5971 [list_ setDelegate:nil];
5973 [sections_ release];
5974 [filtered_ release];
5975 [transition_ release];
5977 [accessory_ release];
5981 - (int) numberOfRowsInTable:(UITable *)table {
5982 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5985 - (float) table:(UITable *)table heightForRow:(int)row {
5989 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5991 reusing = [[[SectionCell alloc] init] autorelease];
5992 [(SectionCell *)reusing setSection:(editing_ ?
5993 [sections_ objectAtIndex:row] :
5994 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5995 ) editing:editing_];
5999 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
6003 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
6007 - (void) tableRowSelected:(NSNotification *)notification {
6008 int row = [[notification object] selectedRow];
6019 title = @"All Packages";
6021 section = [filtered_ objectAtIndex:(row - 1)];
6022 name = [section name];
6028 title = @"(No Section)";
6032 PackageTable *table = [[[PackageTable alloc]
6036 filter:@selector(isVisiblyUninstalledInSection:)
6040 [table setDelegate:delegate_];
6042 [book_ pushPage:table];
6045 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6046 if ((self = [super initWithBook:book]) != nil) {
6047 database_ = database;
6049 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6050 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
6052 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
6053 [self addSubview:transition_];
6055 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
6056 [transition_ transition:0 toView:list_];
6058 UITableColumn *column = [[[UITableColumn alloc]
6059 initWithTitle:@"Name"
6061 width:[self frame].size.width
6064 [list_ setDataSource:self];
6065 [list_ setSeparatorStyle:1];
6066 [list_ addTableColumn:column];
6067 [list_ setDelegate:self];
6068 [list_ setReusesTableCells:YES];
6072 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6073 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6077 - (void) reloadData {
6078 NSArray *packages = [database_ packages];
6080 [sections_ removeAllObjects];
6081 [filtered_ removeAllObjects];
6083 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
6084 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
6087 for (size_t i(0); i != [packages count]; ++i) {
6088 Package *package([packages objectAtIndex:i]);
6089 NSString *name([package section]);
6092 Section *section([sections objectForKey:name]);
6093 if (section == nil) {
6094 section = [[[Section alloc] initWithName:name] autorelease];
6095 [sections setObject:section forKey:name];
6099 if ([package valid] && [package installed] == nil && [package visible])
6100 [filtered addObject:package];
6104 [sections_ addObjectsFromArray:[sections allValues]];
6105 [sections_ sortUsingSelector:@selector(compareByName:)];
6108 [filtered sortUsingSelector:@selector(compareBySection:)];
6111 Section *section = nil;
6112 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
6113 Package *package = [filtered objectAtIndex:offset];
6114 NSString *name = [package section];
6116 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
6117 section = name == nil ?
6118 [[[Section alloc] initWithName:nil] autorelease] :
6119 [sections objectForKey:name];
6120 [filtered_ addObject:section];
6123 [section addToCount];
6131 - (void) resetView {
6133 [self _rightButtonClicked];
6136 - (void) resetViewAnimated:(BOOL)animated {
6137 [list_ resetViewAnimated:animated];
6140 - (void) _rightButtonClicked {
6141 if ((editing_ = !editing_))
6144 [delegate_ updateData];
6145 [book_ reloadTitleForPage:self];
6146 [book_ reloadButtonsForPage:self];
6149 - (NSString *) title {
6150 return editing_ ? @"Section Visibility" : @"Install by Section";
6153 - (NSString *) backButtonTitle {
6157 - (NSString *) rightButtonTitle {
6158 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
6161 - (UINavigationButtonStyle) rightButtonStyle {
6162 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
6165 - (UIView *) accessoryView {
6171 /* Changes View {{{ */
6172 @interface ChangesView : RVPage {
6173 _transient Database *database_;
6174 NSMutableArray *packages_;
6175 NSMutableArray *sections_;
6176 UISectionList *list_;
6180 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6181 - (void) reloadData;
6185 @implementation ChangesView
6188 [[list_ table] setDelegate:nil];
6189 [list_ setDataSource:nil];
6191 [packages_ release];
6192 [sections_ release];
6197 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
6198 return [sections_ count];
6201 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
6202 return [[sections_ objectAtIndex:section] name];
6205 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
6206 return [[sections_ objectAtIndex:section] row];
6209 - (int) numberOfRowsInTable:(UITable *)table {
6210 return [packages_ count];
6213 - (float) table:(UITable *)table heightForRow:(int)row {
6214 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
6217 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
6219 reusing = [[[PackageCell alloc] init] autorelease];
6220 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
6224 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
6228 - (void) tableRowSelected:(NSNotification *)notification {
6229 int row = [[notification object] selectedRow];
6232 Package *package = [packages_ objectAtIndex:row];
6233 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6234 [view setDelegate:delegate_];
6235 [view setPackage:package];
6236 [book_ pushPage:view];
6239 - (void) _leftButtonClicked {
6240 [(CYBook *)book_ update];
6241 [self reloadButtons];
6244 - (void) _rightButtonClicked {
6245 [delegate_ distUpgrade];
6248 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6249 if ((self = [super initWithBook:book]) != nil) {
6250 database_ = database;
6252 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
6253 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6255 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
6256 [self addSubview:list_];
6258 [list_ setShouldHideHeaderInShortLists:NO];
6259 [list_ setDataSource:self];
6260 //[list_ setSectionListStyle:1];
6262 UITableColumn *column = [[[UITableColumn alloc]
6263 initWithTitle:@"Name"
6265 width:[self frame].size.width
6268 UITable *table = [list_ table];
6269 [table setSeparatorStyle:1];
6270 [table addTableColumn:column];
6271 [table setDelegate:self];
6272 [table setReusesTableCells:YES];
6276 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6277 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6281 - (void) reloadData {
6282 NSArray *packages = [database_ packages];
6284 [packages_ removeAllObjects];
6285 [sections_ removeAllObjects];
6288 for (size_t i(0); i != [packages count]; ++i) {
6289 Package *package([packages objectAtIndex:i]);
6292 [package installed] == nil && [package valid] && [package visible] ||
6293 [package upgradableAndEssential:NO]
6295 [packages_ addObject:package];
6299 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
6302 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
6303 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
6304 Section *section = nil;
6308 bool unseens = false;
6310 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
6313 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6314 Package *package = [packages_ objectAtIndex:offset];
6316 if (![package upgradableAndEssential:YES]) {
6318 NSDate *seen = [package seen];
6320 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6323 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
6324 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6325 [sections_ addObject:section];
6329 [section addToCount];
6330 } else if ([package ignored])
6331 [ignored addToCount];
6334 [upgradable addToCount];
6339 CFRelease(formatter);
6342 Section *last = [sections_ lastObject];
6343 size_t count = [last count];
6344 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6345 [sections_ removeLastObject];
6348 if ([ignored count] != 0)
6349 [sections_ insertObject:ignored atIndex:0];
6351 [sections_ insertObject:upgradable atIndex:0];
6354 [self reloadButtons];
6357 - (void) resetViewAnimated:(BOOL)animated {
6358 [list_ resetViewAnimated:animated];
6361 - (NSString *) leftButtonTitle {
6362 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6365 - (NSString *) rightButtonTitle {
6366 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
6369 - (NSString *) title {
6375 /* Search View {{{ */
6376 @protocol SearchViewDelegate
6377 - (void) showKeyboard:(BOOL)show;
6380 @interface SearchView : RVPage {
6382 UISearchField *field_;
6383 UITransitionView *transition_;
6384 PackageTable *table_;
6385 UIPreferencesTable *advanced_;
6391 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6392 - (void) reloadData;
6396 @implementation SearchView
6399 [field_ setDelegate:nil];
6401 [accessory_ release];
6403 [transition_ release];
6405 [advanced_ release];
6410 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6414 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6416 case 0: return @"Advanced Search (Coming Soon!)";
6418 default: _assert(false);
6422 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6426 default: _assert(false);
6430 - (void) _showKeyboard:(BOOL)show {
6431 CGSize keysize = [UIKeyboard defaultSize];
6432 CGRect keydown = [book_ pageBounds];
6433 CGRect keyup = keydown;
6434 keyup.size.height -= keysize.height - ButtonBarHeight_;
6436 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6438 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6439 [animation setSignificantRectFields:8];
6442 [animation setStartFrame:keydown];
6443 [animation setEndFrame:keyup];
6445 [animation setStartFrame:keyup];
6446 [animation setEndFrame:keydown];
6449 UIAnimator *animator = [UIAnimator sharedAnimator];
6452 addAnimations:[NSArray arrayWithObjects:animation, nil]
6453 withDuration:(KeyboardTime_ - delay)
6458 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6460 [delegate_ showKeyboard:show];
6463 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6464 [self _showKeyboard:YES];
6467 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6468 [self _showKeyboard:NO];
6471 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6473 NSString *text([field_ text]);
6474 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6480 - (void) textFieldClearButtonPressed:(UITextField *)field {
6484 - (void) keyboardInputShouldDelete:(id)input {
6488 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6489 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6493 [field_ resignFirstResponder];
6498 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6499 if ((self = [super initWithBook:book]) != nil) {
6500 CGRect pageBounds = [book_ pageBounds];
6502 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6503 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6504 [self addSubview:pinstripe];*/
6506 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6507 [self addSubview:transition_];
6509 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6511 [advanced_ setReusesTableCells:YES];
6512 [advanced_ setDataSource:self];
6513 [advanced_ reloadData];
6515 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6516 CGColor dimmed(space_, 0, 0, 0, 0.5);
6517 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6519 table_ = [[PackageTable alloc]
6523 filter:@selector(isUnfilteredAndSearchedForBy:)
6527 [table_ setShouldHideHeaderInShortLists:NO];
6528 [transition_ transition:0 toView:table_];
6537 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6544 [self bounds].size.width - area.origin.x - 18;
6546 area.size.height = [UISearchField defaultHeight];
6548 field_ = [[UISearchField alloc] initWithFrame:area];
6550 UIFont *font = [UIFont systemFontOfSize:16];
6551 [field_ setFont:font];
6553 [field_ setPlaceholder:@"Package Names & Descriptions"];
6554 [field_ setDelegate:self];
6556 [field_ setPaddingTop:5];
6558 UITextInputTraits *traits([field_ textInputTraits]);
6559 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6560 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6561 [traits setReturnKeyType:UIReturnKeySearch];
6563 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6565 accessory_ = [[UIView alloc] initWithFrame:accrect];
6566 [accessory_ addSubview:field_];
6568 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6569 [configure setShowPressFeedback:YES];
6570 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6571 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6572 [accessory_ addSubview:configure];*/
6574 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6575 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6581 LKAnimation *animation = [LKTransition animation];
6582 [animation setType:@"oglFlip"];
6583 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6584 [animation setFillMode:@"extended"];
6585 [animation setTransitionFlags:3];
6586 [animation setDuration:10];
6587 [animation setSpeed:0.35];
6588 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6589 [[transition_ _layer] addAnimation:animation forKey:0];
6590 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6591 flipped_ = !flipped_;
6595 - (void) configurePushed {
6596 [field_ resignFirstResponder];
6600 - (void) resetViewAnimated:(BOOL)animated {
6603 [table_ resetViewAnimated:animated];
6606 - (void) _reloadData {
6609 - (void) reloadData {
6612 [table_ setObject:[field_ text]];
6613 [table_ reloadData];
6614 [table_ resetCursor];
6617 - (UIView *) accessoryView {
6621 - (NSString *) title {
6625 - (NSString *) backButtonTitle {
6629 - (void) setDelegate:(id)delegate {
6630 [table_ setDelegate:delegate];
6631 [super setDelegate:delegate];
6637 @interface SettingsView : RVPage {
6638 _transient Database *database_;
6641 UIPreferencesTable *table_;
6642 _UISwitchSlider *subscribedSwitch_;
6643 _UISwitchSlider *ignoredSwitch_;
6644 UIPreferencesControlTableCell *subscribedCell_;
6645 UIPreferencesControlTableCell *ignoredCell_;
6648 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6652 @implementation SettingsView
6655 [table_ setDataSource:nil];
6658 if (package_ != nil)
6661 [subscribedSwitch_ release];
6662 [ignoredSwitch_ release];
6663 [subscribedCell_ release];
6664 [ignoredCell_ release];
6668 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6669 if (package_ == nil)
6675 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6676 if (package_ == nil)
6683 default: _assert(false);
6689 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6690 if (package_ == nil)
6697 default: _assert(false);
6703 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6704 if (package_ == nil)
6711 default: _assert(false);
6717 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6718 if (package_ == nil)
6721 _UISwitchSlider *slider([cell control]);
6722 BOOL value([slider value] != 0);
6723 NSMutableDictionary *metadata([package_ metadata]);
6726 if (NSNumber *number = [metadata objectForKey:key])
6727 before = [number boolValue];
6731 if (value != before) {
6732 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6734 [delegate_ updateData];
6738 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6739 [self onSomething:cell withKey:@"IsSubscribed"];
6742 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6743 [self onSomething:cell withKey:@"IsIgnored"];
6746 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6747 if (package_ == nil)
6751 case 0: switch (row) {
6753 return subscribedCell_;
6755 return ignoredCell_;
6756 default: _assert(false);
6759 case 1: switch (row) {
6761 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6762 [cell setShowSelection:NO];
6763 [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."];
6767 default: _assert(false);
6770 default: _assert(false);
6776 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6777 if ((self = [super initWithBook:book])) {
6778 database_ = database;
6779 name_ = [package retain];
6781 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6782 [self addSubview:table_];
6784 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6785 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6787 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6788 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6790 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6791 [subscribedCell_ setShowSelection:NO];
6792 [subscribedCell_ setTitle:@"Show All Changes"];
6793 [subscribedCell_ setControl:subscribedSwitch_];
6795 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6796 [ignoredCell_ setShowSelection:NO];
6797 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6798 [ignoredCell_ setControl:ignoredSwitch_];
6800 [table_ setDataSource:self];
6805 - (void) resetViewAnimated:(BOOL)animated {
6806 [table_ resetViewAnimated:animated];
6809 - (void) reloadData {
6810 if (package_ != nil)
6811 [package_ autorelease];
6812 package_ = [database_ packageWithName:name_];
6813 if (package_ != nil) {
6815 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6816 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6819 [table_ reloadData];
6822 - (NSString *) title {
6828 /* Signature View {{{ */
6829 @interface SignatureView : BrowserView {
6830 _transient Database *database_;
6834 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6838 @implementation SignatureView
6845 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6847 [super webView:sender didClearWindowObject:window forFrame:frame];
6850 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6851 if ((self = [super initWithBook:book]) != nil) {
6852 database_ = database;
6853 package_ = [package retain];
6858 - (void) resetViewAnimated:(BOOL)animated {
6861 - (void) reloadData {
6862 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6868 @interface Cydia : UIApplication <
6869 ConfirmationViewDelegate,
6870 ProgressViewDelegate,
6879 UIToolbar *buttonbar_;
6883 NSMutableArray *essential_;
6884 NSMutableArray *broken_;
6886 Database *database_;
6887 ProgressView *progress_;
6891 UIKeyboard *keyboard_;
6892 UIProgressHUD *hud_;
6894 InstallView *install_;
6895 ChangesView *changes_;
6896 ManageView *manage_;
6897 SearchView *search_;
6902 @implementation Cydia
6905 if ([broken_ count] != 0) {
6906 int count = [broken_ count];
6908 UIActionSheet *sheet = [[[UIActionSheet alloc]
6909 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6910 buttons:[NSArray arrayWithObjects:
6912 @"Ignore (Temporary)",
6914 defaultButtonIndex:0
6919 [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."];
6920 [sheet popupAlertAnimated:YES];
6921 } else if (!Ignored_ && [essential_ count] != 0) {
6922 int count = [essential_ count];
6924 UIActionSheet *sheet = [[[UIActionSheet alloc]
6925 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6926 buttons:[NSArray arrayWithObjects:
6927 @"Upgrade Essential",
6928 @"Complete Upgrade",
6929 @"Ignore (Temporary)",
6931 defaultButtonIndex:0
6936 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6937 [sheet popupAlertAnimated:YES];
6941 - (void) _reloadData {
6942 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6943 [hud setText:@"Reloading Data"];
6944 [overlay_ addSubview:hud];
6947 [database_ reloadData];
6951 [essential_ removeAllObjects];
6952 [broken_ removeAllObjects];
6954 NSArray *packages = [database_ packages];
6955 for (Package *package in packages) {
6957 [broken_ addObject:package];
6958 if ([package upgradableAndEssential:NO]) {
6959 if ([package essential])
6960 [essential_ addObject:package];
6966 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6967 [buttonbar_ setBadgeValue:badge forButton:3];
6968 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6969 [buttonbar_ setBadgeAnimated:YES forButton:3];
6970 [self setApplicationBadge:badge];
6972 [buttonbar_ setBadgeValue:nil forButton:3];
6973 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6974 [buttonbar_ setBadgeAnimated:NO forButton:3];
6975 [self removeApplicationBadge];
6981 if ([packages count] == 0);
6993 [hud removeFromSuperview];*/
6996 - (void) _saveConfig {
6999 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
7005 - (void) updateData {
7008 /* XXX: this is just stupid */
7009 if (tag_ != 2 && install_ != nil)
7010 [install_ reloadData];
7011 if (tag_ != 3 && changes_ != nil)
7012 [changes_ reloadData];
7013 if (tag_ != 5 && search_ != nil)
7014 [search_ reloadData];
7024 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
7025 _assert(file != NULL);
7027 NSArray *keys = [Sources_ allKeys];
7029 for (int i(0), e([keys count]); i != e; ++i) {
7030 NSString *key = [keys objectAtIndex:i];
7031 NSDictionary *source = [Sources_ objectForKey:key];
7033 fprintf(file, "%s %s %s\n",
7034 [[source objectForKey:@"Type"] UTF8String],
7035 [[source objectForKey:@"URI"] UTF8String],
7036 [[source objectForKey:@"Distribution"] UTF8String]
7045 detachNewThreadSelector:@selector(update_)
7048 title:@"Updating Sources"
7052 - (void) reloadData {
7053 @synchronized (self) {
7054 if (confirm_ == nil)
7060 pkgProblemResolver *resolver = [database_ resolver];
7062 resolver->InstallProtect();
7063 if (!resolver->Resolve(true))
7068 [database_ prepare];
7070 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
7071 [confirm_ setDelegate:self];
7073 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
7074 [page setDelegate:self];
7076 [confirm_ setPage:page];
7077 [underlay_ popSubview:confirm_];
7080 - (void) installPackage:(Package *)package {
7081 @synchronized (self) {
7088 - (void) removePackage:(Package *)package {
7089 @synchronized (self) {
7096 - (void) distUpgrade {
7097 @synchronized (self) {
7098 [database_ upgrade];
7104 @synchronized (self) {
7106 if (confirm_ != nil) {
7114 [overlay_ removeFromSuperview];
7118 detachNewThreadSelector:@selector(perform)
7125 - (void) bootstrap_ {
7127 [database_ upgrade];
7128 [database_ prepare];
7129 [database_ perform];
7132 - (void) bootstrap {
7134 detachNewThreadSelector:@selector(bootstrap_)
7137 title:@"Bootstrap Install"
7141 - (void) progressViewIsComplete:(ProgressView *)progress {
7142 if (confirm_ != nil) {
7143 [underlay_ addSubview:overlay_];
7144 [confirm_ popFromSuperviewAnimated:NO];
7150 - (void) setPage:(RVPage *)page {
7151 [page resetViewAnimated:NO];
7152 [page setDelegate:self];
7153 [book_ setPage:page];
7156 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
7157 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
7158 [browser loadURL:url];
7162 - (void) _setHomePage {
7163 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
7166 - (void) buttonBarItemTapped:(id)sender {
7167 unsigned tag = [sender tag];
7169 [book_ resetViewAnimated:YES];
7171 } else if (tag_ == 2 && tag != 2)
7172 [install_ resetView];
7175 case 1: [self _setHomePage]; break;
7177 case 2: [self setPage:install_]; break;
7178 case 3: [self setPage:changes_]; break;
7179 case 4: [self setPage:manage_]; break;
7180 case 5: [self setPage:search_]; break;
7182 default: _assert(false);
7188 - (void) applicationWillSuspend {
7190 [super applicationWillSuspend];
7193 - (void) askForSettings {
7194 UIActionSheet *role = [[[UIActionSheet alloc]
7195 initWithTitle:@"Who Are You?"
7196 buttons:[NSArray arrayWithObjects:
7197 @"User (Graphical Only)",
7198 @"Hacker (+ Command Line)",
7199 @"Developer (No Filters)",
7201 defaultButtonIndex:-1
7206 [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."];
7207 [role popupAlertAnimated:YES];
7212 [self setStatusBarShowsProgress:NO];
7215 [hud_ removeFromSuperview];
7219 pid_t pid = ExecFork();
7221 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
7222 perror("launchctl stop");
7229 [self askForSettings];
7233 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
7235 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7236 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
7237 0, 0, screenrect.size.width, screenrect.size.height - 48
7238 ) database:database_];
7240 [book_ setDelegate:self];
7242 [overlay_ addSubview:book_];
7244 NSArray *buttonitems = [NSArray arrayWithObjects:
7245 [NSDictionary dictionaryWithObjectsAndKeys:
7246 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7247 @"home-up.png", kUIButtonBarButtonInfo,
7248 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
7249 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
7250 self, kUIButtonBarButtonTarget,
7251 @"Home", kUIButtonBarButtonTitle,
7252 @"0", kUIButtonBarButtonType,
7255 [NSDictionary dictionaryWithObjectsAndKeys:
7256 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7257 @"install-up.png", kUIButtonBarButtonInfo,
7258 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
7259 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
7260 self, kUIButtonBarButtonTarget,
7261 @"Sections", kUIButtonBarButtonTitle,
7262 @"0", kUIButtonBarButtonType,
7265 [NSDictionary dictionaryWithObjectsAndKeys:
7266 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7267 @"changes-up.png", kUIButtonBarButtonInfo,
7268 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
7269 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
7270 self, kUIButtonBarButtonTarget,
7271 @"Changes", kUIButtonBarButtonTitle,
7272 @"0", kUIButtonBarButtonType,
7275 [NSDictionary dictionaryWithObjectsAndKeys:
7276 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7277 @"manage-up.png", kUIButtonBarButtonInfo,
7278 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
7279 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
7280 self, kUIButtonBarButtonTarget,
7281 @"Manage", kUIButtonBarButtonTitle,
7282 @"0", kUIButtonBarButtonType,
7285 [NSDictionary dictionaryWithObjectsAndKeys:
7286 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7287 @"search-up.png", kUIButtonBarButtonInfo,
7288 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
7289 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
7290 self, kUIButtonBarButtonTarget,
7291 @"Search", kUIButtonBarButtonTitle,
7292 @"0", kUIButtonBarButtonType,
7296 buttonbar_ = [[UIToolbar alloc]
7298 withFrame:CGRectMake(
7299 0, screenrect.size.height - ButtonBarHeight_,
7300 screenrect.size.width, ButtonBarHeight_
7302 withItemList:buttonitems
7305 [buttonbar_ setDelegate:self];
7306 [buttonbar_ setBarStyle:1];
7307 [buttonbar_ setButtonBarTrackingMode:2];
7309 int buttons[5] = {1, 2, 3, 4, 5};
7310 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7311 [buttonbar_ showButtonGroup:0 withDuration:0];
7313 for (int i = 0; i != 5; ++i)
7314 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
7315 i * 64 + 2, 1, 60, ButtonBarHeight_
7318 [buttonbar_ showSelectionForButton:1];
7319 [overlay_ addSubview:buttonbar_];
7321 [UIKeyboard initImplementationNow];
7322 CGSize keysize = [UIKeyboard defaultSize];
7323 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
7324 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
7325 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
7326 [overlay_ addSubview:keyboard_];
7329 [underlay_ addSubview:overlay_];
7333 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
7334 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
7335 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7337 manage_ = (ManageView *) [[self
7338 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7339 withClass:[ManageView class]
7345 [self _setHomePage];
7348 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7349 NSString *context([sheet context]);
7351 if ([context isEqualToString:@"fixhalf"]) {
7354 @synchronized (self) {
7355 for (int i = 0, e = [broken_ count]; i != e; ++i) {
7356 Package *broken = [broken_ objectAtIndex:i];
7359 NSString *id = [broken id];
7360 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7361 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7362 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7363 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7372 [broken_ removeAllObjects];
7381 } else if ([context isEqualToString:@"role"]) {
7383 case 1: Role_ = @"User"; break;
7384 case 2: Role_ = @"Hacker"; break;
7385 case 3: Role_ = @"Developer"; break;
7392 bool reset = Settings_ != nil;
7394 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7398 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7408 } else if ([context isEqualToString:@"upgrade"]) {
7411 @synchronized (self) {
7412 for (int i = 0, e = [essential_ count]; i != e; ++i) {
7413 Package *essential = [essential_ objectAtIndex:i];
7414 [essential install];
7438 - (void) reorganize { _pooled
7439 system("/usr/libexec/cydia/free.sh");
7440 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7443 - (void) applicationSuspend:(__GSEvent *)event {
7444 if (hud_ == nil && ![progress_ isRunning])
7445 [super applicationSuspend:event];
7448 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7450 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7453 - (void) _setSuspended:(BOOL)value {
7455 [super _setSuspended:value];
7458 - (UIProgressHUD *) addProgressHUD {
7459 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7461 [underlay_ addSubview:hud];
7465 - (void) openMailToURL:(NSURL *)url {
7466 // XXX: this makes me sad
7468 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7470 [UIApp openURL:url];// asPanel:YES];
7474 - (void) clearFirstResponder {
7475 if (id responder = [window_ firstResponder])
7476 [responder resignFirstResponder];
7479 - (RVPage *) pageForPackage:(NSString *)name {
7480 if (Package *package = [database_ packageWithName:name]) {
7481 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7482 [view setPackage:package];
7485 UIActionSheet *sheet = [[[UIActionSheet alloc]
7486 initWithTitle:@"Cannot Locate Package"
7487 buttons:[NSArray arrayWithObjects:@"Close", nil]
7488 defaultButtonIndex:0
7493 [sheet setBodyText:[NSString stringWithFormat:
7494 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7497 [sheet popupAlertAnimated:YES];
7502 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7503 NSString *href = [url absoluteString];
7508 if ([href isEqualToString:@"cydia://add-source"])
7509 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7510 else if ([href isEqualToString:@"cydia://sources"])
7511 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7512 else if ([href isEqualToString:@"cydia://packages"])
7513 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7514 else if ([href hasPrefix:@"cydia://url/"])
7515 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7516 else if ([href hasPrefix:@"cydia://launch/"])
7517 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7518 else if ([href hasPrefix:@"cydia://package-settings/"])
7519 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7520 else if ([href hasPrefix:@"cydia://package-signature/"])
7521 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7522 else if ([href hasPrefix:@"cydia://package/"])
7523 return [self pageForPackage:[href substringFromIndex:16]];
7524 else if ([href hasPrefix:@"cydia://files/"]) {
7525 NSString *name = [href substringFromIndex:14];
7527 if (Package *package = [database_ packageWithName:name]) {
7528 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7529 [files setPackage:package];
7537 - (void) applicationOpenURL:(NSURL *)url {
7538 [super applicationOpenURL:url];
7540 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7541 [self setPage:page];
7542 [buttonbar_ showSelectionForButton:tag];
7547 - (void) applicationDidFinishLaunching:(id)unused {
7548 Font12_ = [[UIFont systemFontOfSize:12] retain];
7549 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7550 Font14_ = [[UIFont systemFontOfSize:14] retain];
7551 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7552 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7554 _assert(pkgInitConfig(*_config));
7555 _assert(pkgInitSystem(*_config, _system));
7559 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7560 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7562 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7564 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7565 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7567 [window_ orderFront:self];
7568 [window_ makeKey:self];
7569 [window_ setHidden:NO];
7571 database_ = [Database sharedInstance];
7572 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7573 [database_ setDelegate:progress_];
7574 [window_ setContentView:progress_];
7576 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7577 [progress_ setContentView:underlay_];
7579 [progress_ resetView];
7582 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7583 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7584 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7585 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7586 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7587 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7588 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7590 [self setIdleTimerDisabled:YES];
7592 hud_ = [self addProgressHUD];
7593 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7595 [self setStatusBarShowsProgress:YES];
7598 detachNewThreadSelector:@selector(reorganize)
7606 - (void) showKeyboard:(BOOL)show {
7607 CGSize keysize = [UIKeyboard defaultSize];
7608 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7609 CGRect keyup = keydown;
7610 keyup.origin.y -= keysize.height;
7612 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7613 [animation setSignificantRectFields:2];
7616 [animation setStartFrame:keydown];
7617 [animation setEndFrame:keyup];
7618 [keyboard_ activate];
7620 [animation setStartFrame:keyup];
7621 [animation setEndFrame:keydown];
7622 [keyboard_ deactivate];
7625 [[UIAnimator sharedAnimator]
7626 addAnimations:[NSArray arrayWithObjects:animation, nil]
7627 withDuration:KeyboardTime_
7632 - (void) slideUp:(UIActionSheet *)alert {
7634 [alert presentSheetFromButtonBar:buttonbar_];
7636 [alert presentSheetInView:overlay_];
7641 void AddPreferences(NSString *plist) { _pooled
7642 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7643 _assert(settings != NULL);
7644 NSMutableArray *items = [settings objectForKey:@"items"];
7648 for (size_t i(0); i != [items count]; ++i) {
7649 NSMutableDictionary *item([items objectAtIndex:i]);
7650 NSString *label = [item objectForKey:@"label"];
7651 if (label != nil && [label isEqualToString:@"Cydia"]) {
7658 for (size_t i(0); i != [items count]; ++i) {
7659 NSDictionary *item([items objectAtIndex:i]);
7660 NSString *label = [item objectForKey:@"label"];
7661 if (label != nil && [label isEqualToString:@"General"]) {
7662 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7663 @"CydiaSettings", @"bundle",
7664 @"PSLinkCell", @"cell",
7665 [NSNumber numberWithBool:YES], @"hasIcon",
7666 [NSNumber numberWithBool:YES], @"isController",
7668 nil] atIndex:(i + 1)];
7674 _assert([settings writeToFile:plist atomically:YES] == YES);
7679 id Alloc_(id self, SEL selector) {
7680 id object = alloc_(self, selector);
7681 lprintf("[%s]A-%p\n", self->isa->name, object);
7686 id Dealloc_(id self, SEL selector) {
7687 id object = dealloc_(self, selector);
7688 lprintf("[%s]D-%p\n", self->isa->name, object);
7692 int main(int argc, char *argv[]) { _pooled
7693 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7695 bool substrate(false);
7701 for (int argi(1); argi != argc; ++argi)
7702 if (strcmp(argv[argi], "--") == 0) {
7704 argv[argi] = argv[0];
7710 for (int argi(1); argi != arge; ++argi)
7711 if (strcmp(args[argi], "--bootstrap") == 0)
7713 else if (strcmp(args[argi], "--substrate") == 0)
7716 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7719 App_ = [[NSBundle mainBundle] bundlePath];
7720 Home_ = NSHomeDirectory();
7721 Locale_ = CFLocaleCopyCurrent();
7724 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7725 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7726 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7727 Sounds_Keyboard_ = [keyboard boolValue];
7733 #if 1 /* XXX: this costs 1.4s of startup performance */
7734 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7735 _assert(errno == ENOENT);
7736 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7737 _assert(errno == ENOENT);
7740 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7741 alloc_ = alloc->method_imp;
7742 alloc->method_imp = (IMP) &Alloc_;*/
7744 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7745 dealloc_ = dealloc->method_imp;
7746 dealloc->method_imp = (IMP) &Dealloc_;*/
7751 size = sizeof(maxproc);
7752 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7753 perror("sysctlbyname(\"kern.maxproc\", ?)");
7754 else if (maxproc < 64) {
7756 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7757 perror("sysctlbyname(\"kern.maxproc\", #)");
7760 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7761 char *machine = new char[size];
7762 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7763 perror("sysctlbyname(\"hw.machine\", ?)");
7767 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7769 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7770 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7772 if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7773 Indices_ = [[NSMutableDictionary alloc] init];
7775 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7776 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7778 Settings_ = [Metadata_ objectForKey:@"Settings"];
7780 Packages_ = [Metadata_ objectForKey:@"Packages"];
7781 Sections_ = [Metadata_ objectForKey:@"Sections"];
7782 Sources_ = [Metadata_ objectForKey:@"Sources"];
7785 if (Settings_ != nil)
7786 Role_ = [Settings_ objectForKey:@"Role"];
7788 if (Packages_ == nil) {
7789 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7790 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7793 if (Sections_ == nil) {
7794 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7795 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7798 if (Sources_ == nil) {
7799 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7800 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7804 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7807 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7808 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7810 if (access("/User", F_OK) != 0)
7811 system("/usr/libexec/cydia/firmware.sh");
7813 _assert([[NSFileManager defaultManager]
7814 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7815 withIntermediateDirectories:YES
7820 space_ = CGColorSpaceCreateDeviceRGB();
7822 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7823 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7824 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7825 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7826 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7827 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7829 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7831 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7833 UIApplicationUseLegacyEvents(YES);
7834 UIKeyboardDisableAutomaticAppearance();
7836 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7838 CGColorSpaceRelease(space_);