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 // XXX: wtf/FastMalloc.h... wtf?
39 #define USE_SYSTEM_MALLOC 1
41 /* #include Directives {{{ */
42 #import "UICaboodle.h"
44 #include <objc/message.h>
45 #include <objc/objc.h>
46 #include <objc/runtime.h>
48 #include <CoreGraphics/CoreGraphics.h>
49 #include <GraphicsServices/GraphicsServices.h>
50 #include <Foundation/Foundation.h>
52 #import <QuartzCore/CALayer.h>
54 #import <UIKit/UIKit.h>
57 #import <MessageUI/MailComposeController.h>
63 #include <ext/stdio_filebuf.h>
65 #include <apt-pkg/acquire.h>
66 #include <apt-pkg/acquire-item.h>
67 #include <apt-pkg/algorithms.h>
68 #include <apt-pkg/cachefile.h>
69 #include <apt-pkg/clean.h>
70 #include <apt-pkg/configuration.h>
71 #include <apt-pkg/debmetaindex.h>
72 #include <apt-pkg/error.h>
73 #include <apt-pkg/init.h>
74 #include <apt-pkg/mmap.h>
75 #include <apt-pkg/pkgrecords.h>
76 #include <apt-pkg/sha1.h>
77 #include <apt-pkg/sourcelist.h>
78 #include <apt-pkg/sptr.h>
79 #include <apt-pkg/strutl.h>
81 #include <sys/types.h>
83 #include <sys/sysctl.h>
84 #include <sys/param.h>
85 #include <sys/mount.h>
91 #include <mach-o/nlist.h>
101 #import "BrowserView.h"
102 #import "ResetView.h"
104 #import "substrate.h"
107 //#define _finline __attribute__((force_inline))
108 #define _finline inline
113 #define _limit(count) do { \
114 static size_t _count(0); \
115 if (++_count == count) \
119 #define _timestamp ({ \
121 gettimeofday(&tv, NULL); \
122 tv.tv_sec * 1000000 + tv.tv_usec; \
125 typedef std::vector<class ProfileTime *> TimeList;
135 ProfileTime(const char *name) :
139 times_.push_back(this);
142 void AddTime(uint64_t time) {
149 std::cerr << std::setw(5) << count_ << ", " << std::setw(7) << total_ << " : " << name_ << std::endl;
161 ProfileTimer(ProfileTime &time) :
168 time_.AddTime(_timestamp - start_);
173 for (TimeList::const_iterator i(times_.begin()); i != times_.end(); ++i)
175 std::cerr << "========" << std::endl;
178 #define _profile(name) { \
179 static ProfileTime name(#name); \
180 ProfileTimer _ ## name(name);
184 /* Objective-C Handle<> {{{ */
185 template <typename Type_>
187 typedef _H<Type_> This_;
192 _finline void Retain_() {
197 _finline void Clear_() {
203 _finline _H(Type_ *value = NULL, bool mended = false) :
214 _finline This_ &operator =(Type_ *value) {
215 if (value_ != value) {
224 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
226 void NSLogPoint(const char *fix, const CGPoint &point) {
227 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
230 void NSLogRect(const char *fix, const CGRect &rect) {
231 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
234 @interface NSObject (Cydia)
235 - (id) yieldToSelector:(SEL)selector withObject:(id)object;
236 - (id) yieldToSelector:(SEL)selector;
239 @implementation NSObject (Cydia)
244 - (void) _yieldToContext:(NSMutableArray *)context { _pooled
245 SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
246 id object([[context objectAtIndex:1] nonretainedObjectValue]);
247 volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
249 /* XXX: deal with exceptions */
250 id value([self performSelector:selector withObject:object]);
252 [context removeAllObjects];
254 [context addObject:value];
259 performSelectorOnMainThread:@selector(doNothing)
265 - (id) yieldToSelector:(SEL)selector withObject:(id)object {
266 /*return [self performSelector:selector withObject:object];*/
268 volatile bool stopped(false);
270 NSMutableArray *context([NSMutableArray arrayWithObjects:
271 [NSValue valueWithPointer:selector],
272 [NSValue valueWithNonretainedObject:object],
273 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
276 NSThread *thread([[[NSThread alloc]
278 selector:@selector(_yieldToContext:)
284 NSRunLoop *loop([NSRunLoop currentRunLoop]);
285 NSDate *future([NSDate distantFuture]);
287 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
289 return [context count] == 0 ? nil : [context objectAtIndex:0];
292 - (id) yieldToSelector:(SEL)selector {
293 return [self yieldToSelector:selector withObject:nil];
298 /* NSForcedOrderingSearch doesn't work on the iPhone */
299 static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
300 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
301 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
302 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
304 /* iPhoneOS 2.0 Compatibility {{{ */
306 @interface UITextView (iPhoneOS)
307 - (void) setTextSize:(float)size;
310 @implementation UITextView (iPhoneOS)
312 - (void) setTextSize:(float)size {
313 [self setFont:[[self font] fontWithSize:size]];
320 extern NSString * const kCAFilterNearest;
322 /* Information Dictionaries {{{ */
323 @interface NSMutableArray (Cydia)
324 - (void) addInfoDictionary:(NSDictionary *)info;
327 @implementation NSMutableArray (Cydia)
329 - (void) addInfoDictionary:(NSDictionary *)info {
330 [self addObject:info];
335 @interface NSMutableDictionary (Cydia)
336 - (void) addInfoDictionary:(NSDictionary *)info;
339 @implementation NSMutableDictionary (Cydia)
341 - (void) addInfoDictionary:(NSDictionary *)info {
342 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
343 [self setObject:info forKey:bundle];
348 /* Pop Transitions {{{ */
349 @interface PopTransitionView : UITransitionView {
354 @implementation PopTransitionView
356 - (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
357 if (from != nil && to == nil)
358 [self removeFromSuperview];
363 @implementation UIView (PopUpView)
365 - (void) popFromSuperviewAnimated:(BOOL)animated {
366 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
369 - (void) popSubview:(UIView *)view {
370 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
371 [transition setDelegate:transition];
372 [self addSubview:transition];
374 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
375 [transition transition:UITransitionNone toView:blank];
376 [transition transition:UITransitionPushFromBottom toView:view];
382 #define lprintf(args...) fprintf(stderr, args)
385 #define ForSaurik (1 && !ForRelease)
386 #define ShowInternals (1 && !ForRelease)
387 #define IgnoreInstall (0 && !ForRelease)
388 #define RecycleWebViews 0
389 #define AlwaysReload (1 && !ForRelease)
393 #define _trace(args...)
395 #define _profile(name) {
398 #define PrintTimes() do {} while (false)
402 @interface NSMutableArray (Radix)
403 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
406 @implementation NSMutableArray (Radix)
408 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
409 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
410 [invocation setSelector:selector];
411 [invocation setArgument:&object atIndex:2];
413 size_t count([self count]);
418 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
420 for (size_t i(0); i != count; ++i) {
421 RadixItem &item(lhs[i]);
424 id object([self objectAtIndex:i]);
425 [invocation setTarget:object];
428 [invocation getReturnValue:&item.key];
431 static const size_t width = 32;
432 static const size_t bits = 11;
433 static const size_t slots = 1 << bits;
434 static const size_t passes = (width + (bits - 1)) / bits;
436 size_t *hist(new size_t[slots]);
438 for (size_t pass(0); pass != passes; ++pass) {
439 memset(hist, 0, sizeof(size_t) * slots);
441 for (size_t i(0); i != count; ++i) {
442 uint32_t key(lhs[i].key);
444 key &= _not(uint32_t) >> width - bits;
449 for (size_t i(0); i != slots; ++i) {
450 size_t local(offset);
455 for (size_t i(0); i != count; ++i) {
456 uint32_t key(lhs[i].key);
458 key &= _not(uint32_t) >> width - bits;
459 rhs[hist[key]++] = lhs[i];
469 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
470 for (size_t i(0); i != count; ++i)
471 [values addObject:[self objectAtIndex:lhs[i].index]];
472 [self setArray:values];
480 /* Apple Bug Fixes {{{ */
481 @implementation UIWebDocumentView (Cydia)
483 - (void) _setScrollerOffset:(CGPoint)offset {
484 UIScroller *scroller([self _scroller]);
486 CGSize size([scroller contentSize]);
487 CGSize bounds([scroller bounds].size);
490 max.x = size.width - bounds.width;
491 max.y = size.height - bounds.height;
499 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
500 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
502 [scroller setOffset:offset];
509 kUIControlEventMouseDown = 1 << 0,
510 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
511 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
512 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
513 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
514 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
515 } UIControlEventMasks;
517 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
518 size_t length([self length] - state->state);
521 else if (length > count)
523 for (size_t i(0); i != length; ++i)
524 objects[i] = [self item:state->state++];
525 state->itemsPtr = objects;
526 state->mutationsPtr = (unsigned long *) self;
530 @interface NSString (UIKit)
531 - (NSString *) stringByAddingPercentEscapes;
532 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
535 @interface NSString (Cydia)
536 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
537 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
538 - (NSComparisonResult) compareByPath:(NSString *)other;
539 - (NSString *) stringByCachingURLWithCurrentCDN;
540 - (NSString *) stringByAddingPercentEscapesIncludingReserved;
543 @implementation NSString (Cydia)
545 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length {
546 return [[[NSString alloc] initWithBytesNoCopy:const_cast<char *>(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
549 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
550 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
553 - (NSComparisonResult) compareByPath:(NSString *)other {
554 NSString *prefix = [self commonPrefixWithString:other options:0];
555 size_t length = [prefix length];
557 NSRange lrange = NSMakeRange(length, [self length] - length);
558 NSRange rrange = NSMakeRange(length, [other length] - length);
560 lrange = [self rangeOfString:@"/" options:0 range:lrange];
561 rrange = [other rangeOfString:@"/" options:0 range:rrange];
563 NSComparisonResult value;
565 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
566 value = NSOrderedSame;
567 else if (lrange.location == NSNotFound)
568 value = NSOrderedAscending;
569 else if (rrange.location == NSNotFound)
570 value = NSOrderedDescending;
572 value = NSOrderedSame;
574 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
575 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
576 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
577 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
579 NSComparisonResult result = [lpath compare:rpath];
580 return result == NSOrderedSame ? value : result;
583 - (NSString *) stringByCachingURLWithCurrentCDN {
585 stringByReplacingOccurrencesOfString:@"://"
586 withString:@"://ne.edgecastcdn.net/8003A4/"
588 /* XXX: this is somewhat inaccurate */
589 range:NSMakeRange(0, 10)
593 - (NSString *) stringByAddingPercentEscapesIncludingReserved {
594 return [(id)CFURLCreateStringByAddingPercentEscapes(
599 kCFStringEncodingUTF8
605 /* Perl-Compatible RegEx {{{ */
615 Pcre(const char *regex) :
620 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
623 lprintf("%d:%s\n", offset, error);
627 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
628 matches_ = new int[(capture_ + 1) * 3];
636 NSString *operator [](size_t match) {
637 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
640 bool operator ()(NSString *data) {
641 // XXX: length is for characters, not for bytes
642 return operator ()([data UTF8String], [data length]);
645 bool operator ()(const char *data, size_t size) {
647 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
651 /* Mime Addresses {{{ */
652 @interface Address : NSObject {
658 - (NSString *) address;
660 + (Address *) addressWithString:(NSString *)string;
661 - (Address *) initWithString:(NSString *)string;
664 @implementation Address
673 - (NSString *) name {
677 - (NSString *) address {
681 + (Address *) addressWithString:(NSString *)string {
682 return [[[Address alloc] initWithString:string] autorelease];
685 + (NSArray *) _attributeKeys {
686 return [NSArray arrayWithObjects:@"address", @"name", nil];
689 - (NSArray *) attributeKeys {
690 return [[self class] _attributeKeys];
693 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
694 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
697 - (Address *) initWithString:(NSString *)string {
698 if ((self = [super init]) != nil) {
699 const char *data = [string UTF8String];
700 size_t size = [string length];
702 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
704 if (address_r(data, size)) {
705 name_ = [address_r[1] retain];
706 address_ = [address_r[2] retain];
708 name_ = [string retain];
716 /* CoreGraphics Primitives {{{ */
727 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
730 Set(space, red, green, blue, alpha);
735 CGColorRelease(color_);
742 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
744 float color[] = {red, green, blue, alpha};
745 color_ = CGColorCreate(space, color);
748 operator CGColorRef() {
754 extern "C" void UISetColor(CGColorRef color);
756 /* Random Global Variables {{{ */
757 static const int PulseInterval_ = 50000;
758 static const int ButtonBarHeight_ = 48;
759 static const float KeyboardTime_ = 0.3f;
761 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
762 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
763 #define NotifyConfig_ "/etc/notify.conf"
765 static CGColor Blue_;
766 static CGColor Blueish_;
767 static CGColor Black_;
769 static CGColor White_;
770 static CGColor Gray_;
772 static NSString *App_;
773 static NSString *Home_;
774 static BOOL Sounds_Keyboard_;
776 static BOOL Advanced_;
778 static BOOL Ignored_;
780 static UIFont *Font12_;
781 static UIFont *Font12Bold_;
782 static UIFont *Font14_;
783 static UIFont *Font18Bold_;
784 static UIFont *Font22Bold_;
786 static const char *Machine_ = NULL;
787 static const NSString *UniqueID_ = nil;
788 static const NSString *Build_ = nil;
789 static const NSString *Product_ = nil;
790 static const NSString *Safari_ = nil;
793 CGColorSpaceRef space_;
798 static NSDictionary *SectionMap_;
799 static NSMutableDictionary *Metadata_;
800 static _transient NSMutableDictionary *Settings_;
801 static _transient NSString *Role_;
802 static _transient NSMutableDictionary *Packages_;
803 static _transient NSMutableDictionary *Sections_;
804 static _transient NSMutableDictionary *Sources_;
805 static bool Changed_;
809 static NSMutableArray *Documents_;
812 NSString *GetLastUpdate() {
813 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
816 return @"Never or Unknown";
818 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
819 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
821 CFRelease(formatter);
823 return [(NSString *) formatted autorelease];
826 /* Display Helpers {{{ */
827 inline float Interpolate(float begin, float end, float fraction) {
828 return (end - begin) * fraction + begin;
831 NSString *SizeString(double size) {
832 bool negative = size < 0;
837 while (size > 1024) {
842 static const char *powers_[] = {"B", "kB", "MB", "GB"};
844 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
847 NSString *StripVersion(NSString *version) {
848 NSRange colon = [version rangeOfString:@":"];
849 if (colon.location != NSNotFound)
850 version = [version substringFromIndex:(colon.location + 1)];
854 NSString *Simplify(NSString *title) {
855 const char *data = [title UTF8String];
856 size_t size = [title length];
858 static Pcre square_r("^\\[(.*)\\]$");
859 if (square_r(data, size))
860 return Simplify(square_r[1]);
862 static Pcre paren_r("^\\((.*)\\)$");
863 if (paren_r(data, size))
864 return Simplify(paren_r[1]);
866 static Pcre title_r("^(.*?) \\(.*\\)$");
867 if (title_r(data, size))
868 return Simplify(title_r[1]);
874 bool isSectionVisible(NSString *section) {
875 NSDictionary *metadata = [Sections_ objectForKey:section];
876 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
877 return hidden == nil || ![hidden boolValue];
880 /* Delegate Prototypes {{{ */
884 @interface NSObject (ProgressDelegate)
887 @implementation NSObject(ProgressDelegate)
889 - (void) _setProgressError:(NSArray *)args {
890 [self performSelector:@selector(setProgressError:forPackage:)
891 withObject:[args objectAtIndex:0]
892 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
898 @protocol ProgressDelegate
899 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
900 - (void) setProgressTitle:(NSString *)title;
901 - (void) setProgressPercent:(float)percent;
902 - (void) startProgress;
903 - (void) addProgressOutput:(NSString *)output;
904 - (bool) isCancelling:(size_t)received;
907 @protocol ConfigurationDelegate
908 - (void) repairWithSelector:(SEL)selector;
909 - (void) setConfigurationData:(NSString *)data;
912 @protocol CydiaDelegate
913 - (void) installPackage:(Package *)package;
914 - (void) removePackage:(Package *)package;
915 - (void) slideUp:(UIActionSheet *)alert;
916 - (void) distUpgrade;
919 - (void) askForSettings;
920 - (UIProgressHUD *) addProgressHUD;
921 - (void) removeProgressHUD:(UIProgressHUD *)hud;
922 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
923 - (RVPage *) pageForPackage:(NSString *)name;
924 - (void) openMailToURL:(NSURL *)url;
925 - (void) clearFirstResponder;
929 /* Status Delegation {{{ */
931 public pkgAcquireStatus
934 _transient NSObject<ProgressDelegate> *delegate_;
942 void setDelegate(id delegate) {
943 delegate_ = delegate;
946 virtual bool MediaChange(std::string media, std::string drive) {
950 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
953 virtual void Fetch(pkgAcquire::ItemDesc &item) {
954 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
955 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
958 virtual void Done(pkgAcquire::ItemDesc &item) {
961 virtual void Fail(pkgAcquire::ItemDesc &item) {
963 item.Owner->Status == pkgAcquire::Item::StatIdle ||
964 item.Owner->Status == pkgAcquire::Item::StatDone
968 std::string &error(item.Owner->ErrorText);
972 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
973 NSArray *fields([description componentsSeparatedByString:@" "]);
974 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
976 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
977 withObject:[NSArray arrayWithObjects:
978 [NSString stringWithUTF8String:error.c_str()],
985 virtual bool Pulse(pkgAcquire *Owner) {
986 bool value = pkgAcquireStatus::Pulse(Owner);
989 double(CurrentBytes + CurrentItems) /
990 double(TotalBytes + TotalItems)
993 [delegate_ setProgressPercent:percent];
994 return [delegate_ isCancelling:CurrentBytes] ? false : value;
997 virtual void Start() {
998 [delegate_ startProgress];
1001 virtual void Stop() {
1005 /* Progress Delegation {{{ */
1010 _transient id<ProgressDelegate> delegate_;
1013 virtual void Update() {
1014 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
1015 [delegate_ setProgressPercent:(Percent / 100)];*/
1024 void setDelegate(id delegate) {
1025 delegate_ = delegate;
1028 virtual void Done() {
1029 //[delegate_ setProgressPercent:1];
1034 /* Database Interface {{{ */
1035 @interface Database : NSObject {
1036 pkgCacheFile cache_;
1037 pkgDepCache::Policy *policy_;
1038 pkgRecords *records_;
1039 pkgProblemResolver *resolver_;
1040 pkgAcquire *fetcher_;
1042 SPtr<pkgPackageManager> manager_;
1043 pkgSourceList *list_;
1045 NSMutableDictionary *sources_;
1046 NSMutableArray *packages_;
1048 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
1057 + (Database *) sharedInstance;
1059 - (void) _readCydia:(NSNumber *)fd;
1060 - (void) _readStatus:(NSNumber *)fd;
1061 - (void) _readOutput:(NSNumber *)fd;
1065 - (Package *) packageWithName:(NSString *)name;
1067 - (pkgCacheFile &) cache;
1068 - (pkgDepCache::Policy *) policy;
1069 - (pkgRecords *) records;
1070 - (pkgProblemResolver *) resolver;
1071 - (pkgAcquire &) fetcher;
1072 - (pkgSourceList &) list;
1073 - (NSArray *) packages;
1074 - (NSArray *) sources;
1075 - (void) reloadData;
1083 - (void) updateWithStatus:(Status &)status;
1085 - (void) setDelegate:(id)delegate;
1086 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
1090 /* Source Class {{{ */
1091 @interface Source : NSObject {
1092 NSString *description_;
1097 NSString *distribution_;
1101 NSString *defaultIcon_;
1103 NSDictionary *record_;
1107 - (Source *) initWithMetaIndex:(metaIndex *)index;
1109 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1111 - (NSDictionary *) record;
1115 - (NSString *) distribution;
1116 - (NSString *) type;
1118 - (NSString *) host;
1120 - (NSString *) name;
1121 - (NSString *) description;
1122 - (NSString *) label;
1123 - (NSString *) origin;
1124 - (NSString *) version;
1126 - (NSString *) defaultIcon;
1130 @implementation Source
1132 #define _clear(field) \
1139 _clear(distribution_)
1142 _clear(description_)
1146 _clear(defaultIcon_)
1155 + (NSArray *) _attributeKeys {
1156 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1159 - (NSArray *) attributeKeys {
1160 return [[self class] _attributeKeys];
1163 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1164 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1167 - (void) setMetaIndex:(metaIndex *)index {
1170 trusted_ = index->IsTrusted();
1172 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1173 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1174 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1176 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1177 if (dindex != NULL) {
1178 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1180 while (std::getline(release, line)) {
1181 std::string::size_type colon(line.find(':'));
1182 if (colon == std::string::npos)
1185 std::string name(line.substr(0, colon));
1186 std::string value(line.substr(colon + 1));
1187 while (!value.empty() && value[0] == ' ')
1188 value = value.substr(1);
1190 if (name == "Default-Icon")
1191 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1192 else if (name == "Description")
1193 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1194 else if (name == "Label")
1195 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1196 else if (name == "Origin")
1197 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1198 else if (name == "Version")
1199 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1203 record_ = [Sources_ objectForKey:[self key]];
1205 record_ = [record_ retain];
1208 - (Source *) initWithMetaIndex:(metaIndex *)index {
1209 if ((self = [super init]) != nil) {
1210 [self setMetaIndex:index];
1214 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1215 NSDictionary *lhr = [self record];
1216 NSDictionary *rhr = [source record];
1219 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1221 NSString *lhs = [self name];
1222 NSString *rhs = [source name];
1224 if ([lhs length] != 0 && [rhs length] != 0) {
1225 unichar lhc = [lhs characterAtIndex:0];
1226 unichar rhc = [rhs characterAtIndex:0];
1228 if (isalpha(lhc) && !isalpha(rhc))
1229 return NSOrderedAscending;
1230 else if (!isalpha(lhc) && isalpha(rhc))
1231 return NSOrderedDescending;
1234 return [lhs compare:rhs options:LaxCompareOptions_];
1237 - (NSDictionary *) record {
1245 - (NSString *) uri {
1249 - (NSString *) distribution {
1250 return distribution_;
1253 - (NSString *) type {
1257 - (NSString *) key {
1258 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1261 - (NSString *) host {
1262 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1265 - (NSString *) name {
1266 return origin_ == nil ? [self host] : origin_;
1269 - (NSString *) description {
1270 return description_;
1273 - (NSString *) label {
1274 return label_ == nil ? [self host] : label_;
1277 - (NSString *) origin {
1281 - (NSString *) version {
1285 - (NSString *) defaultIcon {
1286 return defaultIcon_;
1291 /* Relationship Class {{{ */
1292 @interface Relationship : NSObject {
1297 - (NSString *) type;
1299 - (NSString *) name;
1303 @implementation Relationship
1311 - (NSString *) type {
1319 - (NSString *) name {
1326 /* Package Class {{{ */
1327 @interface Package : NSObject {
1328 pkgCache::PkgIterator iterator_;
1329 _transient Database *database_;
1330 pkgCache::VerIterator version_;
1331 pkgCache::VerFileIterator file_;
1339 NSString *installed_;
1345 NSString *depiction_;
1346 NSString *homepage_;
1352 NSArray *relationships_;
1355 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1356 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1358 - (pkgCache::PkgIterator) iterator;
1360 - (NSString *) section;
1361 - (NSString *) simpleSection;
1365 - (Address *) maintainer;
1367 - (NSString *) description;
1370 - (NSMutableDictionary *) metadata;
1372 - (BOOL) subscribed;
1375 - (NSString *) latest;
1376 - (NSString *) installed;
1379 - (BOOL) upgradableAndEssential:(BOOL)essential;
1382 - (BOOL) unfiltered;
1386 - (BOOL) halfConfigured;
1387 - (BOOL) halfInstalled;
1389 - (NSString *) mode;
1392 - (NSString *) name;
1393 - (NSString *) tagline;
1395 - (NSString *) homepage;
1396 - (NSString *) depiction;
1397 - (Address *) author;
1399 - (NSArray *) files;
1400 - (NSArray *) relationships;
1401 - (NSArray *) warnings;
1402 - (NSArray *) applications;
1404 - (Source *) source;
1405 - (NSString *) role;
1407 - (BOOL) matches:(NSString *)text;
1409 - (bool) hasSupportingRole;
1410 - (BOOL) hasTag:(NSString *)tag;
1411 - (NSString *) primaryPurpose;
1412 - (NSArray *) purposes;
1414 - (NSComparisonResult) compareByName:(Package *)package;
1415 - (NSComparisonResult) compareBySection:(Package *)package;
1417 - (uint32_t) compareForChanges;
1422 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
1423 - (bool) isInstalledAndVisible:(NSNumber *)number;
1424 - (bool) isVisiblyUninstalledInSection:(NSString *)section;
1425 - (bool) isVisibleInSource:(Source *)source;
1429 @implementation Package
1435 if (section_ != nil)
1439 if (installed_ != nil)
1440 [installed_ release];
1448 if (depiction_ != nil)
1449 [depiction_ release];
1450 if (homepage_ != nil)
1451 [homepage_ release];
1452 if (sponsor_ != nil)
1461 if (relationships_ != nil)
1462 [relationships_ release];
1467 + (NSArray *) _attributeKeys {
1468 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1471 - (NSArray *) attributeKeys {
1472 return [[self class] _attributeKeys];
1475 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1476 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1479 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1480 if ((self = [super init]) != nil) { _profile(Package$initWithIterator)
1481 iterator_ = iterator;
1482 database_ = database;
1484 _profile(Package$initWithIterator$Control)
1487 _profile(Package$initWithIterator$Version)
1488 version_ = [database_ policy]->GetCandidateVer(iterator_);
1491 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1493 _profile(Package$initWithIterator$Latest)
1494 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1497 pkgCache::VerIterator current;
1498 NSString *installed;
1500 _profile(Package$initWithIterator$Current)
1501 current = iterator_.CurrentVer();
1502 installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1505 _profile(Package$initWithIterator$Installed)
1506 installed_ = [StripVersion(installed) retain];
1509 _profile(Package$initWithIterator$File)
1510 if (!version_.end())
1511 file_ = version_.FileList();
1513 pkgCache &cache([database_ cache]);
1514 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1518 _profile(Package$initWithIterator$Name)
1519 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1523 _profile(Package$initWithIterator$Parse)
1524 pkgRecords::Parser *parser;
1526 _profile(Package$initWithIterator$Parse$Lookup)
1527 parser = &[database_ records]->Lookup(file_);
1530 const char *begin, *end;
1531 parser->GetRec(begin, end);
1533 NSString *website(nil);
1534 NSString *sponsor(nil);
1535 NSString *author(nil);
1544 {"depiction", &depiction_},
1545 {"homepage", &homepage_},
1546 {"website", &website},
1547 {"sponsor", &sponsor},
1548 {"author", &author},
1552 while (begin != end)
1553 if (*begin == '\n') {
1556 } else if (isblank(*begin)) next: {
1557 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1560 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1561 const char *name(begin);
1562 size_t size(colon - begin);
1564 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1567 const char *stop(begin == NULL ? end : begin);
1568 while (stop[-1] == '\r')
1570 while (++colon != stop && isblank(*colon));
1572 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1573 if (strncasecmp(names[i].name_, name, size) == 0) {
1576 _profile(Package$initWithIterator$Parse$Value)
1577 value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
1580 *names[i].value_ = value;
1590 _profile(Package$initWithIterator$Parse$Retain)
1592 name_ = [name_ retain];
1593 _profile(Package$initWithIterator$Parse$Tagline)
1594 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1597 icon_ = [icon_ retain];
1598 if (depiction_ != nil)
1599 depiction_ = [depiction_ retain];
1600 if (homepage_ == nil)
1601 homepage_ = website;
1602 if ([homepage_ isEqualToString:depiction_])
1604 if (homepage_ != nil)
1605 homepage_ = [homepage_ retain];
1607 sponsor_ = [[Address addressWithString:sponsor] retain];
1609 author_ = [[Address addressWithString:author] retain];
1611 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1615 _profile(Package$initWithIterator$Tags)
1617 for (NSString *tag in tags_)
1618 if ([tag hasPrefix:@"role::"]) {
1619 role_ = [[tag substringFromIndex:6] retain];
1624 NSString *solid(latest == nil ? installed : latest);
1625 bool changed(false);
1627 NSString *key([id_ lowercaseString]);
1629 _profile(Package$initWithIterator$Metadata)
1630 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1631 if (metadata == nil) {
1632 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1637 [metadata setObject:solid forKey:@"LastVersion"];
1640 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1641 NSDate *last([metadata objectForKey:@"LastSeen"]);
1642 NSString *version([metadata objectForKey:@"LastVersion"]);
1645 first = last == nil ? now_ : last;
1646 [metadata setObject:first forKey:@"FirstSeen"];
1651 if (version == nil) {
1652 [metadata setObject:solid forKey:@"LastVersion"];
1654 } else if (![version isEqualToString:solid]) {
1655 [metadata setObject:solid forKey:@"LastVersion"];
1657 [metadata setObject:last forKey:@"LastSeen"];
1663 [Packages_ setObject:metadata forKey:key];
1670 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1671 return [[[Package alloc]
1672 initWithIterator:iterator
1677 - (pkgCache::PkgIterator) iterator {
1681 - (NSString *) section {
1682 if (section_ != nil)
1685 const char *section = iterator_.Section();
1686 if (section == NULL)
1689 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1692 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1693 if (NSString *rename = [value objectForKey:@"Rename"]) {
1698 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1702 - (NSString *) simpleSection {
1703 if (NSString *section = [self section])
1704 return Simplify(section);
1709 - (NSString *) uri {
1712 pkgIndexFile *index;
1713 pkgCache::PkgFileIterator file(file_.File());
1714 if (![database_ list].FindIndex(file, index))
1716 return [NSString stringWithUTF8String:iterator_->Path];
1717 //return [NSString stringWithUTF8String:file.Site()];
1718 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1722 - (Address *) maintainer {
1725 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1726 const std::string &maintainer(parser->Maintainer());
1727 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1731 return version_.end() ? 0 : version_->InstalledSize;
1734 - (NSString *) description {
1737 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1738 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1740 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1741 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1742 if ([lines count] < 2)
1745 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1746 for (size_t i(1), e([lines count]); i != e; ++i) {
1747 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1748 [trimmed addObject:trim];
1751 return [trimmed componentsJoinedByString:@"\n"];
1755 _profile(Package$index)
1756 NSString *name([self name]);
1757 if ([name length] == 0)
1759 unichar character([name characterAtIndex:0]);
1760 if (!isalpha(character))
1762 return toupper(character);
1766 - (NSMutableDictionary *) metadata {
1767 return [Packages_ objectForKey:[id_ lowercaseString]];
1771 NSDictionary *metadata([self metadata]);
1772 if ([self subscribed])
1773 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1775 return [metadata objectForKey:@"FirstSeen"];
1778 - (BOOL) subscribed {
1779 NSDictionary *metadata([self metadata]);
1780 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1781 return [subscribed boolValue];
1787 NSDictionary *metadata([self metadata]);
1788 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1789 return [ignored boolValue];
1794 - (NSString *) latest {
1798 - (NSString *) installed {
1803 return !version_.end();
1806 - (BOOL) upgradableAndEssential:(BOOL)essential {
1807 pkgCache::VerIterator current = iterator_.CurrentVer();
1811 value = essential && [self essential] && [self visible];
1813 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1817 - (BOOL) essential {
1818 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1822 return [database_ cache][iterator_].InstBroken();
1825 - (BOOL) unfiltered {
1826 NSString *section = [self section];
1827 return section == nil || isSectionVisible(section);
1831 return [self hasSupportingRole] && [self unfiltered];
1835 unsigned char current = iterator_->CurrentState;
1836 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1839 - (BOOL) halfConfigured {
1840 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1843 - (BOOL) halfInstalled {
1844 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1848 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1849 return state.Mode != pkgDepCache::ModeKeep;
1852 - (NSString *) mode {
1853 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1855 switch (state.Mode) {
1856 case pkgDepCache::ModeDelete:
1857 if ((state.iFlags & pkgDepCache::Purge) != 0)
1861 case pkgDepCache::ModeKeep:
1862 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1866 case pkgDepCache::ModeInstall:
1867 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1868 return @"Reinstall";
1869 else switch (state.Status) {
1871 return @"Downgrade";
1877 return @"New Install";
1890 - (NSString *) name {
1891 return name_ == nil ? id_ : name_;
1894 - (NSString *) tagline {
1898 - (UIImage *) icon {
1899 NSString *section = [self simpleSection];
1903 if ([icon_ hasPrefix:@"file:///"])
1904 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1905 if (icon == nil) if (section != nil)
1906 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1907 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1908 if ([dicon hasPrefix:@"file:///"])
1909 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1911 icon = [UIImage applicationImageNamed:@"unknown.png"];
1915 - (NSString *) homepage {
1919 - (NSString *) depiction {
1923 - (Address *) sponsor {
1927 - (Address *) author {
1931 - (NSArray *) files {
1932 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1933 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1936 fin.open([path UTF8String]);
1941 while (std::getline(fin, line))
1942 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1947 - (NSArray *) relationships {
1948 return relationships_;
1951 - (NSArray *) warnings {
1952 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1953 const char *name(iterator_.Name());
1955 size_t length(strlen(name));
1956 if (length < 2) invalid:
1957 [warnings addObject:@"illegal package identifier"];
1958 else for (size_t i(0); i != length; ++i)
1960 /* XXX: technically this is not allowed */
1961 (name[i] < 'A' || name[i] > 'Z') &&
1962 (name[i] < 'a' || name[i] > 'z') &&
1963 (name[i] < '0' || name[i] > '9') &&
1964 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1967 if (strcmp(name, "cydia") != 0) {
1969 bool _private = false;
1972 bool repository = [[self section] isEqualToString:@"Repositories"];
1974 if (NSArray *files = [self files])
1975 for (NSString *file in files)
1976 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1978 else if (!_private && [file isEqualToString:@"/private"])
1980 else if (!stash && [file isEqualToString:@"/var/stash"])
1983 /* XXX: this is not sensitive enough. only some folders are valid. */
1984 if (cydia && !repository)
1985 [warnings addObject:@"files installed into Cydia.app"];
1987 [warnings addObject:@"files installed with /private/*"];
1989 [warnings addObject:@"files installed to /var/stash"];
1992 return [warnings count] == 0 ? nil : warnings;
1995 - (NSArray *) applications {
1996 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1998 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
2000 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
2001 if (NSArray *files = [self files])
2002 for (NSString *file in files)
2003 if (application_r(file)) {
2004 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
2005 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
2006 if ([id isEqualToString:me])
2009 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
2011 display = application_r[1];
2013 NSString *bundle([file stringByDeletingLastPathComponent]);
2014 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
2015 if (icon == nil || [icon length] == 0)
2017 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
2019 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
2020 [applications addObject:application];
2022 [application addObject:id];
2023 [application addObject:display];
2024 [application addObject:url];
2027 return [applications count] == 0 ? nil : applications;
2030 - (Source *) source {
2032 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
2039 - (NSString *) role {
2043 - (BOOL) matches:(NSString *)text {
2049 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2050 if (range.location != NSNotFound)
2053 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2054 if (range.location != NSNotFound)
2057 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2058 if (range.location != NSNotFound)
2064 - (bool) hasSupportingRole {
2067 if ([role_ isEqualToString:@"enduser"])
2069 if ([Role_ isEqualToString:@"User"])
2071 if ([role_ isEqualToString:@"hacker"])
2073 if ([Role_ isEqualToString:@"Hacker"])
2075 if ([role_ isEqualToString:@"developer"])
2077 if ([Role_ isEqualToString:@"Developer"])
2082 - (BOOL) hasTag:(NSString *)tag {
2083 return tags_ == nil ? NO : [tags_ containsObject:tag];
2086 - (NSString *) primaryPurpose {
2087 for (NSString *tag in tags_)
2088 if ([tag hasPrefix:@"purpose::"])
2089 return [tag substringFromIndex:9];
2093 - (NSArray *) purposes {
2094 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2095 for (NSString *tag in tags_)
2096 if ([tag hasPrefix:@"purpose::"])
2097 [purposes addObject:[tag substringFromIndex:9]];
2098 return [purposes count] == 0 ? nil : purposes;
2101 - (NSComparisonResult) compareByName:(Package *)package {
2102 NSString *lhs = [self name];
2103 NSString *rhs = [package name];
2105 if ([lhs length] != 0 && [rhs length] != 0) {
2106 unichar lhc = [lhs characterAtIndex:0];
2107 unichar rhc = [rhs characterAtIndex:0];
2109 if (isalpha(lhc) && !isalpha(rhc))
2110 return NSOrderedAscending;
2111 else if (!isalpha(lhc) && isalpha(rhc))
2112 return NSOrderedDescending;
2115 return [lhs compare:rhs options:LaxCompareOptions_];
2118 - (NSComparisonResult) compareBySection:(Package *)package {
2119 NSString *lhs = [self section];
2120 NSString *rhs = [package section];
2122 if (lhs == NULL && rhs != NULL)
2123 return NSOrderedAscending;
2124 else if (lhs != NULL && rhs == NULL)
2125 return NSOrderedDescending;
2126 else if (lhs != NULL && rhs != NULL) {
2127 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2128 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2131 return NSOrderedSame;
2134 - (uint32_t) compareForChanges {
2139 uint32_t timestamp : 30;
2140 uint32_t ignored : 1;
2141 uint32_t upgradable : 1;
2145 bool upgradable([self upgradableAndEssential:YES]);
2146 value.bits.upgradable = upgradable ? 1 : 0;
2149 value.bits.timestamp = 0;
2150 value.bits.ignored = [self ignored] ? 0 : 1;
2151 value.bits.upgradable = 1;
2153 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2154 value.bits.ignored = 0;
2155 value.bits.upgradable = 0;
2158 return _not(uint32_t) - value.key;
2162 pkgProblemResolver *resolver = [database_ resolver];
2163 resolver->Clear(iterator_);
2164 resolver->Protect(iterator_);
2165 pkgCacheFile &cache([database_ cache]);
2166 cache->MarkInstall(iterator_, false);
2167 pkgDepCache::StateCache &state((*cache)[iterator_]);
2168 if (!state.Install())
2169 cache->SetReInstall(iterator_, true);
2173 pkgProblemResolver *resolver = [database_ resolver];
2174 resolver->Clear(iterator_);
2175 resolver->Protect(iterator_);
2176 resolver->Remove(iterator_);
2177 [database_ cache]->MarkDelete(iterator_, true);
2180 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
2181 _profile(Package$isUnfilteredAndSearchedForBy)
2184 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2185 value &= [self unfiltered];
2188 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2189 value &= [self matches:search];
2196 - (bool) isInstalledAndVisible:(NSNumber *)number {
2197 return (![number boolValue] || [self visible]) && [self installed] != nil;
2200 - (bool) isVisiblyUninstalledInSection:(NSString *)name {
2201 NSString *section = [self section];
2205 [self installed] == nil && (
2207 section == nil && [name length] == 0 ||
2208 [name isEqualToString:section]
2212 - (bool) isVisibleInSource:(Source *)source {
2213 return [self source] == source && [self visible];
2218 /* Section Class {{{ */
2219 @interface Section : NSObject {
2226 - (NSComparisonResult) compareByName:(Section *)section;
2227 - (Section *) initWithName:(NSString *)name;
2228 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2229 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2230 - (NSString *) name;
2234 - (void) addToCount;
2238 @implementation Section
2245 - (NSComparisonResult) compareByName:(Section *)section {
2246 NSString *lhs = [self name];
2247 NSString *rhs = [section name];
2249 if ([lhs length] != 0 && [rhs length] != 0) {
2250 unichar lhc = [lhs characterAtIndex:0];
2251 unichar rhc = [rhs characterAtIndex:0];
2253 if (isalpha(lhc) && !isalpha(rhc))
2254 return NSOrderedAscending;
2255 else if (!isalpha(lhc) && isalpha(rhc))
2256 return NSOrderedDescending;
2259 return [lhs compare:rhs options:LaxCompareOptions_];
2262 - (Section *) initWithName:(NSString *)name {
2263 return [self initWithName:name row:0];
2266 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2267 if ((self = [super init]) != nil) {
2268 name_ = [name retain];
2274 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2275 if ((self = [super init]) != nil) {
2276 name_ = [(index == '#' ? @"123" : [NSString stringWithCharacters:&index length:1]) retain];
2282 - (NSString *) name {
2298 - (void) addToCount {
2306 static NSArray *Finishes_;
2308 /* Database Implementation {{{ */
2309 @implementation Database
2311 + (Database *) sharedInstance {
2312 static Database *instance;
2313 if (instance == nil)
2314 instance = [[Database alloc] init];
2323 - (void) _readCydia:(NSNumber *)fd { _pooled
2324 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2325 std::istream is(&ib);
2328 static Pcre finish_r("^finish:([^:]*)$");
2330 while (std::getline(is, line)) {
2331 const char *data(line.c_str());
2332 size_t size = line.size();
2333 lprintf("C:%s\n", data);
2335 if (finish_r(data, size)) {
2336 NSString *finish = finish_r[1];
2337 int index = [Finishes_ indexOfObject:finish];
2338 if (index != INT_MAX && index > Finish_)
2346 - (void) _readStatus:(NSNumber *)fd { _pooled
2347 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2348 std::istream is(&ib);
2351 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2352 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2354 while (std::getline(is, line)) {
2355 const char *data(line.c_str());
2356 size_t size = line.size();
2357 lprintf("S:%s\n", data);
2359 if (conffile_r(data, size)) {
2360 [delegate_ setConfigurationData:conffile_r[1]];
2361 } else if (strncmp(data, "status: ", 8) == 0) {
2362 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2363 [delegate_ setProgressTitle:string];
2364 } else if (pmstatus_r(data, size)) {
2365 std::string type([pmstatus_r[1] UTF8String]);
2366 NSString *id = pmstatus_r[2];
2368 float percent([pmstatus_r[3] floatValue]);
2369 [delegate_ setProgressPercent:(percent / 100)];
2371 NSString *string = pmstatus_r[4];
2373 if (type == "pmerror")
2374 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2375 withObject:[NSArray arrayWithObjects:string, id, nil]
2378 else if (type == "pmstatus") {
2379 [delegate_ setProgressTitle:string];
2380 } else if (type == "pmconffile")
2381 [delegate_ setConfigurationData:string];
2382 else _assert(false);
2383 } else _assert(false);
2389 - (void) _readOutput:(NSNumber *)fd { _pooled
2390 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2391 std::istream is(&ib);
2394 while (std::getline(is, line)) {
2395 lprintf("O:%s\n", line.c_str());
2396 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2406 - (Package *) packageWithName:(NSString *)name {
2407 if (static_cast<pkgDepCache *>(cache_) == NULL)
2409 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2410 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2413 - (Database *) init {
2414 if ((self = [super init]) != nil) {
2421 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2422 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2426 _assert(pipe(fds) != -1);
2429 _config->Set("APT::Keep-Fds::", cydiafd_);
2430 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2433 detachNewThreadSelector:@selector(_readCydia:)
2435 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2438 _assert(pipe(fds) != -1);
2442 detachNewThreadSelector:@selector(_readStatus:)
2444 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2447 _assert(pipe(fds) != -1);
2448 _assert(dup2(fds[0], 0) != -1);
2449 _assert(close(fds[0]) != -1);
2451 input_ = fdopen(fds[1], "a");
2453 _assert(pipe(fds) != -1);
2454 _assert(dup2(fds[1], 1) != -1);
2455 _assert(close(fds[1]) != -1);
2458 detachNewThreadSelector:@selector(_readOutput:)
2460 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2465 - (pkgCacheFile &) cache {
2469 - (pkgDepCache::Policy *) policy {
2473 - (pkgRecords *) records {
2477 - (pkgProblemResolver *) resolver {
2481 - (pkgAcquire &) fetcher {
2485 - (pkgSourceList &) list {
2489 - (NSArray *) packages {
2493 - (NSArray *) sources {
2494 return [sources_ allValues];
2497 - (NSArray *) issues {
2498 if (cache_->BrokenCount() == 0)
2501 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2503 for (Package *package in packages_) {
2504 if (![package broken])
2506 pkgCache::PkgIterator pkg([package iterator]);
2508 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2509 [entry addObject:[package name]];
2510 [issues addObject:entry];
2512 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2516 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2517 pkgCache::DepIterator start;
2518 pkgCache::DepIterator end;
2519 dep.GlobOr(start, end); // ++dep
2521 if (!cache_->IsImportantDep(end))
2523 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2526 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2527 [entry addObject:failure];
2528 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2530 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2531 [failure addObject:[package name]];
2533 pkgCache::PkgIterator target(start.TargetPkg());
2534 if (target->ProvidesList != 0)
2535 [failure addObject:@"?"];
2537 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2539 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2540 else if (!cache_[target].CandidateVerIter(cache_).end())
2541 [failure addObject:@"-"];
2542 else if (target->ProvidesList == 0)
2543 [failure addObject:@"!"];
2545 [failure addObject:@"%"];
2549 if (start.TargetVer() != 0)
2550 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2561 - (void) reloadData { _pooled
2581 if (!cache_.Open(progress_, true)) {
2583 if (!_error->PopMessage(error))
2586 lprintf("cache_.Open():[%s]\n", error.c_str());
2588 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2589 [delegate_ repairWithSelector:@selector(configure)];
2590 else if (error == "The package lists or status file could not be parsed or opened.")
2591 [delegate_ repairWithSelector:@selector(update)];
2592 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2593 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2594 // else if (error == "The list of sources could not be read.")
2595 else _assert(false);
2601 now_ = [[NSDate date] retain];
2603 policy_ = new pkgDepCache::Policy();
2604 records_ = new pkgRecords(cache_);
2605 resolver_ = new pkgProblemResolver(cache_);
2606 fetcher_ = new pkgAcquire(&status_);
2609 list_ = new pkgSourceList();
2610 _assert(list_->ReadMainList());
2612 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2613 _assert(pkgApplyStatus(cache_));
2615 if (cache_->BrokenCount() != 0) {
2616 _assert(pkgFixBroken(cache_));
2617 _assert(cache_->BrokenCount() == 0);
2618 _assert(pkgMinimizeUpgrade(cache_));
2621 [sources_ removeAllObjects];
2622 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2623 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2624 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2626 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2627 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2631 [packages_ removeAllObjects];
2633 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2634 if (Package *package = [Package packageWithIterator:iterator database:self])
2635 [packages_ addObject:package];
2637 [packages_ sortUsingSelector:@selector(compareByName:)];
2640 _config->Set("Acquire::http::Timeout", 15);
2641 _config->Set("Acquire::http::MaxParallel", 4);
2644 - (void) configure {
2645 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2646 system([dpkg UTF8String]);
2654 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2655 _assert(!_error->PendingError());
2658 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2661 public pkgArchiveCleaner
2664 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2669 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2671 while (_error->PopMessage(error))
2672 lprintf("ArchiveCleaner: %s\n", error.c_str());
2677 pkgRecords records(cache_);
2679 lock_ = new FileFd();
2680 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2681 _assert(!_error->PendingError());
2684 // XXX: explain this with an error message
2685 _assert(list.ReadMainList());
2687 manager_ = (_system->CreatePM(cache_));
2688 _assert(manager_->GetArchives(fetcher_, &list, &records));
2689 _assert(!_error->PendingError());
2693 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2695 _assert(list.ReadMainList());
2696 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2697 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2700 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2705 bool failed = false;
2706 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2707 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2710 std::string uri = (*item)->DescURI();
2711 std::string error = (*item)->ErrorText;
2713 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2716 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2717 withObject:[NSArray arrayWithObjects:
2718 [NSString stringWithUTF8String:error.c_str()],
2730 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2732 if (_error->PendingError()) {
2737 if (result == pkgPackageManager::Failed) {
2742 if (result != pkgPackageManager::Completed) {
2747 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2749 _assert(list.ReadMainList());
2750 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2751 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2754 if (![before isEqualToArray:after])
2759 _assert(pkgDistUpgrade(cache_));
2763 [self updateWithStatus:status_];
2766 - (void) updateWithStatus:(Status &)status {
2768 _assert(list.ReadMainList());
2771 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2772 _assert(!_error->PendingError());
2774 pkgAcquire fetcher(&status);
2775 _assert(list.GetIndexes(&fetcher));
2777 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2778 bool failed = false;
2779 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2780 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2781 (*item)->Finished();
2785 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2786 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2787 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2790 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2795 - (void) setDelegate:(id)delegate {
2796 delegate_ = delegate;
2797 status_.setDelegate(delegate);
2798 progress_.setDelegate(delegate);
2801 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2802 pkgIndexFile *index(NULL);
2803 list_->FindIndex(file, index);
2804 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2810 /* PopUp Windows {{{ */
2811 @interface PopUpView : UIView {
2812 _transient id delegate_;
2813 UITransitionView *transition_;
2818 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2822 @implementation PopUpView
2825 [transition_ setDelegate:nil];
2826 [transition_ release];
2832 [transition_ transition:UITransitionPushFromTop toView:nil];
2835 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2836 if (from != nil && to == nil)
2837 [self removeFromSuperview];
2840 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2841 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2842 delegate_ = delegate;
2844 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2845 [self addSubview:transition_];
2847 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2849 [view addSubview:self];
2851 [transition_ setDelegate:self];
2853 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2854 [transition_ transition:UITransitionNone toView:blank];
2855 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2862 /* Mail Composition {{{ */
2863 @interface MailToView : PopUpView {
2864 MailComposeController *controller_;
2867 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2871 @implementation MailToView
2874 [controller_ release];
2878 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2882 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2883 NSLog(@"did:%@", delivery);
2884 // [UIApp setStatusBarShowsProgress:NO];
2885 if ([controller error]){
2886 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2887 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2888 [mailAlertSheet setBodyText:[controller error]];
2889 [mailAlertSheet popupAlertAnimated:YES];
2893 - (void) showError {
2894 NSLog(@"%@", [controller_ error]);
2895 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2896 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2897 [mailAlertSheet setBodyText:[controller_ error]];
2898 [mailAlertSheet popupAlertAnimated:YES];
2901 - (void) deliverMessage { _pooled
2905 if (![controller_ deliverMessage])
2906 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2909 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2910 if ([controller_ needsDelivery])
2911 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2916 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2917 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2918 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2919 [controller_ setDelegate:self];
2920 [controller_ initializeUI];
2921 [controller_ setupForURL:url];
2923 UIView *view([controller_ view]);
2924 [overlay_ addSubview:view];
2930 /* Confirmation View {{{ */
2931 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2932 if (!iterator.end())
2933 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2934 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2936 pkgCache::PkgIterator package(dep.TargetPkg());
2939 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2946 @protocol ConfirmationViewDelegate
2951 @interface ConfirmationView : BrowserView {
2952 _transient Database *database_;
2953 UIActionSheet *essential_;
2960 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2964 @implementation ConfirmationView
2971 if (essential_ != nil)
2972 [essential_ release];
2978 [book_ popFromSuperviewAnimated:YES];
2981 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2982 NSString *context([sheet context]);
2984 if ([context isEqualToString:@"remove"]) {
2992 [delegate_ confirm];
2999 } else if ([context isEqualToString:@"unable"]) {
3003 [super alertSheet:sheet buttonClicked:button];
3006 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3007 [window setValue:changes_ forKey:@"changes"];
3008 [window setValue:issues_ forKey:@"issues"];
3009 [window setValue:sizes_ forKey:@"sizes"];
3010 [super webView:sender didClearWindowObject:window forFrame:frame];
3013 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3014 if ((self = [super initWithBook:book]) != nil) {
3015 database_ = database;
3017 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
3018 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
3019 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
3020 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
3021 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
3025 pkgDepCache::Policy *policy([database_ policy]);
3027 pkgCacheFile &cache([database_ cache]);
3028 NSArray *packages = [database_ packages];
3029 for (Package *package in packages) {
3030 pkgCache::PkgIterator iterator = [package iterator];
3031 pkgDepCache::StateCache &state(cache[iterator]);
3033 NSString *name([package name]);
3035 if (state.NewInstall())
3036 [installing addObject:name];
3037 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
3038 [reinstalling addObject:name];
3039 else if (state.Upgrade())
3040 [upgrading addObject:name];
3041 else if (state.Downgrade())
3042 [downgrading addObject:name];
3043 else if (state.Delete()) {
3044 if ([package essential])
3046 [removing addObject:name];
3049 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3050 substrate_ |= DepSubstrate(iterator.CurrentVer());
3055 else if (Advanced_ || true) {
3056 essential_ = [[UIActionSheet alloc]
3057 initWithTitle:@"Removing Essentials"
3058 buttons:[NSArray arrayWithObjects:
3059 @"Cancel Operation (Safe)",
3060 @"Force Removal (Unsafe)",
3062 defaultButtonIndex:0
3068 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3070 [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."];
3072 essential_ = [[UIActionSheet alloc]
3073 initWithTitle:@"Unable to Comply"
3074 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3075 defaultButtonIndex:0
3080 [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."];
3083 changes_ = [[NSArray alloc] initWithObjects:
3091 issues_ = [database_ issues];
3093 issues_ = [issues_ retain];
3095 sizes_ = [[NSArray alloc] initWithObjects:
3096 SizeString([database_ fetcher].FetchNeeded()),
3097 SizeString([database_ fetcher].PartialPresent()),
3098 SizeString([database_ cache]->UsrSize()),
3101 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3105 - (NSString *) backButtonTitle {
3109 - (NSString *) leftButtonTitle {
3113 - (id) rightButtonTitle {
3114 return issues_ != nil ? nil : [super rightButtonTitle];
3117 - (id) _rightButtonTitle {
3118 #if AlwaysReload || IgnoreInstall
3119 return [super _rightButtonTitle];
3125 - (void) _leftButtonClicked {
3130 - (void) _rightButtonClicked {
3132 return [super _rightButtonClicked];
3134 if (essential_ != nil)
3135 [essential_ popupAlertAnimated:YES];
3139 [delegate_ confirm];
3147 /* Progress Data {{{ */
3148 @interface ProgressData : NSObject {
3154 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3161 @implementation ProgressData
3163 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3164 if ((self = [super init]) != nil) {
3165 selector_ = selector;
3185 /* Progress View {{{ */
3186 @interface ProgressView : UIView <
3187 ConfigurationDelegate,
3190 _transient Database *database_;
3192 UIView *background_;
3193 UITransitionView *transition_;
3195 UINavigationBar *navbar_;
3196 UIProgressBar *progress_;
3197 UITextView *output_;
3198 UITextLabel *status_;
3199 UIPushButton *close_;
3202 SHA1SumValue springlist_;
3203 SHA1SumValue notifyconf_;
3204 SHA1SumValue sandplate_;
3207 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3209 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3210 - (void) setContentView:(UIView *)view;
3213 - (void) _retachThread;
3214 - (void) _detachNewThreadData:(ProgressData *)data;
3215 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3221 @protocol ProgressViewDelegate
3222 - (void) progressViewIsComplete:(ProgressView *)sender;
3225 @implementation ProgressView
3228 [transition_ setDelegate:nil];
3229 [navbar_ setDelegate:nil];
3232 if (background_ != nil)
3233 [background_ release];
3234 [transition_ release];
3237 [progress_ release];
3244 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3245 if (bootstrap_ && from == overlay_ && to == view_)
3249 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3250 if ((self = [super initWithFrame:frame]) != nil) {
3251 database_ = database;
3252 delegate_ = delegate;
3254 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3255 [transition_ setDelegate:self];
3257 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3260 [overlay_ setBackgroundColor:[UIColor blackColor]];
3262 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3263 [background_ setBackgroundColor:[UIColor blackColor]];
3264 [self addSubview:background_];
3267 [self addSubview:transition_];
3269 CGSize navsize = [UINavigationBar defaultSize];
3270 CGRect navrect = {{0, 0}, navsize};
3272 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3273 [overlay_ addSubview:navbar_];
3275 [navbar_ setBarStyle:1];
3276 [navbar_ setDelegate:self];
3278 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3279 [navbar_ pushNavigationItem:navitem];
3281 CGRect bounds = [overlay_ bounds];
3282 CGSize prgsize = [UIProgressBar defaultSize];
3285 (bounds.size.width - prgsize.width) / 2,
3286 bounds.size.height - prgsize.height - 20
3289 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3290 [progress_ setStyle:0];
3292 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3294 bounds.size.height - prgsize.height - 50,
3295 bounds.size.width - 20,
3299 [status_ setColor:[UIColor whiteColor]];
3300 [status_ setBackgroundColor:[UIColor clearColor]];
3302 [status_ setCentersHorizontally:YES];
3303 //[status_ setFont:font];
3306 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3308 navrect.size.height + 20,
3309 bounds.size.width - 20,
3310 bounds.size.height - navsize.height - 62 - navrect.size.height
3314 //[output_ setTextFont:@"Courier New"];
3315 [output_ setTextSize:12];
3317 [output_ setTextColor:[UIColor whiteColor]];
3318 [output_ setBackgroundColor:[UIColor clearColor]];
3320 [output_ setMarginTop:0];
3321 [output_ setAllowsRubberBanding:YES];
3322 [output_ setEditable:NO];
3324 [overlay_ addSubview:output_];
3326 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3328 bounds.size.height - prgsize.height - 50,
3329 bounds.size.width - 20,
3333 [close_ setAutosizesToFit:NO];
3334 [close_ setDrawsShadow:YES];
3335 [close_ setStretchBackground:YES];
3336 [close_ setEnabled:YES];
3338 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3339 [close_ setTitleFont:bold];
3341 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3342 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3343 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3347 - (void) setContentView:(UIView *)view {
3348 view_ = [view retain];
3351 - (void) resetView {
3352 [transition_ transition:6 toView:view_];
3355 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3356 NSString *context([sheet context]);
3358 if ([context isEqualToString:@"error"])
3360 else if ([context isEqualToString:@"conffile"]) {
3361 FILE *input = [database_ input];
3365 fprintf(input, "N\n");
3369 fprintf(input, "Y\n");
3380 - (void) closeButtonPushed {
3389 [delegate_ suspendWithAnimation:YES];
3393 system("launchctl stop com.apple.SpringBoard");
3397 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3406 - (void) _retachThread {
3407 UINavigationItem *item = [navbar_ topItem];
3408 [item setTitle:@"Complete"];
3410 [overlay_ addSubview:close_];
3411 [progress_ removeFromSuperview];
3412 [status_ removeFromSuperview];
3414 [delegate_ progressViewIsComplete:self];
3417 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3418 MMap mmap(file, MMap::ReadOnly);
3420 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3421 if (!(sandplate_ == sha1.Result()))
3426 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3427 MMap mmap(file, MMap::ReadOnly);
3429 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3430 if (!(notifyconf_ == sha1.Result()))
3435 FileFd file(SpringBoard_, FileFd::ReadOnly);
3436 MMap mmap(file, MMap::ReadOnly);
3438 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3439 if (!(springlist_ == sha1.Result()))
3444 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3445 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3446 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3447 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3448 case 4: [close_ setTitle:@"Reboot Device"]; break;
3451 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3453 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3454 [cache autorelease];
3456 NSFileManager *manager = [NSFileManager defaultManager];
3457 NSError *error = nil;
3459 id system = [cache objectForKey:@"System"];
3464 if (stat(Cache_, &info) == -1)
3467 [system removeAllObjects];
3469 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3470 for (NSString *app in apps)
3471 if ([app hasSuffix:@".app"]) {
3472 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3473 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3474 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3476 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3477 [info setObject:path forKey:@"Path"];
3478 [info setObject:@"System" forKey:@"ApplicationType"];
3479 [system addInfoDictionary:info];
3485 [cache writeToFile:@Cache_ atomically:YES];
3487 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3489 if (chmod(Cache_, info.st_mode) == -1)
3493 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3496 notify_post("com.apple.mobile.application_installed");
3498 [delegate_ setStatusBarShowsProgress:NO];
3501 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3502 [[data target] performSelector:[data selector] withObject:[data object]];
3505 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3508 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3509 UINavigationItem *item = [navbar_ topItem];
3510 [item setTitle:title];
3512 [status_ setText:nil];
3513 [output_ setText:@""];
3514 [progress_ setProgress:0];
3516 [close_ removeFromSuperview];
3517 [overlay_ addSubview:progress_];
3518 [overlay_ addSubview:status_];
3520 [delegate_ setStatusBarShowsProgress:YES];
3524 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3525 MMap mmap(file, MMap::ReadOnly);
3527 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3528 sandplate_ = sha1.Result();
3532 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3533 MMap mmap(file, MMap::ReadOnly);
3535 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3536 notifyconf_ = sha1.Result();
3540 FileFd file(SpringBoard_, FileFd::ReadOnly);
3541 MMap mmap(file, MMap::ReadOnly);
3543 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3544 springlist_ = sha1.Result();
3547 [transition_ transition:6 toView:overlay_];
3550 detachNewThreadSelector:@selector(_detachNewThreadData:)
3552 withObject:[[ProgressData alloc]
3553 initWithSelector:selector
3560 - (void) repairWithSelector:(SEL)selector {
3562 detachNewThreadSelector:selector
3569 - (void) setConfigurationData:(NSString *)data {
3571 performSelectorOnMainThread:@selector(_setConfigurationData:)
3577 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3578 Package *package = id == nil ? nil : [database_ packageWithName:id];
3580 UIActionSheet *sheet = [[[UIActionSheet alloc]
3581 initWithTitle:(package == nil ? id : [package name])
3582 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3583 defaultButtonIndex:0
3588 [sheet setBodyText:error];
3589 [sheet popupAlertAnimated:YES];
3592 - (void) setProgressTitle:(NSString *)title {
3594 performSelectorOnMainThread:@selector(_setProgressTitle:)
3600 - (void) setProgressPercent:(float)percent {
3602 performSelectorOnMainThread:@selector(_setProgressPercent:)
3603 withObject:[NSNumber numberWithFloat:percent]
3608 - (void) startProgress {
3611 - (void) addProgressOutput:(NSString *)output {
3613 performSelectorOnMainThread:@selector(_addProgressOutput:)
3619 - (bool) isCancelling:(size_t)received {
3623 - (void) _setConfigurationData:(NSString *)data {
3624 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3626 _assert(conffile_r(data));
3628 NSString *ofile = conffile_r[1];
3629 //NSString *nfile = conffile_r[2];
3631 UIActionSheet *sheet = [[[UIActionSheet alloc]
3632 initWithTitle:@"Configuration Upgrade"
3633 buttons:[NSArray arrayWithObjects:
3634 @"Keep My Old Copy",
3635 @"Accept The New Copy",
3636 // XXX: @"See What Changed",
3638 defaultButtonIndex:0
3643 [sheet setBodyText:[NSString stringWithFormat:
3644 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3647 [sheet popupAlertAnimated:YES];
3650 - (void) _setProgressTitle:(NSString *)title {
3651 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3652 for (size_t i(0), e([words count]); i != e; ++i) {
3653 NSString *word([words objectAtIndex:i]);
3654 if (Package *package = [database_ packageWithName:word])
3655 [words replaceObjectAtIndex:i withObject:[package name]];
3658 [status_ setText:[words componentsJoinedByString:@" "]];
3661 - (void) _setProgressPercent:(NSNumber *)percent {
3662 [progress_ setProgress:[percent floatValue]];
3665 - (void) _addProgressOutput:(NSString *)output {
3666 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3667 CGSize size = [output_ contentSize];
3668 CGRect rect = {{0, size.height}, {size.width, 0}};
3669 [output_ scrollRectToVisible:rect animated:YES];
3672 - (BOOL) isRunning {
3679 /* Package Cell {{{ */
3680 @interface PackageCell : UISimpleTableCell {
3683 NSString *description_;
3687 UITextLabel *status_;
3691 - (PackageCell *) init;
3692 - (void) setPackage:(Package *)package;
3694 + (int) heightForPackage:(Package *)package;
3698 @implementation PackageCell
3700 - (void) clearPackage {
3711 if (description_ != nil) {
3712 [description_ release];
3716 if (source_ != nil) {
3721 if (badge_ != nil) {
3728 [self clearPackage];
3735 - (PackageCell *) init {
3736 if ((self = [super init]) != nil) {
3738 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3739 [status_ setBackgroundColor:[UIColor clearColor]];
3740 [status_ setFont:small];
3745 - (void) setPackage:(Package *)package {
3746 [self clearPackage];
3748 Source *source = [package source];
3749 NSString *section = [package simpleSection];
3751 icon_ = [[package icon] retain];
3753 name_ = [[package name] retain];
3754 description_ = [[package tagline] retain];
3756 NSString *label = nil;
3757 bool trusted = false;
3759 if (source != nil) {
3760 label = [source label];
3761 trusted = [source trusted];
3762 } else if ([[package id] isEqualToString:@"firmware"])
3765 label = @"Unknown/Local";
3767 NSString *from = [NSString stringWithFormat:@"from %@", label];
3769 if (section != nil && ![section isEqualToString:label])
3770 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3772 source_ = [from retain];
3774 if (NSString *purpose = [package primaryPurpose])
3775 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3776 badge_ = [badge_ retain];
3779 if (NSString *mode = [package mode]) {
3780 [badge_ setImage:[UIImage applicationImageNamed:
3781 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3784 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3785 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3786 } else if ([package half]) {
3787 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3788 [status_ setText:@"Package Damaged"];
3789 [status_ setColor:[UIColor redColor]];
3791 [badge_ setImage:nil];
3792 [status_ setText:nil];
3797 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3800 rect.size = [icon_ size];
3802 rect.size.width /= 2;
3803 rect.size.height /= 2;
3805 rect.origin.x = 25 - rect.size.width / 2;
3806 rect.origin.y = 25 - rect.size.height / 2;
3808 [icon_ drawInRect:rect];
3811 if (badge_ != nil) {
3812 CGSize size = [badge_ size];
3814 [badge_ drawAtPoint:CGPointMake(
3815 36 - size.width / 2,
3816 36 - size.height / 2
3825 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3826 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3830 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3832 [super drawContentInRect:rect selected:selected];
3835 + (int) heightForPackage:(Package *)package {
3836 NSString *tagline([package tagline]);
3837 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3839 if ([package hasMode] || [package half])
3848 /* Section Cell {{{ */
3849 @interface SectionCell : UISimpleTableCell {
3854 _UISwitchSlider *switch_;
3859 - (void) setSection:(Section *)section editing:(BOOL)editing;
3863 @implementation SectionCell
3865 - (void) clearSection {
3866 if (section_ != nil) {
3876 if (count_ != nil) {
3883 [self clearSection];
3890 if ((self = [super init]) != nil) {
3891 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3893 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3894 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3898 - (void) onSwitch:(id)sender {
3899 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3900 if (metadata == nil) {
3901 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3902 [Sections_ setObject:metadata forKey:section_];
3906 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3909 - (void) setSection:(Section *)section editing:(BOOL)editing {
3910 if (editing != editing_) {
3912 [switch_ removeFromSuperview];
3914 [self addSubview:switch_];
3918 [self clearSection];
3920 if (section == nil) {
3921 name_ = [@"All Packages" retain];
3924 section_ = [section name];
3925 if (section_ != nil)
3926 section_ = [section_ retain];
3927 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3928 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3931 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3935 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3936 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3943 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3945 CGSize size = [count_ sizeWithFont:Font14_];
3949 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3951 [super drawContentInRect:rect selected:selected];
3957 /* File Table {{{ */
3958 @interface FileTable : RVPage {
3959 _transient Database *database_;
3962 NSMutableArray *files_;
3966 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3967 - (void) setPackage:(Package *)package;
3971 @implementation FileTable
3974 if (package_ != nil)
3983 - (int) numberOfRowsInTable:(UITable *)table {
3984 return files_ == nil ? 0 : [files_ count];
3987 - (float) table:(UITable *)table heightForRow:(int)row {
3991 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3992 if (reusing == nil) {
3993 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3994 UIFont *font = [UIFont systemFontOfSize:16];
3995 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3997 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
4001 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4005 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4006 if ((self = [super initWithBook:book]) != nil) {
4007 database_ = database;
4009 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
4011 list_ = [[UITable alloc] initWithFrame:[self bounds]];
4012 [self addSubview:list_];
4014 UITableColumn *column = [[[UITableColumn alloc]
4015 initWithTitle:@"Name"
4017 width:[self frame].size.width
4020 [list_ setDataSource:self];
4021 [list_ setSeparatorStyle:1];
4022 [list_ addTableColumn:column];
4023 [list_ setDelegate:self];
4024 [list_ setReusesTableCells:YES];
4028 - (void) setPackage:(Package *)package {
4029 if (package_ != nil) {
4030 [package_ autorelease];
4039 [files_ removeAllObjects];
4041 if (package != nil) {
4042 package_ = [package retain];
4043 name_ = [[package id] retain];
4045 if (NSArray *files = [package files])
4046 [files_ addObjectsFromArray:files];
4048 if ([files_ count] != 0) {
4049 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4050 [files_ removeObjectAtIndex:0];
4051 [files_ sortUsingSelector:@selector(compareByPath:)];
4053 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4054 [stack addObject:@"/"];
4056 for (int i(0), e([files_ count]); i != e; ++i) {
4057 NSString *file = [files_ objectAtIndex:i];
4058 while (![file hasPrefix:[stack lastObject]])
4059 [stack removeLastObject];
4060 NSString *directory = [stack lastObject];
4061 [stack addObject:[file stringByAppendingString:@"/"]];
4062 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4063 ([stack count] - 2) * 3, "",
4064 [file substringFromIndex:[directory length]]
4073 - (void) resetViewAnimated:(BOOL)animated {
4074 [list_ resetViewAnimated:animated];
4077 - (void) reloadData {
4078 [self setPackage:[database_ packageWithName:name_]];
4079 [self reloadButtons];
4082 - (NSString *) title {
4083 return @"Installed Files";
4086 - (NSString *) backButtonTitle {
4092 /* Package View {{{ */
4093 @interface PackageView : BrowserView {
4094 _transient Database *database_;
4097 NSMutableArray *buttons_;
4100 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4101 - (void) setPackage:(Package *)package;
4105 @implementation PackageView
4108 if (package_ != nil)
4116 - (void) _clickButtonWithName:(NSString *)name {
4117 if ([name isEqualToString:@"Install"])
4118 [delegate_ installPackage:package_];
4119 else if ([name isEqualToString:@"Reinstall"])
4120 [delegate_ installPackage:package_];
4121 else if ([name isEqualToString:@"Remove"])
4122 [delegate_ removePackage:package_];
4123 else if ([name isEqualToString:@"Upgrade"])
4124 [delegate_ installPackage:package_];
4125 else _assert(false);
4128 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4129 NSString *context([sheet context]);
4131 if ([context isEqualToString:@"modify"]) {
4132 int count = [buttons_ count];
4133 _assert(count != 0);
4134 _assert(button <= count + 1);
4136 if (count != button - 1)
4137 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4141 [super alertSheet:sheet buttonClicked:button];
4144 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4145 return [super webView:sender didFinishLoadForFrame:frame];
4148 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4149 [window setValue:package_ forKey:@"package"];
4150 [super webView:sender didClearWindowObject:window forFrame:frame];
4153 - (bool) _allowJavaScriptPanel {
4158 - (void) _rightButtonClicked {
4159 /*[super _rightButtonClicked];
4162 int count = [buttons_ count];
4163 _assert(count != 0);
4166 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4168 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4169 [buttons addObjectsFromArray:buttons_];
4170 [buttons addObject:@"Cancel"];
4172 [delegate_ slideUp:[[[UIActionSheet alloc]
4175 defaultButtonIndex:2
4183 - (id) _rightButtonTitle {
4184 int count = [buttons_ count];
4185 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4188 - (NSString *) backButtonTitle {
4192 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4193 if ((self = [super initWithBook:book]) != nil) {
4194 database_ = database;
4195 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4199 - (void) setPackage:(Package *)package {
4200 if (package_ != nil) {
4201 [package_ autorelease];
4210 [buttons_ removeAllObjects];
4212 if (package != nil) {
4213 package_ = [package retain];
4214 name_ = [[package id] retain];
4216 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4218 if ([package_ source] == nil);
4219 else if ([package_ upgradableAndEssential:NO])
4220 [buttons_ addObject:@"Upgrade"];
4221 else if ([package_ installed] == nil)
4222 [buttons_ addObject:@"Install"];
4224 [buttons_ addObject:@"Reinstall"];
4225 if ([package_ installed] != nil)
4226 [buttons_ addObject:@"Remove"];
4234 - (void) reloadData {
4235 [self setPackage:[database_ packageWithName:name_]];
4236 [self reloadButtons];
4241 /* Package Table {{{ */
4242 @interface PackageTable : RVPage {
4243 _transient Database *database_;
4245 NSMutableArray *packages_;
4246 NSMutableArray *sections_;
4247 UISectionList *list_;
4250 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4252 - (void) setDelegate:(id)delegate;
4254 - (void) reloadData;
4255 - (void) resetCursor;
4257 - (UISectionList *) list;
4259 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4263 @implementation PackageTable
4266 [list_ setDataSource:nil];
4269 [packages_ release];
4270 [sections_ release];
4275 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4276 return [sections_ count];
4279 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4280 return [[sections_ objectAtIndex:section] name];
4283 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4284 return [[sections_ objectAtIndex:section] row];
4287 - (int) numberOfRowsInTable:(UITable *)table {
4288 return [packages_ count];
4291 - (float) table:(UITable *)table heightForRow:(int)row {
4292 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4295 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4297 reusing = [[[PackageCell alloc] init] autorelease];
4298 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4302 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4306 - (void) tableRowSelected:(NSNotification *)notification {
4307 int row = [[notification object] selectedRow];
4311 Package *package = [packages_ objectAtIndex:row];
4312 package = [database_ packageWithName:[package id]];
4313 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4314 [view setPackage:package];
4315 [view setDelegate:delegate_];
4316 [book_ pushPage:view];
4319 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4320 if ((self = [super initWithBook:book]) != nil) {
4321 database_ = database;
4322 title_ = [title retain];
4324 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4325 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4327 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4328 [list_ setDataSource:self];
4330 UITableColumn *column = [[[UITableColumn alloc]
4331 initWithTitle:@"Name"
4333 width:[self frame].size.width
4336 UITable *table = [list_ table];
4337 [table setSeparatorStyle:1];
4338 [table addTableColumn:column];
4339 [table setDelegate:self];
4340 [table setReusesTableCells:YES];
4342 [self addSubview:list_];
4344 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4345 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4349 - (void) setDelegate:(id)delegate {
4350 delegate_ = delegate;
4353 - (bool) hasPackage:(Package *)package {
4357 - (void) reloadData {
4358 NSArray *packages = [database_ packages];
4360 [packages_ removeAllObjects];
4361 [sections_ removeAllObjects];
4363 _profile(PackageTable$reloadData$Filter)
4364 for (Package *package in packages)
4365 if ([self hasPackage:package])
4366 [packages_ addObject:package];
4369 Section *section = nil;
4371 _profile(PackageTable$reloadData$Section)
4372 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4376 _profile(PackageTable$reloadData$Section$Package)
4377 package = [packages_ objectAtIndex:offset];
4378 index = [package index];
4381 if (section == nil || [section index] != index) {
4382 _profile(PackageTable$reloadData$Section$Allocate)
4383 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4386 _profile(PackageTable$reloadData$Section$Add)
4387 [sections_ addObject:section];
4391 [section addToCount];
4395 _profile(PackageTable$reloadData$List)
4400 - (NSString *) title {
4404 - (void) resetViewAnimated:(BOOL)animated {
4405 [list_ resetViewAnimated:animated];
4408 - (void) resetCursor {
4409 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4412 - (UISectionList *) list {
4416 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4417 [list_ setShouldHideHeaderInShortLists:hide];
4422 /* Filtered Package Table {{{ */
4423 @interface FilteredPackageTable : PackageTable {
4429 - (void) setObject:(id)object;
4431 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4435 @implementation FilteredPackageTable
4443 - (void) setObject:(id)object {
4449 object_ = [object retain];
4452 - (bool) hasPackage:(Package *)package {
4453 _profile(FilteredPackageTable$hasPackage)
4454 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
4458 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4459 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4461 object_ = object == nil ? nil : [object retain];
4463 /* XXX: this is an unsafe optimization of doomy hell */
4464 Method method = class_getInstanceMethod([Package class], filter);
4465 imp_ = method_getImplementation(method);
4466 _assert(imp_ != NULL);
4475 /* Add Source View {{{ */
4476 @interface AddSourceView : RVPage {
4477 _transient Database *database_;
4480 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4484 @implementation AddSourceView
4486 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4487 if ((self = [super initWithBook:book]) != nil) {
4488 database_ = database;
4494 /* Source Cell {{{ */
4495 @interface SourceCell : UITableCell {
4498 NSString *description_;
4504 - (SourceCell *) initWithSource:(Source *)source;
4508 @implementation SourceCell
4513 [description_ release];
4518 - (SourceCell *) initWithSource:(Source *)source {
4519 if ((self = [super init]) != nil) {
4521 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4523 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4524 icon_ = [icon_ retain];
4526 origin_ = [[source name] retain];
4527 label_ = [[source uri] retain];
4528 description_ = [[source description] retain];
4532 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4534 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4541 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4545 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4549 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4551 [super drawContentInRect:rect selected:selected];
4556 /* Source Table {{{ */
4557 @interface SourceTable : RVPage {
4558 _transient Database *database_;
4559 UISectionList *list_;
4560 NSMutableArray *sources_;
4561 UIActionSheet *alert_;
4565 UIProgressHUD *hud_;
4568 //NSURLConnection *installer_;
4569 NSURLConnection *trivial_bz2_;
4570 NSURLConnection *trivial_gz_;
4571 //NSURLConnection *automatic_;
4576 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4580 @implementation SourceTable
4582 - (void) _deallocConnection:(NSURLConnection *)connection {
4583 if (connection != nil) {
4584 [connection cancel];
4585 //[connection setDelegate:nil];
4586 [connection release];
4591 [[list_ table] setDelegate:nil];
4592 [list_ setDataSource:nil];
4601 //[self _deallocConnection:installer_];
4602 [self _deallocConnection:trivial_gz_];
4603 [self _deallocConnection:trivial_bz2_];
4604 //[self _deallocConnection:automatic_];
4611 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4612 return offset_ == 0 ? 1 : 2;
4615 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4616 switch (section + (offset_ == 0 ? 1 : 0)) {
4617 case 0: return @"Entered by User";
4618 case 1: return @"Installed by Packages";
4626 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4627 switch (section + (offset_ == 0 ? 1 : 0)) {
4629 case 1: return offset_;
4637 - (int) numberOfRowsInTable:(UITable *)table {
4638 return [sources_ count];
4641 - (float) table:(UITable *)table heightForRow:(int)row {
4642 Source *source = [sources_ objectAtIndex:row];
4643 return [source description] == nil ? 56 : 73;
4646 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4647 Source *source = [sources_ objectAtIndex:row];
4648 // XXX: weird warning, stupid selectors ;P
4649 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4652 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4656 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4660 - (void) tableRowSelected:(NSNotification*)notification {
4661 UITable *table([list_ table]);
4662 int row([table selectedRow]);
4666 Source *source = [sources_ objectAtIndex:row];
4668 PackageTable *packages = [[[FilteredPackageTable alloc]
4671 title:[source label]
4672 filter:@selector(isVisibleInSource:)
4676 [packages setDelegate:delegate_];
4678 [book_ pushPage:packages];
4681 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4682 Source *source = [sources_ objectAtIndex:row];
4683 return [source record] != nil;
4686 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4687 [[list_ table] setDeleteConfirmationRow:row];
4690 - (void) table:(UITable *)table deleteRow:(int)row {
4691 Source *source = [sources_ objectAtIndex:row];
4692 [Sources_ removeObjectForKey:[source key]];
4693 [delegate_ syncData];
4697 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4700 @"./", @"Distribution",
4701 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4703 [delegate_ syncData];
4706 - (NSString *) getWarning {
4707 NSString *href(href_);
4708 NSRange colon([href rangeOfString:@"://"]);
4709 if (colon.location != NSNotFound)
4710 href = [href substringFromIndex:(colon.location + 3)];
4711 href = [href stringByAddingPercentEscapes];
4712 href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href];
4713 href = [href stringByCachingURLWithCurrentCDN];
4715 NSURL *url([NSURL URLWithString:href]);
4717 NSStringEncoding encoding;
4718 NSError *error(nil);
4720 if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error])
4721 return [warning length] == 0 ? nil : warning;
4725 - (void) _endConnection:(NSURLConnection *)connection {
4726 NSURLConnection **field = NULL;
4727 if (connection == trivial_bz2_)
4728 field = &trivial_bz2_;
4729 else if (connection == trivial_gz_)
4730 field = &trivial_gz_;
4731 _assert(field != NULL);
4732 [connection release];
4736 trivial_bz2_ == nil &&
4742 if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
4745 UIActionSheet *sheet = [[[UIActionSheet alloc]
4746 initWithTitle:@"Source Warning"
4747 buttons:[NSArray arrayWithObjects:@"Add Anyway", @"Cancel", nil]
4748 defaultButtonIndex:0
4753 [sheet setNumberOfRows:1];
4755 [sheet setBodyText:warning];
4756 [sheet popupAlertAnimated:YES];
4759 } else if (error_ != nil) {
4760 UIActionSheet *sheet = [[[UIActionSheet alloc]
4761 initWithTitle:@"Verification Error"
4762 buttons:[NSArray arrayWithObjects:@"OK", nil]
4763 defaultButtonIndex:0
4768 [sheet setBodyText:[error_ localizedDescription]];
4769 [sheet popupAlertAnimated:YES];
4771 UIActionSheet *sheet = [[[UIActionSheet alloc]
4772 initWithTitle:@"Did not Find Repository"
4773 buttons:[NSArray arrayWithObjects:@"OK", nil]
4774 defaultButtonIndex:0
4779 [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."];
4780 [sheet popupAlertAnimated:YES];
4783 [delegate_ setStatusBarShowsProgress:NO];
4784 [delegate_ removeProgressHUD:hud_];
4794 if (error_ != nil) {
4801 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4802 switch ([response statusCode]) {
4808 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4809 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4811 error_ = [error retain];
4812 [self _endConnection:connection];
4815 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4816 [self _endConnection:connection];
4819 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4820 NSMutableURLRequest *request = [NSMutableURLRequest
4821 requestWithURL:[NSURL URLWithString:href]
4822 cachePolicy:NSURLRequestUseProtocolCachePolicy
4823 timeoutInterval:20.0
4826 [request setHTTPMethod:method];
4828 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4831 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4832 NSString *context([sheet context]);
4834 if ([context isEqualToString:@"source"]) {
4837 NSString *href = [[sheet textField] text];
4839 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4841 if (![href hasSuffix:@"/"])
4842 href_ = [href stringByAppendingString:@"/"];
4845 href_ = [href_ retain];
4847 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4848 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4849 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4853 hud_ = [[delegate_ addProgressHUD] retain];
4854 [hud_ setText:@"Verifying URL"];
4865 } else if ([context isEqualToString:@"trivial"])
4867 else if ([context isEqualToString:@"urlerror"])
4869 else if ([context isEqualToString:@"warning"]) {
4889 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4890 if ((self = [super initWithBook:book]) != nil) {
4891 database_ = database;
4892 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4894 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4895 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4896 [list_ setShouldHideHeaderInShortLists:NO];
4898 [self addSubview:list_];
4899 [list_ setDataSource:self];
4901 UITableColumn *column = [[UITableColumn alloc]
4902 initWithTitle:@"Name"
4904 width:[self frame].size.width
4907 UITable *table = [list_ table];
4908 [table setSeparatorStyle:1];
4909 [table addTableColumn:column];
4910 [table setDelegate:self];
4914 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4915 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4919 - (void) reloadData {
4921 _assert(list.ReadMainList());
4923 [sources_ removeAllObjects];
4924 [sources_ addObjectsFromArray:[database_ sources]];
4926 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4929 int count = [sources_ count];
4930 for (offset_ = 0; offset_ != count; ++offset_) {
4931 Source *source = [sources_ objectAtIndex:offset_];
4932 if ([source record] == nil)
4939 - (void) resetViewAnimated:(BOOL)animated {
4940 [list_ resetViewAnimated:animated];
4943 - (void) _leftButtonClicked {
4944 /*[book_ pushPage:[[[AddSourceView alloc]
4949 UIActionSheet *sheet = [[[UIActionSheet alloc]
4950 initWithTitle:@"Enter Cydia/APT URL"
4951 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4952 defaultButtonIndex:0
4957 [sheet setNumberOfRows:1];
4959 [sheet addTextFieldWithValue:@"http://" label:@""];
4961 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4962 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4963 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4964 [traits setKeyboardType:UIKeyboardTypeURL];
4965 // XXX: UIReturnKeyDone
4966 [traits setReturnKeyType:UIReturnKeyNext];
4968 [sheet popupAlertAnimated:YES];
4971 - (void) _rightButtonClicked {
4972 UITable *table = [list_ table];
4973 BOOL editing = [table isRowDeletionEnabled];
4974 [table enableRowDeletion:!editing animated:YES];
4975 [book_ reloadButtonsForPage:self];
4978 - (NSString *) title {
4982 - (NSString *) leftButtonTitle {
4983 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4986 - (id) rightButtonTitle {
4987 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4990 - (UINavigationButtonStyle) rightButtonStyle {
4991 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4997 /* Installed View {{{ */
4998 @interface InstalledView : RVPage {
4999 _transient Database *database_;
5000 FilteredPackageTable *packages_;
5004 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5008 @implementation InstalledView
5011 [packages_ release];
5015 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5016 if ((self = [super initWithBook:book]) != nil) {
5017 database_ = database;
5019 packages_ = [[FilteredPackageTable alloc]
5023 filter:@selector(isInstalledAndVisible:)
5024 with:[NSNumber numberWithBool:YES]
5027 [self addSubview:packages_];
5029 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5030 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5034 - (void) resetViewAnimated:(BOOL)animated {
5035 [packages_ resetViewAnimated:animated];
5038 - (void) reloadData {
5039 [packages_ reloadData];
5042 - (void) _rightButtonClicked {
5043 [packages_ setObject:[NSNumber numberWithBool:expert_]];
5044 [packages_ reloadData];
5046 [book_ reloadButtonsForPage:self];
5049 - (NSString *) title {
5050 return @"Installed";
5053 - (NSString *) backButtonTitle {
5057 - (id) rightButtonTitle {
5058 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
5061 - (UINavigationButtonStyle) rightButtonStyle {
5062 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5065 - (void) setDelegate:(id)delegate {
5066 [super setDelegate:delegate];
5067 [packages_ setDelegate:delegate];
5074 @interface HomeView : BrowserView {
5079 @implementation HomeView
5081 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5082 NSString *context([sheet context]);
5084 if ([context isEqualToString:@"about"])
5087 [super alertSheet:sheet buttonClicked:button];
5090 - (void) _leftButtonClicked {
5091 UIActionSheet *sheet = [[[UIActionSheet alloc]
5092 initWithTitle:@"About Cydia Installer"
5093 buttons:[NSArray arrayWithObjects:@"Close", nil]
5094 defaultButtonIndex:0
5100 @"Copyright (C) 2008-2009\n"
5101 "Jay Freeman (saurik)\n"
5102 "saurik@saurik.com\n"
5103 "http://www.saurik.com/\n"
5106 "http://www.theokorigroup.com/\n"
5108 "College of Creative Studies,\n"
5109 "University of California,\n"
5111 "http://www.ccs.ucsb.edu/"
5114 [sheet popupAlertAnimated:YES];
5117 - (NSString *) leftButtonTitle {
5123 /* Manage View {{{ */
5124 @interface ManageView : BrowserView {
5129 @implementation ManageView
5131 - (NSString *) title {
5135 - (void) _leftButtonClicked {
5136 [delegate_ askForSettings];
5139 - (NSString *) leftButtonTitle {
5144 - (id) _rightButtonTitle {
5156 #include <BrowserView.m>
5158 /* Cydia Book {{{ */
5159 @interface CYBook : RVBook <
5162 _transient Database *database_;
5163 UINavigationBar *overlay_;
5164 UINavigationBar *underlay_;
5165 UIProgressIndicator *indicator_;
5166 UITextLabel *prompt_;
5167 UIProgressBar *progress_;
5168 UINavigationButton *cancel_;
5172 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5178 @implementation CYBook
5182 [indicator_ release];
5184 [progress_ release];
5189 - (NSString *) getTitleForPage:(RVPage *)page {
5190 return Simplify([super getTitleForPage:page]);
5198 [UIView beginAnimations:nil context:NULL];
5200 CGRect ovrframe = [overlay_ frame];
5201 ovrframe.origin.y = 0;
5202 [overlay_ setFrame:ovrframe];
5204 CGRect barframe = [navbar_ frame];
5205 barframe.origin.y += ovrframe.size.height;
5206 [navbar_ setFrame:barframe];
5208 CGRect trnframe = [transition_ frame];
5209 trnframe.origin.y += ovrframe.size.height;
5210 trnframe.size.height -= ovrframe.size.height;
5211 [transition_ setFrame:trnframe];
5213 [UIView endAnimations];
5215 [indicator_ startAnimation];
5216 [prompt_ setText:@"Updating Database"];
5217 [progress_ setProgress:0];
5220 [overlay_ addSubview:cancel_];
5223 detachNewThreadSelector:@selector(_update)
5232 [indicator_ stopAnimation];
5234 [UIView beginAnimations:nil context:NULL];
5236 CGRect ovrframe = [overlay_ frame];
5237 ovrframe.origin.y = -ovrframe.size.height;
5238 [overlay_ setFrame:ovrframe];
5240 CGRect barframe = [navbar_ frame];
5241 barframe.origin.y -= ovrframe.size.height;
5242 [navbar_ setFrame:barframe];
5244 CGRect trnframe = [transition_ frame];
5245 trnframe.origin.y -= ovrframe.size.height;
5246 trnframe.size.height += ovrframe.size.height;
5247 [transition_ setFrame:trnframe];
5249 [UIView commitAnimations];
5251 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5254 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5255 if ((self = [super initWithFrame:frame]) != nil) {
5256 database_ = database;
5258 CGRect ovrrect = [navbar_ bounds];
5259 ovrrect.size.height = [UINavigationBar defaultSize].height;
5260 ovrrect.origin.y = -ovrrect.size.height;
5262 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5263 [self addSubview:overlay_];
5265 ovrrect.origin.y = frame.size.height;
5266 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5267 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5268 [self addSubview:underlay_];
5270 [overlay_ setBarStyle:1];
5271 [underlay_ setBarStyle:1];
5273 int barstyle = [overlay_ _barStyle:NO];
5274 bool ugly = barstyle == 0;
5276 UIProgressIndicatorStyle style = ugly ?
5277 UIProgressIndicatorStyleMediumBrown :
5278 UIProgressIndicatorStyleMediumWhite;
5280 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5281 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5282 CGRect indrect = {{indoffset, indoffset}, indsize};
5284 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5285 [indicator_ setStyle:style];
5286 [overlay_ addSubview:indicator_];
5288 CGSize prmsize = {215, indsize.height + 4};
5291 indoffset * 2 + indsize.width,
5295 unsigned(ovrrect.size.height - prmsize.height) / 2
5298 UIFont *font = [UIFont systemFontOfSize:15];
5300 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5302 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5303 [prompt_ setBackgroundColor:[UIColor clearColor]];
5304 [prompt_ setFont:font];
5306 [overlay_ addSubview:prompt_];
5308 CGSize prgsize = {75, 100};
5311 ovrrect.size.width - prgsize.width - 10,
5312 (ovrrect.size.height - prgsize.height) / 2
5315 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5316 [progress_ setStyle:0];
5317 [overlay_ addSubview:progress_];
5319 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5320 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5322 CGRect frame = [cancel_ frame];
5323 frame.size.width = 65;
5324 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5325 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5326 [cancel_ setFrame:frame];
5328 [cancel_ setBarStyle:barstyle];
5332 - (void) _onCancel {
5334 [cancel_ removeFromSuperview];
5337 - (void) _update { _pooled
5339 status.setDelegate(self);
5341 [database_ updateWithStatus:status];
5344 performSelectorOnMainThread:@selector(_update_)
5350 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5351 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5354 - (void) setProgressTitle:(NSString *)title {
5356 performSelectorOnMainThread:@selector(_setProgressTitle:)
5362 - (void) setProgressPercent:(float)percent {
5364 performSelectorOnMainThread:@selector(_setProgressPercent:)
5365 withObject:[NSNumber numberWithFloat:percent]
5370 - (void) startProgress {
5373 - (void) addProgressOutput:(NSString *)output {
5375 performSelectorOnMainThread:@selector(_addProgressOutput:)
5381 - (bool) isCancelling:(size_t)received {
5385 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5389 - (void) _setProgressTitle:(NSString *)title {
5390 [prompt_ setText:title];
5393 - (void) _setProgressPercent:(NSNumber *)percent {
5394 [progress_ setProgress:[percent floatValue]];
5397 - (void) _addProgressOutput:(NSString *)output {
5402 /* Cydia:// Protocol {{{ */
5403 @interface CydiaURLProtocol : NSURLProtocol {
5408 @implementation CydiaURLProtocol
5410 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5411 NSURL *url([request URL]);
5414 NSString *scheme([[url scheme] lowercaseString]);
5415 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5420 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5424 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5425 id<NSURLProtocolClient> client([self client]);
5427 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5429 NSData *data(UIImagePNGRepresentation(icon));
5431 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5432 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5433 [client URLProtocol:self didLoadData:data];
5434 [client URLProtocolDidFinishLoading:self];
5438 - (void) startLoading {
5439 id<NSURLProtocolClient> client([self client]);
5440 NSURLRequest *request([self request]);
5442 NSURL *url([request URL]);
5443 NSString *href([url absoluteString]);
5445 NSString *path([href substringFromIndex:8]);
5446 NSRange slash([path rangeOfString:@"/"]);
5449 if (slash.location == NSNotFound) {
5453 command = [path substringToIndex:slash.location];
5454 path = [path substringFromIndex:(slash.location + 1)];
5457 Database *database([Database sharedInstance]);
5459 if ([command isEqualToString:@"package-icon"]) {
5462 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5463 Package *package([database packageWithName:path]);
5466 UIImage *icon([package icon]);
5467 [self _returnPNGWithImage:icon forRequest:request];
5468 } else if ([command isEqualToString:@"source-icon"]) {
5471 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5472 NSString *source(Simplify(path));
5473 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5475 icon = [UIImage applicationImageNamed:@"unknown.png"];
5476 [self _returnPNGWithImage:icon forRequest:request];
5477 } else if ([command isEqualToString:@"uikit-image"]) {
5480 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5481 UIImage *icon(_UIImageWithName(path));
5482 [self _returnPNGWithImage:icon forRequest:request];
5483 } else if ([command isEqualToString:@"section-icon"]) {
5486 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5487 NSString *section(Simplify(path));
5488 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5490 icon = [UIImage applicationImageNamed:@"unknown.png"];
5491 [self _returnPNGWithImage:icon forRequest:request];
5493 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5497 - (void) stopLoading {
5503 /* Sections View {{{ */
5504 @interface SectionsView : RVPage {
5505 _transient Database *database_;
5506 NSMutableArray *sections_;
5507 NSMutableArray *filtered_;
5508 UITransitionView *transition_;
5514 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5515 - (void) reloadData;
5520 @implementation SectionsView
5523 [list_ setDataSource:nil];
5524 [list_ setDelegate:nil];
5526 [sections_ release];
5527 [filtered_ release];
5528 [transition_ release];
5530 [accessory_ release];
5534 - (int) numberOfRowsInTable:(UITable *)table {
5535 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5538 - (float) table:(UITable *)table heightForRow:(int)row {
5542 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5544 reusing = [[[SectionCell alloc] init] autorelease];
5545 [(SectionCell *)reusing setSection:(editing_ ?
5546 [sections_ objectAtIndex:row] :
5547 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5548 ) editing:editing_];
5552 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5556 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5560 - (void) tableRowSelected:(NSNotification *)notification {
5561 int row = [[notification object] selectedRow];
5572 title = @"All Packages";
5574 section = [filtered_ objectAtIndex:(row - 1)];
5575 name = [section name];
5581 title = @"(No Section)";
5585 PackageTable *table = [[[FilteredPackageTable alloc]
5589 filter:@selector(isVisiblyUninstalledInSection:)
5593 [table setDelegate:delegate_];
5595 [book_ pushPage:table];
5598 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5599 if ((self = [super initWithBook:book]) != nil) {
5600 database_ = database;
5602 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5603 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5605 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5606 [self addSubview:transition_];
5608 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5609 [transition_ transition:0 toView:list_];
5611 UITableColumn *column = [[[UITableColumn alloc]
5612 initWithTitle:@"Name"
5614 width:[self frame].size.width
5617 [list_ setDataSource:self];
5618 [list_ setSeparatorStyle:1];
5619 [list_ addTableColumn:column];
5620 [list_ setDelegate:self];
5621 [list_ setReusesTableCells:YES];
5625 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5626 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5630 - (void) reloadData {
5631 NSArray *packages = [database_ packages];
5633 [sections_ removeAllObjects];
5634 [filtered_ removeAllObjects];
5636 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5637 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5640 for (Package *package in packages) {
5641 NSString *name([package section]);
5644 Section *section([sections objectForKey:name]);
5645 if (section == nil) {
5646 section = [[[Section alloc] initWithName:name] autorelease];
5647 [sections setObject:section forKey:name];
5651 if ([package valid] && [package installed] == nil && [package visible])
5652 [filtered addObject:package];
5656 [sections_ addObjectsFromArray:[sections allValues]];
5657 [sections_ sortUsingSelector:@selector(compareByName:)];
5660 [filtered sortUsingSelector:@selector(compareBySection:)];
5663 Section *section = nil;
5664 for (Package *package in filtered) {
5665 NSString *name = [package section];
5667 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5668 section = name == nil ?
5669 [[[Section alloc] initWithName:nil] autorelease] :
5670 [sections objectForKey:name];
5671 [filtered_ addObject:section];
5674 [section addToCount];
5682 - (void) resetView {
5684 [self _rightButtonClicked];
5687 - (void) resetViewAnimated:(BOOL)animated {
5688 [list_ resetViewAnimated:animated];
5691 - (void) _rightButtonClicked {
5692 if ((editing_ = !editing_))
5695 [delegate_ updateData];
5696 [book_ reloadTitleForPage:self];
5697 [book_ reloadButtonsForPage:self];
5700 - (NSString *) title {
5701 return editing_ ? @"Section Visibility" : @"Install by Section";
5704 - (NSString *) backButtonTitle {
5708 - (id) rightButtonTitle {
5709 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5712 - (UINavigationButtonStyle) rightButtonStyle {
5713 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5716 - (UIView *) accessoryView {
5722 /* Changes View {{{ */
5723 @interface ChangesView : RVPage {
5724 _transient Database *database_;
5725 NSMutableArray *packages_;
5726 NSMutableArray *sections_;
5727 UISectionList *list_;
5731 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5732 - (void) reloadData;
5736 @implementation ChangesView
5739 [[list_ table] setDelegate:nil];
5740 [list_ setDataSource:nil];
5742 [packages_ release];
5743 [sections_ release];
5748 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5749 return [sections_ count];
5752 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5753 return [[sections_ objectAtIndex:section] name];
5756 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5757 return [[sections_ objectAtIndex:section] row];
5760 - (int) numberOfRowsInTable:(UITable *)table {
5761 return [packages_ count];
5764 - (float) table:(UITable *)table heightForRow:(int)row {
5765 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5768 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5770 reusing = [[[PackageCell alloc] init] autorelease];
5771 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5775 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5779 - (void) tableRowSelected:(NSNotification *)notification {
5780 int row = [[notification object] selectedRow];
5783 Package *package = [packages_ objectAtIndex:row];
5784 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5785 [view setDelegate:delegate_];
5786 [view setPackage:package];
5787 [book_ pushPage:view];
5790 - (void) _leftButtonClicked {
5791 [(CYBook *)book_ update];
5792 [self reloadButtons];
5795 - (void) _rightButtonClicked {
5796 [delegate_ distUpgrade];
5799 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5800 if ((self = [super initWithBook:book]) != nil) {
5801 database_ = database;
5803 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5804 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5806 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5807 [self addSubview:list_];
5809 [list_ setShouldHideHeaderInShortLists:NO];
5810 [list_ setDataSource:self];
5811 //[list_ setSectionListStyle:1];
5813 UITableColumn *column = [[[UITableColumn alloc]
5814 initWithTitle:@"Name"
5816 width:[self frame].size.width
5819 UITable *table = [list_ table];
5820 [table setSeparatorStyle:1];
5821 [table addTableColumn:column];
5822 [table setDelegate:self];
5823 [table setReusesTableCells:YES];
5827 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5828 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5832 - (void) reloadData {
5833 NSArray *packages = [database_ packages];
5835 [packages_ removeAllObjects];
5836 [sections_ removeAllObjects];
5839 for (Package *package in packages)
5841 [package installed] == nil && [package valid] && [package visible] ||
5842 [package upgradableAndEssential:YES]
5844 [packages_ addObject:package];
5847 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5850 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5851 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5852 Section *section = nil;
5856 bool unseens = false;
5858 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5861 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5862 Package *package = [packages_ objectAtIndex:offset];
5864 if (![package upgradableAndEssential:YES]) {
5866 NSDate *seen = [package seen];
5868 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5871 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5872 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5873 [sections_ addObject:section];
5877 [section addToCount];
5878 } else if ([package ignored])
5879 [ignored addToCount];
5882 [upgradable addToCount];
5887 CFRelease(formatter);
5890 Section *last = [sections_ lastObject];
5891 size_t count = [last count];
5892 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5893 [sections_ removeLastObject];
5896 if ([ignored count] != 0)
5897 [sections_ insertObject:ignored atIndex:0];
5899 [sections_ insertObject:upgradable atIndex:0];
5902 [self reloadButtons];
5905 - (void) resetViewAnimated:(BOOL)animated {
5906 [list_ resetViewAnimated:animated];
5909 - (NSString *) leftButtonTitle {
5910 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5913 - (id) rightButtonTitle {
5914 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5917 - (NSString *) title {
5923 /* Search View {{{ */
5924 @protocol SearchViewDelegate
5925 - (void) showKeyboard:(BOOL)show;
5928 @interface SearchView : RVPage {
5930 UISearchField *field_;
5931 UITransitionView *transition_;
5932 FilteredPackageTable *table_;
5933 UIPreferencesTable *advanced_;
5939 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5940 - (void) reloadData;
5944 @implementation SearchView
5947 [field_ setDelegate:nil];
5949 [accessory_ release];
5951 [transition_ release];
5953 [advanced_ release];
5958 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5962 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5964 case 0: return @"Advanced Search (Coming Soon!)";
5966 default: _assert(false);
5970 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5974 default: _assert(false);
5978 - (void) _showKeyboard:(BOOL)show {
5979 CGSize keysize = [UIKeyboard defaultSize];
5980 CGRect keydown = [book_ pageBounds];
5981 CGRect keyup = keydown;
5982 keyup.size.height -= keysize.height - ButtonBarHeight_;
5984 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5986 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5987 [animation setSignificantRectFields:8];
5990 [animation setStartFrame:keydown];
5991 [animation setEndFrame:keyup];
5993 [animation setStartFrame:keyup];
5994 [animation setEndFrame:keydown];
5997 UIAnimator *animator = [UIAnimator sharedAnimator];
6000 addAnimations:[NSArray arrayWithObjects:animation, nil]
6001 withDuration:(KeyboardTime_ - delay)
6006 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6008 [delegate_ showKeyboard:show];
6011 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6012 [self _showKeyboard:YES];
6015 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6016 [self _showKeyboard:NO];
6019 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6021 NSString *text([field_ text]);
6022 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6028 - (void) textFieldClearButtonPressed:(UITextField *)field {
6032 - (void) keyboardInputShouldDelete:(id)input {
6036 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6037 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6041 [field_ resignFirstResponder];
6046 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6047 if ((self = [super initWithBook:book]) != nil) {
6048 CGRect pageBounds = [book_ pageBounds];
6050 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6051 [self addSubview:transition_];
6053 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6055 [advanced_ setReusesTableCells:YES];
6056 [advanced_ setDataSource:self];
6057 [advanced_ reloadData];
6059 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6060 CGColor dimmed(space_, 0, 0, 0, 0.5);
6061 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6063 table_ = [[FilteredPackageTable alloc]
6067 filter:@selector(isUnfilteredAndSearchedForBy:)
6071 [table_ setShouldHideHeaderInShortLists:NO];
6072 [transition_ transition:0 toView:table_];
6081 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6088 [self bounds].size.width - area.origin.x - 18;
6090 area.size.height = [UISearchField defaultHeight];
6092 field_ = [[UISearchField alloc] initWithFrame:area];
6094 UIFont *font = [UIFont systemFontOfSize:16];
6095 [field_ setFont:font];
6097 [field_ setPlaceholder:@"Package Names & Descriptions"];
6098 [field_ setDelegate:self];
6100 [field_ setPaddingTop:5];
6102 UITextInputTraits *traits([field_ textInputTraits]);
6103 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6104 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6105 [traits setReturnKeyType:UIReturnKeySearch];
6107 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6109 accessory_ = [[UIView alloc] initWithFrame:accrect];
6110 [accessory_ addSubview:field_];
6112 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6113 [configure setShowPressFeedback:YES];
6114 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6115 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6116 [accessory_ addSubview:configure];*/
6118 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6119 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6125 LKAnimation *animation = [LKTransition animation];
6126 [animation setType:@"oglFlip"];
6127 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6128 [animation setFillMode:@"extended"];
6129 [animation setTransitionFlags:3];
6130 [animation setDuration:10];
6131 [animation setSpeed:0.35];
6132 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6133 [[transition_ _layer] addAnimation:animation forKey:0];
6134 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6135 flipped_ = !flipped_;
6139 - (void) configurePushed {
6140 [field_ resignFirstResponder];
6144 - (void) resetViewAnimated:(BOOL)animated {
6147 [table_ resetViewAnimated:animated];
6150 - (void) _reloadData {
6153 - (void) reloadData {
6156 [table_ setObject:[field_ text]];
6157 _profile(SearchView$reloadData)
6158 [table_ reloadData];
6161 [table_ resetCursor];
6164 - (UIView *) accessoryView {
6168 - (NSString *) title {
6172 - (NSString *) backButtonTitle {
6176 - (void) setDelegate:(id)delegate {
6177 [table_ setDelegate:delegate];
6178 [super setDelegate:delegate];
6184 @interface SettingsView : RVPage {
6185 _transient Database *database_;
6188 UIPreferencesTable *table_;
6189 _UISwitchSlider *subscribedSwitch_;
6190 _UISwitchSlider *ignoredSwitch_;
6191 UIPreferencesControlTableCell *subscribedCell_;
6192 UIPreferencesControlTableCell *ignoredCell_;
6195 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6199 @implementation SettingsView
6202 [table_ setDataSource:nil];
6205 if (package_ != nil)
6208 [subscribedSwitch_ release];
6209 [ignoredSwitch_ release];
6210 [subscribedCell_ release];
6211 [ignoredCell_ release];
6215 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6216 if (package_ == nil)
6222 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6223 if (package_ == nil)
6230 default: _assert(false);
6236 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6237 if (package_ == nil)
6244 default: _assert(false);
6250 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6251 if (package_ == nil)
6258 default: _assert(false);
6264 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6265 if (package_ == nil)
6268 _UISwitchSlider *slider([cell control]);
6269 BOOL value([slider value] != 0);
6270 NSMutableDictionary *metadata([package_ metadata]);
6273 if (NSNumber *number = [metadata objectForKey:key])
6274 before = [number boolValue];
6278 if (value != before) {
6279 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6281 [delegate_ updateData];
6285 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6286 [self onSomething:cell withKey:@"IsSubscribed"];
6289 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6290 [self onSomething:cell withKey:@"IsIgnored"];
6293 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6294 if (package_ == nil)
6298 case 0: switch (row) {
6300 return subscribedCell_;
6302 return ignoredCell_;
6303 default: _assert(false);
6306 case 1: switch (row) {
6308 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6309 [cell setShowSelection:NO];
6310 [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."];
6314 default: _assert(false);
6317 default: _assert(false);
6323 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6324 if ((self = [super initWithBook:book])) {
6325 database_ = database;
6326 name_ = [package retain];
6328 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6329 [self addSubview:table_];
6331 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6332 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6334 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6335 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6337 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6338 [subscribedCell_ setShowSelection:NO];
6339 [subscribedCell_ setTitle:@"Show All Changes"];
6340 [subscribedCell_ setControl:subscribedSwitch_];
6342 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6343 [ignoredCell_ setShowSelection:NO];
6344 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6345 [ignoredCell_ setControl:ignoredSwitch_];
6347 [table_ setDataSource:self];
6352 - (void) resetViewAnimated:(BOOL)animated {
6353 [table_ resetViewAnimated:animated];
6356 - (void) reloadData {
6357 if (package_ != nil)
6358 [package_ autorelease];
6359 package_ = [database_ packageWithName:name_];
6360 if (package_ != nil) {
6362 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6363 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6366 [table_ reloadData];
6369 - (NSString *) title {
6375 /* Signature View {{{ */
6376 @interface SignatureView : BrowserView {
6377 _transient Database *database_;
6381 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6385 @implementation SignatureView
6392 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6394 [super webView:sender didClearWindowObject:window forFrame:frame];
6397 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6398 if ((self = [super initWithBook:book]) != nil) {
6399 database_ = database;
6400 package_ = [package retain];
6405 - (void) resetViewAnimated:(BOOL)animated {
6408 - (void) reloadData {
6409 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6415 @interface Cydia : UIApplication <
6416 ConfirmationViewDelegate,
6417 ProgressViewDelegate,
6426 UIToolbar *buttonbar_;
6430 NSMutableArray *essential_;
6431 NSMutableArray *broken_;
6433 Database *database_;
6434 ProgressView *progress_;
6438 UIKeyboard *keyboard_;
6439 UIProgressHUD *hud_;
6441 SectionsView *sections_;
6442 ChangesView *changes_;
6443 ManageView *manage_;
6444 SearchView *search_;
6449 @implementation Cydia
6452 if ([broken_ count] != 0) {
6453 int count = [broken_ count];
6455 UIActionSheet *sheet = [[[UIActionSheet alloc]
6456 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6457 buttons:[NSArray arrayWithObjects:
6459 @"Ignore (Temporary)",
6461 defaultButtonIndex:0
6466 [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."];
6467 [sheet popupAlertAnimated:YES];
6468 } else if (!Ignored_ && [essential_ count] != 0) {
6469 int count = [essential_ count];
6471 UIActionSheet *sheet = [[[UIActionSheet alloc]
6472 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6473 buttons:[NSArray arrayWithObjects:
6474 @"Upgrade Essential",
6475 @"Complete Upgrade",
6476 @"Ignore (Temporary)",
6478 defaultButtonIndex:0
6483 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6484 [sheet popupAlertAnimated:YES];
6488 - (void) _reloadData {
6491 static bool loaded(false);
6492 UIProgressHUD *hud([self addProgressHUD]);
6493 [hud setText:(loaded ? @"Reloading Data" : @"Loading Data")];
6496 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6499 [self removeProgressHUD:hud];
6503 [essential_ removeAllObjects];
6504 [broken_ removeAllObjects];
6506 NSArray *packages = [database_ packages];
6507 for (Package *package in packages) {
6509 [broken_ addObject:package];
6510 if ([package upgradableAndEssential:NO]) {
6511 if ([package essential])
6512 [essential_ addObject:package];
6518 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6519 [buttonbar_ setBadgeValue:badge forButton:3];
6520 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6521 [buttonbar_ setBadgeAnimated:YES forButton:3];
6522 [self setApplicationBadge:badge];
6524 [buttonbar_ setBadgeValue:nil forButton:3];
6525 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6526 [buttonbar_ setBadgeAnimated:NO forButton:3];
6527 [self removeApplicationBadge];
6532 // XXX: what is this line of code for?
6533 if ([packages count] == 0);
6534 else if (Loaded_) loaded:
6539 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6540 NSTimeInterval interval([update timeIntervalSinceNow]);
6541 if (interval <= 0 && interval > -600)
6549 - (void) _saveConfig {
6552 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6558 - (void) updateData {
6561 /* XXX: this is just stupid */
6562 if (tag_ != 2 && sections_ != nil)
6563 [sections_ reloadData];
6564 if (tag_ != 3 && changes_ != nil)
6565 [changes_ reloadData];
6566 if (tag_ != 5 && search_ != nil)
6567 [search_ reloadData];
6577 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6578 _assert(file != NULL);
6580 NSArray *keys = [Sources_ allKeys];
6582 for (NSString *key in keys) {
6583 NSDictionary *source = [Sources_ objectForKey:key];
6585 fprintf(file, "%s %s %s\n",
6586 [[source objectForKey:@"Type"] UTF8String],
6587 [[source objectForKey:@"URI"] UTF8String],
6588 [[source objectForKey:@"Distribution"] UTF8String]
6597 detachNewThreadSelector:@selector(update_)
6600 title:@"Updating Sources"
6604 - (void) reloadData {
6605 @synchronized (self) {
6606 if (confirm_ == nil)
6612 pkgProblemResolver *resolver = [database_ resolver];
6614 resolver->InstallProtect();
6615 if (!resolver->Resolve(true))
6619 - (void) popUpBook:(RVBook *)book {
6620 [underlay_ popSubview:book];
6623 - (CGRect) popUpBounds {
6624 return [underlay_ bounds];
6628 [database_ prepare];
6630 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6631 [confirm_ setDelegate:self];
6633 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6634 [page setDelegate:self];
6636 [confirm_ setPage:page];
6637 [self popUpBook:confirm_];
6640 - (void) installPackage:(Package *)package {
6641 @synchronized (self) {
6648 - (void) removePackage:(Package *)package {
6649 @synchronized (self) {
6656 - (void) distUpgrade {
6657 @synchronized (self) {
6658 [database_ upgrade];
6664 @synchronized (self) {
6666 if (confirm_ != nil) {
6674 [overlay_ removeFromSuperview];
6678 detachNewThreadSelector:@selector(perform)
6685 - (void) bootstrap_ {
6687 [database_ upgrade];
6688 [database_ prepare];
6689 [database_ perform];
6692 - (void) bootstrap {
6694 detachNewThreadSelector:@selector(bootstrap_)
6697 title:@"Bootstrap Install"
6701 - (void) progressViewIsComplete:(ProgressView *)progress {
6702 if (confirm_ != nil) {
6703 [underlay_ addSubview:overlay_];
6704 [confirm_ popFromSuperviewAnimated:NO];
6710 - (void) setPage:(RVPage *)page {
6711 [page resetViewAnimated:NO];
6712 [page setDelegate:self];
6713 [book_ setPage:page];
6716 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6717 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6718 [browser loadURL:url];
6722 - (void) _setHomePage {
6723 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6726 - (void) buttonBarItemTapped:(id)sender {
6727 unsigned tag = [sender tag];
6729 [book_ resetViewAnimated:YES];
6731 } else if (tag_ == 2 && tag != 2)
6732 [sections_ resetView];
6735 case 1: [self _setHomePage]; break;
6737 case 2: [self setPage:sections_]; break;
6738 case 3: [self setPage:changes_]; break;
6739 case 4: [self setPage:manage_]; break;
6740 case 5: [self setPage:search_]; break;
6742 default: _assert(false);
6748 - (void) applicationWillSuspend {
6750 [super applicationWillSuspend];
6753 - (void) askForSettings {
6754 UIActionSheet *role = [[[UIActionSheet alloc]
6755 initWithTitle:@"Who Are You?"
6756 buttons:[NSArray arrayWithObjects:
6757 @"User (Graphical Only)",
6758 @"Hacker (+ Command Line)",
6759 @"Developer (No Filters)",
6761 defaultButtonIndex:-1
6766 [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."];
6767 [role popupAlertAnimated:YES];
6772 [self setStatusBarShowsProgress:NO];
6773 [self removeProgressHUD:hud_];
6778 pid_t pid = ExecFork();
6780 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6781 perror("launchctl stop");
6788 [self askForSettings];
6793 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6795 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6796 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6797 0, 0, screenrect.size.width, screenrect.size.height - 48
6798 ) database:database_];
6800 [book_ setDelegate:self];
6802 [overlay_ addSubview:book_];
6804 NSArray *buttonitems = [NSArray arrayWithObjects:
6805 [NSDictionary dictionaryWithObjectsAndKeys:
6806 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6807 @"home-up.png", kUIButtonBarButtonInfo,
6808 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6809 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6810 self, kUIButtonBarButtonTarget,
6811 @"Home", kUIButtonBarButtonTitle,
6812 @"0", kUIButtonBarButtonType,
6815 [NSDictionary dictionaryWithObjectsAndKeys:
6816 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6817 @"install-up.png", kUIButtonBarButtonInfo,
6818 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6819 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6820 self, kUIButtonBarButtonTarget,
6821 @"Sections", kUIButtonBarButtonTitle,
6822 @"0", kUIButtonBarButtonType,
6825 [NSDictionary dictionaryWithObjectsAndKeys:
6826 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6827 @"changes-up.png", kUIButtonBarButtonInfo,
6828 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6829 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6830 self, kUIButtonBarButtonTarget,
6831 @"Changes", kUIButtonBarButtonTitle,
6832 @"0", kUIButtonBarButtonType,
6835 [NSDictionary dictionaryWithObjectsAndKeys:
6836 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6837 @"manage-up.png", kUIButtonBarButtonInfo,
6838 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6839 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6840 self, kUIButtonBarButtonTarget,
6841 @"Manage", kUIButtonBarButtonTitle,
6842 @"0", kUIButtonBarButtonType,
6845 [NSDictionary dictionaryWithObjectsAndKeys:
6846 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6847 @"search-up.png", kUIButtonBarButtonInfo,
6848 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6849 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6850 self, kUIButtonBarButtonTarget,
6851 @"Search", kUIButtonBarButtonTitle,
6852 @"0", kUIButtonBarButtonType,
6856 buttonbar_ = [[UIToolbar alloc]
6858 withFrame:CGRectMake(
6859 0, screenrect.size.height - ButtonBarHeight_,
6860 screenrect.size.width, ButtonBarHeight_
6862 withItemList:buttonitems
6865 [buttonbar_ setDelegate:self];
6866 [buttonbar_ setBarStyle:1];
6867 [buttonbar_ setButtonBarTrackingMode:2];
6869 int buttons[5] = {1, 2, 3, 4, 5};
6870 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6871 [buttonbar_ showButtonGroup:0 withDuration:0];
6873 for (int i = 0; i != 5; ++i)
6874 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6875 i * 64 + 2, 1, 60, ButtonBarHeight_
6878 [buttonbar_ showSelectionForButton:1];
6879 [overlay_ addSubview:buttonbar_];
6881 [UIKeyboard initImplementationNow];
6882 CGSize keysize = [UIKeyboard defaultSize];
6883 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6884 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6885 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6886 [overlay_ addSubview:keyboard_];
6889 [underlay_ addSubview:overlay_];
6893 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6894 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6895 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6897 manage_ = (ManageView *) [[self
6898 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6899 withClass:[ManageView class]
6907 [self _setHomePage];
6910 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6911 NSString *context([sheet context]);
6913 if ([context isEqualToString:@"missing"])
6915 else if ([context isEqualToString:@"fixhalf"]) {
6918 @synchronized (self) {
6919 for (Package *broken in broken_) {
6922 NSString *id = [broken id];
6923 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6924 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6925 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6926 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6935 [broken_ removeAllObjects];
6944 } else if ([context isEqualToString:@"role"]) {
6946 case 1: Role_ = @"User"; break;
6947 case 2: Role_ = @"Hacker"; break;
6948 case 3: Role_ = @"Developer"; break;
6955 bool reset = Settings_ != nil;
6957 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6961 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6971 } else if ([context isEqualToString:@"upgrade"]) {
6974 @synchronized (self) {
6975 for (Package *essential in essential_)
6976 [essential install];
6999 - (void) reorganize { _pooled
7000 system("/usr/libexec/cydia/free.sh");
7001 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7004 - (void) applicationSuspend:(__GSEvent *)event {
7005 if (hud_ == nil && ![progress_ isRunning])
7006 [super applicationSuspend:event];
7009 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7011 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7014 - (void) _setSuspended:(BOOL)value {
7016 [super _setSuspended:value];
7019 - (UIProgressHUD *) addProgressHUD {
7020 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
7021 [window_ setUserInteractionEnabled:NO];
7023 [progress_ addSubview:hud];
7027 - (void) removeProgressHUD:(UIProgressHUD *)hud {
7029 [hud removeFromSuperview];
7030 [window_ setUserInteractionEnabled:YES];
7033 - (void) openMailToURL:(NSURL *)url {
7034 // XXX: this makes me sad
7036 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7038 [UIApp openURL:url];// asPanel:YES];
7042 - (void) clearFirstResponder {
7043 if (id responder = [window_ firstResponder])
7044 [responder resignFirstResponder];
7047 - (RVPage *) pageForPackage:(NSString *)name {
7048 if (Package *package = [database_ packageWithName:name]) {
7049 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7050 [view setPackage:package];
7053 UIActionSheet *sheet = [[[UIActionSheet alloc]
7054 initWithTitle:@"Cannot Locate Package"
7055 buttons:[NSArray arrayWithObjects:@"Close", nil]
7056 defaultButtonIndex:0
7061 [sheet setBodyText:[NSString stringWithFormat:
7062 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7065 [sheet popupAlertAnimated:YES];
7070 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7074 NSString *scheme([[url scheme] lowercaseString]);
7075 if (![scheme isEqualToString:@"cydia"])
7077 NSString *path([url absoluteString]);
7078 if ([path length] < 8)
7080 path = [path substringFromIndex:8];
7081 if (![path hasPrefix:@"/"])
7082 path = [@"/" stringByAppendingString:path];
7084 if ([path isEqualToString:@"/add-source"])
7085 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7086 else if ([path isEqualToString:@"/storage"])
7087 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
7088 else if ([path isEqualToString:@"/sources"])
7089 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7090 else if ([path isEqualToString:@"/packages"])
7091 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7092 else if ([path hasPrefix:@"/url/"])
7093 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
7094 else if ([path hasPrefix:@"/launch/"])
7095 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
7096 else if ([path hasPrefix:@"/package-settings/"])
7097 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
7098 else if ([path hasPrefix:@"/package-signature/"])
7099 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
7100 else if ([path hasPrefix:@"/package/"])
7101 return [self pageForPackage:[path substringFromIndex:9]];
7102 else if ([path hasPrefix:@"/files/"]) {
7103 NSString *name = [path substringFromIndex:7];
7105 if (Package *package = [database_ packageWithName:name]) {
7106 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7107 [files setPackage:package];
7115 - (void) applicationOpenURL:(NSURL *)url {
7116 [super applicationOpenURL:url];
7118 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7119 [self setPage:page];
7120 [buttonbar_ showSelectionForButton:tag];
7125 - (void) applicationDidFinishLaunching:(id)unused {
7127 Font12_ = [[UIFont systemFontOfSize:12] retain];
7128 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7129 Font14_ = [[UIFont systemFontOfSize:14] retain];
7130 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7131 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7133 _assert(pkgInitConfig(*_config));
7134 _assert(pkgInitSystem(*_config, _system));
7138 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7139 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7141 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7143 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7144 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7146 [window_ orderFront:self];
7147 [window_ makeKey:self];
7148 [window_ setHidden:NO];
7150 database_ = [Database sharedInstance];
7151 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7152 [database_ setDelegate:progress_];
7153 [window_ setContentView:progress_];
7155 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7156 [progress_ setContentView:underlay_];
7158 [progress_ resetView];
7161 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7162 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7163 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7164 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7165 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7166 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7167 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7169 [self setIdleTimerDisabled:YES];
7171 hud_ = [[self addProgressHUD] retain];
7172 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7174 [self setStatusBarShowsProgress:YES];
7177 detachNewThreadSelector:@selector(reorganize)
7185 - (void) showKeyboard:(BOOL)show {
7186 CGSize keysize = [UIKeyboard defaultSize];
7187 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7188 CGRect keyup = keydown;
7189 keyup.origin.y -= keysize.height;
7191 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7192 [animation setSignificantRectFields:2];
7195 [animation setStartFrame:keydown];
7196 [animation setEndFrame:keyup];
7197 [keyboard_ activate];
7199 [animation setStartFrame:keyup];
7200 [animation setEndFrame:keydown];
7201 [keyboard_ deactivate];
7204 [[UIAnimator sharedAnimator]
7205 addAnimations:[NSArray arrayWithObjects:animation, nil]
7206 withDuration:KeyboardTime_
7211 - (void) slideUp:(UIActionSheet *)alert {
7213 [alert presentSheetFromButtonBar:buttonbar_];
7215 [alert presentSheetInView:overlay_];
7220 void AddPreferences(NSString *plist) { _pooled
7221 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7222 _assert(settings != NULL);
7223 NSMutableArray *items = [settings objectForKey:@"items"];
7227 for (NSMutableDictionary *item in items) {
7228 NSString *label = [item objectForKey:@"label"];
7229 if (label != nil && [label isEqualToString:@"Cydia"]) {
7236 for (size_t i(0); i != [items count]; ++i) {
7237 NSDictionary *item([items objectAtIndex:i]);
7238 NSString *label = [item objectForKey:@"label"];
7239 if (label != nil && [label isEqualToString:@"General"]) {
7240 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7241 @"CydiaSettings", @"bundle",
7242 @"PSLinkCell", @"cell",
7243 [NSNumber numberWithBool:YES], @"hasIcon",
7244 [NSNumber numberWithBool:YES], @"isController",
7246 nil] atIndex:(i + 1)];
7252 _assert([settings writeToFile:plist atomically:YES] == YES);
7257 id Alloc_(id self, SEL selector) {
7258 id object = alloc_(self, selector);
7259 lprintf("[%s]A-%p\n", self->isa->name, object);
7264 id Dealloc_(id self, SEL selector) {
7265 id object = dealloc_(self, selector);
7266 lprintf("[%s]D-%p\n", self->isa->name, object);
7270 int main(int argc, char *argv[]) { _pooled
7272 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7274 bool substrate(false);
7280 for (int argi(1); argi != argc; ++argi)
7281 if (strcmp(argv[argi], "--") == 0) {
7283 argv[argi] = argv[0];
7289 for (int argi(1); argi != arge; ++argi)
7290 if (strcmp(args[argi], "--bootstrap") == 0)
7292 else if (strcmp(args[argi], "--substrate") == 0)
7295 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7298 App_ = [[NSBundle mainBundle] bundlePath];
7299 Home_ = NSHomeDirectory();
7300 Locale_ = CFLocaleCopyCurrent();
7303 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7304 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7305 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7306 Sounds_Keyboard_ = [keyboard boolValue];
7312 #if 1 /* XXX: this costs 1.4s of startup performance */
7313 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7314 _assert(errno == ENOENT);
7315 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7316 _assert(errno == ENOENT);
7319 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7320 alloc_ = alloc->method_imp;
7321 alloc->method_imp = (IMP) &Alloc_;*/
7323 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7324 dealloc_ = dealloc->method_imp;
7325 dealloc->method_imp = (IMP) &Dealloc_;*/
7330 size = sizeof(maxproc);
7331 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7332 perror("sysctlbyname(\"kern.maxproc\", ?)");
7333 else if (maxproc < 64) {
7335 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7336 perror("sysctlbyname(\"kern.maxproc\", #)");
7339 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7340 char *machine = new char[size];
7341 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7342 perror("sysctlbyname(\"hw.machine\", ?)");
7346 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7348 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7349 Build_ = [system objectForKey:@"ProductBuildVersion"];
7350 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
7351 Product_ = [info objectForKey:@"SafariProductVersion"];
7352 Safari_ = [info objectForKey:@"CFBundleVersion"];
7355 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7356 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7359 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7362 if (Metadata_ == NULL)
7363 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7365 Settings_ = [Metadata_ objectForKey:@"Settings"];
7367 Packages_ = [Metadata_ objectForKey:@"Packages"];
7368 Sections_ = [Metadata_ objectForKey:@"Sections"];
7369 Sources_ = [Metadata_ objectForKey:@"Sources"];
7372 if (Settings_ != nil)
7373 Role_ = [Settings_ objectForKey:@"Role"];
7375 if (Packages_ == nil) {
7376 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7377 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7380 if (Sections_ == nil) {
7381 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7382 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7385 if (Sources_ == nil) {
7386 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7387 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7391 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7394 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7395 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7396 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7397 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7399 if (access("/User", F_OK) != 0) {
7401 system("/usr/libexec/cydia/firmware.sh");
7405 _assert([[NSFileManager defaultManager]
7406 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7407 withIntermediateDirectories:YES
7412 space_ = CGColorSpaceCreateDeviceRGB();
7414 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7415 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7416 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7417 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7418 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7419 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7421 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7423 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7425 UIApplicationUseLegacyEvents(YES);
7426 UIKeyboardDisableAutomaticAppearance();
7429 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7431 CGColorSpaceRelease(space_);