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;
134 ProfileTime(const char *name) :
138 times_.push_back(this);
141 void AddTime(uint64_t time) {
147 std::cerr << std::setw(7) << total_ << " : " << name_ << std::endl;
158 ProfileTimer(ProfileTime &time) :
165 time_.AddTime(_timestamp - start_);
170 for (TimeList::const_iterator i(times_.begin()); i != times_.end(); ++i)
172 std::cerr << "========" << std::endl;
175 #define _profile(name) { \
176 static ProfileTime name(#name); \
177 ProfileTimer _ ## name(name);
181 /* Objective-C Handle<> {{{ */
182 template <typename Type_>
184 typedef _H<Type_> This_;
189 _finline void Retain_() {
194 _finline void Clear_() {
200 _finline _H(Type_ *value = NULL, bool mended = false) :
211 _finline This_ &operator =(Type_ *value) {
212 if (value_ != value) {
221 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
223 void NSLogPoint(const char *fix, const CGPoint &point) {
224 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
227 void NSLogRect(const char *fix, const CGRect &rect) {
228 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
231 @interface NSObject (Cydia)
232 - (void) yieldToSelector:(SEL)selector withObject:(id)object;
235 @implementation NSObject (Cydia)
240 - (void) _yieldToContext:(NSArray *)context { _pooled
241 SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
242 id object([[context objectAtIndex:1] nonretainedObjectValue]);
243 volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
245 [self performSelector:selector withObject:object];
250 performSelectorOnMainThread:@selector(doNothing)
256 - (void) yieldToSelector:(SEL)selector withObject:(id)object {
257 [self performSelector:selector withObject:object];
260 volatile bool stopped(false);
262 NSArray *context([NSArray arrayWithObjects:
263 [NSValue valueWithPointer:selector],
264 [NSValue valueWithNonretainedObject:object],
265 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
268 NSThread *thread([[[NSThread alloc]
270 selector:@selector(_yieldToContext:)
276 NSRunLoop *loop([NSRunLoop currentRunLoop]);
277 NSDate *future([NSDate distantFuture]);
279 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
284 /* NSForcedOrderingSearch doesn't work on the iPhone */
285 static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
286 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
287 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
288 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
290 /* iPhoneOS 2.0 Compatibility {{{ */
292 @interface UITextView (iPhoneOS)
293 - (void) setTextSize:(float)size;
296 @implementation UITextView (iPhoneOS)
298 - (void) setTextSize:(float)size {
299 [self setFont:[[self font] fontWithSize:size]];
306 extern NSString * const kCAFilterNearest;
308 /* Information Dictionaries {{{ */
309 @interface NSMutableArray (Cydia)
310 - (void) addInfoDictionary:(NSDictionary *)info;
313 @implementation NSMutableArray (Cydia)
315 - (void) addInfoDictionary:(NSDictionary *)info {
316 [self addObject:info];
321 @interface NSMutableDictionary (Cydia)
322 - (void) addInfoDictionary:(NSDictionary *)info;
325 @implementation NSMutableDictionary (Cydia)
327 - (void) addInfoDictionary:(NSDictionary *)info {
328 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
329 [self setObject:info forKey:bundle];
334 /* Pop Transitions {{{ */
335 @interface PopTransitionView : UITransitionView {
340 @implementation PopTransitionView
342 - (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
343 if (from != nil && to == nil)
344 [self removeFromSuperview];
349 @implementation UIView (PopUpView)
351 - (void) popFromSuperviewAnimated:(BOOL)animated {
352 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
355 - (void) popSubview:(UIView *)view {
356 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
357 [transition setDelegate:transition];
358 [self addSubview:transition];
360 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
361 [transition transition:UITransitionNone toView:blank];
362 [transition transition:UITransitionPushFromBottom toView:view];
368 #define lprintf(args...) fprintf(stderr, args)
371 #define ForSaurik (1 && !ForRelease)
372 #define ShowInternals (0 && !ForRelease)
373 #define IgnoreInstall (0 && !ForRelease)
374 #define RecycleWebViews 0
375 #define AlwaysReload (1 && !ForRelease)
379 #define _trace(args...)
381 #define _profile(name)
387 @interface NSMutableArray (Radix)
388 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
391 @implementation NSMutableArray (Radix)
393 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
394 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
395 [invocation setSelector:selector];
396 [invocation setArgument:&object atIndex:2];
398 size_t count([self count]);
403 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
405 for (size_t i(0); i != count; ++i) {
406 RadixItem &item(lhs[i]);
409 id object([self objectAtIndex:i]);
410 [invocation setTarget:object];
413 [invocation getReturnValue:&item.key];
416 static const size_t width = 32;
417 static const size_t bits = 11;
418 static const size_t slots = 1 << bits;
419 static const size_t passes = (width + (bits - 1)) / bits;
421 size_t *hist(new size_t[slots]);
423 for (size_t pass(0); pass != passes; ++pass) {
424 memset(hist, 0, sizeof(size_t) * slots);
426 for (size_t i(0); i != count; ++i) {
427 uint32_t key(lhs[i].key);
429 key &= _not(uint32_t) >> width - bits;
434 for (size_t i(0); i != slots; ++i) {
435 size_t local(offset);
440 for (size_t i(0); i != count; ++i) {
441 uint32_t key(lhs[i].key);
443 key &= _not(uint32_t) >> width - bits;
444 rhs[hist[key]++] = lhs[i];
454 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
455 for (size_t i(0); i != count; ++i)
456 [values addObject:[self objectAtIndex:lhs[i].index]];
457 [self setArray:values];
465 /* Apple Bug Fixes {{{ */
466 @implementation UIWebDocumentView (Cydia)
468 - (void) _setScrollerOffset:(CGPoint)offset {
469 UIScroller *scroller([self _scroller]);
471 CGSize size([scroller contentSize]);
472 CGSize bounds([scroller bounds].size);
475 max.x = size.width - bounds.width;
476 max.y = size.height - bounds.height;
484 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
485 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
487 [scroller setOffset:offset];
494 kUIControlEventMouseDown = 1 << 0,
495 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
496 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
497 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
498 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
499 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
500 } UIControlEventMasks;
502 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
503 size_t length([self length] - state->state);
506 else if (length > count)
508 for (size_t i(0); i != length; ++i)
509 objects[i] = [self item:state->state++];
510 state->itemsPtr = objects;
511 state->mutationsPtr = (unsigned long *) self;
515 @interface NSString (UIKit)
516 - (NSString *) stringByAddingPercentEscapes;
517 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
520 @interface NSString (Cydia)
521 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
522 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
523 - (NSComparisonResult) compareByPath:(NSString *)other;
526 @implementation NSString (Cydia)
528 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length {
529 return [[[NSString alloc] initWithBytesNoCopy:const_cast<char *>(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
532 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
533 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
536 - (NSComparisonResult) compareByPath:(NSString *)other {
537 NSString *prefix = [self commonPrefixWithString:other options:0];
538 size_t length = [prefix length];
540 NSRange lrange = NSMakeRange(length, [self length] - length);
541 NSRange rrange = NSMakeRange(length, [other length] - length);
543 lrange = [self rangeOfString:@"/" options:0 range:lrange];
544 rrange = [other rangeOfString:@"/" options:0 range:rrange];
546 NSComparisonResult value;
548 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
549 value = NSOrderedSame;
550 else if (lrange.location == NSNotFound)
551 value = NSOrderedAscending;
552 else if (rrange.location == NSNotFound)
553 value = NSOrderedDescending;
555 value = NSOrderedSame;
557 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
558 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
559 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
560 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
562 NSComparisonResult result = [lpath compare:rpath];
563 return result == NSOrderedSame ? value : result;
568 /* Perl-Compatible RegEx {{{ */
578 Pcre(const char *regex) :
583 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
586 lprintf("%d:%s\n", offset, error);
590 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
591 matches_ = new int[(capture_ + 1) * 3];
599 NSString *operator [](size_t match) {
600 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
603 bool operator ()(NSString *data) {
604 // XXX: length is for characters, not for bytes
605 return operator ()([data UTF8String], [data length]);
608 bool operator ()(const char *data, size_t size) {
610 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
614 /* Mime Addresses {{{ */
615 @interface Address : NSObject {
621 - (NSString *) address;
623 + (Address *) addressWithString:(NSString *)string;
624 - (Address *) initWithString:(NSString *)string;
627 @implementation Address
636 - (NSString *) name {
640 - (NSString *) address {
644 + (Address *) addressWithString:(NSString *)string {
645 return [[[Address alloc] initWithString:string] autorelease];
648 + (NSArray *) _attributeKeys {
649 return [NSArray arrayWithObjects:@"address", @"name", nil];
652 - (NSArray *) attributeKeys {
653 return [[self class] _attributeKeys];
656 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
657 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
660 - (Address *) initWithString:(NSString *)string {
661 if ((self = [super init]) != nil) {
662 const char *data = [string UTF8String];
663 size_t size = [string length];
665 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
667 if (address_r(data, size)) {
668 name_ = [address_r[1] retain];
669 address_ = [address_r[2] retain];
671 name_ = [string retain];
679 /* CoreGraphics Primitives {{{ */
690 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
693 Set(space, red, green, blue, alpha);
698 CGColorRelease(color_);
705 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
707 float color[] = {red, green, blue, alpha};
708 color_ = CGColorCreate(space, color);
711 operator CGColorRef() {
717 extern "C" void UISetColor(CGColorRef color);
719 /* Random Global Variables {{{ */
720 static const int PulseInterval_ = 50000;
721 static const int ButtonBarHeight_ = 48;
722 static const float KeyboardTime_ = 0.3f;
724 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
725 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
726 #define NotifyConfig_ "/etc/notify.conf"
728 static CGColor Blue_;
729 static CGColor Blueish_;
730 static CGColor Black_;
732 static CGColor White_;
733 static CGColor Gray_;
735 static NSString *App_;
736 static NSString *Home_;
737 static BOOL Sounds_Keyboard_;
739 static BOOL Advanced_;
741 static BOOL Ignored_;
743 static UIFont *Font12_;
744 static UIFont *Font12Bold_;
745 static UIFont *Font14_;
746 static UIFont *Font18Bold_;
747 static UIFont *Font22Bold_;
749 static const char *Machine_ = NULL;
750 static const NSString *UniqueID_ = nil;
751 static const NSString *Build_ = nil;
754 CGColorSpaceRef space_;
759 static NSDictionary *SectionMap_;
760 static NSMutableDictionary *Metadata_;
761 static _transient NSMutableDictionary *Settings_;
762 static _transient NSString *Role_;
763 static _transient NSMutableDictionary *Packages_;
764 static _transient NSMutableDictionary *Sections_;
765 static _transient NSMutableDictionary *Sources_;
766 static bool Changed_;
770 static NSMutableArray *Documents_;
773 NSString *GetLastUpdate() {
774 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
777 return @"Never or Unknown";
779 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
780 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
782 CFRelease(formatter);
784 return [(NSString *) formatted autorelease];
787 /* Display Helpers {{{ */
788 inline float Interpolate(float begin, float end, float fraction) {
789 return (end - begin) * fraction + begin;
792 NSString *SizeString(double size) {
793 bool negative = size < 0;
798 while (size > 1024) {
803 static const char *powers_[] = {"B", "kB", "MB", "GB"};
805 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
808 NSString *StripVersion(NSString *version) {
809 NSRange colon = [version rangeOfString:@":"];
810 if (colon.location != NSNotFound)
811 version = [version substringFromIndex:(colon.location + 1)];
815 NSString *Simplify(NSString *title) {
816 const char *data = [title UTF8String];
817 size_t size = [title length];
819 static Pcre square_r("^\\[(.*)\\]$");
820 if (square_r(data, size))
821 return Simplify(square_r[1]);
823 static Pcre paren_r("^\\((.*)\\)$");
824 if (paren_r(data, size))
825 return Simplify(paren_r[1]);
827 static Pcre title_r("^(.*?) \\(.*\\)$");
828 if (title_r(data, size))
829 return Simplify(title_r[1]);
835 bool isSectionVisible(NSString *section) {
836 NSDictionary *metadata = [Sections_ objectForKey:section];
837 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
838 return hidden == nil || ![hidden boolValue];
841 /* Delegate Prototypes {{{ */
845 @interface NSObject (ProgressDelegate)
848 @implementation NSObject(ProgressDelegate)
850 - (void) _setProgressError:(NSArray *)args {
851 [self performSelector:@selector(setProgressError:forPackage:)
852 withObject:[args objectAtIndex:0]
853 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
859 @protocol ProgressDelegate
860 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
861 - (void) setProgressTitle:(NSString *)title;
862 - (void) setProgressPercent:(float)percent;
863 - (void) startProgress;
864 - (void) addProgressOutput:(NSString *)output;
865 - (bool) isCancelling:(size_t)received;
868 @protocol ConfigurationDelegate
869 - (void) repairWithSelector:(SEL)selector;
870 - (void) setConfigurationData:(NSString *)data;
873 @protocol CydiaDelegate
874 - (void) installPackage:(Package *)package;
875 - (void) removePackage:(Package *)package;
876 - (void) slideUp:(UIActionSheet *)alert;
877 - (void) distUpgrade;
880 - (void) askForSettings;
881 - (UIProgressHUD *) addProgressHUD;
882 - (void) removeProgressHUD:(UIProgressHUD *)hud;
883 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
884 - (RVPage *) pageForPackage:(NSString *)name;
885 - (void) openMailToURL:(NSURL *)url;
886 - (void) clearFirstResponder;
890 /* Status Delegation {{{ */
892 public pkgAcquireStatus
895 _transient NSObject<ProgressDelegate> *delegate_;
903 void setDelegate(id delegate) {
904 delegate_ = delegate;
907 virtual bool MediaChange(std::string media, std::string drive) {
911 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
914 virtual void Fetch(pkgAcquire::ItemDesc &item) {
915 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
916 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
919 virtual void Done(pkgAcquire::ItemDesc &item) {
922 virtual void Fail(pkgAcquire::ItemDesc &item) {
924 item.Owner->Status == pkgAcquire::Item::StatIdle ||
925 item.Owner->Status == pkgAcquire::Item::StatDone
929 std::string &error(item.Owner->ErrorText);
933 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
934 NSArray *fields([description componentsSeparatedByString:@" "]);
935 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
937 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
938 withObject:[NSArray arrayWithObjects:
939 [NSString stringWithUTF8String:error.c_str()],
946 virtual bool Pulse(pkgAcquire *Owner) {
947 bool value = pkgAcquireStatus::Pulse(Owner);
950 double(CurrentBytes + CurrentItems) /
951 double(TotalBytes + TotalItems)
954 [delegate_ setProgressPercent:percent];
955 return [delegate_ isCancelling:CurrentBytes] ? false : value;
958 virtual void Start() {
959 [delegate_ startProgress];
962 virtual void Stop() {
966 /* Progress Delegation {{{ */
971 _transient id<ProgressDelegate> delegate_;
974 virtual void Update() {
975 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
976 [delegate_ setProgressPercent:(Percent / 100)];*/
985 void setDelegate(id delegate) {
986 delegate_ = delegate;
989 virtual void Done() {
990 //[delegate_ setProgressPercent:1];
995 /* Database Interface {{{ */
996 @interface Database : NSObject {
998 pkgDepCache::Policy *policy_;
999 pkgRecords *records_;
1000 pkgProblemResolver *resolver_;
1001 pkgAcquire *fetcher_;
1003 SPtr<pkgPackageManager> manager_;
1004 pkgSourceList *list_;
1006 NSMutableDictionary *sources_;
1007 NSMutableArray *packages_;
1009 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
1018 + (Database *) sharedInstance;
1020 - (void) _readCydia:(NSNumber *)fd;
1021 - (void) _readStatus:(NSNumber *)fd;
1022 - (void) _readOutput:(NSNumber *)fd;
1026 - (Package *) packageWithName:(NSString *)name;
1028 - (pkgCacheFile &) cache;
1029 - (pkgDepCache::Policy *) policy;
1030 - (pkgRecords *) records;
1031 - (pkgProblemResolver *) resolver;
1032 - (pkgAcquire &) fetcher;
1033 - (pkgSourceList &) list;
1034 - (NSArray *) packages;
1035 - (NSArray *) sources;
1036 - (void) reloadData;
1044 - (void) updateWithStatus:(Status &)status;
1046 - (void) setDelegate:(id)delegate;
1047 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
1051 /* Source Class {{{ */
1052 @interface Source : NSObject {
1053 NSString *description_;
1058 NSString *distribution_;
1062 NSString *defaultIcon_;
1064 NSDictionary *record_;
1068 - (Source *) initWithMetaIndex:(metaIndex *)index;
1070 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1072 - (NSDictionary *) record;
1076 - (NSString *) distribution;
1077 - (NSString *) type;
1079 - (NSString *) host;
1081 - (NSString *) name;
1082 - (NSString *) description;
1083 - (NSString *) label;
1084 - (NSString *) origin;
1085 - (NSString *) version;
1087 - (NSString *) defaultIcon;
1091 @implementation Source
1093 #define _clear(field) \
1100 _clear(distribution_)
1103 _clear(description_)
1107 _clear(defaultIcon_)
1116 + (NSArray *) _attributeKeys {
1117 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1120 - (NSArray *) attributeKeys {
1121 return [[self class] _attributeKeys];
1124 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1125 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1128 - (void) setMetaIndex:(metaIndex *)index {
1131 trusted_ = index->IsTrusted();
1133 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1134 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1135 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1137 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1138 if (dindex != NULL) {
1139 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1141 while (std::getline(release, line)) {
1142 std::string::size_type colon(line.find(':'));
1143 if (colon == std::string::npos)
1146 std::string name(line.substr(0, colon));
1147 std::string value(line.substr(colon + 1));
1148 while (!value.empty() && value[0] == ' ')
1149 value = value.substr(1);
1151 if (name == "Default-Icon")
1152 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1153 else if (name == "Description")
1154 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1155 else if (name == "Label")
1156 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1157 else if (name == "Origin")
1158 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1159 else if (name == "Version")
1160 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1164 record_ = [Sources_ objectForKey:[self key]];
1166 record_ = [record_ retain];
1169 - (Source *) initWithMetaIndex:(metaIndex *)index {
1170 if ((self = [super init]) != nil) {
1171 [self setMetaIndex:index];
1175 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1176 NSDictionary *lhr = [self record];
1177 NSDictionary *rhr = [source record];
1180 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1182 NSString *lhs = [self name];
1183 NSString *rhs = [source name];
1185 if ([lhs length] != 0 && [rhs length] != 0) {
1186 unichar lhc = [lhs characterAtIndex:0];
1187 unichar rhc = [rhs characterAtIndex:0];
1189 if (isalpha(lhc) && !isalpha(rhc))
1190 return NSOrderedAscending;
1191 else if (!isalpha(lhc) && isalpha(rhc))
1192 return NSOrderedDescending;
1195 return [lhs compare:rhs options:LaxCompareOptions_];
1198 - (NSDictionary *) record {
1206 - (NSString *) uri {
1210 - (NSString *) distribution {
1211 return distribution_;
1214 - (NSString *) type {
1218 - (NSString *) key {
1219 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1222 - (NSString *) host {
1223 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1226 - (NSString *) name {
1227 return origin_ == nil ? [self host] : origin_;
1230 - (NSString *) description {
1231 return description_;
1234 - (NSString *) label {
1235 return label_ == nil ? [self host] : label_;
1238 - (NSString *) origin {
1242 - (NSString *) version {
1246 - (NSString *) defaultIcon {
1247 return defaultIcon_;
1252 /* Relationship Class {{{ */
1253 @interface Relationship : NSObject {
1258 - (NSString *) type;
1260 - (NSString *) name;
1264 @implementation Relationship
1272 - (NSString *) type {
1280 - (NSString *) name {
1287 /* Package Class {{{ */
1288 @interface Package : NSObject {
1289 pkgCache::PkgIterator iterator_;
1290 _transient Database *database_;
1291 pkgCache::VerIterator version_;
1292 pkgCache::VerFileIterator file_;
1300 NSString *installed_;
1306 NSString *depiction_;
1307 NSString *homepage_;
1313 NSArray *relationships_;
1316 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1317 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1319 - (pkgCache::PkgIterator) iterator;
1321 - (NSString *) section;
1322 - (NSString *) simpleSection;
1326 - (Address *) maintainer;
1328 - (NSString *) description;
1331 - (NSMutableDictionary *) metadata;
1333 - (BOOL) subscribed;
1336 - (NSString *) latest;
1337 - (NSString *) installed;
1340 - (BOOL) upgradableAndEssential:(BOOL)essential;
1343 - (BOOL) unfiltered;
1347 - (BOOL) halfConfigured;
1348 - (BOOL) halfInstalled;
1350 - (NSString *) mode;
1353 - (NSString *) name;
1354 - (NSString *) tagline;
1356 - (NSString *) homepage;
1357 - (NSString *) depiction;
1358 - (Address *) author;
1360 - (NSArray *) files;
1361 - (NSArray *) relationships;
1362 - (NSArray *) warnings;
1363 - (NSArray *) applications;
1365 - (Source *) source;
1366 - (NSString *) role;
1368 - (BOOL) matches:(NSString *)text;
1370 - (bool) hasSupportingRole;
1371 - (BOOL) hasTag:(NSString *)tag;
1372 - (NSString *) primaryPurpose;
1373 - (NSArray *) purposes;
1375 - (NSComparisonResult) compareByName:(Package *)package;
1376 - (NSComparisonResult) compareBySection:(Package *)package;
1378 - (uint32_t) compareForChanges;
1383 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
1384 - (bool) isInstalledAndVisible:(NSNumber *)number;
1385 - (bool) isVisiblyUninstalledInSection:(NSString *)section;
1386 - (bool) isVisibleInSource:(Source *)source;
1390 @implementation Package
1396 if (section_ != nil)
1400 if (installed_ != nil)
1401 [installed_ release];
1409 if (depiction_ != nil)
1410 [depiction_ release];
1411 if (homepage_ != nil)
1412 [homepage_ release];
1413 if (sponsor_ != nil)
1422 if (relationships_ != nil)
1423 [relationships_ release];
1428 + (NSArray *) _attributeKeys {
1429 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1432 - (NSArray *) attributeKeys {
1433 return [[self class] _attributeKeys];
1436 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1437 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1440 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1441 if ((self = [super init]) != nil) { _profile(Package$initWithIterator)
1442 iterator_ = iterator;
1443 database_ = database;
1445 _profile(Package$initWithIterator$Control)
1448 _profile(Package$initWithIterator$Version)
1449 version_ = [database_ policy]->GetCandidateVer(iterator_);
1452 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1454 _profile(Package$initWithIterator$Latest)
1455 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1458 pkgCache::VerIterator current;
1459 NSString *installed;
1461 _profile(Package$initWithIterator$Current)
1462 current = iterator_.CurrentVer();
1463 installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1466 _profile(Package$initWithIterator$Installed)
1467 installed_ = [StripVersion(installed) retain];
1470 _profile(Package$initWithIterator$File)
1471 if (!version_.end())
1472 file_ = version_.FileList();
1474 pkgCache &cache([database_ cache]);
1475 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1479 _profile(Package$initWithIterator$Name)
1480 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1484 _profile(Package$initWithIterator$Parse)
1485 pkgRecords::Parser *parser;
1487 _profile(Package$initWithIterator$Parse$Lookup)
1488 parser = &[database_ records]->Lookup(file_);
1491 const char *begin, *end;
1492 parser->GetRec(begin, end);
1494 NSString *website(nil);
1495 NSString *sponsor(nil);
1496 NSString *author(nil);
1505 {"depiction", &depiction_},
1506 {"homepage", &homepage_},
1507 {"website", &website},
1508 {"sponsor", &sponsor},
1509 {"author", &author},
1513 while (begin != end)
1514 if (*begin == '\n') {
1517 } else if (isblank(*begin)) next: {
1518 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1521 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1522 const char *name(begin);
1523 size_t size(colon - begin);
1525 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1528 const char *stop(begin == NULL ? end : begin);
1529 while (stop[-1] == '\r')
1531 while (++colon != stop && isblank(*colon));
1533 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1534 if (strncasecmp(names[i].name_, name, size) == 0) {
1537 _profile(Package$initWithIterator$Parse$Value)
1538 value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
1541 *names[i].value_ = value;
1551 _profile(Package$initWithIterator$Parse$Retain)
1553 name_ = [name_ retain];
1554 _profile(Package$initWithIterator$Parse$Tagline)
1555 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1558 icon_ = [icon_ retain];
1559 if (depiction_ != nil)
1560 depiction_ = [depiction_ retain];
1561 if (homepage_ == nil)
1562 homepage_ = website;
1563 if ([homepage_ isEqualToString:depiction_])
1565 if (homepage_ != nil)
1566 homepage_ = [homepage_ retain];
1568 sponsor_ = [[Address addressWithString:sponsor] retain];
1570 author_ = [[Address addressWithString:author] retain];
1572 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1576 _profile(Package$initWithIterator$Tags)
1578 for (NSString *tag in tags_)
1579 if ([tag hasPrefix:@"role::"]) {
1580 role_ = [[tag substringFromIndex:6] retain];
1585 NSString *solid(latest == nil ? installed : latest);
1586 bool changed(false);
1588 NSString *key([id_ lowercaseString]);
1590 _profile(Package$initWithIterator$Metadata)
1591 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1592 if (metadata == nil) {
1593 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1598 [metadata setObject:solid forKey:@"LastVersion"];
1601 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1602 NSDate *last([metadata objectForKey:@"LastSeen"]);
1603 NSString *version([metadata objectForKey:@"LastVersion"]);
1606 first = last == nil ? now_ : last;
1607 [metadata setObject:first forKey:@"FirstSeen"];
1612 if (version == nil) {
1613 [metadata setObject:solid forKey:@"LastVersion"];
1615 } else if (![version isEqualToString:solid]) {
1616 [metadata setObject:solid forKey:@"LastVersion"];
1618 [metadata setObject:last forKey:@"LastSeen"];
1624 [Packages_ setObject:metadata forKey:key];
1631 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1632 return [[[Package alloc]
1633 initWithIterator:iterator
1638 - (pkgCache::PkgIterator) iterator {
1642 - (NSString *) section {
1643 if (section_ != nil)
1646 const char *section = iterator_.Section();
1647 if (section == NULL)
1650 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1653 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1654 if (NSString *rename = [value objectForKey:@"Rename"]) {
1659 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1663 - (NSString *) simpleSection {
1664 if (NSString *section = [self section])
1665 return Simplify(section);
1670 - (NSString *) uri {
1673 pkgIndexFile *index;
1674 pkgCache::PkgFileIterator file(file_.File());
1675 if (![database_ list].FindIndex(file, index))
1677 return [NSString stringWithUTF8String:iterator_->Path];
1678 //return [NSString stringWithUTF8String:file.Site()];
1679 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1683 - (Address *) maintainer {
1686 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1687 const std::string &maintainer(parser->Maintainer());
1688 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1692 return version_.end() ? 0 : version_->InstalledSize;
1695 - (NSString *) description {
1698 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1699 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1701 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1702 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1703 if ([lines count] < 2)
1706 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1707 for (size_t i(1), e([lines count]); i != e; ++i) {
1708 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1709 [trimmed addObject:trim];
1712 return [trimmed componentsJoinedByString:@"\n"];
1716 _profile(Package$index)
1717 NSString *name([self name]);
1718 if ([name length] == 0)
1720 unichar character([name characterAtIndex:0]);
1721 if (!isalpha(character))
1727 - (NSMutableDictionary *) metadata {
1728 return [Packages_ objectForKey:[id_ lowercaseString]];
1732 NSDictionary *metadata([self metadata]);
1733 if ([self subscribed])
1734 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1736 return [metadata objectForKey:@"FirstSeen"];
1739 - (BOOL) subscribed {
1740 NSDictionary *metadata([self metadata]);
1741 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1742 return [subscribed boolValue];
1748 NSDictionary *metadata([self metadata]);
1749 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1750 return [ignored boolValue];
1755 - (NSString *) latest {
1759 - (NSString *) installed {
1764 return !version_.end();
1767 - (BOOL) upgradableAndEssential:(BOOL)essential {
1768 pkgCache::VerIterator current = iterator_.CurrentVer();
1772 value = essential && [self essential] && [self visible];
1774 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1778 - (BOOL) essential {
1779 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1783 return [database_ cache][iterator_].InstBroken();
1786 - (BOOL) unfiltered {
1787 NSString *section = [self section];
1788 return section == nil || isSectionVisible(section);
1792 return [self hasSupportingRole] && [self unfiltered];
1796 unsigned char current = iterator_->CurrentState;
1797 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1800 - (BOOL) halfConfigured {
1801 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1804 - (BOOL) halfInstalled {
1805 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1809 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1810 return state.Mode != pkgDepCache::ModeKeep;
1813 - (NSString *) mode {
1814 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1816 switch (state.Mode) {
1817 case pkgDepCache::ModeDelete:
1818 if ((state.iFlags & pkgDepCache::Purge) != 0)
1822 case pkgDepCache::ModeKeep:
1823 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1827 case pkgDepCache::ModeInstall:
1828 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1829 return @"Reinstall";
1830 else switch (state.Status) {
1832 return @"Downgrade";
1838 return @"New Install";
1851 - (NSString *) name {
1852 return name_ == nil ? id_ : name_;
1855 - (NSString *) tagline {
1859 - (UIImage *) icon {
1860 NSString *section = [self simpleSection];
1864 if ([icon_ hasPrefix:@"file:///"])
1865 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1866 if (icon == nil) if (section != nil)
1867 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1868 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1869 if ([dicon hasPrefix:@"file:///"])
1870 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1872 icon = [UIImage applicationImageNamed:@"unknown.png"];
1876 - (NSString *) homepage {
1880 - (NSString *) depiction {
1884 - (Address *) sponsor {
1888 - (Address *) author {
1892 - (NSArray *) files {
1893 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1894 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1897 fin.open([path UTF8String]);
1902 while (std::getline(fin, line))
1903 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1908 - (NSArray *) relationships {
1909 return relationships_;
1912 - (NSArray *) warnings {
1913 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1914 const char *name(iterator_.Name());
1916 size_t length(strlen(name));
1917 if (length < 2) invalid:
1918 [warnings addObject:@"illegal package identifier"];
1919 else for (size_t i(0); i != length; ++i)
1921 /* XXX: technically this is not allowed */
1922 (name[i] < 'A' || name[i] > 'Z') &&
1923 (name[i] < 'a' || name[i] > 'z') &&
1924 (name[i] < '0' || name[i] > '9') &&
1925 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1928 if (strcmp(name, "cydia") != 0) {
1930 bool _private = false;
1933 bool repository = [[self section] isEqualToString:@"Repositories"];
1935 if (NSArray *files = [self files])
1936 for (NSString *file in files)
1937 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1939 else if (!_private && [file isEqualToString:@"/private"])
1941 else if (!stash && [file isEqualToString:@"/var/stash"])
1944 /* XXX: this is not sensitive enough. only some folders are valid. */
1945 if (cydia && !repository)
1946 [warnings addObject:@"files installed into Cydia.app"];
1948 [warnings addObject:@"files installed with /private/*"];
1950 [warnings addObject:@"files installed to /var/stash"];
1953 return [warnings count] == 0 ? nil : warnings;
1956 - (NSArray *) applications {
1957 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1959 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1961 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1962 if (NSArray *files = [self files])
1963 for (NSString *file in files)
1964 if (application_r(file)) {
1965 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1966 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1967 if ([id isEqualToString:me])
1970 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1972 display = application_r[1];
1974 NSString *bundle([file stringByDeletingLastPathComponent]);
1975 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1976 if (icon == nil || [icon length] == 0)
1978 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1980 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1981 [applications addObject:application];
1983 [application addObject:id];
1984 [application addObject:display];
1985 [application addObject:url];
1988 return [applications count] == 0 ? nil : applications;
1991 - (Source *) source {
1993 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
2000 - (NSString *) role {
2004 - (BOOL) matches:(NSString *)text {
2010 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2011 if (range.location != NSNotFound)
2014 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2015 if (range.location != NSNotFound)
2018 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2019 if (range.location != NSNotFound)
2025 - (bool) hasSupportingRole {
2028 if ([role_ isEqualToString:@"enduser"])
2030 if ([Role_ isEqualToString:@"User"])
2032 if ([role_ isEqualToString:@"hacker"])
2034 if ([Role_ isEqualToString:@"Hacker"])
2036 if ([role_ isEqualToString:@"developer"])
2038 if ([Role_ isEqualToString:@"Developer"])
2043 - (BOOL) hasTag:(NSString *)tag {
2044 return tags_ == nil ? NO : [tags_ containsObject:tag];
2047 - (NSString *) primaryPurpose {
2048 for (NSString *tag in tags_)
2049 if ([tag hasPrefix:@"purpose::"])
2050 return [tag substringFromIndex:9];
2054 - (NSArray *) purposes {
2055 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2056 for (NSString *tag in tags_)
2057 if ([tag hasPrefix:@"purpose::"])
2058 [purposes addObject:[tag substringFromIndex:9]];
2059 return [purposes count] == 0 ? nil : purposes;
2062 - (NSComparisonResult) compareByName:(Package *)package {
2063 NSString *lhs = [self name];
2064 NSString *rhs = [package name];
2066 if ([lhs length] != 0 && [rhs length] != 0) {
2067 unichar lhc = [lhs characterAtIndex:0];
2068 unichar rhc = [rhs characterAtIndex:0];
2070 if (isalpha(lhc) && !isalpha(rhc))
2071 return NSOrderedAscending;
2072 else if (!isalpha(lhc) && isalpha(rhc))
2073 return NSOrderedDescending;
2076 return [lhs compare:rhs options:LaxCompareOptions_];
2079 - (NSComparisonResult) compareBySection:(Package *)package {
2080 NSString *lhs = [self section];
2081 NSString *rhs = [package section];
2083 if (lhs == NULL && rhs != NULL)
2084 return NSOrderedAscending;
2085 else if (lhs != NULL && rhs == NULL)
2086 return NSOrderedDescending;
2087 else if (lhs != NULL && rhs != NULL) {
2088 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2089 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2092 return NSOrderedSame;
2095 - (uint32_t) compareForChanges {
2100 uint32_t timestamp : 30;
2101 uint32_t ignored : 1;
2102 uint32_t upgradable : 1;
2106 bool upgradable([self upgradableAndEssential:YES]);
2107 value.bits.upgradable = upgradable ? 1 : 0;
2110 value.bits.timestamp = 0;
2111 value.bits.ignored = [self ignored] ? 0 : 1;
2112 value.bits.upgradable = 1;
2114 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2115 value.bits.ignored = 0;
2116 value.bits.upgradable = 0;
2119 return _not(uint32_t) - value.key;
2123 pkgProblemResolver *resolver = [database_ resolver];
2124 resolver->Clear(iterator_);
2125 resolver->Protect(iterator_);
2126 pkgCacheFile &cache([database_ cache]);
2127 cache->MarkInstall(iterator_, false);
2128 pkgDepCache::StateCache &state((*cache)[iterator_]);
2129 if (!state.Install())
2130 cache->SetReInstall(iterator_, true);
2134 pkgProblemResolver *resolver = [database_ resolver];
2135 resolver->Clear(iterator_);
2136 resolver->Protect(iterator_);
2137 resolver->Remove(iterator_);
2138 [database_ cache]->MarkDelete(iterator_, true);
2141 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
2142 _profile(Package$isUnfilteredAndSearchedForBy)
2145 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2146 value &= [self unfiltered];
2149 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2150 value &= [self matches:search];
2157 - (bool) isInstalledAndVisible:(NSNumber *)number {
2158 return (![number boolValue] || [self visible]) && [self installed] != nil;
2161 - (bool) isVisiblyUninstalledInSection:(NSString *)name {
2162 NSString *section = [self section];
2166 [self installed] == nil && (
2168 section == nil && [name length] == 0 ||
2169 [name isEqualToString:section]
2173 - (bool) isVisibleInSource:(Source *)source {
2174 return [self source] == source && [self visible];
2179 /* Section Class {{{ */
2180 @interface Section : NSObject {
2187 - (NSComparisonResult) compareByName:(Section *)section;
2188 - (Section *) initWithName:(NSString *)name;
2189 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2190 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2191 - (NSString *) name;
2195 - (void) addToCount;
2199 @implementation Section
2206 - (NSComparisonResult) compareByName:(Section *)section {
2207 NSString *lhs = [self name];
2208 NSString *rhs = [section name];
2210 if ([lhs length] != 0 && [rhs length] != 0) {
2211 unichar lhc = [lhs characterAtIndex:0];
2212 unichar rhc = [rhs characterAtIndex:0];
2214 if (isalpha(lhc) && !isalpha(rhc))
2215 return NSOrderedAscending;
2216 else if (!isalpha(lhc) && isalpha(rhc))
2217 return NSOrderedDescending;
2220 return [lhs compare:rhs options:LaxCompareOptions_];
2223 - (Section *) initWithName:(NSString *)name {
2224 return [self initWithName:name row:0];
2227 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2228 if ((self = [super init]) != nil) {
2229 name_ = [name retain];
2235 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2236 if ((self = [super init]) != nil) {
2237 name_ = [[NSString stringWithCharacters:&index length:1] retain];
2243 - (NSString *) name {
2259 - (void) addToCount {
2267 static NSArray *Finishes_;
2269 /* Database Implementation {{{ */
2270 @implementation Database
2272 + (Database *) sharedInstance {
2273 static Database *instance;
2274 if (instance == nil)
2275 instance = [[Database alloc] init];
2284 - (void) _readCydia:(NSNumber *)fd { _pooled
2285 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2286 std::istream is(&ib);
2289 static Pcre finish_r("^finish:([^:]*)$");
2291 while (std::getline(is, line)) {
2292 const char *data(line.c_str());
2293 size_t size = line.size();
2294 lprintf("C:%s\n", data);
2296 if (finish_r(data, size)) {
2297 NSString *finish = finish_r[1];
2298 int index = [Finishes_ indexOfObject:finish];
2299 if (index != INT_MAX && index > Finish_)
2307 - (void) _readStatus:(NSNumber *)fd { _pooled
2308 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2309 std::istream is(&ib);
2312 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2313 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2315 while (std::getline(is, line)) {
2316 const char *data(line.c_str());
2317 size_t size = line.size();
2318 lprintf("S:%s\n", data);
2320 if (conffile_r(data, size)) {
2321 [delegate_ setConfigurationData:conffile_r[1]];
2322 } else if (strncmp(data, "status: ", 8) == 0) {
2323 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2324 [delegate_ setProgressTitle:string];
2325 } else if (pmstatus_r(data, size)) {
2326 std::string type([pmstatus_r[1] UTF8String]);
2327 NSString *id = pmstatus_r[2];
2329 float percent([pmstatus_r[3] floatValue]);
2330 [delegate_ setProgressPercent:(percent / 100)];
2332 NSString *string = pmstatus_r[4];
2334 if (type == "pmerror")
2335 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2336 withObject:[NSArray arrayWithObjects:string, id, nil]
2339 else if (type == "pmstatus") {
2340 [delegate_ setProgressTitle:string];
2341 } else if (type == "pmconffile")
2342 [delegate_ setConfigurationData:string];
2343 else _assert(false);
2344 } else _assert(false);
2350 - (void) _readOutput:(NSNumber *)fd { _pooled
2351 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2352 std::istream is(&ib);
2355 while (std::getline(is, line)) {
2356 lprintf("O:%s\n", line.c_str());
2357 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2367 - (Package *) packageWithName:(NSString *)name {
2368 if (static_cast<pkgDepCache *>(cache_) == NULL)
2370 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2371 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2374 - (Database *) init {
2375 if ((self = [super init]) != nil) {
2382 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2383 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2387 _assert(pipe(fds) != -1);
2390 _config->Set("APT::Keep-Fds::", cydiafd_);
2391 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2394 detachNewThreadSelector:@selector(_readCydia:)
2396 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2399 _assert(pipe(fds) != -1);
2403 detachNewThreadSelector:@selector(_readStatus:)
2405 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2408 _assert(pipe(fds) != -1);
2409 _assert(dup2(fds[0], 0) != -1);
2410 _assert(close(fds[0]) != -1);
2412 input_ = fdopen(fds[1], "a");
2414 _assert(pipe(fds) != -1);
2415 _assert(dup2(fds[1], 1) != -1);
2416 _assert(close(fds[1]) != -1);
2419 detachNewThreadSelector:@selector(_readOutput:)
2421 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2426 - (pkgCacheFile &) cache {
2430 - (pkgDepCache::Policy *) policy {
2434 - (pkgRecords *) records {
2438 - (pkgProblemResolver *) resolver {
2442 - (pkgAcquire &) fetcher {
2446 - (pkgSourceList &) list {
2450 - (NSArray *) packages {
2454 - (NSArray *) sources {
2455 return [sources_ allValues];
2458 - (NSArray *) issues {
2459 if (cache_->BrokenCount() == 0)
2462 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2464 for (Package *package in packages_) {
2465 if (![package broken])
2467 pkgCache::PkgIterator pkg([package iterator]);
2469 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2470 [entry addObject:[package name]];
2471 [issues addObject:entry];
2473 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2477 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2478 pkgCache::DepIterator start;
2479 pkgCache::DepIterator end;
2480 dep.GlobOr(start, end); // ++dep
2482 if (!cache_->IsImportantDep(end))
2484 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2487 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2488 [entry addObject:failure];
2489 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2491 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2492 [failure addObject:[package name]];
2494 pkgCache::PkgIterator target(start.TargetPkg());
2495 if (target->ProvidesList != 0)
2496 [failure addObject:@"?"];
2498 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2500 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2501 else if (!cache_[target].CandidateVerIter(cache_).end())
2502 [failure addObject:@"-"];
2503 else if (target->ProvidesList == 0)
2504 [failure addObject:@"!"];
2506 [failure addObject:@"%"];
2510 if (start.TargetVer() != 0)
2511 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2522 - (void) reloadData { _pooled
2542 if (!cache_.Open(progress_, true)) {
2544 if (!_error->PopMessage(error))
2547 lprintf("cache_.Open():[%s]\n", error.c_str());
2549 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2550 [delegate_ repairWithSelector:@selector(configure)];
2551 else if (error == "The package lists or status file could not be parsed or opened.")
2552 [delegate_ repairWithSelector:@selector(update)];
2553 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2554 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2555 // else if (error == "The list of sources could not be read.")
2556 else _assert(false);
2562 now_ = [[NSDate date] retain];
2564 policy_ = new pkgDepCache::Policy();
2565 records_ = new pkgRecords(cache_);
2566 resolver_ = new pkgProblemResolver(cache_);
2567 fetcher_ = new pkgAcquire(&status_);
2570 list_ = new pkgSourceList();
2571 _assert(list_->ReadMainList());
2573 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2574 _assert(pkgApplyStatus(cache_));
2576 if (cache_->BrokenCount() != 0) {
2577 _assert(pkgFixBroken(cache_));
2578 _assert(cache_->BrokenCount() == 0);
2579 _assert(pkgMinimizeUpgrade(cache_));
2582 [sources_ removeAllObjects];
2583 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2584 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2585 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2587 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2588 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2592 [packages_ removeAllObjects];
2594 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2595 if (Package *package = [Package packageWithIterator:iterator database:self])
2596 [packages_ addObject:package];
2598 [packages_ sortUsingSelector:@selector(compareByName:)];
2601 _config->Set("Acquire::http::Timeout", 15);
2602 _config->Set("Acquire::http::MaxParallel", 4);
2605 - (void) configure {
2606 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2607 system([dpkg UTF8String]);
2615 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2616 _assert(!_error->PendingError());
2619 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2622 public pkgArchiveCleaner
2625 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2630 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2632 while (_error->PopMessage(error))
2633 lprintf("ArchiveCleaner: %s\n", error.c_str());
2638 pkgRecords records(cache_);
2640 lock_ = new FileFd();
2641 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2642 _assert(!_error->PendingError());
2645 // XXX: explain this with an error message
2646 _assert(list.ReadMainList());
2648 manager_ = (_system->CreatePM(cache_));
2649 _assert(manager_->GetArchives(fetcher_, &list, &records));
2650 _assert(!_error->PendingError());
2654 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2656 _assert(list.ReadMainList());
2657 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2658 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2661 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2666 bool failed = false;
2667 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2668 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2671 std::string uri = (*item)->DescURI();
2672 std::string error = (*item)->ErrorText;
2674 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2677 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2678 withObject:[NSArray arrayWithObjects:
2679 [NSString stringWithUTF8String:error.c_str()],
2691 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2693 if (_error->PendingError()) {
2698 if (result == pkgPackageManager::Failed) {
2703 if (result != pkgPackageManager::Completed) {
2708 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2710 _assert(list.ReadMainList());
2711 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2712 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2715 if (![before isEqualToArray:after])
2720 _assert(pkgDistUpgrade(cache_));
2724 [self updateWithStatus:status_];
2727 - (void) updateWithStatus:(Status &)status {
2729 _assert(list.ReadMainList());
2732 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2733 _assert(!_error->PendingError());
2735 pkgAcquire fetcher(&status);
2736 _assert(list.GetIndexes(&fetcher));
2738 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2739 bool failed = false;
2740 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2741 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2742 (*item)->Finished();
2746 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2747 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2748 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2751 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2756 - (void) setDelegate:(id)delegate {
2757 delegate_ = delegate;
2758 status_.setDelegate(delegate);
2759 progress_.setDelegate(delegate);
2762 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2763 pkgIndexFile *index(NULL);
2764 list_->FindIndex(file, index);
2765 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2771 /* PopUp Windows {{{ */
2772 @interface PopUpView : UIView {
2773 _transient id delegate_;
2774 UITransitionView *transition_;
2779 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2783 @implementation PopUpView
2786 [transition_ setDelegate:nil];
2787 [transition_ release];
2793 [transition_ transition:UITransitionPushFromTop toView:nil];
2796 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2797 if (from != nil && to == nil)
2798 [self removeFromSuperview];
2801 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2802 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2803 delegate_ = delegate;
2805 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2806 [self addSubview:transition_];
2808 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2810 [view addSubview:self];
2812 [transition_ setDelegate:self];
2814 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2815 [transition_ transition:UITransitionNone toView:blank];
2816 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2823 /* Mail Composition {{{ */
2824 @interface MailToView : PopUpView {
2825 MailComposeController *controller_;
2828 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2832 @implementation MailToView
2835 [controller_ release];
2839 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2843 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2844 NSLog(@"did:%@", delivery);
2845 // [UIApp setStatusBarShowsProgress:NO];
2846 if ([controller error]){
2847 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2848 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2849 [mailAlertSheet setBodyText:[controller error]];
2850 [mailAlertSheet popupAlertAnimated:YES];
2854 - (void) showError {
2855 NSLog(@"%@", [controller_ error]);
2856 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2857 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2858 [mailAlertSheet setBodyText:[controller_ error]];
2859 [mailAlertSheet popupAlertAnimated:YES];
2862 - (void) deliverMessage { _pooled
2866 if (![controller_ deliverMessage])
2867 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2870 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2871 if ([controller_ needsDelivery])
2872 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2877 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2878 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2879 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2880 [controller_ setDelegate:self];
2881 [controller_ initializeUI];
2882 [controller_ setupForURL:url];
2884 UIView *view([controller_ view]);
2885 [overlay_ addSubview:view];
2891 /* Confirmation View {{{ */
2892 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2893 if (!iterator.end())
2894 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2895 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2897 pkgCache::PkgIterator package(dep.TargetPkg());
2900 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2907 @protocol ConfirmationViewDelegate
2912 @interface ConfirmationView : BrowserView {
2913 _transient Database *database_;
2914 UIActionSheet *essential_;
2921 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2925 @implementation ConfirmationView
2932 if (essential_ != nil)
2933 [essential_ release];
2939 [book_ popFromSuperviewAnimated:YES];
2942 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2943 NSString *context([sheet context]);
2945 if ([context isEqualToString:@"remove"]) {
2953 [delegate_ confirm];
2960 } else if ([context isEqualToString:@"unable"]) {
2964 [super alertSheet:sheet buttonClicked:button];
2967 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2968 [window setValue:changes_ forKey:@"changes"];
2969 [window setValue:issues_ forKey:@"issues"];
2970 [window setValue:sizes_ forKey:@"sizes"];
2971 [super webView:sender didClearWindowObject:window forFrame:frame];
2974 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2975 if ((self = [super initWithBook:book]) != nil) {
2976 database_ = database;
2978 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2979 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2980 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2981 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2982 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2986 pkgDepCache::Policy *policy([database_ policy]);
2988 pkgCacheFile &cache([database_ cache]);
2989 NSArray *packages = [database_ packages];
2990 for (Package *package in packages) {
2991 pkgCache::PkgIterator iterator = [package iterator];
2992 pkgDepCache::StateCache &state(cache[iterator]);
2994 NSString *name([package name]);
2996 if (state.NewInstall())
2997 [installing addObject:name];
2998 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2999 [reinstalling addObject:name];
3000 else if (state.Upgrade())
3001 [upgrading addObject:name];
3002 else if (state.Downgrade())
3003 [downgrading addObject:name];
3004 else if (state.Delete()) {
3005 if ([package essential])
3007 [removing addObject:name];
3010 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3011 substrate_ |= DepSubstrate(iterator.CurrentVer());
3016 else if (Advanced_ || true) {
3017 essential_ = [[UIActionSheet alloc]
3018 initWithTitle:@"Removing Essentials"
3019 buttons:[NSArray arrayWithObjects:
3020 @"Cancel Operation (Safe)",
3021 @"Force Removal (Unsafe)",
3023 defaultButtonIndex:0
3029 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3031 [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."];
3033 essential_ = [[UIActionSheet alloc]
3034 initWithTitle:@"Unable to Comply"
3035 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3036 defaultButtonIndex:0
3041 [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."];
3044 changes_ = [[NSArray alloc] initWithObjects:
3052 issues_ = [database_ issues];
3054 issues_ = [issues_ retain];
3056 sizes_ = [[NSArray alloc] initWithObjects:
3057 SizeString([database_ fetcher].FetchNeeded()),
3058 SizeString([database_ fetcher].PartialPresent()),
3059 SizeString([database_ cache]->UsrSize()),
3062 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3066 - (NSString *) backButtonTitle {
3070 - (NSString *) leftButtonTitle {
3074 - (id) _rightButtonTitle {
3075 #if AlwaysReload || IgnoreInstall
3078 return issues_ == nil ? @"Confirm" : nil;
3082 - (void) _leftButtonClicked {
3087 - (void) _rightButtonClicked {
3089 return [super _rightButtonClicked];
3091 if (essential_ != nil)
3092 [essential_ popupAlertAnimated:YES];
3096 [delegate_ confirm];
3104 /* Progress Data {{{ */
3105 @interface ProgressData : NSObject {
3111 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3118 @implementation ProgressData
3120 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3121 if ((self = [super init]) != nil) {
3122 selector_ = selector;
3142 /* Progress View {{{ */
3143 @interface ProgressView : UIView <
3144 ConfigurationDelegate,
3147 _transient Database *database_;
3149 UIView *background_;
3150 UITransitionView *transition_;
3152 UINavigationBar *navbar_;
3153 UIProgressBar *progress_;
3154 UITextView *output_;
3155 UITextLabel *status_;
3156 UIPushButton *close_;
3159 SHA1SumValue springlist_;
3160 SHA1SumValue notifyconf_;
3161 SHA1SumValue sandplate_;
3164 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3166 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3167 - (void) setContentView:(UIView *)view;
3170 - (void) _retachThread;
3171 - (void) _detachNewThreadData:(ProgressData *)data;
3172 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3178 @protocol ProgressViewDelegate
3179 - (void) progressViewIsComplete:(ProgressView *)sender;
3182 @implementation ProgressView
3185 [transition_ setDelegate:nil];
3186 [navbar_ setDelegate:nil];
3189 if (background_ != nil)
3190 [background_ release];
3191 [transition_ release];
3194 [progress_ release];
3201 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3202 if (bootstrap_ && from == overlay_ && to == view_)
3206 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3207 if ((self = [super initWithFrame:frame]) != nil) {
3208 database_ = database;
3209 delegate_ = delegate;
3211 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3212 [transition_ setDelegate:self];
3214 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3217 [overlay_ setBackgroundColor:[UIColor blackColor]];
3219 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3220 [background_ setBackgroundColor:[UIColor blackColor]];
3221 [self addSubview:background_];
3224 [self addSubview:transition_];
3226 CGSize navsize = [UINavigationBar defaultSize];
3227 CGRect navrect = {{0, 0}, navsize};
3229 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3230 [overlay_ addSubview:navbar_];
3232 [navbar_ setBarStyle:1];
3233 [navbar_ setDelegate:self];
3235 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3236 [navbar_ pushNavigationItem:navitem];
3238 CGRect bounds = [overlay_ bounds];
3239 CGSize prgsize = [UIProgressBar defaultSize];
3242 (bounds.size.width - prgsize.width) / 2,
3243 bounds.size.height - prgsize.height - 20
3246 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3247 [progress_ setStyle:0];
3249 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3251 bounds.size.height - prgsize.height - 50,
3252 bounds.size.width - 20,
3256 [status_ setColor:[UIColor whiteColor]];
3257 [status_ setBackgroundColor:[UIColor clearColor]];
3259 [status_ setCentersHorizontally:YES];
3260 //[status_ setFont:font];
3263 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3265 navrect.size.height + 20,
3266 bounds.size.width - 20,
3267 bounds.size.height - navsize.height - 62 - navrect.size.height
3271 //[output_ setTextFont:@"Courier New"];
3272 [output_ setTextSize:12];
3274 [output_ setTextColor:[UIColor whiteColor]];
3275 [output_ setBackgroundColor:[UIColor clearColor]];
3277 [output_ setMarginTop:0];
3278 [output_ setAllowsRubberBanding:YES];
3279 [output_ setEditable:NO];
3281 [overlay_ addSubview:output_];
3283 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3285 bounds.size.height - prgsize.height - 50,
3286 bounds.size.width - 20,
3290 [close_ setAutosizesToFit:NO];
3291 [close_ setDrawsShadow:YES];
3292 [close_ setStretchBackground:YES];
3293 [close_ setEnabled:YES];
3295 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3296 [close_ setTitleFont:bold];
3298 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3299 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3300 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3304 - (void) setContentView:(UIView *)view {
3305 view_ = [view retain];
3308 - (void) resetView {
3309 [transition_ transition:6 toView:view_];
3312 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3313 NSString *context([sheet context]);
3315 if ([context isEqualToString:@"error"])
3317 else if ([context isEqualToString:@"conffile"]) {
3318 FILE *input = [database_ input];
3322 fprintf(input, "N\n");
3326 fprintf(input, "Y\n");
3337 - (void) closeButtonPushed {
3346 [delegate_ suspendWithAnimation:YES];
3350 system("launchctl stop com.apple.SpringBoard");
3354 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3363 - (void) _retachThread {
3364 UINavigationItem *item = [navbar_ topItem];
3365 [item setTitle:@"Complete"];
3367 [overlay_ addSubview:close_];
3368 [progress_ removeFromSuperview];
3369 [status_ removeFromSuperview];
3371 [delegate_ progressViewIsComplete:self];
3374 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3375 MMap mmap(file, MMap::ReadOnly);
3377 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3378 if (!(sandplate_ == sha1.Result()))
3383 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3384 MMap mmap(file, MMap::ReadOnly);
3386 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3387 if (!(notifyconf_ == sha1.Result()))
3392 FileFd file(SpringBoard_, FileFd::ReadOnly);
3393 MMap mmap(file, MMap::ReadOnly);
3395 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3396 if (!(springlist_ == sha1.Result()))
3401 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3402 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3403 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3404 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3405 case 4: [close_ setTitle:@"Reboot Device"]; break;
3408 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3410 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3411 [cache autorelease];
3413 NSFileManager *manager = [NSFileManager defaultManager];
3414 NSError *error = nil;
3416 id system = [cache objectForKey:@"System"];
3421 if (stat(Cache_, &info) == -1)
3424 [system removeAllObjects];
3426 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3427 for (NSString *app in apps)
3428 if ([app hasSuffix:@".app"]) {
3429 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3430 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3431 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3433 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3434 [info setObject:path forKey:@"Path"];
3435 [info setObject:@"System" forKey:@"ApplicationType"];
3436 [system addInfoDictionary:info];
3442 [cache writeToFile:@Cache_ atomically:YES];
3444 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3446 if (chmod(Cache_, info.st_mode) == -1)
3450 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3453 notify_post("com.apple.mobile.application_installed");
3455 [delegate_ setStatusBarShowsProgress:NO];
3458 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3459 [[data target] performSelector:[data selector] withObject:[data object]];
3462 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3465 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3466 UINavigationItem *item = [navbar_ topItem];
3467 [item setTitle:title];
3469 [status_ setText:nil];
3470 [output_ setText:@""];
3471 [progress_ setProgress:0];
3473 [close_ removeFromSuperview];
3474 [overlay_ addSubview:progress_];
3475 [overlay_ addSubview:status_];
3477 [delegate_ setStatusBarShowsProgress:YES];
3481 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3482 MMap mmap(file, MMap::ReadOnly);
3484 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3485 sandplate_ = sha1.Result();
3489 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3490 MMap mmap(file, MMap::ReadOnly);
3492 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3493 notifyconf_ = sha1.Result();
3497 FileFd file(SpringBoard_, FileFd::ReadOnly);
3498 MMap mmap(file, MMap::ReadOnly);
3500 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3501 springlist_ = sha1.Result();
3504 [transition_ transition:6 toView:overlay_];
3507 detachNewThreadSelector:@selector(_detachNewThreadData:)
3509 withObject:[[ProgressData alloc]
3510 initWithSelector:selector
3517 - (void) repairWithSelector:(SEL)selector {
3519 detachNewThreadSelector:selector
3526 - (void) setConfigurationData:(NSString *)data {
3528 performSelectorOnMainThread:@selector(_setConfigurationData:)
3534 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3535 Package *package = id == nil ? nil : [database_ packageWithName:id];
3537 UIActionSheet *sheet = [[[UIActionSheet alloc]
3538 initWithTitle:(package == nil ? id : [package name])
3539 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3540 defaultButtonIndex:0
3545 [sheet setBodyText:error];
3546 [sheet popupAlertAnimated:YES];
3549 - (void) setProgressTitle:(NSString *)title {
3551 performSelectorOnMainThread:@selector(_setProgressTitle:)
3557 - (void) setProgressPercent:(float)percent {
3559 performSelectorOnMainThread:@selector(_setProgressPercent:)
3560 withObject:[NSNumber numberWithFloat:percent]
3565 - (void) startProgress {
3568 - (void) addProgressOutput:(NSString *)output {
3570 performSelectorOnMainThread:@selector(_addProgressOutput:)
3576 - (bool) isCancelling:(size_t)received {
3580 - (void) _setConfigurationData:(NSString *)data {
3581 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3583 _assert(conffile_r(data));
3585 NSString *ofile = conffile_r[1];
3586 //NSString *nfile = conffile_r[2];
3588 UIActionSheet *sheet = [[[UIActionSheet alloc]
3589 initWithTitle:@"Configuration Upgrade"
3590 buttons:[NSArray arrayWithObjects:
3591 @"Keep My Old Copy",
3592 @"Accept The New Copy",
3593 // XXX: @"See What Changed",
3595 defaultButtonIndex:0
3600 [sheet setBodyText:[NSString stringWithFormat:
3601 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3604 [sheet popupAlertAnimated:YES];
3607 - (void) _setProgressTitle:(NSString *)title {
3608 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3609 for (size_t i(0), e([words count]); i != e; ++i) {
3610 NSString *word([words objectAtIndex:i]);
3611 if (Package *package = [database_ packageWithName:word])
3612 [words replaceObjectAtIndex:i withObject:[package name]];
3615 [status_ setText:[words componentsJoinedByString:@" "]];
3618 - (void) _setProgressPercent:(NSNumber *)percent {
3619 [progress_ setProgress:[percent floatValue]];
3622 - (void) _addProgressOutput:(NSString *)output {
3623 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3624 CGSize size = [output_ contentSize];
3625 CGRect rect = {{0, size.height}, {size.width, 0}};
3626 [output_ scrollRectToVisible:rect animated:YES];
3629 - (BOOL) isRunning {
3636 /* Package Cell {{{ */
3637 @interface PackageCell : UISimpleTableCell {
3640 NSString *description_;
3644 UITextLabel *status_;
3648 - (PackageCell *) init;
3649 - (void) setPackage:(Package *)package;
3651 + (int) heightForPackage:(Package *)package;
3655 @implementation PackageCell
3657 - (void) clearPackage {
3668 if (description_ != nil) {
3669 [description_ release];
3673 if (source_ != nil) {
3678 if (badge_ != nil) {
3685 [self clearPackage];
3692 - (PackageCell *) init {
3693 if ((self = [super init]) != nil) {
3695 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3696 [status_ setBackgroundColor:[UIColor clearColor]];
3697 [status_ setFont:small];
3702 - (void) setPackage:(Package *)package {
3703 [self clearPackage];
3705 Source *source = [package source];
3706 NSString *section = [package simpleSection];
3708 icon_ = [[package icon] retain];
3710 name_ = [[package name] retain];
3711 description_ = [[package tagline] retain];
3713 NSString *label = nil;
3714 bool trusted = false;
3716 if (source != nil) {
3717 label = [source label];
3718 trusted = [source trusted];
3719 } else if ([[package id] isEqualToString:@"firmware"])
3722 label = @"Unknown/Local";
3724 NSString *from = [NSString stringWithFormat:@"from %@", label];
3726 if (section != nil && ![section isEqualToString:label])
3727 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3729 source_ = [from retain];
3731 if (NSString *purpose = [package primaryPurpose])
3732 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3733 badge_ = [badge_ retain];
3736 if (NSString *mode = [package mode]) {
3737 [badge_ setImage:[UIImage applicationImageNamed:
3738 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3741 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3742 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3743 } else if ([package half]) {
3744 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3745 [status_ setText:@"Package Damaged"];
3746 [status_ setColor:[UIColor redColor]];
3748 [badge_ setImage:nil];
3749 [status_ setText:nil];
3754 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3757 rect.size = [icon_ size];
3759 rect.size.width /= 2;
3760 rect.size.height /= 2;
3762 rect.origin.x = 25 - rect.size.width / 2;
3763 rect.origin.y = 25 - rect.size.height / 2;
3765 [icon_ drawInRect:rect];
3768 if (badge_ != nil) {
3769 CGSize size = [badge_ size];
3771 [badge_ drawAtPoint:CGPointMake(
3772 36 - size.width / 2,
3773 36 - size.height / 2
3782 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3783 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3787 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3789 [super drawContentInRect:rect selected:selected];
3792 + (int) heightForPackage:(Package *)package {
3793 NSString *tagline([package tagline]);
3794 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3796 if ([package hasMode] || [package half])
3805 /* Section Cell {{{ */
3806 @interface SectionCell : UISimpleTableCell {
3811 _UISwitchSlider *switch_;
3816 - (void) setSection:(Section *)section editing:(BOOL)editing;
3820 @implementation SectionCell
3822 - (void) clearSection {
3823 if (section_ != nil) {
3833 if (count_ != nil) {
3840 [self clearSection];
3847 if ((self = [super init]) != nil) {
3848 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3850 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3851 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3855 - (void) onSwitch:(id)sender {
3856 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3857 if (metadata == nil) {
3858 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3859 [Sections_ setObject:metadata forKey:section_];
3863 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3866 - (void) setSection:(Section *)section editing:(BOOL)editing {
3867 if (editing != editing_) {
3869 [switch_ removeFromSuperview];
3871 [self addSubview:switch_];
3875 [self clearSection];
3877 if (section == nil) {
3878 name_ = [@"All Packages" retain];
3881 section_ = [section name];
3882 if (section_ != nil)
3883 section_ = [section_ retain];
3884 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3885 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3888 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3892 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3893 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3900 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3902 CGSize size = [count_ sizeWithFont:Font14_];
3906 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3908 [super drawContentInRect:rect selected:selected];
3914 /* File Table {{{ */
3915 @interface FileTable : RVPage {
3916 _transient Database *database_;
3919 NSMutableArray *files_;
3923 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3924 - (void) setPackage:(Package *)package;
3928 @implementation FileTable
3931 if (package_ != nil)
3940 - (int) numberOfRowsInTable:(UITable *)table {
3941 return files_ == nil ? 0 : [files_ count];
3944 - (float) table:(UITable *)table heightForRow:(int)row {
3948 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3949 if (reusing == nil) {
3950 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3951 UIFont *font = [UIFont systemFontOfSize:16];
3952 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3954 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3958 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3962 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3963 if ((self = [super initWithBook:book]) != nil) {
3964 database_ = database;
3966 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3968 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3969 [self addSubview:list_];
3971 UITableColumn *column = [[[UITableColumn alloc]
3972 initWithTitle:@"Name"
3974 width:[self frame].size.width
3977 [list_ setDataSource:self];
3978 [list_ setSeparatorStyle:1];
3979 [list_ addTableColumn:column];
3980 [list_ setDelegate:self];
3981 [list_ setReusesTableCells:YES];
3985 - (void) setPackage:(Package *)package {
3986 if (package_ != nil) {
3987 [package_ autorelease];
3996 [files_ removeAllObjects];
3998 if (package != nil) {
3999 package_ = [package retain];
4000 name_ = [[package id] retain];
4002 if (NSArray *files = [package files])
4003 [files_ addObjectsFromArray:files];
4005 if ([files_ count] != 0) {
4006 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4007 [files_ removeObjectAtIndex:0];
4008 [files_ sortUsingSelector:@selector(compareByPath:)];
4010 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4011 [stack addObject:@"/"];
4013 for (int i(0), e([files_ count]); i != e; ++i) {
4014 NSString *file = [files_ objectAtIndex:i];
4015 while (![file hasPrefix:[stack lastObject]])
4016 [stack removeLastObject];
4017 NSString *directory = [stack lastObject];
4018 [stack addObject:[file stringByAppendingString:@"/"]];
4019 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4020 ([stack count] - 2) * 3, "",
4021 [file substringFromIndex:[directory length]]
4030 - (void) resetViewAnimated:(BOOL)animated {
4031 [list_ resetViewAnimated:animated];
4034 - (void) reloadData {
4035 [self setPackage:[database_ packageWithName:name_]];
4036 [self reloadButtons];
4039 - (NSString *) title {
4040 return @"Installed Files";
4043 - (NSString *) backButtonTitle {
4049 /* Package View {{{ */
4050 @interface PackageView : BrowserView {
4051 _transient Database *database_;
4054 NSMutableArray *buttons_;
4057 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4058 - (void) setPackage:(Package *)package;
4062 @implementation PackageView
4065 if (package_ != nil)
4073 - (void) _clickButtonWithName:(NSString *)name {
4074 if ([name isEqualToString:@"Install"])
4075 [delegate_ installPackage:package_];
4076 else if ([name isEqualToString:@"Reinstall"])
4077 [delegate_ installPackage:package_];
4078 else if ([name isEqualToString:@"Remove"])
4079 [delegate_ removePackage:package_];
4080 else if ([name isEqualToString:@"Upgrade"])
4081 [delegate_ installPackage:package_];
4082 else _assert(false);
4085 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4086 NSString *context([sheet context]);
4088 if ([context isEqualToString:@"modify"]) {
4089 int count = [buttons_ count];
4090 _assert(count != 0);
4091 _assert(button <= count + 1);
4093 if (count != button - 1)
4094 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4098 [super alertSheet:sheet buttonClicked:button];
4101 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4102 return [super webView:sender didFinishLoadForFrame:frame];
4105 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4106 [window setValue:package_ forKey:@"package"];
4107 [super webView:sender didClearWindowObject:window forFrame:frame];
4111 - (void) _rightButtonClicked {
4112 /*[super _rightButtonClicked];
4115 int count = [buttons_ count];
4116 _assert(count != 0);
4119 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4121 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4122 [buttons addObjectsFromArray:buttons_];
4123 [buttons addObject:@"Cancel"];
4125 [delegate_ slideUp:[[[UIActionSheet alloc]
4128 defaultButtonIndex:2
4136 - (id) _rightButtonTitle {
4137 int count = [buttons_ count];
4138 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4141 - (NSString *) backButtonTitle {
4145 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4146 if ((self = [super initWithBook:book]) != nil) {
4147 database_ = database;
4148 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4152 - (void) setPackage:(Package *)package {
4153 if (package_ != nil) {
4154 [package_ autorelease];
4163 [buttons_ removeAllObjects];
4165 if (package != nil) {
4166 package_ = [package retain];
4167 name_ = [[package id] retain];
4169 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4171 if ([package_ source] == nil);
4172 else if ([package_ upgradableAndEssential:NO])
4173 [buttons_ addObject:@"Upgrade"];
4174 else if ([package_ installed] == nil)
4175 [buttons_ addObject:@"Install"];
4177 [buttons_ addObject:@"Reinstall"];
4178 if ([package_ installed] != nil)
4179 [buttons_ addObject:@"Remove"];
4187 - (void) reloadData {
4188 [self setPackage:[database_ packageWithName:name_]];
4189 [self reloadButtons];
4194 /* Package Table {{{ */
4195 @interface PackageTable : RVPage {
4196 _transient Database *database_;
4198 NSMutableArray *packages_;
4199 NSMutableArray *sections_;
4200 UISectionList *list_;
4203 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4205 - (void) setDelegate:(id)delegate;
4207 - (void) reloadData;
4208 - (void) resetCursor;
4210 - (UISectionList *) list;
4212 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4216 @implementation PackageTable
4219 [list_ setDataSource:nil];
4222 [packages_ release];
4223 [sections_ release];
4228 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4229 return [sections_ count];
4232 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4233 return [[sections_ objectAtIndex:section] name];
4236 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4237 return [[sections_ objectAtIndex:section] row];
4240 - (int) numberOfRowsInTable:(UITable *)table {
4241 return [packages_ count];
4244 - (float) table:(UITable *)table heightForRow:(int)row {
4245 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4248 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4250 reusing = [[[PackageCell alloc] init] autorelease];
4251 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4255 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4259 - (void) tableRowSelected:(NSNotification *)notification {
4260 int row = [[notification object] selectedRow];
4264 Package *package = [packages_ objectAtIndex:row];
4265 package = [database_ packageWithName:[package id]];
4266 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4267 [view setPackage:package];
4268 [view setDelegate:delegate_];
4269 [book_ pushPage:view];
4272 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4273 if ((self = [super initWithBook:book]) != nil) {
4274 database_ = database;
4275 title_ = [title retain];
4277 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4278 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4280 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4281 [list_ setDataSource:self];
4283 UITableColumn *column = [[[UITableColumn alloc]
4284 initWithTitle:@"Name"
4286 width:[self frame].size.width
4289 UITable *table = [list_ table];
4290 [table setSeparatorStyle:1];
4291 [table addTableColumn:column];
4292 [table setDelegate:self];
4293 [table setReusesTableCells:YES];
4295 [self addSubview:list_];
4297 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4298 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4302 - (void) setDelegate:(id)delegate {
4303 delegate_ = delegate;
4306 - (bool) hasPackage:(Package *)package {
4310 - (void) reloadData {
4311 NSArray *packages = [database_ packages];
4313 [packages_ removeAllObjects];
4314 [sections_ removeAllObjects];
4316 _profile(PackageTable$reloadData$Filter)
4317 for (Package *package in packages)
4318 if ([self hasPackage:package])
4319 [packages_ addObject:package];
4322 Section *section = nil;
4324 _profile(PackageTable$reloadData$Section)
4325 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4329 _profile(PackageTable$reloadData$Section$Package)
4330 package = [packages_ objectAtIndex:offset];
4331 index = [package index];
4334 if (section == nil || [section index] != index) {
4335 _profile(PackageTable$reloadData$Section$Allocate)
4336 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4339 _profile(PackageTable$reloadData$Section$Add)
4340 [sections_ addObject:section];
4344 [section addToCount];
4348 _profile(PackageTable$reloadData$List)
4353 - (NSString *) title {
4357 - (void) resetViewAnimated:(BOOL)animated {
4358 [list_ resetViewAnimated:animated];
4361 - (void) resetCursor {
4362 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4365 - (UISectionList *) list {
4369 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4370 [list_ setShouldHideHeaderInShortLists:hide];
4375 /* Filtered Package Table {{{ */
4376 @interface FilteredPackageTable : PackageTable {
4381 - (void) setObject:(id)object;
4383 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4387 @implementation FilteredPackageTable
4395 - (void) setObject:(id)object {
4401 object_ = [object retain];
4404 - (bool) hasPackage:(Package *)package {
4405 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(&objc_msgSend))(package, filter_, object_);
4408 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4409 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4411 object_ = object == nil ? nil : [object retain];
4420 /* Add Source View {{{ */
4421 @interface AddSourceView : RVPage {
4422 _transient Database *database_;
4425 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4429 @implementation AddSourceView
4431 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4432 if ((self = [super initWithBook:book]) != nil) {
4433 database_ = database;
4439 /* Source Cell {{{ */
4440 @interface SourceCell : UITableCell {
4443 NSString *description_;
4449 - (SourceCell *) initWithSource:(Source *)source;
4453 @implementation SourceCell
4458 [description_ release];
4463 - (SourceCell *) initWithSource:(Source *)source {
4464 if ((self = [super init]) != nil) {
4466 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4468 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4469 icon_ = [icon_ retain];
4471 origin_ = [[source name] retain];
4472 label_ = [[source uri] retain];
4473 description_ = [[source description] retain];
4477 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4479 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4486 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4490 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4494 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4496 [super drawContentInRect:rect selected:selected];
4501 /* Source Table {{{ */
4502 @interface SourceTable : RVPage {
4503 _transient Database *database_;
4504 UISectionList *list_;
4505 NSMutableArray *sources_;
4506 UIActionSheet *alert_;
4510 UIProgressHUD *hud_;
4513 //NSURLConnection *installer_;
4514 NSURLConnection *trivial_bz2_;
4515 NSURLConnection *trivial_gz_;
4516 //NSURLConnection *automatic_;
4521 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4525 @implementation SourceTable
4527 - (void) _deallocConnection:(NSURLConnection *)connection {
4528 if (connection != nil) {
4529 [connection cancel];
4530 //[connection setDelegate:nil];
4531 [connection release];
4536 [[list_ table] setDelegate:nil];
4537 [list_ setDataSource:nil];
4546 //[self _deallocConnection:installer_];
4547 [self _deallocConnection:trivial_gz_];
4548 [self _deallocConnection:trivial_bz2_];
4549 //[self _deallocConnection:automatic_];
4556 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4557 return offset_ == 0 ? 1 : 2;
4560 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4561 switch (section + (offset_ == 0 ? 1 : 0)) {
4562 case 0: return @"Entered by User";
4563 case 1: return @"Installed by Packages";
4571 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4572 switch (section + (offset_ == 0 ? 1 : 0)) {
4574 case 1: return offset_;
4582 - (int) numberOfRowsInTable:(UITable *)table {
4583 return [sources_ count];
4586 - (float) table:(UITable *)table heightForRow:(int)row {
4587 Source *source = [sources_ objectAtIndex:row];
4588 return [source description] == nil ? 56 : 73;
4591 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4592 Source *source = [sources_ objectAtIndex:row];
4593 // XXX: weird warning, stupid selectors ;P
4594 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4597 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4601 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4605 - (void) tableRowSelected:(NSNotification*)notification {
4606 UITable *table([list_ table]);
4607 int row([table selectedRow]);
4611 Source *source = [sources_ objectAtIndex:row];
4613 PackageTable *packages = [[[FilteredPackageTable alloc]
4616 title:[source label]
4617 filter:@selector(isVisibleInSource:)
4621 [packages setDelegate:delegate_];
4623 [book_ pushPage:packages];
4626 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4627 Source *source = [sources_ objectAtIndex:row];
4628 return [source record] != nil;
4631 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4632 [[list_ table] setDeleteConfirmationRow:row];
4635 - (void) table:(UITable *)table deleteRow:(int)row {
4636 Source *source = [sources_ objectAtIndex:row];
4637 [Sources_ removeObjectForKey:[source key]];
4638 [delegate_ syncData];
4641 - (void) _endConnection:(NSURLConnection *)connection {
4642 NSURLConnection **field = NULL;
4643 if (connection == trivial_bz2_)
4644 field = &trivial_bz2_;
4645 else if (connection == trivial_gz_)
4646 field = &trivial_gz_;
4647 _assert(field != NULL);
4648 [connection release];
4652 trivial_bz2_ == nil &&
4655 [delegate_ setStatusBarShowsProgress:NO];
4656 [delegate_ removeProgressHUD:hud_];
4662 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4665 @"./", @"Distribution",
4666 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4668 [delegate_ syncData];
4669 } else if (error_ != nil) {
4670 UIActionSheet *sheet = [[[UIActionSheet alloc]
4671 initWithTitle:@"Verification Error"
4672 buttons:[NSArray arrayWithObjects:@"OK", nil]
4673 defaultButtonIndex:0
4678 [sheet setBodyText:[error_ localizedDescription]];
4679 [sheet popupAlertAnimated:YES];
4681 UIActionSheet *sheet = [[[UIActionSheet alloc]
4682 initWithTitle:@"Did not Find Repository"
4683 buttons:[NSArray arrayWithObjects:@"OK", nil]
4684 defaultButtonIndex:0
4689 [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."];
4690 [sheet popupAlertAnimated:YES];
4696 if (error_ != nil) {
4703 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4704 switch ([response statusCode]) {
4710 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4711 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4713 error_ = [error retain];
4714 [self _endConnection:connection];
4717 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4718 [self _endConnection:connection];
4721 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4722 NSMutableURLRequest *request = [NSMutableURLRequest
4723 requestWithURL:[NSURL URLWithString:href]
4724 cachePolicy:NSURLRequestUseProtocolCachePolicy
4725 timeoutInterval:20.0
4728 [request setHTTPMethod:method];
4730 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4733 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4734 NSString *context([sheet context]);
4736 if ([context isEqualToString:@"source"]) {
4739 NSString *href = [[sheet textField] text];
4741 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4743 if (![href hasSuffix:@"/"])
4744 href_ = [href stringByAppendingString:@"/"];
4747 href_ = [href_ retain];
4749 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4750 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4751 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4755 hud_ = [[delegate_ addProgressHUD] retain];
4756 [hud_ setText:@"Verifying URL"];
4767 } else if ([context isEqualToString:@"trivial"])
4769 else if ([context isEqualToString:@"urlerror"])
4773 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4774 if ((self = [super initWithBook:book]) != nil) {
4775 database_ = database;
4776 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4778 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4779 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4780 [list_ setShouldHideHeaderInShortLists:NO];
4782 [self addSubview:list_];
4783 [list_ setDataSource:self];
4785 UITableColumn *column = [[UITableColumn alloc]
4786 initWithTitle:@"Name"
4788 width:[self frame].size.width
4791 UITable *table = [list_ table];
4792 [table setSeparatorStyle:1];
4793 [table addTableColumn:column];
4794 [table setDelegate:self];
4798 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4799 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4803 - (void) reloadData {
4805 _assert(list.ReadMainList());
4807 [sources_ removeAllObjects];
4808 [sources_ addObjectsFromArray:[database_ sources]];
4810 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4813 int count = [sources_ count];
4814 for (offset_ = 0; offset_ != count; ++offset_) {
4815 Source *source = [sources_ objectAtIndex:offset_];
4816 if ([source record] == nil)
4823 - (void) resetViewAnimated:(BOOL)animated {
4824 [list_ resetViewAnimated:animated];
4827 - (void) _leftButtonClicked {
4828 /*[book_ pushPage:[[[AddSourceView alloc]
4833 UIActionSheet *sheet = [[[UIActionSheet alloc]
4834 initWithTitle:@"Enter Cydia/APT URL"
4835 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4836 defaultButtonIndex:0
4841 [sheet setNumberOfRows:1];
4843 [sheet addTextFieldWithValue:@"http://" label:@""];
4845 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4846 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4847 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4848 [traits setKeyboardType:UIKeyboardTypeURL];
4849 // XXX: UIReturnKeyDone
4850 [traits setReturnKeyType:UIReturnKeyNext];
4852 [sheet popupAlertAnimated:YES];
4855 - (void) _rightButtonClicked {
4856 UITable *table = [list_ table];
4857 BOOL editing = [table isRowDeletionEnabled];
4858 [table enableRowDeletion:!editing animated:YES];
4859 [book_ reloadButtonsForPage:self];
4862 - (NSString *) title {
4866 - (NSString *) leftButtonTitle {
4867 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4870 - (id) rightButtonTitle {
4871 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4874 - (UINavigationButtonStyle) rightButtonStyle {
4875 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4881 /* Installed View {{{ */
4882 @interface InstalledView : RVPage {
4883 _transient Database *database_;
4884 FilteredPackageTable *packages_;
4888 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4892 @implementation InstalledView
4895 [packages_ release];
4899 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4900 if ((self = [super initWithBook:book]) != nil) {
4901 database_ = database;
4903 packages_ = [[FilteredPackageTable alloc]
4907 filter:@selector(isInstalledAndVisible:)
4908 with:[NSNumber numberWithBool:YES]
4911 [self addSubview:packages_];
4913 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4914 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4918 - (void) resetViewAnimated:(BOOL)animated {
4919 [packages_ resetViewAnimated:animated];
4922 - (void) reloadData {
4923 [packages_ reloadData];
4926 - (void) _rightButtonClicked {
4927 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4928 [packages_ reloadData];
4930 [book_ reloadButtonsForPage:self];
4933 - (NSString *) title {
4934 return @"Installed";
4937 - (NSString *) backButtonTitle {
4941 - (id) rightButtonTitle {
4942 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4945 - (UINavigationButtonStyle) rightButtonStyle {
4946 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4949 - (void) setDelegate:(id)delegate {
4950 [super setDelegate:delegate];
4951 [packages_ setDelegate:delegate];
4958 @interface HomeView : BrowserView {
4963 @implementation HomeView
4965 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4966 NSString *context([sheet context]);
4968 if ([context isEqualToString:@"about"])
4971 [super alertSheet:sheet buttonClicked:button];
4974 - (void) _leftButtonClicked {
4975 UIActionSheet *sheet = [[[UIActionSheet alloc]
4976 initWithTitle:@"About Cydia Installer"
4977 buttons:[NSArray arrayWithObjects:@"Close", nil]
4978 defaultButtonIndex:0
4984 @"Copyright (C) 2008\n"
4985 "Jay Freeman (saurik)\n"
4986 "saurik@saurik.com\n"
4987 "http://www.saurik.com/\n"
4990 "http://www.theokorigroup.com/\n"
4992 "College of Creative Studies,\n"
4993 "University of California,\n"
4995 "http://www.ccs.ucsb.edu/"
4998 [sheet popupAlertAnimated:YES];
5001 - (NSString *) leftButtonTitle {
5007 /* Manage View {{{ */
5008 @interface ManageView : BrowserView {
5013 @implementation ManageView
5015 - (NSString *) title {
5019 - (void) _leftButtonClicked {
5020 [delegate_ askForSettings];
5023 - (NSString *) leftButtonTitle {
5028 - (id) _rightButtonTitle {
5040 #include <BrowserView.m>
5042 /* Cydia Book {{{ */
5043 @interface CYBook : RVBook <
5046 _transient Database *database_;
5047 UINavigationBar *overlay_;
5048 UINavigationBar *underlay_;
5049 UIProgressIndicator *indicator_;
5050 UITextLabel *prompt_;
5051 UIProgressBar *progress_;
5052 UINavigationButton *cancel_;
5056 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5062 @implementation CYBook
5066 [indicator_ release];
5068 [progress_ release];
5073 - (NSString *) getTitleForPage:(RVPage *)page {
5074 return Simplify([super getTitleForPage:page]);
5082 [UIView beginAnimations:nil context:NULL];
5084 CGRect ovrframe = [overlay_ frame];
5085 ovrframe.origin.y = 0;
5086 [overlay_ setFrame:ovrframe];
5088 CGRect barframe = [navbar_ frame];
5089 barframe.origin.y += ovrframe.size.height;
5090 [navbar_ setFrame:barframe];
5092 CGRect trnframe = [transition_ frame];
5093 trnframe.origin.y += ovrframe.size.height;
5094 trnframe.size.height -= ovrframe.size.height;
5095 [transition_ setFrame:trnframe];
5097 [UIView endAnimations];
5099 [indicator_ startAnimation];
5100 [prompt_ setText:@"Updating Database"];
5101 [progress_ setProgress:0];
5104 [overlay_ addSubview:cancel_];
5107 detachNewThreadSelector:@selector(_update)
5116 [indicator_ stopAnimation];
5118 [UIView beginAnimations:nil context:NULL];
5120 CGRect ovrframe = [overlay_ frame];
5121 ovrframe.origin.y = -ovrframe.size.height;
5122 [overlay_ setFrame:ovrframe];
5124 CGRect barframe = [navbar_ frame];
5125 barframe.origin.y -= ovrframe.size.height;
5126 [navbar_ setFrame:barframe];
5128 CGRect trnframe = [transition_ frame];
5129 trnframe.origin.y -= ovrframe.size.height;
5130 trnframe.size.height += ovrframe.size.height;
5131 [transition_ setFrame:trnframe];
5133 [UIView commitAnimations];
5135 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5138 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5139 if ((self = [super initWithFrame:frame]) != nil) {
5140 database_ = database;
5142 CGRect ovrrect = [navbar_ bounds];
5143 ovrrect.size.height = [UINavigationBar defaultSize].height;
5144 ovrrect.origin.y = -ovrrect.size.height;
5146 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5147 [self addSubview:overlay_];
5149 ovrrect.origin.y = frame.size.height;
5150 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5151 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5152 [self addSubview:underlay_];
5154 [overlay_ setBarStyle:1];
5155 [underlay_ setBarStyle:1];
5157 int barstyle = [overlay_ _barStyle:NO];
5158 bool ugly = barstyle == 0;
5160 UIProgressIndicatorStyle style = ugly ?
5161 UIProgressIndicatorStyleMediumBrown :
5162 UIProgressIndicatorStyleMediumWhite;
5164 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5165 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5166 CGRect indrect = {{indoffset, indoffset}, indsize};
5168 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5169 [indicator_ setStyle:style];
5170 [overlay_ addSubview:indicator_];
5172 CGSize prmsize = {215, indsize.height + 4};
5175 indoffset * 2 + indsize.width,
5179 unsigned(ovrrect.size.height - prmsize.height) / 2
5182 UIFont *font = [UIFont systemFontOfSize:15];
5184 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5186 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5187 [prompt_ setBackgroundColor:[UIColor clearColor]];
5188 [prompt_ setFont:font];
5190 [overlay_ addSubview:prompt_];
5192 CGSize prgsize = {75, 100};
5195 ovrrect.size.width - prgsize.width - 10,
5196 (ovrrect.size.height - prgsize.height) / 2
5199 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5200 [progress_ setStyle:0];
5201 [overlay_ addSubview:progress_];
5203 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5204 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5206 CGRect frame = [cancel_ frame];
5207 frame.size.width = 65;
5208 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5209 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5210 [cancel_ setFrame:frame];
5212 [cancel_ setBarStyle:barstyle];
5216 - (void) _onCancel {
5218 [cancel_ removeFromSuperview];
5221 - (void) _update { _pooled
5223 status.setDelegate(self);
5225 [database_ updateWithStatus:status];
5228 performSelectorOnMainThread:@selector(_update_)
5234 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5235 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5238 - (void) setProgressTitle:(NSString *)title {
5240 performSelectorOnMainThread:@selector(_setProgressTitle:)
5246 - (void) setProgressPercent:(float)percent {
5248 performSelectorOnMainThread:@selector(_setProgressPercent:)
5249 withObject:[NSNumber numberWithFloat:percent]
5254 - (void) startProgress {
5257 - (void) addProgressOutput:(NSString *)output {
5259 performSelectorOnMainThread:@selector(_addProgressOutput:)
5265 - (bool) isCancelling:(size_t)received {
5269 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5273 - (void) _setProgressTitle:(NSString *)title {
5274 [prompt_ setText:title];
5277 - (void) _setProgressPercent:(NSNumber *)percent {
5278 [progress_ setProgress:[percent floatValue]];
5281 - (void) _addProgressOutput:(NSString *)output {
5286 /* Cydia:// Protocol {{{ */
5287 @interface CydiaURLProtocol : NSURLProtocol {
5292 @implementation CydiaURLProtocol
5294 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5295 NSURL *url([request URL]);
5298 NSString *scheme([[url scheme] lowercaseString]);
5299 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5304 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5308 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5309 id<NSURLProtocolClient> client([self client]);
5311 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5313 NSData *data(UIImagePNGRepresentation(icon));
5315 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5316 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5317 [client URLProtocol:self didLoadData:data];
5318 [client URLProtocolDidFinishLoading:self];
5322 - (void) startLoading {
5323 id<NSURLProtocolClient> client([self client]);
5324 NSURLRequest *request([self request]);
5326 NSURL *url([request URL]);
5327 NSString *href([url absoluteString]);
5329 NSString *path([href substringFromIndex:8]);
5330 NSRange slash([path rangeOfString:@"/"]);
5333 if (slash.location == NSNotFound) {
5337 command = [path substringToIndex:slash.location];
5338 path = [path substringFromIndex:(slash.location + 1)];
5341 Database *database([Database sharedInstance]);
5343 if ([command isEqualToString:@"package-icon"]) {
5346 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5347 Package *package([database packageWithName:path]);
5350 UIImage *icon([package icon]);
5351 [self _returnPNGWithImage:icon forRequest:request];
5352 } else if ([command isEqualToString:@"source-icon"]) {
5355 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5356 NSString *source(Simplify(path));
5357 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5359 icon = [UIImage applicationImageNamed:@"unknown.png"];
5360 [self _returnPNGWithImage:icon forRequest:request];
5361 } else if ([command isEqualToString:@"uikit-image"]) {
5364 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5365 UIImage *icon(_UIImageWithName(path));
5366 [self _returnPNGWithImage:icon forRequest:request];
5367 } else if ([command isEqualToString:@"section-icon"]) {
5370 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5371 NSString *section(Simplify(path));
5372 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5374 icon = [UIImage applicationImageNamed:@"unknown.png"];
5375 [self _returnPNGWithImage:icon forRequest:request];
5377 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5381 - (void) stopLoading {
5387 /* Sections View {{{ */
5388 @interface SectionsView : RVPage {
5389 _transient Database *database_;
5390 NSMutableArray *sections_;
5391 NSMutableArray *filtered_;
5392 UITransitionView *transition_;
5398 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5399 - (void) reloadData;
5404 @implementation SectionsView
5407 [list_ setDataSource:nil];
5408 [list_ setDelegate:nil];
5410 [sections_ release];
5411 [filtered_ release];
5412 [transition_ release];
5414 [accessory_ release];
5418 - (int) numberOfRowsInTable:(UITable *)table {
5419 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5422 - (float) table:(UITable *)table heightForRow:(int)row {
5426 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5428 reusing = [[[SectionCell alloc] init] autorelease];
5429 [(SectionCell *)reusing setSection:(editing_ ?
5430 [sections_ objectAtIndex:row] :
5431 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5432 ) editing:editing_];
5436 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5440 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5444 - (void) tableRowSelected:(NSNotification *)notification {
5445 int row = [[notification object] selectedRow];
5456 title = @"All Packages";
5458 section = [filtered_ objectAtIndex:(row - 1)];
5459 name = [section name];
5465 title = @"(No Section)";
5469 PackageTable *table = [[[FilteredPackageTable alloc]
5473 filter:@selector(isVisiblyUninstalledInSection:)
5477 [table setDelegate:delegate_];
5479 [book_ pushPage:table];
5482 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5483 if ((self = [super initWithBook:book]) != nil) {
5484 database_ = database;
5486 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5487 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5489 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5490 [self addSubview:transition_];
5492 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5493 [transition_ transition:0 toView:list_];
5495 UITableColumn *column = [[[UITableColumn alloc]
5496 initWithTitle:@"Name"
5498 width:[self frame].size.width
5501 [list_ setDataSource:self];
5502 [list_ setSeparatorStyle:1];
5503 [list_ addTableColumn:column];
5504 [list_ setDelegate:self];
5505 [list_ setReusesTableCells:YES];
5509 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5510 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5514 - (void) reloadData {
5515 NSArray *packages = [database_ packages];
5517 [sections_ removeAllObjects];
5518 [filtered_ removeAllObjects];
5520 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5521 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5524 for (Package *package in packages) {
5525 NSString *name([package section]);
5528 Section *section([sections objectForKey:name]);
5529 if (section == nil) {
5530 section = [[[Section alloc] initWithName:name] autorelease];
5531 [sections setObject:section forKey:name];
5535 if ([package valid] && [package installed] == nil && [package visible])
5536 [filtered addObject:package];
5540 [sections_ addObjectsFromArray:[sections allValues]];
5541 [sections_ sortUsingSelector:@selector(compareByName:)];
5544 [filtered sortUsingSelector:@selector(compareBySection:)];
5547 Section *section = nil;
5548 for (Package *package in filtered) {
5549 NSString *name = [package section];
5551 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5552 section = name == nil ?
5553 [[[Section alloc] initWithName:nil] autorelease] :
5554 [sections objectForKey:name];
5555 [filtered_ addObject:section];
5558 [section addToCount];
5566 - (void) resetView {
5568 [self _rightButtonClicked];
5571 - (void) resetViewAnimated:(BOOL)animated {
5572 [list_ resetViewAnimated:animated];
5575 - (void) _rightButtonClicked {
5576 if ((editing_ = !editing_))
5579 [delegate_ updateData];
5580 [book_ reloadTitleForPage:self];
5581 [book_ reloadButtonsForPage:self];
5584 - (NSString *) title {
5585 return editing_ ? @"Section Visibility" : @"Install by Section";
5588 - (NSString *) backButtonTitle {
5592 - (id) rightButtonTitle {
5593 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5596 - (UINavigationButtonStyle) rightButtonStyle {
5597 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5600 - (UIView *) accessoryView {
5606 /* Changes View {{{ */
5607 @interface ChangesView : RVPage {
5608 _transient Database *database_;
5609 NSMutableArray *packages_;
5610 NSMutableArray *sections_;
5611 UISectionList *list_;
5615 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5616 - (void) reloadData;
5620 @implementation ChangesView
5623 [[list_ table] setDelegate:nil];
5624 [list_ setDataSource:nil];
5626 [packages_ release];
5627 [sections_ release];
5632 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5633 return [sections_ count];
5636 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5637 return [[sections_ objectAtIndex:section] name];
5640 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5641 return [[sections_ objectAtIndex:section] row];
5644 - (int) numberOfRowsInTable:(UITable *)table {
5645 return [packages_ count];
5648 - (float) table:(UITable *)table heightForRow:(int)row {
5649 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5652 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5654 reusing = [[[PackageCell alloc] init] autorelease];
5655 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5659 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5663 - (void) tableRowSelected:(NSNotification *)notification {
5664 int row = [[notification object] selectedRow];
5667 Package *package = [packages_ objectAtIndex:row];
5668 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5669 [view setDelegate:delegate_];
5670 [view setPackage:package];
5671 [book_ pushPage:view];
5674 - (void) _leftButtonClicked {
5675 [(CYBook *)book_ update];
5676 [self reloadButtons];
5679 - (void) _rightButtonClicked {
5680 [delegate_ distUpgrade];
5683 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5684 if ((self = [super initWithBook:book]) != nil) {
5685 database_ = database;
5687 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5688 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5690 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5691 [self addSubview:list_];
5693 [list_ setShouldHideHeaderInShortLists:NO];
5694 [list_ setDataSource:self];
5695 //[list_ setSectionListStyle:1];
5697 UITableColumn *column = [[[UITableColumn alloc]
5698 initWithTitle:@"Name"
5700 width:[self frame].size.width
5703 UITable *table = [list_ table];
5704 [table setSeparatorStyle:1];
5705 [table addTableColumn:column];
5706 [table setDelegate:self];
5707 [table setReusesTableCells:YES];
5711 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5712 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5716 - (void) reloadData {
5717 NSArray *packages = [database_ packages];
5719 [packages_ removeAllObjects];
5720 [sections_ removeAllObjects];
5723 for (Package *package in packages)
5725 [package installed] == nil && [package valid] && [package visible] ||
5726 [package upgradableAndEssential:YES]
5728 [packages_ addObject:package];
5731 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5734 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5735 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5736 Section *section = nil;
5740 bool unseens = false;
5742 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5745 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5746 Package *package = [packages_ objectAtIndex:offset];
5748 if (![package upgradableAndEssential:YES]) {
5750 NSDate *seen = [package seen];
5752 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5755 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5756 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5757 [sections_ addObject:section];
5761 [section addToCount];
5762 } else if ([package ignored])
5763 [ignored addToCount];
5766 [upgradable addToCount];
5771 CFRelease(formatter);
5774 Section *last = [sections_ lastObject];
5775 size_t count = [last count];
5776 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5777 [sections_ removeLastObject];
5780 if ([ignored count] != 0)
5781 [sections_ insertObject:ignored atIndex:0];
5783 [sections_ insertObject:upgradable atIndex:0];
5786 [self reloadButtons];
5789 - (void) resetViewAnimated:(BOOL)animated {
5790 [list_ resetViewAnimated:animated];
5793 - (NSString *) leftButtonTitle {
5794 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5797 - (id) rightButtonTitle {
5798 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5801 - (NSString *) title {
5807 /* Search View {{{ */
5808 @protocol SearchViewDelegate
5809 - (void) showKeyboard:(BOOL)show;
5812 @interface SearchView : RVPage {
5814 UISearchField *field_;
5815 UITransitionView *transition_;
5816 FilteredPackageTable *table_;
5817 UIPreferencesTable *advanced_;
5823 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5824 - (void) reloadData;
5828 @implementation SearchView
5831 [field_ setDelegate:nil];
5833 [accessory_ release];
5835 [transition_ release];
5837 [advanced_ release];
5842 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5846 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5848 case 0: return @"Advanced Search (Coming Soon!)";
5850 default: _assert(false);
5854 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5858 default: _assert(false);
5862 - (void) _showKeyboard:(BOOL)show {
5863 CGSize keysize = [UIKeyboard defaultSize];
5864 CGRect keydown = [book_ pageBounds];
5865 CGRect keyup = keydown;
5866 keyup.size.height -= keysize.height - ButtonBarHeight_;
5868 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5870 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5871 [animation setSignificantRectFields:8];
5874 [animation setStartFrame:keydown];
5875 [animation setEndFrame:keyup];
5877 [animation setStartFrame:keyup];
5878 [animation setEndFrame:keydown];
5881 UIAnimator *animator = [UIAnimator sharedAnimator];
5884 addAnimations:[NSArray arrayWithObjects:animation, nil]
5885 withDuration:(KeyboardTime_ - delay)
5890 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5892 [delegate_ showKeyboard:show];
5895 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5896 [self _showKeyboard:YES];
5899 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5900 [self _showKeyboard:NO];
5903 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5905 NSString *text([field_ text]);
5906 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5912 - (void) textFieldClearButtonPressed:(UITextField *)field {
5916 - (void) keyboardInputShouldDelete:(id)input {
5920 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5921 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5925 [field_ resignFirstResponder];
5930 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5931 if ((self = [super initWithBook:book]) != nil) {
5932 CGRect pageBounds = [book_ pageBounds];
5934 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5935 [self addSubview:transition_];
5937 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5939 [advanced_ setReusesTableCells:YES];
5940 [advanced_ setDataSource:self];
5941 [advanced_ reloadData];
5943 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5944 CGColor dimmed(space_, 0, 0, 0, 0.5);
5945 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5947 table_ = [[FilteredPackageTable alloc]
5951 filter:@selector(isUnfilteredAndSearchedForBy:)
5955 [table_ setShouldHideHeaderInShortLists:NO];
5956 [transition_ transition:0 toView:table_];
5965 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5972 [self bounds].size.width - area.origin.x - 18;
5974 area.size.height = [UISearchField defaultHeight];
5976 field_ = [[UISearchField alloc] initWithFrame:area];
5978 UIFont *font = [UIFont systemFontOfSize:16];
5979 [field_ setFont:font];
5981 [field_ setPlaceholder:@"Package Names & Descriptions"];
5982 [field_ setDelegate:self];
5984 [field_ setPaddingTop:5];
5986 UITextInputTraits *traits([field_ textInputTraits]);
5987 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5988 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5989 [traits setReturnKeyType:UIReturnKeySearch];
5991 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5993 accessory_ = [[UIView alloc] initWithFrame:accrect];
5994 [accessory_ addSubview:field_];
5996 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5997 [configure setShowPressFeedback:YES];
5998 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5999 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6000 [accessory_ addSubview:configure];*/
6002 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6003 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6009 LKAnimation *animation = [LKTransition animation];
6010 [animation setType:@"oglFlip"];
6011 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6012 [animation setFillMode:@"extended"];
6013 [animation setTransitionFlags:3];
6014 [animation setDuration:10];
6015 [animation setSpeed:0.35];
6016 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6017 [[transition_ _layer] addAnimation:animation forKey:0];
6018 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6019 flipped_ = !flipped_;
6023 - (void) configurePushed {
6024 [field_ resignFirstResponder];
6028 - (void) resetViewAnimated:(BOOL)animated {
6031 [table_ resetViewAnimated:animated];
6034 - (void) _reloadData {
6037 - (void) reloadData {
6040 [table_ setObject:[field_ text]];
6041 _profile(SearchView$reloadData)
6042 [table_ reloadData];
6045 [table_ resetCursor];
6048 - (UIView *) accessoryView {
6052 - (NSString *) title {
6056 - (NSString *) backButtonTitle {
6060 - (void) setDelegate:(id)delegate {
6061 [table_ setDelegate:delegate];
6062 [super setDelegate:delegate];
6068 @interface SettingsView : RVPage {
6069 _transient Database *database_;
6072 UIPreferencesTable *table_;
6073 _UISwitchSlider *subscribedSwitch_;
6074 _UISwitchSlider *ignoredSwitch_;
6075 UIPreferencesControlTableCell *subscribedCell_;
6076 UIPreferencesControlTableCell *ignoredCell_;
6079 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6083 @implementation SettingsView
6086 [table_ setDataSource:nil];
6089 if (package_ != nil)
6092 [subscribedSwitch_ release];
6093 [ignoredSwitch_ release];
6094 [subscribedCell_ release];
6095 [ignoredCell_ release];
6099 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6100 if (package_ == nil)
6106 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6107 if (package_ == nil)
6114 default: _assert(false);
6120 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6121 if (package_ == nil)
6128 default: _assert(false);
6134 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6135 if (package_ == nil)
6142 default: _assert(false);
6148 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6149 if (package_ == nil)
6152 _UISwitchSlider *slider([cell control]);
6153 BOOL value([slider value] != 0);
6154 NSMutableDictionary *metadata([package_ metadata]);
6157 if (NSNumber *number = [metadata objectForKey:key])
6158 before = [number boolValue];
6162 if (value != before) {
6163 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6165 [delegate_ updateData];
6169 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6170 [self onSomething:cell withKey:@"IsSubscribed"];
6173 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6174 [self onSomething:cell withKey:@"IsIgnored"];
6177 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6178 if (package_ == nil)
6182 case 0: switch (row) {
6184 return subscribedCell_;
6186 return ignoredCell_;
6187 default: _assert(false);
6190 case 1: switch (row) {
6192 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6193 [cell setShowSelection:NO];
6194 [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."];
6198 default: _assert(false);
6201 default: _assert(false);
6207 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6208 if ((self = [super initWithBook:book])) {
6209 database_ = database;
6210 name_ = [package retain];
6212 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6213 [self addSubview:table_];
6215 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6216 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6218 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6219 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6221 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6222 [subscribedCell_ setShowSelection:NO];
6223 [subscribedCell_ setTitle:@"Show All Changes"];
6224 [subscribedCell_ setControl:subscribedSwitch_];
6226 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6227 [ignoredCell_ setShowSelection:NO];
6228 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6229 [ignoredCell_ setControl:ignoredSwitch_];
6231 [table_ setDataSource:self];
6236 - (void) resetViewAnimated:(BOOL)animated {
6237 [table_ resetViewAnimated:animated];
6240 - (void) reloadData {
6241 if (package_ != nil)
6242 [package_ autorelease];
6243 package_ = [database_ packageWithName:name_];
6244 if (package_ != nil) {
6246 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6247 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6250 [table_ reloadData];
6253 - (NSString *) title {
6259 /* Signature View {{{ */
6260 @interface SignatureView : BrowserView {
6261 _transient Database *database_;
6265 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6269 @implementation SignatureView
6276 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6278 [super webView:sender didClearWindowObject:window forFrame:frame];
6281 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6282 if ((self = [super initWithBook:book]) != nil) {
6283 database_ = database;
6284 package_ = [package retain];
6289 - (void) resetViewAnimated:(BOOL)animated {
6292 - (void) reloadData {
6293 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6299 @interface Cydia : UIApplication <
6300 ConfirmationViewDelegate,
6301 ProgressViewDelegate,
6310 UIToolbar *buttonbar_;
6314 NSMutableArray *essential_;
6315 NSMutableArray *broken_;
6317 Database *database_;
6318 ProgressView *progress_;
6322 UIKeyboard *keyboard_;
6323 UIProgressHUD *hud_;
6325 SectionsView *sections_;
6326 ChangesView *changes_;
6327 ManageView *manage_;
6328 SearchView *search_;
6333 @implementation Cydia
6336 if ([broken_ count] != 0) {
6337 int count = [broken_ count];
6339 UIActionSheet *sheet = [[[UIActionSheet alloc]
6340 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6341 buttons:[NSArray arrayWithObjects:
6343 @"Ignore (Temporary)",
6345 defaultButtonIndex:0
6350 [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."];
6351 [sheet popupAlertAnimated:YES];
6352 } else if (!Ignored_ && [essential_ count] != 0) {
6353 int count = [essential_ count];
6355 UIActionSheet *sheet = [[[UIActionSheet alloc]
6356 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6357 buttons:[NSArray arrayWithObjects:
6358 @"Upgrade Essential",
6359 @"Complete Upgrade",
6360 @"Ignore (Temporary)",
6362 defaultButtonIndex:0
6367 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6368 [sheet popupAlertAnimated:YES];
6372 - (void) _reloadData {
6375 UIProgressHUD *hud([self addProgressHUD]);
6376 [hud setText:@"Reloading Data"];
6378 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6381 [self removeProgressHUD:hud];
6385 [essential_ removeAllObjects];
6386 [broken_ removeAllObjects];
6388 NSArray *packages = [database_ packages];
6389 for (Package *package in packages) {
6391 [broken_ addObject:package];
6392 if ([package upgradableAndEssential:NO]) {
6393 if ([package essential])
6394 [essential_ addObject:package];
6400 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6401 [buttonbar_ setBadgeValue:badge forButton:3];
6402 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6403 [buttonbar_ setBadgeAnimated:YES forButton:3];
6404 [self setApplicationBadge:badge];
6406 [buttonbar_ setBadgeValue:nil forButton:3];
6407 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6408 [buttonbar_ setBadgeAnimated:NO forButton:3];
6409 [self removeApplicationBadge];
6414 // XXX: what is this line of code for?
6415 if ([packages count] == 0);
6416 else if (Loaded_) loaded:
6421 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6422 NSTimeInterval interval([update timeIntervalSinceNow]);
6423 if (interval <= 0 && interval > -600)
6431 - (void) _saveConfig {
6434 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6440 - (void) updateData {
6443 /* XXX: this is just stupid */
6444 if (tag_ != 2 && sections_ != nil)
6445 [sections_ reloadData];
6446 if (tag_ != 3 && changes_ != nil)
6447 [changes_ reloadData];
6448 if (tag_ != 5 && search_ != nil)
6449 [search_ reloadData];
6459 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6460 _assert(file != NULL);
6462 NSArray *keys = [Sources_ allKeys];
6464 for (NSString *key in keys) {
6465 NSDictionary *source = [Sources_ objectForKey:key];
6467 fprintf(file, "%s %s %s\n",
6468 [[source objectForKey:@"Type"] UTF8String],
6469 [[source objectForKey:@"URI"] UTF8String],
6470 [[source objectForKey:@"Distribution"] UTF8String]
6479 detachNewThreadSelector:@selector(update_)
6482 title:@"Updating Sources"
6486 - (void) reloadData {
6487 @synchronized (self) {
6488 if (confirm_ == nil)
6494 pkgProblemResolver *resolver = [database_ resolver];
6496 resolver->InstallProtect();
6497 if (!resolver->Resolve(true))
6501 - (void) popUpBook:(RVBook *)book {
6502 [underlay_ popSubview:book];
6505 - (CGRect) popUpBounds {
6506 return [underlay_ bounds];
6510 [database_ prepare];
6512 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6513 [confirm_ setDelegate:self];
6515 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6516 [page setDelegate:self];
6518 [confirm_ setPage:page];
6519 [self popUpBook:confirm_];
6522 - (void) installPackage:(Package *)package {
6523 @synchronized (self) {
6530 - (void) removePackage:(Package *)package {
6531 @synchronized (self) {
6538 - (void) distUpgrade {
6539 @synchronized (self) {
6540 [database_ upgrade];
6546 @synchronized (self) {
6548 if (confirm_ != nil) {
6556 [overlay_ removeFromSuperview];
6560 detachNewThreadSelector:@selector(perform)
6567 - (void) bootstrap_ {
6569 [database_ upgrade];
6570 [database_ prepare];
6571 [database_ perform];
6574 - (void) bootstrap {
6576 detachNewThreadSelector:@selector(bootstrap_)
6579 title:@"Bootstrap Install"
6583 - (void) progressViewIsComplete:(ProgressView *)progress {
6584 if (confirm_ != nil) {
6585 [underlay_ addSubview:overlay_];
6586 [confirm_ popFromSuperviewAnimated:NO];
6592 - (void) setPage:(RVPage *)page {
6593 [page resetViewAnimated:NO];
6594 [page setDelegate:self];
6595 [book_ setPage:page];
6598 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6599 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6600 [browser loadURL:url];
6604 - (void) _setHomePage {
6605 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6608 - (void) buttonBarItemTapped:(id)sender {
6609 unsigned tag = [sender tag];
6611 [book_ resetViewAnimated:YES];
6613 } else if (tag_ == 2 && tag != 2)
6614 [sections_ resetView];
6617 case 1: [self _setHomePage]; break;
6619 case 2: [self setPage:sections_]; break;
6620 case 3: [self setPage:changes_]; break;
6621 case 4: [self setPage:manage_]; break;
6622 case 5: [self setPage:search_]; break;
6624 default: _assert(false);
6630 - (void) applicationWillSuspend {
6632 [super applicationWillSuspend];
6635 - (void) askForSettings {
6636 UIActionSheet *role = [[[UIActionSheet alloc]
6637 initWithTitle:@"Who Are You?"
6638 buttons:[NSArray arrayWithObjects:
6639 @"User (Graphical Only)",
6640 @"Hacker (+ Command Line)",
6641 @"Developer (No Filters)",
6643 defaultButtonIndex:-1
6648 [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."];
6649 [role popupAlertAnimated:YES];
6654 [self setStatusBarShowsProgress:NO];
6655 [self removeProgressHUD:hud_];
6660 pid_t pid = ExecFork();
6662 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6663 perror("launchctl stop");
6670 [self askForSettings];
6675 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6677 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6678 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6679 0, 0, screenrect.size.width, screenrect.size.height - 48
6680 ) database:database_];
6682 [book_ setDelegate:self];
6684 [overlay_ addSubview:book_];
6686 NSArray *buttonitems = [NSArray arrayWithObjects:
6687 [NSDictionary dictionaryWithObjectsAndKeys:
6688 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6689 @"home-up.png", kUIButtonBarButtonInfo,
6690 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6691 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6692 self, kUIButtonBarButtonTarget,
6693 @"Home", kUIButtonBarButtonTitle,
6694 @"0", kUIButtonBarButtonType,
6697 [NSDictionary dictionaryWithObjectsAndKeys:
6698 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6699 @"install-up.png", kUIButtonBarButtonInfo,
6700 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6701 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6702 self, kUIButtonBarButtonTarget,
6703 @"Sections", kUIButtonBarButtonTitle,
6704 @"0", kUIButtonBarButtonType,
6707 [NSDictionary dictionaryWithObjectsAndKeys:
6708 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6709 @"changes-up.png", kUIButtonBarButtonInfo,
6710 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6711 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6712 self, kUIButtonBarButtonTarget,
6713 @"Changes", kUIButtonBarButtonTitle,
6714 @"0", kUIButtonBarButtonType,
6717 [NSDictionary dictionaryWithObjectsAndKeys:
6718 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6719 @"manage-up.png", kUIButtonBarButtonInfo,
6720 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6721 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6722 self, kUIButtonBarButtonTarget,
6723 @"Manage", kUIButtonBarButtonTitle,
6724 @"0", kUIButtonBarButtonType,
6727 [NSDictionary dictionaryWithObjectsAndKeys:
6728 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6729 @"search-up.png", kUIButtonBarButtonInfo,
6730 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6731 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6732 self, kUIButtonBarButtonTarget,
6733 @"Search", kUIButtonBarButtonTitle,
6734 @"0", kUIButtonBarButtonType,
6738 buttonbar_ = [[UIToolbar alloc]
6740 withFrame:CGRectMake(
6741 0, screenrect.size.height - ButtonBarHeight_,
6742 screenrect.size.width, ButtonBarHeight_
6744 withItemList:buttonitems
6747 [buttonbar_ setDelegate:self];
6748 [buttonbar_ setBarStyle:1];
6749 [buttonbar_ setButtonBarTrackingMode:2];
6751 int buttons[5] = {1, 2, 3, 4, 5};
6752 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6753 [buttonbar_ showButtonGroup:0 withDuration:0];
6755 for (int i = 0; i != 5; ++i)
6756 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6757 i * 64 + 2, 1, 60, ButtonBarHeight_
6760 [buttonbar_ showSelectionForButton:1];
6761 [overlay_ addSubview:buttonbar_];
6763 [UIKeyboard initImplementationNow];
6764 CGSize keysize = [UIKeyboard defaultSize];
6765 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6766 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6767 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6768 [overlay_ addSubview:keyboard_];
6771 [underlay_ addSubview:overlay_];
6775 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6776 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6777 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6779 manage_ = (ManageView *) [[self
6780 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6781 withClass:[ManageView class]
6789 [self _setHomePage];
6792 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6793 NSString *context([sheet context]);
6795 if ([context isEqualToString:@"missing"])
6797 else if ([context isEqualToString:@"fixhalf"]) {
6800 @synchronized (self) {
6801 for (Package *broken in broken_) {
6804 NSString *id = [broken id];
6805 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6806 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6807 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6808 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6817 [broken_ removeAllObjects];
6826 } else if ([context isEqualToString:@"role"]) {
6828 case 1: Role_ = @"User"; break;
6829 case 2: Role_ = @"Hacker"; break;
6830 case 3: Role_ = @"Developer"; break;
6837 bool reset = Settings_ != nil;
6839 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6843 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6853 } else if ([context isEqualToString:@"upgrade"]) {
6856 @synchronized (self) {
6857 for (Package *essential in essential_)
6858 [essential install];
6881 - (void) reorganize { _pooled
6882 system("/usr/libexec/cydia/free.sh");
6883 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6886 - (void) applicationSuspend:(__GSEvent *)event {
6887 if (hud_ == nil && ![progress_ isRunning])
6888 [super applicationSuspend:event];
6891 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6893 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6896 - (void) _setSuspended:(BOOL)value {
6898 [super _setSuspended:value];
6901 - (UIProgressHUD *) addProgressHUD {
6902 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
6903 [window_ setUserInteractionEnabled:NO];
6905 [progress_ addSubview:hud];
6909 - (void) removeProgressHUD:(UIProgressHUD *)hud {
6911 [hud removeFromSuperview];
6912 [window_ setUserInteractionEnabled:YES];
6915 - (void) openMailToURL:(NSURL *)url {
6916 // XXX: this makes me sad
6918 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6920 [UIApp openURL:url];// asPanel:YES];
6924 - (void) clearFirstResponder {
6925 if (id responder = [window_ firstResponder])
6926 [responder resignFirstResponder];
6929 - (RVPage *) pageForPackage:(NSString *)name {
6930 if (Package *package = [database_ packageWithName:name]) {
6931 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6932 [view setPackage:package];
6935 UIActionSheet *sheet = [[[UIActionSheet alloc]
6936 initWithTitle:@"Cannot Locate Package"
6937 buttons:[NSArray arrayWithObjects:@"Close", nil]
6938 defaultButtonIndex:0
6943 [sheet setBodyText:[NSString stringWithFormat:
6944 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6947 [sheet popupAlertAnimated:YES];
6952 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6956 NSString *scheme([[url scheme] lowercaseString]);
6957 if (![scheme isEqualToString:@"cydia"])
6959 NSString *path([url absoluteString]);
6960 if ([path length] < 8)
6962 path = [path substringFromIndex:8];
6963 if (![path hasPrefix:@"/"])
6964 path = [@"/" stringByAppendingString:path];
6966 if ([path isEqualToString:@"/add-source"])
6967 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6968 else if ([path isEqualToString:@"/storage"])
6969 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6970 else if ([path isEqualToString:@"/sources"])
6971 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6972 else if ([path isEqualToString:@"/packages"])
6973 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6974 else if ([path hasPrefix:@"/url/"])
6975 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6976 else if ([path hasPrefix:@"/launch/"])
6977 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6978 else if ([path hasPrefix:@"/package-settings/"])
6979 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6980 else if ([path hasPrefix:@"/package-signature/"])
6981 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6982 else if ([path hasPrefix:@"/package/"])
6983 return [self pageForPackage:[path substringFromIndex:9]];
6984 else if ([path hasPrefix:@"/files/"]) {
6985 NSString *name = [path substringFromIndex:7];
6987 if (Package *package = [database_ packageWithName:name]) {
6988 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6989 [files setPackage:package];
6997 - (void) applicationOpenURL:(NSURL *)url {
6998 [super applicationOpenURL:url];
7000 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7001 [self setPage:page];
7002 [buttonbar_ showSelectionForButton:tag];
7007 - (void) applicationDidFinishLaunching:(id)unused {
7009 Font12_ = [[UIFont systemFontOfSize:12] retain];
7010 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7011 Font14_ = [[UIFont systemFontOfSize:14] retain];
7012 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7013 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7015 _assert(pkgInitConfig(*_config));
7016 _assert(pkgInitSystem(*_config, _system));
7020 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7021 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7023 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7025 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7026 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7028 [window_ orderFront:self];
7029 [window_ makeKey:self];
7030 [window_ setHidden:NO];
7032 database_ = [Database sharedInstance];
7033 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7034 [database_ setDelegate:progress_];
7035 [window_ setContentView:progress_];
7037 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7038 [progress_ setContentView:underlay_];
7040 [progress_ resetView];
7043 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7044 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7045 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7046 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7047 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7048 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7049 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7051 [self setIdleTimerDisabled:YES];
7053 hud_ = [[self addProgressHUD] retain];
7054 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7056 [self setStatusBarShowsProgress:YES];
7059 detachNewThreadSelector:@selector(reorganize)
7067 - (void) showKeyboard:(BOOL)show {
7068 CGSize keysize = [UIKeyboard defaultSize];
7069 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7070 CGRect keyup = keydown;
7071 keyup.origin.y -= keysize.height;
7073 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7074 [animation setSignificantRectFields:2];
7077 [animation setStartFrame:keydown];
7078 [animation setEndFrame:keyup];
7079 [keyboard_ activate];
7081 [animation setStartFrame:keyup];
7082 [animation setEndFrame:keydown];
7083 [keyboard_ deactivate];
7086 [[UIAnimator sharedAnimator]
7087 addAnimations:[NSArray arrayWithObjects:animation, nil]
7088 withDuration:KeyboardTime_
7093 - (void) slideUp:(UIActionSheet *)alert {
7095 [alert presentSheetFromButtonBar:buttonbar_];
7097 [alert presentSheetInView:overlay_];
7102 void AddPreferences(NSString *plist) { _pooled
7103 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7104 _assert(settings != NULL);
7105 NSMutableArray *items = [settings objectForKey:@"items"];
7109 for (NSMutableDictionary *item in items) {
7110 NSString *label = [item objectForKey:@"label"];
7111 if (label != nil && [label isEqualToString:@"Cydia"]) {
7118 for (size_t i(0); i != [items count]; ++i) {
7119 NSDictionary *item([items objectAtIndex:i]);
7120 NSString *label = [item objectForKey:@"label"];
7121 if (label != nil && [label isEqualToString:@"General"]) {
7122 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7123 @"CydiaSettings", @"bundle",
7124 @"PSLinkCell", @"cell",
7125 [NSNumber numberWithBool:YES], @"hasIcon",
7126 [NSNumber numberWithBool:YES], @"isController",
7128 nil] atIndex:(i + 1)];
7134 _assert([settings writeToFile:plist atomically:YES] == YES);
7139 id Alloc_(id self, SEL selector) {
7140 id object = alloc_(self, selector);
7141 lprintf("[%s]A-%p\n", self->isa->name, object);
7146 id Dealloc_(id self, SEL selector) {
7147 id object = dealloc_(self, selector);
7148 lprintf("[%s]D-%p\n", self->isa->name, object);
7152 int main(int argc, char *argv[]) { _pooled
7154 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7156 bool substrate(false);
7162 for (int argi(1); argi != argc; ++argi)
7163 if (strcmp(argv[argi], "--") == 0) {
7165 argv[argi] = argv[0];
7171 for (int argi(1); argi != arge; ++argi)
7172 if (strcmp(args[argi], "--bootstrap") == 0)
7174 else if (strcmp(args[argi], "--substrate") == 0)
7177 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7180 App_ = [[NSBundle mainBundle] bundlePath];
7181 Home_ = NSHomeDirectory();
7182 Locale_ = CFLocaleCopyCurrent();
7185 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7186 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7187 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7188 Sounds_Keyboard_ = [keyboard boolValue];
7194 #if 0 /* XXX: this costs 1.4s of startup performance */
7195 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7196 _assert(errno == ENOENT);
7197 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7198 _assert(errno == ENOENT);
7201 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7202 alloc_ = alloc->method_imp;
7203 alloc->method_imp = (IMP) &Alloc_;*/
7205 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7206 dealloc_ = dealloc->method_imp;
7207 dealloc->method_imp = (IMP) &Dealloc_;*/
7212 size = sizeof(maxproc);
7213 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7214 perror("sysctlbyname(\"kern.maxproc\", ?)");
7215 else if (maxproc < 64) {
7217 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7218 perror("sysctlbyname(\"kern.maxproc\", #)");
7221 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7222 char *machine = new char[size];
7223 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7224 perror("sysctlbyname(\"hw.machine\", ?)");
7228 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7230 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7231 Build_ = [system objectForKey:@"ProductBuildVersion"];
7233 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7234 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7237 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7240 if (Metadata_ == NULL)
7241 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7243 Settings_ = [Metadata_ objectForKey:@"Settings"];
7245 Packages_ = [Metadata_ objectForKey:@"Packages"];
7246 Sections_ = [Metadata_ objectForKey:@"Sections"];
7247 Sources_ = [Metadata_ objectForKey:@"Sources"];
7250 if (Settings_ != nil)
7251 Role_ = [Settings_ objectForKey:@"Role"];
7253 if (Packages_ == nil) {
7254 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7255 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7258 if (Sections_ == nil) {
7259 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7260 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7263 if (Sources_ == nil) {
7264 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7265 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7269 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7272 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7273 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7274 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7275 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7277 if (access("/User", F_OK) != 0) {
7279 system("/usr/libexec/cydia/firmware.sh");
7283 _assert([[NSFileManager defaultManager]
7284 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7285 withIntermediateDirectories:YES
7290 space_ = CGColorSpaceCreateDeviceRGB();
7292 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7293 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7294 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7295 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7296 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7297 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7299 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7301 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7303 UIApplicationUseLegacyEvents(YES);
7304 UIKeyboardDisableAutomaticAppearance();
7307 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7309 CGColorSpaceRelease(space_);