1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #import "UICaboodle.h"
41 #include <objc/objc.h>
42 #include <objc/runtime.h>
44 #include <CoreGraphics/CoreGraphics.h>
45 #include <GraphicsServices/GraphicsServices.h>
46 #include <Foundation/Foundation.h>
48 #import <QuartzCore/CALayer.h>
50 #import <UIKit/UIKit.h>
53 #import <MessageUI/MailComposeController.h>
55 #include <WebKit/DOMCSSPrimitiveValue.h>
56 #include <WebKit/DOMCSSStyleDeclaration.h>
57 #include <WebKit/DOMDocument.h>
58 #include <WebKit/DOMHTMLBodyElement.h>
59 #include <WebKit/DOMNodeList.h>
60 #include <WebKit/DOMRGBColor.h>
62 #include <WebKit/WebFrame.h>
63 #include <WebKit/WebPolicyDelegate.h>
64 #include <WebKit/WebScriptObject.h>
66 #import <WebKit/WebView.h>
67 #import <WebKit/WebView-WebPrivate.h>
72 #include <ext/stdio_filebuf.h>
74 #include <apt-pkg/acquire.h>
75 #include <apt-pkg/acquire-item.h>
76 #include <apt-pkg/algorithms.h>
77 #include <apt-pkg/cachefile.h>
78 #include <apt-pkg/clean.h>
79 #include <apt-pkg/configuration.h>
80 #include <apt-pkg/debmetaindex.h>
81 #include <apt-pkg/error.h>
82 #include <apt-pkg/init.h>
83 #include <apt-pkg/mmap.h>
84 #include <apt-pkg/pkgrecords.h>
85 #include <apt-pkg/sha1.h>
86 #include <apt-pkg/sourcelist.h>
87 #include <apt-pkg/sptr.h>
88 #include <apt-pkg/strutl.h>
90 #include <sys/types.h>
92 #include <sys/sysctl.h>
98 #include <mach-o/nlist.h>
108 #import "BrowserView.h"
109 #import "ResetView.h"
112 //#define _finline __attribute__((force_inline))
113 #define _finline inline
118 #define _limit(count) do { \
119 static size_t _count(0); \
120 if (++_count == count) \
124 static uint64_t profile_;
126 #define _timestamp ({ \
128 gettimeofday(&tv, NULL); \
129 tv.tv_sec * 1000000 + tv.tv_usec; \
132 /* Objective-C Handle<> {{{ */
133 template <typename Type_>
135 typedef _H<Type_> This_;
140 _finline void Retain_() {
145 _finline void Clear_() {
151 _finline _H(Type_ *value = NULL, bool mended = false) :
162 _finline This_ &operator =(Type_ *value) {
163 if (value_ != value) {
172 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
174 void NSLogPoint(const char *fix, const CGPoint &point) {
175 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
178 void NSLogRect(const char *fix, const CGRect &rect) {
179 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
182 /* NSForcedOrderingSearch doesn't work on the iPhone */
183 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
184 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
185 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
187 /* iPhoneOS 2.0 Compatibility {{{ */
189 @interface UITextView (iPhoneOS)
190 - (void) setTextSize:(float)size;
193 @implementation UITextView (iPhoneOS)
195 - (void) setTextSize:(float)size {
196 [self setFont:[[self font] fontWithSize:size]];
203 extern NSString * const kCAFilterNearest;
205 /* Information Dictionaries {{{ */
206 @interface NSMutableArray (Cydia)
207 - (void) addInfoDictionary:(NSDictionary *)info;
210 @implementation NSMutableArray (Cydia)
212 - (void) addInfoDictionary:(NSDictionary *)info {
213 [self addObject:info];
218 @interface NSMutableDictionary (Cydia)
219 - (void) addInfoDictionary:(NSDictionary *)info;
222 @implementation NSMutableDictionary (Cydia)
224 - (void) addInfoDictionary:(NSDictionary *)info {
225 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
226 [self setObject:info forKey:bundle];
231 /* Pop Transitions {{{ */
232 @interface PopTransitionView : UITransitionView {
237 @implementation PopTransitionView
239 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
240 if (from != nil && to == nil)
241 [self removeFromSuperview];
246 @interface UIView (PopUpView)
247 - (void) popFromSuperviewAnimated:(BOOL)animated;
248 - (void) popSubview:(UIView *)view;
251 @implementation UIView (PopUpView)
253 - (void) popFromSuperviewAnimated:(BOOL)animated {
254 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
257 - (void) popSubview:(UIView *)view {
258 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
259 [transition setDelegate:transition];
260 [self addSubview:transition];
262 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
263 [transition transition:UITransitionNone toView:blank];
264 [transition transition:UITransitionPushFromBottom toView:view];
270 #define lprintf(args...) fprintf(stderr, args)
273 #define ForSaurik (1 && !ForRelease)
274 #define RecycleWebViews 0
275 #define AlwaysReload (1 && !ForRelease)
278 @interface NSMutableArray (Radix)
279 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
282 @implementation NSMutableArray (Radix)
284 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
285 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
286 [invocation setSelector:selector];
287 [invocation setArgument:&object atIndex:2];
289 size_t count([self count]);
294 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
296 for (size_t i(0); i != count; ++i) {
297 RadixItem &item(lhs[i]);
300 id object([self objectAtIndex:i]);
301 [invocation setTarget:object];
304 [invocation getReturnValue:&item.key];
307 static const size_t width = 32;
308 static const size_t bits = 11;
309 static const size_t slots = 1 << bits;
310 static const size_t passes = (width + (bits - 1)) / bits;
312 size_t *hist(new size_t[slots]);
314 for (size_t pass(0); pass != passes; ++pass) {
315 memset(hist, 0, sizeof(size_t) * slots);
317 for (size_t i(0); i != count; ++i) {
318 uint32_t key(lhs[i].key);
320 key &= _not(uint32_t) >> width - bits;
325 for (size_t i(0); i != slots; ++i) {
326 size_t local(offset);
331 for (size_t i(0); i != count; ++i) {
332 uint32_t key(lhs[i].key);
334 key &= _not(uint32_t) >> width - bits;
335 rhs[hist[key]++] = lhs[i];
345 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
346 for (size_t i(0); i != count; ++i)
347 [values addObject:[self objectAtIndex:lhs[i].index]];
348 [self setArray:values];
356 /* Apple Bug Fixes {{{ */
357 @implementation UIWebDocumentView (Cydia)
359 - (void) _setScrollerOffset:(CGPoint)offset {
360 UIScroller *scroller([self _scroller]);
362 CGSize size([scroller contentSize]);
363 CGSize bounds([scroller bounds].size);
366 max.x = size.width - bounds.width;
367 max.y = size.height - bounds.height;
375 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
376 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
378 [scroller setOffset:offset];
385 kUIControlEventMouseDown = 1 << 0,
386 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
387 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
388 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
389 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
390 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
391 } UIControlEventMasks;
393 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
394 size_t length([self length] - state->state);
397 else if (length > count)
399 for (size_t i(0); i != length; ++i)
400 objects[i] = [self item:state->state++];
401 state->itemsPtr = objects;
402 state->mutationsPtr = (unsigned long *) self;
406 @interface NSString (UIKit)
407 - (NSString *) stringByAddingPercentEscapes;
408 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
411 @interface NSString (Cydia)
412 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
413 - (NSComparisonResult) compareByPath:(NSString *)other;
416 @implementation NSString (Cydia)
418 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
419 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
422 - (NSComparisonResult) compareByPath:(NSString *)other {
423 NSString *prefix = [self commonPrefixWithString:other options:0];
424 size_t length = [prefix length];
426 NSRange lrange = NSMakeRange(length, [self length] - length);
427 NSRange rrange = NSMakeRange(length, [other length] - length);
429 lrange = [self rangeOfString:@"/" options:0 range:lrange];
430 rrange = [other rangeOfString:@"/" options:0 range:rrange];
432 NSComparisonResult value;
434 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
435 value = NSOrderedSame;
436 else if (lrange.location == NSNotFound)
437 value = NSOrderedAscending;
438 else if (rrange.location == NSNotFound)
439 value = NSOrderedDescending;
441 value = NSOrderedSame;
443 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
444 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
445 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
446 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
448 NSComparisonResult result = [lpath compare:rpath];
449 return result == NSOrderedSame ? value : result;
454 /* Perl-Compatible RegEx {{{ */
464 Pcre(const char *regex) :
469 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
472 lprintf("%d:%s\n", offset, error);
476 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
477 matches_ = new int[(capture_ + 1) * 3];
485 NSString *operator [](size_t match) {
486 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
489 bool operator ()(NSString *data) {
490 // XXX: length is for characters, not for bytes
491 return operator ()([data UTF8String], [data length]);
494 bool operator ()(const char *data, size_t size) {
496 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
500 /* Mime Addresses {{{ */
501 @interface Address : NSObject {
507 - (NSString *) address;
509 + (Address *) addressWithString:(NSString *)string;
510 - (Address *) initWithString:(NSString *)string;
513 @implementation Address
522 - (NSString *) name {
526 - (NSString *) address {
530 + (Address *) addressWithString:(NSString *)string {
531 return [[[Address alloc] initWithString:string] autorelease];
534 + (NSArray *) _attributeKeys {
535 return [NSArray arrayWithObjects:@"address", @"name", nil];
538 - (NSArray *) attributeKeys {
539 return [[self class] _attributeKeys];
542 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
543 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
546 - (Address *) initWithString:(NSString *)string {
547 if ((self = [super init]) != nil) {
548 const char *data = [string UTF8String];
549 size_t size = [string length];
551 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
553 if (address_r(data, size)) {
554 name_ = [address_r[1] retain];
555 address_ = [address_r[2] retain];
557 name_ = [string retain];
565 /* CoreGraphics Primitives {{{ */
576 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
579 Set(space, red, green, blue, alpha);
584 CGColorRelease(color_);
591 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
593 float color[] = {red, green, blue, alpha};
594 color_ = CGColorCreate(space, color);
597 operator CGColorRef() {
603 extern "C" void UISetColor(CGColorRef color);
605 /* Random Global Variables {{{ */
606 static const int PulseInterval_ = 50000;
607 static const int ButtonBarHeight_ = 48;
608 static const float KeyboardTime_ = 0.3f;
610 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
611 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
613 static CGColor Blue_;
614 static CGColor Blueish_;
615 static CGColor Black_;
617 static CGColor White_;
618 static CGColor Gray_;
620 static NSString *App_;
621 static NSString *Home_;
622 static BOOL Sounds_Keyboard_;
624 static BOOL Advanced_;
628 static BOOL Ignored_;
630 static UIFont *Font12_;
631 static UIFont *Font12Bold_;
632 static UIFont *Font14_;
633 static UIFont *Font18Bold_;
634 static UIFont *Font22Bold_;
636 static const char *Machine_ = NULL;
637 static const NSString *UniqueID_ = NULL;
644 CGColorSpaceRef space_;
646 #define FW_LEAST(major, minor, bugfix) \
647 (major < Major_ || major == Major_ && \
648 (minor < Minor_ || minor == Minor_ && \
654 static NSDictionary *SectionMap_;
655 static NSMutableDictionary *Metadata_;
656 static NSMutableDictionary *Indices_;
657 static _transient NSMutableDictionary *Settings_;
658 static _transient NSString *Role_;
659 static _transient NSMutableDictionary *Packages_;
660 static _transient NSMutableDictionary *Sections_;
661 static _transient NSMutableDictionary *Sources_;
662 static bool Changed_;
666 static NSMutableArray *Documents_;
669 NSString *GetLastUpdate() {
670 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
673 return @"Never or Unknown";
675 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
676 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
678 CFRelease(formatter);
680 return [(NSString *) formatted autorelease];
683 /* Display Helpers {{{ */
684 inline float Interpolate(float begin, float end, float fraction) {
685 return (end - begin) * fraction + begin;
688 NSString *SizeString(double size) {
689 bool negative = size < 0;
694 while (size > 1024) {
699 static const char *powers_[] = {"B", "kB", "MB", "GB"};
701 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
704 NSString *StripVersion(NSString *version) {
705 NSRange colon = [version rangeOfString:@":"];
706 if (colon.location != NSNotFound)
707 version = [version substringFromIndex:(colon.location + 1)];
711 NSString *Simplify(NSString *title) {
712 const char *data = [title UTF8String];
713 size_t size = [title length];
715 static Pcre square_r("^\\[(.*)\\]$");
716 if (square_r(data, size))
717 return Simplify(square_r[1]);
719 static Pcre paren_r("^\\((.*)\\)$");
720 if (paren_r(data, size))
721 return Simplify(paren_r[1]);
723 static Pcre title_r("^(.*?) \\(.*\\)$");
724 if (title_r(data, size))
725 return Simplify(title_r[1]);
731 bool isSectionVisible(NSString *section) {
732 NSDictionary *metadata = [Sections_ objectForKey:section];
733 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
734 return hidden == nil || ![hidden boolValue];
737 /* Delegate Prototypes {{{ */
741 @interface NSObject (ProgressDelegate)
744 @implementation NSObject(ProgressDelegate)
746 - (void) _setProgressError:(NSArray *)args {
747 [self performSelector:@selector(setProgressError:forPackage:)
748 withObject:[args objectAtIndex:0]
749 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
755 @protocol ProgressDelegate
756 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
757 - (void) setProgressTitle:(NSString *)title;
758 - (void) setProgressPercent:(float)percent;
759 - (void) startProgress;
760 - (void) addProgressOutput:(NSString *)output;
761 - (bool) isCancelling:(size_t)received;
764 @protocol ConfigurationDelegate
765 - (void) repairWithSelector:(SEL)selector;
766 - (void) setConfigurationData:(NSString *)data;
769 @protocol CydiaDelegate
770 - (void) installPackage:(Package *)package;
771 - (void) removePackage:(Package *)package;
772 - (void) slideUp:(UIActionSheet *)alert;
773 - (void) distUpgrade;
776 - (void) askForSettings;
777 - (UIProgressHUD *) addProgressHUD;
778 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
779 - (RVPage *) pageForPackage:(NSString *)name;
780 - (void) openMailToURL:(NSURL *)url;
784 /* Status Delegation {{{ */
786 public pkgAcquireStatus
789 _transient NSObject<ProgressDelegate> *delegate_;
797 void setDelegate(id delegate) {
798 delegate_ = delegate;
801 virtual bool MediaChange(std::string media, std::string drive) {
805 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
808 virtual void Fetch(pkgAcquire::ItemDesc &item) {
809 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
810 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
813 virtual void Done(pkgAcquire::ItemDesc &item) {
816 virtual void Fail(pkgAcquire::ItemDesc &item) {
818 item.Owner->Status == pkgAcquire::Item::StatIdle ||
819 item.Owner->Status == pkgAcquire::Item::StatDone
823 std::string &error(item.Owner->ErrorText);
827 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
828 NSArray *fields([description componentsSeparatedByString:@" "]);
829 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
831 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
832 withObject:[NSArray arrayWithObjects:
833 [NSString stringWithUTF8String:error.c_str()],
840 virtual bool Pulse(pkgAcquire *Owner) {
841 bool value = pkgAcquireStatus::Pulse(Owner);
844 double(CurrentBytes + CurrentItems) /
845 double(TotalBytes + TotalItems)
848 [delegate_ setProgressPercent:percent];
849 return [delegate_ isCancelling:CurrentBytes] ? false : value;
852 virtual void Start() {
853 [delegate_ startProgress];
856 virtual void Stop() {
860 /* Progress Delegation {{{ */
865 _transient id<ProgressDelegate> delegate_;
868 virtual void Update() {
869 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
870 [delegate_ setProgressPercent:(Percent / 100)];
879 void setDelegate(id delegate) {
880 delegate_ = delegate;
883 virtual void Done() {
884 [delegate_ setProgressPercent:1];
889 /* Database Interface {{{ */
890 @interface Database : NSObject {
892 pkgDepCache::Policy *policy_;
893 pkgRecords *records_;
894 pkgProblemResolver *resolver_;
895 pkgAcquire *fetcher_;
897 SPtr<pkgPackageManager> manager_;
898 pkgSourceList *list_;
900 NSMutableDictionary *sources_;
901 NSMutableArray *packages_;
903 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
912 + (Database *) sharedInstance;
914 - (void) _readCydia:(NSNumber *)fd;
915 - (void) _readStatus:(NSNumber *)fd;
916 - (void) _readOutput:(NSNumber *)fd;
920 - (Package *) packageWithName:(NSString *)name;
922 - (pkgCacheFile &) cache;
923 - (pkgDepCache::Policy *) policy;
924 - (pkgRecords *) records;
925 - (pkgProblemResolver *) resolver;
926 - (pkgAcquire &) fetcher;
927 - (NSArray *) packages;
928 - (NSArray *) sources;
937 - (void) updateWithStatus:(Status &)status;
939 - (void) setDelegate:(id)delegate;
940 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
944 /* Source Class {{{ */
945 @interface Source : NSObject {
946 NSString *description_;
951 NSString *distribution_;
955 NSString *defaultIcon_;
957 NSDictionary *record_;
961 - (Source *) initWithMetaIndex:(metaIndex *)index;
963 - (NSComparisonResult) compareByNameAndType:(Source *)source;
965 - (NSDictionary *) record;
969 - (NSString *) distribution;
975 - (NSString *) description;
976 - (NSString *) label;
977 - (NSString *) origin;
978 - (NSString *) version;
980 - (NSString *) defaultIcon;
984 @implementation Source
988 [distribution_ release];
991 if (description_ != nil)
992 [description_ release];
999 if (defaultIcon_ != nil)
1000 [defaultIcon_ release];
1007 + (NSArray *) _attributeKeys {
1008 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1011 - (NSArray *) attributeKeys {
1012 return [[self class] _attributeKeys];
1015 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1016 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1019 - (Source *) initWithMetaIndex:(metaIndex *)index {
1020 if ((self = [super init]) != nil) {
1021 trusted_ = index->IsTrusted();
1023 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1024 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1025 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1027 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1028 if (dindex != NULL) {
1029 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1031 while (std::getline(release, line)) {
1032 std::string::size_type colon(line.find(':'));
1033 if (colon == std::string::npos)
1036 std::string name(line.substr(0, colon));
1037 std::string value(line.substr(colon + 1));
1038 while (!value.empty() && value[0] == ' ')
1039 value = value.substr(1);
1041 if (name == "Default-Icon")
1042 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1043 else if (name == "Description")
1044 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1045 else if (name == "Label")
1046 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1047 else if (name == "Origin")
1048 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1049 else if (name == "Version")
1050 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1054 record_ = [Sources_ objectForKey:[self key]];
1056 record_ = [record_ retain];
1060 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1061 NSDictionary *lhr = [self record];
1062 NSDictionary *rhr = [source record];
1065 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1067 NSString *lhs = [self name];
1068 NSString *rhs = [source name];
1070 if ([lhs length] != 0 && [rhs length] != 0) {
1071 unichar lhc = [lhs characterAtIndex:0];
1072 unichar rhc = [rhs characterAtIndex:0];
1074 if (isalpha(lhc) && !isalpha(rhc))
1075 return NSOrderedAscending;
1076 else if (!isalpha(lhc) && isalpha(rhc))
1077 return NSOrderedDescending;
1080 return [lhs compare:rhs options:LaxCompareOptions_];
1083 - (NSDictionary *) record {
1091 - (NSString *) uri {
1095 - (NSString *) distribution {
1096 return distribution_;
1099 - (NSString *) type {
1103 - (NSString *) key {
1104 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1107 - (NSString *) host {
1108 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1111 - (NSString *) name {
1112 return origin_ == nil ? [self host] : origin_;
1115 - (NSString *) description {
1116 return description_;
1119 - (NSString *) label {
1120 return label_ == nil ? [self host] : label_;
1123 - (NSString *) origin {
1127 - (NSString *) version {
1131 - (NSString *) defaultIcon {
1132 return defaultIcon_;
1137 /* Relationship Class {{{ */
1138 @interface Relationship : NSObject {
1143 - (NSString *) type;
1145 - (NSString *) name;
1149 @implementation Relationship
1157 - (NSString *) type {
1165 - (NSString *) name {
1172 /* Package Class {{{ */
1173 @interface Package : NSObject {
1174 pkgCache::PkgIterator iterator_;
1175 _transient Database *database_;
1176 pkgCache::VerIterator version_;
1177 pkgCache::VerFileIterator file_;
1185 NSString *installed_;
1191 NSString *depiction_;
1192 NSString *homepage_;
1198 NSArray *relationships_;
1201 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1202 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1204 - (pkgCache::PkgIterator) iterator;
1206 - (NSString *) section;
1207 - (NSString *) simpleSection;
1209 - (Address *) maintainer;
1211 - (NSString *) description;
1212 - (NSString *) index;
1214 - (NSMutableDictionary *) metadata;
1216 - (BOOL) subscribed;
1219 - (NSString *) latest;
1220 - (NSString *) installed;
1223 - (BOOL) upgradableAndEssential:(BOOL)essential;
1226 - (BOOL) unfiltered;
1230 - (BOOL) halfConfigured;
1231 - (BOOL) halfInstalled;
1233 - (NSString *) mode;
1236 - (NSString *) name;
1237 - (NSString *) tagline;
1239 - (NSString *) homepage;
1240 - (NSString *) depiction;
1241 - (Address *) author;
1243 - (NSArray *) files;
1244 - (NSArray *) relationships;
1245 - (NSArray *) warnings;
1246 - (NSArray *) applications;
1248 - (Source *) source;
1249 - (NSString *) role;
1250 - (NSString *) rating;
1252 - (BOOL) matches:(NSString *)text;
1254 - (bool) hasSupportingRole;
1255 - (BOOL) hasTag:(NSString *)tag;
1256 - (NSString *) primaryPurpose;
1257 - (NSArray *) purposes;
1259 - (NSComparisonResult) compareByName:(Package *)package;
1260 - (NSComparisonResult) compareBySection:(Package *)package;
1262 - (uint32_t) compareForChanges;
1267 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1268 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1269 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1270 - (NSNumber *) isVisibleInSource:(Source *)source;
1274 @implementation Package
1280 if (section_ != nil)
1284 if (installed_ != nil)
1285 [installed_ release];
1293 if (depiction_ != nil)
1294 [depiction_ release];
1295 if (homepage_ != nil)
1296 [homepage_ release];
1297 if (sponsor_ != nil)
1306 if (relationships_ != nil)
1307 [relationships_ release];
1312 + (NSArray *) _attributeKeys {
1313 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1316 - (NSArray *) attributeKeys {
1317 return [[self class] _attributeKeys];
1320 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1321 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1324 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1325 if ((self = [super init]) != nil) {
1326 iterator_ = iterator;
1327 database_ = database;
1329 version_ = [database_ policy]->GetCandidateVer(iterator_);
1330 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1331 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1333 pkgCache::VerIterator current = iterator_.CurrentVer();
1334 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1335 installed_ = [StripVersion(installed) retain];
1337 if (!version_.end())
1338 file_ = version_.FileList();
1340 pkgCache &cache([database_ cache]);
1341 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1344 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1347 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1349 const char *begin, *end;
1350 parser->GetRec(begin, end);
1352 NSString *website(nil);
1353 NSString *sponsor(nil);
1354 NSString *author(nil);
1363 {"depiction", &depiction_},
1364 {"homepage", &homepage_},
1365 {"website", &website},
1366 {"sponsor", &sponsor},
1367 {"author", &author},
1371 while (begin != end)
1372 if (*begin == '\n') {
1375 } else if (isblank(*begin)) next: {
1376 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1379 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1380 const char *name(begin);
1381 size_t size(colon - begin);
1383 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1386 const char *stop(begin == NULL ? end : begin);
1387 while (stop[-1] == '\r')
1389 while (++colon != stop && isblank(*colon));
1391 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1392 if (strncasecmp(names[i].name_, name, size) == 0) {
1393 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1394 *names[i].value_ = value;
1405 name_ = [name_ retain];
1406 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1408 icon_ = [icon_ retain];
1409 if (depiction_ != nil)
1410 depiction_ = [depiction_ retain];
1411 if (homepage_ == nil)
1412 homepage_ = website;
1413 if ([homepage_ isEqualToString:depiction_])
1415 if (homepage_ != nil)
1416 homepage_ = [homepage_ retain];
1418 sponsor_ = [[Address addressWithString:sponsor] retain];
1420 author_ = [[Address addressWithString:author] retain];
1422 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1426 for (int i(0), e([tags_ count]); i != e; ++i) {
1427 NSString *tag = [tags_ objectAtIndex:i];
1428 if ([tag hasPrefix:@"role::"]) {
1429 role_ = [[tag substringFromIndex:6] retain];
1434 NSString *solid(latest == nil ? installed : latest);
1435 bool changed(false);
1437 NSString *key([id_ lowercaseString]);
1439 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1440 if (metadata == nil) {
1441 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1446 [metadata setObject:solid forKey:@"LastVersion"];
1449 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1450 NSDate *last([metadata objectForKey:@"LastSeen"]);
1451 NSString *version([metadata objectForKey:@"LastVersion"]);
1454 first = last == nil ? now_ : last;
1455 [metadata setObject:first forKey:@"FirstSeen"];
1460 if (version == nil) {
1461 [metadata setObject:solid forKey:@"LastVersion"];
1463 } else if (![version isEqualToString:solid]) {
1464 [metadata setObject:solid forKey:@"LastVersion"];
1466 [metadata setObject:last forKey:@"LastSeen"];
1472 [Packages_ setObject:metadata forKey:key];
1478 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1479 return [[[Package alloc]
1480 initWithIterator:iterator
1485 - (pkgCache::PkgIterator) iterator {
1489 - (NSString *) section {
1490 if (section_ != nil)
1493 const char *section = iterator_.Section();
1494 if (section == NULL)
1497 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1500 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1501 if (NSString *rename = [value objectForKey:@"Rename"]) {
1506 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1510 - (NSString *) simpleSection {
1511 if (NSString *section = [self section])
1512 return Simplify(section);
1518 - (Address *) maintainer {
1521 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1522 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1526 return version_.end() ? 0 : version_->InstalledSize;
1529 - (NSString *) description {
1532 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1533 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1535 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1536 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1537 if ([lines count] < 2)
1540 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1541 for (size_t i(1); i != [lines count]; ++i) {
1542 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1543 [trimmed addObject:trim];
1546 return [trimmed componentsJoinedByString:@"\n"];
1549 - (NSString *) index {
1550 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1551 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1554 - (NSMutableDictionary *) metadata {
1555 return [Packages_ objectForKey:[id_ lowercaseString]];
1559 NSDictionary *metadata([self metadata]);
1560 if ([self subscribed])
1561 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1563 return [metadata objectForKey:@"FirstSeen"];
1566 - (BOOL) subscribed {
1567 NSDictionary *metadata([self metadata]);
1568 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1569 return [subscribed boolValue];
1575 NSDictionary *metadata([self metadata]);
1576 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1577 return [ignored boolValue];
1582 - (NSString *) latest {
1586 - (NSString *) installed {
1591 return !version_.end();
1594 - (BOOL) upgradableAndEssential:(BOOL)essential {
1595 pkgCache::VerIterator current = iterator_.CurrentVer();
1598 return essential && [self essential];
1600 return !version_.end() && version_ != current;
1603 - (BOOL) essential {
1604 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1608 return [database_ cache][iterator_].InstBroken();
1611 - (BOOL) unfiltered {
1612 NSString *section = [self section];
1613 return section == nil || isSectionVisible(section);
1617 return [self hasSupportingRole] && [self unfiltered];
1621 unsigned char current = iterator_->CurrentState;
1622 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1625 - (BOOL) halfConfigured {
1626 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1629 - (BOOL) halfInstalled {
1630 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1634 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1635 return state.Mode != pkgDepCache::ModeKeep;
1638 - (NSString *) mode {
1639 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1641 switch (state.Mode) {
1642 case pkgDepCache::ModeDelete:
1643 if ((state.iFlags & pkgDepCache::Purge) != 0)
1647 case pkgDepCache::ModeKeep:
1648 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1652 case pkgDepCache::ModeInstall:
1653 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1654 return @"Reinstall";
1655 else switch (state.Status) {
1657 return @"Downgrade";
1663 return @"New Install";
1676 - (NSString *) name {
1677 return name_ == nil ? id_ : name_;
1680 - (NSString *) tagline {
1684 - (UIImage *) icon {
1685 NSString *section = [self simpleSection];
1688 if (NSString *icon = icon_)
1689 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1690 if (icon == nil) if (section != nil)
1691 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1692 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1693 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1695 icon = [UIImage applicationImageNamed:@"unknown.png"];
1699 - (NSString *) homepage {
1703 - (NSString *) depiction {
1707 - (Address *) sponsor {
1711 - (Address *) author {
1715 - (NSArray *) files {
1716 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1717 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1720 fin.open([path UTF8String]);
1725 while (std::getline(fin, line))
1726 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1731 - (NSArray *) relationships {
1732 return relationships_;
1735 - (NSArray *) warnings {
1736 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1737 const char *name(iterator_.Name());
1739 size_t length(strlen(name));
1740 if (length < 2) invalid:
1741 [warnings addObject:@"illegal package identifier"];
1742 else for (size_t i(0); i != length; ++i)
1744 (name[i] < 'a' || name[i] > 'z') &&
1745 (name[i] < '0' || name[i] > '9') &&
1746 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1749 if (strcmp(name, "cydia") != 0) {
1753 if (NSArray *files = [self files])
1754 for (NSString *file in files)
1755 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1757 else if (!stash && [file isEqualToString:@"/var/stash"])
1761 [warnings addObject:@"files installed into Cydia.app"];
1763 [warnings addObject:@"files installed to /var/stash"];
1766 return [warnings count] == 0 ? nil : warnings;
1769 - (NSArray *) applications {
1770 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1772 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1774 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1775 if (NSArray *files = [self files])
1776 for (NSString *file in files)
1777 if (application_r(file)) {
1778 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1779 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1780 if ([id isEqualToString:me])
1783 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1785 display = application_r[1];
1787 NSString *bundle([file stringByDeletingLastPathComponent]);
1788 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1789 if (icon == nil || [icon length] == 0)
1791 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1793 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1794 [applications addObject:application];
1796 [application addObject:id];
1797 [application addObject:display];
1798 [application addObject:url];
1801 return [applications count] == 0 ? nil : applications;
1804 - (Source *) source {
1806 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1813 - (NSString *) role {
1817 - (NSString *) rating {
1818 if (NSString *pattern = [Indices_ objectForKey:@"Rating"])
1819 return [pattern stringByReplacingOccurrencesOfString:@"%@" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1824 - (BOOL) matches:(NSString *)text {
1830 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1831 if (range.location != NSNotFound)
1834 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1835 if (range.location != NSNotFound)
1838 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1839 if (range.location != NSNotFound)
1845 - (bool) hasSupportingRole {
1848 if ([role_ isEqualToString:@"enduser"])
1850 if ([Role_ isEqualToString:@"User"])
1852 if ([role_ isEqualToString:@"hacker"])
1854 if ([Role_ isEqualToString:@"Hacker"])
1856 if ([role_ isEqualToString:@"developer"])
1858 if ([Role_ isEqualToString:@"Developer"])
1863 - (BOOL) hasTag:(NSString *)tag {
1864 return tags_ == nil ? NO : [tags_ containsObject:tag];
1867 - (NSString *) primaryPurpose {
1868 for (NSString *tag in tags_)
1869 if ([tag hasPrefix:@"purpose::"])
1870 return [tag substringFromIndex:9];
1874 - (NSArray *) purposes {
1875 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1876 for (NSString *tag in tags_)
1877 if ([tag hasPrefix:@"purpose::"])
1878 [purposes addObject:[tag substringFromIndex:9]];
1879 return [purposes count] == 0 ? nil : purposes;
1882 - (NSComparisonResult) compareByName:(Package *)package {
1883 NSString *lhs = [self name];
1884 NSString *rhs = [package name];
1886 if ([lhs length] != 0 && [rhs length] != 0) {
1887 unichar lhc = [lhs characterAtIndex:0];
1888 unichar rhc = [rhs characterAtIndex:0];
1890 if (isalpha(lhc) && !isalpha(rhc))
1891 return NSOrderedAscending;
1892 else if (!isalpha(lhc) && isalpha(rhc))
1893 return NSOrderedDescending;
1896 return [lhs compare:rhs options:LaxCompareOptions_];
1899 - (NSComparisonResult) compareBySection:(Package *)package {
1900 NSString *lhs = [self section];
1901 NSString *rhs = [package section];
1903 if (lhs == NULL && rhs != NULL)
1904 return NSOrderedAscending;
1905 else if (lhs != NULL && rhs == NULL)
1906 return NSOrderedDescending;
1907 else if (lhs != NULL && rhs != NULL) {
1908 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1909 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1912 return NSOrderedSame;
1915 - (uint32_t) compareForChanges {
1920 uint32_t timestamp : 30;
1921 uint32_t ignored : 1;
1922 uint32_t upgradable : 1;
1926 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1928 if ([self upgradableAndEssential:YES]) {
1929 value.bits.timestamp = 0;
1930 value.bits.ignored = [self ignored] ? 0 : 1;
1931 value.bits.upgradable = 1;
1933 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1934 value.bits.ignored = 0;
1935 value.bits.upgradable = 0;
1938 return _not(uint32_t) - value.key;
1942 pkgProblemResolver *resolver = [database_ resolver];
1943 resolver->Clear(iterator_);
1944 resolver->Protect(iterator_);
1945 pkgCacheFile &cache([database_ cache]);
1946 cache->MarkInstall(iterator_, false);
1947 pkgDepCache::StateCache &state((*cache)[iterator_]);
1948 if (!state.Install())
1949 cache->SetReInstall(iterator_, true);
1953 pkgProblemResolver *resolver = [database_ resolver];
1954 resolver->Clear(iterator_);
1955 resolver->Protect(iterator_);
1956 resolver->Remove(iterator_);
1957 [database_ cache]->MarkDelete(iterator_, true);
1960 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1961 return [NSNumber numberWithBool:(
1962 [self unfiltered] && [self matches:search]
1966 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1967 return [NSNumber numberWithBool:(
1968 (![number boolValue] || [self visible]) && [self installed] != nil
1972 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1973 NSString *section = [self section];
1975 return [NSNumber numberWithBool:(
1977 [self installed] == nil && (
1979 section == nil && [name length] == 0 ||
1980 [name isEqualToString:section]
1985 - (NSNumber *) isVisibleInSource:(Source *)source {
1986 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1991 /* Section Class {{{ */
1992 @interface Section : NSObject {
1998 - (NSComparisonResult) compareByName:(Section *)section;
1999 - (Section *) initWithName:(NSString *)name;
2000 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2001 - (NSString *) name;
2004 - (void) addToCount;
2008 @implementation Section
2015 - (NSComparisonResult) compareByName:(Section *)section {
2016 NSString *lhs = [self name];
2017 NSString *rhs = [section name];
2019 if ([lhs length] != 0 && [rhs length] != 0) {
2020 unichar lhc = [lhs characterAtIndex:0];
2021 unichar rhc = [rhs characterAtIndex:0];
2023 if (isalpha(lhc) && !isalpha(rhc))
2024 return NSOrderedAscending;
2025 else if (!isalpha(lhc) && isalpha(rhc))
2026 return NSOrderedDescending;
2029 return [lhs compare:rhs options:LaxCompareOptions_];
2032 - (Section *) initWithName:(NSString *)name {
2033 return [self initWithName:name row:0];
2036 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2037 if ((self = [super init]) != nil) {
2038 name_ = [name retain];
2043 - (NSString *) name {
2055 - (void) addToCount {
2063 static NSArray *Finishes_;
2065 /* Database Implementation {{{ */
2066 @implementation Database
2068 + (Database *) sharedInstance {
2069 static Database *instance;
2070 if (instance == nil)
2071 instance = [[Database alloc] init];
2080 - (void) _readCydia:(NSNumber *)fd { _pooled
2081 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2082 std::istream is(&ib);
2085 static Pcre finish_r("^finish:([^:]*)$");
2087 while (std::getline(is, line)) {
2088 const char *data(line.c_str());
2089 size_t size = line.size();
2090 lprintf("C:%s\n", data);
2092 if (finish_r(data, size)) {
2093 NSString *finish = finish_r[1];
2094 int index = [Finishes_ indexOfObject:finish];
2095 if (index != INT_MAX && index > Finish_)
2103 - (void) _readStatus:(NSNumber *)fd { _pooled
2104 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2105 std::istream is(&ib);
2108 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2109 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2111 while (std::getline(is, line)) {
2112 const char *data(line.c_str());
2113 size_t size = line.size();
2114 lprintf("S:%s\n", data);
2116 if (conffile_r(data, size)) {
2117 [delegate_ setConfigurationData:conffile_r[1]];
2118 } else if (strncmp(data, "status: ", 8) == 0) {
2119 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2120 [delegate_ setProgressTitle:string];
2121 } else if (pmstatus_r(data, size)) {
2122 std::string type([pmstatus_r[1] UTF8String]);
2123 NSString *id = pmstatus_r[2];
2125 float percent([pmstatus_r[3] floatValue]);
2126 [delegate_ setProgressPercent:(percent / 100)];
2128 NSString *string = pmstatus_r[4];
2130 if (type == "pmerror")
2131 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2132 withObject:[NSArray arrayWithObjects:string, id, nil]
2135 else if (type == "pmstatus") {
2136 [delegate_ setProgressTitle:string];
2137 } else if (type == "pmconffile")
2138 [delegate_ setConfigurationData:string];
2139 else _assert(false);
2140 } else _assert(false);
2146 - (void) _readOutput:(NSNumber *)fd { _pooled
2147 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2148 std::istream is(&ib);
2151 while (std::getline(is, line)) {
2152 lprintf("O:%s\n", line.c_str());
2153 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2163 - (Package *) packageWithName:(NSString *)name {
2164 if (static_cast<pkgDepCache *>(cache_) == NULL)
2166 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2167 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2170 - (Database *) init {
2171 if ((self = [super init]) != nil) {
2178 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2179 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2183 _assert(pipe(fds) != -1);
2186 _config->Set("APT::Keep-Fds::", cydiafd_);
2187 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2190 detachNewThreadSelector:@selector(_readCydia:)
2192 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2195 _assert(pipe(fds) != -1);
2199 detachNewThreadSelector:@selector(_readStatus:)
2201 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2204 _assert(pipe(fds) != -1);
2205 _assert(dup2(fds[0], 0) != -1);
2206 _assert(close(fds[0]) != -1);
2208 input_ = fdopen(fds[1], "a");
2210 _assert(pipe(fds) != -1);
2211 _assert(dup2(fds[1], 1) != -1);
2212 _assert(close(fds[1]) != -1);
2215 detachNewThreadSelector:@selector(_readOutput:)
2217 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2222 - (pkgCacheFile &) cache {
2226 - (pkgDepCache::Policy *) policy {
2230 - (pkgRecords *) records {
2234 - (pkgProblemResolver *) resolver {
2238 - (pkgAcquire &) fetcher {
2242 - (NSArray *) packages {
2246 - (NSArray *) sources {
2247 return [sources_ allValues];
2250 - (NSArray *) issues {
2251 if (cache_->BrokenCount() == 0)
2254 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2256 for (Package *package in packages_) {
2257 if (![package broken])
2259 pkgCache::PkgIterator pkg([package iterator]);
2261 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2262 [entry addObject:[package name]];
2263 [issues addObject:entry];
2265 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2269 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2270 pkgCache::DepIterator start;
2271 pkgCache::DepIterator end;
2272 dep.GlobOr(start, end); // ++dep
2274 if (!cache_->IsImportantDep(end))
2276 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2279 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2280 [entry addObject:failure];
2281 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2283 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2284 [failure addObject:[package name]];
2286 pkgCache::PkgIterator target(start.TargetPkg());
2287 if (target->ProvidesList != 0)
2288 [failure addObject:@"?"];
2290 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2292 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2293 else if (!cache_[target].CandidateVerIter(cache_).end())
2294 [failure addObject:@"-"];
2295 else if (target->ProvidesList == 0)
2296 [failure addObject:@"!"];
2298 [failure addObject:@"%"];
2302 if (start.TargetVer() != 0)
2303 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2314 - (void) reloadData {
2334 if (!cache_.Open(progress_, true)) {
2336 if (!_error->PopMessage(error))
2339 lprintf("cache_.Open():[%s]\n", error.c_str());
2341 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2342 [delegate_ repairWithSelector:@selector(configure)];
2343 else if (error == "The package lists or status file could not be parsed or opened.")
2344 [delegate_ repairWithSelector:@selector(update)];
2345 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2346 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2347 // else if (error == "The list of sources could not be read.")
2348 else _assert(false);
2354 now_ = [[NSDate date] retain];
2356 policy_ = new pkgDepCache::Policy();
2357 records_ = new pkgRecords(cache_);
2358 resolver_ = new pkgProblemResolver(cache_);
2359 fetcher_ = new pkgAcquire(&status_);
2362 list_ = new pkgSourceList();
2363 _assert(list_->ReadMainList());
2365 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2366 _assert(pkgApplyStatus(cache_));
2368 if (cache_->BrokenCount() != 0) {
2369 _assert(pkgFixBroken(cache_));
2370 _assert(cache_->BrokenCount() == 0);
2371 _assert(pkgMinimizeUpgrade(cache_));
2374 [sources_ removeAllObjects];
2375 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2376 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2377 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2379 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2380 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2384 [packages_ removeAllObjects];
2387 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2388 if (Package *package = [Package packageWithIterator:iterator database:self])
2389 [packages_ addObject:package];
2391 [packages_ sortUsingSelector:@selector(compareByName:)];
2395 - (void) configure {
2396 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2397 system([dpkg UTF8String]);
2405 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2406 _assert(!_error->PendingError());
2409 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2412 public pkgArchiveCleaner
2415 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2420 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2422 while (_error->PopMessage(error))
2423 lprintf("ArchiveCleaner: %s\n", error.c_str());
2428 pkgRecords records(cache_);
2430 lock_ = new FileFd();
2431 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2432 _assert(!_error->PendingError());
2435 // XXX: explain this with an error message
2436 _assert(list.ReadMainList());
2438 manager_ = (_system->CreatePM(cache_));
2439 _assert(manager_->GetArchives(fetcher_, &list, &records));
2440 _assert(!_error->PendingError());
2444 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2446 _assert(list.ReadMainList());
2447 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2448 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2451 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2456 bool failed = false;
2457 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2458 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2461 std::string uri = (*item)->DescURI();
2462 std::string error = (*item)->ErrorText;
2464 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2467 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2468 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2479 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2481 if (_error->PendingError()) {
2486 if (result == pkgPackageManager::Failed) {
2491 if (result != pkgPackageManager::Completed) {
2496 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2498 _assert(list.ReadMainList());
2499 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2500 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2503 if (![before isEqualToArray:after])
2508 _assert(pkgDistUpgrade(cache_));
2512 [self updateWithStatus:status_];
2515 - (void) updateWithStatus:(Status &)status {
2517 _assert(list.ReadMainList());
2520 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2521 _assert(!_error->PendingError());
2523 pkgAcquire fetcher(&status);
2524 _assert(list.GetIndexes(&fetcher));
2526 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2527 bool failed = false;
2528 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2529 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2530 (*item)->Finished();
2534 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2535 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2536 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2539 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2544 - (void) setDelegate:(id)delegate {
2545 delegate_ = delegate;
2546 status_.setDelegate(delegate);
2547 progress_.setDelegate(delegate);
2550 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2551 pkgIndexFile *index(NULL);
2552 list_->FindIndex(file, index);
2553 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2559 /* PopUp Windows {{{ */
2560 @interface PopUpView : UIView {
2561 _transient id delegate_;
2562 UITransitionView *transition_;
2567 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2571 @implementation PopUpView
2574 [transition_ setDelegate:nil];
2575 [transition_ release];
2581 [transition_ transition:UITransitionPushFromTop toView:nil];
2584 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2585 if (from != nil && to == nil)
2586 [self removeFromSuperview];
2589 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2590 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2591 delegate_ = delegate;
2593 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2594 [self addSubview:transition_];
2596 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2598 [view addSubview:self];
2600 [transition_ setDelegate:self];
2602 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2603 [transition_ transition:UITransitionNone toView:blank];
2604 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2611 /* Mail Composition {{{ */
2612 @interface MailToView : PopUpView {
2613 MailComposeController *controller_;
2616 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2620 @implementation MailToView
2623 [controller_ release];
2627 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2631 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2632 NSLog(@"did:%@", delivery);
2633 // [UIApp setStatusBarShowsProgress:NO];
2634 if ([controller error]){
2635 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2636 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2637 [mailAlertSheet setBodyText:[controller error]];
2638 [mailAlertSheet popupAlertAnimated:YES];
2642 - (void) showError {
2643 NSLog(@"%@", [controller_ error]);
2644 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2645 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2646 [mailAlertSheet setBodyText:[controller_ error]];
2647 [mailAlertSheet popupAlertAnimated:YES];
2650 - (void) deliverMessage { _pooled
2654 if (![controller_ deliverMessage])
2655 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2658 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2659 if ([controller_ needsDelivery])
2660 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2665 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2666 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2667 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2668 [controller_ setDelegate:self];
2669 [controller_ initializeUI];
2670 [controller_ setupForURL:url];
2672 UIView *view([controller_ view]);
2673 [overlay_ addSubview:view];
2679 /* Confirmation View {{{ */
2680 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2681 if (!iterator.end())
2682 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2683 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2685 pkgCache::PkgIterator package(dep.TargetPkg());
2688 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2695 @protocol ConfirmationViewDelegate
2700 @interface ConfirmationView : BrowserView {
2701 _transient Database *database_;
2702 UIActionSheet *essential_;
2709 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2713 @implementation ConfirmationView
2720 if (essential_ != nil)
2721 [essential_ release];
2727 [book_ popFromSuperviewAnimated:YES];
2730 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2731 NSString *context([sheet context]);
2733 if ([context isEqualToString:@"remove"]) {
2741 [delegate_ confirm];
2748 } else if ([context isEqualToString:@"unable"]) {
2752 [super alertSheet:sheet buttonClicked:button];
2755 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2756 [window setValue:changes_ forKey:@"changes"];
2757 [window setValue:issues_ forKey:@"issues"];
2758 [window setValue:sizes_ forKey:@"sizes"];
2759 [super webView:sender didClearWindowObject:window forFrame:frame];
2762 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2763 if ((self = [super initWithBook:book]) != nil) {
2764 database_ = database;
2766 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2767 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2768 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2769 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2770 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2774 pkgDepCache::Policy *policy([database_ policy]);
2776 pkgCacheFile &cache([database_ cache]);
2777 NSArray *packages = [database_ packages];
2778 for (size_t i(0), e = [packages count]; i != e; ++i) {
2779 Package *package = [packages objectAtIndex:i];
2780 pkgCache::PkgIterator iterator = [package iterator];
2781 pkgDepCache::StateCache &state(cache[iterator]);
2783 NSString *name([package name]);
2785 if (state.NewInstall())
2786 [installing addObject:name];
2787 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2788 [reinstalling addObject:name];
2789 else if (state.Upgrade())
2790 [upgrading addObject:name];
2791 else if (state.Downgrade())
2792 [downgrading addObject:name];
2793 else if (state.Delete()) {
2794 if ([package essential])
2796 [removing addObject:name];
2799 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2800 substrate_ |= DepSubstrate(iterator.CurrentVer());
2805 else if (Advanced_ || true) {
2806 essential_ = [[UIActionSheet alloc]
2807 initWithTitle:@"Removing Essentials"
2808 buttons:[NSArray arrayWithObjects:
2809 @"Cancel Operation (Safe)",
2810 @"Force Removal (Unsafe)",
2812 defaultButtonIndex:0
2818 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2820 [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."];
2822 essential_ = [[UIActionSheet alloc]
2823 initWithTitle:@"Unable to Comply"
2824 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2825 defaultButtonIndex:0
2830 [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."];
2833 changes_ = [[NSArray alloc] initWithObjects:
2841 issues_ = [database_ issues];
2843 issues_ = [issues_ retain];
2845 sizes_ = [[NSArray alloc] initWithObjects:
2846 SizeString([database_ fetcher].FetchNeeded()),
2847 SizeString([database_ fetcher].PartialPresent()),
2848 SizeString([database_ cache]->UsrSize()),
2851 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2855 - (NSString *) backButtonTitle {
2859 - (NSString *) leftButtonTitle {
2863 - (NSString *) _rightButtonTitle {
2864 return issues_ == nil ? @"Confirm" : nil;
2867 - (void) _leftButtonClicked {
2872 - (void) _rightButtonClicked {
2873 if (essential_ != nil)
2874 [essential_ popupAlertAnimated:YES];
2878 [delegate_ confirm];
2886 /* Progress Data {{{ */
2887 @interface ProgressData : NSObject {
2893 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2900 @implementation ProgressData
2902 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2903 if ((self = [super init]) != nil) {
2904 selector_ = selector;
2924 /* Progress View {{{ */
2925 @interface ProgressView : UIView <
2926 ConfigurationDelegate,
2929 _transient Database *database_;
2931 UIView *background_;
2932 UITransitionView *transition_;
2934 UINavigationBar *navbar_;
2935 UIProgressBar *progress_;
2936 UITextView *output_;
2937 UITextLabel *status_;
2938 UIPushButton *close_;
2941 SHA1SumValue springlist_;
2942 SHA1SumValue sandplate_;
2944 NSTimeInterval last_;
2947 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2949 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2950 - (void) setContentView:(UIView *)view;
2953 - (void) _retachThread;
2954 - (void) _detachNewThreadData:(ProgressData *)data;
2955 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2961 @protocol ProgressViewDelegate
2962 - (void) progressViewIsComplete:(ProgressView *)sender;
2965 @implementation ProgressView
2968 [transition_ setDelegate:nil];
2969 [navbar_ setDelegate:nil];
2972 if (background_ != nil)
2973 [background_ release];
2974 [transition_ release];
2977 [progress_ release];
2984 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2985 if (bootstrap_ && from == overlay_ && to == view_)
2989 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2990 if ((self = [super initWithFrame:frame]) != nil) {
2991 database_ = database;
2992 delegate_ = delegate;
2994 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2995 [transition_ setDelegate:self];
2997 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3000 [overlay_ setBackgroundColor:[UIColor blackColor]];
3002 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3003 [background_ setBackgroundColor:[UIColor blackColor]];
3004 [self addSubview:background_];
3007 [self addSubview:transition_];
3009 CGSize navsize = [UINavigationBar defaultSize];
3010 CGRect navrect = {{0, 0}, navsize};
3012 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3013 [overlay_ addSubview:navbar_];
3015 [navbar_ setBarStyle:1];
3016 [navbar_ setDelegate:self];
3018 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3019 [navbar_ pushNavigationItem:navitem];
3021 CGRect bounds = [overlay_ bounds];
3022 CGSize prgsize = [UIProgressBar defaultSize];
3025 (bounds.size.width - prgsize.width) / 2,
3026 bounds.size.height - prgsize.height - 20
3029 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3030 [progress_ setStyle:0];
3032 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3034 bounds.size.height - prgsize.height - 50,
3035 bounds.size.width - 20,
3039 [status_ setColor:[UIColor whiteColor]];
3040 [status_ setBackgroundColor:[UIColor clearColor]];
3042 [status_ setCentersHorizontally:YES];
3043 //[status_ setFont:font];
3045 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3047 navrect.size.height + 20,
3048 bounds.size.width - 20,
3049 bounds.size.height - navsize.height - 62 - navrect.size.height
3052 //[output_ setTextFont:@"Courier New"];
3053 [output_ setTextSize:12];
3055 [output_ setTextColor:[UIColor whiteColor]];
3056 [output_ setBackgroundColor:[UIColor clearColor]];
3058 [output_ setMarginTop:0];
3059 [output_ setAllowsRubberBanding:YES];
3060 [output_ setEditable:NO];
3062 [overlay_ addSubview:output_];
3064 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3066 bounds.size.height - prgsize.height - 50,
3067 bounds.size.width - 20,
3071 [close_ setAutosizesToFit:NO];
3072 [close_ setDrawsShadow:YES];
3073 [close_ setStretchBackground:YES];
3074 [close_ setEnabled:YES];
3076 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3077 [close_ setTitleFont:bold];
3079 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3080 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3081 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3085 - (void) setContentView:(UIView *)view {
3086 view_ = [view retain];
3089 - (void) resetView {
3090 [transition_ transition:6 toView:view_];
3093 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3094 NSString *context([sheet context]);
3096 if ([context isEqualToString:@"conffile"]) {
3097 FILE *input = [database_ input];
3101 fprintf(input, "N\n");
3105 fprintf(input, "Y\n");
3116 - (void) closeButtonPushed {
3125 [delegate_ suspendWithAnimation:YES];
3129 system("launchctl stop com.apple.SpringBoard");
3133 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3142 - (void) _retachThread {
3143 UINavigationItem *item = [navbar_ topItem];
3144 [item setTitle:@"Complete"];
3146 [overlay_ addSubview:close_];
3147 [progress_ removeFromSuperview];
3148 [status_ removeFromSuperview];
3150 [delegate_ progressViewIsComplete:self];
3153 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3154 MMap mmap(file, MMap::ReadOnly);
3156 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3157 if (!(sandplate_ == sha1.Result()))
3162 FileFd file(SpringBoard_, FileFd::ReadOnly);
3163 MMap mmap(file, MMap::ReadOnly);
3165 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3166 if (!(springlist_ == sha1.Result()))
3171 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3172 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3173 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3174 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3175 case 4: [close_ setTitle:@"Reboot Device"]; break;
3178 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3180 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3181 [cache autorelease];
3183 NSFileManager *manager = [NSFileManager defaultManager];
3184 NSError *error = nil;
3186 id system = [cache objectForKey:@"System"];
3191 if (stat(Cache_, &info) == -1)
3194 [system removeAllObjects];
3196 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3197 for (NSString *app in apps)
3198 if ([app hasSuffix:@".app"]) {
3199 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3200 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3201 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3203 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3204 [info setObject:path forKey:@"Path"];
3205 [info setObject:@"System" forKey:@"ApplicationType"];
3206 [system addInfoDictionary:info];
3212 [cache writeToFile:@Cache_ atomically:YES];
3214 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3216 if (chmod(Cache_, info.st_mode) == -1)
3220 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3223 notify_post("com.apple.mobile.application_installed");
3225 [delegate_ setStatusBarShowsProgress:NO];
3228 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3229 [[data target] performSelector:[data selector] withObject:[data object]];
3232 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3235 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3236 UINavigationItem *item = [navbar_ topItem];
3237 [item setTitle:title];
3239 [status_ setText:nil];
3240 [output_ setText:@""];
3241 [progress_ setProgress:0];
3244 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3246 [close_ removeFromSuperview];
3247 [overlay_ addSubview:progress_];
3248 [overlay_ addSubview:status_];
3250 [delegate_ setStatusBarShowsProgress:YES];
3254 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3255 MMap mmap(file, MMap::ReadOnly);
3257 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3258 sandplate_ = sha1.Result();
3262 FileFd file(SpringBoard_, FileFd::ReadOnly);
3263 MMap mmap(file, MMap::ReadOnly);
3265 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3266 springlist_ = sha1.Result();
3269 [transition_ transition:6 toView:overlay_];
3272 detachNewThreadSelector:@selector(_detachNewThreadData:)
3274 withObject:[[ProgressData alloc]
3275 initWithSelector:selector
3282 - (void) repairWithSelector:(SEL)selector {
3284 detachNewThreadSelector:selector
3291 - (void) setConfigurationData:(NSString *)data {
3293 performSelectorOnMainThread:@selector(_setConfigurationData:)
3299 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3300 Package *package = id == nil ? nil : [database_ packageWithName:id];
3302 UIActionSheet *sheet = [[[UIActionSheet alloc]
3303 initWithTitle:(package == nil ? id : [package name])
3304 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3305 defaultButtonIndex:0
3310 [sheet setBodyText:error];
3311 [sheet popupAlertAnimated:YES];
3314 - (void) setProgressTitle:(NSString *)title {
3316 performSelectorOnMainThread:@selector(_setProgressTitle:)
3322 - (void) setProgressPercent:(float)percent {
3324 performSelectorOnMainThread:@selector(_setProgressPercent:)
3325 withObject:[NSNumber numberWithFloat:percent]
3330 - (void) startProgress {
3331 last_ = [NSDate timeIntervalSinceReferenceDate];
3334 - (void) addProgressOutput:(NSString *)output {
3336 performSelectorOnMainThread:@selector(_addProgressOutput:)
3342 - (bool) isCancelling:(size_t)received {
3344 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3345 if (received_ != received) {
3346 received_ = received;
3348 } else if (now - last_ > 30)
3355 - (void) _setConfigurationData:(NSString *)data {
3356 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3358 _assert(conffile_r(data));
3360 NSString *ofile = conffile_r[1];
3361 //NSString *nfile = conffile_r[2];
3363 UIActionSheet *sheet = [[[UIActionSheet alloc]
3364 initWithTitle:@"Configuration Upgrade"
3365 buttons:[NSArray arrayWithObjects:
3366 @"Keep My Old Copy",
3367 @"Accept The New Copy",
3368 // XXX: @"See What Changed",
3370 defaultButtonIndex:0
3375 [sheet setBodyText:[NSString stringWithFormat:
3376 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3379 [sheet popupAlertAnimated:YES];
3382 - (void) _setProgressTitle:(NSString *)title {
3383 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3384 for (size_t i(0), e([words count]); i != e; ++i) {
3385 NSString *word([words objectAtIndex:i]);
3386 if (Package *package = [database_ packageWithName:word])
3387 [words replaceObjectAtIndex:i withObject:[package name]];
3390 [status_ setText:[words componentsJoinedByString:@" "]];
3393 - (void) _setProgressPercent:(NSNumber *)percent {
3394 [progress_ setProgress:[percent floatValue]];
3397 - (void) _addProgressOutput:(NSString *)output {
3398 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3399 CGSize size = [output_ contentSize];
3400 CGRect rect = {{0, size.height}, {size.width, 0}};
3401 [output_ scrollRectToVisible:rect animated:YES];
3404 - (BOOL) isRunning {
3411 /* Package Cell {{{ */
3412 @interface PackageCell : UISimpleTableCell {
3415 NSString *description_;
3419 UITextLabel *status_;
3423 - (PackageCell *) init;
3424 - (void) setPackage:(Package *)package;
3426 + (int) heightForPackage:(Package *)package;
3430 @implementation PackageCell
3432 - (void) clearPackage {
3443 if (description_ != nil) {
3444 [description_ release];
3448 if (source_ != nil) {
3453 if (badge_ != nil) {
3460 [self clearPackage];
3467 - (PackageCell *) init {
3468 if ((self = [super init]) != nil) {
3470 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3471 [status_ setBackgroundColor:[UIColor clearColor]];
3472 [status_ setFont:small];
3477 - (void) setPackage:(Package *)package {
3478 [self clearPackage];
3480 Source *source = [package source];
3481 NSString *section = [package simpleSection];
3483 icon_ = [[package icon] retain];
3485 name_ = [[package name] retain];
3486 description_ = [[package tagline] retain];
3488 NSString *label = nil;
3489 bool trusted = false;
3491 if (source != nil) {
3492 label = [source label];
3493 trusted = [source trusted];
3494 } else if ([[package id] isEqualToString:@"firmware"])
3497 label = @"Unknown/Local";
3499 NSString *from = [NSString stringWithFormat:@"from %@", label];
3501 if (section != nil && ![section isEqualToString:label])
3502 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3504 source_ = [from retain];
3506 if (NSString *purpose = [package primaryPurpose])
3507 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3508 badge_ = [badge_ retain];
3511 if (NSString *mode = [package mode]) {
3512 [badge_ setImage:[UIImage applicationImageNamed:
3513 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3516 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3517 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3518 } else if ([package half]) {
3519 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3520 [status_ setText:@"Package Damaged"];
3521 [status_ setColor:[UIColor redColor]];
3523 [badge_ setImage:nil];
3524 [status_ setText:nil];
3529 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3532 rect.size = [icon_ size];
3534 rect.size.width /= 2;
3535 rect.size.height /= 2;
3537 rect.origin.x = 25 - rect.size.width / 2;
3538 rect.origin.y = 25 - rect.size.height / 2;
3540 [icon_ drawInRect:rect];
3543 if (badge_ != nil) {
3544 CGSize size = [badge_ size];
3546 [badge_ drawAtPoint:CGPointMake(
3547 36 - size.width / 2,
3548 36 - size.height / 2
3557 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3558 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3562 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3564 [super drawContentInRect:rect selected:selected];
3567 + (int) heightForPackage:(Package *)package {
3568 NSString *tagline([package tagline]);
3569 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3571 if ([package hasMode] || [package half])
3580 /* Section Cell {{{ */
3581 @interface SectionCell : UISimpleTableCell {
3586 _UISwitchSlider *switch_;
3591 - (void) setSection:(Section *)section editing:(BOOL)editing;
3595 @implementation SectionCell
3597 - (void) clearSection {
3598 if (section_ != nil) {
3608 if (count_ != nil) {
3615 [self clearSection];
3622 if ((self = [super init]) != nil) {
3623 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3625 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3626 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3630 - (void) onSwitch:(id)sender {
3631 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3632 if (metadata == nil) {
3633 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3634 [Sections_ setObject:metadata forKey:section_];
3638 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3641 - (void) setSection:(Section *)section editing:(BOOL)editing {
3642 if (editing != editing_) {
3644 [switch_ removeFromSuperview];
3646 [self addSubview:switch_];
3650 [self clearSection];
3652 if (section == nil) {
3653 name_ = [@"All Packages" retain];
3656 section_ = [section name];
3657 if (section_ != nil)
3658 section_ = [section_ retain];
3659 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3660 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3663 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3667 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3668 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3675 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3677 CGSize size = [count_ sizeWithFont:Font14_];
3681 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3683 [super drawContentInRect:rect selected:selected];
3689 /* File Table {{{ */
3690 @interface FileTable : RVPage {
3691 _transient Database *database_;
3694 NSMutableArray *files_;
3698 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3699 - (void) setPackage:(Package *)package;
3703 @implementation FileTable
3706 if (package_ != nil)
3715 - (int) numberOfRowsInTable:(UITable *)table {
3716 return files_ == nil ? 0 : [files_ count];
3719 - (float) table:(UITable *)table heightForRow:(int)row {
3723 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3724 if (reusing == nil) {
3725 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3726 UIFont *font = [UIFont systemFontOfSize:16];
3727 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3729 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3733 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3737 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3738 if ((self = [super initWithBook:book]) != nil) {
3739 database_ = database;
3741 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3743 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3744 [self addSubview:list_];
3746 UITableColumn *column = [[[UITableColumn alloc]
3747 initWithTitle:@"Name"
3749 width:[self frame].size.width
3752 [list_ setDataSource:self];
3753 [list_ setSeparatorStyle:1];
3754 [list_ addTableColumn:column];
3755 [list_ setDelegate:self];
3756 [list_ setReusesTableCells:YES];
3760 - (void) setPackage:(Package *)package {
3761 if (package_ != nil) {
3762 [package_ autorelease];
3771 [files_ removeAllObjects];
3773 if (package != nil) {
3774 package_ = [package retain];
3775 name_ = [[package id] retain];
3777 if (NSArray *files = [package files])
3778 [files_ addObjectsFromArray:files];
3780 if ([files_ count] != 0) {
3781 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3782 [files_ removeObjectAtIndex:0];
3783 [files_ sortUsingSelector:@selector(compareByPath:)];
3785 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3786 [stack addObject:@"/"];
3788 for (int i(0), e([files_ count]); i != e; ++i) {
3789 NSString *file = [files_ objectAtIndex:i];
3790 while (![file hasPrefix:[stack lastObject]])
3791 [stack removeLastObject];
3792 NSString *directory = [stack lastObject];
3793 [stack addObject:[file stringByAppendingString:@"/"]];
3794 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3795 ([stack count] - 2) * 3, "",
3796 [file substringFromIndex:[directory length]]
3805 - (void) resetViewAnimated:(BOOL)animated {
3806 [list_ resetViewAnimated:animated];
3809 - (void) reloadData {
3810 [self setPackage:[database_ packageWithName:name_]];
3811 [self reloadButtons];
3814 - (NSString *) title {
3815 return @"Installed Files";
3818 - (NSString *) backButtonTitle {
3824 /* Package View {{{ */
3825 @interface PackageView : BrowserView {
3826 _transient Database *database_;
3829 NSMutableArray *buttons_;
3832 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3833 - (void) setPackage:(Package *)package;
3837 @implementation PackageView
3840 if (package_ != nil)
3848 - (void) _clickButtonWithName:(NSString *)name {
3849 if ([name isEqualToString:@"Install"])
3850 [delegate_ installPackage:package_];
3851 else if ([name isEqualToString:@"Reinstall"])
3852 [delegate_ installPackage:package_];
3853 else if ([name isEqualToString:@"Remove"])
3854 [delegate_ removePackage:package_];
3855 else if ([name isEqualToString:@"Upgrade"])
3856 [delegate_ installPackage:package_];
3857 else _assert(false);
3860 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3861 NSString *context([sheet context]);
3863 if ([context isEqualToString:@"modify"]) {
3864 int count = [buttons_ count];
3865 _assert(count != 0);
3866 _assert(button <= count + 1);
3868 if (count != button - 1)
3869 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3873 [super alertSheet:sheet buttonClicked:button];
3876 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3877 return [super webView:sender didFinishLoadForFrame:frame];
3880 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3881 [window setValue:package_ forKey:@"package"];
3882 [super webView:sender didClearWindowObject:window forFrame:frame];
3886 - (void) _rightButtonClicked {
3887 /*[super _rightButtonClicked];
3890 int count = [buttons_ count];
3891 _assert(count != 0);
3894 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3896 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3897 [buttons addObjectsFromArray:buttons_];
3898 [buttons addObject:@"Cancel"];
3900 [delegate_ slideUp:[[[UIActionSheet alloc]
3903 defaultButtonIndex:2
3911 - (NSString *) _rightButtonTitle {
3912 int count = [buttons_ count];
3913 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3916 - (NSString *) backButtonTitle {
3920 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3921 if ((self = [super initWithBook:book]) != nil) {
3922 database_ = database;
3923 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3927 - (void) setPackage:(Package *)package {
3928 if (package_ != nil) {
3929 [package_ autorelease];
3938 [buttons_ removeAllObjects];
3940 if (package != nil) {
3941 package_ = [package retain];
3942 name_ = [[package id] retain];
3944 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3946 if ([package_ source] == nil);
3947 else if ([package_ upgradableAndEssential:NO])
3948 [buttons_ addObject:@"Upgrade"];
3949 else if ([package_ installed] == nil)
3950 [buttons_ addObject:@"Install"];
3952 [buttons_ addObject:@"Reinstall"];
3953 if ([package_ installed] != nil)
3954 [buttons_ addObject:@"Remove"];
3958 - (void) reloadData {
3959 [self setPackage:[database_ packageWithName:name_]];
3960 [self reloadButtons];
3965 /* Package Table {{{ */
3966 @interface PackageTable : RVPage {
3967 _transient Database *database_;
3971 NSMutableArray *packages_;
3972 NSMutableArray *sections_;
3973 UISectionList *list_;
3976 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3978 - (void) setDelegate:(id)delegate;
3979 - (void) setObject:(id)object;
3981 - (void) reloadData;
3982 - (void) resetCursor;
3984 - (UISectionList *) list;
3986 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3990 @implementation PackageTable
3993 [list_ setDataSource:nil];
3998 [packages_ release];
3999 [sections_ release];
4004 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4005 return [sections_ count];
4008 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4009 return [[sections_ objectAtIndex:section] name];
4012 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4013 return [[sections_ objectAtIndex:section] row];
4016 - (int) numberOfRowsInTable:(UITable *)table {
4017 return [packages_ count];
4020 - (float) table:(UITable *)table heightForRow:(int)row {
4021 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4024 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4026 reusing = [[[PackageCell alloc] init] autorelease];
4027 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4031 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4035 - (void) tableRowSelected:(NSNotification *)notification {
4036 int row = [[notification object] selectedRow];
4040 Package *package = [packages_ objectAtIndex:row];
4041 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4042 [view setDelegate:delegate_];
4043 [view setPackage:package];
4044 [book_ pushPage:view];
4047 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4048 if ((self = [super initWithBook:book]) != nil) {
4049 database_ = database;
4050 title_ = [title retain];
4052 object_ = object == nil ? nil : [object retain];
4054 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4055 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4057 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4058 [list_ setDataSource:self];
4060 UITableColumn *column = [[[UITableColumn alloc]
4061 initWithTitle:@"Name"
4063 width:[self frame].size.width
4066 UITable *table = [list_ table];
4067 [table setSeparatorStyle:1];
4068 [table addTableColumn:column];
4069 [table setDelegate:self];
4070 [table setReusesTableCells:YES];
4072 [self addSubview:list_];
4075 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4076 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4080 - (void) setDelegate:(id)delegate {
4081 delegate_ = delegate;
4084 - (void) setObject:(id)object {
4090 object_ = [object retain];
4093 - (void) reloadData {
4094 NSArray *packages = [database_ packages];
4096 [packages_ removeAllObjects];
4097 [sections_ removeAllObjects];
4099 for (size_t i(0); i != [packages count]; ++i) {
4100 Package *package([packages objectAtIndex:i]);
4101 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4102 [packages_ addObject:package];
4105 Section *section = nil;
4107 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4108 Package *package = [packages_ objectAtIndex:offset];
4109 NSString *name = [package index];
4111 if (section == nil || ![[section name] isEqualToString:name]) {
4112 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4113 [sections_ addObject:section];
4116 [section addToCount];
4122 - (NSString *) title {
4126 - (void) resetViewAnimated:(BOOL)animated {
4127 [list_ resetViewAnimated:animated];
4130 - (void) resetCursor {
4131 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4134 - (UISectionList *) list {
4138 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4139 [list_ setShouldHideHeaderInShortLists:hide];
4145 /* Add Source View {{{ */
4146 @interface AddSourceView : RVPage {
4147 _transient Database *database_;
4150 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4154 @implementation AddSourceView
4156 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4157 if ((self = [super initWithBook:book]) != nil) {
4158 database_ = database;
4164 /* Source Cell {{{ */
4165 @interface SourceCell : UITableCell {
4168 NSString *description_;
4174 - (SourceCell *) initWithSource:(Source *)source;
4178 @implementation SourceCell
4183 [description_ release];
4188 - (SourceCell *) initWithSource:(Source *)source {
4189 if ((self = [super init]) != nil) {
4191 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4193 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4194 icon_ = [icon_ retain];
4196 origin_ = [[source name] retain];
4197 label_ = [[source uri] retain];
4198 description_ = [[source description] retain];
4202 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4204 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4211 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4215 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4219 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4221 [super drawContentInRect:rect selected:selected];
4226 /* Source Table {{{ */
4227 @interface SourceTable : RVPage {
4228 _transient Database *database_;
4229 UISectionList *list_;
4230 NSMutableArray *sources_;
4231 UIActionSheet *alert_;
4235 UIProgressHUD *hud_;
4238 //NSURLConnection *installer_;
4239 NSURLConnection *trivial_bz2_;
4240 NSURLConnection *trivial_gz_;
4241 //NSURLConnection *automatic_;
4246 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4250 @implementation SourceTable
4252 - (void) _deallocConnection:(NSURLConnection *)connection {
4253 if (connection != nil) {
4254 [connection cancel];
4255 //[connection setDelegate:nil];
4256 [connection release];
4261 [[list_ table] setDelegate:nil];
4262 [list_ setDataSource:nil];
4271 //[self _deallocConnection:installer_];
4272 [self _deallocConnection:trivial_gz_];
4273 [self _deallocConnection:trivial_bz2_];
4274 //[self _deallocConnection:automatic_];
4281 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4282 return offset_ == 0 ? 1 : 2;
4285 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4286 switch (section + (offset_ == 0 ? 1 : 0)) {
4287 case 0: return @"Entered by User";
4288 case 1: return @"Installed by Packages";
4296 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4297 switch (section + (offset_ == 0 ? 1 : 0)) {
4299 case 1: return offset_;
4307 - (int) numberOfRowsInTable:(UITable *)table {
4308 return [sources_ count];
4311 - (float) table:(UITable *)table heightForRow:(int)row {
4312 Source *source = [sources_ objectAtIndex:row];
4313 return [source description] == nil ? 56 : 73;
4316 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4317 Source *source = [sources_ objectAtIndex:row];
4318 // XXX: weird warning, stupid selectors ;P
4319 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4322 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4326 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4330 - (void) tableRowSelected:(NSNotification*)notification {
4331 UITable *table([list_ table]);
4332 int row([table selectedRow]);
4336 Source *source = [sources_ objectAtIndex:row];
4338 PackageTable *packages = [[[PackageTable alloc]
4341 title:[source label]
4342 filter:@selector(isVisibleInSource:)
4346 [packages setDelegate:delegate_];
4348 [book_ pushPage:packages];
4351 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4352 Source *source = [sources_ objectAtIndex:row];
4353 return [source record] != nil;
4356 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4357 [[list_ table] setDeleteConfirmationRow:row];
4360 - (void) table:(UITable *)table deleteRow:(int)row {
4361 Source *source = [sources_ objectAtIndex:row];
4362 [Sources_ removeObjectForKey:[source key]];
4363 [delegate_ syncData];
4366 - (void) _endConnection:(NSURLConnection *)connection {
4367 NSURLConnection **field = NULL;
4368 if (connection == trivial_bz2_)
4369 field = &trivial_bz2_;
4370 else if (connection == trivial_gz_)
4371 field = &trivial_gz_;
4372 _assert(field != NULL);
4373 [connection release];
4377 trivial_bz2_ == nil &&
4380 [delegate_ setStatusBarShowsProgress:NO];
4383 [hud_ removeFromSuperview];
4388 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4391 @"./", @"Distribution",
4392 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4394 [delegate_ syncData];
4395 } else if (error_ != nil) {
4396 UIActionSheet *sheet = [[[UIActionSheet alloc]
4397 initWithTitle:@"Verification Error"
4398 buttons:[NSArray arrayWithObjects:@"OK", nil]
4399 defaultButtonIndex:0
4404 [sheet setBodyText:[error_ localizedDescription]];
4405 [sheet popupAlertAnimated:YES];
4407 UIActionSheet *sheet = [[[UIActionSheet alloc]
4408 initWithTitle:@"Did not Find Repository"
4409 buttons:[NSArray arrayWithObjects:@"OK", nil]
4410 defaultButtonIndex:0
4415 [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."];
4416 [sheet popupAlertAnimated:YES];
4422 if (error_ != nil) {
4429 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4430 switch ([response statusCode]) {
4436 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4437 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4439 error_ = [error retain];
4440 [self _endConnection:connection];
4443 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4444 [self _endConnection:connection];
4447 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4448 NSMutableURLRequest *request = [NSMutableURLRequest
4449 requestWithURL:[NSURL URLWithString:href]
4450 cachePolicy:NSURLRequestUseProtocolCachePolicy
4451 timeoutInterval:20.0
4454 [request setHTTPMethod:method];
4456 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4459 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4460 NSString *context([sheet context]);
4462 if ([context isEqualToString:@"source"]) {
4465 NSString *href = [[sheet textField] text];
4467 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4469 if (![href hasSuffix:@"/"])
4470 href_ = [href stringByAppendingString:@"/"];
4473 href_ = [href_ retain];
4475 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4476 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4477 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4481 hud_ = [delegate_ addProgressHUD];
4482 [hud_ setText:@"Verifying URL"];
4493 } else if ([context isEqualToString:@"trivial"])
4495 else if ([context isEqualToString:@"urlerror"])
4499 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4500 if ((self = [super initWithBook:book]) != nil) {
4501 database_ = database;
4502 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4504 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4505 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4506 [list_ setShouldHideHeaderInShortLists:NO];
4508 [self addSubview:list_];
4509 [list_ setDataSource:self];
4511 UITableColumn *column = [[UITableColumn alloc]
4512 initWithTitle:@"Name"
4514 width:[self frame].size.width
4517 UITable *table = [list_ table];
4518 [table setSeparatorStyle:1];
4519 [table addTableColumn:column];
4520 [table setDelegate:self];
4524 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4525 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4529 - (void) reloadData {
4531 _assert(list.ReadMainList());
4533 [sources_ removeAllObjects];
4534 [sources_ addObjectsFromArray:[database_ sources]];
4536 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4539 int count = [sources_ count];
4540 for (offset_ = 0; offset_ != count; ++offset_) {
4541 Source *source = [sources_ objectAtIndex:offset_];
4542 if ([source record] == nil)
4549 - (void) resetViewAnimated:(BOOL)animated {
4550 [list_ resetViewAnimated:animated];
4553 - (void) _leftButtonClicked {
4554 /*[book_ pushPage:[[[AddSourceView alloc]
4559 UIActionSheet *sheet = [[[UIActionSheet alloc]
4560 initWithTitle:@"Enter Cydia/APT URL"
4561 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4562 defaultButtonIndex:0
4567 [sheet setNumberOfRows:1];
4569 [sheet addTextFieldWithValue:@"http://" label:@""];
4571 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4572 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4573 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4574 [traits setKeyboardType:UIKeyboardTypeURL];
4575 // XXX: UIReturnKeyDone
4576 [traits setReturnKeyType:UIReturnKeyNext];
4578 [sheet popupAlertAnimated:YES];
4581 - (void) _rightButtonClicked {
4582 UITable *table = [list_ table];
4583 BOOL editing = [table isRowDeletionEnabled];
4584 [table enableRowDeletion:!editing animated:YES];
4585 [book_ reloadButtonsForPage:self];
4588 - (NSString *) title {
4592 - (NSString *) leftButtonTitle {
4593 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4596 - (NSString *) rightButtonTitle {
4597 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4600 - (UINavigationButtonStyle) rightButtonStyle {
4601 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4607 /* Installed View {{{ */
4608 @interface InstalledView : RVPage {
4609 _transient Database *database_;
4610 PackageTable *packages_;
4614 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4618 @implementation InstalledView
4621 [packages_ release];
4625 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4626 if ((self = [super initWithBook:book]) != nil) {
4627 database_ = database;
4629 packages_ = [[PackageTable alloc]
4633 filter:@selector(isInstalledAndVisible:)
4634 with:[NSNumber numberWithBool:YES]
4637 [self addSubview:packages_];
4639 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4640 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4644 - (void) resetViewAnimated:(BOOL)animated {
4645 [packages_ resetViewAnimated:animated];
4648 - (void) reloadData {
4649 [packages_ reloadData];
4652 - (void) _rightButtonClicked {
4653 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4654 [packages_ reloadData];
4656 [book_ reloadButtonsForPage:self];
4659 - (NSString *) title {
4660 return @"Installed";
4663 - (NSString *) backButtonTitle {
4667 - (NSString *) rightButtonTitle {
4668 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4671 - (UINavigationButtonStyle) rightButtonStyle {
4672 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4675 - (void) setDelegate:(id)delegate {
4676 [super setDelegate:delegate];
4677 [packages_ setDelegate:delegate];
4684 @interface HomeView : BrowserView {
4689 @implementation HomeView
4691 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4692 NSString *context([sheet context]);
4694 if ([context isEqualToString:@"about"])
4697 [super alertSheet:sheet buttonClicked:button];
4700 - (void) _leftButtonClicked {
4701 UIActionSheet *sheet = [[[UIActionSheet alloc]
4702 initWithTitle:@"About Cydia Installer"
4703 buttons:[NSArray arrayWithObjects:@"Close", nil]
4704 defaultButtonIndex:0
4710 @"Copyright (C) 2008\n"
4711 "Jay Freeman (saurik)\n"
4712 "saurik@saurik.com\n"
4713 "http://www.saurik.com/\n"
4716 "http://www.theokorigroup.com/\n"
4718 "College of Creative Studies,\n"
4719 "University of California,\n"
4721 "http://www.ccs.ucsb.edu/"
4724 [sheet popupAlertAnimated:YES];
4727 - (NSString *) leftButtonTitle {
4733 /* Manage View {{{ */
4734 @interface ManageView : BrowserView {
4739 @implementation ManageView
4741 - (NSString *) title {
4745 - (void) _leftButtonClicked {
4746 [delegate_ askForSettings];
4749 - (NSString *) leftButtonTitle {
4754 - (NSString *) _rightButtonTitle {
4762 @interface WebView (Cydia)
4763 - (void) setScriptDebugDelegate:(id)delegate;
4764 - (void) _setFormDelegate:(id)delegate;
4765 - (void) _setUIKitDelegate:(id)delegate;
4766 - (void) setWebMailDelegate:(id)delegate;
4767 - (void) _setLayoutInterval:(float)interval;
4770 /* Indirect Delegate {{{ */
4771 @interface IndirectDelegate : NSProxy {
4772 _transient volatile id delegate_;
4775 - (void) setDelegate:(id)delegate;
4776 - (id) initWithDelegate:(id)delegate;
4779 @implementation IndirectDelegate
4781 - (void) setDelegate:(id)delegate {
4782 delegate_ = delegate;
4785 - (id) initWithDelegate:(id)delegate {
4786 delegate_ = delegate;
4790 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4791 if (delegate_ != nil)
4792 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4794 // XXX: I fucking hate Apple so very very bad
4795 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4798 - (void) forwardInvocation:(NSInvocation *)inv {
4799 SEL sel = [inv selector];
4800 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4801 [inv invokeWithTarget:delegate_];
4806 /* Browser Implementation {{{ */
4807 @implementation BrowserView
4808 #include "internals.h"
4811 if (challenge_ != nil)
4812 [challenge_ release];
4814 WebView *webview = [webview_ webView];
4815 [webview setFrameLoadDelegate:nil];
4816 [webview setResourceLoadDelegate:nil];
4817 [webview setUIDelegate:nil];
4818 [webview setScriptDebugDelegate:nil];
4819 [webview setPolicyDelegate:nil];
4821 [webview setDownloadDelegate:nil];
4823 [webview _setFormDelegate:nil];
4824 [webview _setUIKitDelegate:nil];
4825 [webview setWebMailDelegate:nil];
4826 [webview setEditingDelegate:nil];
4828 [webview_ setDelegate:nil];
4829 [webview_ setGestureDelegate:nil];
4831 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4836 [webview_ removeFromSuperview];
4837 [Documents_ addObject:[webview_ autorelease]];
4842 [indirect_ setDelegate:nil];
4843 [indirect_ release];
4845 [scroller_ setDelegate:nil];
4847 [background_ release];
4848 [scroller_ release];
4850 [indicator_ release];
4851 if (confirm_ != nil)
4858 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4859 [self loadRequest:[NSURLRequest
4862 timeoutInterval:30.0
4866 - (void) loadURL:(NSURL *)url {
4867 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4870 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4871 NSMutableURLRequest *copy = [request mutableCopy];
4873 if (Machine_ != NULL)
4874 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4875 if (UniqueID_ != nil)
4876 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4879 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4884 - (void) loadRequest:(NSURLRequest *)request {
4886 [webview_ loadRequest:request];
4889 - (void) reloadURL {
4890 if ([urls_ count] == 0)
4892 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4893 [urls_ removeLastObject];
4894 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4897 - (WebView *) webView {
4898 return [webview_ webView];
4901 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4902 [scroller_ setContentSize:frame.size];
4905 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4906 [self view:sender didSetFrame:frame];
4909 - (void) pushPage:(RVPage *)page {
4910 [self setBackButtonTitle:title_];
4911 [page setDelegate:delegate_];
4912 [book_ pushPage:page];
4915 - (BOOL) getSpecial:(NSURL *)url {
4916 NSString *href([url absoluteString]);
4917 NSString *scheme([[url scheme] lowercaseString]);
4921 if ([href hasPrefix:@"apptapp://package/"])
4922 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4923 else if ([scheme isEqualToString:@"cydia"]) {
4924 page = [delegate_ pageForURL:url hasTag:NULL];
4927 } else if (![scheme isEqualToString:@"apptapp"])
4931 [self pushPage:page];
4935 - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4936 UIActionSheet *sheet = [[[UIActionSheet alloc]
4937 initWithTitle:@"JavaScript Alert"
4938 buttons:[NSArray arrayWithObjects:@"OK", nil]
4939 defaultButtonIndex:0
4944 [sheet setBodyText:message];
4945 [sheet popupAlertAnimated:YES];
4948 - (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4949 UIActionSheet *sheet = [[[UIActionSheet alloc]
4950 initWithTitle:@"JavaScript Confirm"
4951 buttons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil]
4952 defaultButtonIndex:0
4957 [sheet setNumberOfRows:1];
4958 [sheet setBodyText:message];
4959 [sheet popupAlertAnimated:YES];
4961 NSRunLoop *loop([NSRunLoop currentRunLoop]);
4962 NSDate *future([NSDate distantFuture]);
4964 while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
4966 NSNumber *confirm([confirm_ autorelease]);
4968 return [confirm boolValue];
4971 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4972 [window setValue:delegate_ forKey:@"cydia"];
4975 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4976 if (NSURL *url = [request URL]) {
4977 NSLog(@"win:%@:%@", url, [action description]);
4978 if (![self getSpecial:url]) {
4979 NSString *scheme([[url scheme] lowercaseString]);
4980 if ([scheme isEqualToString:@"mailto"])
4981 [delegate_ openMailToURL:url];
4990 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4991 if ([WebView canShowMIMEType:type])
4994 // XXX: handle more mime types!
4996 if (frame == [webView mainFrame])
4997 [UIApp openURL:[request URL]];
5001 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
5002 NSURL *url([request URL]);
5004 if (url == nil) use: {
5008 else NSLog(@"nav:%@:%@", url, [action description]);
5010 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
5013 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
5014 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
5017 [UIApp openURL:url];
5023 int store(_not(int));
5024 if (NSURL *itms = [url itmsURL:&store]) {
5025 NSLog(@"itms#%@#%u#%@", url, store, itms);
5027 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
5028 store == 2 && [capability containsObject:@"com.apple.AppStore"]
5035 NSString *scheme([[url scheme] lowercaseString]);
5037 if ([scheme isEqualToString:@"tel"]) {
5038 // XXX: intelligence
5042 if ([scheme isEqualToString:@"mailto"]) {
5043 [delegate_ openMailToURL:url];
5047 if ([self getSpecial:url])
5049 else if ([WebView _canHandleRequest:request])
5051 else if ([url isSpringboardHandledURL])
5057 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
5058 //lprintf("Status:%s\n", [text UTF8String]);
5061 - (void) _pushPage {
5065 [book_ pushPage:self];
5068 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5069 NSString *context([sheet context]);
5071 if ([context isEqualToString:@"alert"])
5073 else if ([context isEqualToString:@"confirm"]) {
5076 confirm_ = [NSNumber numberWithBool:YES];
5080 confirm_ = [NSNumber numberWithBool:NO];
5085 } else if ([context isEqualToString:@"challenge"]) {
5086 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
5090 NSString *username([[sheet textFieldAtIndex:0] text]);
5091 NSString *password([[sheet textFieldAtIndex:1] text]);
5093 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
5095 [sender useCredential:credential forAuthenticationChallenge:challenge_];
5099 [sender cancelAuthenticationChallenge:challenge_];
5106 [challenge_ release];
5113 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
5114 challenge_ = [challenge retain];
5116 NSURLProtectionSpace *space([challenge protectionSpace]);
5117 NSString *realm([space realm]);
5121 UIActionSheet *sheet = [[[UIActionSheet alloc]
5123 buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
5124 defaultButtonIndex:0
5126 context:@"challenge"
5129 [sheet setNumberOfRows:1];
5131 [sheet addTextFieldWithValue:@"" label:@"username"];
5132 [sheet addTextFieldWithValue:@"" label:@"password"];
5134 UITextField *username([sheet textFieldAtIndex:0]); {
5135 UITextInputTraits *traits([username textInputTraits]);
5136 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5137 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5138 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5139 [traits setReturnKeyType:UIReturnKeyNext];
5142 UITextField *password([sheet textFieldAtIndex:1]); {
5143 UITextInputTraits *traits([password textInputTraits]);
5144 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5145 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5146 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5147 // XXX: UIReturnKeyDone
5148 [traits setReturnKeyType:UIReturnKeyNext];
5149 [traits setSecureTextEntry:YES];
5152 [sheet popupAlertAnimated:YES];
5155 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
5156 NSURL *url = [request URL];
5157 if ([self getSpecial:url])
5160 return [self _addHeadersToRequest:request];
5163 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
5164 [self setBackButtonTitle:title_];
5166 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
5167 [browser setDelegate:delegate_];
5170 [browser loadRequest:[self _addHeadersToRequest:request]];
5171 [book_ pushPage:browser];
5174 return [browser webView];
5177 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
5178 return [self _createWebViewWithRequest:request pushed:(request != nil)];
5181 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
5182 return [self _createWebViewWithRequest:request pushed:YES];
5185 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
5186 if ([frame parentFrame] != nil)
5189 title_ = [title retain];
5190 [book_ reloadTitleForPage:self];
5193 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
5194 if ([frame parentFrame] != nil)
5199 [indicator_ startAnimation];
5200 [self reloadButtons];
5202 if (title_ != nil) {
5207 [book_ reloadTitleForPage:self];
5209 WebView *webview = [webview_ webView];
5210 NSString *href = [webview mainFrameURL];
5211 [urls_ addObject:[NSURL URLWithString:href]];
5213 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
5215 CGRect webrect = [scroller_ bounds];
5216 webrect.size.height = 0;
5217 [webview_ setFrame:webrect];
5220 - (void) _finishLoading {
5223 [indicator_ stopAnimation];
5224 [self reloadButtons];
5228 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5229 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5232 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5233 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5236 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5237 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5240 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5241 return [webview_ webView:sender didCommitLoadForFrame:frame];
5244 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5245 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5248 - (void) _clearBackground {
5249 [background_ setBackgroundColor:[UIColor pinStripeColor]];
5250 [background_ setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5251 [scroller_ setShowBackgroundShadow:NO];
5254 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5255 if ([frame parentFrame] == nil) {
5256 [self _finishLoading];
5258 [self _clearBackground];
5260 if (DOMDocument *document = [frame DOMDocument])
5261 if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
5262 for (DOMHTMLBodyElement *body in bodies) {
5263 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
5265 bool colored(false);
5267 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
5268 DOMRGBColor *rgb([color getRGBColorValue]);
5270 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
5271 NSLog(@"alpha:%g", alpha);
5276 [background_ setBackgroundColor:[UIColor
5277 colorWithRed:([[rgb red] getFloatValue:DOM_CSS_NUMBER] / 255)
5278 green:([[rgb green] getFloatValue:DOM_CSS_NUMBER] / 255)
5279 blue:([[rgb blue] getFloatValue:DOM_CSS_NUMBER] / 255)
5285 if (DOMCSSPrimitiveValue *image = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-image"])) {
5286 NSString *src([image getStringValue]);
5287 if ([src isEqualToString:@"none"])
5289 NSLog(@"img:%@", [image getStringValue]);
5290 } else none: if (colored)
5291 [background_ setImage:nil];
5297 return [webview_ webView:sender didFinishLoadForFrame:frame];
5300 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5301 if ([frame parentFrame] != nil)
5303 [self _finishLoading];
5305 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5306 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5307 [[error localizedDescription] stringByAddingPercentEscapes]
5311 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5313 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5317 - (id) initWithBook:(RVBook *)book {
5318 if ((self = [super initWithBook:book]) != nil) {
5321 struct CGRect bounds = [self bounds];
5323 background_ = [[UIImageView alloc] initWithFrame:bounds];
5324 [self _clearBackground];
5325 [self addSubview:background_];
5327 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5328 [self addSubview:scroller_];
5330 [scroller_ setScrollingEnabled:YES];
5331 [scroller_ setAdjustForContentSizeChange:YES];
5332 [scroller_ setClipsSubviews:YES];
5333 [scroller_ setAllowsRubberBanding:YES];
5334 [scroller_ setScrollDecelerationFactor:0.99];
5335 [scroller_ setDelegate:self];
5337 CGRect webrect = [scroller_ bounds];
5338 webrect.size.height = 0;
5343 webview_ = [Documents_ lastObject];
5344 if (webview_ != nil) {
5345 webview_ = [webview_ retain];
5346 webview = [webview_ webView];
5347 [Documents_ removeLastObject];
5348 [webview_ setFrame:webrect];
5353 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5354 webview = [webview_ webView];
5356 // XXX: this is terribly (too?) expensive
5357 [webview_ setDrawsBackground:NO];
5359 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5361 [webview_ setAllowsMessaging:YES];
5363 [webview_ setTilingEnabled:YES];
5364 [webview_ setDrawsGrid:NO];
5365 [webview_ setLogsTilingChanges:NO];
5366 [webview_ setTileMinificationFilter:kCAFilterNearest];
5367 [webview_ setDetectsPhoneNumbers:NO];
5368 [webview_ setAutoresizes:YES];
5370 [webview_ setMinimumScale:0.25f forDocumentTypes:0x10];
5371 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10];
5372 [webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
5374 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2];
5376 [webview_ setMinimumScale:1.0f forDocumentTypes:0x8];
5377 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8];
5378 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8];
5380 [webview_ _setDocumentType:0x4];
5382 [webview_ setZoomsFocusedFormControl:YES];
5383 [webview_ setContentsPosition:7];
5384 [webview_ setEnabledGestures:0xa];
5385 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
5386 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
5388 [webview_ setSmoothsFonts:YES];
5390 [webview _setUsesLoaderCache:YES];
5391 [webview setGroupName:@"Cydia"];
5392 [webview _setLayoutInterval:0];
5395 [webview_ setDelegate:self];
5396 [webview_ setGestureDelegate:self];
5397 [scroller_ addSubview:webview_];
5399 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5401 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5402 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5403 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5405 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5406 NSString *application = package == nil ? @"Cydia" : [NSString
5407 stringWithFormat:@"Cydia/%@",
5409 ]; [webview setApplicationNameForUserAgent:application];
5411 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5413 [webview setFrameLoadDelegate:self];
5414 [webview setResourceLoadDelegate:indirect_];
5415 [webview setUIDelegate:self];
5416 [webview setScriptDebugDelegate:self];
5417 [webview setPolicyDelegate:self];
5419 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5421 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5422 [background_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5423 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5427 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5428 [webview_ redrawScaledDocument];
5431 - (void) _rightButtonClicked {
5436 - (NSString *) _rightButtonTitle {
5440 - (NSString *) rightButtonTitle {
5441 return loading_ ? @"" : [self _rightButtonTitle];
5444 - (NSString *) title {
5445 return title_ == nil ? @"Loading" : title_;
5448 - (NSString *) backButtonTitle {
5452 - (void) setPageActive:(BOOL)active {
5454 [indicator_ removeFromSuperview];
5456 [[book_ navigationBar] addSubview:indicator_];
5459 - (void) resetViewAnimated:(BOOL)animated {
5462 - (void) setPushed:(bool)pushed {
5469 /* Cydia Book {{{ */
5470 @interface CYBook : RVBook <
5473 _transient Database *database_;
5474 UINavigationBar *overlay_;
5475 UINavigationBar *underlay_;
5476 UIProgressIndicator *indicator_;
5477 UITextLabel *prompt_;
5478 UIProgressBar *progress_;
5479 UINavigationButton *cancel_;
5482 NSTimeInterval last_;
5485 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5491 @implementation CYBook
5495 [indicator_ release];
5497 [progress_ release];
5502 - (NSString *) getTitleForPage:(RVPage *)page {
5503 return Simplify([super getTitleForPage:page]);
5511 [UIView beginAnimations:nil context:NULL];
5513 CGRect ovrframe = [overlay_ frame];
5514 ovrframe.origin.y = 0;
5515 [overlay_ setFrame:ovrframe];
5517 CGRect barframe = [navbar_ frame];
5518 barframe.origin.y += ovrframe.size.height;
5519 [navbar_ setFrame:barframe];
5521 CGRect trnframe = [transition_ frame];
5522 trnframe.origin.y += ovrframe.size.height;
5523 trnframe.size.height -= ovrframe.size.height;
5524 [transition_ setFrame:trnframe];
5526 [UIView endAnimations];
5528 [indicator_ startAnimation];
5529 [prompt_ setText:@"Updating Database"];
5530 [progress_ setProgress:0];
5533 last_ = [NSDate timeIntervalSinceReferenceDate];
5535 [overlay_ addSubview:cancel_];
5538 detachNewThreadSelector:@selector(_update)
5547 [indicator_ stopAnimation];
5549 [UIView beginAnimations:nil context:NULL];
5551 CGRect ovrframe = [overlay_ frame];
5552 ovrframe.origin.y = -ovrframe.size.height;
5553 [overlay_ setFrame:ovrframe];
5555 CGRect barframe = [navbar_ frame];
5556 barframe.origin.y -= ovrframe.size.height;
5557 [navbar_ setFrame:barframe];
5559 CGRect trnframe = [transition_ frame];
5560 trnframe.origin.y -= ovrframe.size.height;
5561 trnframe.size.height += ovrframe.size.height;
5562 [transition_ setFrame:trnframe];
5564 [UIView commitAnimations];
5566 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5569 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5570 if ((self = [super initWithFrame:frame]) != nil) {
5571 database_ = database;
5573 CGRect ovrrect = [navbar_ bounds];
5574 ovrrect.size.height = [UINavigationBar defaultSize].height;
5575 ovrrect.origin.y = -ovrrect.size.height;
5577 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5578 [self addSubview:overlay_];
5580 ovrrect.origin.y = frame.size.height;
5581 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5582 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5583 [self addSubview:underlay_];
5585 [overlay_ setBarStyle:1];
5586 [underlay_ setBarStyle:1];
5588 int barstyle = [overlay_ _barStyle:NO];
5589 bool ugly = barstyle == 0;
5591 UIProgressIndicatorStyle style = ugly ?
5592 UIProgressIndicatorStyleMediumBrown :
5593 UIProgressIndicatorStyleMediumWhite;
5595 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5596 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5597 CGRect indrect = {{indoffset, indoffset}, indsize};
5599 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5600 [indicator_ setStyle:style];
5601 [overlay_ addSubview:indicator_];
5603 CGSize prmsize = {215, indsize.height + 4};
5606 indoffset * 2 + indsize.width,
5610 unsigned(ovrrect.size.height - prmsize.height) / 2
5613 UIFont *font = [UIFont systemFontOfSize:15];
5615 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5617 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5618 [prompt_ setBackgroundColor:[UIColor clearColor]];
5619 [prompt_ setFont:font];
5621 [overlay_ addSubview:prompt_];
5623 CGSize prgsize = {75, 100};
5626 ovrrect.size.width - prgsize.width - 10,
5627 (ovrrect.size.height - prgsize.height) / 2
5630 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5631 [progress_ setStyle:0];
5632 [overlay_ addSubview:progress_];
5634 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5635 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5637 CGRect frame = [cancel_ frame];
5638 frame.size.width = 65;
5639 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5640 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5641 [cancel_ setFrame:frame];
5643 [cancel_ setBarStyle:barstyle];
5647 - (void) _onCancel {
5649 [cancel_ removeFromSuperview];
5652 - (void) _update { _pooled
5654 status.setDelegate(self);
5656 [database_ updateWithStatus:status];
5659 performSelectorOnMainThread:@selector(_update_)
5665 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5666 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5669 - (void) setProgressTitle:(NSString *)title {
5671 performSelectorOnMainThread:@selector(_setProgressTitle:)
5677 - (void) setProgressPercent:(float)percent {
5679 performSelectorOnMainThread:@selector(_setProgressPercent:)
5680 withObject:[NSNumber numberWithFloat:percent]
5685 - (void) startProgress {
5688 - (void) addProgressOutput:(NSString *)output {
5690 performSelectorOnMainThread:@selector(_addProgressOutput:)
5696 - (bool) isCancelling:(size_t)received {
5697 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5698 if (received_ != received) {
5699 received_ = received;
5701 } else if (now - last_ > 15)
5706 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5710 - (void) _setProgressTitle:(NSString *)title {
5711 [prompt_ setText:title];
5714 - (void) _setProgressPercent:(NSNumber *)percent {
5715 [progress_ setProgress:[percent floatValue]];
5718 - (void) _addProgressOutput:(NSString *)output {
5723 /* Cydia:// Protocol {{{ */
5724 @interface CydiaURLProtocol : NSURLProtocol {
5729 @implementation CydiaURLProtocol
5731 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5732 NSURL *url([request URL]);
5735 NSString *scheme([[url scheme] lowercaseString]);
5736 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5741 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5745 - (void) startLoading {
5746 id<NSURLProtocolClient> client([self client]);
5747 NSURLRequest *request([self request]);
5749 NSURL *url([request URL]);
5750 NSString *href([url absoluteString]);
5752 NSString *path([href substringFromIndex:8]);
5753 NSRange slash([path rangeOfString:@"/"]);
5756 if (slash.location == NSNotFound) {
5760 command = [path substringToIndex:slash.location];
5761 path = [path substringFromIndex:(slash.location + 1)];
5764 Database *database([Database sharedInstance]);
5766 if ([command isEqualToString:@"package-icon"]) {
5769 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5770 Package *package([database packageWithName:path]);
5774 UIImage *icon([package icon]);
5776 NSData *data(UIImagePNGRepresentation(icon));
5778 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5779 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5780 [client URLProtocol:self didLoadData:data];
5781 [client URLProtocolDidFinishLoading:self];
5782 } else if ([command isEqualToString:@"source-icon"]) {
5785 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5786 NSString *source(Simplify(path));
5788 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5790 icon = [UIImage applicationImageNamed:@"unknown.png"];
5792 NSData *data(UIImagePNGRepresentation(icon));
5794 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5795 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5796 [client URLProtocol:self didLoadData:data];
5797 [client URLProtocolDidFinishLoading:self];
5798 } else if ([command isEqualToString:@"section-icon"]) {
5801 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5802 NSString *section(Simplify(path));
5804 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5806 icon = [UIImage applicationImageNamed:@"unknown.png"];
5808 NSData *data(UIImagePNGRepresentation(icon));
5810 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5811 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5812 [client URLProtocol:self didLoadData:data];
5813 [client URLProtocolDidFinishLoading:self];
5815 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5819 - (void) stopLoading {
5825 /* Install View {{{ */
5826 @interface InstallView : RVPage {
5827 _transient Database *database_;
5828 NSMutableArray *sections_;
5829 NSMutableArray *filtered_;
5830 UITransitionView *transition_;
5836 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5837 - (void) reloadData;
5842 @implementation InstallView
5845 [list_ setDataSource:nil];
5846 [list_ setDelegate:nil];
5848 [sections_ release];
5849 [filtered_ release];
5850 [transition_ release];
5852 [accessory_ release];
5856 - (int) numberOfRowsInTable:(UITable *)table {
5857 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5860 - (float) table:(UITable *)table heightForRow:(int)row {
5864 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5866 reusing = [[[SectionCell alloc] init] autorelease];
5867 [(SectionCell *)reusing setSection:(editing_ ?
5868 [sections_ objectAtIndex:row] :
5869 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5870 ) editing:editing_];
5874 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5878 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5882 - (void) tableRowSelected:(NSNotification *)notification {
5883 int row = [[notification object] selectedRow];
5894 title = @"All Packages";
5896 section = [filtered_ objectAtIndex:(row - 1)];
5897 name = [section name];
5903 title = @"(No Section)";
5907 PackageTable *table = [[[PackageTable alloc]
5911 filter:@selector(isVisiblyUninstalledInSection:)
5915 [table setDelegate:delegate_];
5917 [book_ pushPage:table];
5920 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5921 if ((self = [super initWithBook:book]) != nil) {
5922 database_ = database;
5924 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5925 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5927 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5928 [self addSubview:transition_];
5930 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5931 [transition_ transition:0 toView:list_];
5933 UITableColumn *column = [[[UITableColumn alloc]
5934 initWithTitle:@"Name"
5936 width:[self frame].size.width
5939 [list_ setDataSource:self];
5940 [list_ setSeparatorStyle:1];
5941 [list_ addTableColumn:column];
5942 [list_ setDelegate:self];
5943 [list_ setReusesTableCells:YES];
5947 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5948 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5952 - (void) reloadData {
5953 NSArray *packages = [database_ packages];
5955 [sections_ removeAllObjects];
5956 [filtered_ removeAllObjects];
5958 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5959 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5962 for (size_t i(0); i != [packages count]; ++i) {
5963 Package *package([packages objectAtIndex:i]);
5964 NSString *name([package section]);
5967 Section *section([sections objectForKey:name]);
5968 if (section == nil) {
5969 section = [[[Section alloc] initWithName:name] autorelease];
5970 [sections setObject:section forKey:name];
5974 if ([package valid] && [package installed] == nil && [package visible])
5975 [filtered addObject:package];
5979 [sections_ addObjectsFromArray:[sections allValues]];
5980 [sections_ sortUsingSelector:@selector(compareByName:)];
5983 [filtered sortUsingSelector:@selector(compareBySection:)];
5986 Section *section = nil;
5987 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5988 Package *package = [filtered objectAtIndex:offset];
5989 NSString *name = [package section];
5991 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5992 section = name == nil ?
5993 [[[Section alloc] initWithName:nil] autorelease] :
5994 [sections objectForKey:name];
5995 [filtered_ addObject:section];
5998 [section addToCount];
6006 - (void) resetView {
6008 [self _rightButtonClicked];
6011 - (void) resetViewAnimated:(BOOL)animated {
6012 [list_ resetViewAnimated:animated];
6015 - (void) _rightButtonClicked {
6016 if ((editing_ = !editing_))
6019 [delegate_ updateData];
6020 [book_ reloadTitleForPage:self];
6021 [book_ reloadButtonsForPage:self];
6024 - (NSString *) title {
6025 return editing_ ? @"Section Visibility" : @"Install by Section";
6028 - (NSString *) backButtonTitle {
6032 - (NSString *) rightButtonTitle {
6033 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
6036 - (UINavigationButtonStyle) rightButtonStyle {
6037 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
6040 - (UIView *) accessoryView {
6046 /* Changes View {{{ */
6047 @interface ChangesView : RVPage {
6048 _transient Database *database_;
6049 NSMutableArray *packages_;
6050 NSMutableArray *sections_;
6051 UISectionList *list_;
6055 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6056 - (void) reloadData;
6060 @implementation ChangesView
6063 [[list_ table] setDelegate:nil];
6064 [list_ setDataSource:nil];
6066 [packages_ release];
6067 [sections_ release];
6072 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
6073 return [sections_ count];
6076 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
6077 return [[sections_ objectAtIndex:section] name];
6080 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
6081 return [[sections_ objectAtIndex:section] row];
6084 - (int) numberOfRowsInTable:(UITable *)table {
6085 return [packages_ count];
6088 - (float) table:(UITable *)table heightForRow:(int)row {
6089 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
6092 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
6094 reusing = [[[PackageCell alloc] init] autorelease];
6095 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
6099 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
6103 - (void) tableRowSelected:(NSNotification *)notification {
6104 int row = [[notification object] selectedRow];
6107 Package *package = [packages_ objectAtIndex:row];
6108 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6109 [view setDelegate:delegate_];
6110 [view setPackage:package];
6111 [book_ pushPage:view];
6114 - (void) _leftButtonClicked {
6115 [(CYBook *)book_ update];
6116 [self reloadButtons];
6119 - (void) _rightButtonClicked {
6120 [delegate_ distUpgrade];
6123 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6124 if ((self = [super initWithBook:book]) != nil) {
6125 database_ = database;
6127 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
6128 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6130 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
6131 [self addSubview:list_];
6133 [list_ setShouldHideHeaderInShortLists:NO];
6134 [list_ setDataSource:self];
6135 //[list_ setSectionListStyle:1];
6137 UITableColumn *column = [[[UITableColumn alloc]
6138 initWithTitle:@"Name"
6140 width:[self frame].size.width
6143 UITable *table = [list_ table];
6144 [table setSeparatorStyle:1];
6145 [table addTableColumn:column];
6146 [table setDelegate:self];
6147 [table setReusesTableCells:YES];
6151 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6152 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6156 - (void) reloadData {
6157 NSArray *packages = [database_ packages];
6159 [packages_ removeAllObjects];
6160 [sections_ removeAllObjects];
6163 for (size_t i(0); i != [packages count]; ++i) {
6164 Package *package([packages objectAtIndex:i]);
6167 [package installed] == nil && [package valid] && [package visible] ||
6168 [package upgradableAndEssential:NO]
6170 [packages_ addObject:package];
6174 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
6177 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
6178 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
6179 Section *section = nil;
6183 bool unseens = false;
6185 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
6188 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6189 Package *package = [packages_ objectAtIndex:offset];
6191 if (![package upgradableAndEssential:YES]) {
6193 NSDate *seen = [package seen];
6195 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6198 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
6199 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6200 [sections_ addObject:section];
6204 [section addToCount];
6205 } else if ([package ignored])
6206 [ignored addToCount];
6209 [upgradable addToCount];
6214 CFRelease(formatter);
6217 Section *last = [sections_ lastObject];
6218 size_t count = [last count];
6219 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6220 [sections_ removeLastObject];
6223 if ([ignored count] != 0)
6224 [sections_ insertObject:ignored atIndex:0];
6226 [sections_ insertObject:upgradable atIndex:0];
6229 [self reloadButtons];
6232 - (void) resetViewAnimated:(BOOL)animated {
6233 [list_ resetViewAnimated:animated];
6236 - (NSString *) leftButtonTitle {
6237 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6240 - (NSString *) rightButtonTitle {
6241 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
6244 - (NSString *) title {
6250 /* Search View {{{ */
6251 @protocol SearchViewDelegate
6252 - (void) showKeyboard:(BOOL)show;
6255 @interface SearchView : RVPage {
6257 UISearchField *field_;
6258 UITransitionView *transition_;
6259 PackageTable *table_;
6260 UIPreferencesTable *advanced_;
6266 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6267 - (void) reloadData;
6271 @implementation SearchView
6274 [field_ setDelegate:nil];
6276 [accessory_ release];
6278 [transition_ release];
6280 [advanced_ release];
6285 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6289 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6291 case 0: return @"Advanced Search (Coming Soon!)";
6293 default: _assert(false);
6297 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6301 default: _assert(false);
6305 - (void) _showKeyboard:(BOOL)show {
6306 CGSize keysize = [UIKeyboard defaultSize];
6307 CGRect keydown = [book_ pageBounds];
6308 CGRect keyup = keydown;
6309 keyup.size.height -= keysize.height - ButtonBarHeight_;
6311 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6313 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6314 [animation setSignificantRectFields:8];
6317 [animation setStartFrame:keydown];
6318 [animation setEndFrame:keyup];
6320 [animation setStartFrame:keyup];
6321 [animation setEndFrame:keydown];
6324 UIAnimator *animator = [UIAnimator sharedAnimator];
6327 addAnimations:[NSArray arrayWithObjects:animation, nil]
6328 withDuration:(KeyboardTime_ - delay)
6333 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6335 [delegate_ showKeyboard:show];
6338 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6339 [self _showKeyboard:YES];
6342 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6343 [self _showKeyboard:NO];
6346 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6348 NSString *text([field_ text]);
6349 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6355 - (void) textFieldClearButtonPressed:(UITextField *)field {
6359 - (void) keyboardInputShouldDelete:(id)input {
6363 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6364 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6368 [field_ resignFirstResponder];
6373 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6374 if ((self = [super initWithBook:book]) != nil) {
6375 CGRect pageBounds = [book_ pageBounds];
6377 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6378 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6379 [self addSubview:pinstripe];*/
6381 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6382 [self addSubview:transition_];
6384 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6386 [advanced_ setReusesTableCells:YES];
6387 [advanced_ setDataSource:self];
6388 [advanced_ reloadData];
6390 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6391 CGColor dimmed(space_, 0, 0, 0, 0.5);
6392 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6394 table_ = [[PackageTable alloc]
6398 filter:@selector(isUnfilteredAndSearchedForBy:)
6402 [table_ setShouldHideHeaderInShortLists:NO];
6403 [transition_ transition:0 toView:table_];
6412 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6419 [self bounds].size.width - area.origin.x - 18;
6421 area.size.height = [UISearchField defaultHeight];
6423 field_ = [[UISearchField alloc] initWithFrame:area];
6425 UIFont *font = [UIFont systemFontOfSize:16];
6426 [field_ setFont:font];
6428 [field_ setPlaceholder:@"Package Names & Descriptions"];
6429 [field_ setDelegate:self];
6431 [field_ setPaddingTop:5];
6433 UITextInputTraits *traits([field_ textInputTraits]);
6434 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6435 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6436 [traits setReturnKeyType:UIReturnKeySearch];
6438 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6440 accessory_ = [[UIView alloc] initWithFrame:accrect];
6441 [accessory_ addSubview:field_];
6443 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6444 [configure setShowPressFeedback:YES];
6445 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6446 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6447 [accessory_ addSubview:configure];*/
6449 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6450 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6456 LKAnimation *animation = [LKTransition animation];
6457 [animation setType:@"oglFlip"];
6458 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6459 [animation setFillMode:@"extended"];
6460 [animation setTransitionFlags:3];
6461 [animation setDuration:10];
6462 [animation setSpeed:0.35];
6463 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6464 [[transition_ _layer] addAnimation:animation forKey:0];
6465 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6466 flipped_ = !flipped_;
6470 - (void) configurePushed {
6471 [field_ resignFirstResponder];
6475 - (void) resetViewAnimated:(BOOL)animated {
6478 [table_ resetViewAnimated:animated];
6481 - (void) _reloadData {
6484 - (void) reloadData {
6487 [table_ setObject:[field_ text]];
6488 [table_ reloadData];
6489 [table_ resetCursor];
6492 - (UIView *) accessoryView {
6496 - (NSString *) title {
6500 - (NSString *) backButtonTitle {
6504 - (void) setDelegate:(id)delegate {
6505 [table_ setDelegate:delegate];
6506 [super setDelegate:delegate];
6512 @interface SettingsView : RVPage {
6513 _transient Database *database_;
6516 UIPreferencesTable *table_;
6517 _UISwitchSlider *subscribedSwitch_;
6518 _UISwitchSlider *ignoredSwitch_;
6519 UIPreferencesControlTableCell *subscribedCell_;
6520 UIPreferencesControlTableCell *ignoredCell_;
6523 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6527 @implementation SettingsView
6530 [table_ setDataSource:nil];
6533 if (package_ != nil)
6536 [subscribedSwitch_ release];
6537 [ignoredSwitch_ release];
6538 [subscribedCell_ release];
6539 [ignoredCell_ release];
6543 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6544 if (package_ == nil)
6550 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6551 if (package_ == nil)
6558 default: _assert(false);
6564 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6565 if (package_ == nil)
6572 default: _assert(false);
6578 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6579 if (package_ == nil)
6586 default: _assert(false);
6592 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6593 if (package_ == nil)
6596 _UISwitchSlider *slider([cell control]);
6597 BOOL value([slider value] != 0);
6598 NSMutableDictionary *metadata([package_ metadata]);
6601 if (NSNumber *number = [metadata objectForKey:key])
6602 before = [number boolValue];
6606 if (value != before) {
6607 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6609 [delegate_ updateData];
6613 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6614 [self onSomething:cell withKey:@"IsSubscribed"];
6617 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6618 [self onSomething:cell withKey:@"IsIgnored"];
6621 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6622 if (package_ == nil)
6626 case 0: switch (row) {
6628 return subscribedCell_;
6630 return ignoredCell_;
6631 default: _assert(false);
6634 case 1: switch (row) {
6636 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6637 [cell setShowSelection:NO];
6638 [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."];
6642 default: _assert(false);
6645 default: _assert(false);
6651 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6652 if ((self = [super initWithBook:book])) {
6653 database_ = database;
6654 name_ = [package retain];
6656 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6657 [self addSubview:table_];
6659 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6660 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6662 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6663 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6665 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6666 [subscribedCell_ setShowSelection:NO];
6667 [subscribedCell_ setTitle:@"Show All Changes"];
6668 [subscribedCell_ setControl:subscribedSwitch_];
6670 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6671 [ignoredCell_ setShowSelection:NO];
6672 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6673 [ignoredCell_ setControl:ignoredSwitch_];
6675 [table_ setDataSource:self];
6680 - (void) resetViewAnimated:(BOOL)animated {
6681 [table_ resetViewAnimated:animated];
6684 - (void) reloadData {
6685 if (package_ != nil)
6686 [package_ autorelease];
6687 package_ = [database_ packageWithName:name_];
6688 if (package_ != nil) {
6690 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6691 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6694 [table_ reloadData];
6697 - (NSString *) title {
6703 /* Signature View {{{ */
6704 @interface SignatureView : BrowserView {
6705 _transient Database *database_;
6709 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6713 @implementation SignatureView
6720 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6722 [super webView:sender didClearWindowObject:window forFrame:frame];
6725 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6726 if ((self = [super initWithBook:book]) != nil) {
6727 database_ = database;
6728 package_ = [package retain];
6733 - (void) resetViewAnimated:(BOOL)animated {
6736 - (void) reloadData {
6737 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6743 @interface Cydia : UIApplication <
6744 ConfirmationViewDelegate,
6745 ProgressViewDelegate,
6754 UIToolbar *buttonbar_;
6758 NSMutableArray *essential_;
6759 NSMutableArray *broken_;
6761 Database *database_;
6762 ProgressView *progress_;
6766 UIKeyboard *keyboard_;
6767 UIProgressHUD *hud_;
6769 InstallView *install_;
6770 ChangesView *changes_;
6771 ManageView *manage_;
6772 SearchView *search_;
6777 @implementation Cydia
6780 if ([broken_ count] != 0) {
6781 int count = [broken_ count];
6783 UIActionSheet *sheet = [[[UIActionSheet alloc]
6784 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6785 buttons:[NSArray arrayWithObjects:
6787 @"Ignore (Temporary)",
6789 defaultButtonIndex:0
6794 [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."];
6795 [sheet popupAlertAnimated:YES];
6796 } else if (!Ignored_ && [essential_ count] != 0) {
6797 int count = [essential_ count];
6799 UIActionSheet *sheet = [[[UIActionSheet alloc]
6800 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6801 buttons:[NSArray arrayWithObjects:
6802 @"Upgrade Essential",
6803 @"Complete Upgrade",
6804 @"Ignore (Temporary)",
6806 defaultButtonIndex:0
6811 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6812 [sheet popupAlertAnimated:YES];
6816 - (void) _reloadData {
6817 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6818 [hud setText:@"Reloading Data"];
6819 [overlay_ addSubview:hud];
6822 [database_ reloadData];
6826 [essential_ removeAllObjects];
6827 [broken_ removeAllObjects];
6829 NSArray *packages = [database_ packages];
6830 for (Package *package in packages) {
6832 [broken_ addObject:package];
6833 if ([package upgradableAndEssential:NO]) {
6834 if ([package essential])
6835 [essential_ addObject:package];
6841 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6842 [buttonbar_ setBadgeValue:badge forButton:3];
6843 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6844 [buttonbar_ setBadgeAnimated:YES forButton:3];
6845 [self setApplicationBadge:badge];
6847 [buttonbar_ setBadgeValue:nil forButton:3];
6848 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6849 [buttonbar_ setBadgeAnimated:NO forButton:3];
6850 [self removeApplicationBadge];
6856 if ([packages count] == 0);
6868 [hud removeFromSuperview];*/
6871 - (void) _saveConfig {
6874 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6880 - (void) updateData {
6883 /* XXX: this is just stupid */
6884 if (tag_ != 2 && install_ != nil)
6885 [install_ reloadData];
6886 if (tag_ != 3 && changes_ != nil)
6887 [changes_ reloadData];
6888 if (tag_ != 5 && search_ != nil)
6889 [search_ reloadData];
6899 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6900 _assert(file != NULL);
6902 NSArray *keys = [Sources_ allKeys];
6904 for (int i(0), e([keys count]); i != e; ++i) {
6905 NSString *key = [keys objectAtIndex:i];
6906 NSDictionary *source = [Sources_ objectForKey:key];
6908 fprintf(file, "%s %s %s\n",
6909 [[source objectForKey:@"Type"] UTF8String],
6910 [[source objectForKey:@"URI"] UTF8String],
6911 [[source objectForKey:@"Distribution"] UTF8String]
6920 detachNewThreadSelector:@selector(update_)
6923 title:@"Updating Sources"
6927 - (void) reloadData {
6928 @synchronized (self) {
6929 if (confirm_ == nil)
6935 pkgProblemResolver *resolver = [database_ resolver];
6937 resolver->InstallProtect();
6938 if (!resolver->Resolve(true))
6943 [database_ prepare];
6945 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6946 [confirm_ setDelegate:self];
6948 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6949 [page setDelegate:self];
6951 [confirm_ setPage:page];
6952 [underlay_ popSubview:confirm_];
6955 - (void) installPackage:(Package *)package {
6956 @synchronized (self) {
6963 - (void) removePackage:(Package *)package {
6964 @synchronized (self) {
6971 - (void) distUpgrade {
6972 @synchronized (self) {
6973 [database_ upgrade];
6979 @synchronized (self) {
6981 if (confirm_ != nil) {
6989 [overlay_ removeFromSuperview];
6993 detachNewThreadSelector:@selector(perform)
7000 - (void) bootstrap_ {
7002 [database_ upgrade];
7003 [database_ prepare];
7004 [database_ perform];
7007 - (void) bootstrap {
7009 detachNewThreadSelector:@selector(bootstrap_)
7012 title:@"Bootstrap Install"
7016 - (void) progressViewIsComplete:(ProgressView *)progress {
7017 if (confirm_ != nil) {
7018 [underlay_ addSubview:overlay_];
7019 [confirm_ popFromSuperviewAnimated:NO];
7025 - (void) setPage:(RVPage *)page {
7026 [page resetViewAnimated:NO];
7027 [page setDelegate:self];
7028 [book_ setPage:page];
7031 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
7032 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
7033 [browser loadURL:url];
7037 - (void) _setHomePage {
7038 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
7041 - (void) buttonBarItemTapped:(id)sender {
7042 unsigned tag = [sender tag];
7044 [book_ resetViewAnimated:YES];
7046 } else if (tag_ == 2 && tag != 2)
7047 [install_ resetView];
7050 case 1: [self _setHomePage]; break;
7052 case 2: [self setPage:install_]; break;
7053 case 3: [self setPage:changes_]; break;
7054 case 4: [self setPage:manage_]; break;
7055 case 5: [self setPage:search_]; break;
7057 default: _assert(false);
7063 - (void) applicationWillSuspend {
7065 [super applicationWillSuspend];
7068 - (void) askForSettings {
7069 UIActionSheet *role = [[[UIActionSheet alloc]
7070 initWithTitle:@"Who Are You?"
7071 buttons:[NSArray arrayWithObjects:
7072 @"User (Graphical Only)",
7073 @"Hacker (+ Command Line)",
7074 @"Developer (No Filters)",
7076 defaultButtonIndex:-1
7081 [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."];
7082 [role popupAlertAnimated:YES];
7087 [self setStatusBarShowsProgress:NO];
7090 [hud_ removeFromSuperview];
7094 pid_t pid = ExecFork();
7096 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
7097 perror("launchctl stop");
7104 [self askForSettings];
7108 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
7110 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7111 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
7112 0, 0, screenrect.size.width, screenrect.size.height - 48
7113 ) database:database_];
7115 [book_ setDelegate:self];
7117 [overlay_ addSubview:book_];
7119 NSArray *buttonitems = [NSArray arrayWithObjects:
7120 [NSDictionary dictionaryWithObjectsAndKeys:
7121 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7122 @"home-up.png", kUIButtonBarButtonInfo,
7123 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
7124 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
7125 self, kUIButtonBarButtonTarget,
7126 @"Home", kUIButtonBarButtonTitle,
7127 @"0", kUIButtonBarButtonType,
7130 [NSDictionary dictionaryWithObjectsAndKeys:
7131 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7132 @"install-up.png", kUIButtonBarButtonInfo,
7133 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
7134 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
7135 self, kUIButtonBarButtonTarget,
7136 @"Sections", kUIButtonBarButtonTitle,
7137 @"0", kUIButtonBarButtonType,
7140 [NSDictionary dictionaryWithObjectsAndKeys:
7141 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7142 @"changes-up.png", kUIButtonBarButtonInfo,
7143 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
7144 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
7145 self, kUIButtonBarButtonTarget,
7146 @"Changes", kUIButtonBarButtonTitle,
7147 @"0", kUIButtonBarButtonType,
7150 [NSDictionary dictionaryWithObjectsAndKeys:
7151 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7152 @"manage-up.png", kUIButtonBarButtonInfo,
7153 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
7154 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
7155 self, kUIButtonBarButtonTarget,
7156 @"Manage", kUIButtonBarButtonTitle,
7157 @"0", kUIButtonBarButtonType,
7160 [NSDictionary dictionaryWithObjectsAndKeys:
7161 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7162 @"search-up.png", kUIButtonBarButtonInfo,
7163 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
7164 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
7165 self, kUIButtonBarButtonTarget,
7166 @"Search", kUIButtonBarButtonTitle,
7167 @"0", kUIButtonBarButtonType,
7171 buttonbar_ = [[UIToolbar alloc]
7173 withFrame:CGRectMake(
7174 0, screenrect.size.height - ButtonBarHeight_,
7175 screenrect.size.width, ButtonBarHeight_
7177 withItemList:buttonitems
7180 [buttonbar_ setDelegate:self];
7181 [buttonbar_ setBarStyle:1];
7182 [buttonbar_ setButtonBarTrackingMode:2];
7184 int buttons[5] = {1, 2, 3, 4, 5};
7185 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7186 [buttonbar_ showButtonGroup:0 withDuration:0];
7188 for (int i = 0; i != 5; ++i)
7189 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
7190 i * 64 + 2, 1, 60, ButtonBarHeight_
7193 [buttonbar_ showSelectionForButton:1];
7194 [overlay_ addSubview:buttonbar_];
7196 [UIKeyboard initImplementationNow];
7197 CGSize keysize = [UIKeyboard defaultSize];
7198 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
7199 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
7200 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
7201 [overlay_ addSubview:keyboard_];
7204 [underlay_ addSubview:overlay_];
7208 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
7209 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
7210 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7212 manage_ = (ManageView *) [[self
7213 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7214 withClass:[ManageView class]
7220 [self _setHomePage];
7223 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7224 NSString *context([sheet context]);
7226 if ([context isEqualToString:@"fixhalf"]) {
7229 @synchronized (self) {
7230 for (int i = 0, e = [broken_ count]; i != e; ++i) {
7231 Package *broken = [broken_ objectAtIndex:i];
7234 NSString *id = [broken id];
7235 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7236 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7237 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7238 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7247 [broken_ removeAllObjects];
7256 } else if ([context isEqualToString:@"role"]) {
7258 case 1: Role_ = @"User"; break;
7259 case 2: Role_ = @"Hacker"; break;
7260 case 3: Role_ = @"Developer"; break;
7267 bool reset = Settings_ != nil;
7269 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7273 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7283 } else if ([context isEqualToString:@"upgrade"]) {
7286 @synchronized (self) {
7287 for (int i = 0, e = [essential_ count]; i != e; ++i) {
7288 Package *essential = [essential_ objectAtIndex:i];
7289 [essential install];
7313 - (void) reorganize { _pooled
7314 system("/usr/libexec/cydia/free.sh");
7315 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7318 - (void) applicationSuspend:(__GSEvent *)event {
7319 if (hud_ == nil && ![progress_ isRunning])
7320 [super applicationSuspend:event];
7323 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7325 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7328 - (void) _setSuspended:(BOOL)value {
7330 [super _setSuspended:value];
7333 - (UIProgressHUD *) addProgressHUD {
7334 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7336 [underlay_ addSubview:hud];
7340 - (void) openMailToURL:(NSURL *)url {
7341 // XXX: this makes me sad
7343 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7345 [UIApp openURL:url];
7349 - (RVPage *) pageForPackage:(NSString *)name {
7350 if (Package *package = [database_ packageWithName:name]) {
7351 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7352 [view setPackage:package];
7355 UIActionSheet *sheet = [[[UIActionSheet alloc]
7356 initWithTitle:@"Cannot Locate Package"
7357 buttons:[NSArray arrayWithObjects:@"Close", nil]
7358 defaultButtonIndex:0
7363 [sheet setBodyText:[NSString stringWithFormat:
7364 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7367 [sheet popupAlertAnimated:YES];
7372 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7373 NSString *href = [url absoluteString];
7378 if ([href isEqualToString:@"cydia://add-source"])
7379 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7380 else if ([href isEqualToString:@"cydia://sources"])
7381 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7382 else if ([href isEqualToString:@"cydia://packages"])
7383 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7384 else if ([href hasPrefix:@"cydia://url/"])
7385 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7386 else if ([href hasPrefix:@"cydia://launch/"])
7387 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7388 else if ([href hasPrefix:@"cydia://package-settings/"])
7389 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7390 else if ([href hasPrefix:@"cydia://package-signature/"])
7391 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7392 else if ([href hasPrefix:@"cydia://package/"])
7393 return [self pageForPackage:[href substringFromIndex:16]];
7394 else if ([href hasPrefix:@"cydia://files/"]) {
7395 NSString *name = [href substringFromIndex:14];
7397 if (Package *package = [database_ packageWithName:name]) {
7398 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7399 [files setPackage:package];
7407 - (void) applicationOpenURL:(NSURL *)url {
7408 [super applicationOpenURL:url];
7410 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7411 [self setPage:page];
7412 [buttonbar_ showSelectionForButton:tag];
7417 - (void) applicationDidFinishLaunching:(id)unused {
7418 Font12_ = [[UIFont systemFontOfSize:12] retain];
7419 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7420 Font14_ = [[UIFont systemFontOfSize:14] retain];
7421 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7422 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7424 _assert(pkgInitConfig(*_config));
7425 _assert(pkgInitSystem(*_config, _system));
7429 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7430 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7432 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7434 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7435 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7437 [window_ orderFront:self];
7438 [window_ makeKey:self];
7439 [window_ setHidden:NO];
7441 database_ = [Database sharedInstance];
7442 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7443 [database_ setDelegate:progress_];
7444 [window_ setContentView:progress_];
7446 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7447 [progress_ setContentView:underlay_];
7449 [progress_ resetView];
7452 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7453 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7454 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7455 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7456 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7457 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7458 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7460 [self setIdleTimerDisabled:YES];
7462 hud_ = [self addProgressHUD];
7463 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7465 [self setStatusBarShowsProgress:YES];
7468 detachNewThreadSelector:@selector(reorganize)
7476 /* Web Scripting {{{ */
7477 + (NSString *) webScriptNameForSelector:(SEL)selector {
7478 if (selector == @selector(supports:))
7483 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7484 return selector != @selector(supports:);
7487 - (BOOL) supports:(NSString *)feature {
7488 return [feature isEqualToString:@"window.open"];
7492 - (void) showKeyboard:(BOOL)show {
7493 CGSize keysize = [UIKeyboard defaultSize];
7494 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7495 CGRect keyup = keydown;
7496 keyup.origin.y -= keysize.height;
7498 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7499 [animation setSignificantRectFields:2];
7502 [animation setStartFrame:keydown];
7503 [animation setEndFrame:keyup];
7504 [keyboard_ activate];
7506 [animation setStartFrame:keyup];
7507 [animation setEndFrame:keydown];
7508 [keyboard_ deactivate];
7511 [[UIAnimator sharedAnimator]
7512 addAnimations:[NSArray arrayWithObjects:animation, nil]
7513 withDuration:KeyboardTime_
7518 - (void) slideUp:(UIActionSheet *)alert {
7520 [alert presentSheetFromButtonBar:buttonbar_];
7522 [alert presentSheetInView:overlay_];
7527 void AddPreferences(NSString *plist) { _pooled
7528 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7529 _assert(settings != NULL);
7530 NSMutableArray *items = [settings objectForKey:@"items"];
7534 for (size_t i(0); i != [items count]; ++i) {
7535 NSMutableDictionary *item([items objectAtIndex:i]);
7536 NSString *label = [item objectForKey:@"label"];
7537 if (label != nil && [label isEqualToString:@"Cydia"]) {
7544 for (size_t i(0); i != [items count]; ++i) {
7545 NSDictionary *item([items objectAtIndex:i]);
7546 NSString *label = [item objectForKey:@"label"];
7547 if (label != nil && [label isEqualToString:@"General"]) {
7548 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7549 @"CydiaSettings", @"bundle",
7550 @"PSLinkCell", @"cell",
7551 [NSNumber numberWithBool:YES], @"hasIcon",
7552 [NSNumber numberWithBool:YES], @"isController",
7554 nil] atIndex:(i + 1)];
7560 _assert([settings writeToFile:plist atomically:YES] == YES);
7565 id Alloc_(id self, SEL selector) {
7566 id object = alloc_(self, selector);
7567 lprintf("[%s]A-%p\n", self->isa->name, object);
7572 id Dealloc_(id self, SEL selector) {
7573 id object = dealloc_(self, selector);
7574 lprintf("[%s]D-%p\n", self->isa->name, object);
7578 int main(int argc, char *argv[]) { _pooled
7579 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7581 bool substrate(false);
7587 for (int argi(1); argi != argc; ++argi)
7588 if (strcmp(argv[argi], "--") == 0) {
7590 argv[argi] = argv[0];
7596 for (int argi(1); argi != arge; ++argi)
7597 if (strcmp(args[argi], "--bootstrap") == 0)
7599 else if (strcmp(args[argi], "--substrate") == 0)
7602 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7605 App_ = [[NSBundle mainBundle] bundlePath];
7606 Home_ = NSHomeDirectory();
7607 Locale_ = CFLocaleCopyCurrent();
7610 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7611 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7612 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7613 Sounds_Keyboard_ = [keyboard boolValue];
7619 #if 1 /* XXX: this costs 1.4s of startup performance */
7620 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7621 _assert(errno == ENOENT);
7622 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7623 _assert(errno == ENOENT);
7626 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7627 alloc_ = alloc->method_imp;
7628 alloc->method_imp = (IMP) &Alloc_;*/
7630 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7631 dealloc_ = dealloc->method_imp;
7632 dealloc->method_imp = (IMP) &Dealloc_;*/
7637 size = sizeof(maxproc);
7638 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7639 perror("sysctlbyname(\"kern.maxproc\", ?)");
7640 else if (maxproc < 64) {
7642 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7643 perror("sysctlbyname(\"kern.maxproc\", #)");
7646 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7647 char *machine = new char[size];
7648 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7649 perror("sysctlbyname(\"hw.machine\", ?)");
7653 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7655 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7656 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7658 if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7659 Indices_ = [[NSMutableDictionary alloc] init];
7661 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7662 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7664 Settings_ = [Metadata_ objectForKey:@"Settings"];
7666 Packages_ = [Metadata_ objectForKey:@"Packages"];
7667 Sections_ = [Metadata_ objectForKey:@"Sections"];
7668 Sources_ = [Metadata_ objectForKey:@"Sources"];
7671 if (Settings_ != nil)
7672 Role_ = [Settings_ objectForKey:@"Role"];
7674 if (Packages_ == nil) {
7675 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7676 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7679 if (Sections_ == nil) {
7680 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7681 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7684 if (Sources_ == nil) {
7685 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7686 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7690 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7693 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7694 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7696 if (access("/User", F_OK) != 0)
7697 system("/usr/libexec/cydia/firmware.sh");
7699 _assert([[NSFileManager defaultManager]
7700 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7701 withIntermediateDirectories:YES
7706 space_ = CGColorSpaceCreateDeviceRGB();
7708 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7709 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7710 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7711 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7712 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7713 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7715 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7717 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7719 UIApplicationUseLegacyEvents(YES);
7720 UIKeyboardDisableAutomaticAppearance();
7722 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7724 CGColorSpaceRelease(space_);