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 {
1038 pkgCacheFile cache_;
1039 pkgDepCache::Policy *policy_;
1040 pkgRecords *records_;
1041 pkgProblemResolver *resolver_;
1042 pkgAcquire *fetcher_;
1044 SPtr<pkgPackageManager> manager_;
1045 pkgSourceList *list_;
1047 NSMutableDictionary *sources_;
1048 NSMutableArray *packages_;
1050 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
1059 + (Database *) sharedInstance;
1062 - (void) _readCydia:(NSNumber *)fd;
1063 - (void) _readStatus:(NSNumber *)fd;
1064 - (void) _readOutput:(NSNumber *)fd;
1068 - (Package *) packageWithName:(NSString *)name;
1070 - (pkgCacheFile &) cache;
1071 - (pkgDepCache::Policy *) policy;
1072 - (pkgRecords *) records;
1073 - (pkgProblemResolver *) resolver;
1074 - (pkgAcquire &) fetcher;
1075 - (pkgSourceList &) list;
1076 - (NSArray *) packages;
1077 - (NSArray *) sources;
1078 - (void) reloadData;
1086 - (void) updateWithStatus:(Status &)status;
1088 - (void) setDelegate:(id)delegate;
1089 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
1093 /* Source Class {{{ */
1094 @interface Source : NSObject {
1095 NSString *description_;
1100 NSString *distribution_;
1104 NSString *defaultIcon_;
1106 NSDictionary *record_;
1110 - (Source *) initWithMetaIndex:(metaIndex *)index;
1112 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1114 - (NSDictionary *) record;
1118 - (NSString *) distribution;
1119 - (NSString *) type;
1121 - (NSString *) host;
1123 - (NSString *) name;
1124 - (NSString *) description;
1125 - (NSString *) label;
1126 - (NSString *) origin;
1127 - (NSString *) version;
1129 - (NSString *) defaultIcon;
1133 @implementation Source
1135 #define _clear(field) \
1142 _clear(distribution_)
1145 _clear(description_)
1149 _clear(defaultIcon_)
1158 + (NSArray *) _attributeKeys {
1159 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1162 - (NSArray *) attributeKeys {
1163 return [[self class] _attributeKeys];
1166 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1167 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1170 - (void) setMetaIndex:(metaIndex *)index {
1173 trusted_ = index->IsTrusted();
1175 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1176 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1177 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1179 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1180 if (dindex != NULL) {
1181 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1183 while (std::getline(release, line)) {
1184 std::string::size_type colon(line.find(':'));
1185 if (colon == std::string::npos)
1188 std::string name(line.substr(0, colon));
1189 std::string value(line.substr(colon + 1));
1190 while (!value.empty() && value[0] == ' ')
1191 value = value.substr(1);
1193 if (name == "Default-Icon")
1194 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1195 else if (name == "Description")
1196 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1197 else if (name == "Label")
1198 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1199 else if (name == "Origin")
1200 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1201 else if (name == "Version")
1202 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1206 record_ = [Sources_ objectForKey:[self key]];
1208 record_ = [record_ retain];
1211 - (Source *) initWithMetaIndex:(metaIndex *)index {
1212 if ((self = [super init]) != nil) {
1213 [self setMetaIndex:index];
1217 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1218 NSDictionary *lhr = [self record];
1219 NSDictionary *rhr = [source record];
1222 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1224 NSString *lhs = [self name];
1225 NSString *rhs = [source name];
1227 if ([lhs length] != 0 && [rhs length] != 0) {
1228 unichar lhc = [lhs characterAtIndex:0];
1229 unichar rhc = [rhs characterAtIndex:0];
1231 if (isalpha(lhc) && !isalpha(rhc))
1232 return NSOrderedAscending;
1233 else if (!isalpha(lhc) && isalpha(rhc))
1234 return NSOrderedDescending;
1237 return [lhs compare:rhs options:LaxCompareOptions_];
1240 - (NSDictionary *) record {
1248 - (NSString *) uri {
1252 - (NSString *) distribution {
1253 return distribution_;
1256 - (NSString *) type {
1260 - (NSString *) key {
1261 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1264 - (NSString *) host {
1265 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1268 - (NSString *) name {
1269 return origin_ == nil ? [self host] : origin_;
1272 - (NSString *) description {
1273 return description_;
1276 - (NSString *) label {
1277 return label_ == nil ? [self host] : label_;
1280 - (NSString *) origin {
1284 - (NSString *) version {
1288 - (NSString *) defaultIcon {
1289 return defaultIcon_;
1294 /* Relationship Class {{{ */
1295 @interface Relationship : NSObject {
1300 - (NSString *) type;
1302 - (NSString *) name;
1306 @implementation Relationship
1314 - (NSString *) type {
1322 - (NSString *) name {
1329 /* Package Class {{{ */
1330 @interface Package : NSObject {
1333 pkgCache::PkgIterator iterator_;
1334 _transient Database *database_;
1335 pkgCache::VerIterator version_;
1336 pkgCache::VerFileIterator file_;
1345 NSString *installed_;
1351 NSString *depiction_;
1352 NSString *homepage_;
1358 NSArray *relationships_;
1361 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1362 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1364 - (pkgCache::PkgIterator) iterator;
1366 - (NSString *) section;
1367 - (NSString *) simpleSection;
1371 - (Address *) maintainer;
1373 - (NSString *) description;
1376 - (NSMutableDictionary *) metadata;
1378 - (BOOL) subscribed;
1381 - (NSString *) latest;
1382 - (NSString *) installed;
1385 - (BOOL) upgradableAndEssential:(BOOL)essential;
1388 - (BOOL) unfiltered;
1392 - (BOOL) halfConfigured;
1393 - (BOOL) halfInstalled;
1395 - (NSString *) mode;
1398 - (NSString *) name;
1399 - (NSString *) tagline;
1401 - (NSString *) homepage;
1402 - (NSString *) depiction;
1403 - (Address *) author;
1405 - (NSArray *) files;
1406 - (NSArray *) relationships;
1407 - (NSArray *) warnings;
1408 - (NSArray *) applications;
1410 - (Source *) source;
1411 - (NSString *) role;
1413 - (BOOL) matches:(NSString *)text;
1415 - (bool) hasSupportingRole;
1416 - (BOOL) hasTag:(NSString *)tag;
1417 - (NSString *) primaryPurpose;
1418 - (NSArray *) purposes;
1420 - (NSComparisonResult) compareByName:(Package *)package;
1421 - (NSComparisonResult) compareBySection:(Package *)package;
1423 - (uint32_t) compareForChanges;
1428 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
1429 - (bool) isInstalledAndVisible:(NSNumber *)number;
1430 - (bool) isVisiblyUninstalledInSection:(NSString *)section;
1431 - (bool) isVisibleInSource:(Source *)source;
1435 @implementation Package
1440 if (section_ != nil)
1444 if (installed_ != nil)
1445 [installed_ release];
1453 if (depiction_ != nil)
1454 [depiction_ release];
1455 if (homepage_ != nil)
1456 [homepage_ release];
1457 if (sponsor_ != nil)
1466 if (relationships_ != nil)
1467 [relationships_ release];
1472 + (NSArray *) _attributeKeys {
1473 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1476 - (NSArray *) attributeKeys {
1477 return [[self class] _attributeKeys];
1480 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1481 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1484 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1485 if ((self = [super init]) != nil) {
1486 _profile(Package$initWithIterator)
1487 @synchronized (database) {
1488 era_ = [database era];
1490 iterator_ = iterator;
1491 database_ = database;
1493 _profile(Package$initWithIterator$Control)
1496 _profile(Package$initWithIterator$Version)
1497 version_ = [database_ policy]->GetCandidateVer(iterator_);
1500 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1502 _profile(Package$initWithIterator$Latest)
1503 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1506 pkgCache::VerIterator current;
1507 NSString *installed;
1509 _profile(Package$initWithIterator$Current)
1510 current = iterator_.CurrentVer();
1511 installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1514 _profile(Package$initWithIterator$Installed)
1515 installed_ = [StripVersion(installed) retain];
1518 _profile(Package$initWithIterator$File)
1519 if (!version_.end())
1520 file_ = version_.FileList();
1522 pkgCache &cache([database_ cache]);
1523 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1527 _profile(Package$initWithIterator$Name)
1528 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1532 _profile(Package$initWithIterator$Parse)
1533 pkgRecords::Parser *parser;
1535 _profile(Package$initWithIterator$Parse$Lookup)
1536 parser = &[database_ records]->Lookup(file_);
1539 const char *begin, *end;
1540 parser->GetRec(begin, end);
1542 NSString *website(nil);
1543 NSString *sponsor(nil);
1544 NSString *author(nil);
1553 {"depiction", &depiction_},
1554 {"homepage", &homepage_},
1555 {"website", &website},
1556 {"sponsor", &sponsor},
1557 {"author", &author},
1561 while (begin != end)
1562 if (*begin == '\n') {
1565 } else if (isblank(*begin)) next: {
1566 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1569 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1570 const char *name(begin);
1571 size_t size(colon - begin);
1573 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1576 const char *stop(begin == NULL ? end : begin);
1577 while (stop[-1] == '\r')
1579 while (++colon != stop && isblank(*colon));
1581 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1582 if (strncasecmp(names[i].name_, name, size) == 0) {
1585 _profile(Package$initWithIterator$Parse$Value)
1586 value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
1589 *names[i].value_ = value;
1599 _profile(Package$initWithIterator$Parse$Retain)
1601 name_ = [name_ retain];
1602 _profile(Package$initWithIterator$Parse$Tagline)
1603 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1606 icon_ = [icon_ retain];
1607 if (depiction_ != nil)
1608 depiction_ = [depiction_ retain];
1609 if (homepage_ == nil)
1610 homepage_ = website;
1611 if ([homepage_ isEqualToString:depiction_])
1613 if (homepage_ != nil)
1614 homepage_ = [homepage_ retain];
1616 sponsor_ = [[Address addressWithString:sponsor] retain];
1618 author_ = [[Address addressWithString:author] retain];
1620 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1624 _profile(Package$initWithIterator$Tags)
1626 for (NSString *tag in tags_)
1627 if ([tag hasPrefix:@"role::"]) {
1628 role_ = [[tag substringFromIndex:6] retain];
1633 NSString *solid(latest == nil ? installed : latest);
1634 bool changed(false);
1636 NSString *key([id_ lowercaseString]);
1638 _profile(Package$initWithIterator$Metadata)
1639 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1640 if (metadata == nil) {
1641 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1646 [metadata setObject:solid forKey:@"LastVersion"];
1649 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1650 NSDate *last([metadata objectForKey:@"LastSeen"]);
1651 NSString *version([metadata objectForKey:@"LastVersion"]);
1654 first = last == nil ? now_ : last;
1655 [metadata setObject:first forKey:@"FirstSeen"];
1660 if (version == nil) {
1661 [metadata setObject:solid forKey:@"LastVersion"];
1663 } else if (![version isEqualToString:solid]) {
1664 [metadata setObject:solid forKey:@"LastVersion"];
1666 [metadata setObject:last forKey:@"LastSeen"];
1672 [Packages_ setObject:metadata forKey:key];
1677 const char *section(iterator_.Section());
1678 if (section == NULL)
1681 NSString *name([[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_']);
1684 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1685 if (NSString *rename = [value objectForKey:@"Rename"]) {
1690 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1693 essential_ = (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1694 } _end } return self;
1697 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1698 return [[[Package alloc]
1699 initWithIterator:iterator
1704 - (pkgCache::PkgIterator) iterator {
1708 - (NSString *) section {
1712 - (NSString *) simpleSection {
1713 if (NSString *section = [self section])
1714 return Simplify(section);
1719 - (NSString *) uri {
1722 pkgIndexFile *index;
1723 pkgCache::PkgFileIterator file(file_.File());
1724 if (![database_ list].FindIndex(file, index))
1726 return [NSString stringWithUTF8String:iterator_->Path];
1727 //return [NSString stringWithUTF8String:file.Site()];
1728 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1732 - (Address *) maintainer {
1735 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1736 const std::string &maintainer(parser->Maintainer());
1737 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1741 return version_.end() ? 0 : version_->InstalledSize;
1744 - (NSString *) description {
1747 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1748 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1750 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1751 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1752 if ([lines count] < 2)
1755 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1756 for (size_t i(1), e([lines count]); i != e; ++i) {
1757 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1758 [trimmed addObject:trim];
1761 return [trimmed componentsJoinedByString:@"\n"];
1765 _profile(Package$index)
1766 NSString *name([self name]);
1767 if ([name length] == 0)
1769 unichar character([name characterAtIndex:0]);
1770 if (!isalpha(character))
1772 return toupper(character);
1776 - (NSMutableDictionary *) metadata {
1777 return [Packages_ objectForKey:[id_ lowercaseString]];
1781 NSDictionary *metadata([self metadata]);
1782 if ([self subscribed])
1783 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1785 return [metadata objectForKey:@"FirstSeen"];
1788 - (BOOL) subscribed {
1789 NSDictionary *metadata([self metadata]);
1790 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1791 return [subscribed boolValue];
1797 NSDictionary *metadata([self metadata]);
1798 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1799 return [ignored boolValue];
1804 - (NSString *) latest {
1808 - (NSString *) installed {
1813 return !version_.end();
1816 - (BOOL) upgradableAndEssential:(BOOL)essential {
1817 pkgCache::VerIterator current = iterator_.CurrentVer();
1821 value = essential && [self essential] && [self visible];
1823 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1827 - (BOOL) essential {
1832 return [database_ cache][iterator_].InstBroken();
1835 - (BOOL) unfiltered {
1836 NSString *section = [self section];
1837 return section == nil || isSectionVisible(section);
1841 return [self hasSupportingRole] && [self unfiltered];
1845 unsigned char current = iterator_->CurrentState;
1846 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1849 - (BOOL) halfConfigured {
1850 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1853 - (BOOL) halfInstalled {
1854 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1858 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1859 return state.Mode != pkgDepCache::ModeKeep;
1862 - (NSString *) mode {
1863 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1865 switch (state.Mode) {
1866 case pkgDepCache::ModeDelete:
1867 if ((state.iFlags & pkgDepCache::Purge) != 0)
1871 case pkgDepCache::ModeKeep:
1872 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1876 case pkgDepCache::ModeInstall:
1877 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1878 return @"Reinstall";
1879 else switch (state.Status) {
1881 return @"Downgrade";
1887 return @"New Install";
1900 - (NSString *) name {
1901 return name_ == nil ? id_ : name_;
1904 - (NSString *) tagline {
1908 - (UIImage *) icon {
1909 NSString *section = [self simpleSection];
1913 if ([icon_ hasPrefix:@"file:///"])
1914 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1915 if (icon == nil) if (section != nil)
1916 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1917 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1918 if ([dicon hasPrefix:@"file:///"])
1919 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1921 icon = [UIImage applicationImageNamed:@"unknown.png"];
1925 - (NSString *) homepage {
1929 - (NSString *) depiction {
1933 - (Address *) sponsor {
1937 - (Address *) author {
1941 - (NSArray *) files {
1942 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1943 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1946 fin.open([path UTF8String]);
1951 while (std::getline(fin, line))
1952 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1957 - (NSArray *) relationships {
1958 return relationships_;
1961 - (NSArray *) warnings {
1962 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1963 const char *name(iterator_.Name());
1965 size_t length(strlen(name));
1966 if (length < 2) invalid:
1967 [warnings addObject:@"illegal package identifier"];
1968 else for (size_t i(0); i != length; ++i)
1970 /* XXX: technically this is not allowed */
1971 (name[i] < 'A' || name[i] > 'Z') &&
1972 (name[i] < 'a' || name[i] > 'z') &&
1973 (name[i] < '0' || name[i] > '9') &&
1974 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1977 if (strcmp(name, "cydia") != 0) {
1979 bool _private = false;
1982 bool repository = [[self section] isEqualToString:@"Repositories"];
1984 if (NSArray *files = [self files])
1985 for (NSString *file in files)
1986 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1988 else if (!_private && [file isEqualToString:@"/private"])
1990 else if (!stash && [file isEqualToString:@"/var/stash"])
1993 /* XXX: this is not sensitive enough. only some folders are valid. */
1994 if (cydia && !repository)
1995 [warnings addObject:@"files installed into Cydia.app"];
1997 [warnings addObject:@"files installed with /private/*"];
1999 [warnings addObject:@"files installed to /var/stash"];
2002 return [warnings count] == 0 ? nil : warnings;
2005 - (NSArray *) applications {
2006 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
2008 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
2010 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
2011 if (NSArray *files = [self files])
2012 for (NSString *file in files)
2013 if (application_r(file)) {
2014 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
2015 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
2016 if ([id isEqualToString:me])
2019 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
2021 display = application_r[1];
2023 NSString *bundle([file stringByDeletingLastPathComponent]);
2024 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
2025 if (icon == nil || [icon length] == 0)
2027 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
2029 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
2030 [applications addObject:application];
2032 [application addObject:id];
2033 [application addObject:display];
2034 [application addObject:url];
2037 return [applications count] == 0 ? nil : applications;
2040 - (Source *) source {
2042 @synchronized (database_) {
2043 if ([database_ era] != era_ || file_.end())
2046 source_ = [database_ getSource:file_.File()];
2058 - (NSString *) role {
2062 - (BOOL) matches:(NSString *)text {
2068 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2069 if (range.location != NSNotFound)
2072 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2073 if (range.location != NSNotFound)
2076 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2077 if (range.location != NSNotFound)
2083 - (bool) hasSupportingRole {
2086 if ([role_ isEqualToString:@"enduser"])
2088 if ([Role_ isEqualToString:@"User"])
2090 if ([role_ isEqualToString:@"hacker"])
2092 if ([Role_ isEqualToString:@"Hacker"])
2094 if ([role_ isEqualToString:@"developer"])
2096 if ([Role_ isEqualToString:@"Developer"])
2101 - (BOOL) hasTag:(NSString *)tag {
2102 return tags_ == nil ? NO : [tags_ containsObject:tag];
2105 - (NSString *) primaryPurpose {
2106 for (NSString *tag in tags_)
2107 if ([tag hasPrefix:@"purpose::"])
2108 return [tag substringFromIndex:9];
2112 - (NSArray *) purposes {
2113 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2114 for (NSString *tag in tags_)
2115 if ([tag hasPrefix:@"purpose::"])
2116 [purposes addObject:[tag substringFromIndex:9]];
2117 return [purposes count] == 0 ? nil : purposes;
2120 - (NSComparisonResult) compareByName:(Package *)package {
2121 NSString *lhs = [self name];
2122 NSString *rhs = [package name];
2124 if ([lhs length] != 0 && [rhs length] != 0) {
2125 unichar lhc = [lhs characterAtIndex:0];
2126 unichar rhc = [rhs characterAtIndex:0];
2128 if (isalpha(lhc) && !isalpha(rhc))
2129 return NSOrderedAscending;
2130 else if (!isalpha(lhc) && isalpha(rhc))
2131 return NSOrderedDescending;
2134 return [lhs compare:rhs options:LaxCompareOptions_];
2137 - (NSComparisonResult) compareBySection:(Package *)package {
2138 NSString *lhs = [self section];
2139 NSString *rhs = [package section];
2141 if (lhs == NULL && rhs != NULL)
2142 return NSOrderedAscending;
2143 else if (lhs != NULL && rhs == NULL)
2144 return NSOrderedDescending;
2145 else if (lhs != NULL && rhs != NULL) {
2146 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2147 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2150 return NSOrderedSame;
2153 - (uint32_t) compareForChanges {
2158 uint32_t timestamp : 30;
2159 uint32_t ignored : 1;
2160 uint32_t upgradable : 1;
2164 bool upgradable([self upgradableAndEssential:YES]);
2165 value.bits.upgradable = upgradable ? 1 : 0;
2168 value.bits.timestamp = 0;
2169 value.bits.ignored = [self ignored] ? 0 : 1;
2170 value.bits.upgradable = 1;
2172 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2173 value.bits.ignored = 0;
2174 value.bits.upgradable = 0;
2177 return _not(uint32_t) - value.key;
2181 pkgProblemResolver *resolver = [database_ resolver];
2182 resolver->Clear(iterator_);
2183 resolver->Protect(iterator_);
2184 pkgCacheFile &cache([database_ cache]);
2185 cache->MarkInstall(iterator_, false);
2186 pkgDepCache::StateCache &state((*cache)[iterator_]);
2187 if (!state.Install())
2188 cache->SetReInstall(iterator_, true);
2192 pkgProblemResolver *resolver = [database_ resolver];
2193 resolver->Clear(iterator_);
2194 resolver->Protect(iterator_);
2195 resolver->Remove(iterator_);
2196 [database_ cache]->MarkDelete(iterator_, true);
2199 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
2200 _profile(Package$isUnfilteredAndSearchedForBy)
2203 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2204 value &= [self unfiltered];
2207 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2208 value &= [self matches:search];
2215 - (bool) isInstalledAndVisible:(NSNumber *)number {
2216 return (![number boolValue] || [self visible]) && [self installed] != nil;
2219 - (bool) isVisiblyUninstalledInSection:(NSString *)name {
2220 NSString *section = [self section];
2224 [self installed] == nil && (
2226 section == nil && [name length] == 0 ||
2227 [name isEqualToString:section]
2231 - (bool) isVisibleInSource:(Source *)source {
2232 return [self source] == source && [self visible];
2237 /* Section Class {{{ */
2238 @interface Section : NSObject {
2245 - (NSComparisonResult) compareByName:(Section *)section;
2246 - (Section *) initWithName:(NSString *)name;
2247 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2248 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2249 - (NSString *) name;
2253 - (void) addToCount;
2257 @implementation Section
2264 - (NSComparisonResult) compareByName:(Section *)section {
2265 NSString *lhs = [self name];
2266 NSString *rhs = [section name];
2268 if ([lhs length] != 0 && [rhs length] != 0) {
2269 unichar lhc = [lhs characterAtIndex:0];
2270 unichar rhc = [rhs characterAtIndex:0];
2272 if (isalpha(lhc) && !isalpha(rhc))
2273 return NSOrderedAscending;
2274 else if (!isalpha(lhc) && isalpha(rhc))
2275 return NSOrderedDescending;
2278 return [lhs compare:rhs options:LaxCompareOptions_];
2281 - (Section *) initWithName:(NSString *)name {
2282 return [self initWithName:name row:0];
2285 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2286 if ((self = [super init]) != nil) {
2287 name_ = [name retain];
2293 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2294 if ((self = [super init]) != nil) {
2295 name_ = [(index == '#' ? @"123" : [NSString stringWithCharacters:&index length:1]) retain];
2301 - (NSString *) name {
2317 - (void) addToCount {
2325 static NSArray *Finishes_;
2327 /* Database Implementation {{{ */
2328 @implementation Database
2330 + (Database *) sharedInstance {
2331 static Database *instance;
2332 if (instance == nil)
2333 instance = [[Database alloc] init];
2346 - (void) _readCydia:(NSNumber *)fd { _pooled
2347 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2348 std::istream is(&ib);
2351 static Pcre finish_r("^finish:([^:]*)$");
2353 while (std::getline(is, line)) {
2354 const char *data(line.c_str());
2355 size_t size = line.size();
2356 lprintf("C:%s\n", data);
2358 if (finish_r(data, size)) {
2359 NSString *finish = finish_r[1];
2360 int index = [Finishes_ indexOfObject:finish];
2361 if (index != INT_MAX && index > Finish_)
2369 - (void) _readStatus:(NSNumber *)fd { _pooled
2370 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2371 std::istream is(&ib);
2374 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2375 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2377 while (std::getline(is, line)) {
2378 const char *data(line.c_str());
2379 size_t size = line.size();
2380 lprintf("S:%s\n", data);
2382 if (conffile_r(data, size)) {
2383 [delegate_ setConfigurationData:conffile_r[1]];
2384 } else if (strncmp(data, "status: ", 8) == 0) {
2385 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2386 [delegate_ setProgressTitle:string];
2387 } else if (pmstatus_r(data, size)) {
2388 std::string type([pmstatus_r[1] UTF8String]);
2389 NSString *id = pmstatus_r[2];
2391 float percent([pmstatus_r[3] floatValue]);
2392 [delegate_ setProgressPercent:(percent / 100)];
2394 NSString *string = pmstatus_r[4];
2396 if (type == "pmerror")
2397 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2398 withObject:[NSArray arrayWithObjects:string, id, nil]
2401 else if (type == "pmstatus") {
2402 [delegate_ setProgressTitle:string];
2403 } else if (type == "pmconffile")
2404 [delegate_ setConfigurationData:string];
2405 else _assert(false);
2406 } else _assert(false);
2412 - (void) _readOutput:(NSNumber *)fd { _pooled
2413 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2414 std::istream is(&ib);
2417 while (std::getline(is, line)) {
2418 lprintf("O:%s\n", line.c_str());
2419 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2429 - (Package *) packageWithName:(NSString *)name {
2430 if (static_cast<pkgDepCache *>(cache_) == NULL)
2432 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2433 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2436 - (Database *) init {
2437 if ((self = [super init]) != nil) {
2444 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2445 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2449 _assert(pipe(fds) != -1);
2452 _config->Set("APT::Keep-Fds::", cydiafd_);
2453 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2456 detachNewThreadSelector:@selector(_readCydia:)
2458 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2461 _assert(pipe(fds) != -1);
2465 detachNewThreadSelector:@selector(_readStatus:)
2467 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2470 _assert(pipe(fds) != -1);
2471 _assert(dup2(fds[0], 0) != -1);
2472 _assert(close(fds[0]) != -1);
2474 input_ = fdopen(fds[1], "a");
2476 _assert(pipe(fds) != -1);
2477 _assert(dup2(fds[1], 1) != -1);
2478 _assert(close(fds[1]) != -1);
2481 detachNewThreadSelector:@selector(_readOutput:)
2483 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2488 - (pkgCacheFile &) cache {
2492 - (pkgDepCache::Policy *) policy {
2496 - (pkgRecords *) records {
2500 - (pkgProblemResolver *) resolver {
2504 - (pkgAcquire &) fetcher {
2508 - (pkgSourceList &) list {
2512 - (NSArray *) packages {
2516 - (NSArray *) sources {
2517 return [sources_ allValues];
2520 - (NSArray *) issues {
2521 if (cache_->BrokenCount() == 0)
2524 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2526 for (Package *package in packages_) {
2527 if (![package broken])
2529 pkgCache::PkgIterator pkg([package iterator]);
2531 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2532 [entry addObject:[package name]];
2533 [issues addObject:entry];
2535 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2539 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2540 pkgCache::DepIterator start;
2541 pkgCache::DepIterator end;
2542 dep.GlobOr(start, end); // ++dep
2544 if (!cache_->IsImportantDep(end))
2546 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2549 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2550 [entry addObject:failure];
2551 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2553 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2554 [failure addObject:[package name]];
2556 pkgCache::PkgIterator target(start.TargetPkg());
2557 if (target->ProvidesList != 0)
2558 [failure addObject:@"?"];
2560 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2562 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2563 else if (!cache_[target].CandidateVerIter(cache_).end())
2564 [failure addObject:@"-"];
2565 else if (target->ProvidesList == 0)
2566 [failure addObject:@"!"];
2568 [failure addObject:@"%"];
2572 if (start.TargetVer() != 0)
2573 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2584 - (void) reloadData { _pooled
2585 @synchronized (self) {
2608 if (!cache_.Open(progress_, true)) {
2610 if (!_error->PopMessage(error))
2613 lprintf("cache_.Open():[%s]\n", error.c_str());
2615 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2616 [delegate_ repairWithSelector:@selector(configure)];
2617 else if (error == "The package lists or status file could not be parsed or opened.")
2618 [delegate_ repairWithSelector:@selector(update)];
2619 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2620 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2621 // else if (error == "The list of sources could not be read.")
2622 else _assert(false);
2628 now_ = [[NSDate date] retain];
2630 policy_ = new pkgDepCache::Policy();
2631 records_ = new pkgRecords(cache_);
2632 resolver_ = new pkgProblemResolver(cache_);
2633 fetcher_ = new pkgAcquire(&status_);
2636 list_ = new pkgSourceList();
2637 _assert(list_->ReadMainList());
2639 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2640 _assert(pkgApplyStatus(cache_));
2642 if (cache_->BrokenCount() != 0) {
2643 _assert(pkgFixBroken(cache_));
2644 _assert(cache_->BrokenCount() == 0);
2645 _assert(pkgMinimizeUpgrade(cache_));
2648 [sources_ removeAllObjects];
2649 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2650 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2651 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2653 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2654 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2658 [packages_ removeAllObjects];
2660 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2661 if (Package *package = [Package packageWithIterator:iterator database:self])
2662 [packages_ addObject:package];
2664 [packages_ sortUsingSelector:@selector(compareByName:)];
2667 _config->Set("Acquire::http::Timeout", 15);
2668 _config->Set("Acquire::http::MaxParallel", 4);
2671 - (void) configure {
2672 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2673 system([dpkg UTF8String]);
2681 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2682 _assert(!_error->PendingError());
2685 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2688 public pkgArchiveCleaner
2691 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2696 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2698 while (_error->PopMessage(error))
2699 lprintf("ArchiveCleaner: %s\n", error.c_str());
2704 pkgRecords records(cache_);
2706 lock_ = new FileFd();
2707 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2708 _assert(!_error->PendingError());
2711 // XXX: explain this with an error message
2712 _assert(list.ReadMainList());
2714 manager_ = (_system->CreatePM(cache_));
2715 _assert(manager_->GetArchives(fetcher_, &list, &records));
2716 _assert(!_error->PendingError());
2720 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2722 _assert(list.ReadMainList());
2723 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2724 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2727 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2732 bool failed = false;
2733 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2734 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2737 std::string uri = (*item)->DescURI();
2738 std::string error = (*item)->ErrorText;
2740 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2743 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2744 withObject:[NSArray arrayWithObjects:
2745 [NSString stringWithUTF8String:error.c_str()],
2757 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2759 if (_error->PendingError()) {
2764 if (result == pkgPackageManager::Failed) {
2769 if (result != pkgPackageManager::Completed) {
2774 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2776 _assert(list.ReadMainList());
2777 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2778 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2781 if (![before isEqualToArray:after])
2786 _assert(pkgDistUpgrade(cache_));
2790 [self updateWithStatus:status_];
2793 - (void) updateWithStatus:(Status &)status {
2795 _assert(list.ReadMainList());
2798 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2799 _assert(!_error->PendingError());
2801 pkgAcquire fetcher(&status);
2802 _assert(list.GetIndexes(&fetcher));
2804 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2805 bool failed = false;
2806 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2807 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2808 (*item)->Finished();
2812 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2813 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2814 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2817 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2822 - (void) setDelegate:(id)delegate {
2823 delegate_ = delegate;
2824 status_.setDelegate(delegate);
2825 progress_.setDelegate(delegate);
2828 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2829 pkgIndexFile *index(NULL);
2830 list_->FindIndex(file, index);
2831 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2837 /* PopUp Windows {{{ */
2838 @interface PopUpView : UIView {
2839 _transient id delegate_;
2840 UITransitionView *transition_;
2845 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2849 @implementation PopUpView
2852 [transition_ setDelegate:nil];
2853 [transition_ release];
2859 [transition_ transition:UITransitionPushFromTop toView:nil];
2862 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2863 if (from != nil && to == nil)
2864 [self removeFromSuperview];
2867 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2868 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2869 delegate_ = delegate;
2871 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2872 [self addSubview:transition_];
2874 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2876 [view addSubview:self];
2878 [transition_ setDelegate:self];
2880 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2881 [transition_ transition:UITransitionNone toView:blank];
2882 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2889 /* Mail Composition {{{ */
2890 @interface MailToView : PopUpView {
2891 MailComposeController *controller_;
2894 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2898 @implementation MailToView
2901 [controller_ release];
2905 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2909 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2910 NSLog(@"did:%@", delivery);
2911 // [UIApp setStatusBarShowsProgress:NO];
2912 if ([controller error]){
2913 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2914 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2915 [mailAlertSheet setBodyText:[controller error]];
2916 [mailAlertSheet popupAlertAnimated:YES];
2920 - (void) showError {
2921 NSLog(@"%@", [controller_ error]);
2922 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2923 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2924 [mailAlertSheet setBodyText:[controller_ error]];
2925 [mailAlertSheet popupAlertAnimated:YES];
2928 - (void) deliverMessage { _pooled
2932 if (![controller_ deliverMessage])
2933 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2936 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2937 if ([controller_ needsDelivery])
2938 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2943 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2944 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2945 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2946 [controller_ setDelegate:self];
2947 [controller_ initializeUI];
2948 [controller_ setupForURL:url];
2950 UIView *view([controller_ view]);
2951 [overlay_ addSubview:view];
2957 /* Confirmation View {{{ */
2958 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2959 if (!iterator.end())
2960 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2961 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2963 pkgCache::PkgIterator package(dep.TargetPkg());
2966 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2973 @protocol ConfirmationViewDelegate
2978 @interface ConfirmationView : BrowserView {
2979 _transient Database *database_;
2980 UIActionSheet *essential_;
2987 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2991 @implementation ConfirmationView
2998 if (essential_ != nil)
2999 [essential_ release];
3005 [book_ popFromSuperviewAnimated:YES];
3008 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3009 NSString *context([sheet context]);
3011 if ([context isEqualToString:@"remove"]) {
3019 [delegate_ confirm];
3026 } else if ([context isEqualToString:@"unable"]) {
3030 [super alertSheet:sheet buttonClicked:button];
3033 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3034 [window setValue:changes_ forKey:@"changes"];
3035 [window setValue:issues_ forKey:@"issues"];
3036 [window setValue:sizes_ forKey:@"sizes"];
3037 [super webView:sender didClearWindowObject:window forFrame:frame];
3040 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3041 if ((self = [super initWithBook:book]) != nil) {
3042 database_ = database;
3044 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
3045 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
3046 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
3047 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
3048 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
3052 pkgDepCache::Policy *policy([database_ policy]);
3054 pkgCacheFile &cache([database_ cache]);
3055 NSArray *packages = [database_ packages];
3056 for (Package *package in packages) {
3057 pkgCache::PkgIterator iterator = [package iterator];
3058 pkgDepCache::StateCache &state(cache[iterator]);
3060 NSString *name([package name]);
3062 if (state.NewInstall())
3063 [installing addObject:name];
3064 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
3065 [reinstalling addObject:name];
3066 else if (state.Upgrade())
3067 [upgrading addObject:name];
3068 else if (state.Downgrade())
3069 [downgrading addObject:name];
3070 else if (state.Delete()) {
3071 if ([package essential])
3073 [removing addObject:name];
3076 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3077 substrate_ |= DepSubstrate(iterator.CurrentVer());
3082 else if (Advanced_ || true) {
3083 essential_ = [[UIActionSheet alloc]
3084 initWithTitle:@"Removing Essentials"
3085 buttons:[NSArray arrayWithObjects:
3086 @"Cancel Operation (Safe)",
3087 @"Force Removal (Unsafe)",
3089 defaultButtonIndex:0
3095 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3097 [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."];
3099 essential_ = [[UIActionSheet alloc]
3100 initWithTitle:@"Unable to Comply"
3101 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3102 defaultButtonIndex:0
3107 [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."];
3110 changes_ = [[NSArray alloc] initWithObjects:
3118 issues_ = [database_ issues];
3120 issues_ = [issues_ retain];
3122 sizes_ = [[NSArray alloc] initWithObjects:
3123 SizeString([database_ fetcher].FetchNeeded()),
3124 SizeString([database_ fetcher].PartialPresent()),
3125 SizeString([database_ cache]->UsrSize()),
3128 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3132 - (NSString *) backButtonTitle {
3136 - (NSString *) leftButtonTitle {
3140 - (id) rightButtonTitle {
3141 return issues_ != nil ? nil : [super rightButtonTitle];
3144 - (id) _rightButtonTitle {
3145 #if AlwaysReload || IgnoreInstall
3146 return [super _rightButtonTitle];
3152 - (void) _leftButtonClicked {
3157 - (void) _rightButtonClicked {
3159 return [super _rightButtonClicked];
3161 if (essential_ != nil)
3162 [essential_ popupAlertAnimated:YES];
3166 [delegate_ confirm];
3174 /* Progress Data {{{ */
3175 @interface ProgressData : NSObject {
3181 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3188 @implementation ProgressData
3190 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3191 if ((self = [super init]) != nil) {
3192 selector_ = selector;
3212 /* Progress View {{{ */
3213 @interface ProgressView : UIView <
3214 ConfigurationDelegate,
3217 _transient Database *database_;
3219 UIView *background_;
3220 UITransitionView *transition_;
3222 UINavigationBar *navbar_;
3223 UIProgressBar *progress_;
3224 UITextView *output_;
3225 UITextLabel *status_;
3226 UIPushButton *close_;
3229 SHA1SumValue springlist_;
3230 SHA1SumValue notifyconf_;
3231 SHA1SumValue sandplate_;
3234 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3236 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3237 - (void) setContentView:(UIView *)view;
3240 - (void) _retachThread;
3241 - (void) _detachNewThreadData:(ProgressData *)data;
3242 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3248 @protocol ProgressViewDelegate
3249 - (void) progressViewIsComplete:(ProgressView *)sender;
3252 @implementation ProgressView
3255 [transition_ setDelegate:nil];
3256 [navbar_ setDelegate:nil];
3259 if (background_ != nil)
3260 [background_ release];
3261 [transition_ release];
3264 [progress_ release];
3271 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3272 if (bootstrap_ && from == overlay_ && to == view_)
3276 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3277 if ((self = [super initWithFrame:frame]) != nil) {
3278 database_ = database;
3279 delegate_ = delegate;
3281 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3282 [transition_ setDelegate:self];
3284 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3287 [overlay_ setBackgroundColor:[UIColor blackColor]];
3289 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3290 [background_ setBackgroundColor:[UIColor blackColor]];
3291 [self addSubview:background_];
3294 [self addSubview:transition_];
3296 CGSize navsize = [UINavigationBar defaultSize];
3297 CGRect navrect = {{0, 0}, navsize};
3299 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3300 [overlay_ addSubview:navbar_];
3302 [navbar_ setBarStyle:1];
3303 [navbar_ setDelegate:self];
3305 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3306 [navbar_ pushNavigationItem:navitem];
3308 CGRect bounds = [overlay_ bounds];
3309 CGSize prgsize = [UIProgressBar defaultSize];
3312 (bounds.size.width - prgsize.width) / 2,
3313 bounds.size.height - prgsize.height - 20
3316 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3317 [progress_ setStyle:0];
3319 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3321 bounds.size.height - prgsize.height - 50,
3322 bounds.size.width - 20,
3326 [status_ setColor:[UIColor whiteColor]];
3327 [status_ setBackgroundColor:[UIColor clearColor]];
3329 [status_ setCentersHorizontally:YES];
3330 //[status_ setFont:font];
3333 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3335 navrect.size.height + 20,
3336 bounds.size.width - 20,
3337 bounds.size.height - navsize.height - 62 - navrect.size.height
3341 //[output_ setTextFont:@"Courier New"];
3342 [output_ setTextSize:12];
3344 [output_ setTextColor:[UIColor whiteColor]];
3345 [output_ setBackgroundColor:[UIColor clearColor]];
3347 [output_ setMarginTop:0];
3348 [output_ setAllowsRubberBanding:YES];
3349 [output_ setEditable:NO];
3351 [overlay_ addSubview:output_];
3353 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3355 bounds.size.height - prgsize.height - 50,
3356 bounds.size.width - 20,
3360 [close_ setAutosizesToFit:NO];
3361 [close_ setDrawsShadow:YES];
3362 [close_ setStretchBackground:YES];
3363 [close_ setEnabled:YES];
3365 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3366 [close_ setTitleFont:bold];
3368 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3369 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3370 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3374 - (void) setContentView:(UIView *)view {
3375 view_ = [view retain];
3378 - (void) resetView {
3379 [transition_ transition:6 toView:view_];
3382 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3383 NSString *context([sheet context]);
3385 if ([context isEqualToString:@"error"])
3387 else if ([context isEqualToString:@"conffile"]) {
3388 FILE *input = [database_ input];
3392 fprintf(input, "N\n");
3396 fprintf(input, "Y\n");
3407 - (void) closeButtonPushed {
3416 [delegate_ suspendWithAnimation:YES];
3420 system("launchctl stop com.apple.SpringBoard");
3424 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3433 - (void) _retachThread {
3434 UINavigationItem *item = [navbar_ topItem];
3435 [item setTitle:@"Complete"];
3437 [overlay_ addSubview:close_];
3438 [progress_ removeFromSuperview];
3439 [status_ removeFromSuperview];
3441 [delegate_ progressViewIsComplete:self];
3444 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3445 MMap mmap(file, MMap::ReadOnly);
3447 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3448 if (!(sandplate_ == sha1.Result()))
3453 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3454 MMap mmap(file, MMap::ReadOnly);
3456 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3457 if (!(notifyconf_ == sha1.Result()))
3462 FileFd file(SpringBoard_, FileFd::ReadOnly);
3463 MMap mmap(file, MMap::ReadOnly);
3465 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3466 if (!(springlist_ == sha1.Result()))
3471 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3472 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3473 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3474 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3475 case 4: [close_ setTitle:@"Reboot Device"]; break;
3478 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3480 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3481 [cache autorelease];
3483 NSFileManager *manager = [NSFileManager defaultManager];
3484 NSError *error = nil;
3486 id system = [cache objectForKey:@"System"];
3491 if (stat(Cache_, &info) == -1)
3494 [system removeAllObjects];
3496 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3497 for (NSString *app in apps)
3498 if ([app hasSuffix:@".app"]) {
3499 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3500 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3501 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3503 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3504 [info setObject:path forKey:@"Path"];
3505 [info setObject:@"System" forKey:@"ApplicationType"];
3506 [system addInfoDictionary:info];
3512 [cache writeToFile:@Cache_ atomically:YES];
3514 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3516 if (chmod(Cache_, info.st_mode) == -1)
3520 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3523 notify_post("com.apple.mobile.application_installed");
3525 [delegate_ setStatusBarShowsProgress:NO];
3528 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3529 [[data target] performSelector:[data selector] withObject:[data object]];
3532 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3535 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3536 UINavigationItem *item = [navbar_ topItem];
3537 [item setTitle:title];
3539 [status_ setText:nil];
3540 [output_ setText:@""];
3541 [progress_ setProgress:0];
3543 [close_ removeFromSuperview];
3544 [overlay_ addSubview:progress_];
3545 [overlay_ addSubview:status_];
3547 [delegate_ setStatusBarShowsProgress:YES];
3551 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3552 MMap mmap(file, MMap::ReadOnly);
3554 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3555 sandplate_ = sha1.Result();
3559 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3560 MMap mmap(file, MMap::ReadOnly);
3562 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3563 notifyconf_ = sha1.Result();
3567 FileFd file(SpringBoard_, FileFd::ReadOnly);
3568 MMap mmap(file, MMap::ReadOnly);
3570 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3571 springlist_ = sha1.Result();
3574 [transition_ transition:6 toView:overlay_];
3577 detachNewThreadSelector:@selector(_detachNewThreadData:)
3579 withObject:[[ProgressData alloc]
3580 initWithSelector:selector
3587 - (void) repairWithSelector:(SEL)selector {
3589 detachNewThreadSelector:selector
3596 - (void) setConfigurationData:(NSString *)data {
3598 performSelectorOnMainThread:@selector(_setConfigurationData:)
3604 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3605 Package *package = id == nil ? nil : [database_ packageWithName:id];
3607 UIActionSheet *sheet = [[[UIActionSheet alloc]
3608 initWithTitle:(package == nil ? id : [package name])
3609 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3610 defaultButtonIndex:0
3615 [sheet setBodyText:error];
3616 [sheet popupAlertAnimated:YES];
3619 - (void) setProgressTitle:(NSString *)title {
3621 performSelectorOnMainThread:@selector(_setProgressTitle:)
3627 - (void) setProgressPercent:(float)percent {
3629 performSelectorOnMainThread:@selector(_setProgressPercent:)
3630 withObject:[NSNumber numberWithFloat:percent]
3635 - (void) startProgress {
3638 - (void) addProgressOutput:(NSString *)output {
3640 performSelectorOnMainThread:@selector(_addProgressOutput:)
3646 - (bool) isCancelling:(size_t)received {
3650 - (void) _setConfigurationData:(NSString *)data {
3651 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3653 _assert(conffile_r(data));
3655 NSString *ofile = conffile_r[1];
3656 //NSString *nfile = conffile_r[2];
3658 UIActionSheet *sheet = [[[UIActionSheet alloc]
3659 initWithTitle:@"Configuration Upgrade"
3660 buttons:[NSArray arrayWithObjects:
3661 @"Keep My Old Copy",
3662 @"Accept The New Copy",
3663 // XXX: @"See What Changed",
3665 defaultButtonIndex:0
3670 [sheet setBodyText:[NSString stringWithFormat:
3671 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3674 [sheet popupAlertAnimated:YES];
3677 - (void) _setProgressTitle:(NSString *)title {
3678 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3679 for (size_t i(0), e([words count]); i != e; ++i) {
3680 NSString *word([words objectAtIndex:i]);
3681 if (Package *package = [database_ packageWithName:word])
3682 [words replaceObjectAtIndex:i withObject:[package name]];
3685 [status_ setText:[words componentsJoinedByString:@" "]];
3688 - (void) _setProgressPercent:(NSNumber *)percent {
3689 [progress_ setProgress:[percent floatValue]];
3692 - (void) _addProgressOutput:(NSString *)output {
3693 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3694 CGSize size = [output_ contentSize];
3695 CGRect rect = {{0, size.height}, {size.width, 0}};
3696 [output_ scrollRectToVisible:rect animated:YES];
3699 - (BOOL) isRunning {
3706 /* Package Cell {{{ */
3707 @interface PackageCell : UISimpleTableCell {
3710 NSString *description_;
3714 UITextLabel *status_;
3718 - (PackageCell *) init;
3719 - (void) setPackage:(Package *)package;
3721 + (int) heightForPackage:(Package *)package;
3725 @implementation PackageCell
3727 - (void) clearPackage {
3738 if (description_ != nil) {
3739 [description_ release];
3743 if (source_ != nil) {
3748 if (badge_ != nil) {
3755 [self clearPackage];
3762 - (PackageCell *) init {
3763 if ((self = [super init]) != nil) {
3765 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3766 [status_ setBackgroundColor:[UIColor clearColor]];
3767 [status_ setFont:small];
3772 - (void) setPackage:(Package *)package {
3773 [self clearPackage];
3775 Source *source = [package source];
3776 NSString *section = [package simpleSection];
3778 icon_ = [[package icon] retain];
3780 name_ = [[package name] retain];
3781 description_ = [[package tagline] retain];
3783 NSString *label = nil;
3784 bool trusted = false;
3786 if (source != nil) {
3787 label = [source label];
3788 trusted = [source trusted];
3789 } else if ([[package id] isEqualToString:@"firmware"])
3792 label = @"Unknown/Local";
3794 NSString *from = [NSString stringWithFormat:@"from %@", label];
3796 if (section != nil && ![section isEqualToString:label])
3797 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3799 source_ = [from retain];
3801 if (NSString *purpose = [package primaryPurpose])
3802 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3803 badge_ = [badge_ retain];
3806 if (NSString *mode = [package mode]) {
3807 [badge_ setImage:[UIImage applicationImageNamed:
3808 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3811 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3812 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3813 } else if ([package half]) {
3814 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3815 [status_ setText:@"Package Damaged"];
3816 [status_ setColor:[UIColor redColor]];
3818 [badge_ setImage:nil];
3819 [status_ setText:nil];
3824 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3827 rect.size = [icon_ size];
3829 rect.size.width /= 2;
3830 rect.size.height /= 2;
3832 rect.origin.x = 25 - rect.size.width / 2;
3833 rect.origin.y = 25 - rect.size.height / 2;
3835 [icon_ drawInRect:rect];
3838 if (badge_ != nil) {
3839 CGSize size = [badge_ size];
3841 [badge_ drawAtPoint:CGPointMake(
3842 36 - size.width / 2,
3843 36 - size.height / 2
3852 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3853 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3857 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3859 [super drawContentInRect:rect selected:selected];
3862 + (int) heightForPackage:(Package *)package {
3863 NSString *tagline([package tagline]);
3864 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3866 if ([package hasMode] || [package half])
3875 /* Section Cell {{{ */
3876 @interface SectionCell : UISimpleTableCell {
3881 _UISwitchSlider *switch_;
3886 - (void) setSection:(Section *)section editing:(BOOL)editing;
3890 @implementation SectionCell
3892 - (void) clearSection {
3893 if (section_ != nil) {
3903 if (count_ != nil) {
3910 [self clearSection];
3917 if ((self = [super init]) != nil) {
3918 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3920 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3921 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3925 - (void) onSwitch:(id)sender {
3926 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3927 if (metadata == nil) {
3928 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3929 [Sections_ setObject:metadata forKey:section_];
3933 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3936 - (void) setSection:(Section *)section editing:(BOOL)editing {
3937 if (editing != editing_) {
3939 [switch_ removeFromSuperview];
3941 [self addSubview:switch_];
3945 [self clearSection];
3947 if (section == nil) {
3948 name_ = [@"All Packages" retain];
3951 section_ = [section name];
3952 if (section_ != nil)
3953 section_ = [section_ retain];
3954 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3955 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3958 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3962 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3963 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3970 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3972 CGSize size = [count_ sizeWithFont:Font14_];
3976 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3978 [super drawContentInRect:rect selected:selected];
3984 /* File Table {{{ */
3985 @interface FileTable : RVPage {
3986 _transient Database *database_;
3989 NSMutableArray *files_;
3993 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3994 - (void) setPackage:(Package *)package;
3998 @implementation FileTable
4001 if (package_ != nil)
4010 - (int) numberOfRowsInTable:(UITable *)table {
4011 return files_ == nil ? 0 : [files_ count];
4014 - (float) table:(UITable *)table heightForRow:(int)row {
4018 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4019 if (reusing == nil) {
4020 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
4021 UIFont *font = [UIFont systemFontOfSize:16];
4022 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
4024 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
4028 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4032 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4033 if ((self = [super initWithBook:book]) != nil) {
4034 database_ = database;
4036 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
4038 list_ = [[UITable alloc] initWithFrame:[self bounds]];
4039 [self addSubview:list_];
4041 UITableColumn *column = [[[UITableColumn alloc]
4042 initWithTitle:@"Name"
4044 width:[self frame].size.width
4047 [list_ setDataSource:self];
4048 [list_ setSeparatorStyle:1];
4049 [list_ addTableColumn:column];
4050 [list_ setDelegate:self];
4051 [list_ setReusesTableCells:YES];
4055 - (void) setPackage:(Package *)package {
4056 if (package_ != nil) {
4057 [package_ autorelease];
4066 [files_ removeAllObjects];
4068 if (package != nil) {
4069 package_ = [package retain];
4070 name_ = [[package id] retain];
4072 if (NSArray *files = [package files])
4073 [files_ addObjectsFromArray:files];
4075 if ([files_ count] != 0) {
4076 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4077 [files_ removeObjectAtIndex:0];
4078 [files_ sortUsingSelector:@selector(compareByPath:)];
4080 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4081 [stack addObject:@"/"];
4083 for (int i(0), e([files_ count]); i != e; ++i) {
4084 NSString *file = [files_ objectAtIndex:i];
4085 while (![file hasPrefix:[stack lastObject]])
4086 [stack removeLastObject];
4087 NSString *directory = [stack lastObject];
4088 [stack addObject:[file stringByAppendingString:@"/"]];
4089 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4090 ([stack count] - 2) * 3, "",
4091 [file substringFromIndex:[directory length]]
4100 - (void) resetViewAnimated:(BOOL)animated {
4101 [list_ resetViewAnimated:animated];
4104 - (void) reloadData {
4105 [self setPackage:[database_ packageWithName:name_]];
4106 [self reloadButtons];
4109 - (NSString *) title {
4110 return @"Installed Files";
4113 - (NSString *) backButtonTitle {
4119 /* Package View {{{ */
4120 @interface PackageView : BrowserView {
4121 _transient Database *database_;
4124 NSMutableArray *buttons_;
4127 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4128 - (void) setPackage:(Package *)package;
4132 @implementation PackageView
4135 if (package_ != nil)
4143 - (void) _clickButtonWithName:(NSString *)name {
4144 if ([name isEqualToString:@"Install"])
4145 [delegate_ installPackage:package_];
4146 else if ([name isEqualToString:@"Reinstall"])
4147 [delegate_ installPackage:package_];
4148 else if ([name isEqualToString:@"Remove"])
4149 [delegate_ removePackage:package_];
4150 else if ([name isEqualToString:@"Upgrade"])
4151 [delegate_ installPackage:package_];
4152 else _assert(false);
4155 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4156 NSString *context([sheet context]);
4158 if ([context isEqualToString:@"modify"]) {
4159 int count = [buttons_ count];
4160 _assert(count != 0);
4161 _assert(button <= count + 1);
4163 if (count != button - 1)
4164 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4168 [super alertSheet:sheet buttonClicked:button];
4171 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4172 return [super webView:sender didFinishLoadForFrame:frame];
4175 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4176 [window setValue:package_ forKey:@"package"];
4177 [super webView:sender didClearWindowObject:window forFrame:frame];
4180 - (bool) _allowJavaScriptPanel {
4185 - (void) _rightButtonClicked {
4186 /*[super _rightButtonClicked];
4189 int count = [buttons_ count];
4190 _assert(count != 0);
4193 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4195 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4196 [buttons addObjectsFromArray:buttons_];
4197 [buttons addObject:@"Cancel"];
4199 [delegate_ slideUp:[[[UIActionSheet alloc]
4202 defaultButtonIndex:2
4210 - (id) _rightButtonTitle {
4211 int count = [buttons_ count];
4212 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4215 - (NSString *) backButtonTitle {
4219 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4220 if ((self = [super initWithBook:book]) != nil) {
4221 database_ = database;
4222 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4226 - (void) setPackage:(Package *)package {
4227 if (package_ != nil) {
4228 [package_ autorelease];
4237 [buttons_ removeAllObjects];
4239 if (package != nil) {
4240 package_ = [package retain];
4241 name_ = [[package id] retain];
4243 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4245 if ([package_ source] == nil);
4246 else if ([package_ upgradableAndEssential:NO])
4247 [buttons_ addObject:@"Upgrade"];
4248 else if ([package_ installed] == nil)
4249 [buttons_ addObject:@"Install"];
4251 [buttons_ addObject:@"Reinstall"];
4252 if ([package_ installed] != nil)
4253 [buttons_ addObject:@"Remove"];
4261 - (void) reloadData {
4262 [self setPackage:[database_ packageWithName:name_]];
4263 [self reloadButtons];
4268 /* Package Table {{{ */
4269 @interface PackageTable : RVPage {
4270 _transient Database *database_;
4272 NSMutableArray *packages_;
4273 NSMutableArray *sections_;
4274 UISectionList *list_;
4277 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4279 - (void) setDelegate:(id)delegate;
4281 - (void) reloadData;
4282 - (void) resetCursor;
4284 - (UISectionList *) list;
4286 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4290 @implementation PackageTable
4293 [list_ setDataSource:nil];
4296 [packages_ release];
4297 [sections_ release];
4302 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4303 return [sections_ count];
4306 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4307 return [[sections_ objectAtIndex:section] name];
4310 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4311 return [[sections_ objectAtIndex:section] row];
4314 - (int) numberOfRowsInTable:(UITable *)table {
4315 return [packages_ count];
4318 - (float) table:(UITable *)table heightForRow:(int)row {
4319 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4322 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4324 reusing = [[[PackageCell alloc] init] autorelease];
4325 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4329 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4333 - (void) tableRowSelected:(NSNotification *)notification {
4334 int row = [[notification object] selectedRow];
4338 Package *package = [packages_ objectAtIndex:row];
4339 package = [database_ packageWithName:[package id]];
4340 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4341 [view setPackage:package];
4342 [view setDelegate:delegate_];
4343 [book_ pushPage:view];
4346 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4347 if ((self = [super initWithBook:book]) != nil) {
4348 database_ = database;
4349 title_ = [title retain];
4351 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4352 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4354 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4355 [list_ setDataSource:self];
4357 UITableColumn *column = [[[UITableColumn alloc]
4358 initWithTitle:@"Name"
4360 width:[self frame].size.width
4363 UITable *table = [list_ table];
4364 [table setSeparatorStyle:1];
4365 [table addTableColumn:column];
4366 [table setDelegate:self];
4367 [table setReusesTableCells:YES];
4369 [self addSubview:list_];
4371 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4372 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4376 - (void) setDelegate:(id)delegate {
4377 delegate_ = delegate;
4380 - (bool) hasPackage:(Package *)package {
4384 - (void) reloadData {
4385 NSArray *packages = [database_ packages];
4387 [packages_ removeAllObjects];
4388 [sections_ removeAllObjects];
4390 _profile(PackageTable$reloadData$Filter)
4391 for (Package *package in packages)
4392 if ([self hasPackage:package])
4393 [packages_ addObject:package];
4396 Section *section = nil;
4398 _profile(PackageTable$reloadData$Section)
4399 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4403 _profile(PackageTable$reloadData$Section$Package)
4404 package = [packages_ objectAtIndex:offset];
4405 index = [package index];
4408 if (section == nil || [section index] != index) {
4409 _profile(PackageTable$reloadData$Section$Allocate)
4410 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4413 _profile(PackageTable$reloadData$Section$Add)
4414 [sections_ addObject:section];
4418 [section addToCount];
4422 _profile(PackageTable$reloadData$List)
4427 - (NSString *) title {
4431 - (void) resetViewAnimated:(BOOL)animated {
4432 [list_ resetViewAnimated:animated];
4435 - (void) resetCursor {
4436 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4439 - (UISectionList *) list {
4443 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4444 [list_ setShouldHideHeaderInShortLists:hide];
4449 /* Filtered Package Table {{{ */
4450 @interface FilteredPackageTable : PackageTable {
4456 - (void) setObject:(id)object;
4458 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4462 @implementation FilteredPackageTable
4470 - (void) setObject:(id)object {
4476 object_ = [object retain];
4479 - (bool) hasPackage:(Package *)package {
4480 _profile(FilteredPackageTable$hasPackage)
4481 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
4485 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4486 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4488 object_ = object == nil ? nil : [object retain];
4490 /* XXX: this is an unsafe optimization of doomy hell */
4491 Method method = class_getInstanceMethod([Package class], filter);
4492 imp_ = method_getImplementation(method);
4493 _assert(imp_ != NULL);
4502 /* Add Source View {{{ */
4503 @interface AddSourceView : RVPage {
4504 _transient Database *database_;
4507 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4511 @implementation AddSourceView
4513 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4514 if ((self = [super initWithBook:book]) != nil) {
4515 database_ = database;
4521 /* Source Cell {{{ */
4522 @interface SourceCell : UITableCell {
4525 NSString *description_;
4531 - (SourceCell *) initWithSource:(Source *)source;
4535 @implementation SourceCell
4540 [description_ release];
4545 - (SourceCell *) initWithSource:(Source *)source {
4546 if ((self = [super init]) != nil) {
4548 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4550 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4551 icon_ = [icon_ retain];
4553 origin_ = [[source name] retain];
4554 label_ = [[source uri] retain];
4555 description_ = [[source description] retain];
4559 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4561 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4568 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4572 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4576 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4578 [super drawContentInRect:rect selected:selected];
4583 /* Source Table {{{ */
4584 @interface SourceTable : RVPage {
4585 _transient Database *database_;
4586 UISectionList *list_;
4587 NSMutableArray *sources_;
4588 UIActionSheet *alert_;
4592 UIProgressHUD *hud_;
4595 //NSURLConnection *installer_;
4596 NSURLConnection *trivial_bz2_;
4597 NSURLConnection *trivial_gz_;
4598 //NSURLConnection *automatic_;
4603 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4607 @implementation SourceTable
4609 - (void) _deallocConnection:(NSURLConnection *)connection {
4610 if (connection != nil) {
4611 [connection cancel];
4612 //[connection setDelegate:nil];
4613 [connection release];
4618 [[list_ table] setDelegate:nil];
4619 [list_ setDataSource:nil];
4628 //[self _deallocConnection:installer_];
4629 [self _deallocConnection:trivial_gz_];
4630 [self _deallocConnection:trivial_bz2_];
4631 //[self _deallocConnection:automatic_];
4638 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4639 return offset_ == 0 ? 1 : 2;
4642 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4643 switch (section + (offset_ == 0 ? 1 : 0)) {
4644 case 0: return @"Entered by User";
4645 case 1: return @"Installed by Packages";
4653 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4654 switch (section + (offset_ == 0 ? 1 : 0)) {
4656 case 1: return offset_;
4664 - (int) numberOfRowsInTable:(UITable *)table {
4665 return [sources_ count];
4668 - (float) table:(UITable *)table heightForRow:(int)row {
4669 Source *source = [sources_ objectAtIndex:row];
4670 return [source description] == nil ? 56 : 73;
4673 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4674 Source *source = [sources_ objectAtIndex:row];
4675 // XXX: weird warning, stupid selectors ;P
4676 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4679 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4683 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4687 - (void) tableRowSelected:(NSNotification*)notification {
4688 UITable *table([list_ table]);
4689 int row([table selectedRow]);
4693 Source *source = [sources_ objectAtIndex:row];
4695 PackageTable *packages = [[[FilteredPackageTable alloc]
4698 title:[source label]
4699 filter:@selector(isVisibleInSource:)
4703 [packages setDelegate:delegate_];
4705 [book_ pushPage:packages];
4708 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4709 Source *source = [sources_ objectAtIndex:row];
4710 return [source record] != nil;
4713 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4714 [[list_ table] setDeleteConfirmationRow:row];
4717 - (void) table:(UITable *)table deleteRow:(int)row {
4718 Source *source = [sources_ objectAtIndex:row];
4719 [Sources_ removeObjectForKey:[source key]];
4720 [delegate_ syncData];
4724 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4727 @"./", @"Distribution",
4728 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4730 [delegate_ syncData];
4733 - (NSString *) getWarning {
4734 NSString *href(href_);
4735 NSRange colon([href rangeOfString:@"://"]);
4736 if (colon.location != NSNotFound)
4737 href = [href substringFromIndex:(colon.location + 3)];
4738 href = [href stringByAddingPercentEscapes];
4739 href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href];
4740 href = [href stringByCachingURLWithCurrentCDN];
4742 NSURL *url([NSURL URLWithString:href]);
4744 NSStringEncoding encoding;
4745 NSError *error(nil);
4747 if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error])
4748 return [warning length] == 0 ? nil : warning;
4752 - (void) _endConnection:(NSURLConnection *)connection {
4753 NSURLConnection **field = NULL;
4754 if (connection == trivial_bz2_)
4755 field = &trivial_bz2_;
4756 else if (connection == trivial_gz_)
4757 field = &trivial_gz_;
4758 _assert(field != NULL);
4759 [connection release];
4763 trivial_bz2_ == nil &&
4769 if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
4772 UIActionSheet *sheet = [[[UIActionSheet alloc]
4773 initWithTitle:@"Source Warning"
4774 buttons:[NSArray arrayWithObjects:@"Add Anyway", @"Cancel", nil]
4775 defaultButtonIndex:0
4780 [sheet setNumberOfRows:1];
4782 [sheet setBodyText:warning];
4783 [sheet popupAlertAnimated:YES];
4786 } else if (error_ != nil) {
4787 UIActionSheet *sheet = [[[UIActionSheet alloc]
4788 initWithTitle:@"Verification Error"
4789 buttons:[NSArray arrayWithObjects:@"OK", nil]
4790 defaultButtonIndex:0
4795 [sheet setBodyText:[error_ localizedDescription]];
4796 [sheet popupAlertAnimated:YES];
4798 UIActionSheet *sheet = [[[UIActionSheet alloc]
4799 initWithTitle:@"Did not Find Repository"
4800 buttons:[NSArray arrayWithObjects:@"OK", nil]
4801 defaultButtonIndex:0
4806 [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."];
4807 [sheet popupAlertAnimated:YES];
4810 [delegate_ setStatusBarShowsProgress:NO];
4811 [delegate_ removeProgressHUD:hud_];
4821 if (error_ != nil) {
4828 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4829 switch ([response statusCode]) {
4835 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4836 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4838 error_ = [error retain];
4839 [self _endConnection:connection];
4842 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4843 [self _endConnection:connection];
4846 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4847 NSMutableURLRequest *request = [NSMutableURLRequest
4848 requestWithURL:[NSURL URLWithString:href]
4849 cachePolicy:NSURLRequestUseProtocolCachePolicy
4850 timeoutInterval:20.0
4853 [request setHTTPMethod:method];
4855 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4858 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4859 NSString *context([sheet context]);
4861 if ([context isEqualToString:@"source"]) {
4864 NSString *href = [[sheet textField] text];
4866 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4868 if (![href hasSuffix:@"/"])
4869 href_ = [href stringByAppendingString:@"/"];
4872 href_ = [href_ retain];
4874 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4875 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4876 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4880 hud_ = [[delegate_ addProgressHUD] retain];
4881 [hud_ setText:@"Verifying URL"];
4892 } else if ([context isEqualToString:@"trivial"])
4894 else if ([context isEqualToString:@"urlerror"])
4896 else if ([context isEqualToString:@"warning"]) {
4916 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4917 if ((self = [super initWithBook:book]) != nil) {
4918 database_ = database;
4919 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4921 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4922 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4923 [list_ setShouldHideHeaderInShortLists:NO];
4925 [self addSubview:list_];
4926 [list_ setDataSource:self];
4928 UITableColumn *column = [[UITableColumn alloc]
4929 initWithTitle:@"Name"
4931 width:[self frame].size.width
4934 UITable *table = [list_ table];
4935 [table setSeparatorStyle:1];
4936 [table addTableColumn:column];
4937 [table setDelegate:self];
4941 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4942 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4946 - (void) reloadData {
4948 _assert(list.ReadMainList());
4950 [sources_ removeAllObjects];
4951 [sources_ addObjectsFromArray:[database_ sources]];
4953 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4956 int count = [sources_ count];
4957 for (offset_ = 0; offset_ != count; ++offset_) {
4958 Source *source = [sources_ objectAtIndex:offset_];
4959 if ([source record] == nil)
4966 - (void) resetViewAnimated:(BOOL)animated {
4967 [list_ resetViewAnimated:animated];
4970 - (void) _leftButtonClicked {
4971 /*[book_ pushPage:[[[AddSourceView alloc]
4976 UIActionSheet *sheet = [[[UIActionSheet alloc]
4977 initWithTitle:@"Enter Cydia/APT URL"
4978 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4979 defaultButtonIndex:0
4984 [sheet setNumberOfRows:1];
4986 [sheet addTextFieldWithValue:@"http://" label:@""];
4988 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4989 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4990 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4991 [traits setKeyboardType:UIKeyboardTypeURL];
4992 // XXX: UIReturnKeyDone
4993 [traits setReturnKeyType:UIReturnKeyNext];
4995 [sheet popupAlertAnimated:YES];
4998 - (void) _rightButtonClicked {
4999 UITable *table = [list_ table];
5000 BOOL editing = [table isRowDeletionEnabled];
5001 [table enableRowDeletion:!editing animated:YES];
5002 [book_ reloadButtonsForPage:self];
5005 - (NSString *) title {
5009 - (NSString *) leftButtonTitle {
5010 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
5013 - (id) rightButtonTitle {
5014 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
5017 - (UINavigationButtonStyle) rightButtonStyle {
5018 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5024 /* Installed View {{{ */
5025 @interface InstalledView : RVPage {
5026 _transient Database *database_;
5027 FilteredPackageTable *packages_;
5031 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5035 @implementation InstalledView
5038 [packages_ release];
5042 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5043 if ((self = [super initWithBook:book]) != nil) {
5044 database_ = database;
5046 packages_ = [[FilteredPackageTable alloc]
5050 filter:@selector(isInstalledAndVisible:)
5051 with:[NSNumber numberWithBool:YES]
5054 [self addSubview:packages_];
5056 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5057 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5061 - (void) resetViewAnimated:(BOOL)animated {
5062 [packages_ resetViewAnimated:animated];
5065 - (void) reloadData {
5066 [packages_ reloadData];
5069 - (void) _rightButtonClicked {
5070 [packages_ setObject:[NSNumber numberWithBool:expert_]];
5071 [packages_ reloadData];
5073 [book_ reloadButtonsForPage:self];
5076 - (NSString *) title {
5077 return @"Installed";
5080 - (NSString *) backButtonTitle {
5084 - (id) rightButtonTitle {
5085 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
5088 - (UINavigationButtonStyle) rightButtonStyle {
5089 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5092 - (void) setDelegate:(id)delegate {
5093 [super setDelegate:delegate];
5094 [packages_ setDelegate:delegate];
5101 @interface HomeView : BrowserView {
5106 @implementation HomeView
5108 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5109 NSString *context([sheet context]);
5111 if ([context isEqualToString:@"about"])
5114 [super alertSheet:sheet buttonClicked:button];
5117 - (void) _leftButtonClicked {
5118 UIActionSheet *sheet = [[[UIActionSheet alloc]
5119 initWithTitle:@"About Cydia Installer"
5120 buttons:[NSArray arrayWithObjects:@"Close", nil]
5121 defaultButtonIndex:0
5127 @"Copyright (C) 2008-2009\n"
5128 "Jay Freeman (saurik)\n"
5129 "saurik@saurik.com\n"
5130 "http://www.saurik.com/\n"
5133 "http://www.theokorigroup.com/\n"
5135 "College of Creative Studies,\n"
5136 "University of California,\n"
5138 "http://www.ccs.ucsb.edu/"
5141 [sheet popupAlertAnimated:YES];
5144 - (NSString *) leftButtonTitle {
5150 /* Manage View {{{ */
5151 @interface ManageView : BrowserView {
5156 @implementation ManageView
5158 - (NSString *) title {
5162 - (void) _leftButtonClicked {
5163 [delegate_ askForSettings];
5166 - (NSString *) leftButtonTitle {
5171 - (id) _rightButtonTitle {
5183 #include <BrowserView.m>
5185 /* Cydia Book {{{ */
5186 @interface CYBook : RVBook <
5189 _transient Database *database_;
5190 UINavigationBar *overlay_;
5191 UINavigationBar *underlay_;
5192 UIProgressIndicator *indicator_;
5193 UITextLabel *prompt_;
5194 UIProgressBar *progress_;
5195 UINavigationButton *cancel_;
5199 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5205 @implementation CYBook
5209 [indicator_ release];
5211 [progress_ release];
5216 - (NSString *) getTitleForPage:(RVPage *)page {
5217 return Simplify([super getTitleForPage:page]);
5225 [UIView beginAnimations:nil context:NULL];
5227 CGRect ovrframe = [overlay_ frame];
5228 ovrframe.origin.y = 0;
5229 [overlay_ setFrame:ovrframe];
5231 CGRect barframe = [navbar_ frame];
5232 barframe.origin.y += ovrframe.size.height;
5233 [navbar_ setFrame:barframe];
5235 CGRect trnframe = [transition_ frame];
5236 trnframe.origin.y += ovrframe.size.height;
5237 trnframe.size.height -= ovrframe.size.height;
5238 [transition_ setFrame:trnframe];
5240 [UIView endAnimations];
5242 [indicator_ startAnimation];
5243 [prompt_ setText:@"Updating Database"];
5244 [progress_ setProgress:0];
5247 [overlay_ addSubview:cancel_];
5250 detachNewThreadSelector:@selector(_update)
5259 [indicator_ stopAnimation];
5261 [UIView beginAnimations:nil context:NULL];
5263 CGRect ovrframe = [overlay_ frame];
5264 ovrframe.origin.y = -ovrframe.size.height;
5265 [overlay_ setFrame:ovrframe];
5267 CGRect barframe = [navbar_ frame];
5268 barframe.origin.y -= ovrframe.size.height;
5269 [navbar_ setFrame:barframe];
5271 CGRect trnframe = [transition_ frame];
5272 trnframe.origin.y -= ovrframe.size.height;
5273 trnframe.size.height += ovrframe.size.height;
5274 [transition_ setFrame:trnframe];
5276 [UIView commitAnimations];
5278 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5281 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5282 if ((self = [super initWithFrame:frame]) != nil) {
5283 database_ = database;
5285 CGRect ovrrect = [navbar_ bounds];
5286 ovrrect.size.height = [UINavigationBar defaultSize].height;
5287 ovrrect.origin.y = -ovrrect.size.height;
5289 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5290 [self addSubview:overlay_];
5292 ovrrect.origin.y = frame.size.height;
5293 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5294 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5295 [self addSubview:underlay_];
5297 [overlay_ setBarStyle:1];
5298 [underlay_ setBarStyle:1];
5300 int barstyle = [overlay_ _barStyle:NO];
5301 bool ugly = barstyle == 0;
5303 UIProgressIndicatorStyle style = ugly ?
5304 UIProgressIndicatorStyleMediumBrown :
5305 UIProgressIndicatorStyleMediumWhite;
5307 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5308 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5309 CGRect indrect = {{indoffset, indoffset}, indsize};
5311 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5312 [indicator_ setStyle:style];
5313 [overlay_ addSubview:indicator_];
5315 CGSize prmsize = {215, indsize.height + 4};
5318 indoffset * 2 + indsize.width,
5322 unsigned(ovrrect.size.height - prmsize.height) / 2
5325 UIFont *font = [UIFont systemFontOfSize:15];
5327 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5329 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5330 [prompt_ setBackgroundColor:[UIColor clearColor]];
5331 [prompt_ setFont:font];
5333 [overlay_ addSubview:prompt_];
5335 CGSize prgsize = {75, 100};
5338 ovrrect.size.width - prgsize.width - 10,
5339 (ovrrect.size.height - prgsize.height) / 2
5342 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5343 [progress_ setStyle:0];
5344 [overlay_ addSubview:progress_];
5346 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5347 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5349 CGRect frame = [cancel_ frame];
5350 frame.size.width = 65;
5351 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5352 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5353 [cancel_ setFrame:frame];
5355 [cancel_ setBarStyle:barstyle];
5359 - (void) _onCancel {
5361 [cancel_ removeFromSuperview];
5364 - (void) _update { _pooled
5366 status.setDelegate(self);
5368 [database_ updateWithStatus:status];
5371 performSelectorOnMainThread:@selector(_update_)
5377 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5378 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5381 - (void) setProgressTitle:(NSString *)title {
5383 performSelectorOnMainThread:@selector(_setProgressTitle:)
5389 - (void) setProgressPercent:(float)percent {
5391 performSelectorOnMainThread:@selector(_setProgressPercent:)
5392 withObject:[NSNumber numberWithFloat:percent]
5397 - (void) startProgress {
5400 - (void) addProgressOutput:(NSString *)output {
5402 performSelectorOnMainThread:@selector(_addProgressOutput:)
5408 - (bool) isCancelling:(size_t)received {
5412 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5416 - (void) _setProgressTitle:(NSString *)title {
5417 [prompt_ setText:title];
5420 - (void) _setProgressPercent:(NSNumber *)percent {
5421 [progress_ setProgress:[percent floatValue]];
5424 - (void) _addProgressOutput:(NSString *)output {
5429 /* Cydia:// Protocol {{{ */
5430 @interface CydiaURLProtocol : NSURLProtocol {
5435 @implementation CydiaURLProtocol
5437 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5438 NSURL *url([request URL]);
5441 NSString *scheme([[url scheme] lowercaseString]);
5442 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5447 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5451 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5452 id<NSURLProtocolClient> client([self client]);
5454 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5456 NSData *data(UIImagePNGRepresentation(icon));
5458 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5459 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5460 [client URLProtocol:self didLoadData:data];
5461 [client URLProtocolDidFinishLoading:self];
5465 - (void) startLoading {
5466 id<NSURLProtocolClient> client([self client]);
5467 NSURLRequest *request([self request]);
5469 NSURL *url([request URL]);
5470 NSString *href([url absoluteString]);
5472 NSString *path([href substringFromIndex:8]);
5473 NSRange slash([path rangeOfString:@"/"]);
5476 if (slash.location == NSNotFound) {
5480 command = [path substringToIndex:slash.location];
5481 path = [path substringFromIndex:(slash.location + 1)];
5484 Database *database([Database sharedInstance]);
5486 if ([command isEqualToString:@"package-icon"]) {
5489 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5490 Package *package([database packageWithName:path]);
5493 UIImage *icon([package icon]);
5494 [self _returnPNGWithImage:icon forRequest:request];
5495 } else if ([command isEqualToString:@"source-icon"]) {
5498 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5499 NSString *source(Simplify(path));
5500 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5502 icon = [UIImage applicationImageNamed:@"unknown.png"];
5503 [self _returnPNGWithImage:icon forRequest:request];
5504 } else if ([command isEqualToString:@"uikit-image"]) {
5507 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5508 UIImage *icon(_UIImageWithName(path));
5509 [self _returnPNGWithImage:icon forRequest:request];
5510 } else if ([command isEqualToString:@"section-icon"]) {
5513 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5514 NSString *section(Simplify(path));
5515 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5517 icon = [UIImage applicationImageNamed:@"unknown.png"];
5518 [self _returnPNGWithImage:icon forRequest:request];
5520 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5524 - (void) stopLoading {
5530 /* Sections View {{{ */
5531 @interface SectionsView : RVPage {
5532 _transient Database *database_;
5533 NSMutableArray *sections_;
5534 NSMutableArray *filtered_;
5535 UITransitionView *transition_;
5541 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5542 - (void) reloadData;
5547 @implementation SectionsView
5550 [list_ setDataSource:nil];
5551 [list_ setDelegate:nil];
5553 [sections_ release];
5554 [filtered_ release];
5555 [transition_ release];
5557 [accessory_ release];
5561 - (int) numberOfRowsInTable:(UITable *)table {
5562 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5565 - (float) table:(UITable *)table heightForRow:(int)row {
5569 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5571 reusing = [[[SectionCell alloc] init] autorelease];
5572 [(SectionCell *)reusing setSection:(editing_ ?
5573 [sections_ objectAtIndex:row] :
5574 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5575 ) editing:editing_];
5579 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5583 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5587 - (void) tableRowSelected:(NSNotification *)notification {
5588 int row = [[notification object] selectedRow];
5599 title = @"All Packages";
5601 section = [filtered_ objectAtIndex:(row - 1)];
5602 name = [section name];
5608 title = @"(No Section)";
5612 PackageTable *table = [[[FilteredPackageTable alloc]
5616 filter:@selector(isVisiblyUninstalledInSection:)
5620 [table setDelegate:delegate_];
5622 [book_ pushPage:table];
5625 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5626 if ((self = [super initWithBook:book]) != nil) {
5627 database_ = database;
5629 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5630 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5632 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5633 [self addSubview:transition_];
5635 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5636 [transition_ transition:0 toView:list_];
5638 UITableColumn *column = [[[UITableColumn alloc]
5639 initWithTitle:@"Name"
5641 width:[self frame].size.width
5644 [list_ setDataSource:self];
5645 [list_ setSeparatorStyle:1];
5646 [list_ addTableColumn:column];
5647 [list_ setDelegate:self];
5648 [list_ setReusesTableCells:YES];
5652 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5653 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5657 - (void) reloadData {
5658 NSArray *packages = [database_ packages];
5660 [sections_ removeAllObjects];
5661 [filtered_ removeAllObjects];
5663 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5664 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5667 for (Package *package in packages) {
5668 NSString *name([package section]);
5671 Section *section([sections objectForKey:name]);
5672 if (section == nil) {
5673 section = [[[Section alloc] initWithName:name] autorelease];
5674 [sections setObject:section forKey:name];
5678 if ([package valid] && [package installed] == nil && [package visible])
5679 [filtered addObject:package];
5683 [sections_ addObjectsFromArray:[sections allValues]];
5684 [sections_ sortUsingSelector:@selector(compareByName:)];
5687 [filtered sortUsingSelector:@selector(compareBySection:)];
5690 Section *section = nil;
5691 for (Package *package in filtered) {
5692 NSString *name = [package section];
5694 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5695 section = name == nil ?
5696 [[[Section alloc] initWithName:nil] autorelease] :
5697 [sections objectForKey:name];
5698 [filtered_ addObject:section];
5701 [section addToCount];
5709 - (void) resetView {
5711 [self _rightButtonClicked];
5714 - (void) resetViewAnimated:(BOOL)animated {
5715 [list_ resetViewAnimated:animated];
5718 - (void) _rightButtonClicked {
5719 if ((editing_ = !editing_))
5722 [delegate_ updateData];
5723 [book_ reloadTitleForPage:self];
5724 [book_ reloadButtonsForPage:self];
5727 - (NSString *) title {
5728 return editing_ ? @"Section Visibility" : @"Install by Section";
5731 - (NSString *) backButtonTitle {
5735 - (id) rightButtonTitle {
5736 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5739 - (UINavigationButtonStyle) rightButtonStyle {
5740 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5743 - (UIView *) accessoryView {
5749 /* Changes View {{{ */
5750 @interface ChangesView : RVPage {
5751 _transient Database *database_;
5752 NSMutableArray *packages_;
5753 NSMutableArray *sections_;
5754 UISectionList *list_;
5758 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5759 - (void) reloadData;
5763 @implementation ChangesView
5766 [[list_ table] setDelegate:nil];
5767 [list_ setDataSource:nil];
5769 [packages_ release];
5770 [sections_ release];
5775 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5776 return [sections_ count];
5779 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5780 return [[sections_ objectAtIndex:section] name];
5783 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5784 return [[sections_ objectAtIndex:section] row];
5787 - (int) numberOfRowsInTable:(UITable *)table {
5788 return [packages_ count];
5791 - (float) table:(UITable *)table heightForRow:(int)row {
5792 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5795 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5797 reusing = [[[PackageCell alloc] init] autorelease];
5798 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5802 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5806 - (void) tableRowSelected:(NSNotification *)notification {
5807 int row = [[notification object] selectedRow];
5810 Package *package = [packages_ objectAtIndex:row];
5811 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5812 [view setDelegate:delegate_];
5813 [view setPackage:package];
5814 [book_ pushPage:view];
5817 - (void) _leftButtonClicked {
5818 [(CYBook *)book_ update];
5819 [self reloadButtons];
5822 - (void) _rightButtonClicked {
5823 [delegate_ distUpgrade];
5826 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5827 if ((self = [super initWithBook:book]) != nil) {
5828 database_ = database;
5830 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5831 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5833 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5834 [self addSubview:list_];
5836 [list_ setShouldHideHeaderInShortLists:NO];
5837 [list_ setDataSource:self];
5838 //[list_ setSectionListStyle:1];
5840 UITableColumn *column = [[[UITableColumn alloc]
5841 initWithTitle:@"Name"
5843 width:[self frame].size.width
5846 UITable *table = [list_ table];
5847 [table setSeparatorStyle:1];
5848 [table addTableColumn:column];
5849 [table setDelegate:self];
5850 [table setReusesTableCells:YES];
5854 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5855 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5859 - (void) reloadData {
5860 NSArray *packages = [database_ packages];
5862 [packages_ removeAllObjects];
5863 [sections_ removeAllObjects];
5866 for (Package *package in packages)
5868 [package installed] == nil && [package valid] && [package visible] ||
5869 [package upgradableAndEssential:YES]
5871 [packages_ addObject:package];
5874 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5877 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5878 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5879 Section *section = nil;
5883 bool unseens = false;
5885 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5888 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5889 Package *package = [packages_ objectAtIndex:offset];
5891 if (![package upgradableAndEssential:YES]) {
5893 NSDate *seen = [package seen];
5895 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5898 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5899 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5900 [sections_ addObject:section];
5904 [section addToCount];
5905 } else if ([package ignored])
5906 [ignored addToCount];
5909 [upgradable addToCount];
5914 CFRelease(formatter);
5917 Section *last = [sections_ lastObject];
5918 size_t count = [last count];
5919 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5920 [sections_ removeLastObject];
5923 if ([ignored count] != 0)
5924 [sections_ insertObject:ignored atIndex:0];
5926 [sections_ insertObject:upgradable atIndex:0];
5929 [self reloadButtons];
5932 - (void) resetViewAnimated:(BOOL)animated {
5933 [list_ resetViewAnimated:animated];
5936 - (NSString *) leftButtonTitle {
5937 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5940 - (id) rightButtonTitle {
5941 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5944 - (NSString *) title {
5950 /* Search View {{{ */
5951 @protocol SearchViewDelegate
5952 - (void) showKeyboard:(BOOL)show;
5955 @interface SearchView : RVPage {
5957 UISearchField *field_;
5958 UITransitionView *transition_;
5959 FilteredPackageTable *table_;
5960 UIPreferencesTable *advanced_;
5966 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5967 - (void) reloadData;
5971 @implementation SearchView
5974 [field_ setDelegate:nil];
5976 [accessory_ release];
5978 [transition_ release];
5980 [advanced_ release];
5985 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5989 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5991 case 0: return @"Advanced Search (Coming Soon!)";
5993 default: _assert(false);
5997 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6001 default: _assert(false);
6005 - (void) _showKeyboard:(BOOL)show {
6006 CGSize keysize = [UIKeyboard defaultSize];
6007 CGRect keydown = [book_ pageBounds];
6008 CGRect keyup = keydown;
6009 keyup.size.height -= keysize.height - ButtonBarHeight_;
6011 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6013 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6014 [animation setSignificantRectFields:8];
6017 [animation setStartFrame:keydown];
6018 [animation setEndFrame:keyup];
6020 [animation setStartFrame:keyup];
6021 [animation setEndFrame:keydown];
6024 UIAnimator *animator = [UIAnimator sharedAnimator];
6027 addAnimations:[NSArray arrayWithObjects:animation, nil]
6028 withDuration:(KeyboardTime_ - delay)
6033 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6035 [delegate_ showKeyboard:show];
6038 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6039 [self _showKeyboard:YES];
6042 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6043 [self _showKeyboard:NO];
6046 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6048 NSString *text([field_ text]);
6049 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6055 - (void) textFieldClearButtonPressed:(UITextField *)field {
6059 - (void) keyboardInputShouldDelete:(id)input {
6063 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6064 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6068 [field_ resignFirstResponder];
6073 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6074 if ((self = [super initWithBook:book]) != nil) {
6075 CGRect pageBounds = [book_ pageBounds];
6077 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6078 [self addSubview:transition_];
6080 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6082 [advanced_ setReusesTableCells:YES];
6083 [advanced_ setDataSource:self];
6084 [advanced_ reloadData];
6086 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6087 CGColor dimmed(space_, 0, 0, 0, 0.5);
6088 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6090 table_ = [[FilteredPackageTable alloc]
6094 filter:@selector(isUnfilteredAndSearchedForBy:)
6098 [table_ setShouldHideHeaderInShortLists:NO];
6099 [transition_ transition:0 toView:table_];
6108 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6115 [self bounds].size.width - area.origin.x - 18;
6117 area.size.height = [UISearchField defaultHeight];
6119 field_ = [[UISearchField alloc] initWithFrame:area];
6121 UIFont *font = [UIFont systemFontOfSize:16];
6122 [field_ setFont:font];
6124 [field_ setPlaceholder:@"Package Names & Descriptions"];
6125 [field_ setDelegate:self];
6127 [field_ setPaddingTop:5];
6129 UITextInputTraits *traits([field_ textInputTraits]);
6130 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6131 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6132 [traits setReturnKeyType:UIReturnKeySearch];
6134 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6136 accessory_ = [[UIView alloc] initWithFrame:accrect];
6137 [accessory_ addSubview:field_];
6139 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6140 [configure setShowPressFeedback:YES];
6141 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6142 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6143 [accessory_ addSubview:configure];*/
6145 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6146 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6152 LKAnimation *animation = [LKTransition animation];
6153 [animation setType:@"oglFlip"];
6154 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6155 [animation setFillMode:@"extended"];
6156 [animation setTransitionFlags:3];
6157 [animation setDuration:10];
6158 [animation setSpeed:0.35];
6159 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6160 [[transition_ _layer] addAnimation:animation forKey:0];
6161 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6162 flipped_ = !flipped_;
6166 - (void) configurePushed {
6167 [field_ resignFirstResponder];
6171 - (void) resetViewAnimated:(BOOL)animated {
6174 [table_ resetViewAnimated:animated];
6177 - (void) _reloadData {
6180 - (void) reloadData {
6183 [table_ setObject:[field_ text]];
6184 _profile(SearchView$reloadData)
6185 [table_ reloadData];
6188 [table_ resetCursor];
6191 - (UIView *) accessoryView {
6195 - (NSString *) title {
6199 - (NSString *) backButtonTitle {
6203 - (void) setDelegate:(id)delegate {
6204 [table_ setDelegate:delegate];
6205 [super setDelegate:delegate];
6211 @interface SettingsView : RVPage {
6212 _transient Database *database_;
6215 UIPreferencesTable *table_;
6216 _UISwitchSlider *subscribedSwitch_;
6217 _UISwitchSlider *ignoredSwitch_;
6218 UIPreferencesControlTableCell *subscribedCell_;
6219 UIPreferencesControlTableCell *ignoredCell_;
6222 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6226 @implementation SettingsView
6229 [table_ setDataSource:nil];
6232 if (package_ != nil)
6235 [subscribedSwitch_ release];
6236 [ignoredSwitch_ release];
6237 [subscribedCell_ release];
6238 [ignoredCell_ release];
6242 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6243 if (package_ == nil)
6249 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6250 if (package_ == nil)
6257 default: _assert(false);
6263 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6264 if (package_ == nil)
6271 default: _assert(false);
6277 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6278 if (package_ == nil)
6285 default: _assert(false);
6291 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6292 if (package_ == nil)
6295 _UISwitchSlider *slider([cell control]);
6296 BOOL value([slider value] != 0);
6297 NSMutableDictionary *metadata([package_ metadata]);
6300 if (NSNumber *number = [metadata objectForKey:key])
6301 before = [number boolValue];
6305 if (value != before) {
6306 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6308 [delegate_ updateData];
6312 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6313 [self onSomething:cell withKey:@"IsSubscribed"];
6316 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6317 [self onSomething:cell withKey:@"IsIgnored"];
6320 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6321 if (package_ == nil)
6325 case 0: switch (row) {
6327 return subscribedCell_;
6329 return ignoredCell_;
6330 default: _assert(false);
6333 case 1: switch (row) {
6335 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6336 [cell setShowSelection:NO];
6337 [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."];
6341 default: _assert(false);
6344 default: _assert(false);
6350 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6351 if ((self = [super initWithBook:book])) {
6352 database_ = database;
6353 name_ = [package retain];
6355 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6356 [self addSubview:table_];
6358 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6359 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6361 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6362 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6364 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6365 [subscribedCell_ setShowSelection:NO];
6366 [subscribedCell_ setTitle:@"Show All Changes"];
6367 [subscribedCell_ setControl:subscribedSwitch_];
6369 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6370 [ignoredCell_ setShowSelection:NO];
6371 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6372 [ignoredCell_ setControl:ignoredSwitch_];
6374 [table_ setDataSource:self];
6379 - (void) resetViewAnimated:(BOOL)animated {
6380 [table_ resetViewAnimated:animated];
6383 - (void) reloadData {
6384 if (package_ != nil)
6385 [package_ autorelease];
6386 package_ = [database_ packageWithName:name_];
6387 if (package_ != nil) {
6389 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6390 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6393 [table_ reloadData];
6396 - (NSString *) title {
6402 /* Signature View {{{ */
6403 @interface SignatureView : BrowserView {
6404 _transient Database *database_;
6408 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6412 @implementation SignatureView
6419 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6421 [super webView:sender didClearWindowObject:window forFrame:frame];
6424 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6425 if ((self = [super initWithBook:book]) != nil) {
6426 database_ = database;
6427 package_ = [package retain];
6432 - (void) resetViewAnimated:(BOOL)animated {
6435 - (void) reloadData {
6436 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6442 @interface Cydia : UIApplication <
6443 ConfirmationViewDelegate,
6444 ProgressViewDelegate,
6453 UIToolbar *buttonbar_;
6457 NSMutableArray *essential_;
6458 NSMutableArray *broken_;
6460 Database *database_;
6461 ProgressView *progress_;
6465 UIKeyboard *keyboard_;
6466 UIProgressHUD *hud_;
6468 SectionsView *sections_;
6469 ChangesView *changes_;
6470 ManageView *manage_;
6471 SearchView *search_;
6476 @implementation Cydia
6479 if ([broken_ count] != 0) {
6480 int count = [broken_ count];
6482 UIActionSheet *sheet = [[[UIActionSheet alloc]
6483 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6484 buttons:[NSArray arrayWithObjects:
6486 @"Ignore (Temporary)",
6488 defaultButtonIndex:0
6493 [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."];
6494 [sheet popupAlertAnimated:YES];
6495 } else if (!Ignored_ && [essential_ count] != 0) {
6496 int count = [essential_ count];
6498 UIActionSheet *sheet = [[[UIActionSheet alloc]
6499 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6500 buttons:[NSArray arrayWithObjects:
6501 @"Upgrade Essential",
6502 @"Complete Upgrade",
6503 @"Ignore (Temporary)",
6505 defaultButtonIndex:0
6510 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6511 [sheet popupAlertAnimated:YES];
6515 - (void) _reloadData {
6518 static bool loaded(false);
6519 UIProgressHUD *hud([self addProgressHUD]);
6520 [hud setText:(loaded ? @"Reloading Data" : @"Loading Data")];
6523 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6526 [self removeProgressHUD:hud];
6530 [essential_ removeAllObjects];
6531 [broken_ removeAllObjects];
6533 NSArray *packages = [database_ packages];
6534 for (Package *package in packages) {
6536 [broken_ addObject:package];
6537 if ([package upgradableAndEssential:NO]) {
6538 if ([package essential])
6539 [essential_ addObject:package];
6545 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6546 [buttonbar_ setBadgeValue:badge forButton:3];
6547 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6548 [buttonbar_ setBadgeAnimated:YES forButton:3];
6549 [self setApplicationBadge:badge];
6551 [buttonbar_ setBadgeValue:nil forButton:3];
6552 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6553 [buttonbar_ setBadgeAnimated:NO forButton:3];
6554 [self removeApplicationBadge];
6559 // XXX: what is this line of code for?
6560 if ([packages count] == 0);
6561 else if (Loaded_ || ForSaurik) loaded:
6566 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6567 NSTimeInterval interval([update timeIntervalSinceNow]);
6568 if (interval <= 0 && interval > -600)
6576 - (void) _saveConfig {
6579 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6585 - (void) updateData {
6588 /* XXX: this is just stupid */
6589 if (tag_ != 2 && sections_ != nil)
6590 [sections_ reloadData];
6591 if (tag_ != 3 && changes_ != nil)
6592 [changes_ reloadData];
6593 if (tag_ != 5 && search_ != nil)
6594 [search_ reloadData];
6604 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6605 _assert(file != NULL);
6607 NSArray *keys = [Sources_ allKeys];
6609 for (NSString *key in keys) {
6610 NSDictionary *source = [Sources_ objectForKey:key];
6612 fprintf(file, "%s %s %s\n",
6613 [[source objectForKey:@"Type"] UTF8String],
6614 [[source objectForKey:@"URI"] UTF8String],
6615 [[source objectForKey:@"Distribution"] UTF8String]
6624 detachNewThreadSelector:@selector(update_)
6627 title:@"Updating Sources"
6631 - (void) reloadData {
6632 @synchronized (self) {
6633 if (confirm_ == nil)
6639 pkgProblemResolver *resolver = [database_ resolver];
6641 resolver->InstallProtect();
6642 if (!resolver->Resolve(true))
6646 - (void) popUpBook:(RVBook *)book {
6647 [underlay_ popSubview:book];
6650 - (CGRect) popUpBounds {
6651 return [underlay_ bounds];
6655 [database_ prepare];
6657 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6658 [confirm_ setDelegate:self];
6660 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6661 [page setDelegate:self];
6663 [confirm_ setPage:page];
6664 [self popUpBook:confirm_];
6667 - (void) installPackage:(Package *)package {
6668 @synchronized (self) {
6675 - (void) removePackage:(Package *)package {
6676 @synchronized (self) {
6683 - (void) distUpgrade {
6684 @synchronized (self) {
6685 [database_ upgrade];
6691 @synchronized (self) {
6693 if (confirm_ != nil) {
6701 [overlay_ removeFromSuperview];
6705 detachNewThreadSelector:@selector(perform)
6712 - (void) bootstrap_ {
6714 [database_ upgrade];
6715 [database_ prepare];
6716 [database_ perform];
6719 - (void) bootstrap {
6721 detachNewThreadSelector:@selector(bootstrap_)
6724 title:@"Bootstrap Install"
6728 - (void) progressViewIsComplete:(ProgressView *)progress {
6729 if (confirm_ != nil) {
6730 [underlay_ addSubview:overlay_];
6731 [confirm_ popFromSuperviewAnimated:NO];
6737 - (void) setPage:(RVPage *)page {
6738 [page resetViewAnimated:NO];
6739 [page setDelegate:self];
6740 [book_ setPage:page];
6743 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6744 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6745 [browser loadURL:url];
6749 - (void) _setHomePage {
6750 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6753 - (void) buttonBarItemTapped:(id)sender {
6754 unsigned tag = [sender tag];
6756 [book_ resetViewAnimated:YES];
6758 } else if (tag_ == 2 && tag != 2)
6759 [sections_ resetView];
6762 case 1: [self _setHomePage]; break;
6764 case 2: [self setPage:sections_]; break;
6765 case 3: [self setPage:changes_]; break;
6766 case 4: [self setPage:manage_]; break;
6767 case 5: [self setPage:search_]; break;
6769 default: _assert(false);
6775 - (void) applicationWillSuspend {
6777 [super applicationWillSuspend];
6780 - (void) askForSettings {
6781 UIActionSheet *role = [[[UIActionSheet alloc]
6782 initWithTitle:@"Who Are You?"
6783 buttons:[NSArray arrayWithObjects:
6784 @"User (Graphical Only)",
6785 @"Hacker (+ Command Line)",
6786 @"Developer (No Filters)",
6788 defaultButtonIndex:-1
6793 [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."];
6794 [role popupAlertAnimated:YES];
6799 [self setStatusBarShowsProgress:NO];
6800 [self removeProgressHUD:hud_];
6805 pid_t pid = ExecFork();
6807 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6808 perror("launchctl stop");
6815 [self askForSettings];
6820 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6822 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6823 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6824 0, 0, screenrect.size.width, screenrect.size.height - 48
6825 ) database:database_];
6827 [book_ setDelegate:self];
6829 [overlay_ addSubview:book_];
6831 NSArray *buttonitems = [NSArray arrayWithObjects:
6832 [NSDictionary dictionaryWithObjectsAndKeys:
6833 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6834 @"home-up.png", kUIButtonBarButtonInfo,
6835 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6836 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6837 self, kUIButtonBarButtonTarget,
6838 @"Home", kUIButtonBarButtonTitle,
6839 @"0", kUIButtonBarButtonType,
6842 [NSDictionary dictionaryWithObjectsAndKeys:
6843 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6844 @"install-up.png", kUIButtonBarButtonInfo,
6845 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6846 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6847 self, kUIButtonBarButtonTarget,
6848 @"Sections", kUIButtonBarButtonTitle,
6849 @"0", kUIButtonBarButtonType,
6852 [NSDictionary dictionaryWithObjectsAndKeys:
6853 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6854 @"changes-up.png", kUIButtonBarButtonInfo,
6855 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6856 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6857 self, kUIButtonBarButtonTarget,
6858 @"Changes", kUIButtonBarButtonTitle,
6859 @"0", kUIButtonBarButtonType,
6862 [NSDictionary dictionaryWithObjectsAndKeys:
6863 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6864 @"manage-up.png", kUIButtonBarButtonInfo,
6865 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6866 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6867 self, kUIButtonBarButtonTarget,
6868 @"Manage", kUIButtonBarButtonTitle,
6869 @"0", kUIButtonBarButtonType,
6872 [NSDictionary dictionaryWithObjectsAndKeys:
6873 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6874 @"search-up.png", kUIButtonBarButtonInfo,
6875 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6876 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6877 self, kUIButtonBarButtonTarget,
6878 @"Search", kUIButtonBarButtonTitle,
6879 @"0", kUIButtonBarButtonType,
6883 buttonbar_ = [[UIToolbar alloc]
6885 withFrame:CGRectMake(
6886 0, screenrect.size.height - ButtonBarHeight_,
6887 screenrect.size.width, ButtonBarHeight_
6889 withItemList:buttonitems
6892 [buttonbar_ setDelegate:self];
6893 [buttonbar_ setBarStyle:1];
6894 [buttonbar_ setButtonBarTrackingMode:2];
6896 int buttons[5] = {1, 2, 3, 4, 5};
6897 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6898 [buttonbar_ showButtonGroup:0 withDuration:0];
6900 for (int i = 0; i != 5; ++i)
6901 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6902 i * 64 + 2, 1, 60, ButtonBarHeight_
6905 [buttonbar_ showSelectionForButton:1];
6906 [overlay_ addSubview:buttonbar_];
6908 [UIKeyboard initImplementationNow];
6909 CGSize keysize = [UIKeyboard defaultSize];
6910 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6911 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6912 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6913 [overlay_ addSubview:keyboard_];
6916 [underlay_ addSubview:overlay_];
6920 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6921 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6922 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6924 manage_ = (ManageView *) [[self
6925 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6926 withClass:[ManageView class]
6934 [self _setHomePage];
6937 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6938 NSString *context([sheet context]);
6940 if ([context isEqualToString:@"missing"])
6942 else if ([context isEqualToString:@"fixhalf"]) {
6945 @synchronized (self) {
6946 for (Package *broken in broken_) {
6949 NSString *id = [broken id];
6950 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6951 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6952 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6953 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6962 [broken_ removeAllObjects];
6971 } else if ([context isEqualToString:@"role"]) {
6973 case 1: Role_ = @"User"; break;
6974 case 2: Role_ = @"Hacker"; break;
6975 case 3: Role_ = @"Developer"; break;
6982 bool reset = Settings_ != nil;
6984 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6988 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6998 } else if ([context isEqualToString:@"upgrade"]) {
7001 @synchronized (self) {
7002 for (Package *essential in essential_)
7003 [essential install];
7026 - (void) reorganize { _pooled
7027 system("/usr/libexec/cydia/free.sh");
7028 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7031 - (void) applicationSuspend:(__GSEvent *)event {
7032 if (hud_ == nil && ![progress_ isRunning])
7033 [super applicationSuspend:event];
7036 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7038 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7041 - (void) _setSuspended:(BOOL)value {
7043 [super _setSuspended:value];
7046 - (UIProgressHUD *) addProgressHUD {
7047 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
7048 [window_ setUserInteractionEnabled:NO];
7050 [progress_ addSubview:hud];
7054 - (void) removeProgressHUD:(UIProgressHUD *)hud {
7056 [hud removeFromSuperview];
7057 [window_ setUserInteractionEnabled:YES];
7060 - (void) openMailToURL:(NSURL *)url {
7061 // XXX: this makes me sad
7063 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7065 [UIApp openURL:url];// asPanel:YES];
7069 - (void) clearFirstResponder {
7070 if (id responder = [window_ firstResponder])
7071 [responder resignFirstResponder];
7074 - (RVPage *) pageForPackage:(NSString *)name {
7075 if (Package *package = [database_ packageWithName:name]) {
7076 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7077 [view setPackage:package];
7080 UIActionSheet *sheet = [[[UIActionSheet alloc]
7081 initWithTitle:@"Cannot Locate Package"
7082 buttons:[NSArray arrayWithObjects:@"Close", nil]
7083 defaultButtonIndex:0
7088 [sheet setBodyText:[NSString stringWithFormat:
7089 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7092 [sheet popupAlertAnimated:YES];
7097 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7101 NSString *scheme([[url scheme] lowercaseString]);
7102 if (![scheme isEqualToString:@"cydia"])
7104 NSString *path([url absoluteString]);
7105 if ([path length] < 8)
7107 path = [path substringFromIndex:8];
7108 if (![path hasPrefix:@"/"])
7109 path = [@"/" stringByAppendingString:path];
7111 if ([path isEqualToString:@"/add-source"])
7112 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7113 else if ([path isEqualToString:@"/storage"])
7114 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
7115 else if ([path isEqualToString:@"/sources"])
7116 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7117 else if ([path isEqualToString:@"/packages"])
7118 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7119 else if ([path hasPrefix:@"/url/"])
7120 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
7121 else if ([path hasPrefix:@"/launch/"])
7122 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
7123 else if ([path hasPrefix:@"/package-settings/"])
7124 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
7125 else if ([path hasPrefix:@"/package-signature/"])
7126 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
7127 else if ([path hasPrefix:@"/package/"])
7128 return [self pageForPackage:[path substringFromIndex:9]];
7129 else if ([path hasPrefix:@"/files/"]) {
7130 NSString *name = [path substringFromIndex:7];
7132 if (Package *package = [database_ packageWithName:name]) {
7133 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7134 [files setPackage:package];
7142 - (void) applicationOpenURL:(NSURL *)url {
7143 [super applicationOpenURL:url];
7145 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7146 [self setPage:page];
7147 [buttonbar_ showSelectionForButton:tag];
7152 - (void) applicationDidFinishLaunching:(id)unused {
7154 Font12_ = [[UIFont systemFontOfSize:12] retain];
7155 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7156 Font14_ = [[UIFont systemFontOfSize:14] retain];
7157 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7158 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7160 _assert(pkgInitConfig(*_config));
7161 _assert(pkgInitSystem(*_config, _system));
7165 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7166 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7168 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7170 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7171 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7173 [window_ orderFront:self];
7174 [window_ makeKey:self];
7175 [window_ setHidden:NO];
7177 database_ = [Database sharedInstance];
7178 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7179 [database_ setDelegate:progress_];
7180 [window_ setContentView:progress_];
7182 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7183 [progress_ setContentView:underlay_];
7185 [progress_ resetView];
7188 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7189 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7190 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7191 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7192 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7193 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7194 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7196 [self setIdleTimerDisabled:YES];
7198 hud_ = [[self addProgressHUD] retain];
7199 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7201 [self setStatusBarShowsProgress:YES];
7204 detachNewThreadSelector:@selector(reorganize)
7212 - (void) showKeyboard:(BOOL)show {
7213 CGSize keysize = [UIKeyboard defaultSize];
7214 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7215 CGRect keyup = keydown;
7216 keyup.origin.y -= keysize.height;
7218 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7219 [animation setSignificantRectFields:2];
7222 [animation setStartFrame:keydown];
7223 [animation setEndFrame:keyup];
7224 [keyboard_ activate];
7226 [animation setStartFrame:keyup];
7227 [animation setEndFrame:keydown];
7228 [keyboard_ deactivate];
7231 [[UIAnimator sharedAnimator]
7232 addAnimations:[NSArray arrayWithObjects:animation, nil]
7233 withDuration:KeyboardTime_
7238 - (void) slideUp:(UIActionSheet *)alert {
7240 [alert presentSheetFromButtonBar:buttonbar_];
7242 [alert presentSheetInView:overlay_];
7247 void AddPreferences(NSString *plist) { _pooled
7248 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7249 _assert(settings != NULL);
7250 NSMutableArray *items = [settings objectForKey:@"items"];
7254 for (NSMutableDictionary *item in items) {
7255 NSString *label = [item objectForKey:@"label"];
7256 if (label != nil && [label isEqualToString:@"Cydia"]) {
7263 for (size_t i(0); i != [items count]; ++i) {
7264 NSDictionary *item([items objectAtIndex:i]);
7265 NSString *label = [item objectForKey:@"label"];
7266 if (label != nil && [label isEqualToString:@"General"]) {
7267 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7268 @"CydiaSettings", @"bundle",
7269 @"PSLinkCell", @"cell",
7270 [NSNumber numberWithBool:YES], @"hasIcon",
7271 [NSNumber numberWithBool:YES], @"isController",
7273 nil] atIndex:(i + 1)];
7279 _assert([settings writeToFile:plist atomically:YES] == YES);
7284 id Alloc_(id self, SEL selector) {
7285 id object = alloc_(self, selector);
7286 lprintf("[%s]A-%p\n", self->isa->name, object);
7291 id Dealloc_(id self, SEL selector) {
7292 id object = dealloc_(self, selector);
7293 lprintf("[%s]D-%p\n", self->isa->name, object);
7297 int main(int argc, char *argv[]) { _pooled
7299 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7301 bool substrate(false);
7307 for (int argi(1); argi != argc; ++argi)
7308 if (strcmp(argv[argi], "--") == 0) {
7310 argv[argi] = argv[0];
7316 for (int argi(1); argi != arge; ++argi)
7317 if (strcmp(args[argi], "--bootstrap") == 0)
7319 else if (strcmp(args[argi], "--substrate") == 0)
7322 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7325 App_ = [[NSBundle mainBundle] bundlePath];
7326 Home_ = NSHomeDirectory();
7327 Locale_ = CFLocaleCopyCurrent();
7330 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7331 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7332 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7333 Sounds_Keyboard_ = [keyboard boolValue];
7339 #if 1 /* XXX: this costs 1.4s of startup performance */
7340 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7341 _assert(errno == ENOENT);
7342 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7343 _assert(errno == ENOENT);
7346 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7347 alloc_ = alloc->method_imp;
7348 alloc->method_imp = (IMP) &Alloc_;*/
7350 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7351 dealloc_ = dealloc->method_imp;
7352 dealloc->method_imp = (IMP) &Dealloc_;*/
7357 size = sizeof(maxproc);
7358 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7359 perror("sysctlbyname(\"kern.maxproc\", ?)");
7360 else if (maxproc < 64) {
7362 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7363 perror("sysctlbyname(\"kern.maxproc\", #)");
7366 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7367 char *machine = new char[size];
7368 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7369 perror("sysctlbyname(\"hw.machine\", ?)");
7373 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7375 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7376 Build_ = [system objectForKey:@"ProductBuildVersion"];
7377 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
7378 Product_ = [info objectForKey:@"SafariProductVersion"];
7379 Safari_ = [info objectForKey:@"CFBundleVersion"];
7382 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7383 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7386 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7389 if (Metadata_ == NULL)
7390 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7392 Settings_ = [Metadata_ objectForKey:@"Settings"];
7394 Packages_ = [Metadata_ objectForKey:@"Packages"];
7395 Sections_ = [Metadata_ objectForKey:@"Sections"];
7396 Sources_ = [Metadata_ objectForKey:@"Sources"];
7399 if (Settings_ != nil)
7400 Role_ = [Settings_ objectForKey:@"Role"];
7402 if (Packages_ == nil) {
7403 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7404 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7407 if (Sections_ == nil) {
7408 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7409 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7412 if (Sources_ == nil) {
7413 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7414 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7418 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7421 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7422 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7423 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7424 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7426 if (access("/User", F_OK) != 0) {
7428 system("/usr/libexec/cydia/firmware.sh");
7432 _assert([[NSFileManager defaultManager]
7433 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7434 withIntermediateDirectories:YES
7439 space_ = CGColorSpaceCreateDeviceRGB();
7441 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7442 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7443 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7444 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7445 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7446 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7448 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7450 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7452 UIApplicationUseLegacyEvents(YES);
7453 UIKeyboardDisableAutomaticAppearance();
7456 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7458 CGColorSpaceRelease(space_);