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 /*[self performSelector:selector withObject:object];
269 volatile bool stopped(false);
271 NSMutableArray *context([NSMutableArray arrayWithObjects:
272 [NSValue valueWithPointer:selector],
273 [NSValue valueWithNonretainedObject:object],
274 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
277 NSThread *thread([[[NSThread alloc]
279 selector:@selector(_yieldToContext:)
285 NSRunLoop *loop([NSRunLoop currentRunLoop]);
286 NSDate *future([NSDate distantFuture]);
288 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
290 return [context count] == 0 ? nil : [context objectAtIndex:0];
293 - (id) yieldToSelector:(SEL)selector {
294 return [self yieldToSelector:selector withObject:nil];
299 /* NSForcedOrderingSearch doesn't work on the iPhone */
300 static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
301 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
302 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
303 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
305 /* iPhoneOS 2.0 Compatibility {{{ */
307 @interface UITextView (iPhoneOS)
308 - (void) setTextSize:(float)size;
311 @implementation UITextView (iPhoneOS)
313 - (void) setTextSize:(float)size {
314 [self setFont:[[self font] fontWithSize:size]];
321 extern NSString * const kCAFilterNearest;
323 /* Information Dictionaries {{{ */
324 @interface NSMutableArray (Cydia)
325 - (void) addInfoDictionary:(NSDictionary *)info;
328 @implementation NSMutableArray (Cydia)
330 - (void) addInfoDictionary:(NSDictionary *)info {
331 [self addObject:info];
336 @interface NSMutableDictionary (Cydia)
337 - (void) addInfoDictionary:(NSDictionary *)info;
340 @implementation NSMutableDictionary (Cydia)
342 - (void) addInfoDictionary:(NSDictionary *)info {
343 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
344 [self setObject:info forKey:bundle];
349 /* Pop Transitions {{{ */
350 @interface PopTransitionView : UITransitionView {
355 @implementation PopTransitionView
357 - (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
358 if (from != nil && to == nil)
359 [self removeFromSuperview];
364 @implementation UIView (PopUpView)
366 - (void) popFromSuperviewAnimated:(BOOL)animated {
367 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
370 - (void) popSubview:(UIView *)view {
371 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
372 [transition setDelegate:transition];
373 [self addSubview:transition];
375 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
376 [transition transition:UITransitionNone toView:blank];
377 [transition transition:UITransitionPushFromBottom toView:view];
383 #define lprintf(args...) fprintf(stderr, args)
386 #define ForSaurik (1 && !ForRelease)
387 #define ShowInternals (0 && !ForRelease)
388 #define IgnoreInstall (0 && !ForRelease)
389 #define RecycleWebViews 0
390 #define AlwaysReload (1 && !ForRelease)
394 #define _trace(args...)
396 #define _profile(name)
399 #define PrintTimes() do {} while (false)
403 @interface NSMutableArray (Radix)
404 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
407 @implementation NSMutableArray (Radix)
409 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
410 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
411 [invocation setSelector:selector];
412 [invocation setArgument:&object atIndex:2];
414 size_t count([self count]);
419 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
421 for (size_t i(0); i != count; ++i) {
422 RadixItem &item(lhs[i]);
425 id object([self objectAtIndex:i]);
426 [invocation setTarget:object];
429 [invocation getReturnValue:&item.key];
432 static const size_t width = 32;
433 static const size_t bits = 11;
434 static const size_t slots = 1 << bits;
435 static const size_t passes = (width + (bits - 1)) / bits;
437 size_t *hist(new size_t[slots]);
439 for (size_t pass(0); pass != passes; ++pass) {
440 memset(hist, 0, sizeof(size_t) * slots);
442 for (size_t i(0); i != count; ++i) {
443 uint32_t key(lhs[i].key);
445 key &= _not(uint32_t) >> width - bits;
450 for (size_t i(0); i != slots; ++i) {
451 size_t local(offset);
456 for (size_t i(0); i != count; ++i) {
457 uint32_t key(lhs[i].key);
459 key &= _not(uint32_t) >> width - bits;
460 rhs[hist[key]++] = lhs[i];
470 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
471 for (size_t i(0); i != count; ++i)
472 [values addObject:[self objectAtIndex:lhs[i].index]];
473 [self setArray:values];
481 /* Apple Bug Fixes {{{ */
482 @implementation UIWebDocumentView (Cydia)
484 - (void) _setScrollerOffset:(CGPoint)offset {
485 UIScroller *scroller([self _scroller]);
487 CGSize size([scroller contentSize]);
488 CGSize bounds([scroller bounds].size);
491 max.x = size.width - bounds.width;
492 max.y = size.height - bounds.height;
500 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
501 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
503 [scroller setOffset:offset];
510 kUIControlEventMouseDown = 1 << 0,
511 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
512 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
513 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
514 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
515 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
516 } UIControlEventMasks;
518 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
519 size_t length([self length] - state->state);
522 else if (length > count)
524 for (size_t i(0); i != length; ++i)
525 objects[i] = [self item:state->state++];
526 state->itemsPtr = objects;
527 state->mutationsPtr = (unsigned long *) self;
531 @interface NSString (UIKit)
532 - (NSString *) stringByAddingPercentEscapes;
533 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
536 @interface NSString (Cydia)
537 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
538 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
539 - (NSComparisonResult) compareByPath:(NSString *)other;
540 - (NSString *) stringByCachingURLWithCurrentCDN;
541 - (NSString *) stringByAddingPercentEscapesIncludingReserved;
544 @implementation NSString (Cydia)
546 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length {
547 return [[[NSString alloc] initWithBytesNoCopy:const_cast<char *>(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
550 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
551 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
554 - (NSComparisonResult) compareByPath:(NSString *)other {
555 NSString *prefix = [self commonPrefixWithString:other options:0];
556 size_t length = [prefix length];
558 NSRange lrange = NSMakeRange(length, [self length] - length);
559 NSRange rrange = NSMakeRange(length, [other length] - length);
561 lrange = [self rangeOfString:@"/" options:0 range:lrange];
562 rrange = [other rangeOfString:@"/" options:0 range:rrange];
564 NSComparisonResult value;
566 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
567 value = NSOrderedSame;
568 else if (lrange.location == NSNotFound)
569 value = NSOrderedAscending;
570 else if (rrange.location == NSNotFound)
571 value = NSOrderedDescending;
573 value = NSOrderedSame;
575 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
576 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
577 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
578 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
580 NSComparisonResult result = [lpath compare:rpath];
581 return result == NSOrderedSame ? value : result;
584 - (NSString *) stringByCachingURLWithCurrentCDN {
586 stringByReplacingOccurrencesOfString:@"://"
587 withString:@"://ne.edgecastcdn.net/8003A4/"
589 /* XXX: this is somewhat inaccurate */
590 range:NSMakeRange(0, 10)
594 - (NSString *) stringByAddingPercentEscapesIncludingReserved {
595 return [(id)CFURLCreateStringByAddingPercentEscapes(
600 kCFStringEncodingUTF8
606 /* Perl-Compatible RegEx {{{ */
616 Pcre(const char *regex) :
621 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
624 lprintf("%d:%s\n", offset, error);
628 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
629 matches_ = new int[(capture_ + 1) * 3];
637 NSString *operator [](size_t match) {
638 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
641 bool operator ()(NSString *data) {
642 // XXX: length is for characters, not for bytes
643 return operator ()([data UTF8String], [data length]);
646 bool operator ()(const char *data, size_t size) {
648 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
652 /* Mime Addresses {{{ */
653 @interface Address : NSObject {
659 - (NSString *) address;
661 + (Address *) addressWithString:(NSString *)string;
662 - (Address *) initWithString:(NSString *)string;
665 @implementation Address
674 - (NSString *) name {
678 - (NSString *) address {
682 + (Address *) addressWithString:(NSString *)string {
683 return [[[Address alloc] initWithString:string] autorelease];
686 + (NSArray *) _attributeKeys {
687 return [NSArray arrayWithObjects:@"address", @"name", nil];
690 - (NSArray *) attributeKeys {
691 return [[self class] _attributeKeys];
694 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
695 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
698 - (Address *) initWithString:(NSString *)string {
699 if ((self = [super init]) != nil) {
700 const char *data = [string UTF8String];
701 size_t size = [string length];
703 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
705 if (address_r(data, size)) {
706 name_ = [address_r[1] retain];
707 address_ = [address_r[2] retain];
709 name_ = [string retain];
717 /* CoreGraphics Primitives {{{ */
728 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
731 Set(space, red, green, blue, alpha);
736 CGColorRelease(color_);
743 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
745 float color[] = {red, green, blue, alpha};
746 color_ = CGColorCreate(space, color);
749 operator CGColorRef() {
755 extern "C" void UISetColor(CGColorRef color);
757 /* Random Global Variables {{{ */
758 static const int PulseInterval_ = 50000;
759 static const int ButtonBarHeight_ = 48;
760 static const float KeyboardTime_ = 0.3f;
762 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
763 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
764 #define NotifyConfig_ "/etc/notify.conf"
766 static CGColor Blue_;
767 static CGColor Blueish_;
768 static CGColor Black_;
770 static CGColor White_;
771 static CGColor Gray_;
773 static NSString *App_;
774 static NSString *Home_;
775 static BOOL Sounds_Keyboard_;
777 static BOOL Advanced_;
779 static BOOL Ignored_;
781 static UIFont *Font12_;
782 static UIFont *Font12Bold_;
783 static UIFont *Font14_;
784 static UIFont *Font18Bold_;
785 static UIFont *Font22Bold_;
787 static const char *Machine_ = NULL;
788 static const NSString *UniqueID_ = nil;
789 static const NSString *Build_ = nil;
792 CGColorSpaceRef space_;
797 static NSDictionary *SectionMap_;
798 static NSMutableDictionary *Metadata_;
799 static _transient NSMutableDictionary *Settings_;
800 static _transient NSString *Role_;
801 static _transient NSMutableDictionary *Packages_;
802 static _transient NSMutableDictionary *Sections_;
803 static _transient NSMutableDictionary *Sources_;
804 static bool Changed_;
808 static NSMutableArray *Documents_;
811 NSString *GetLastUpdate() {
812 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
815 return @"Never or Unknown";
817 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
818 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
820 CFRelease(formatter);
822 return [(NSString *) formatted autorelease];
825 /* Display Helpers {{{ */
826 inline float Interpolate(float begin, float end, float fraction) {
827 return (end - begin) * fraction + begin;
830 NSString *SizeString(double size) {
831 bool negative = size < 0;
836 while (size > 1024) {
841 static const char *powers_[] = {"B", "kB", "MB", "GB"};
843 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
846 NSString *StripVersion(NSString *version) {
847 NSRange colon = [version rangeOfString:@":"];
848 if (colon.location != NSNotFound)
849 version = [version substringFromIndex:(colon.location + 1)];
853 NSString *Simplify(NSString *title) {
854 const char *data = [title UTF8String];
855 size_t size = [title length];
857 static Pcre square_r("^\\[(.*)\\]$");
858 if (square_r(data, size))
859 return Simplify(square_r[1]);
861 static Pcre paren_r("^\\((.*)\\)$");
862 if (paren_r(data, size))
863 return Simplify(paren_r[1]);
865 static Pcre title_r("^(.*?) \\(.*\\)$");
866 if (title_r(data, size))
867 return Simplify(title_r[1]);
873 bool isSectionVisible(NSString *section) {
874 NSDictionary *metadata = [Sections_ objectForKey:section];
875 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
876 return hidden == nil || ![hidden boolValue];
879 /* Delegate Prototypes {{{ */
883 @interface NSObject (ProgressDelegate)
886 @implementation NSObject(ProgressDelegate)
888 - (void) _setProgressError:(NSArray *)args {
889 [self performSelector:@selector(setProgressError:forPackage:)
890 withObject:[args objectAtIndex:0]
891 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
897 @protocol ProgressDelegate
898 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
899 - (void) setProgressTitle:(NSString *)title;
900 - (void) setProgressPercent:(float)percent;
901 - (void) startProgress;
902 - (void) addProgressOutput:(NSString *)output;
903 - (bool) isCancelling:(size_t)received;
906 @protocol ConfigurationDelegate
907 - (void) repairWithSelector:(SEL)selector;
908 - (void) setConfigurationData:(NSString *)data;
911 @protocol CydiaDelegate
912 - (void) installPackage:(Package *)package;
913 - (void) removePackage:(Package *)package;
914 - (void) slideUp:(UIActionSheet *)alert;
915 - (void) distUpgrade;
918 - (void) askForSettings;
919 - (UIProgressHUD *) addProgressHUD;
920 - (void) removeProgressHUD:(UIProgressHUD *)hud;
921 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
922 - (RVPage *) pageForPackage:(NSString *)name;
923 - (void) openMailToURL:(NSURL *)url;
924 - (void) clearFirstResponder;
928 /* Status Delegation {{{ */
930 public pkgAcquireStatus
933 _transient NSObject<ProgressDelegate> *delegate_;
941 void setDelegate(id delegate) {
942 delegate_ = delegate;
945 virtual bool MediaChange(std::string media, std::string drive) {
949 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
952 virtual void Fetch(pkgAcquire::ItemDesc &item) {
953 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
954 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
957 virtual void Done(pkgAcquire::ItemDesc &item) {
960 virtual void Fail(pkgAcquire::ItemDesc &item) {
962 item.Owner->Status == pkgAcquire::Item::StatIdle ||
963 item.Owner->Status == pkgAcquire::Item::StatDone
967 std::string &error(item.Owner->ErrorText);
971 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
972 NSArray *fields([description componentsSeparatedByString:@" "]);
973 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
975 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
976 withObject:[NSArray arrayWithObjects:
977 [NSString stringWithUTF8String:error.c_str()],
984 virtual bool Pulse(pkgAcquire *Owner) {
985 bool value = pkgAcquireStatus::Pulse(Owner);
988 double(CurrentBytes + CurrentItems) /
989 double(TotalBytes + TotalItems)
992 [delegate_ setProgressPercent:percent];
993 return [delegate_ isCancelling:CurrentBytes] ? false : value;
996 virtual void Start() {
997 [delegate_ startProgress];
1000 virtual void Stop() {
1004 /* Progress Delegation {{{ */
1009 _transient id<ProgressDelegate> delegate_;
1012 virtual void Update() {
1013 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
1014 [delegate_ setProgressPercent:(Percent / 100)];*/
1023 void setDelegate(id delegate) {
1024 delegate_ = delegate;
1027 virtual void Done() {
1028 //[delegate_ setProgressPercent:1];
1033 /* Database Interface {{{ */
1034 @interface Database : NSObject {
1035 pkgCacheFile cache_;
1036 pkgDepCache::Policy *policy_;
1037 pkgRecords *records_;
1038 pkgProblemResolver *resolver_;
1039 pkgAcquire *fetcher_;
1041 SPtr<pkgPackageManager> manager_;
1042 pkgSourceList *list_;
1044 NSMutableDictionary *sources_;
1045 NSMutableArray *packages_;
1047 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
1056 + (Database *) sharedInstance;
1058 - (void) _readCydia:(NSNumber *)fd;
1059 - (void) _readStatus:(NSNumber *)fd;
1060 - (void) _readOutput:(NSNumber *)fd;
1064 - (Package *) packageWithName:(NSString *)name;
1066 - (pkgCacheFile &) cache;
1067 - (pkgDepCache::Policy *) policy;
1068 - (pkgRecords *) records;
1069 - (pkgProblemResolver *) resolver;
1070 - (pkgAcquire &) fetcher;
1071 - (pkgSourceList &) list;
1072 - (NSArray *) packages;
1073 - (NSArray *) sources;
1074 - (void) reloadData;
1082 - (void) updateWithStatus:(Status &)status;
1084 - (void) setDelegate:(id)delegate;
1085 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
1089 /* Source Class {{{ */
1090 @interface Source : NSObject {
1091 NSString *description_;
1096 NSString *distribution_;
1100 NSString *defaultIcon_;
1102 NSDictionary *record_;
1106 - (Source *) initWithMetaIndex:(metaIndex *)index;
1108 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1110 - (NSDictionary *) record;
1114 - (NSString *) distribution;
1115 - (NSString *) type;
1117 - (NSString *) host;
1119 - (NSString *) name;
1120 - (NSString *) description;
1121 - (NSString *) label;
1122 - (NSString *) origin;
1123 - (NSString *) version;
1125 - (NSString *) defaultIcon;
1129 @implementation Source
1131 #define _clear(field) \
1138 _clear(distribution_)
1141 _clear(description_)
1145 _clear(defaultIcon_)
1154 + (NSArray *) _attributeKeys {
1155 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1158 - (NSArray *) attributeKeys {
1159 return [[self class] _attributeKeys];
1162 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1163 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1166 - (void) setMetaIndex:(metaIndex *)index {
1169 trusted_ = index->IsTrusted();
1171 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1172 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1173 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1175 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1176 if (dindex != NULL) {
1177 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1179 while (std::getline(release, line)) {
1180 std::string::size_type colon(line.find(':'));
1181 if (colon == std::string::npos)
1184 std::string name(line.substr(0, colon));
1185 std::string value(line.substr(colon + 1));
1186 while (!value.empty() && value[0] == ' ')
1187 value = value.substr(1);
1189 if (name == "Default-Icon")
1190 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1191 else if (name == "Description")
1192 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1193 else if (name == "Label")
1194 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1195 else if (name == "Origin")
1196 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1197 else if (name == "Version")
1198 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1202 record_ = [Sources_ objectForKey:[self key]];
1204 record_ = [record_ retain];
1207 - (Source *) initWithMetaIndex:(metaIndex *)index {
1208 if ((self = [super init]) != nil) {
1209 [self setMetaIndex:index];
1213 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1214 NSDictionary *lhr = [self record];
1215 NSDictionary *rhr = [source record];
1218 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1220 NSString *lhs = [self name];
1221 NSString *rhs = [source name];
1223 if ([lhs length] != 0 && [rhs length] != 0) {
1224 unichar lhc = [lhs characterAtIndex:0];
1225 unichar rhc = [rhs characterAtIndex:0];
1227 if (isalpha(lhc) && !isalpha(rhc))
1228 return NSOrderedAscending;
1229 else if (!isalpha(lhc) && isalpha(rhc))
1230 return NSOrderedDescending;
1233 return [lhs compare:rhs options:LaxCompareOptions_];
1236 - (NSDictionary *) record {
1244 - (NSString *) uri {
1248 - (NSString *) distribution {
1249 return distribution_;
1252 - (NSString *) type {
1256 - (NSString *) key {
1257 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1260 - (NSString *) host {
1261 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1264 - (NSString *) name {
1265 return origin_ == nil ? [self host] : origin_;
1268 - (NSString *) description {
1269 return description_;
1272 - (NSString *) label {
1273 return label_ == nil ? [self host] : label_;
1276 - (NSString *) origin {
1280 - (NSString *) version {
1284 - (NSString *) defaultIcon {
1285 return defaultIcon_;
1290 /* Relationship Class {{{ */
1291 @interface Relationship : NSObject {
1296 - (NSString *) type;
1298 - (NSString *) name;
1302 @implementation Relationship
1310 - (NSString *) type {
1318 - (NSString *) name {
1325 /* Package Class {{{ */
1326 @interface Package : NSObject {
1327 pkgCache::PkgIterator iterator_;
1328 _transient Database *database_;
1329 pkgCache::VerIterator version_;
1330 pkgCache::VerFileIterator file_;
1338 NSString *installed_;
1344 NSString *depiction_;
1345 NSString *homepage_;
1351 NSArray *relationships_;
1354 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1355 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1357 - (pkgCache::PkgIterator) iterator;
1359 - (NSString *) section;
1360 - (NSString *) simpleSection;
1364 - (Address *) maintainer;
1366 - (NSString *) description;
1369 - (NSMutableDictionary *) metadata;
1371 - (BOOL) subscribed;
1374 - (NSString *) latest;
1375 - (NSString *) installed;
1378 - (BOOL) upgradableAndEssential:(BOOL)essential;
1381 - (BOOL) unfiltered;
1385 - (BOOL) halfConfigured;
1386 - (BOOL) halfInstalled;
1388 - (NSString *) mode;
1391 - (NSString *) name;
1392 - (NSString *) tagline;
1394 - (NSString *) homepage;
1395 - (NSString *) depiction;
1396 - (Address *) author;
1398 - (NSArray *) files;
1399 - (NSArray *) relationships;
1400 - (NSArray *) warnings;
1401 - (NSArray *) applications;
1403 - (Source *) source;
1404 - (NSString *) role;
1406 - (BOOL) matches:(NSString *)text;
1408 - (bool) hasSupportingRole;
1409 - (BOOL) hasTag:(NSString *)tag;
1410 - (NSString *) primaryPurpose;
1411 - (NSArray *) purposes;
1413 - (NSComparisonResult) compareByName:(Package *)package;
1414 - (NSComparisonResult) compareBySection:(Package *)package;
1416 - (uint32_t) compareForChanges;
1421 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
1422 - (bool) isInstalledAndVisible:(NSNumber *)number;
1423 - (bool) isVisiblyUninstalledInSection:(NSString *)section;
1424 - (bool) isVisibleInSource:(Source *)source;
1428 @implementation Package
1434 if (section_ != nil)
1438 if (installed_ != nil)
1439 [installed_ release];
1447 if (depiction_ != nil)
1448 [depiction_ release];
1449 if (homepage_ != nil)
1450 [homepage_ release];
1451 if (sponsor_ != nil)
1460 if (relationships_ != nil)
1461 [relationships_ release];
1466 + (NSArray *) _attributeKeys {
1467 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1470 - (NSArray *) attributeKeys {
1471 return [[self class] _attributeKeys];
1474 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1475 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1478 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1479 if ((self = [super init]) != nil) { _profile(Package$initWithIterator)
1480 iterator_ = iterator;
1481 database_ = database;
1483 _profile(Package$initWithIterator$Control)
1486 _profile(Package$initWithIterator$Version)
1487 version_ = [database_ policy]->GetCandidateVer(iterator_);
1490 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1492 _profile(Package$initWithIterator$Latest)
1493 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1496 pkgCache::VerIterator current;
1497 NSString *installed;
1499 _profile(Package$initWithIterator$Current)
1500 current = iterator_.CurrentVer();
1501 installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1504 _profile(Package$initWithIterator$Installed)
1505 installed_ = [StripVersion(installed) retain];
1508 _profile(Package$initWithIterator$File)
1509 if (!version_.end())
1510 file_ = version_.FileList();
1512 pkgCache &cache([database_ cache]);
1513 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1517 _profile(Package$initWithIterator$Name)
1518 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1522 _profile(Package$initWithIterator$Parse)
1523 pkgRecords::Parser *parser;
1525 _profile(Package$initWithIterator$Parse$Lookup)
1526 parser = &[database_ records]->Lookup(file_);
1529 const char *begin, *end;
1530 parser->GetRec(begin, end);
1532 NSString *website(nil);
1533 NSString *sponsor(nil);
1534 NSString *author(nil);
1543 {"depiction", &depiction_},
1544 {"homepage", &homepage_},
1545 {"website", &website},
1546 {"sponsor", &sponsor},
1547 {"author", &author},
1551 while (begin != end)
1552 if (*begin == '\n') {
1555 } else if (isblank(*begin)) next: {
1556 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1559 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1560 const char *name(begin);
1561 size_t size(colon - begin);
1563 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1566 const char *stop(begin == NULL ? end : begin);
1567 while (stop[-1] == '\r')
1569 while (++colon != stop && isblank(*colon));
1571 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1572 if (strncasecmp(names[i].name_, name, size) == 0) {
1575 _profile(Package$initWithIterator$Parse$Value)
1576 value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
1579 *names[i].value_ = value;
1589 _profile(Package$initWithIterator$Parse$Retain)
1591 name_ = [name_ retain];
1592 _profile(Package$initWithIterator$Parse$Tagline)
1593 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1596 icon_ = [icon_ retain];
1597 if (depiction_ != nil)
1598 depiction_ = [depiction_ retain];
1599 if (homepage_ == nil)
1600 homepage_ = website;
1601 if ([homepage_ isEqualToString:depiction_])
1603 if (homepage_ != nil)
1604 homepage_ = [homepage_ retain];
1606 sponsor_ = [[Address addressWithString:sponsor] retain];
1608 author_ = [[Address addressWithString:author] retain];
1610 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1614 _profile(Package$initWithIterator$Tags)
1616 for (NSString *tag in tags_)
1617 if ([tag hasPrefix:@"role::"]) {
1618 role_ = [[tag substringFromIndex:6] retain];
1623 NSString *solid(latest == nil ? installed : latest);
1624 bool changed(false);
1626 NSString *key([id_ lowercaseString]);
1628 _profile(Package$initWithIterator$Metadata)
1629 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1630 if (metadata == nil) {
1631 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1636 [metadata setObject:solid forKey:@"LastVersion"];
1639 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1640 NSDate *last([metadata objectForKey:@"LastSeen"]);
1641 NSString *version([metadata objectForKey:@"LastVersion"]);
1644 first = last == nil ? now_ : last;
1645 [metadata setObject:first forKey:@"FirstSeen"];
1650 if (version == nil) {
1651 [metadata setObject:solid forKey:@"LastVersion"];
1653 } else if (![version isEqualToString:solid]) {
1654 [metadata setObject:solid forKey:@"LastVersion"];
1656 [metadata setObject:last forKey:@"LastSeen"];
1662 [Packages_ setObject:metadata forKey:key];
1669 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1670 return [[[Package alloc]
1671 initWithIterator:iterator
1676 - (pkgCache::PkgIterator) iterator {
1680 - (NSString *) section {
1681 if (section_ != nil)
1684 const char *section = iterator_.Section();
1685 if (section == NULL)
1688 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1691 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1692 if (NSString *rename = [value objectForKey:@"Rename"]) {
1697 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1701 - (NSString *) simpleSection {
1702 if (NSString *section = [self section])
1703 return Simplify(section);
1708 - (NSString *) uri {
1711 pkgIndexFile *index;
1712 pkgCache::PkgFileIterator file(file_.File());
1713 if (![database_ list].FindIndex(file, index))
1715 return [NSString stringWithUTF8String:iterator_->Path];
1716 //return [NSString stringWithUTF8String:file.Site()];
1717 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1721 - (Address *) maintainer {
1724 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1725 const std::string &maintainer(parser->Maintainer());
1726 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1730 return version_.end() ? 0 : version_->InstalledSize;
1733 - (NSString *) description {
1736 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1737 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1739 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1740 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1741 if ([lines count] < 2)
1744 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1745 for (size_t i(1), e([lines count]); i != e; ++i) {
1746 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1747 [trimmed addObject:trim];
1750 return [trimmed componentsJoinedByString:@"\n"];
1754 _profile(Package$index)
1755 NSString *name([self name]);
1756 if ([name length] == 0)
1758 unichar character([name characterAtIndex:0]);
1759 if (!isalpha(character))
1765 - (NSMutableDictionary *) metadata {
1766 return [Packages_ objectForKey:[id_ lowercaseString]];
1770 NSDictionary *metadata([self metadata]);
1771 if ([self subscribed])
1772 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1774 return [metadata objectForKey:@"FirstSeen"];
1777 - (BOOL) subscribed {
1778 NSDictionary *metadata([self metadata]);
1779 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1780 return [subscribed boolValue];
1786 NSDictionary *metadata([self metadata]);
1787 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1788 return [ignored boolValue];
1793 - (NSString *) latest {
1797 - (NSString *) installed {
1802 return !version_.end();
1805 - (BOOL) upgradableAndEssential:(BOOL)essential {
1806 pkgCache::VerIterator current = iterator_.CurrentVer();
1810 value = essential && [self essential] && [self visible];
1812 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1816 - (BOOL) essential {
1817 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1821 return [database_ cache][iterator_].InstBroken();
1824 - (BOOL) unfiltered {
1825 NSString *section = [self section];
1826 return section == nil || isSectionVisible(section);
1830 return [self hasSupportingRole] && [self unfiltered];
1834 unsigned char current = iterator_->CurrentState;
1835 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1838 - (BOOL) halfConfigured {
1839 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1842 - (BOOL) halfInstalled {
1843 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1847 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1848 return state.Mode != pkgDepCache::ModeKeep;
1851 - (NSString *) mode {
1852 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1854 switch (state.Mode) {
1855 case pkgDepCache::ModeDelete:
1856 if ((state.iFlags & pkgDepCache::Purge) != 0)
1860 case pkgDepCache::ModeKeep:
1861 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1865 case pkgDepCache::ModeInstall:
1866 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1867 return @"Reinstall";
1868 else switch (state.Status) {
1870 return @"Downgrade";
1876 return @"New Install";
1889 - (NSString *) name {
1890 return name_ == nil ? id_ : name_;
1893 - (NSString *) tagline {
1897 - (UIImage *) icon {
1898 NSString *section = [self simpleSection];
1902 if ([icon_ hasPrefix:@"file:///"])
1903 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1904 if (icon == nil) if (section != nil)
1905 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1906 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1907 if ([dicon hasPrefix:@"file:///"])
1908 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1910 icon = [UIImage applicationImageNamed:@"unknown.png"];
1914 - (NSString *) homepage {
1918 - (NSString *) depiction {
1922 - (Address *) sponsor {
1926 - (Address *) author {
1930 - (NSArray *) files {
1931 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1932 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1935 fin.open([path UTF8String]);
1940 while (std::getline(fin, line))
1941 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1946 - (NSArray *) relationships {
1947 return relationships_;
1950 - (NSArray *) warnings {
1951 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1952 const char *name(iterator_.Name());
1954 size_t length(strlen(name));
1955 if (length < 2) invalid:
1956 [warnings addObject:@"illegal package identifier"];
1957 else for (size_t i(0); i != length; ++i)
1959 /* XXX: technically this is not allowed */
1960 (name[i] < 'A' || name[i] > 'Z') &&
1961 (name[i] < 'a' || name[i] > 'z') &&
1962 (name[i] < '0' || name[i] > '9') &&
1963 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1966 if (strcmp(name, "cydia") != 0) {
1968 bool _private = false;
1971 bool repository = [[self section] isEqualToString:@"Repositories"];
1973 if (NSArray *files = [self files])
1974 for (NSString *file in files)
1975 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1977 else if (!_private && [file isEqualToString:@"/private"])
1979 else if (!stash && [file isEqualToString:@"/var/stash"])
1982 /* XXX: this is not sensitive enough. only some folders are valid. */
1983 if (cydia && !repository)
1984 [warnings addObject:@"files installed into Cydia.app"];
1986 [warnings addObject:@"files installed with /private/*"];
1988 [warnings addObject:@"files installed to /var/stash"];
1991 return [warnings count] == 0 ? nil : warnings;
1994 - (NSArray *) applications {
1995 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1997 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1999 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
2000 if (NSArray *files = [self files])
2001 for (NSString *file in files)
2002 if (application_r(file)) {
2003 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
2004 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
2005 if ([id isEqualToString:me])
2008 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
2010 display = application_r[1];
2012 NSString *bundle([file stringByDeletingLastPathComponent]);
2013 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
2014 if (icon == nil || [icon length] == 0)
2016 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
2018 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
2019 [applications addObject:application];
2021 [application addObject:id];
2022 [application addObject:display];
2023 [application addObject:url];
2026 return [applications count] == 0 ? nil : applications;
2029 - (Source *) source {
2031 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
2038 - (NSString *) role {
2042 - (BOOL) matches:(NSString *)text {
2048 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2049 if (range.location != NSNotFound)
2052 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2053 if (range.location != NSNotFound)
2056 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2057 if (range.location != NSNotFound)
2063 - (bool) hasSupportingRole {
2066 if ([role_ isEqualToString:@"enduser"])
2068 if ([Role_ isEqualToString:@"User"])
2070 if ([role_ isEqualToString:@"hacker"])
2072 if ([Role_ isEqualToString:@"Hacker"])
2074 if ([role_ isEqualToString:@"developer"])
2076 if ([Role_ isEqualToString:@"Developer"])
2081 - (BOOL) hasTag:(NSString *)tag {
2082 return tags_ == nil ? NO : [tags_ containsObject:tag];
2085 - (NSString *) primaryPurpose {
2086 for (NSString *tag in tags_)
2087 if ([tag hasPrefix:@"purpose::"])
2088 return [tag substringFromIndex:9];
2092 - (NSArray *) purposes {
2093 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2094 for (NSString *tag in tags_)
2095 if ([tag hasPrefix:@"purpose::"])
2096 [purposes addObject:[tag substringFromIndex:9]];
2097 return [purposes count] == 0 ? nil : purposes;
2100 - (NSComparisonResult) compareByName:(Package *)package {
2101 NSString *lhs = [self name];
2102 NSString *rhs = [package name];
2104 if ([lhs length] != 0 && [rhs length] != 0) {
2105 unichar lhc = [lhs characterAtIndex:0];
2106 unichar rhc = [rhs characterAtIndex:0];
2108 if (isalpha(lhc) && !isalpha(rhc))
2109 return NSOrderedAscending;
2110 else if (!isalpha(lhc) && isalpha(rhc))
2111 return NSOrderedDescending;
2114 return [lhs compare:rhs options:LaxCompareOptions_];
2117 - (NSComparisonResult) compareBySection:(Package *)package {
2118 NSString *lhs = [self section];
2119 NSString *rhs = [package section];
2121 if (lhs == NULL && rhs != NULL)
2122 return NSOrderedAscending;
2123 else if (lhs != NULL && rhs == NULL)
2124 return NSOrderedDescending;
2125 else if (lhs != NULL && rhs != NULL) {
2126 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2127 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2130 return NSOrderedSame;
2133 - (uint32_t) compareForChanges {
2138 uint32_t timestamp : 30;
2139 uint32_t ignored : 1;
2140 uint32_t upgradable : 1;
2144 bool upgradable([self upgradableAndEssential:YES]);
2145 value.bits.upgradable = upgradable ? 1 : 0;
2148 value.bits.timestamp = 0;
2149 value.bits.ignored = [self ignored] ? 0 : 1;
2150 value.bits.upgradable = 1;
2152 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2153 value.bits.ignored = 0;
2154 value.bits.upgradable = 0;
2157 return _not(uint32_t) - value.key;
2161 pkgProblemResolver *resolver = [database_ resolver];
2162 resolver->Clear(iterator_);
2163 resolver->Protect(iterator_);
2164 pkgCacheFile &cache([database_ cache]);
2165 cache->MarkInstall(iterator_, false);
2166 pkgDepCache::StateCache &state((*cache)[iterator_]);
2167 if (!state.Install())
2168 cache->SetReInstall(iterator_, true);
2172 pkgProblemResolver *resolver = [database_ resolver];
2173 resolver->Clear(iterator_);
2174 resolver->Protect(iterator_);
2175 resolver->Remove(iterator_);
2176 [database_ cache]->MarkDelete(iterator_, true);
2179 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
2180 _profile(Package$isUnfilteredAndSearchedForBy)
2183 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2184 value &= [self unfiltered];
2187 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2188 value &= [self matches:search];
2195 - (bool) isInstalledAndVisible:(NSNumber *)number {
2196 return (![number boolValue] || [self visible]) && [self installed] != nil;
2199 - (bool) isVisiblyUninstalledInSection:(NSString *)name {
2200 NSString *section = [self section];
2204 [self installed] == nil && (
2206 section == nil && [name length] == 0 ||
2207 [name isEqualToString:section]
2211 - (bool) isVisibleInSource:(Source *)source {
2212 return [self source] == source && [self visible];
2217 /* Section Class {{{ */
2218 @interface Section : NSObject {
2225 - (NSComparisonResult) compareByName:(Section *)section;
2226 - (Section *) initWithName:(NSString *)name;
2227 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2228 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2229 - (NSString *) name;
2233 - (void) addToCount;
2237 @implementation Section
2244 - (NSComparisonResult) compareByName:(Section *)section {
2245 NSString *lhs = [self name];
2246 NSString *rhs = [section name];
2248 if ([lhs length] != 0 && [rhs length] != 0) {
2249 unichar lhc = [lhs characterAtIndex:0];
2250 unichar rhc = [rhs characterAtIndex:0];
2252 if (isalpha(lhc) && !isalpha(rhc))
2253 return NSOrderedAscending;
2254 else if (!isalpha(lhc) && isalpha(rhc))
2255 return NSOrderedDescending;
2258 return [lhs compare:rhs options:LaxCompareOptions_];
2261 - (Section *) initWithName:(NSString *)name {
2262 return [self initWithName:name row:0];
2265 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2266 if ((self = [super init]) != nil) {
2267 name_ = [name retain];
2273 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2274 if ((self = [super init]) != nil) {
2275 name_ = [[NSString stringWithCharacters:&index length:1] retain];
2281 - (NSString *) name {
2297 - (void) addToCount {
2305 static NSArray *Finishes_;
2307 /* Database Implementation {{{ */
2308 @implementation Database
2310 + (Database *) sharedInstance {
2311 static Database *instance;
2312 if (instance == nil)
2313 instance = [[Database alloc] init];
2322 - (void) _readCydia:(NSNumber *)fd { _pooled
2323 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2324 std::istream is(&ib);
2327 static Pcre finish_r("^finish:([^:]*)$");
2329 while (std::getline(is, line)) {
2330 const char *data(line.c_str());
2331 size_t size = line.size();
2332 lprintf("C:%s\n", data);
2334 if (finish_r(data, size)) {
2335 NSString *finish = finish_r[1];
2336 int index = [Finishes_ indexOfObject:finish];
2337 if (index != INT_MAX && index > Finish_)
2345 - (void) _readStatus:(NSNumber *)fd { _pooled
2346 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2347 std::istream is(&ib);
2350 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2351 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2353 while (std::getline(is, line)) {
2354 const char *data(line.c_str());
2355 size_t size = line.size();
2356 lprintf("S:%s\n", data);
2358 if (conffile_r(data, size)) {
2359 [delegate_ setConfigurationData:conffile_r[1]];
2360 } else if (strncmp(data, "status: ", 8) == 0) {
2361 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2362 [delegate_ setProgressTitle:string];
2363 } else if (pmstatus_r(data, size)) {
2364 std::string type([pmstatus_r[1] UTF8String]);
2365 NSString *id = pmstatus_r[2];
2367 float percent([pmstatus_r[3] floatValue]);
2368 [delegate_ setProgressPercent:(percent / 100)];
2370 NSString *string = pmstatus_r[4];
2372 if (type == "pmerror")
2373 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2374 withObject:[NSArray arrayWithObjects:string, id, nil]
2377 else if (type == "pmstatus") {
2378 [delegate_ setProgressTitle:string];
2379 } else if (type == "pmconffile")
2380 [delegate_ setConfigurationData:string];
2381 else _assert(false);
2382 } else _assert(false);
2388 - (void) _readOutput:(NSNumber *)fd { _pooled
2389 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2390 std::istream is(&ib);
2393 while (std::getline(is, line)) {
2394 lprintf("O:%s\n", line.c_str());
2395 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2405 - (Package *) packageWithName:(NSString *)name {
2406 if (static_cast<pkgDepCache *>(cache_) == NULL)
2408 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2409 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2412 - (Database *) init {
2413 if ((self = [super init]) != nil) {
2420 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2421 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2425 _assert(pipe(fds) != -1);
2428 _config->Set("APT::Keep-Fds::", cydiafd_);
2429 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2432 detachNewThreadSelector:@selector(_readCydia:)
2434 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2437 _assert(pipe(fds) != -1);
2441 detachNewThreadSelector:@selector(_readStatus:)
2443 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2446 _assert(pipe(fds) != -1);
2447 _assert(dup2(fds[0], 0) != -1);
2448 _assert(close(fds[0]) != -1);
2450 input_ = fdopen(fds[1], "a");
2452 _assert(pipe(fds) != -1);
2453 _assert(dup2(fds[1], 1) != -1);
2454 _assert(close(fds[1]) != -1);
2457 detachNewThreadSelector:@selector(_readOutput:)
2459 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2464 - (pkgCacheFile &) cache {
2468 - (pkgDepCache::Policy *) policy {
2472 - (pkgRecords *) records {
2476 - (pkgProblemResolver *) resolver {
2480 - (pkgAcquire &) fetcher {
2484 - (pkgSourceList &) list {
2488 - (NSArray *) packages {
2492 - (NSArray *) sources {
2493 return [sources_ allValues];
2496 - (NSArray *) issues {
2497 if (cache_->BrokenCount() == 0)
2500 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2502 for (Package *package in packages_) {
2503 if (![package broken])
2505 pkgCache::PkgIterator pkg([package iterator]);
2507 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2508 [entry addObject:[package name]];
2509 [issues addObject:entry];
2511 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2515 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2516 pkgCache::DepIterator start;
2517 pkgCache::DepIterator end;
2518 dep.GlobOr(start, end); // ++dep
2520 if (!cache_->IsImportantDep(end))
2522 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2525 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2526 [entry addObject:failure];
2527 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2529 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2530 [failure addObject:[package name]];
2532 pkgCache::PkgIterator target(start.TargetPkg());
2533 if (target->ProvidesList != 0)
2534 [failure addObject:@"?"];
2536 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2538 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2539 else if (!cache_[target].CandidateVerIter(cache_).end())
2540 [failure addObject:@"-"];
2541 else if (target->ProvidesList == 0)
2542 [failure addObject:@"!"];
2544 [failure addObject:@"%"];
2548 if (start.TargetVer() != 0)
2549 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2560 - (void) reloadData { _pooled
2580 if (!cache_.Open(progress_, true)) {
2582 if (!_error->PopMessage(error))
2585 lprintf("cache_.Open():[%s]\n", error.c_str());
2587 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2588 [delegate_ repairWithSelector:@selector(configure)];
2589 else if (error == "The package lists or status file could not be parsed or opened.")
2590 [delegate_ repairWithSelector:@selector(update)];
2591 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2592 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2593 // else if (error == "The list of sources could not be read.")
2594 else _assert(false);
2600 now_ = [[NSDate date] retain];
2602 policy_ = new pkgDepCache::Policy();
2603 records_ = new pkgRecords(cache_);
2604 resolver_ = new pkgProblemResolver(cache_);
2605 fetcher_ = new pkgAcquire(&status_);
2608 list_ = new pkgSourceList();
2609 _assert(list_->ReadMainList());
2611 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2612 _assert(pkgApplyStatus(cache_));
2614 if (cache_->BrokenCount() != 0) {
2615 _assert(pkgFixBroken(cache_));
2616 _assert(cache_->BrokenCount() == 0);
2617 _assert(pkgMinimizeUpgrade(cache_));
2620 [sources_ removeAllObjects];
2621 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2622 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2623 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2625 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2626 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2630 [packages_ removeAllObjects];
2632 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2633 if (Package *package = [Package packageWithIterator:iterator database:self])
2634 [packages_ addObject:package];
2636 [packages_ sortUsingSelector:@selector(compareByName:)];
2639 _config->Set("Acquire::http::Timeout", 15);
2640 _config->Set("Acquire::http::MaxParallel", 4);
2643 - (void) configure {
2644 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2645 system([dpkg UTF8String]);
2653 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2654 _assert(!_error->PendingError());
2657 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2660 public pkgArchiveCleaner
2663 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2668 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2670 while (_error->PopMessage(error))
2671 lprintf("ArchiveCleaner: %s\n", error.c_str());
2676 pkgRecords records(cache_);
2678 lock_ = new FileFd();
2679 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2680 _assert(!_error->PendingError());
2683 // XXX: explain this with an error message
2684 _assert(list.ReadMainList());
2686 manager_ = (_system->CreatePM(cache_));
2687 _assert(manager_->GetArchives(fetcher_, &list, &records));
2688 _assert(!_error->PendingError());
2692 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2694 _assert(list.ReadMainList());
2695 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2696 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2699 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2704 bool failed = false;
2705 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2706 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2709 std::string uri = (*item)->DescURI();
2710 std::string error = (*item)->ErrorText;
2712 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2715 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2716 withObject:[NSArray arrayWithObjects:
2717 [NSString stringWithUTF8String:error.c_str()],
2729 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2731 if (_error->PendingError()) {
2736 if (result == pkgPackageManager::Failed) {
2741 if (result != pkgPackageManager::Completed) {
2746 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2748 _assert(list.ReadMainList());
2749 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2750 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2753 if (![before isEqualToArray:after])
2758 _assert(pkgDistUpgrade(cache_));
2762 [self updateWithStatus:status_];
2765 - (void) updateWithStatus:(Status &)status {
2767 _assert(list.ReadMainList());
2770 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2771 _assert(!_error->PendingError());
2773 pkgAcquire fetcher(&status);
2774 _assert(list.GetIndexes(&fetcher));
2776 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2777 bool failed = false;
2778 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2779 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2780 (*item)->Finished();
2784 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2785 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2786 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2789 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2794 - (void) setDelegate:(id)delegate {
2795 delegate_ = delegate;
2796 status_.setDelegate(delegate);
2797 progress_.setDelegate(delegate);
2800 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2801 pkgIndexFile *index(NULL);
2802 list_->FindIndex(file, index);
2803 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2809 /* PopUp Windows {{{ */
2810 @interface PopUpView : UIView {
2811 _transient id delegate_;
2812 UITransitionView *transition_;
2817 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2821 @implementation PopUpView
2824 [transition_ setDelegate:nil];
2825 [transition_ release];
2831 [transition_ transition:UITransitionPushFromTop toView:nil];
2834 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2835 if (from != nil && to == nil)
2836 [self removeFromSuperview];
2839 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2840 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2841 delegate_ = delegate;
2843 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2844 [self addSubview:transition_];
2846 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2848 [view addSubview:self];
2850 [transition_ setDelegate:self];
2852 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2853 [transition_ transition:UITransitionNone toView:blank];
2854 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2861 /* Mail Composition {{{ */
2862 @interface MailToView : PopUpView {
2863 MailComposeController *controller_;
2866 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2870 @implementation MailToView
2873 [controller_ release];
2877 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2881 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2882 NSLog(@"did:%@", delivery);
2883 // [UIApp setStatusBarShowsProgress:NO];
2884 if ([controller error]){
2885 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2886 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2887 [mailAlertSheet setBodyText:[controller error]];
2888 [mailAlertSheet popupAlertAnimated:YES];
2892 - (void) showError {
2893 NSLog(@"%@", [controller_ error]);
2894 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2895 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2896 [mailAlertSheet setBodyText:[controller_ error]];
2897 [mailAlertSheet popupAlertAnimated:YES];
2900 - (void) deliverMessage { _pooled
2904 if (![controller_ deliverMessage])
2905 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2908 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2909 if ([controller_ needsDelivery])
2910 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2915 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2916 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2917 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2918 [controller_ setDelegate:self];
2919 [controller_ initializeUI];
2920 [controller_ setupForURL:url];
2922 UIView *view([controller_ view]);
2923 [overlay_ addSubview:view];
2929 /* Confirmation View {{{ */
2930 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2931 if (!iterator.end())
2932 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2933 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2935 pkgCache::PkgIterator package(dep.TargetPkg());
2938 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2945 @protocol ConfirmationViewDelegate
2950 @interface ConfirmationView : BrowserView {
2951 _transient Database *database_;
2952 UIActionSheet *essential_;
2959 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2963 @implementation ConfirmationView
2970 if (essential_ != nil)
2971 [essential_ release];
2977 [book_ popFromSuperviewAnimated:YES];
2980 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2981 NSString *context([sheet context]);
2983 if ([context isEqualToString:@"remove"]) {
2991 [delegate_ confirm];
2998 } else if ([context isEqualToString:@"unable"]) {
3002 [super alertSheet:sheet buttonClicked:button];
3005 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3006 [window setValue:changes_ forKey:@"changes"];
3007 [window setValue:issues_ forKey:@"issues"];
3008 [window setValue:sizes_ forKey:@"sizes"];
3009 [super webView:sender didClearWindowObject:window forFrame:frame];
3012 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3013 if ((self = [super initWithBook:book]) != nil) {
3014 database_ = database;
3016 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
3017 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
3018 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
3019 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
3020 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
3024 pkgDepCache::Policy *policy([database_ policy]);
3026 pkgCacheFile &cache([database_ cache]);
3027 NSArray *packages = [database_ packages];
3028 for (Package *package in packages) {
3029 pkgCache::PkgIterator iterator = [package iterator];
3030 pkgDepCache::StateCache &state(cache[iterator]);
3032 NSString *name([package name]);
3034 if (state.NewInstall())
3035 [installing addObject:name];
3036 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
3037 [reinstalling addObject:name];
3038 else if (state.Upgrade())
3039 [upgrading addObject:name];
3040 else if (state.Downgrade())
3041 [downgrading addObject:name];
3042 else if (state.Delete()) {
3043 if ([package essential])
3045 [removing addObject:name];
3048 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3049 substrate_ |= DepSubstrate(iterator.CurrentVer());
3054 else if (Advanced_ || true) {
3055 essential_ = [[UIActionSheet alloc]
3056 initWithTitle:@"Removing Essentials"
3057 buttons:[NSArray arrayWithObjects:
3058 @"Cancel Operation (Safe)",
3059 @"Force Removal (Unsafe)",
3061 defaultButtonIndex:0
3067 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3069 [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."];
3071 essential_ = [[UIActionSheet alloc]
3072 initWithTitle:@"Unable to Comply"
3073 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3074 defaultButtonIndex:0
3079 [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."];
3082 changes_ = [[NSArray alloc] initWithObjects:
3090 issues_ = [database_ issues];
3092 issues_ = [issues_ retain];
3094 sizes_ = [[NSArray alloc] initWithObjects:
3095 SizeString([database_ fetcher].FetchNeeded()),
3096 SizeString([database_ fetcher].PartialPresent()),
3097 SizeString([database_ cache]->UsrSize()),
3100 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3104 - (NSString *) backButtonTitle {
3108 - (NSString *) leftButtonTitle {
3112 - (id) _rightButtonTitle {
3113 #if AlwaysReload || IgnoreInstall
3116 return issues_ == nil ? @"Confirm" : nil;
3120 - (void) _leftButtonClicked {
3125 - (void) _rightButtonClicked {
3127 return [super _rightButtonClicked];
3129 if (essential_ != nil)
3130 [essential_ popupAlertAnimated:YES];
3134 [delegate_ confirm];
3142 /* Progress Data {{{ */
3143 @interface ProgressData : NSObject {
3149 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3156 @implementation ProgressData
3158 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3159 if ((self = [super init]) != nil) {
3160 selector_ = selector;
3180 /* Progress View {{{ */
3181 @interface ProgressView : UIView <
3182 ConfigurationDelegate,
3185 _transient Database *database_;
3187 UIView *background_;
3188 UITransitionView *transition_;
3190 UINavigationBar *navbar_;
3191 UIProgressBar *progress_;
3192 UITextView *output_;
3193 UITextLabel *status_;
3194 UIPushButton *close_;
3197 SHA1SumValue springlist_;
3198 SHA1SumValue notifyconf_;
3199 SHA1SumValue sandplate_;
3202 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3204 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3205 - (void) setContentView:(UIView *)view;
3208 - (void) _retachThread;
3209 - (void) _detachNewThreadData:(ProgressData *)data;
3210 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3216 @protocol ProgressViewDelegate
3217 - (void) progressViewIsComplete:(ProgressView *)sender;
3220 @implementation ProgressView
3223 [transition_ setDelegate:nil];
3224 [navbar_ setDelegate:nil];
3227 if (background_ != nil)
3228 [background_ release];
3229 [transition_ release];
3232 [progress_ release];
3239 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3240 if (bootstrap_ && from == overlay_ && to == view_)
3244 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3245 if ((self = [super initWithFrame:frame]) != nil) {
3246 database_ = database;
3247 delegate_ = delegate;
3249 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3250 [transition_ setDelegate:self];
3252 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3255 [overlay_ setBackgroundColor:[UIColor blackColor]];
3257 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3258 [background_ setBackgroundColor:[UIColor blackColor]];
3259 [self addSubview:background_];
3262 [self addSubview:transition_];
3264 CGSize navsize = [UINavigationBar defaultSize];
3265 CGRect navrect = {{0, 0}, navsize};
3267 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3268 [overlay_ addSubview:navbar_];
3270 [navbar_ setBarStyle:1];
3271 [navbar_ setDelegate:self];
3273 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3274 [navbar_ pushNavigationItem:navitem];
3276 CGRect bounds = [overlay_ bounds];
3277 CGSize prgsize = [UIProgressBar defaultSize];
3280 (bounds.size.width - prgsize.width) / 2,
3281 bounds.size.height - prgsize.height - 20
3284 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3285 [progress_ setStyle:0];
3287 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3289 bounds.size.height - prgsize.height - 50,
3290 bounds.size.width - 20,
3294 [status_ setColor:[UIColor whiteColor]];
3295 [status_ setBackgroundColor:[UIColor clearColor]];
3297 [status_ setCentersHorizontally:YES];
3298 //[status_ setFont:font];
3301 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3303 navrect.size.height + 20,
3304 bounds.size.width - 20,
3305 bounds.size.height - navsize.height - 62 - navrect.size.height
3309 //[output_ setTextFont:@"Courier New"];
3310 [output_ setTextSize:12];
3312 [output_ setTextColor:[UIColor whiteColor]];
3313 [output_ setBackgroundColor:[UIColor clearColor]];
3315 [output_ setMarginTop:0];
3316 [output_ setAllowsRubberBanding:YES];
3317 [output_ setEditable:NO];
3319 [overlay_ addSubview:output_];
3321 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3323 bounds.size.height - prgsize.height - 50,
3324 bounds.size.width - 20,
3328 [close_ setAutosizesToFit:NO];
3329 [close_ setDrawsShadow:YES];
3330 [close_ setStretchBackground:YES];
3331 [close_ setEnabled:YES];
3333 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3334 [close_ setTitleFont:bold];
3336 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3337 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3338 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3342 - (void) setContentView:(UIView *)view {
3343 view_ = [view retain];
3346 - (void) resetView {
3347 [transition_ transition:6 toView:view_];
3350 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3351 NSString *context([sheet context]);
3353 if ([context isEqualToString:@"error"])
3355 else if ([context isEqualToString:@"conffile"]) {
3356 FILE *input = [database_ input];
3360 fprintf(input, "N\n");
3364 fprintf(input, "Y\n");
3375 - (void) closeButtonPushed {
3384 [delegate_ suspendWithAnimation:YES];
3388 system("launchctl stop com.apple.SpringBoard");
3392 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3401 - (void) _retachThread {
3402 UINavigationItem *item = [navbar_ topItem];
3403 [item setTitle:@"Complete"];
3405 [overlay_ addSubview:close_];
3406 [progress_ removeFromSuperview];
3407 [status_ removeFromSuperview];
3409 [delegate_ progressViewIsComplete:self];
3412 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3413 MMap mmap(file, MMap::ReadOnly);
3415 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3416 if (!(sandplate_ == sha1.Result()))
3421 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3422 MMap mmap(file, MMap::ReadOnly);
3424 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3425 if (!(notifyconf_ == sha1.Result()))
3430 FileFd file(SpringBoard_, FileFd::ReadOnly);
3431 MMap mmap(file, MMap::ReadOnly);
3433 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3434 if (!(springlist_ == sha1.Result()))
3439 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3440 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3441 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3442 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3443 case 4: [close_ setTitle:@"Reboot Device"]; break;
3446 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3448 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3449 [cache autorelease];
3451 NSFileManager *manager = [NSFileManager defaultManager];
3452 NSError *error = nil;
3454 id system = [cache objectForKey:@"System"];
3459 if (stat(Cache_, &info) == -1)
3462 [system removeAllObjects];
3464 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3465 for (NSString *app in apps)
3466 if ([app hasSuffix:@".app"]) {
3467 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3468 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3469 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3471 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3472 [info setObject:path forKey:@"Path"];
3473 [info setObject:@"System" forKey:@"ApplicationType"];
3474 [system addInfoDictionary:info];
3480 [cache writeToFile:@Cache_ atomically:YES];
3482 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3484 if (chmod(Cache_, info.st_mode) == -1)
3488 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3491 notify_post("com.apple.mobile.application_installed");
3493 [delegate_ setStatusBarShowsProgress:NO];
3496 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3497 [[data target] performSelector:[data selector] withObject:[data object]];
3500 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3503 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3504 UINavigationItem *item = [navbar_ topItem];
3505 [item setTitle:title];
3507 [status_ setText:nil];
3508 [output_ setText:@""];
3509 [progress_ setProgress:0];
3511 [close_ removeFromSuperview];
3512 [overlay_ addSubview:progress_];
3513 [overlay_ addSubview:status_];
3515 [delegate_ setStatusBarShowsProgress:YES];
3519 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3520 MMap mmap(file, MMap::ReadOnly);
3522 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3523 sandplate_ = sha1.Result();
3527 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3528 MMap mmap(file, MMap::ReadOnly);
3530 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3531 notifyconf_ = sha1.Result();
3535 FileFd file(SpringBoard_, FileFd::ReadOnly);
3536 MMap mmap(file, MMap::ReadOnly);
3538 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3539 springlist_ = sha1.Result();
3542 [transition_ transition:6 toView:overlay_];
3545 detachNewThreadSelector:@selector(_detachNewThreadData:)
3547 withObject:[[ProgressData alloc]
3548 initWithSelector:selector
3555 - (void) repairWithSelector:(SEL)selector {
3557 detachNewThreadSelector:selector
3564 - (void) setConfigurationData:(NSString *)data {
3566 performSelectorOnMainThread:@selector(_setConfigurationData:)
3572 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3573 Package *package = id == nil ? nil : [database_ packageWithName:id];
3575 UIActionSheet *sheet = [[[UIActionSheet alloc]
3576 initWithTitle:(package == nil ? id : [package name])
3577 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3578 defaultButtonIndex:0
3583 [sheet setBodyText:error];
3584 [sheet popupAlertAnimated:YES];
3587 - (void) setProgressTitle:(NSString *)title {
3589 performSelectorOnMainThread:@selector(_setProgressTitle:)
3595 - (void) setProgressPercent:(float)percent {
3597 performSelectorOnMainThread:@selector(_setProgressPercent:)
3598 withObject:[NSNumber numberWithFloat:percent]
3603 - (void) startProgress {
3606 - (void) addProgressOutput:(NSString *)output {
3608 performSelectorOnMainThread:@selector(_addProgressOutput:)
3614 - (bool) isCancelling:(size_t)received {
3618 - (void) _setConfigurationData:(NSString *)data {
3619 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3621 _assert(conffile_r(data));
3623 NSString *ofile = conffile_r[1];
3624 //NSString *nfile = conffile_r[2];
3626 UIActionSheet *sheet = [[[UIActionSheet alloc]
3627 initWithTitle:@"Configuration Upgrade"
3628 buttons:[NSArray arrayWithObjects:
3629 @"Keep My Old Copy",
3630 @"Accept The New Copy",
3631 // XXX: @"See What Changed",
3633 defaultButtonIndex:0
3638 [sheet setBodyText:[NSString stringWithFormat:
3639 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3642 [sheet popupAlertAnimated:YES];
3645 - (void) _setProgressTitle:(NSString *)title {
3646 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3647 for (size_t i(0), e([words count]); i != e; ++i) {
3648 NSString *word([words objectAtIndex:i]);
3649 if (Package *package = [database_ packageWithName:word])
3650 [words replaceObjectAtIndex:i withObject:[package name]];
3653 [status_ setText:[words componentsJoinedByString:@" "]];
3656 - (void) _setProgressPercent:(NSNumber *)percent {
3657 [progress_ setProgress:[percent floatValue]];
3660 - (void) _addProgressOutput:(NSString *)output {
3661 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3662 CGSize size = [output_ contentSize];
3663 CGRect rect = {{0, size.height}, {size.width, 0}};
3664 [output_ scrollRectToVisible:rect animated:YES];
3667 - (BOOL) isRunning {
3674 /* Package Cell {{{ */
3675 @interface PackageCell : UISimpleTableCell {
3678 NSString *description_;
3682 UITextLabel *status_;
3686 - (PackageCell *) init;
3687 - (void) setPackage:(Package *)package;
3689 + (int) heightForPackage:(Package *)package;
3693 @implementation PackageCell
3695 - (void) clearPackage {
3706 if (description_ != nil) {
3707 [description_ release];
3711 if (source_ != nil) {
3716 if (badge_ != nil) {
3723 [self clearPackage];
3730 - (PackageCell *) init {
3731 if ((self = [super init]) != nil) {
3733 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3734 [status_ setBackgroundColor:[UIColor clearColor]];
3735 [status_ setFont:small];
3740 - (void) setPackage:(Package *)package {
3741 [self clearPackage];
3743 Source *source = [package source];
3744 NSString *section = [package simpleSection];
3746 icon_ = [[package icon] retain];
3748 name_ = [[package name] retain];
3749 description_ = [[package tagline] retain];
3751 NSString *label = nil;
3752 bool trusted = false;
3754 if (source != nil) {
3755 label = [source label];
3756 trusted = [source trusted];
3757 } else if ([[package id] isEqualToString:@"firmware"])
3760 label = @"Unknown/Local";
3762 NSString *from = [NSString stringWithFormat:@"from %@", label];
3764 if (section != nil && ![section isEqualToString:label])
3765 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3767 source_ = [from retain];
3769 if (NSString *purpose = [package primaryPurpose])
3770 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3771 badge_ = [badge_ retain];
3774 if (NSString *mode = [package mode]) {
3775 [badge_ setImage:[UIImage applicationImageNamed:
3776 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3779 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3780 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3781 } else if ([package half]) {
3782 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3783 [status_ setText:@"Package Damaged"];
3784 [status_ setColor:[UIColor redColor]];
3786 [badge_ setImage:nil];
3787 [status_ setText:nil];
3792 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3795 rect.size = [icon_ size];
3797 rect.size.width /= 2;
3798 rect.size.height /= 2;
3800 rect.origin.x = 25 - rect.size.width / 2;
3801 rect.origin.y = 25 - rect.size.height / 2;
3803 [icon_ drawInRect:rect];
3806 if (badge_ != nil) {
3807 CGSize size = [badge_ size];
3809 [badge_ drawAtPoint:CGPointMake(
3810 36 - size.width / 2,
3811 36 - size.height / 2
3820 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3821 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3825 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3827 [super drawContentInRect:rect selected:selected];
3830 + (int) heightForPackage:(Package *)package {
3831 NSString *tagline([package tagline]);
3832 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3834 if ([package hasMode] || [package half])
3843 /* Section Cell {{{ */
3844 @interface SectionCell : UISimpleTableCell {
3849 _UISwitchSlider *switch_;
3854 - (void) setSection:(Section *)section editing:(BOOL)editing;
3858 @implementation SectionCell
3860 - (void) clearSection {
3861 if (section_ != nil) {
3871 if (count_ != nil) {
3878 [self clearSection];
3885 if ((self = [super init]) != nil) {
3886 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3888 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3889 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3893 - (void) onSwitch:(id)sender {
3894 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3895 if (metadata == nil) {
3896 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3897 [Sections_ setObject:metadata forKey:section_];
3901 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3904 - (void) setSection:(Section *)section editing:(BOOL)editing {
3905 if (editing != editing_) {
3907 [switch_ removeFromSuperview];
3909 [self addSubview:switch_];
3913 [self clearSection];
3915 if (section == nil) {
3916 name_ = [@"All Packages" retain];
3919 section_ = [section name];
3920 if (section_ != nil)
3921 section_ = [section_ retain];
3922 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3923 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3926 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3930 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3931 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3938 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3940 CGSize size = [count_ sizeWithFont:Font14_];
3944 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3946 [super drawContentInRect:rect selected:selected];
3952 /* File Table {{{ */
3953 @interface FileTable : RVPage {
3954 _transient Database *database_;
3957 NSMutableArray *files_;
3961 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3962 - (void) setPackage:(Package *)package;
3966 @implementation FileTable
3969 if (package_ != nil)
3978 - (int) numberOfRowsInTable:(UITable *)table {
3979 return files_ == nil ? 0 : [files_ count];
3982 - (float) table:(UITable *)table heightForRow:(int)row {
3986 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3987 if (reusing == nil) {
3988 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3989 UIFont *font = [UIFont systemFontOfSize:16];
3990 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3992 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3996 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4000 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4001 if ((self = [super initWithBook:book]) != nil) {
4002 database_ = database;
4004 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
4006 list_ = [[UITable alloc] initWithFrame:[self bounds]];
4007 [self addSubview:list_];
4009 UITableColumn *column = [[[UITableColumn alloc]
4010 initWithTitle:@"Name"
4012 width:[self frame].size.width
4015 [list_ setDataSource:self];
4016 [list_ setSeparatorStyle:1];
4017 [list_ addTableColumn:column];
4018 [list_ setDelegate:self];
4019 [list_ setReusesTableCells:YES];
4023 - (void) setPackage:(Package *)package {
4024 if (package_ != nil) {
4025 [package_ autorelease];
4034 [files_ removeAllObjects];
4036 if (package != nil) {
4037 package_ = [package retain];
4038 name_ = [[package id] retain];
4040 if (NSArray *files = [package files])
4041 [files_ addObjectsFromArray:files];
4043 if ([files_ count] != 0) {
4044 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4045 [files_ removeObjectAtIndex:0];
4046 [files_ sortUsingSelector:@selector(compareByPath:)];
4048 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4049 [stack addObject:@"/"];
4051 for (int i(0), e([files_ count]); i != e; ++i) {
4052 NSString *file = [files_ objectAtIndex:i];
4053 while (![file hasPrefix:[stack lastObject]])
4054 [stack removeLastObject];
4055 NSString *directory = [stack lastObject];
4056 [stack addObject:[file stringByAppendingString:@"/"]];
4057 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4058 ([stack count] - 2) * 3, "",
4059 [file substringFromIndex:[directory length]]
4068 - (void) resetViewAnimated:(BOOL)animated {
4069 [list_ resetViewAnimated:animated];
4072 - (void) reloadData {
4073 [self setPackage:[database_ packageWithName:name_]];
4074 [self reloadButtons];
4077 - (NSString *) title {
4078 return @"Installed Files";
4081 - (NSString *) backButtonTitle {
4087 /* Package View {{{ */
4088 @interface PackageView : BrowserView {
4089 _transient Database *database_;
4092 NSMutableArray *buttons_;
4095 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4096 - (void) setPackage:(Package *)package;
4100 @implementation PackageView
4103 if (package_ != nil)
4111 - (void) _clickButtonWithName:(NSString *)name {
4112 if ([name isEqualToString:@"Install"])
4113 [delegate_ installPackage:package_];
4114 else if ([name isEqualToString:@"Reinstall"])
4115 [delegate_ installPackage:package_];
4116 else if ([name isEqualToString:@"Remove"])
4117 [delegate_ removePackage:package_];
4118 else if ([name isEqualToString:@"Upgrade"])
4119 [delegate_ installPackage:package_];
4120 else _assert(false);
4123 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4124 NSString *context([sheet context]);
4126 if ([context isEqualToString:@"modify"]) {
4127 int count = [buttons_ count];
4128 _assert(count != 0);
4129 _assert(button <= count + 1);
4131 if (count != button - 1)
4132 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4136 [super alertSheet:sheet buttonClicked:button];
4139 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4140 return [super webView:sender didFinishLoadForFrame:frame];
4143 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4144 [window setValue:package_ forKey:@"package"];
4145 [super webView:sender didClearWindowObject:window forFrame:frame];
4149 - (void) _rightButtonClicked {
4150 /*[super _rightButtonClicked];
4153 int count = [buttons_ count];
4154 _assert(count != 0);
4157 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4159 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4160 [buttons addObjectsFromArray:buttons_];
4161 [buttons addObject:@"Cancel"];
4163 [delegate_ slideUp:[[[UIActionSheet alloc]
4166 defaultButtonIndex:2
4174 - (id) _rightButtonTitle {
4175 int count = [buttons_ count];
4176 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4179 - (NSString *) backButtonTitle {
4183 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4184 if ((self = [super initWithBook:book]) != nil) {
4185 database_ = database;
4186 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4190 - (void) setPackage:(Package *)package {
4191 if (package_ != nil) {
4192 [package_ autorelease];
4201 [buttons_ removeAllObjects];
4203 if (package != nil) {
4204 package_ = [package retain];
4205 name_ = [[package id] retain];
4207 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4209 if ([package_ source] == nil);
4210 else if ([package_ upgradableAndEssential:NO])
4211 [buttons_ addObject:@"Upgrade"];
4212 else if ([package_ installed] == nil)
4213 [buttons_ addObject:@"Install"];
4215 [buttons_ addObject:@"Reinstall"];
4216 if ([package_ installed] != nil)
4217 [buttons_ addObject:@"Remove"];
4225 - (void) reloadData {
4226 [self setPackage:[database_ packageWithName:name_]];
4227 [self reloadButtons];
4232 /* Package Table {{{ */
4233 @interface PackageTable : RVPage {
4234 _transient Database *database_;
4236 NSMutableArray *packages_;
4237 NSMutableArray *sections_;
4238 UISectionList *list_;
4241 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4243 - (void) setDelegate:(id)delegate;
4245 - (void) reloadData;
4246 - (void) resetCursor;
4248 - (UISectionList *) list;
4250 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4254 @implementation PackageTable
4257 [list_ setDataSource:nil];
4260 [packages_ release];
4261 [sections_ release];
4266 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4267 return [sections_ count];
4270 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4271 return [[sections_ objectAtIndex:section] name];
4274 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4275 return [[sections_ objectAtIndex:section] row];
4278 - (int) numberOfRowsInTable:(UITable *)table {
4279 return [packages_ count];
4282 - (float) table:(UITable *)table heightForRow:(int)row {
4283 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4286 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4288 reusing = [[[PackageCell alloc] init] autorelease];
4289 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4293 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4297 - (void) tableRowSelected:(NSNotification *)notification {
4298 int row = [[notification object] selectedRow];
4302 Package *package = [packages_ objectAtIndex:row];
4303 package = [database_ packageWithName:[package id]];
4304 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4305 [view setPackage:package];
4306 [view setDelegate:delegate_];
4307 [book_ pushPage:view];
4310 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4311 if ((self = [super initWithBook:book]) != nil) {
4312 database_ = database;
4313 title_ = [title retain];
4315 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4316 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4318 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4319 [list_ setDataSource:self];
4321 UITableColumn *column = [[[UITableColumn alloc]
4322 initWithTitle:@"Name"
4324 width:[self frame].size.width
4327 UITable *table = [list_ table];
4328 [table setSeparatorStyle:1];
4329 [table addTableColumn:column];
4330 [table setDelegate:self];
4331 [table setReusesTableCells:YES];
4333 [self addSubview:list_];
4335 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4336 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4340 - (void) setDelegate:(id)delegate {
4341 delegate_ = delegate;
4344 - (bool) hasPackage:(Package *)package {
4348 - (void) reloadData {
4349 NSArray *packages = [database_ packages];
4351 [packages_ removeAllObjects];
4352 [sections_ removeAllObjects];
4354 _profile(PackageTable$reloadData$Filter)
4355 for (Package *package in packages)
4356 if ([self hasPackage:package])
4357 [packages_ addObject:package];
4360 Section *section = nil;
4362 _profile(PackageTable$reloadData$Section)
4363 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4367 _profile(PackageTable$reloadData$Section$Package)
4368 package = [packages_ objectAtIndex:offset];
4369 index = [package index];
4372 if (section == nil || [section index] != index) {
4373 _profile(PackageTable$reloadData$Section$Allocate)
4374 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4377 _profile(PackageTable$reloadData$Section$Add)
4378 [sections_ addObject:section];
4382 [section addToCount];
4386 _profile(PackageTable$reloadData$List)
4391 - (NSString *) title {
4395 - (void) resetViewAnimated:(BOOL)animated {
4396 [list_ resetViewAnimated:animated];
4399 - (void) resetCursor {
4400 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4403 - (UISectionList *) list {
4407 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4408 [list_ setShouldHideHeaderInShortLists:hide];
4413 /* Filtered Package Table {{{ */
4414 @interface FilteredPackageTable : PackageTable {
4420 - (void) setObject:(id)object;
4422 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4426 @implementation FilteredPackageTable
4434 - (void) setObject:(id)object {
4440 object_ = [object retain];
4443 - (bool) hasPackage:(Package *)package {
4444 _profile(FilteredPackageTable$hasPackage)
4445 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
4449 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4450 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4452 object_ = object == nil ? nil : [object retain];
4454 /* XXX: this is an unsafe optimization of doomy hell */
4455 Method method = class_getInstanceMethod([Package class], filter);
4456 imp_ = method_getImplementation(method);
4457 _assert(imp_ != NULL);
4466 /* Add Source View {{{ */
4467 @interface AddSourceView : RVPage {
4468 _transient Database *database_;
4471 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4475 @implementation AddSourceView
4477 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4478 if ((self = [super initWithBook:book]) != nil) {
4479 database_ = database;
4485 /* Source Cell {{{ */
4486 @interface SourceCell : UITableCell {
4489 NSString *description_;
4495 - (SourceCell *) initWithSource:(Source *)source;
4499 @implementation SourceCell
4504 [description_ release];
4509 - (SourceCell *) initWithSource:(Source *)source {
4510 if ((self = [super init]) != nil) {
4512 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4514 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4515 icon_ = [icon_ retain];
4517 origin_ = [[source name] retain];
4518 label_ = [[source uri] retain];
4519 description_ = [[source description] retain];
4523 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4525 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4532 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4536 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4540 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4542 [super drawContentInRect:rect selected:selected];
4547 /* Source Table {{{ */
4548 @interface SourceTable : RVPage {
4549 _transient Database *database_;
4550 UISectionList *list_;
4551 NSMutableArray *sources_;
4552 UIActionSheet *alert_;
4556 UIProgressHUD *hud_;
4559 //NSURLConnection *installer_;
4560 NSURLConnection *trivial_bz2_;
4561 NSURLConnection *trivial_gz_;
4562 //NSURLConnection *automatic_;
4567 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4571 @implementation SourceTable
4573 - (void) _deallocConnection:(NSURLConnection *)connection {
4574 if (connection != nil) {
4575 [connection cancel];
4576 //[connection setDelegate:nil];
4577 [connection release];
4582 [[list_ table] setDelegate:nil];
4583 [list_ setDataSource:nil];
4592 //[self _deallocConnection:installer_];
4593 [self _deallocConnection:trivial_gz_];
4594 [self _deallocConnection:trivial_bz2_];
4595 //[self _deallocConnection:automatic_];
4602 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4603 return offset_ == 0 ? 1 : 2;
4606 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4607 switch (section + (offset_ == 0 ? 1 : 0)) {
4608 case 0: return @"Entered by User";
4609 case 1: return @"Installed by Packages";
4617 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4618 switch (section + (offset_ == 0 ? 1 : 0)) {
4620 case 1: return offset_;
4628 - (int) numberOfRowsInTable:(UITable *)table {
4629 return [sources_ count];
4632 - (float) table:(UITable *)table heightForRow:(int)row {
4633 Source *source = [sources_ objectAtIndex:row];
4634 return [source description] == nil ? 56 : 73;
4637 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4638 Source *source = [sources_ objectAtIndex:row];
4639 // XXX: weird warning, stupid selectors ;P
4640 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4643 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4647 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4651 - (void) tableRowSelected:(NSNotification*)notification {
4652 UITable *table([list_ table]);
4653 int row([table selectedRow]);
4657 Source *source = [sources_ objectAtIndex:row];
4659 PackageTable *packages = [[[FilteredPackageTable alloc]
4662 title:[source label]
4663 filter:@selector(isVisibleInSource:)
4667 [packages setDelegate:delegate_];
4669 [book_ pushPage:packages];
4672 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4673 Source *source = [sources_ objectAtIndex:row];
4674 return [source record] != nil;
4677 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4678 [[list_ table] setDeleteConfirmationRow:row];
4681 - (void) table:(UITable *)table deleteRow:(int)row {
4682 Source *source = [sources_ objectAtIndex:row];
4683 [Sources_ removeObjectForKey:[source key]];
4684 [delegate_ syncData];
4688 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4691 @"./", @"Distribution",
4692 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4694 [delegate_ syncData];
4697 - (NSString *) getWarning {
4698 NSString *href([href_ stringByAddingPercentEscapesIncludingReserved]);
4699 href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href];
4700 href = [href stringByCachingURLWithCurrentCDN];
4702 NSURL *url([NSURL URLWithString:href]);
4704 NSStringEncoding encoding;
4705 NSError *error(nil);
4707 if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error])
4708 return [warning length] == 0 ? nil : warning;
4712 - (void) _endConnection:(NSURLConnection *)connection {
4713 NSURLConnection **field = NULL;
4714 if (connection == trivial_bz2_)
4715 field = &trivial_bz2_;
4716 else if (connection == trivial_gz_)
4717 field = &trivial_gz_;
4718 _assert(field != NULL);
4719 [connection release];
4723 trivial_bz2_ == nil &&
4726 [delegate_ setStatusBarShowsProgress:NO];
4727 [delegate_ removeProgressHUD:hud_];
4733 if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
4734 UIActionSheet *sheet = [[[UIActionSheet alloc]
4735 initWithTitle:@"Repository Warning"
4736 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4737 defaultButtonIndex:0
4742 [sheet setNumberOfRows:1];
4744 [sheet setBodyText:warning];
4745 [sheet popupAlertAnimated:YES];
4748 } else if (error_ != nil) {
4749 UIActionSheet *sheet = [[[UIActionSheet alloc]
4750 initWithTitle:@"Verification Error"
4751 buttons:[NSArray arrayWithObjects:@"OK", nil]
4752 defaultButtonIndex:0
4757 [sheet setBodyText:[error_ localizedDescription]];
4758 [sheet popupAlertAnimated:YES];
4760 UIActionSheet *sheet = [[[UIActionSheet alloc]
4761 initWithTitle:@"Did not Find Repository"
4762 buttons:[NSArray arrayWithObjects:@"OK", nil]
4763 defaultButtonIndex:0
4768 [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."];
4769 [sheet popupAlertAnimated:YES];
4775 if (error_ != nil) {
4782 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4783 switch ([response statusCode]) {
4789 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4790 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4792 error_ = [error retain];
4793 [self _endConnection:connection];
4796 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4797 [self _endConnection:connection];
4800 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4801 NSMutableURLRequest *request = [NSMutableURLRequest
4802 requestWithURL:[NSURL URLWithString:href]
4803 cachePolicy:NSURLRequestUseProtocolCachePolicy
4804 timeoutInterval:20.0
4807 [request setHTTPMethod:method];
4809 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4812 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4813 NSString *context([sheet context]);
4815 if ([context isEqualToString:@"source"]) {
4818 NSString *href = [[sheet textField] text];
4820 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4822 if (![href hasSuffix:@"/"])
4823 href_ = [href stringByAppendingString:@"/"];
4826 href_ = [href_ retain];
4828 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4829 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4830 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4834 hud_ = [[delegate_ addProgressHUD] retain];
4835 [hud_ setText:@"Verifying URL"];
4846 } else if ([context isEqualToString:@"trivial"])
4848 else if ([context isEqualToString:@"urlerror"])
4850 else if ([context isEqualToString:@"warning"]) {
4867 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4868 if ((self = [super initWithBook:book]) != nil) {
4869 database_ = database;
4870 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4872 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4873 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4874 [list_ setShouldHideHeaderInShortLists:NO];
4876 [self addSubview:list_];
4877 [list_ setDataSource:self];
4879 UITableColumn *column = [[UITableColumn alloc]
4880 initWithTitle:@"Name"
4882 width:[self frame].size.width
4885 UITable *table = [list_ table];
4886 [table setSeparatorStyle:1];
4887 [table addTableColumn:column];
4888 [table setDelegate:self];
4892 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4893 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4897 - (void) reloadData {
4899 _assert(list.ReadMainList());
4901 [sources_ removeAllObjects];
4902 [sources_ addObjectsFromArray:[database_ sources]];
4904 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4907 int count = [sources_ count];
4908 for (offset_ = 0; offset_ != count; ++offset_) {
4909 Source *source = [sources_ objectAtIndex:offset_];
4910 if ([source record] == nil)
4917 - (void) resetViewAnimated:(BOOL)animated {
4918 [list_ resetViewAnimated:animated];
4921 - (void) _leftButtonClicked {
4922 /*[book_ pushPage:[[[AddSourceView alloc]
4927 UIActionSheet *sheet = [[[UIActionSheet alloc]
4928 initWithTitle:@"Enter Cydia/APT URL"
4929 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4930 defaultButtonIndex:0
4935 [sheet setNumberOfRows:1];
4937 [sheet addTextFieldWithValue:@"http://" label:@""];
4939 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4940 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4941 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4942 [traits setKeyboardType:UIKeyboardTypeURL];
4943 // XXX: UIReturnKeyDone
4944 [traits setReturnKeyType:UIReturnKeyNext];
4946 [sheet popupAlertAnimated:YES];
4949 - (void) _rightButtonClicked {
4950 UITable *table = [list_ table];
4951 BOOL editing = [table isRowDeletionEnabled];
4952 [table enableRowDeletion:!editing animated:YES];
4953 [book_ reloadButtonsForPage:self];
4956 - (NSString *) title {
4960 - (NSString *) leftButtonTitle {
4961 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4964 - (id) rightButtonTitle {
4965 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4968 - (UINavigationButtonStyle) rightButtonStyle {
4969 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4975 /* Installed View {{{ */
4976 @interface InstalledView : RVPage {
4977 _transient Database *database_;
4978 FilteredPackageTable *packages_;
4982 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4986 @implementation InstalledView
4989 [packages_ release];
4993 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4994 if ((self = [super initWithBook:book]) != nil) {
4995 database_ = database;
4997 packages_ = [[FilteredPackageTable alloc]
5001 filter:@selector(isInstalledAndVisible:)
5002 with:[NSNumber numberWithBool:YES]
5005 [self addSubview:packages_];
5007 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5008 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5012 - (void) resetViewAnimated:(BOOL)animated {
5013 [packages_ resetViewAnimated:animated];
5016 - (void) reloadData {
5017 [packages_ reloadData];
5020 - (void) _rightButtonClicked {
5021 [packages_ setObject:[NSNumber numberWithBool:expert_]];
5022 [packages_ reloadData];
5024 [book_ reloadButtonsForPage:self];
5027 - (NSString *) title {
5028 return @"Installed";
5031 - (NSString *) backButtonTitle {
5035 - (id) rightButtonTitle {
5036 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
5039 - (UINavigationButtonStyle) rightButtonStyle {
5040 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5043 - (void) setDelegate:(id)delegate {
5044 [super setDelegate:delegate];
5045 [packages_ setDelegate:delegate];
5052 @interface HomeView : BrowserView {
5057 @implementation HomeView
5059 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5060 NSString *context([sheet context]);
5062 if ([context isEqualToString:@"about"])
5065 [super alertSheet:sheet buttonClicked:button];
5068 - (void) _leftButtonClicked {
5069 UIActionSheet *sheet = [[[UIActionSheet alloc]
5070 initWithTitle:@"About Cydia Installer"
5071 buttons:[NSArray arrayWithObjects:@"Close", nil]
5072 defaultButtonIndex:0
5078 @"Copyright (C) 2008\n"
5079 "Jay Freeman (saurik)\n"
5080 "saurik@saurik.com\n"
5081 "http://www.saurik.com/\n"
5084 "http://www.theokorigroup.com/\n"
5086 "College of Creative Studies,\n"
5087 "University of California,\n"
5089 "http://www.ccs.ucsb.edu/"
5092 [sheet popupAlertAnimated:YES];
5095 - (NSString *) leftButtonTitle {
5101 /* Manage View {{{ */
5102 @interface ManageView : BrowserView {
5107 @implementation ManageView
5109 - (NSString *) title {
5113 - (void) _leftButtonClicked {
5114 [delegate_ askForSettings];
5117 - (NSString *) leftButtonTitle {
5122 - (id) _rightButtonTitle {
5134 #include <BrowserView.m>
5136 /* Cydia Book {{{ */
5137 @interface CYBook : RVBook <
5140 _transient Database *database_;
5141 UINavigationBar *overlay_;
5142 UINavigationBar *underlay_;
5143 UIProgressIndicator *indicator_;
5144 UITextLabel *prompt_;
5145 UIProgressBar *progress_;
5146 UINavigationButton *cancel_;
5150 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5156 @implementation CYBook
5160 [indicator_ release];
5162 [progress_ release];
5167 - (NSString *) getTitleForPage:(RVPage *)page {
5168 return Simplify([super getTitleForPage:page]);
5176 [UIView beginAnimations:nil context:NULL];
5178 CGRect ovrframe = [overlay_ frame];
5179 ovrframe.origin.y = 0;
5180 [overlay_ setFrame:ovrframe];
5182 CGRect barframe = [navbar_ frame];
5183 barframe.origin.y += ovrframe.size.height;
5184 [navbar_ setFrame:barframe];
5186 CGRect trnframe = [transition_ frame];
5187 trnframe.origin.y += ovrframe.size.height;
5188 trnframe.size.height -= ovrframe.size.height;
5189 [transition_ setFrame:trnframe];
5191 [UIView endAnimations];
5193 [indicator_ startAnimation];
5194 [prompt_ setText:@"Updating Database"];
5195 [progress_ setProgress:0];
5198 [overlay_ addSubview:cancel_];
5201 detachNewThreadSelector:@selector(_update)
5210 [indicator_ stopAnimation];
5212 [UIView beginAnimations:nil context:NULL];
5214 CGRect ovrframe = [overlay_ frame];
5215 ovrframe.origin.y = -ovrframe.size.height;
5216 [overlay_ setFrame:ovrframe];
5218 CGRect barframe = [navbar_ frame];
5219 barframe.origin.y -= ovrframe.size.height;
5220 [navbar_ setFrame:barframe];
5222 CGRect trnframe = [transition_ frame];
5223 trnframe.origin.y -= ovrframe.size.height;
5224 trnframe.size.height += ovrframe.size.height;
5225 [transition_ setFrame:trnframe];
5227 [UIView commitAnimations];
5229 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5232 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5233 if ((self = [super initWithFrame:frame]) != nil) {
5234 database_ = database;
5236 CGRect ovrrect = [navbar_ bounds];
5237 ovrrect.size.height = [UINavigationBar defaultSize].height;
5238 ovrrect.origin.y = -ovrrect.size.height;
5240 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5241 [self addSubview:overlay_];
5243 ovrrect.origin.y = frame.size.height;
5244 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5245 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5246 [self addSubview:underlay_];
5248 [overlay_ setBarStyle:1];
5249 [underlay_ setBarStyle:1];
5251 int barstyle = [overlay_ _barStyle:NO];
5252 bool ugly = barstyle == 0;
5254 UIProgressIndicatorStyle style = ugly ?
5255 UIProgressIndicatorStyleMediumBrown :
5256 UIProgressIndicatorStyleMediumWhite;
5258 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5259 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5260 CGRect indrect = {{indoffset, indoffset}, indsize};
5262 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5263 [indicator_ setStyle:style];
5264 [overlay_ addSubview:indicator_];
5266 CGSize prmsize = {215, indsize.height + 4};
5269 indoffset * 2 + indsize.width,
5273 unsigned(ovrrect.size.height - prmsize.height) / 2
5276 UIFont *font = [UIFont systemFontOfSize:15];
5278 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5280 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5281 [prompt_ setBackgroundColor:[UIColor clearColor]];
5282 [prompt_ setFont:font];
5284 [overlay_ addSubview:prompt_];
5286 CGSize prgsize = {75, 100};
5289 ovrrect.size.width - prgsize.width - 10,
5290 (ovrrect.size.height - prgsize.height) / 2
5293 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5294 [progress_ setStyle:0];
5295 [overlay_ addSubview:progress_];
5297 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5298 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5300 CGRect frame = [cancel_ frame];
5301 frame.size.width = 65;
5302 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5303 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5304 [cancel_ setFrame:frame];
5306 [cancel_ setBarStyle:barstyle];
5310 - (void) _onCancel {
5312 [cancel_ removeFromSuperview];
5315 - (void) _update { _pooled
5317 status.setDelegate(self);
5319 [database_ updateWithStatus:status];
5322 performSelectorOnMainThread:@selector(_update_)
5328 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5329 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5332 - (void) setProgressTitle:(NSString *)title {
5334 performSelectorOnMainThread:@selector(_setProgressTitle:)
5340 - (void) setProgressPercent:(float)percent {
5342 performSelectorOnMainThread:@selector(_setProgressPercent:)
5343 withObject:[NSNumber numberWithFloat:percent]
5348 - (void) startProgress {
5351 - (void) addProgressOutput:(NSString *)output {
5353 performSelectorOnMainThread:@selector(_addProgressOutput:)
5359 - (bool) isCancelling:(size_t)received {
5363 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5367 - (void) _setProgressTitle:(NSString *)title {
5368 [prompt_ setText:title];
5371 - (void) _setProgressPercent:(NSNumber *)percent {
5372 [progress_ setProgress:[percent floatValue]];
5375 - (void) _addProgressOutput:(NSString *)output {
5380 /* Cydia:// Protocol {{{ */
5381 @interface CydiaURLProtocol : NSURLProtocol {
5386 @implementation CydiaURLProtocol
5388 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5389 NSURL *url([request URL]);
5392 NSString *scheme([[url scheme] lowercaseString]);
5393 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5398 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5402 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5403 id<NSURLProtocolClient> client([self client]);
5405 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5407 NSData *data(UIImagePNGRepresentation(icon));
5409 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5410 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5411 [client URLProtocol:self didLoadData:data];
5412 [client URLProtocolDidFinishLoading:self];
5416 - (void) startLoading {
5417 id<NSURLProtocolClient> client([self client]);
5418 NSURLRequest *request([self request]);
5420 NSURL *url([request URL]);
5421 NSString *href([url absoluteString]);
5423 NSString *path([href substringFromIndex:8]);
5424 NSRange slash([path rangeOfString:@"/"]);
5427 if (slash.location == NSNotFound) {
5431 command = [path substringToIndex:slash.location];
5432 path = [path substringFromIndex:(slash.location + 1)];
5435 Database *database([Database sharedInstance]);
5437 if ([command isEqualToString:@"package-icon"]) {
5440 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5441 Package *package([database packageWithName:path]);
5444 UIImage *icon([package icon]);
5445 [self _returnPNGWithImage:icon forRequest:request];
5446 } else if ([command isEqualToString:@"source-icon"]) {
5449 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5450 NSString *source(Simplify(path));
5451 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5453 icon = [UIImage applicationImageNamed:@"unknown.png"];
5454 [self _returnPNGWithImage:icon forRequest:request];
5455 } else if ([command isEqualToString:@"uikit-image"]) {
5458 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5459 UIImage *icon(_UIImageWithName(path));
5460 [self _returnPNGWithImage:icon forRequest:request];
5461 } else if ([command isEqualToString:@"section-icon"]) {
5464 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5465 NSString *section(Simplify(path));
5466 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5468 icon = [UIImage applicationImageNamed:@"unknown.png"];
5469 [self _returnPNGWithImage:icon forRequest:request];
5471 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5475 - (void) stopLoading {
5481 /* Sections View {{{ */
5482 @interface SectionsView : RVPage {
5483 _transient Database *database_;
5484 NSMutableArray *sections_;
5485 NSMutableArray *filtered_;
5486 UITransitionView *transition_;
5492 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5493 - (void) reloadData;
5498 @implementation SectionsView
5501 [list_ setDataSource:nil];
5502 [list_ setDelegate:nil];
5504 [sections_ release];
5505 [filtered_ release];
5506 [transition_ release];
5508 [accessory_ release];
5512 - (int) numberOfRowsInTable:(UITable *)table {
5513 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5516 - (float) table:(UITable *)table heightForRow:(int)row {
5520 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5522 reusing = [[[SectionCell alloc] init] autorelease];
5523 [(SectionCell *)reusing setSection:(editing_ ?
5524 [sections_ objectAtIndex:row] :
5525 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5526 ) editing:editing_];
5530 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5534 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5538 - (void) tableRowSelected:(NSNotification *)notification {
5539 int row = [[notification object] selectedRow];
5550 title = @"All Packages";
5552 section = [filtered_ objectAtIndex:(row - 1)];
5553 name = [section name];
5559 title = @"(No Section)";
5563 PackageTable *table = [[[FilteredPackageTable alloc]
5567 filter:@selector(isVisiblyUninstalledInSection:)
5571 [table setDelegate:delegate_];
5573 [book_ pushPage:table];
5576 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5577 if ((self = [super initWithBook:book]) != nil) {
5578 database_ = database;
5580 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5581 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5583 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5584 [self addSubview:transition_];
5586 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5587 [transition_ transition:0 toView:list_];
5589 UITableColumn *column = [[[UITableColumn alloc]
5590 initWithTitle:@"Name"
5592 width:[self frame].size.width
5595 [list_ setDataSource:self];
5596 [list_ setSeparatorStyle:1];
5597 [list_ addTableColumn:column];
5598 [list_ setDelegate:self];
5599 [list_ setReusesTableCells:YES];
5603 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5604 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5608 - (void) reloadData {
5609 NSArray *packages = [database_ packages];
5611 [sections_ removeAllObjects];
5612 [filtered_ removeAllObjects];
5614 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5615 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5618 for (Package *package in packages) {
5619 NSString *name([package section]);
5622 Section *section([sections objectForKey:name]);
5623 if (section == nil) {
5624 section = [[[Section alloc] initWithName:name] autorelease];
5625 [sections setObject:section forKey:name];
5629 if ([package valid] && [package installed] == nil && [package visible])
5630 [filtered addObject:package];
5634 [sections_ addObjectsFromArray:[sections allValues]];
5635 [sections_ sortUsingSelector:@selector(compareByName:)];
5638 [filtered sortUsingSelector:@selector(compareBySection:)];
5641 Section *section = nil;
5642 for (Package *package in filtered) {
5643 NSString *name = [package section];
5645 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5646 section = name == nil ?
5647 [[[Section alloc] initWithName:nil] autorelease] :
5648 [sections objectForKey:name];
5649 [filtered_ addObject:section];
5652 [section addToCount];
5660 - (void) resetView {
5662 [self _rightButtonClicked];
5665 - (void) resetViewAnimated:(BOOL)animated {
5666 [list_ resetViewAnimated:animated];
5669 - (void) _rightButtonClicked {
5670 if ((editing_ = !editing_))
5673 [delegate_ updateData];
5674 [book_ reloadTitleForPage:self];
5675 [book_ reloadButtonsForPage:self];
5678 - (NSString *) title {
5679 return editing_ ? @"Section Visibility" : @"Install by Section";
5682 - (NSString *) backButtonTitle {
5686 - (id) rightButtonTitle {
5687 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5690 - (UINavigationButtonStyle) rightButtonStyle {
5691 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5694 - (UIView *) accessoryView {
5700 /* Changes View {{{ */
5701 @interface ChangesView : RVPage {
5702 _transient Database *database_;
5703 NSMutableArray *packages_;
5704 NSMutableArray *sections_;
5705 UISectionList *list_;
5709 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5710 - (void) reloadData;
5714 @implementation ChangesView
5717 [[list_ table] setDelegate:nil];
5718 [list_ setDataSource:nil];
5720 [packages_ release];
5721 [sections_ release];
5726 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5727 return [sections_ count];
5730 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5731 return [[sections_ objectAtIndex:section] name];
5734 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5735 return [[sections_ objectAtIndex:section] row];
5738 - (int) numberOfRowsInTable:(UITable *)table {
5739 return [packages_ count];
5742 - (float) table:(UITable *)table heightForRow:(int)row {
5743 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5746 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5748 reusing = [[[PackageCell alloc] init] autorelease];
5749 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5753 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5757 - (void) tableRowSelected:(NSNotification *)notification {
5758 int row = [[notification object] selectedRow];
5761 Package *package = [packages_ objectAtIndex:row];
5762 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5763 [view setDelegate:delegate_];
5764 [view setPackage:package];
5765 [book_ pushPage:view];
5768 - (void) _leftButtonClicked {
5769 [(CYBook *)book_ update];
5770 [self reloadButtons];
5773 - (void) _rightButtonClicked {
5774 [delegate_ distUpgrade];
5777 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5778 if ((self = [super initWithBook:book]) != nil) {
5779 database_ = database;
5781 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5782 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5784 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5785 [self addSubview:list_];
5787 [list_ setShouldHideHeaderInShortLists:NO];
5788 [list_ setDataSource:self];
5789 //[list_ setSectionListStyle:1];
5791 UITableColumn *column = [[[UITableColumn alloc]
5792 initWithTitle:@"Name"
5794 width:[self frame].size.width
5797 UITable *table = [list_ table];
5798 [table setSeparatorStyle:1];
5799 [table addTableColumn:column];
5800 [table setDelegate:self];
5801 [table setReusesTableCells:YES];
5805 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5806 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5810 - (void) reloadData {
5811 NSArray *packages = [database_ packages];
5813 [packages_ removeAllObjects];
5814 [sections_ removeAllObjects];
5817 for (Package *package in packages)
5819 [package installed] == nil && [package valid] && [package visible] ||
5820 [package upgradableAndEssential:YES]
5822 [packages_ addObject:package];
5825 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5828 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5829 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5830 Section *section = nil;
5834 bool unseens = false;
5836 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5839 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5840 Package *package = [packages_ objectAtIndex:offset];
5842 if (![package upgradableAndEssential:YES]) {
5844 NSDate *seen = [package seen];
5846 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5849 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5850 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5851 [sections_ addObject:section];
5855 [section addToCount];
5856 } else if ([package ignored])
5857 [ignored addToCount];
5860 [upgradable addToCount];
5865 CFRelease(formatter);
5868 Section *last = [sections_ lastObject];
5869 size_t count = [last count];
5870 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5871 [sections_ removeLastObject];
5874 if ([ignored count] != 0)
5875 [sections_ insertObject:ignored atIndex:0];
5877 [sections_ insertObject:upgradable atIndex:0];
5880 [self reloadButtons];
5883 - (void) resetViewAnimated:(BOOL)animated {
5884 [list_ resetViewAnimated:animated];
5887 - (NSString *) leftButtonTitle {
5888 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5891 - (id) rightButtonTitle {
5892 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5895 - (NSString *) title {
5901 /* Search View {{{ */
5902 @protocol SearchViewDelegate
5903 - (void) showKeyboard:(BOOL)show;
5906 @interface SearchView : RVPage {
5908 UISearchField *field_;
5909 UITransitionView *transition_;
5910 FilteredPackageTable *table_;
5911 UIPreferencesTable *advanced_;
5917 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5918 - (void) reloadData;
5922 @implementation SearchView
5925 [field_ setDelegate:nil];
5927 [accessory_ release];
5929 [transition_ release];
5931 [advanced_ release];
5936 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5940 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5942 case 0: return @"Advanced Search (Coming Soon!)";
5944 default: _assert(false);
5948 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5952 default: _assert(false);
5956 - (void) _showKeyboard:(BOOL)show {
5957 CGSize keysize = [UIKeyboard defaultSize];
5958 CGRect keydown = [book_ pageBounds];
5959 CGRect keyup = keydown;
5960 keyup.size.height -= keysize.height - ButtonBarHeight_;
5962 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5964 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5965 [animation setSignificantRectFields:8];
5968 [animation setStartFrame:keydown];
5969 [animation setEndFrame:keyup];
5971 [animation setStartFrame:keyup];
5972 [animation setEndFrame:keydown];
5975 UIAnimator *animator = [UIAnimator sharedAnimator];
5978 addAnimations:[NSArray arrayWithObjects:animation, nil]
5979 withDuration:(KeyboardTime_ - delay)
5984 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5986 [delegate_ showKeyboard:show];
5989 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5990 [self _showKeyboard:YES];
5993 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5994 [self _showKeyboard:NO];
5997 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5999 NSString *text([field_ text]);
6000 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6006 - (void) textFieldClearButtonPressed:(UITextField *)field {
6010 - (void) keyboardInputShouldDelete:(id)input {
6014 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6015 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6019 [field_ resignFirstResponder];
6024 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6025 if ((self = [super initWithBook:book]) != nil) {
6026 CGRect pageBounds = [book_ pageBounds];
6028 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6029 [self addSubview:transition_];
6031 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6033 [advanced_ setReusesTableCells:YES];
6034 [advanced_ setDataSource:self];
6035 [advanced_ reloadData];
6037 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6038 CGColor dimmed(space_, 0, 0, 0, 0.5);
6039 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6041 table_ = [[FilteredPackageTable alloc]
6045 filter:@selector(isUnfilteredAndSearchedForBy:)
6049 [table_ setShouldHideHeaderInShortLists:NO];
6050 [transition_ transition:0 toView:table_];
6059 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6066 [self bounds].size.width - area.origin.x - 18;
6068 area.size.height = [UISearchField defaultHeight];
6070 field_ = [[UISearchField alloc] initWithFrame:area];
6072 UIFont *font = [UIFont systemFontOfSize:16];
6073 [field_ setFont:font];
6075 [field_ setPlaceholder:@"Package Names & Descriptions"];
6076 [field_ setDelegate:self];
6078 [field_ setPaddingTop:5];
6080 UITextInputTraits *traits([field_ textInputTraits]);
6081 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6082 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6083 [traits setReturnKeyType:UIReturnKeySearch];
6085 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6087 accessory_ = [[UIView alloc] initWithFrame:accrect];
6088 [accessory_ addSubview:field_];
6090 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6091 [configure setShowPressFeedback:YES];
6092 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6093 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6094 [accessory_ addSubview:configure];*/
6096 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6097 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6103 LKAnimation *animation = [LKTransition animation];
6104 [animation setType:@"oglFlip"];
6105 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6106 [animation setFillMode:@"extended"];
6107 [animation setTransitionFlags:3];
6108 [animation setDuration:10];
6109 [animation setSpeed:0.35];
6110 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6111 [[transition_ _layer] addAnimation:animation forKey:0];
6112 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6113 flipped_ = !flipped_;
6117 - (void) configurePushed {
6118 [field_ resignFirstResponder];
6122 - (void) resetViewAnimated:(BOOL)animated {
6125 [table_ resetViewAnimated:animated];
6128 - (void) _reloadData {
6131 - (void) reloadData {
6134 [table_ setObject:[field_ text]];
6135 _profile(SearchView$reloadData)
6136 [table_ reloadData];
6139 [table_ resetCursor];
6142 - (UIView *) accessoryView {
6146 - (NSString *) title {
6150 - (NSString *) backButtonTitle {
6154 - (void) setDelegate:(id)delegate {
6155 [table_ setDelegate:delegate];
6156 [super setDelegate:delegate];
6162 @interface SettingsView : RVPage {
6163 _transient Database *database_;
6166 UIPreferencesTable *table_;
6167 _UISwitchSlider *subscribedSwitch_;
6168 _UISwitchSlider *ignoredSwitch_;
6169 UIPreferencesControlTableCell *subscribedCell_;
6170 UIPreferencesControlTableCell *ignoredCell_;
6173 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6177 @implementation SettingsView
6180 [table_ setDataSource:nil];
6183 if (package_ != nil)
6186 [subscribedSwitch_ release];
6187 [ignoredSwitch_ release];
6188 [subscribedCell_ release];
6189 [ignoredCell_ release];
6193 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6194 if (package_ == nil)
6200 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6201 if (package_ == nil)
6208 default: _assert(false);
6214 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6215 if (package_ == nil)
6222 default: _assert(false);
6228 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6229 if (package_ == nil)
6236 default: _assert(false);
6242 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6243 if (package_ == nil)
6246 _UISwitchSlider *slider([cell control]);
6247 BOOL value([slider value] != 0);
6248 NSMutableDictionary *metadata([package_ metadata]);
6251 if (NSNumber *number = [metadata objectForKey:key])
6252 before = [number boolValue];
6256 if (value != before) {
6257 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6259 [delegate_ updateData];
6263 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6264 [self onSomething:cell withKey:@"IsSubscribed"];
6267 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6268 [self onSomething:cell withKey:@"IsIgnored"];
6271 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6272 if (package_ == nil)
6276 case 0: switch (row) {
6278 return subscribedCell_;
6280 return ignoredCell_;
6281 default: _assert(false);
6284 case 1: switch (row) {
6286 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6287 [cell setShowSelection:NO];
6288 [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."];
6292 default: _assert(false);
6295 default: _assert(false);
6301 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6302 if ((self = [super initWithBook:book])) {
6303 database_ = database;
6304 name_ = [package retain];
6306 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6307 [self addSubview:table_];
6309 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6310 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6312 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6313 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6315 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6316 [subscribedCell_ setShowSelection:NO];
6317 [subscribedCell_ setTitle:@"Show All Changes"];
6318 [subscribedCell_ setControl:subscribedSwitch_];
6320 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6321 [ignoredCell_ setShowSelection:NO];
6322 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6323 [ignoredCell_ setControl:ignoredSwitch_];
6325 [table_ setDataSource:self];
6330 - (void) resetViewAnimated:(BOOL)animated {
6331 [table_ resetViewAnimated:animated];
6334 - (void) reloadData {
6335 if (package_ != nil)
6336 [package_ autorelease];
6337 package_ = [database_ packageWithName:name_];
6338 if (package_ != nil) {
6340 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6341 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6344 [table_ reloadData];
6347 - (NSString *) title {
6353 /* Signature View {{{ */
6354 @interface SignatureView : BrowserView {
6355 _transient Database *database_;
6359 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6363 @implementation SignatureView
6370 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6372 [super webView:sender didClearWindowObject:window forFrame:frame];
6375 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6376 if ((self = [super initWithBook:book]) != nil) {
6377 database_ = database;
6378 package_ = [package retain];
6383 - (void) resetViewAnimated:(BOOL)animated {
6386 - (void) reloadData {
6387 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6393 @interface Cydia : UIApplication <
6394 ConfirmationViewDelegate,
6395 ProgressViewDelegate,
6404 UIToolbar *buttonbar_;
6408 NSMutableArray *essential_;
6409 NSMutableArray *broken_;
6411 Database *database_;
6412 ProgressView *progress_;
6416 UIKeyboard *keyboard_;
6417 UIProgressHUD *hud_;
6419 SectionsView *sections_;
6420 ChangesView *changes_;
6421 ManageView *manage_;
6422 SearchView *search_;
6427 @implementation Cydia
6430 if ([broken_ count] != 0) {
6431 int count = [broken_ count];
6433 UIActionSheet *sheet = [[[UIActionSheet alloc]
6434 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6435 buttons:[NSArray arrayWithObjects:
6437 @"Ignore (Temporary)",
6439 defaultButtonIndex:0
6444 [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."];
6445 [sheet popupAlertAnimated:YES];
6446 } else if (!Ignored_ && [essential_ count] != 0) {
6447 int count = [essential_ count];
6449 UIActionSheet *sheet = [[[UIActionSheet alloc]
6450 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6451 buttons:[NSArray arrayWithObjects:
6452 @"Upgrade Essential",
6453 @"Complete Upgrade",
6454 @"Ignore (Temporary)",
6456 defaultButtonIndex:0
6461 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6462 [sheet popupAlertAnimated:YES];
6466 - (void) _reloadData {
6469 UIProgressHUD *hud([self addProgressHUD]);
6470 [hud setText:@"Reloading Data"];
6472 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6475 [self removeProgressHUD:hud];
6479 [essential_ removeAllObjects];
6480 [broken_ removeAllObjects];
6482 NSArray *packages = [database_ packages];
6483 for (Package *package in packages) {
6485 [broken_ addObject:package];
6486 if ([package upgradableAndEssential:NO]) {
6487 if ([package essential])
6488 [essential_ addObject:package];
6494 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6495 [buttonbar_ setBadgeValue:badge forButton:3];
6496 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6497 [buttonbar_ setBadgeAnimated:YES forButton:3];
6498 [self setApplicationBadge:badge];
6500 [buttonbar_ setBadgeValue:nil forButton:3];
6501 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6502 [buttonbar_ setBadgeAnimated:NO forButton:3];
6503 [self removeApplicationBadge];
6508 // XXX: what is this line of code for?
6509 if ([packages count] == 0);
6510 else if (Loaded_) loaded:
6515 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6516 NSTimeInterval interval([update timeIntervalSinceNow]);
6517 if (interval <= 0 && interval > -600)
6525 - (void) _saveConfig {
6528 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6534 - (void) updateData {
6537 /* XXX: this is just stupid */
6538 if (tag_ != 2 && sections_ != nil)
6539 [sections_ reloadData];
6540 if (tag_ != 3 && changes_ != nil)
6541 [changes_ reloadData];
6542 if (tag_ != 5 && search_ != nil)
6543 [search_ reloadData];
6553 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6554 _assert(file != NULL);
6556 NSArray *keys = [Sources_ allKeys];
6558 for (NSString *key in keys) {
6559 NSDictionary *source = [Sources_ objectForKey:key];
6561 fprintf(file, "%s %s %s\n",
6562 [[source objectForKey:@"Type"] UTF8String],
6563 [[source objectForKey:@"URI"] UTF8String],
6564 [[source objectForKey:@"Distribution"] UTF8String]
6573 detachNewThreadSelector:@selector(update_)
6576 title:@"Updating Sources"
6580 - (void) reloadData {
6581 @synchronized (self) {
6582 if (confirm_ == nil)
6588 pkgProblemResolver *resolver = [database_ resolver];
6590 resolver->InstallProtect();
6591 if (!resolver->Resolve(true))
6595 - (void) popUpBook:(RVBook *)book {
6596 [underlay_ popSubview:book];
6599 - (CGRect) popUpBounds {
6600 return [underlay_ bounds];
6604 [database_ prepare];
6606 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6607 [confirm_ setDelegate:self];
6609 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6610 [page setDelegate:self];
6612 [confirm_ setPage:page];
6613 [self popUpBook:confirm_];
6616 - (void) installPackage:(Package *)package {
6617 @synchronized (self) {
6624 - (void) removePackage:(Package *)package {
6625 @synchronized (self) {
6632 - (void) distUpgrade {
6633 @synchronized (self) {
6634 [database_ upgrade];
6640 @synchronized (self) {
6642 if (confirm_ != nil) {
6650 [overlay_ removeFromSuperview];
6654 detachNewThreadSelector:@selector(perform)
6661 - (void) bootstrap_ {
6663 [database_ upgrade];
6664 [database_ prepare];
6665 [database_ perform];
6668 - (void) bootstrap {
6670 detachNewThreadSelector:@selector(bootstrap_)
6673 title:@"Bootstrap Install"
6677 - (void) progressViewIsComplete:(ProgressView *)progress {
6678 if (confirm_ != nil) {
6679 [underlay_ addSubview:overlay_];
6680 [confirm_ popFromSuperviewAnimated:NO];
6686 - (void) setPage:(RVPage *)page {
6687 [page resetViewAnimated:NO];
6688 [page setDelegate:self];
6689 [book_ setPage:page];
6692 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6693 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6694 [browser loadURL:url];
6698 - (void) _setHomePage {
6699 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6702 - (void) buttonBarItemTapped:(id)sender {
6703 unsigned tag = [sender tag];
6705 [book_ resetViewAnimated:YES];
6707 } else if (tag_ == 2 && tag != 2)
6708 [sections_ resetView];
6711 case 1: [self _setHomePage]; break;
6713 case 2: [self setPage:sections_]; break;
6714 case 3: [self setPage:changes_]; break;
6715 case 4: [self setPage:manage_]; break;
6716 case 5: [self setPage:search_]; break;
6718 default: _assert(false);
6724 - (void) applicationWillSuspend {
6726 [super applicationWillSuspend];
6729 - (void) askForSettings {
6730 UIActionSheet *role = [[[UIActionSheet alloc]
6731 initWithTitle:@"Who Are You?"
6732 buttons:[NSArray arrayWithObjects:
6733 @"User (Graphical Only)",
6734 @"Hacker (+ Command Line)",
6735 @"Developer (No Filters)",
6737 defaultButtonIndex:-1
6742 [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."];
6743 [role popupAlertAnimated:YES];
6748 [self setStatusBarShowsProgress:NO];
6749 [self removeProgressHUD:hud_];
6754 pid_t pid = ExecFork();
6756 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6757 perror("launchctl stop");
6764 [self askForSettings];
6769 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6771 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6772 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6773 0, 0, screenrect.size.width, screenrect.size.height - 48
6774 ) database:database_];
6776 [book_ setDelegate:self];
6778 [overlay_ addSubview:book_];
6780 NSArray *buttonitems = [NSArray arrayWithObjects:
6781 [NSDictionary dictionaryWithObjectsAndKeys:
6782 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6783 @"home-up.png", kUIButtonBarButtonInfo,
6784 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6785 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6786 self, kUIButtonBarButtonTarget,
6787 @"Home", kUIButtonBarButtonTitle,
6788 @"0", kUIButtonBarButtonType,
6791 [NSDictionary dictionaryWithObjectsAndKeys:
6792 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6793 @"install-up.png", kUIButtonBarButtonInfo,
6794 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6795 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6796 self, kUIButtonBarButtonTarget,
6797 @"Sections", kUIButtonBarButtonTitle,
6798 @"0", kUIButtonBarButtonType,
6801 [NSDictionary dictionaryWithObjectsAndKeys:
6802 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6803 @"changes-up.png", kUIButtonBarButtonInfo,
6804 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6805 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6806 self, kUIButtonBarButtonTarget,
6807 @"Changes", kUIButtonBarButtonTitle,
6808 @"0", kUIButtonBarButtonType,
6811 [NSDictionary dictionaryWithObjectsAndKeys:
6812 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6813 @"manage-up.png", kUIButtonBarButtonInfo,
6814 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6815 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6816 self, kUIButtonBarButtonTarget,
6817 @"Manage", kUIButtonBarButtonTitle,
6818 @"0", kUIButtonBarButtonType,
6821 [NSDictionary dictionaryWithObjectsAndKeys:
6822 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6823 @"search-up.png", kUIButtonBarButtonInfo,
6824 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6825 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6826 self, kUIButtonBarButtonTarget,
6827 @"Search", kUIButtonBarButtonTitle,
6828 @"0", kUIButtonBarButtonType,
6832 buttonbar_ = [[UIToolbar alloc]
6834 withFrame:CGRectMake(
6835 0, screenrect.size.height - ButtonBarHeight_,
6836 screenrect.size.width, ButtonBarHeight_
6838 withItemList:buttonitems
6841 [buttonbar_ setDelegate:self];
6842 [buttonbar_ setBarStyle:1];
6843 [buttonbar_ setButtonBarTrackingMode:2];
6845 int buttons[5] = {1, 2, 3, 4, 5};
6846 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6847 [buttonbar_ showButtonGroup:0 withDuration:0];
6849 for (int i = 0; i != 5; ++i)
6850 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6851 i * 64 + 2, 1, 60, ButtonBarHeight_
6854 [buttonbar_ showSelectionForButton:1];
6855 [overlay_ addSubview:buttonbar_];
6857 [UIKeyboard initImplementationNow];
6858 CGSize keysize = [UIKeyboard defaultSize];
6859 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6860 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6861 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6862 [overlay_ addSubview:keyboard_];
6865 [underlay_ addSubview:overlay_];
6869 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6870 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6871 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6873 manage_ = (ManageView *) [[self
6874 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6875 withClass:[ManageView class]
6883 [self _setHomePage];
6886 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6887 NSString *context([sheet context]);
6889 if ([context isEqualToString:@"missing"])
6891 else if ([context isEqualToString:@"fixhalf"]) {
6894 @synchronized (self) {
6895 for (Package *broken in broken_) {
6898 NSString *id = [broken id];
6899 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6900 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6901 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6902 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6911 [broken_ removeAllObjects];
6920 } else if ([context isEqualToString:@"role"]) {
6922 case 1: Role_ = @"User"; break;
6923 case 2: Role_ = @"Hacker"; break;
6924 case 3: Role_ = @"Developer"; break;
6931 bool reset = Settings_ != nil;
6933 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6937 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6947 } else if ([context isEqualToString:@"upgrade"]) {
6950 @synchronized (self) {
6951 for (Package *essential in essential_)
6952 [essential install];
6975 - (void) reorganize { _pooled
6976 system("/usr/libexec/cydia/free.sh");
6977 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6980 - (void) applicationSuspend:(__GSEvent *)event {
6981 if (hud_ == nil && ![progress_ isRunning])
6982 [super applicationSuspend:event];
6985 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6987 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6990 - (void) _setSuspended:(BOOL)value {
6992 [super _setSuspended:value];
6995 - (UIProgressHUD *) addProgressHUD {
6996 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
6997 [window_ setUserInteractionEnabled:NO];
6999 [progress_ addSubview:hud];
7003 - (void) removeProgressHUD:(UIProgressHUD *)hud {
7005 [hud removeFromSuperview];
7006 [window_ setUserInteractionEnabled:YES];
7009 - (void) openMailToURL:(NSURL *)url {
7010 // XXX: this makes me sad
7012 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7014 [UIApp openURL:url];// asPanel:YES];
7018 - (void) clearFirstResponder {
7019 if (id responder = [window_ firstResponder])
7020 [responder resignFirstResponder];
7023 - (RVPage *) pageForPackage:(NSString *)name {
7024 if (Package *package = [database_ packageWithName:name]) {
7025 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7026 [view setPackage:package];
7029 UIActionSheet *sheet = [[[UIActionSheet alloc]
7030 initWithTitle:@"Cannot Locate Package"
7031 buttons:[NSArray arrayWithObjects:@"Close", nil]
7032 defaultButtonIndex:0
7037 [sheet setBodyText:[NSString stringWithFormat:
7038 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7041 [sheet popupAlertAnimated:YES];
7046 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7050 NSString *scheme([[url scheme] lowercaseString]);
7051 if (![scheme isEqualToString:@"cydia"])
7053 NSString *path([url absoluteString]);
7054 if ([path length] < 8)
7056 path = [path substringFromIndex:8];
7057 if (![path hasPrefix:@"/"])
7058 path = [@"/" stringByAppendingString:path];
7060 if ([path isEqualToString:@"/add-source"])
7061 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7062 else if ([path isEqualToString:@"/storage"])
7063 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
7064 else if ([path isEqualToString:@"/sources"])
7065 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7066 else if ([path isEqualToString:@"/packages"])
7067 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7068 else if ([path hasPrefix:@"/url/"])
7069 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
7070 else if ([path hasPrefix:@"/launch/"])
7071 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
7072 else if ([path hasPrefix:@"/package-settings/"])
7073 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
7074 else if ([path hasPrefix:@"/package-signature/"])
7075 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
7076 else if ([path hasPrefix:@"/package/"])
7077 return [self pageForPackage:[path substringFromIndex:9]];
7078 else if ([path hasPrefix:@"/files/"]) {
7079 NSString *name = [path substringFromIndex:7];
7081 if (Package *package = [database_ packageWithName:name]) {
7082 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7083 [files setPackage:package];
7091 - (void) applicationOpenURL:(NSURL *)url {
7092 [super applicationOpenURL:url];
7094 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7095 [self setPage:page];
7096 [buttonbar_ showSelectionForButton:tag];
7101 - (void) applicationDidFinishLaunching:(id)unused {
7103 Font12_ = [[UIFont systemFontOfSize:12] retain];
7104 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7105 Font14_ = [[UIFont systemFontOfSize:14] retain];
7106 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7107 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7109 _assert(pkgInitConfig(*_config));
7110 _assert(pkgInitSystem(*_config, _system));
7114 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7115 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7117 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7119 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7120 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7122 [window_ orderFront:self];
7123 [window_ makeKey:self];
7124 [window_ setHidden:NO];
7126 database_ = [Database sharedInstance];
7127 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7128 [database_ setDelegate:progress_];
7129 [window_ setContentView:progress_];
7131 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7132 [progress_ setContentView:underlay_];
7134 [progress_ resetView];
7137 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7138 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7139 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7140 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7141 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7142 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7143 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7145 [self setIdleTimerDisabled:YES];
7147 hud_ = [[self addProgressHUD] retain];
7148 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7150 [self setStatusBarShowsProgress:YES];
7153 detachNewThreadSelector:@selector(reorganize)
7161 - (void) showKeyboard:(BOOL)show {
7162 CGSize keysize = [UIKeyboard defaultSize];
7163 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7164 CGRect keyup = keydown;
7165 keyup.origin.y -= keysize.height;
7167 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7168 [animation setSignificantRectFields:2];
7171 [animation setStartFrame:keydown];
7172 [animation setEndFrame:keyup];
7173 [keyboard_ activate];
7175 [animation setStartFrame:keyup];
7176 [animation setEndFrame:keydown];
7177 [keyboard_ deactivate];
7180 [[UIAnimator sharedAnimator]
7181 addAnimations:[NSArray arrayWithObjects:animation, nil]
7182 withDuration:KeyboardTime_
7187 - (void) slideUp:(UIActionSheet *)alert {
7189 [alert presentSheetFromButtonBar:buttonbar_];
7191 [alert presentSheetInView:overlay_];
7196 void AddPreferences(NSString *plist) { _pooled
7197 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7198 _assert(settings != NULL);
7199 NSMutableArray *items = [settings objectForKey:@"items"];
7203 for (NSMutableDictionary *item in items) {
7204 NSString *label = [item objectForKey:@"label"];
7205 if (label != nil && [label isEqualToString:@"Cydia"]) {
7212 for (size_t i(0); i != [items count]; ++i) {
7213 NSDictionary *item([items objectAtIndex:i]);
7214 NSString *label = [item objectForKey:@"label"];
7215 if (label != nil && [label isEqualToString:@"General"]) {
7216 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7217 @"CydiaSettings", @"bundle",
7218 @"PSLinkCell", @"cell",
7219 [NSNumber numberWithBool:YES], @"hasIcon",
7220 [NSNumber numberWithBool:YES], @"isController",
7222 nil] atIndex:(i + 1)];
7228 _assert([settings writeToFile:plist atomically:YES] == YES);
7233 id Alloc_(id self, SEL selector) {
7234 id object = alloc_(self, selector);
7235 lprintf("[%s]A-%p\n", self->isa->name, object);
7240 id Dealloc_(id self, SEL selector) {
7241 id object = dealloc_(self, selector);
7242 lprintf("[%s]D-%p\n", self->isa->name, object);
7246 int main(int argc, char *argv[]) { _pooled
7248 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7250 bool substrate(false);
7256 for (int argi(1); argi != argc; ++argi)
7257 if (strcmp(argv[argi], "--") == 0) {
7259 argv[argi] = argv[0];
7265 for (int argi(1); argi != arge; ++argi)
7266 if (strcmp(args[argi], "--bootstrap") == 0)
7268 else if (strcmp(args[argi], "--substrate") == 0)
7271 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7274 App_ = [[NSBundle mainBundle] bundlePath];
7275 Home_ = NSHomeDirectory();
7276 Locale_ = CFLocaleCopyCurrent();
7279 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7280 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7281 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7282 Sounds_Keyboard_ = [keyboard boolValue];
7288 #if 1 /* XXX: this costs 1.4s of startup performance */
7289 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7290 _assert(errno == ENOENT);
7291 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7292 _assert(errno == ENOENT);
7295 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7296 alloc_ = alloc->method_imp;
7297 alloc->method_imp = (IMP) &Alloc_;*/
7299 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7300 dealloc_ = dealloc->method_imp;
7301 dealloc->method_imp = (IMP) &Dealloc_;*/
7306 size = sizeof(maxproc);
7307 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7308 perror("sysctlbyname(\"kern.maxproc\", ?)");
7309 else if (maxproc < 64) {
7311 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7312 perror("sysctlbyname(\"kern.maxproc\", #)");
7315 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7316 char *machine = new char[size];
7317 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7318 perror("sysctlbyname(\"hw.machine\", ?)");
7322 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7324 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7325 Build_ = [system objectForKey:@"ProductBuildVersion"];
7327 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7328 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7331 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7334 if (Metadata_ == NULL)
7335 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7337 Settings_ = [Metadata_ objectForKey:@"Settings"];
7339 Packages_ = [Metadata_ objectForKey:@"Packages"];
7340 Sections_ = [Metadata_ objectForKey:@"Sections"];
7341 Sources_ = [Metadata_ objectForKey:@"Sources"];
7344 if (Settings_ != nil)
7345 Role_ = [Settings_ objectForKey:@"Role"];
7347 if (Packages_ == nil) {
7348 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7349 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7352 if (Sections_ == nil) {
7353 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7354 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7357 if (Sources_ == nil) {
7358 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7359 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7363 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7366 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7367 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7368 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7369 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7371 if (access("/User", F_OK) != 0) {
7373 system("/usr/libexec/cydia/firmware.sh");
7377 _assert([[NSFileManager defaultManager]
7378 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7379 withIntermediateDirectories:YES
7384 space_ = CGColorSpaceCreateDeviceRGB();
7386 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7387 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7388 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7389 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7390 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7391 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7393 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7395 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7397 UIApplicationUseLegacyEvents(YES);
7398 UIKeyboardDisableAutomaticAppearance();
7401 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7403 CGColorSpaceRelease(space_);