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/objc.h>
45 #include <objc/runtime.h>
47 #include <CoreGraphics/CoreGraphics.h>
48 #include <GraphicsServices/GraphicsServices.h>
49 #include <Foundation/Foundation.h>
51 #import <QuartzCore/CALayer.h>
53 #import <UIKit/UIKit.h>
56 #import <MessageUI/MailComposeController.h>
62 #include <ext/stdio_filebuf.h>
64 #include <apt-pkg/acquire.h>
65 #include <apt-pkg/acquire-item.h>
66 #include <apt-pkg/algorithms.h>
67 #include <apt-pkg/cachefile.h>
68 #include <apt-pkg/clean.h>
69 #include <apt-pkg/configuration.h>
70 #include <apt-pkg/debmetaindex.h>
71 #include <apt-pkg/error.h>
72 #include <apt-pkg/init.h>
73 #include <apt-pkg/mmap.h>
74 #include <apt-pkg/pkgrecords.h>
75 #include <apt-pkg/sha1.h>
76 #include <apt-pkg/sourcelist.h>
77 #include <apt-pkg/sptr.h>
78 #include <apt-pkg/strutl.h>
80 #include <sys/types.h>
82 #include <sys/sysctl.h>
83 #include <sys/param.h>
84 #include <sys/mount.h>
90 #include <mach-o/nlist.h>
100 #import "BrowserView.h"
101 #import "ResetView.h"
103 #import "substrate.h"
106 //#define _finline __attribute__((force_inline))
107 #define _finline inline
112 #define _limit(count) do { \
113 static size_t _count(0); \
114 if (++_count == count) \
118 #define _timestamp ({ \
120 gettimeofday(&tv, NULL); \
121 tv.tv_sec * 1000000 + tv.tv_usec; \
124 typedef std::vector<class ProfileTime *> TimeList;
133 ProfileTime(const char *name) :
137 times_.push_back(this);
140 void AddTime(uint64_t time) {
146 std::cerr << std::setw(7) << total_ << " : " << name_ << std::endl;
157 ProfileTimer(ProfileTime &time) :
164 time_.AddTime(_timestamp - start_);
169 for (TimeList::const_iterator i(times_.begin()); i != times_.end(); ++i)
171 std::cerr << "========" << std::endl;
174 #define _profile(name) { \
175 static ProfileTime name(#name); \
176 ProfileTimer _ ## name(name);
180 /* Objective-C Handle<> {{{ */
181 template <typename Type_>
183 typedef _H<Type_> This_;
188 _finline void Retain_() {
193 _finline void Clear_() {
199 _finline _H(Type_ *value = NULL, bool mended = false) :
210 _finline This_ &operator =(Type_ *value) {
211 if (value_ != value) {
220 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
222 void NSLogPoint(const char *fix, const CGPoint &point) {
223 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
226 void NSLogRect(const char *fix, const CGRect &rect) {
227 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
230 @interface NSObject (Cydia)
231 - (void) yieldToSelector:(SEL)selector withObject:(id)object;
234 @implementation NSObject (Cydia)
239 - (void) _yieldToContext:(NSArray *)context { _pooled
240 SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
241 id object([[context objectAtIndex:1] nonretainedObjectValue]);
242 volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
244 [self performSelector:selector withObject:object];
249 performSelectorOnMainThread:@selector(doNothing)
255 - (void) yieldToSelector:(SEL)selector withObject:(id)object {
256 [self performSelector:selector withObject:object];
259 volatile bool stopped(false);
261 NSArray *context([NSArray arrayWithObjects:
262 [NSValue valueWithPointer:selector],
263 [NSValue valueWithNonretainedObject:object],
264 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
267 NSThread *thread([[[NSThread alloc]
269 selector:@selector(_yieldToContext:)
275 NSRunLoop *loop([NSRunLoop currentRunLoop]);
276 NSDate *future([NSDate distantFuture]);
278 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
283 /* NSForcedOrderingSearch doesn't work on the iPhone */
284 static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
285 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
286 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
287 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
289 /* iPhoneOS 2.0 Compatibility {{{ */
291 @interface UITextView (iPhoneOS)
292 - (void) setTextSize:(float)size;
295 @implementation UITextView (iPhoneOS)
297 - (void) setTextSize:(float)size {
298 [self setFont:[[self font] fontWithSize:size]];
305 extern NSString * const kCAFilterNearest;
307 /* Information Dictionaries {{{ */
308 @interface NSMutableArray (Cydia)
309 - (void) addInfoDictionary:(NSDictionary *)info;
312 @implementation NSMutableArray (Cydia)
314 - (void) addInfoDictionary:(NSDictionary *)info {
315 [self addObject:info];
320 @interface NSMutableDictionary (Cydia)
321 - (void) addInfoDictionary:(NSDictionary *)info;
324 @implementation NSMutableDictionary (Cydia)
326 - (void) addInfoDictionary:(NSDictionary *)info {
327 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
328 [self setObject:info forKey:bundle];
333 /* Pop Transitions {{{ */
334 @interface PopTransitionView : UITransitionView {
339 @implementation PopTransitionView
341 - (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
342 if (from != nil && to == nil)
343 [self removeFromSuperview];
348 @implementation UIView (PopUpView)
350 - (void) popFromSuperviewAnimated:(BOOL)animated {
351 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
354 - (void) popSubview:(UIView *)view {
355 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
356 [transition setDelegate:transition];
357 [self addSubview:transition];
359 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
360 [transition transition:UITransitionNone toView:blank];
361 [transition transition:UITransitionPushFromBottom toView:view];
367 #define lprintf(args...) fprintf(stderr, args)
370 #define ForSaurik (1 && !ForRelease)
371 #define ShowInternals (0 && !ForRelease)
372 #define IgnoreInstall (0 && !ForRelease)
373 #define RecycleWebViews 0
374 #define AlwaysReload (1 && !ForRelease)
378 #define _trace(args...)
380 #define _profile(name)
386 @interface NSMutableArray (Radix)
387 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
390 @implementation NSMutableArray (Radix)
392 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
393 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
394 [invocation setSelector:selector];
395 [invocation setArgument:&object atIndex:2];
397 size_t count([self count]);
402 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
404 for (size_t i(0); i != count; ++i) {
405 RadixItem &item(lhs[i]);
408 id object([self objectAtIndex:i]);
409 [invocation setTarget:object];
412 [invocation getReturnValue:&item.key];
415 static const size_t width = 32;
416 static const size_t bits = 11;
417 static const size_t slots = 1 << bits;
418 static const size_t passes = (width + (bits - 1)) / bits;
420 size_t *hist(new size_t[slots]);
422 for (size_t pass(0); pass != passes; ++pass) {
423 memset(hist, 0, sizeof(size_t) * slots);
425 for (size_t i(0); i != count; ++i) {
426 uint32_t key(lhs[i].key);
428 key &= _not(uint32_t) >> width - bits;
433 for (size_t i(0); i != slots; ++i) {
434 size_t local(offset);
439 for (size_t i(0); i != count; ++i) {
440 uint32_t key(lhs[i].key);
442 key &= _not(uint32_t) >> width - bits;
443 rhs[hist[key]++] = lhs[i];
453 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
454 for (size_t i(0); i != count; ++i)
455 [values addObject:[self objectAtIndex:lhs[i].index]];
456 [self setArray:values];
464 /* Apple Bug Fixes {{{ */
465 @implementation UIWebDocumentView (Cydia)
467 - (void) _setScrollerOffset:(CGPoint)offset {
468 UIScroller *scroller([self _scroller]);
470 CGSize size([scroller contentSize]);
471 CGSize bounds([scroller bounds].size);
474 max.x = size.width - bounds.width;
475 max.y = size.height - bounds.height;
483 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
484 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
486 [scroller setOffset:offset];
493 kUIControlEventMouseDown = 1 << 0,
494 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
495 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
496 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
497 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
498 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
499 } UIControlEventMasks;
501 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
502 size_t length([self length] - state->state);
505 else if (length > count)
507 for (size_t i(0); i != length; ++i)
508 objects[i] = [self item:state->state++];
509 state->itemsPtr = objects;
510 state->mutationsPtr = (unsigned long *) self;
514 @interface NSString (UIKit)
515 - (NSString *) stringByAddingPercentEscapes;
516 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
519 @interface NSString (Cydia)
520 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
521 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
522 - (NSComparisonResult) compareByPath:(NSString *)other;
525 @implementation NSString (Cydia)
527 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length {
528 return [[[NSString alloc] initWithBytesNoCopy:const_cast<char *>(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
531 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
532 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
535 - (NSComparisonResult) compareByPath:(NSString *)other {
536 NSString *prefix = [self commonPrefixWithString:other options:0];
537 size_t length = [prefix length];
539 NSRange lrange = NSMakeRange(length, [self length] - length);
540 NSRange rrange = NSMakeRange(length, [other length] - length);
542 lrange = [self rangeOfString:@"/" options:0 range:lrange];
543 rrange = [other rangeOfString:@"/" options:0 range:rrange];
545 NSComparisonResult value;
547 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
548 value = NSOrderedSame;
549 else if (lrange.location == NSNotFound)
550 value = NSOrderedAscending;
551 else if (rrange.location == NSNotFound)
552 value = NSOrderedDescending;
554 value = NSOrderedSame;
556 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
557 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
558 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
559 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
561 NSComparisonResult result = [lpath compare:rpath];
562 return result == NSOrderedSame ? value : result;
567 /* Perl-Compatible RegEx {{{ */
577 Pcre(const char *regex) :
582 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
585 lprintf("%d:%s\n", offset, error);
589 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
590 matches_ = new int[(capture_ + 1) * 3];
598 NSString *operator [](size_t match) {
599 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
602 bool operator ()(NSString *data) {
603 // XXX: length is for characters, not for bytes
604 return operator ()([data UTF8String], [data length]);
607 bool operator ()(const char *data, size_t size) {
609 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
613 /* Mime Addresses {{{ */
614 @interface Address : NSObject {
620 - (NSString *) address;
622 + (Address *) addressWithString:(NSString *)string;
623 - (Address *) initWithString:(NSString *)string;
626 @implementation Address
635 - (NSString *) name {
639 - (NSString *) address {
643 + (Address *) addressWithString:(NSString *)string {
644 return [[[Address alloc] initWithString:string] autorelease];
647 + (NSArray *) _attributeKeys {
648 return [NSArray arrayWithObjects:@"address", @"name", nil];
651 - (NSArray *) attributeKeys {
652 return [[self class] _attributeKeys];
655 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
656 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
659 - (Address *) initWithString:(NSString *)string {
660 if ((self = [super init]) != nil) {
661 const char *data = [string UTF8String];
662 size_t size = [string length];
664 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
666 if (address_r(data, size)) {
667 name_ = [address_r[1] retain];
668 address_ = [address_r[2] retain];
670 name_ = [string retain];
678 /* CoreGraphics Primitives {{{ */
689 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
692 Set(space, red, green, blue, alpha);
697 CGColorRelease(color_);
704 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
706 float color[] = {red, green, blue, alpha};
707 color_ = CGColorCreate(space, color);
710 operator CGColorRef() {
716 extern "C" void UISetColor(CGColorRef color);
718 /* Random Global Variables {{{ */
719 static const int PulseInterval_ = 50000;
720 static const int ButtonBarHeight_ = 48;
721 static const float KeyboardTime_ = 0.3f;
723 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
724 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
725 #define NotifyConfig_ "/etc/notify.conf"
727 static CGColor Blue_;
728 static CGColor Blueish_;
729 static CGColor Black_;
731 static CGColor White_;
732 static CGColor Gray_;
734 static NSString *App_;
735 static NSString *Home_;
736 static BOOL Sounds_Keyboard_;
738 static BOOL Advanced_;
740 static BOOL Ignored_;
742 static UIFont *Font12_;
743 static UIFont *Font12Bold_;
744 static UIFont *Font14_;
745 static UIFont *Font18Bold_;
746 static UIFont *Font22Bold_;
748 static const char *Machine_ = NULL;
749 static const NSString *UniqueID_ = nil;
750 static const NSString *Build_ = nil;
753 CGColorSpaceRef space_;
758 static NSDictionary *SectionMap_;
759 static NSMutableDictionary *Metadata_;
760 static _transient NSMutableDictionary *Settings_;
761 static _transient NSString *Role_;
762 static _transient NSMutableDictionary *Packages_;
763 static _transient NSMutableDictionary *Sections_;
764 static _transient NSMutableDictionary *Sources_;
765 static bool Changed_;
769 static NSMutableArray *Documents_;
772 NSString *GetLastUpdate() {
773 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
776 return @"Never or Unknown";
778 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
779 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
781 CFRelease(formatter);
783 return [(NSString *) formatted autorelease];
786 /* Display Helpers {{{ */
787 inline float Interpolate(float begin, float end, float fraction) {
788 return (end - begin) * fraction + begin;
791 NSString *SizeString(double size) {
792 bool negative = size < 0;
797 while (size > 1024) {
802 static const char *powers_[] = {"B", "kB", "MB", "GB"};
804 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
807 NSString *StripVersion(NSString *version) {
808 NSRange colon = [version rangeOfString:@":"];
809 if (colon.location != NSNotFound)
810 version = [version substringFromIndex:(colon.location + 1)];
814 NSString *Simplify(NSString *title) {
815 const char *data = [title UTF8String];
816 size_t size = [title length];
818 static Pcre square_r("^\\[(.*)\\]$");
819 if (square_r(data, size))
820 return Simplify(square_r[1]);
822 static Pcre paren_r("^\\((.*)\\)$");
823 if (paren_r(data, size))
824 return Simplify(paren_r[1]);
826 static Pcre title_r("^(.*?) \\(.*\\)$");
827 if (title_r(data, size))
828 return Simplify(title_r[1]);
834 bool isSectionVisible(NSString *section) {
835 NSDictionary *metadata = [Sections_ objectForKey:section];
836 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
837 return hidden == nil || ![hidden boolValue];
840 /* Delegate Prototypes {{{ */
844 @interface NSObject (ProgressDelegate)
847 @implementation NSObject(ProgressDelegate)
849 - (void) _setProgressError:(NSArray *)args {
850 [self performSelector:@selector(setProgressError:forPackage:)
851 withObject:[args objectAtIndex:0]
852 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
858 @protocol ProgressDelegate
859 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
860 - (void) setProgressTitle:(NSString *)title;
861 - (void) setProgressPercent:(float)percent;
862 - (void) startProgress;
863 - (void) addProgressOutput:(NSString *)output;
864 - (bool) isCancelling:(size_t)received;
867 @protocol ConfigurationDelegate
868 - (void) repairWithSelector:(SEL)selector;
869 - (void) setConfigurationData:(NSString *)data;
872 @protocol CydiaDelegate
873 - (void) installPackage:(Package *)package;
874 - (void) removePackage:(Package *)package;
875 - (void) slideUp:(UIActionSheet *)alert;
876 - (void) distUpgrade;
879 - (void) askForSettings;
880 - (UIProgressHUD *) addProgressHUD;
881 - (void) removeProgressHUD:(UIProgressHUD *)hud;
882 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
883 - (RVPage *) pageForPackage:(NSString *)name;
884 - (void) openMailToURL:(NSURL *)url;
885 - (void) clearFirstResponder;
889 /* Status Delegation {{{ */
891 public pkgAcquireStatus
894 _transient NSObject<ProgressDelegate> *delegate_;
902 void setDelegate(id delegate) {
903 delegate_ = delegate;
906 virtual bool MediaChange(std::string media, std::string drive) {
910 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
913 virtual void Fetch(pkgAcquire::ItemDesc &item) {
914 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
915 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
918 virtual void Done(pkgAcquire::ItemDesc &item) {
921 virtual void Fail(pkgAcquire::ItemDesc &item) {
923 item.Owner->Status == pkgAcquire::Item::StatIdle ||
924 item.Owner->Status == pkgAcquire::Item::StatDone
928 std::string &error(item.Owner->ErrorText);
932 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
933 NSArray *fields([description componentsSeparatedByString:@" "]);
934 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
936 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
937 withObject:[NSArray arrayWithObjects:
938 [NSString stringWithUTF8String:error.c_str()],
945 virtual bool Pulse(pkgAcquire *Owner) {
946 bool value = pkgAcquireStatus::Pulse(Owner);
949 double(CurrentBytes + CurrentItems) /
950 double(TotalBytes + TotalItems)
953 [delegate_ setProgressPercent:percent];
954 return [delegate_ isCancelling:CurrentBytes] ? false : value;
957 virtual void Start() {
958 [delegate_ startProgress];
961 virtual void Stop() {
965 /* Progress Delegation {{{ */
970 _transient id<ProgressDelegate> delegate_;
973 virtual void Update() {
974 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
975 [delegate_ setProgressPercent:(Percent / 100)];*/
984 void setDelegate(id delegate) {
985 delegate_ = delegate;
988 virtual void Done() {
989 //[delegate_ setProgressPercent:1];
994 /* Database Interface {{{ */
995 @interface Database : NSObject {
997 pkgDepCache::Policy *policy_;
998 pkgRecords *records_;
999 pkgProblemResolver *resolver_;
1000 pkgAcquire *fetcher_;
1002 SPtr<pkgPackageManager> manager_;
1003 pkgSourceList *list_;
1005 NSMutableDictionary *sources_;
1006 NSMutableArray *packages_;
1008 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
1017 + (Database *) sharedInstance;
1019 - (void) _readCydia:(NSNumber *)fd;
1020 - (void) _readStatus:(NSNumber *)fd;
1021 - (void) _readOutput:(NSNumber *)fd;
1025 - (Package *) packageWithName:(NSString *)name;
1027 - (pkgCacheFile &) cache;
1028 - (pkgDepCache::Policy *) policy;
1029 - (pkgRecords *) records;
1030 - (pkgProblemResolver *) resolver;
1031 - (pkgAcquire &) fetcher;
1032 - (pkgSourceList &) list;
1033 - (NSArray *) packages;
1034 - (NSArray *) sources;
1035 - (void) reloadData;
1043 - (void) updateWithStatus:(Status &)status;
1045 - (void) setDelegate:(id)delegate;
1046 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
1050 /* Source Class {{{ */
1051 @interface Source : NSObject {
1052 NSString *description_;
1057 NSString *distribution_;
1061 NSString *defaultIcon_;
1063 NSDictionary *record_;
1067 - (Source *) initWithMetaIndex:(metaIndex *)index;
1069 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1071 - (NSDictionary *) record;
1075 - (NSString *) distribution;
1076 - (NSString *) type;
1078 - (NSString *) host;
1080 - (NSString *) name;
1081 - (NSString *) description;
1082 - (NSString *) label;
1083 - (NSString *) origin;
1084 - (NSString *) version;
1086 - (NSString *) defaultIcon;
1090 @implementation Source
1092 #define _clear(field) \
1099 _clear(distribution_)
1102 _clear(description_)
1106 _clear(defaultIcon_)
1115 + (NSArray *) _attributeKeys {
1116 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1119 - (NSArray *) attributeKeys {
1120 return [[self class] _attributeKeys];
1123 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1124 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1127 - (void) setMetaIndex:(metaIndex *)index {
1130 trusted_ = index->IsTrusted();
1132 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1133 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1134 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1136 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1137 if (dindex != NULL) {
1138 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1140 while (std::getline(release, line)) {
1141 std::string::size_type colon(line.find(':'));
1142 if (colon == std::string::npos)
1145 std::string name(line.substr(0, colon));
1146 std::string value(line.substr(colon + 1));
1147 while (!value.empty() && value[0] == ' ')
1148 value = value.substr(1);
1150 if (name == "Default-Icon")
1151 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1152 else if (name == "Description")
1153 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1154 else if (name == "Label")
1155 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1156 else if (name == "Origin")
1157 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1158 else if (name == "Version")
1159 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1163 record_ = [Sources_ objectForKey:[self key]];
1165 record_ = [record_ retain];
1168 - (Source *) initWithMetaIndex:(metaIndex *)index {
1169 if ((self = [super init]) != nil) {
1170 [self setMetaIndex:index];
1174 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1175 NSDictionary *lhr = [self record];
1176 NSDictionary *rhr = [source record];
1179 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1181 NSString *lhs = [self name];
1182 NSString *rhs = [source name];
1184 if ([lhs length] != 0 && [rhs length] != 0) {
1185 unichar lhc = [lhs characterAtIndex:0];
1186 unichar rhc = [rhs characterAtIndex:0];
1188 if (isalpha(lhc) && !isalpha(rhc))
1189 return NSOrderedAscending;
1190 else if (!isalpha(lhc) && isalpha(rhc))
1191 return NSOrderedDescending;
1194 return [lhs compare:rhs options:LaxCompareOptions_];
1197 - (NSDictionary *) record {
1205 - (NSString *) uri {
1209 - (NSString *) distribution {
1210 return distribution_;
1213 - (NSString *) type {
1217 - (NSString *) key {
1218 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1221 - (NSString *) host {
1222 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1225 - (NSString *) name {
1226 return origin_ == nil ? [self host] : origin_;
1229 - (NSString *) description {
1230 return description_;
1233 - (NSString *) label {
1234 return label_ == nil ? [self host] : label_;
1237 - (NSString *) origin {
1241 - (NSString *) version {
1245 - (NSString *) defaultIcon {
1246 return defaultIcon_;
1251 /* Relationship Class {{{ */
1252 @interface Relationship : NSObject {
1257 - (NSString *) type;
1259 - (NSString *) name;
1263 @implementation Relationship
1271 - (NSString *) type {
1279 - (NSString *) name {
1286 /* Package Class {{{ */
1287 @interface Package : NSObject {
1288 pkgCache::PkgIterator iterator_;
1289 _transient Database *database_;
1290 pkgCache::VerIterator version_;
1291 pkgCache::VerFileIterator file_;
1299 NSString *installed_;
1305 NSString *depiction_;
1306 NSString *homepage_;
1312 NSArray *relationships_;
1315 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1316 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1318 - (pkgCache::PkgIterator) iterator;
1320 - (NSString *) section;
1321 - (NSString *) simpleSection;
1325 - (Address *) maintainer;
1327 - (NSString *) description;
1330 - (NSMutableDictionary *) metadata;
1332 - (BOOL) subscribed;
1335 - (NSString *) latest;
1336 - (NSString *) installed;
1339 - (BOOL) upgradableAndEssential:(BOOL)essential;
1342 - (BOOL) unfiltered;
1346 - (BOOL) halfConfigured;
1347 - (BOOL) halfInstalled;
1349 - (NSString *) mode;
1352 - (NSString *) name;
1353 - (NSString *) tagline;
1355 - (NSString *) homepage;
1356 - (NSString *) depiction;
1357 - (Address *) author;
1359 - (NSArray *) files;
1360 - (NSArray *) relationships;
1361 - (NSArray *) warnings;
1362 - (NSArray *) applications;
1364 - (Source *) source;
1365 - (NSString *) role;
1367 - (BOOL) matches:(NSString *)text;
1369 - (bool) hasSupportingRole;
1370 - (BOOL) hasTag:(NSString *)tag;
1371 - (NSString *) primaryPurpose;
1372 - (NSArray *) purposes;
1374 - (NSComparisonResult) compareByName:(Package *)package;
1375 - (NSComparisonResult) compareBySection:(Package *)package;
1377 - (uint32_t) compareForChanges;
1382 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1383 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1384 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1385 - (NSNumber *) isVisibleInSource:(Source *)source;
1389 @implementation Package
1395 if (section_ != nil)
1399 if (installed_ != nil)
1400 [installed_ release];
1408 if (depiction_ != nil)
1409 [depiction_ release];
1410 if (homepage_ != nil)
1411 [homepage_ release];
1412 if (sponsor_ != nil)
1421 if (relationships_ != nil)
1422 [relationships_ release];
1427 + (NSArray *) _attributeKeys {
1428 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1431 - (NSArray *) attributeKeys {
1432 return [[self class] _attributeKeys];
1435 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1436 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1439 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1440 if ((self = [super init]) != nil) { _profile(Package$initWithIterator)
1441 iterator_ = iterator;
1442 database_ = database;
1444 _profile(Package$initWithIterator$Control)
1447 _profile(Package$initWithIterator$Version)
1448 version_ = [database_ policy]->GetCandidateVer(iterator_);
1451 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1453 _profile(Package$initWithIterator$Latest)
1454 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1457 pkgCache::VerIterator current;
1458 NSString *installed;
1460 _profile(Package$initWithIterator$Current)
1461 current = iterator_.CurrentVer();
1462 installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1465 _profile(Package$initWithIterator$Installed)
1466 installed_ = [StripVersion(installed) retain];
1469 _profile(Package$initWithIterator$File)
1470 if (!version_.end())
1471 file_ = version_.FileList();
1473 pkgCache &cache([database_ cache]);
1474 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1478 _profile(Package$initWithIterator$Name)
1479 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1483 _profile(Package$initWithIterator$Parse)
1484 pkgRecords::Parser *parser;
1486 _profile(Package$initWithIterator$Parse$Lookup)
1487 parser = &[database_ records]->Lookup(file_);
1490 const char *begin, *end;
1491 parser->GetRec(begin, end);
1493 NSString *website(nil);
1494 NSString *sponsor(nil);
1495 NSString *author(nil);
1504 {"depiction", &depiction_},
1505 {"homepage", &homepage_},
1506 {"website", &website},
1507 {"sponsor", &sponsor},
1508 {"author", &author},
1512 while (begin != end)
1513 if (*begin == '\n') {
1516 } else if (isblank(*begin)) next: {
1517 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1520 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1521 const char *name(begin);
1522 size_t size(colon - begin);
1524 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1527 const char *stop(begin == NULL ? end : begin);
1528 while (stop[-1] == '\r')
1530 while (++colon != stop && isblank(*colon));
1532 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1533 if (strncasecmp(names[i].name_, name, size) == 0) {
1536 _profile(Package$initWithIterator$Parse$Value)
1537 value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
1540 *names[i].value_ = value;
1550 _profile(Package$initWithIterator$Parse$Retain)
1552 name_ = [name_ retain];
1553 _profile(Package$initWithIterator$Parse$Tagline)
1554 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1557 icon_ = [icon_ retain];
1558 if (depiction_ != nil)
1559 depiction_ = [depiction_ retain];
1560 if (homepage_ == nil)
1561 homepage_ = website;
1562 if ([homepage_ isEqualToString:depiction_])
1564 if (homepage_ != nil)
1565 homepage_ = [homepage_ retain];
1567 sponsor_ = [[Address addressWithString:sponsor] retain];
1569 author_ = [[Address addressWithString:author] retain];
1571 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1575 _profile(Package$initWithIterator$Tags)
1577 for (int i(0), e([tags_ count]); i != e; ++i) {
1578 NSString *tag = [tags_ objectAtIndex:i];
1579 if ([tag hasPrefix:@"role::"]) {
1580 role_ = [[tag substringFromIndex:6] retain];
1586 NSString *solid(latest == nil ? installed : latest);
1587 bool changed(false);
1589 NSString *key([id_ lowercaseString]);
1591 _profile(Package$initWithIterator$Metadata)
1592 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1593 if (metadata == nil) {
1594 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1599 [metadata setObject:solid forKey:@"LastVersion"];
1602 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1603 NSDate *last([metadata objectForKey:@"LastSeen"]);
1604 NSString *version([metadata objectForKey:@"LastVersion"]);
1607 first = last == nil ? now_ : last;
1608 [metadata setObject:first forKey:@"FirstSeen"];
1613 if (version == nil) {
1614 [metadata setObject:solid forKey:@"LastVersion"];
1616 } else if (![version isEqualToString:solid]) {
1617 [metadata setObject:solid forKey:@"LastVersion"];
1619 [metadata setObject:last forKey:@"LastSeen"];
1625 [Packages_ setObject:metadata forKey:key];
1632 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1633 return [[[Package alloc]
1634 initWithIterator:iterator
1639 - (pkgCache::PkgIterator) iterator {
1643 - (NSString *) section {
1644 if (section_ != nil)
1647 const char *section = iterator_.Section();
1648 if (section == NULL)
1651 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1654 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1655 if (NSString *rename = [value objectForKey:@"Rename"]) {
1660 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1664 - (NSString *) simpleSection {
1665 if (NSString *section = [self section])
1666 return Simplify(section);
1671 - (NSString *) uri {
1674 pkgIndexFile *index;
1675 pkgCache::PkgFileIterator file(file_.File());
1676 if (![database_ list].FindIndex(file, index))
1678 return [NSString stringWithUTF8String:iterator_->Path];
1679 //return [NSString stringWithUTF8String:file.Site()];
1680 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1684 - (Address *) maintainer {
1687 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1688 const std::string &maintainer(parser->Maintainer());
1689 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1693 return version_.end() ? 0 : version_->InstalledSize;
1696 - (NSString *) description {
1699 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1700 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1702 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1703 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1704 if ([lines count] < 2)
1707 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1708 for (size_t i(1); i != [lines count]; ++i) {
1709 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1710 [trimmed addObject:trim];
1713 return [trimmed componentsJoinedByString:@"\n"];
1717 _profile(Package$index)
1718 NSString *name([self name]);
1719 if ([name length] == 0)
1721 unichar character([name characterAtIndex:0]);
1722 if (!isalpha(character))
1728 - (NSMutableDictionary *) metadata {
1729 return [Packages_ objectForKey:[id_ lowercaseString]];
1733 NSDictionary *metadata([self metadata]);
1734 if ([self subscribed])
1735 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1737 return [metadata objectForKey:@"FirstSeen"];
1740 - (BOOL) subscribed {
1741 NSDictionary *metadata([self metadata]);
1742 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1743 return [subscribed boolValue];
1749 NSDictionary *metadata([self metadata]);
1750 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1751 return [ignored boolValue];
1756 - (NSString *) latest {
1760 - (NSString *) installed {
1765 return !version_.end();
1768 - (BOOL) upgradableAndEssential:(BOOL)essential {
1769 pkgCache::VerIterator current = iterator_.CurrentVer();
1773 value = essential && [self essential] && [self visible];
1775 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1779 - (BOOL) essential {
1780 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1784 return [database_ cache][iterator_].InstBroken();
1787 - (BOOL) unfiltered {
1788 NSString *section = [self section];
1789 return section == nil || isSectionVisible(section);
1793 return [self hasSupportingRole] && [self unfiltered];
1797 unsigned char current = iterator_->CurrentState;
1798 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1801 - (BOOL) halfConfigured {
1802 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1805 - (BOOL) halfInstalled {
1806 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1810 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1811 return state.Mode != pkgDepCache::ModeKeep;
1814 - (NSString *) mode {
1815 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1817 switch (state.Mode) {
1818 case pkgDepCache::ModeDelete:
1819 if ((state.iFlags & pkgDepCache::Purge) != 0)
1823 case pkgDepCache::ModeKeep:
1824 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1828 case pkgDepCache::ModeInstall:
1829 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1830 return @"Reinstall";
1831 else switch (state.Status) {
1833 return @"Downgrade";
1839 return @"New Install";
1852 - (NSString *) name {
1853 return name_ == nil ? id_ : name_;
1856 - (NSString *) tagline {
1860 - (UIImage *) icon {
1861 NSString *section = [self simpleSection];
1865 if ([icon_ hasPrefix:@"file:///"])
1866 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1867 if (icon == nil) if (section != nil)
1868 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1869 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1870 if ([dicon hasPrefix:@"file:///"])
1871 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1873 icon = [UIImage applicationImageNamed:@"unknown.png"];
1877 - (NSString *) homepage {
1881 - (NSString *) depiction {
1885 - (Address *) sponsor {
1889 - (Address *) author {
1893 - (NSArray *) files {
1894 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1895 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1898 fin.open([path UTF8String]);
1903 while (std::getline(fin, line))
1904 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1909 - (NSArray *) relationships {
1910 return relationships_;
1913 - (NSArray *) warnings {
1914 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1915 const char *name(iterator_.Name());
1917 size_t length(strlen(name));
1918 if (length < 2) invalid:
1919 [warnings addObject:@"illegal package identifier"];
1920 else for (size_t i(0); i != length; ++i)
1922 /* XXX: technically this is not allowed */
1923 (name[i] < 'A' || name[i] > 'Z') &&
1924 (name[i] < 'a' || name[i] > 'z') &&
1925 (name[i] < '0' || name[i] > '9') &&
1926 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1929 if (strcmp(name, "cydia") != 0) {
1931 bool _private = false;
1934 bool repository = [[self section] isEqualToString:@"Repositories"];
1936 if (NSArray *files = [self files])
1937 for (NSString *file in files)
1938 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1940 else if (!_private && [file isEqualToString:@"/private"])
1942 else if (!stash && [file isEqualToString:@"/var/stash"])
1945 /* XXX: this is not sensitive enough. only some folders are valid. */
1946 if (cydia && !repository)
1947 [warnings addObject:@"files installed into Cydia.app"];
1949 [warnings addObject:@"files installed with /private/*"];
1951 [warnings addObject:@"files installed to /var/stash"];
1954 return [warnings count] == 0 ? nil : warnings;
1957 - (NSArray *) applications {
1958 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1960 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1962 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1963 if (NSArray *files = [self files])
1964 for (NSString *file in files)
1965 if (application_r(file)) {
1966 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1967 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1968 if ([id isEqualToString:me])
1971 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1973 display = application_r[1];
1975 NSString *bundle([file stringByDeletingLastPathComponent]);
1976 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1977 if (icon == nil || [icon length] == 0)
1979 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1981 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1982 [applications addObject:application];
1984 [application addObject:id];
1985 [application addObject:display];
1986 [application addObject:url];
1989 return [applications count] == 0 ? nil : applications;
1992 - (Source *) source {
1994 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
2001 - (NSString *) role {
2005 - (BOOL) matches:(NSString *)text {
2011 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2012 if (range.location != NSNotFound)
2015 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2016 if (range.location != NSNotFound)
2019 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2020 if (range.location != NSNotFound)
2026 - (bool) hasSupportingRole {
2029 if ([role_ isEqualToString:@"enduser"])
2031 if ([Role_ isEqualToString:@"User"])
2033 if ([role_ isEqualToString:@"hacker"])
2035 if ([Role_ isEqualToString:@"Hacker"])
2037 if ([role_ isEqualToString:@"developer"])
2039 if ([Role_ isEqualToString:@"Developer"])
2044 - (BOOL) hasTag:(NSString *)tag {
2045 return tags_ == nil ? NO : [tags_ containsObject:tag];
2048 - (NSString *) primaryPurpose {
2049 for (NSString *tag in tags_)
2050 if ([tag hasPrefix:@"purpose::"])
2051 return [tag substringFromIndex:9];
2055 - (NSArray *) purposes {
2056 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2057 for (NSString *tag in tags_)
2058 if ([tag hasPrefix:@"purpose::"])
2059 [purposes addObject:[tag substringFromIndex:9]];
2060 return [purposes count] == 0 ? nil : purposes;
2063 - (NSComparisonResult) compareByName:(Package *)package {
2064 NSString *lhs = [self name];
2065 NSString *rhs = [package name];
2067 if ([lhs length] != 0 && [rhs length] != 0) {
2068 unichar lhc = [lhs characterAtIndex:0];
2069 unichar rhc = [rhs characterAtIndex:0];
2071 if (isalpha(lhc) && !isalpha(rhc))
2072 return NSOrderedAscending;
2073 else if (!isalpha(lhc) && isalpha(rhc))
2074 return NSOrderedDescending;
2077 return [lhs compare:rhs options:LaxCompareOptions_];
2080 - (NSComparisonResult) compareBySection:(Package *)package {
2081 NSString *lhs = [self section];
2082 NSString *rhs = [package section];
2084 if (lhs == NULL && rhs != NULL)
2085 return NSOrderedAscending;
2086 else if (lhs != NULL && rhs == NULL)
2087 return NSOrderedDescending;
2088 else if (lhs != NULL && rhs != NULL) {
2089 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2090 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2093 return NSOrderedSame;
2096 - (uint32_t) compareForChanges {
2101 uint32_t timestamp : 30;
2102 uint32_t ignored : 1;
2103 uint32_t upgradable : 1;
2107 bool upgradable([self upgradableAndEssential:YES]);
2108 value.bits.upgradable = upgradable ? 1 : 0;
2111 value.bits.timestamp = 0;
2112 value.bits.ignored = [self ignored] ? 0 : 1;
2113 value.bits.upgradable = 1;
2115 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2116 value.bits.ignored = 0;
2117 value.bits.upgradable = 0;
2120 return _not(uint32_t) - value.key;
2124 pkgProblemResolver *resolver = [database_ resolver];
2125 resolver->Clear(iterator_);
2126 resolver->Protect(iterator_);
2127 pkgCacheFile &cache([database_ cache]);
2128 cache->MarkInstall(iterator_, false);
2129 pkgDepCache::StateCache &state((*cache)[iterator_]);
2130 if (!state.Install())
2131 cache->SetReInstall(iterator_, true);
2135 pkgProblemResolver *resolver = [database_ resolver];
2136 resolver->Clear(iterator_);
2137 resolver->Protect(iterator_);
2138 resolver->Remove(iterator_);
2139 [database_ cache]->MarkDelete(iterator_, true);
2142 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
2143 _profile(Package$isUnfilteredAndSearchedForBy)
2146 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2147 value &= [self unfiltered];
2150 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2151 value &= [self matches:search];
2154 return [NSNumber numberWithBool:value];
2158 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
2159 return [NSNumber numberWithBool:(
2160 (![number boolValue] || [self visible]) && [self installed] != nil
2164 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
2165 NSString *section = [self section];
2167 return [NSNumber numberWithBool:(
2169 [self installed] == nil && (
2171 section == nil && [name length] == 0 ||
2172 [name isEqualToString:section]
2177 - (NSNumber *) isVisibleInSource:(Source *)source {
2178 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2183 /* Section Class {{{ */
2184 @interface Section : NSObject {
2191 - (NSComparisonResult) compareByName:(Section *)section;
2192 - (Section *) initWithName:(NSString *)name;
2193 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2194 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2195 - (NSString *) name;
2199 - (void) addToCount;
2203 @implementation Section
2210 - (NSComparisonResult) compareByName:(Section *)section {
2211 NSString *lhs = [self name];
2212 NSString *rhs = [section name];
2214 if ([lhs length] != 0 && [rhs length] != 0) {
2215 unichar lhc = [lhs characterAtIndex:0];
2216 unichar rhc = [rhs characterAtIndex:0];
2218 if (isalpha(lhc) && !isalpha(rhc))
2219 return NSOrderedAscending;
2220 else if (!isalpha(lhc) && isalpha(rhc))
2221 return NSOrderedDescending;
2224 return [lhs compare:rhs options:LaxCompareOptions_];
2227 - (Section *) initWithName:(NSString *)name {
2228 return [self initWithName:name row:0];
2231 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2232 if ((self = [super init]) != nil) {
2233 name_ = [name retain];
2239 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2240 if ((self = [super init]) != nil) {
2241 name_ = [[NSString stringWithCharacters:&index length:1] retain];
2247 - (NSString *) name {
2263 - (void) addToCount {
2271 static NSArray *Finishes_;
2273 /* Database Implementation {{{ */
2274 @implementation Database
2276 + (Database *) sharedInstance {
2277 static Database *instance;
2278 if (instance == nil)
2279 instance = [[Database alloc] init];
2288 - (void) _readCydia:(NSNumber *)fd { _pooled
2289 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2290 std::istream is(&ib);
2293 static Pcre finish_r("^finish:([^:]*)$");
2295 while (std::getline(is, line)) {
2296 const char *data(line.c_str());
2297 size_t size = line.size();
2298 lprintf("C:%s\n", data);
2300 if (finish_r(data, size)) {
2301 NSString *finish = finish_r[1];
2302 int index = [Finishes_ indexOfObject:finish];
2303 if (index != INT_MAX && index > Finish_)
2311 - (void) _readStatus:(NSNumber *)fd { _pooled
2312 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2313 std::istream is(&ib);
2316 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2317 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2319 while (std::getline(is, line)) {
2320 const char *data(line.c_str());
2321 size_t size = line.size();
2322 lprintf("S:%s\n", data);
2324 if (conffile_r(data, size)) {
2325 [delegate_ setConfigurationData:conffile_r[1]];
2326 } else if (strncmp(data, "status: ", 8) == 0) {
2327 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2328 [delegate_ setProgressTitle:string];
2329 } else if (pmstatus_r(data, size)) {
2330 std::string type([pmstatus_r[1] UTF8String]);
2331 NSString *id = pmstatus_r[2];
2333 float percent([pmstatus_r[3] floatValue]);
2334 [delegate_ setProgressPercent:(percent / 100)];
2336 NSString *string = pmstatus_r[4];
2338 if (type == "pmerror")
2339 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2340 withObject:[NSArray arrayWithObjects:string, id, nil]
2343 else if (type == "pmstatus") {
2344 [delegate_ setProgressTitle:string];
2345 } else if (type == "pmconffile")
2346 [delegate_ setConfigurationData:string];
2347 else _assert(false);
2348 } else _assert(false);
2354 - (void) _readOutput:(NSNumber *)fd { _pooled
2355 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2356 std::istream is(&ib);
2359 while (std::getline(is, line)) {
2360 lprintf("O:%s\n", line.c_str());
2361 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2371 - (Package *) packageWithName:(NSString *)name {
2372 if (static_cast<pkgDepCache *>(cache_) == NULL)
2374 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2375 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2378 - (Database *) init {
2379 if ((self = [super init]) != nil) {
2386 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2387 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2391 _assert(pipe(fds) != -1);
2394 _config->Set("APT::Keep-Fds::", cydiafd_);
2395 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2398 detachNewThreadSelector:@selector(_readCydia:)
2400 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2403 _assert(pipe(fds) != -1);
2407 detachNewThreadSelector:@selector(_readStatus:)
2409 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2412 _assert(pipe(fds) != -1);
2413 _assert(dup2(fds[0], 0) != -1);
2414 _assert(close(fds[0]) != -1);
2416 input_ = fdopen(fds[1], "a");
2418 _assert(pipe(fds) != -1);
2419 _assert(dup2(fds[1], 1) != -1);
2420 _assert(close(fds[1]) != -1);
2423 detachNewThreadSelector:@selector(_readOutput:)
2425 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2430 - (pkgCacheFile &) cache {
2434 - (pkgDepCache::Policy *) policy {
2438 - (pkgRecords *) records {
2442 - (pkgProblemResolver *) resolver {
2446 - (pkgAcquire &) fetcher {
2450 - (pkgSourceList &) list {
2454 - (NSArray *) packages {
2458 - (NSArray *) sources {
2459 return [sources_ allValues];
2462 - (NSArray *) issues {
2463 if (cache_->BrokenCount() == 0)
2466 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2468 for (Package *package in packages_) {
2469 if (![package broken])
2471 pkgCache::PkgIterator pkg([package iterator]);
2473 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2474 [entry addObject:[package name]];
2475 [issues addObject:entry];
2477 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2481 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2482 pkgCache::DepIterator start;
2483 pkgCache::DepIterator end;
2484 dep.GlobOr(start, end); // ++dep
2486 if (!cache_->IsImportantDep(end))
2488 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2491 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2492 [entry addObject:failure];
2493 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2495 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2496 [failure addObject:[package name]];
2498 pkgCache::PkgIterator target(start.TargetPkg());
2499 if (target->ProvidesList != 0)
2500 [failure addObject:@"?"];
2502 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2504 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2505 else if (!cache_[target].CandidateVerIter(cache_).end())
2506 [failure addObject:@"-"];
2507 else if (target->ProvidesList == 0)
2508 [failure addObject:@"!"];
2510 [failure addObject:@"%"];
2514 if (start.TargetVer() != 0)
2515 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2526 - (void) reloadData { _pooled
2546 if (!cache_.Open(progress_, true)) {
2548 if (!_error->PopMessage(error))
2551 lprintf("cache_.Open():[%s]\n", error.c_str());
2553 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2554 [delegate_ repairWithSelector:@selector(configure)];
2555 else if (error == "The package lists or status file could not be parsed or opened.")
2556 [delegate_ repairWithSelector:@selector(update)];
2557 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2558 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2559 // else if (error == "The list of sources could not be read.")
2560 else _assert(false);
2566 now_ = [[NSDate date] retain];
2568 policy_ = new pkgDepCache::Policy();
2569 records_ = new pkgRecords(cache_);
2570 resolver_ = new pkgProblemResolver(cache_);
2571 fetcher_ = new pkgAcquire(&status_);
2574 list_ = new pkgSourceList();
2575 _assert(list_->ReadMainList());
2577 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2578 _assert(pkgApplyStatus(cache_));
2580 if (cache_->BrokenCount() != 0) {
2581 _assert(pkgFixBroken(cache_));
2582 _assert(cache_->BrokenCount() == 0);
2583 _assert(pkgMinimizeUpgrade(cache_));
2586 [sources_ removeAllObjects];
2587 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2588 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2589 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2591 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2592 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2596 [packages_ removeAllObjects];
2598 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2599 if (Package *package = [Package packageWithIterator:iterator database:self])
2600 [packages_ addObject:package];
2602 [packages_ sortUsingSelector:@selector(compareByName:)];
2605 _config->Set("Acquire::http::Timeout", 15);
2606 _config->Set("Acquire::http::MaxParallel", 4);
2609 - (void) configure {
2610 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2611 system([dpkg UTF8String]);
2619 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2620 _assert(!_error->PendingError());
2623 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2626 public pkgArchiveCleaner
2629 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2634 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2636 while (_error->PopMessage(error))
2637 lprintf("ArchiveCleaner: %s\n", error.c_str());
2642 pkgRecords records(cache_);
2644 lock_ = new FileFd();
2645 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2646 _assert(!_error->PendingError());
2649 // XXX: explain this with an error message
2650 _assert(list.ReadMainList());
2652 manager_ = (_system->CreatePM(cache_));
2653 _assert(manager_->GetArchives(fetcher_, &list, &records));
2654 _assert(!_error->PendingError());
2658 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2660 _assert(list.ReadMainList());
2661 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2662 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2665 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2670 bool failed = false;
2671 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2672 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2675 std::string uri = (*item)->DescURI();
2676 std::string error = (*item)->ErrorText;
2678 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2681 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2682 withObject:[NSArray arrayWithObjects:
2683 [NSString stringWithUTF8String:error.c_str()],
2695 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2697 if (_error->PendingError()) {
2702 if (result == pkgPackageManager::Failed) {
2707 if (result != pkgPackageManager::Completed) {
2712 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2714 _assert(list.ReadMainList());
2715 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2716 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2719 if (![before isEqualToArray:after])
2724 _assert(pkgDistUpgrade(cache_));
2728 [self updateWithStatus:status_];
2731 - (void) updateWithStatus:(Status &)status {
2733 _assert(list.ReadMainList());
2736 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2737 _assert(!_error->PendingError());
2739 pkgAcquire fetcher(&status);
2740 _assert(list.GetIndexes(&fetcher));
2742 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2743 bool failed = false;
2744 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2745 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2746 (*item)->Finished();
2750 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2751 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2752 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2755 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2760 - (void) setDelegate:(id)delegate {
2761 delegate_ = delegate;
2762 status_.setDelegate(delegate);
2763 progress_.setDelegate(delegate);
2766 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2767 pkgIndexFile *index(NULL);
2768 list_->FindIndex(file, index);
2769 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2775 /* PopUp Windows {{{ */
2776 @interface PopUpView : UIView {
2777 _transient id delegate_;
2778 UITransitionView *transition_;
2783 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2787 @implementation PopUpView
2790 [transition_ setDelegate:nil];
2791 [transition_ release];
2797 [transition_ transition:UITransitionPushFromTop toView:nil];
2800 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2801 if (from != nil && to == nil)
2802 [self removeFromSuperview];
2805 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2806 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2807 delegate_ = delegate;
2809 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2810 [self addSubview:transition_];
2812 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2814 [view addSubview:self];
2816 [transition_ setDelegate:self];
2818 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2819 [transition_ transition:UITransitionNone toView:blank];
2820 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2827 /* Mail Composition {{{ */
2828 @interface MailToView : PopUpView {
2829 MailComposeController *controller_;
2832 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2836 @implementation MailToView
2839 [controller_ release];
2843 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2847 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2848 NSLog(@"did:%@", delivery);
2849 // [UIApp setStatusBarShowsProgress:NO];
2850 if ([controller error]){
2851 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2852 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2853 [mailAlertSheet setBodyText:[controller error]];
2854 [mailAlertSheet popupAlertAnimated:YES];
2858 - (void) showError {
2859 NSLog(@"%@", [controller_ error]);
2860 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2861 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2862 [mailAlertSheet setBodyText:[controller_ error]];
2863 [mailAlertSheet popupAlertAnimated:YES];
2866 - (void) deliverMessage { _pooled
2870 if (![controller_ deliverMessage])
2871 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2874 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2875 if ([controller_ needsDelivery])
2876 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2881 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2882 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2883 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2884 [controller_ setDelegate:self];
2885 [controller_ initializeUI];
2886 [controller_ setupForURL:url];
2888 UIView *view([controller_ view]);
2889 [overlay_ addSubview:view];
2895 /* Confirmation View {{{ */
2896 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2897 if (!iterator.end())
2898 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2899 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2901 pkgCache::PkgIterator package(dep.TargetPkg());
2904 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2911 @protocol ConfirmationViewDelegate
2916 @interface ConfirmationView : BrowserView {
2917 _transient Database *database_;
2918 UIActionSheet *essential_;
2925 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2929 @implementation ConfirmationView
2936 if (essential_ != nil)
2937 [essential_ release];
2943 [book_ popFromSuperviewAnimated:YES];
2946 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2947 NSString *context([sheet context]);
2949 if ([context isEqualToString:@"remove"]) {
2957 [delegate_ confirm];
2964 } else if ([context isEqualToString:@"unable"]) {
2968 [super alertSheet:sheet buttonClicked:button];
2971 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2972 [window setValue:changes_ forKey:@"changes"];
2973 [window setValue:issues_ forKey:@"issues"];
2974 [window setValue:sizes_ forKey:@"sizes"];
2975 [super webView:sender didClearWindowObject:window forFrame:frame];
2978 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2979 if ((self = [super initWithBook:book]) != nil) {
2980 database_ = database;
2982 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2983 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2984 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2985 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2986 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2990 pkgDepCache::Policy *policy([database_ policy]);
2992 pkgCacheFile &cache([database_ cache]);
2993 NSArray *packages = [database_ packages];
2994 for (size_t i(0), e = [packages count]; i != e; ++i) {
2995 Package *package = [packages objectAtIndex:i];
2996 pkgCache::PkgIterator iterator = [package iterator];
2997 pkgDepCache::StateCache &state(cache[iterator]);
2999 NSString *name([package name]);
3001 if (state.NewInstall())
3002 [installing addObject:name];
3003 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
3004 [reinstalling addObject:name];
3005 else if (state.Upgrade())
3006 [upgrading addObject:name];
3007 else if (state.Downgrade())
3008 [downgrading addObject:name];
3009 else if (state.Delete()) {
3010 if ([package essential])
3012 [removing addObject:name];
3015 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3016 substrate_ |= DepSubstrate(iterator.CurrentVer());
3021 else if (Advanced_ || true) {
3022 essential_ = [[UIActionSheet alloc]
3023 initWithTitle:@"Removing Essentials"
3024 buttons:[NSArray arrayWithObjects:
3025 @"Cancel Operation (Safe)",
3026 @"Force Removal (Unsafe)",
3028 defaultButtonIndex:0
3034 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3036 [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."];
3038 essential_ = [[UIActionSheet alloc]
3039 initWithTitle:@"Unable to Comply"
3040 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3041 defaultButtonIndex:0
3046 [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."];
3049 changes_ = [[NSArray alloc] initWithObjects:
3057 issues_ = [database_ issues];
3059 issues_ = [issues_ retain];
3061 sizes_ = [[NSArray alloc] initWithObjects:
3062 SizeString([database_ fetcher].FetchNeeded()),
3063 SizeString([database_ fetcher].PartialPresent()),
3064 SizeString([database_ cache]->UsrSize()),
3067 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3071 - (NSString *) backButtonTitle {
3075 - (NSString *) leftButtonTitle {
3079 - (id) _rightButtonTitle {
3080 #if AlwaysReload || IgnoreInstall
3083 return issues_ == nil ? @"Confirm" : nil;
3087 - (void) _leftButtonClicked {
3092 - (void) _rightButtonClicked {
3094 return [super _rightButtonClicked];
3096 if (essential_ != nil)
3097 [essential_ popupAlertAnimated:YES];
3101 [delegate_ confirm];
3109 /* Progress Data {{{ */
3110 @interface ProgressData : NSObject {
3116 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3123 @implementation ProgressData
3125 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3126 if ((self = [super init]) != nil) {
3127 selector_ = selector;
3147 /* Progress View {{{ */
3148 @interface ProgressView : UIView <
3149 ConfigurationDelegate,
3152 _transient Database *database_;
3154 UIView *background_;
3155 UITransitionView *transition_;
3157 UINavigationBar *navbar_;
3158 UIProgressBar *progress_;
3159 UITextView *output_;
3160 UITextLabel *status_;
3161 UIPushButton *close_;
3164 SHA1SumValue springlist_;
3165 SHA1SumValue notifyconf_;
3166 SHA1SumValue sandplate_;
3169 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3171 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3172 - (void) setContentView:(UIView *)view;
3175 - (void) _retachThread;
3176 - (void) _detachNewThreadData:(ProgressData *)data;
3177 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3183 @protocol ProgressViewDelegate
3184 - (void) progressViewIsComplete:(ProgressView *)sender;
3187 @implementation ProgressView
3190 [transition_ setDelegate:nil];
3191 [navbar_ setDelegate:nil];
3194 if (background_ != nil)
3195 [background_ release];
3196 [transition_ release];
3199 [progress_ release];
3206 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3207 if (bootstrap_ && from == overlay_ && to == view_)
3211 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3212 if ((self = [super initWithFrame:frame]) != nil) {
3213 database_ = database;
3214 delegate_ = delegate;
3216 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3217 [transition_ setDelegate:self];
3219 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3222 [overlay_ setBackgroundColor:[UIColor blackColor]];
3224 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3225 [background_ setBackgroundColor:[UIColor blackColor]];
3226 [self addSubview:background_];
3229 [self addSubview:transition_];
3231 CGSize navsize = [UINavigationBar defaultSize];
3232 CGRect navrect = {{0, 0}, navsize};
3234 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3235 [overlay_ addSubview:navbar_];
3237 [navbar_ setBarStyle:1];
3238 [navbar_ setDelegate:self];
3240 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3241 [navbar_ pushNavigationItem:navitem];
3243 CGRect bounds = [overlay_ bounds];
3244 CGSize prgsize = [UIProgressBar defaultSize];
3247 (bounds.size.width - prgsize.width) / 2,
3248 bounds.size.height - prgsize.height - 20
3251 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3252 [progress_ setStyle:0];
3254 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3256 bounds.size.height - prgsize.height - 50,
3257 bounds.size.width - 20,
3261 [status_ setColor:[UIColor whiteColor]];
3262 [status_ setBackgroundColor:[UIColor clearColor]];
3264 [status_ setCentersHorizontally:YES];
3265 //[status_ setFont:font];
3268 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3270 navrect.size.height + 20,
3271 bounds.size.width - 20,
3272 bounds.size.height - navsize.height - 62 - navrect.size.height
3276 //[output_ setTextFont:@"Courier New"];
3277 [output_ setTextSize:12];
3279 [output_ setTextColor:[UIColor whiteColor]];
3280 [output_ setBackgroundColor:[UIColor clearColor]];
3282 [output_ setMarginTop:0];
3283 [output_ setAllowsRubberBanding:YES];
3284 [output_ setEditable:NO];
3286 [overlay_ addSubview:output_];
3288 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3290 bounds.size.height - prgsize.height - 50,
3291 bounds.size.width - 20,
3295 [close_ setAutosizesToFit:NO];
3296 [close_ setDrawsShadow:YES];
3297 [close_ setStretchBackground:YES];
3298 [close_ setEnabled:YES];
3300 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3301 [close_ setTitleFont:bold];
3303 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3304 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3305 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3309 - (void) setContentView:(UIView *)view {
3310 view_ = [view retain];
3313 - (void) resetView {
3314 [transition_ transition:6 toView:view_];
3317 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3318 NSString *context([sheet context]);
3320 if ([context isEqualToString:@"error"])
3322 else if ([context isEqualToString:@"conffile"]) {
3323 FILE *input = [database_ input];
3327 fprintf(input, "N\n");
3331 fprintf(input, "Y\n");
3342 - (void) closeButtonPushed {
3351 [delegate_ suspendWithAnimation:YES];
3355 system("launchctl stop com.apple.SpringBoard");
3359 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3368 - (void) _retachThread {
3369 UINavigationItem *item = [navbar_ topItem];
3370 [item setTitle:@"Complete"];
3372 [overlay_ addSubview:close_];
3373 [progress_ removeFromSuperview];
3374 [status_ removeFromSuperview];
3376 [delegate_ progressViewIsComplete:self];
3379 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3380 MMap mmap(file, MMap::ReadOnly);
3382 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3383 if (!(sandplate_ == sha1.Result()))
3388 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3389 MMap mmap(file, MMap::ReadOnly);
3391 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3392 if (!(notifyconf_ == sha1.Result()))
3397 FileFd file(SpringBoard_, FileFd::ReadOnly);
3398 MMap mmap(file, MMap::ReadOnly);
3400 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3401 if (!(springlist_ == sha1.Result()))
3406 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3407 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3408 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3409 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3410 case 4: [close_ setTitle:@"Reboot Device"]; break;
3413 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3415 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3416 [cache autorelease];
3418 NSFileManager *manager = [NSFileManager defaultManager];
3419 NSError *error = nil;
3421 id system = [cache objectForKey:@"System"];
3426 if (stat(Cache_, &info) == -1)
3429 [system removeAllObjects];
3431 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3432 for (NSString *app in apps)
3433 if ([app hasSuffix:@".app"]) {
3434 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3435 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3436 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3438 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3439 [info setObject:path forKey:@"Path"];
3440 [info setObject:@"System" forKey:@"ApplicationType"];
3441 [system addInfoDictionary:info];
3447 [cache writeToFile:@Cache_ atomically:YES];
3449 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3451 if (chmod(Cache_, info.st_mode) == -1)
3455 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3458 notify_post("com.apple.mobile.application_installed");
3460 [delegate_ setStatusBarShowsProgress:NO];
3463 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3464 [[data target] performSelector:[data selector] withObject:[data object]];
3467 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3470 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3471 UINavigationItem *item = [navbar_ topItem];
3472 [item setTitle:title];
3474 [status_ setText:nil];
3475 [output_ setText:@""];
3476 [progress_ setProgress:0];
3478 [close_ removeFromSuperview];
3479 [overlay_ addSubview:progress_];
3480 [overlay_ addSubview:status_];
3482 [delegate_ setStatusBarShowsProgress:YES];
3486 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3487 MMap mmap(file, MMap::ReadOnly);
3489 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3490 sandplate_ = sha1.Result();
3494 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3495 MMap mmap(file, MMap::ReadOnly);
3497 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3498 notifyconf_ = sha1.Result();
3502 FileFd file(SpringBoard_, FileFd::ReadOnly);
3503 MMap mmap(file, MMap::ReadOnly);
3505 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3506 springlist_ = sha1.Result();
3509 [transition_ transition:6 toView:overlay_];
3512 detachNewThreadSelector:@selector(_detachNewThreadData:)
3514 withObject:[[ProgressData alloc]
3515 initWithSelector:selector
3522 - (void) repairWithSelector:(SEL)selector {
3524 detachNewThreadSelector:selector
3531 - (void) setConfigurationData:(NSString *)data {
3533 performSelectorOnMainThread:@selector(_setConfigurationData:)
3539 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3540 Package *package = id == nil ? nil : [database_ packageWithName:id];
3542 UIActionSheet *sheet = [[[UIActionSheet alloc]
3543 initWithTitle:(package == nil ? id : [package name])
3544 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3545 defaultButtonIndex:0
3550 [sheet setBodyText:error];
3551 [sheet popupAlertAnimated:YES];
3554 - (void) setProgressTitle:(NSString *)title {
3556 performSelectorOnMainThread:@selector(_setProgressTitle:)
3562 - (void) setProgressPercent:(float)percent {
3564 performSelectorOnMainThread:@selector(_setProgressPercent:)
3565 withObject:[NSNumber numberWithFloat:percent]
3570 - (void) startProgress {
3573 - (void) addProgressOutput:(NSString *)output {
3575 performSelectorOnMainThread:@selector(_addProgressOutput:)
3581 - (bool) isCancelling:(size_t)received {
3585 - (void) _setConfigurationData:(NSString *)data {
3586 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3588 _assert(conffile_r(data));
3590 NSString *ofile = conffile_r[1];
3591 //NSString *nfile = conffile_r[2];
3593 UIActionSheet *sheet = [[[UIActionSheet alloc]
3594 initWithTitle:@"Configuration Upgrade"
3595 buttons:[NSArray arrayWithObjects:
3596 @"Keep My Old Copy",
3597 @"Accept The New Copy",
3598 // XXX: @"See What Changed",
3600 defaultButtonIndex:0
3605 [sheet setBodyText:[NSString stringWithFormat:
3606 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3609 [sheet popupAlertAnimated:YES];
3612 - (void) _setProgressTitle:(NSString *)title {
3613 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3614 for (size_t i(0), e([words count]); i != e; ++i) {
3615 NSString *word([words objectAtIndex:i]);
3616 if (Package *package = [database_ packageWithName:word])
3617 [words replaceObjectAtIndex:i withObject:[package name]];
3620 [status_ setText:[words componentsJoinedByString:@" "]];
3623 - (void) _setProgressPercent:(NSNumber *)percent {
3624 [progress_ setProgress:[percent floatValue]];
3627 - (void) _addProgressOutput:(NSString *)output {
3628 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3629 CGSize size = [output_ contentSize];
3630 CGRect rect = {{0, size.height}, {size.width, 0}};
3631 [output_ scrollRectToVisible:rect animated:YES];
3634 - (BOOL) isRunning {
3641 /* Package Cell {{{ */
3642 @interface PackageCell : UISimpleTableCell {
3645 NSString *description_;
3649 UITextLabel *status_;
3653 - (PackageCell *) init;
3654 - (void) setPackage:(Package *)package;
3656 + (int) heightForPackage:(Package *)package;
3660 @implementation PackageCell
3662 - (void) clearPackage {
3673 if (description_ != nil) {
3674 [description_ release];
3678 if (source_ != nil) {
3683 if (badge_ != nil) {
3690 [self clearPackage];
3697 - (PackageCell *) init {
3698 if ((self = [super init]) != nil) {
3700 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3701 [status_ setBackgroundColor:[UIColor clearColor]];
3702 [status_ setFont:small];
3707 - (void) setPackage:(Package *)package {
3708 [self clearPackage];
3710 Source *source = [package source];
3711 NSString *section = [package simpleSection];
3713 icon_ = [[package icon] retain];
3715 name_ = [[package name] retain];
3716 description_ = [[package tagline] retain];
3718 NSString *label = nil;
3719 bool trusted = false;
3721 if (source != nil) {
3722 label = [source label];
3723 trusted = [source trusted];
3724 } else if ([[package id] isEqualToString:@"firmware"])
3727 label = @"Unknown/Local";
3729 NSString *from = [NSString stringWithFormat:@"from %@", label];
3731 if (section != nil && ![section isEqualToString:label])
3732 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3734 source_ = [from retain];
3736 if (NSString *purpose = [package primaryPurpose])
3737 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3738 badge_ = [badge_ retain];
3741 if (NSString *mode = [package mode]) {
3742 [badge_ setImage:[UIImage applicationImageNamed:
3743 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3746 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3747 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3748 } else if ([package half]) {
3749 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3750 [status_ setText:@"Package Damaged"];
3751 [status_ setColor:[UIColor redColor]];
3753 [badge_ setImage:nil];
3754 [status_ setText:nil];
3759 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3762 rect.size = [icon_ size];
3764 rect.size.width /= 2;
3765 rect.size.height /= 2;
3767 rect.origin.x = 25 - rect.size.width / 2;
3768 rect.origin.y = 25 - rect.size.height / 2;
3770 [icon_ drawInRect:rect];
3773 if (badge_ != nil) {
3774 CGSize size = [badge_ size];
3776 [badge_ drawAtPoint:CGPointMake(
3777 36 - size.width / 2,
3778 36 - size.height / 2
3787 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3788 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3792 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3794 [super drawContentInRect:rect selected:selected];
3797 + (int) heightForPackage:(Package *)package {
3798 NSString *tagline([package tagline]);
3799 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3801 if ([package hasMode] || [package half])
3810 /* Section Cell {{{ */
3811 @interface SectionCell : UISimpleTableCell {
3816 _UISwitchSlider *switch_;
3821 - (void) setSection:(Section *)section editing:(BOOL)editing;
3825 @implementation SectionCell
3827 - (void) clearSection {
3828 if (section_ != nil) {
3838 if (count_ != nil) {
3845 [self clearSection];
3852 if ((self = [super init]) != nil) {
3853 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3855 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3856 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3860 - (void) onSwitch:(id)sender {
3861 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3862 if (metadata == nil) {
3863 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3864 [Sections_ setObject:metadata forKey:section_];
3868 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3871 - (void) setSection:(Section *)section editing:(BOOL)editing {
3872 if (editing != editing_) {
3874 [switch_ removeFromSuperview];
3876 [self addSubview:switch_];
3880 [self clearSection];
3882 if (section == nil) {
3883 name_ = [@"All Packages" retain];
3886 section_ = [section name];
3887 if (section_ != nil)
3888 section_ = [section_ retain];
3889 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3890 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3893 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3897 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3898 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3905 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3907 CGSize size = [count_ sizeWithFont:Font14_];
3911 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3913 [super drawContentInRect:rect selected:selected];
3919 /* File Table {{{ */
3920 @interface FileTable : RVPage {
3921 _transient Database *database_;
3924 NSMutableArray *files_;
3928 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3929 - (void) setPackage:(Package *)package;
3933 @implementation FileTable
3936 if (package_ != nil)
3945 - (int) numberOfRowsInTable:(UITable *)table {
3946 return files_ == nil ? 0 : [files_ count];
3949 - (float) table:(UITable *)table heightForRow:(int)row {
3953 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3954 if (reusing == nil) {
3955 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3956 UIFont *font = [UIFont systemFontOfSize:16];
3957 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3959 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3963 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3967 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3968 if ((self = [super initWithBook:book]) != nil) {
3969 database_ = database;
3971 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3973 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3974 [self addSubview:list_];
3976 UITableColumn *column = [[[UITableColumn alloc]
3977 initWithTitle:@"Name"
3979 width:[self frame].size.width
3982 [list_ setDataSource:self];
3983 [list_ setSeparatorStyle:1];
3984 [list_ addTableColumn:column];
3985 [list_ setDelegate:self];
3986 [list_ setReusesTableCells:YES];
3990 - (void) setPackage:(Package *)package {
3991 if (package_ != nil) {
3992 [package_ autorelease];
4001 [files_ removeAllObjects];
4003 if (package != nil) {
4004 package_ = [package retain];
4005 name_ = [[package id] retain];
4007 if (NSArray *files = [package files])
4008 [files_ addObjectsFromArray:files];
4010 if ([files_ count] != 0) {
4011 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4012 [files_ removeObjectAtIndex:0];
4013 [files_ sortUsingSelector:@selector(compareByPath:)];
4015 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4016 [stack addObject:@"/"];
4018 for (int i(0), e([files_ count]); i != e; ++i) {
4019 NSString *file = [files_ objectAtIndex:i];
4020 while (![file hasPrefix:[stack lastObject]])
4021 [stack removeLastObject];
4022 NSString *directory = [stack lastObject];
4023 [stack addObject:[file stringByAppendingString:@"/"]];
4024 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4025 ([stack count] - 2) * 3, "",
4026 [file substringFromIndex:[directory length]]
4035 - (void) resetViewAnimated:(BOOL)animated {
4036 [list_ resetViewAnimated:animated];
4039 - (void) reloadData {
4040 [self setPackage:[database_ packageWithName:name_]];
4041 [self reloadButtons];
4044 - (NSString *) title {
4045 return @"Installed Files";
4048 - (NSString *) backButtonTitle {
4054 /* Package View {{{ */
4055 @interface PackageView : BrowserView {
4056 _transient Database *database_;
4059 NSMutableArray *buttons_;
4062 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4063 - (void) setPackage:(Package *)package;
4067 @implementation PackageView
4070 if (package_ != nil)
4078 - (void) _clickButtonWithName:(NSString *)name {
4079 if ([name isEqualToString:@"Install"])
4080 [delegate_ installPackage:package_];
4081 else if ([name isEqualToString:@"Reinstall"])
4082 [delegate_ installPackage:package_];
4083 else if ([name isEqualToString:@"Remove"])
4084 [delegate_ removePackage:package_];
4085 else if ([name isEqualToString:@"Upgrade"])
4086 [delegate_ installPackage:package_];
4087 else _assert(false);
4090 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4091 NSString *context([sheet context]);
4093 if ([context isEqualToString:@"modify"]) {
4094 int count = [buttons_ count];
4095 _assert(count != 0);
4096 _assert(button <= count + 1);
4098 if (count != button - 1)
4099 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4103 [super alertSheet:sheet buttonClicked:button];
4106 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4107 return [super webView:sender didFinishLoadForFrame:frame];
4110 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4111 [window setValue:package_ forKey:@"package"];
4112 [super webView:sender didClearWindowObject:window forFrame:frame];
4116 - (void) _rightButtonClicked {
4117 /*[super _rightButtonClicked];
4120 int count = [buttons_ count];
4121 _assert(count != 0);
4124 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4126 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4127 [buttons addObjectsFromArray:buttons_];
4128 [buttons addObject:@"Cancel"];
4130 [delegate_ slideUp:[[[UIActionSheet alloc]
4133 defaultButtonIndex:2
4141 - (id) _rightButtonTitle {
4142 int count = [buttons_ count];
4143 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4146 - (NSString *) backButtonTitle {
4150 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4151 if ((self = [super initWithBook:book]) != nil) {
4152 database_ = database;
4153 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4157 - (void) setPackage:(Package *)package {
4158 if (package_ != nil) {
4159 [package_ autorelease];
4168 [buttons_ removeAllObjects];
4170 if (package != nil) {
4171 package_ = [package retain];
4172 name_ = [[package id] retain];
4174 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4176 if ([package_ source] == nil);
4177 else if ([package_ upgradableAndEssential:NO])
4178 [buttons_ addObject:@"Upgrade"];
4179 else if ([package_ installed] == nil)
4180 [buttons_ addObject:@"Install"];
4182 [buttons_ addObject:@"Reinstall"];
4183 if ([package_ installed] != nil)
4184 [buttons_ addObject:@"Remove"];
4192 - (void) reloadData {
4193 [self setPackage:[database_ packageWithName:name_]];
4194 [self reloadButtons];
4199 /* Package Table {{{ */
4200 @interface PackageTable : RVPage {
4201 _transient Database *database_;
4203 NSMutableArray *packages_;
4204 NSMutableArray *sections_;
4205 UISectionList *list_;
4208 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4210 - (void) setDelegate:(id)delegate;
4212 - (void) reloadData;
4213 - (void) resetCursor;
4215 - (UISectionList *) list;
4217 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4221 @implementation PackageTable
4224 [list_ setDataSource:nil];
4227 [packages_ release];
4228 [sections_ release];
4233 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4234 return [sections_ count];
4237 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4238 return [[sections_ objectAtIndex:section] name];
4241 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4242 return [[sections_ objectAtIndex:section] row];
4245 - (int) numberOfRowsInTable:(UITable *)table {
4246 return [packages_ count];
4249 - (float) table:(UITable *)table heightForRow:(int)row {
4250 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4253 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4255 reusing = [[[PackageCell alloc] init] autorelease];
4256 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4260 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4264 - (void) tableRowSelected:(NSNotification *)notification {
4265 int row = [[notification object] selectedRow];
4269 Package *package = [packages_ objectAtIndex:row];
4270 package = [database_ packageWithName:[package id]];
4271 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4272 [view setPackage:package];
4273 [view setDelegate:delegate_];
4274 [book_ pushPage:view];
4277 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4278 if ((self = [super initWithBook:book]) != nil) {
4279 database_ = database;
4280 title_ = [title retain];
4282 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4283 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4285 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4286 [list_ setDataSource:self];
4288 UITableColumn *column = [[[UITableColumn alloc]
4289 initWithTitle:@"Name"
4291 width:[self frame].size.width
4294 UITable *table = [list_ table];
4295 [table setSeparatorStyle:1];
4296 [table addTableColumn:column];
4297 [table setDelegate:self];
4298 [table setReusesTableCells:YES];
4300 [self addSubview:list_];
4302 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4303 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4307 - (void) setDelegate:(id)delegate {
4308 delegate_ = delegate;
4311 - (bool) hasPackage:(Package *)package {
4315 - (void) reloadData {
4316 NSArray *packages = [database_ packages];
4318 [packages_ removeAllObjects];
4319 [sections_ removeAllObjects];
4321 _profile(PackageTable$reloadData$Filter)
4322 for (size_t i(0); i != [packages count]; ++i) {
4323 Package *package([packages objectAtIndex:i]);
4324 if ([self hasPackage:package])
4325 [packages_ addObject:package];
4329 Section *section = nil;
4331 _profile(PackageTable$reloadData$Section)
4332 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4333 Package *package = [packages_ objectAtIndex:offset];
4334 unichar index = [package index];
4336 if (section == nil || [section index] != index) {
4337 _profile(PackageTable$reloadData$Section$Allocate)
4338 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4341 [sections_ addObject:section];
4344 [section addToCount];
4351 - (NSString *) title {
4355 - (void) resetViewAnimated:(BOOL)animated {
4356 [list_ resetViewAnimated:animated];
4359 - (void) resetCursor {
4360 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4363 - (UISectionList *) list {
4367 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4368 [list_ setShouldHideHeaderInShortLists:hide];
4373 /* Filtered Package Table {{{ */
4374 @interface FilteredPackageTable : PackageTable {
4379 - (void) setObject:(id)object;
4381 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4385 @implementation FilteredPackageTable
4393 - (void) setObject:(id)object {
4399 object_ = [object retain];
4402 - (bool) hasPackage:(Package *)package {
4403 return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
4406 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4407 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4409 object_ = object == nil ? nil : [object retain];
4418 /* Add Source View {{{ */
4419 @interface AddSourceView : RVPage {
4420 _transient Database *database_;
4423 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4427 @implementation AddSourceView
4429 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4430 if ((self = [super initWithBook:book]) != nil) {
4431 database_ = database;
4437 /* Source Cell {{{ */
4438 @interface SourceCell : UITableCell {
4441 NSString *description_;
4447 - (SourceCell *) initWithSource:(Source *)source;
4451 @implementation SourceCell
4456 [description_ release];
4461 - (SourceCell *) initWithSource:(Source *)source {
4462 if ((self = [super init]) != nil) {
4464 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4466 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4467 icon_ = [icon_ retain];
4469 origin_ = [[source name] retain];
4470 label_ = [[source uri] retain];
4471 description_ = [[source description] retain];
4475 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4477 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4484 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4488 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4492 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4494 [super drawContentInRect:rect selected:selected];
4499 /* Source Table {{{ */
4500 @interface SourceTable : RVPage {
4501 _transient Database *database_;
4502 UISectionList *list_;
4503 NSMutableArray *sources_;
4504 UIActionSheet *alert_;
4508 UIProgressHUD *hud_;
4511 //NSURLConnection *installer_;
4512 NSURLConnection *trivial_bz2_;
4513 NSURLConnection *trivial_gz_;
4514 //NSURLConnection *automatic_;
4519 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4523 @implementation SourceTable
4525 - (void) _deallocConnection:(NSURLConnection *)connection {
4526 if (connection != nil) {
4527 [connection cancel];
4528 //[connection setDelegate:nil];
4529 [connection release];
4534 [[list_ table] setDelegate:nil];
4535 [list_ setDataSource:nil];
4544 //[self _deallocConnection:installer_];
4545 [self _deallocConnection:trivial_gz_];
4546 [self _deallocConnection:trivial_bz2_];
4547 //[self _deallocConnection:automatic_];
4554 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4555 return offset_ == 0 ? 1 : 2;
4558 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4559 switch (section + (offset_ == 0 ? 1 : 0)) {
4560 case 0: return @"Entered by User";
4561 case 1: return @"Installed by Packages";
4569 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4570 switch (section + (offset_ == 0 ? 1 : 0)) {
4572 case 1: return offset_;
4580 - (int) numberOfRowsInTable:(UITable *)table {
4581 return [sources_ count];
4584 - (float) table:(UITable *)table heightForRow:(int)row {
4585 Source *source = [sources_ objectAtIndex:row];
4586 return [source description] == nil ? 56 : 73;
4589 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4590 Source *source = [sources_ objectAtIndex:row];
4591 // XXX: weird warning, stupid selectors ;P
4592 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4595 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4599 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4603 - (void) tableRowSelected:(NSNotification*)notification {
4604 UITable *table([list_ table]);
4605 int row([table selectedRow]);
4609 Source *source = [sources_ objectAtIndex:row];
4611 PackageTable *packages = [[[FilteredPackageTable alloc]
4614 title:[source label]
4615 filter:@selector(isVisibleInSource:)
4619 [packages setDelegate:delegate_];
4621 [book_ pushPage:packages];
4624 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4625 Source *source = [sources_ objectAtIndex:row];
4626 return [source record] != nil;
4629 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4630 [[list_ table] setDeleteConfirmationRow:row];
4633 - (void) table:(UITable *)table deleteRow:(int)row {
4634 Source *source = [sources_ objectAtIndex:row];
4635 [Sources_ removeObjectForKey:[source key]];
4636 [delegate_ syncData];
4639 - (void) _endConnection:(NSURLConnection *)connection {
4640 NSURLConnection **field = NULL;
4641 if (connection == trivial_bz2_)
4642 field = &trivial_bz2_;
4643 else if (connection == trivial_gz_)
4644 field = &trivial_gz_;
4645 _assert(field != NULL);
4646 [connection release];
4650 trivial_bz2_ == nil &&
4653 [delegate_ setStatusBarShowsProgress:NO];
4654 [delegate_ removeProgressHUD:hud_];
4660 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4663 @"./", @"Distribution",
4664 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4666 [delegate_ syncData];
4667 } else if (error_ != nil) {
4668 UIActionSheet *sheet = [[[UIActionSheet alloc]
4669 initWithTitle:@"Verification Error"
4670 buttons:[NSArray arrayWithObjects:@"OK", nil]
4671 defaultButtonIndex:0
4676 [sheet setBodyText:[error_ localizedDescription]];
4677 [sheet popupAlertAnimated:YES];
4679 UIActionSheet *sheet = [[[UIActionSheet alloc]
4680 initWithTitle:@"Did not Find Repository"
4681 buttons:[NSArray arrayWithObjects:@"OK", nil]
4682 defaultButtonIndex:0
4687 [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."];
4688 [sheet popupAlertAnimated:YES];
4694 if (error_ != nil) {
4701 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4702 switch ([response statusCode]) {
4708 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4709 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4711 error_ = [error retain];
4712 [self _endConnection:connection];
4715 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4716 [self _endConnection:connection];
4719 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4720 NSMutableURLRequest *request = [NSMutableURLRequest
4721 requestWithURL:[NSURL URLWithString:href]
4722 cachePolicy:NSURLRequestUseProtocolCachePolicy
4723 timeoutInterval:20.0
4726 [request setHTTPMethod:method];
4728 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4731 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4732 NSString *context([sheet context]);
4734 if ([context isEqualToString:@"source"]) {
4737 NSString *href = [[sheet textField] text];
4739 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4741 if (![href hasSuffix:@"/"])
4742 href_ = [href stringByAppendingString:@"/"];
4745 href_ = [href_ retain];
4747 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4748 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4749 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4753 hud_ = [[delegate_ addProgressHUD] retain];
4754 [hud_ setText:@"Verifying URL"];
4765 } else if ([context isEqualToString:@"trivial"])
4767 else if ([context isEqualToString:@"urlerror"])
4771 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4772 if ((self = [super initWithBook:book]) != nil) {
4773 database_ = database;
4774 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4776 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4777 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4778 [list_ setShouldHideHeaderInShortLists:NO];
4780 [self addSubview:list_];
4781 [list_ setDataSource:self];
4783 UITableColumn *column = [[UITableColumn alloc]
4784 initWithTitle:@"Name"
4786 width:[self frame].size.width
4789 UITable *table = [list_ table];
4790 [table setSeparatorStyle:1];
4791 [table addTableColumn:column];
4792 [table setDelegate:self];
4796 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4797 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4801 - (void) reloadData {
4803 _assert(list.ReadMainList());
4805 [sources_ removeAllObjects];
4806 [sources_ addObjectsFromArray:[database_ sources]];
4808 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4811 int count = [sources_ count];
4812 for (offset_ = 0; offset_ != count; ++offset_) {
4813 Source *source = [sources_ objectAtIndex:offset_];
4814 if ([source record] == nil)
4821 - (void) resetViewAnimated:(BOOL)animated {
4822 [list_ resetViewAnimated:animated];
4825 - (void) _leftButtonClicked {
4826 /*[book_ pushPage:[[[AddSourceView alloc]
4831 UIActionSheet *sheet = [[[UIActionSheet alloc]
4832 initWithTitle:@"Enter Cydia/APT URL"
4833 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4834 defaultButtonIndex:0
4839 [sheet setNumberOfRows:1];
4841 [sheet addTextFieldWithValue:@"http://" label:@""];
4843 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4844 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4845 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4846 [traits setKeyboardType:UIKeyboardTypeURL];
4847 // XXX: UIReturnKeyDone
4848 [traits setReturnKeyType:UIReturnKeyNext];
4850 [sheet popupAlertAnimated:YES];
4853 - (void) _rightButtonClicked {
4854 UITable *table = [list_ table];
4855 BOOL editing = [table isRowDeletionEnabled];
4856 [table enableRowDeletion:!editing animated:YES];
4857 [book_ reloadButtonsForPage:self];
4860 - (NSString *) title {
4864 - (NSString *) leftButtonTitle {
4865 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4868 - (id) rightButtonTitle {
4869 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4872 - (UINavigationButtonStyle) rightButtonStyle {
4873 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4879 /* Installed View {{{ */
4880 @interface InstalledView : RVPage {
4881 _transient Database *database_;
4882 FilteredPackageTable *packages_;
4886 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4890 @implementation InstalledView
4893 [packages_ release];
4897 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4898 if ((self = [super initWithBook:book]) != nil) {
4899 database_ = database;
4901 packages_ = [[FilteredPackageTable alloc]
4905 filter:@selector(isInstalledAndVisible:)
4906 with:[NSNumber numberWithBool:YES]
4909 [self addSubview:packages_];
4911 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4912 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4916 - (void) resetViewAnimated:(BOOL)animated {
4917 [packages_ resetViewAnimated:animated];
4920 - (void) reloadData {
4921 [packages_ reloadData];
4924 - (void) _rightButtonClicked {
4925 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4926 [packages_ reloadData];
4928 [book_ reloadButtonsForPage:self];
4931 - (NSString *) title {
4932 return @"Installed";
4935 - (NSString *) backButtonTitle {
4939 - (id) rightButtonTitle {
4940 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4943 - (UINavigationButtonStyle) rightButtonStyle {
4944 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4947 - (void) setDelegate:(id)delegate {
4948 [super setDelegate:delegate];
4949 [packages_ setDelegate:delegate];
4956 @interface HomeView : BrowserView {
4961 @implementation HomeView
4963 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4964 NSString *context([sheet context]);
4966 if ([context isEqualToString:@"about"])
4969 [super alertSheet:sheet buttonClicked:button];
4972 - (void) _leftButtonClicked {
4973 UIActionSheet *sheet = [[[UIActionSheet alloc]
4974 initWithTitle:@"About Cydia Installer"
4975 buttons:[NSArray arrayWithObjects:@"Close", nil]
4976 defaultButtonIndex:0
4982 @"Copyright (C) 2008\n"
4983 "Jay Freeman (saurik)\n"
4984 "saurik@saurik.com\n"
4985 "http://www.saurik.com/\n"
4988 "http://www.theokorigroup.com/\n"
4990 "College of Creative Studies,\n"
4991 "University of California,\n"
4993 "http://www.ccs.ucsb.edu/"
4996 [sheet popupAlertAnimated:YES];
4999 - (NSString *) leftButtonTitle {
5005 /* Manage View {{{ */
5006 @interface ManageView : BrowserView {
5011 @implementation ManageView
5013 - (NSString *) title {
5017 - (void) _leftButtonClicked {
5018 [delegate_ askForSettings];
5021 - (NSString *) leftButtonTitle {
5026 - (id) _rightButtonTitle {
5038 #include <BrowserView.m>
5040 /* Cydia Book {{{ */
5041 @interface CYBook : RVBook <
5044 _transient Database *database_;
5045 UINavigationBar *overlay_;
5046 UINavigationBar *underlay_;
5047 UIProgressIndicator *indicator_;
5048 UITextLabel *prompt_;
5049 UIProgressBar *progress_;
5050 UINavigationButton *cancel_;
5054 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5060 @implementation CYBook
5064 [indicator_ release];
5066 [progress_ release];
5071 - (NSString *) getTitleForPage:(RVPage *)page {
5072 return Simplify([super getTitleForPage:page]);
5080 [UIView beginAnimations:nil context:NULL];
5082 CGRect ovrframe = [overlay_ frame];
5083 ovrframe.origin.y = 0;
5084 [overlay_ setFrame:ovrframe];
5086 CGRect barframe = [navbar_ frame];
5087 barframe.origin.y += ovrframe.size.height;
5088 [navbar_ setFrame:barframe];
5090 CGRect trnframe = [transition_ frame];
5091 trnframe.origin.y += ovrframe.size.height;
5092 trnframe.size.height -= ovrframe.size.height;
5093 [transition_ setFrame:trnframe];
5095 [UIView endAnimations];
5097 [indicator_ startAnimation];
5098 [prompt_ setText:@"Updating Database"];
5099 [progress_ setProgress:0];
5102 [overlay_ addSubview:cancel_];
5105 detachNewThreadSelector:@selector(_update)
5114 [indicator_ stopAnimation];
5116 [UIView beginAnimations:nil context:NULL];
5118 CGRect ovrframe = [overlay_ frame];
5119 ovrframe.origin.y = -ovrframe.size.height;
5120 [overlay_ setFrame:ovrframe];
5122 CGRect barframe = [navbar_ frame];
5123 barframe.origin.y -= ovrframe.size.height;
5124 [navbar_ setFrame:barframe];
5126 CGRect trnframe = [transition_ frame];
5127 trnframe.origin.y -= ovrframe.size.height;
5128 trnframe.size.height += ovrframe.size.height;
5129 [transition_ setFrame:trnframe];
5131 [UIView commitAnimations];
5133 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5136 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5137 if ((self = [super initWithFrame:frame]) != nil) {
5138 database_ = database;
5140 CGRect ovrrect = [navbar_ bounds];
5141 ovrrect.size.height = [UINavigationBar defaultSize].height;
5142 ovrrect.origin.y = -ovrrect.size.height;
5144 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5145 [self addSubview:overlay_];
5147 ovrrect.origin.y = frame.size.height;
5148 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5149 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5150 [self addSubview:underlay_];
5152 [overlay_ setBarStyle:1];
5153 [underlay_ setBarStyle:1];
5155 int barstyle = [overlay_ _barStyle:NO];
5156 bool ugly = barstyle == 0;
5158 UIProgressIndicatorStyle style = ugly ?
5159 UIProgressIndicatorStyleMediumBrown :
5160 UIProgressIndicatorStyleMediumWhite;
5162 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5163 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5164 CGRect indrect = {{indoffset, indoffset}, indsize};
5166 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5167 [indicator_ setStyle:style];
5168 [overlay_ addSubview:indicator_];
5170 CGSize prmsize = {215, indsize.height + 4};
5173 indoffset * 2 + indsize.width,
5177 unsigned(ovrrect.size.height - prmsize.height) / 2
5180 UIFont *font = [UIFont systemFontOfSize:15];
5182 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5184 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5185 [prompt_ setBackgroundColor:[UIColor clearColor]];
5186 [prompt_ setFont:font];
5188 [overlay_ addSubview:prompt_];
5190 CGSize prgsize = {75, 100};
5193 ovrrect.size.width - prgsize.width - 10,
5194 (ovrrect.size.height - prgsize.height) / 2
5197 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5198 [progress_ setStyle:0];
5199 [overlay_ addSubview:progress_];
5201 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5202 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5204 CGRect frame = [cancel_ frame];
5205 frame.size.width = 65;
5206 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5207 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5208 [cancel_ setFrame:frame];
5210 [cancel_ setBarStyle:barstyle];
5214 - (void) _onCancel {
5216 [cancel_ removeFromSuperview];
5219 - (void) _update { _pooled
5221 status.setDelegate(self);
5223 [database_ updateWithStatus:status];
5226 performSelectorOnMainThread:@selector(_update_)
5232 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5233 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5236 - (void) setProgressTitle:(NSString *)title {
5238 performSelectorOnMainThread:@selector(_setProgressTitle:)
5244 - (void) setProgressPercent:(float)percent {
5246 performSelectorOnMainThread:@selector(_setProgressPercent:)
5247 withObject:[NSNumber numberWithFloat:percent]
5252 - (void) startProgress {
5255 - (void) addProgressOutput:(NSString *)output {
5257 performSelectorOnMainThread:@selector(_addProgressOutput:)
5263 - (bool) isCancelling:(size_t)received {
5267 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5271 - (void) _setProgressTitle:(NSString *)title {
5272 [prompt_ setText:title];
5275 - (void) _setProgressPercent:(NSNumber *)percent {
5276 [progress_ setProgress:[percent floatValue]];
5279 - (void) _addProgressOutput:(NSString *)output {
5284 /* Cydia:// Protocol {{{ */
5285 @interface CydiaURLProtocol : NSURLProtocol {
5290 @implementation CydiaURLProtocol
5292 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5293 NSURL *url([request URL]);
5296 NSString *scheme([[url scheme] lowercaseString]);
5297 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5302 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5306 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5307 id<NSURLProtocolClient> client([self client]);
5309 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5311 NSData *data(UIImagePNGRepresentation(icon));
5313 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5314 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5315 [client URLProtocol:self didLoadData:data];
5316 [client URLProtocolDidFinishLoading:self];
5320 - (void) startLoading {
5321 id<NSURLProtocolClient> client([self client]);
5322 NSURLRequest *request([self request]);
5324 NSURL *url([request URL]);
5325 NSString *href([url absoluteString]);
5327 NSString *path([href substringFromIndex:8]);
5328 NSRange slash([path rangeOfString:@"/"]);
5331 if (slash.location == NSNotFound) {
5335 command = [path substringToIndex:slash.location];
5336 path = [path substringFromIndex:(slash.location + 1)];
5339 Database *database([Database sharedInstance]);
5341 if ([command isEqualToString:@"package-icon"]) {
5344 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5345 Package *package([database packageWithName:path]);
5348 UIImage *icon([package icon]);
5349 [self _returnPNGWithImage:icon forRequest:request];
5350 } else if ([command isEqualToString:@"source-icon"]) {
5353 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5354 NSString *source(Simplify(path));
5355 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5357 icon = [UIImage applicationImageNamed:@"unknown.png"];
5358 [self _returnPNGWithImage:icon forRequest:request];
5359 } else if ([command isEqualToString:@"uikit-image"]) {
5362 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5363 UIImage *icon(_UIImageWithName(path));
5364 [self _returnPNGWithImage:icon forRequest:request];
5365 } else if ([command isEqualToString:@"section-icon"]) {
5368 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5369 NSString *section(Simplify(path));
5370 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5372 icon = [UIImage applicationImageNamed:@"unknown.png"];
5373 [self _returnPNGWithImage:icon forRequest:request];
5375 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5379 - (void) stopLoading {
5385 /* Sections View {{{ */
5386 @interface SectionsView : RVPage {
5387 _transient Database *database_;
5388 NSMutableArray *sections_;
5389 NSMutableArray *filtered_;
5390 UITransitionView *transition_;
5396 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5397 - (void) reloadData;
5402 @implementation SectionsView
5405 [list_ setDataSource:nil];
5406 [list_ setDelegate:nil];
5408 [sections_ release];
5409 [filtered_ release];
5410 [transition_ release];
5412 [accessory_ release];
5416 - (int) numberOfRowsInTable:(UITable *)table {
5417 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5420 - (float) table:(UITable *)table heightForRow:(int)row {
5424 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5426 reusing = [[[SectionCell alloc] init] autorelease];
5427 [(SectionCell *)reusing setSection:(editing_ ?
5428 [sections_ objectAtIndex:row] :
5429 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5430 ) editing:editing_];
5434 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5438 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5442 - (void) tableRowSelected:(NSNotification *)notification {
5443 int row = [[notification object] selectedRow];
5454 title = @"All Packages";
5456 section = [filtered_ objectAtIndex:(row - 1)];
5457 name = [section name];
5463 title = @"(No Section)";
5467 PackageTable *table = [[[FilteredPackageTable alloc]
5471 filter:@selector(isVisiblyUninstalledInSection:)
5475 [table setDelegate:delegate_];
5477 [book_ pushPage:table];
5480 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5481 if ((self = [super initWithBook:book]) != nil) {
5482 database_ = database;
5484 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5485 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5487 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5488 [self addSubview:transition_];
5490 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5491 [transition_ transition:0 toView:list_];
5493 UITableColumn *column = [[[UITableColumn alloc]
5494 initWithTitle:@"Name"
5496 width:[self frame].size.width
5499 [list_ setDataSource:self];
5500 [list_ setSeparatorStyle:1];
5501 [list_ addTableColumn:column];
5502 [list_ setDelegate:self];
5503 [list_ setReusesTableCells:YES];
5507 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5508 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5512 - (void) reloadData {
5513 NSArray *packages = [database_ packages];
5515 [sections_ removeAllObjects];
5516 [filtered_ removeAllObjects];
5518 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5519 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5522 for (size_t i(0); i != [packages count]; ++i) {
5523 Package *package([packages objectAtIndex:i]);
5524 NSString *name([package section]);
5527 Section *section([sections objectForKey:name]);
5528 if (section == nil) {
5529 section = [[[Section alloc] initWithName:name] autorelease];
5530 [sections setObject:section forKey:name];
5534 if ([package valid] && [package installed] == nil && [package visible])
5535 [filtered addObject:package];
5539 [sections_ addObjectsFromArray:[sections allValues]];
5540 [sections_ sortUsingSelector:@selector(compareByName:)];
5543 [filtered sortUsingSelector:@selector(compareBySection:)];
5546 Section *section = nil;
5547 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5548 Package *package = [filtered objectAtIndex:offset];
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 (size_t i(0); i != [packages count]; ++i) {
5724 Package *package([packages objectAtIndex:i]);
5727 [package installed] == nil && [package valid] && [package visible] ||
5728 [package upgradableAndEssential:YES]
5730 [packages_ addObject:package];
5734 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5737 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5738 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5739 Section *section = nil;
5743 bool unseens = false;
5745 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5748 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5749 Package *package = [packages_ objectAtIndex:offset];
5751 if (![package upgradableAndEssential:YES]) {
5753 NSDate *seen = [package seen];
5755 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5758 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5759 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5760 [sections_ addObject:section];
5764 [section addToCount];
5765 } else if ([package ignored])
5766 [ignored addToCount];
5769 [upgradable addToCount];
5774 CFRelease(formatter);
5777 Section *last = [sections_ lastObject];
5778 size_t count = [last count];
5779 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5780 [sections_ removeLastObject];
5783 if ([ignored count] != 0)
5784 [sections_ insertObject:ignored atIndex:0];
5786 [sections_ insertObject:upgradable atIndex:0];
5789 [self reloadButtons];
5792 - (void) resetViewAnimated:(BOOL)animated {
5793 [list_ resetViewAnimated:animated];
5796 - (NSString *) leftButtonTitle {
5797 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5800 - (id) rightButtonTitle {
5801 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5804 - (NSString *) title {
5810 /* Search View {{{ */
5811 @protocol SearchViewDelegate
5812 - (void) showKeyboard:(BOOL)show;
5815 @interface SearchView : RVPage {
5817 UISearchField *field_;
5818 UITransitionView *transition_;
5819 FilteredPackageTable *table_;
5820 UIPreferencesTable *advanced_;
5826 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5827 - (void) reloadData;
5831 @implementation SearchView
5834 [field_ setDelegate:nil];
5836 [accessory_ release];
5838 [transition_ release];
5840 [advanced_ release];
5845 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5849 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5851 case 0: return @"Advanced Search (Coming Soon!)";
5853 default: _assert(false);
5857 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5861 default: _assert(false);
5865 - (void) _showKeyboard:(BOOL)show {
5866 CGSize keysize = [UIKeyboard defaultSize];
5867 CGRect keydown = [book_ pageBounds];
5868 CGRect keyup = keydown;
5869 keyup.size.height -= keysize.height - ButtonBarHeight_;
5871 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5873 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5874 [animation setSignificantRectFields:8];
5877 [animation setStartFrame:keydown];
5878 [animation setEndFrame:keyup];
5880 [animation setStartFrame:keyup];
5881 [animation setEndFrame:keydown];
5884 UIAnimator *animator = [UIAnimator sharedAnimator];
5887 addAnimations:[NSArray arrayWithObjects:animation, nil]
5888 withDuration:(KeyboardTime_ - delay)
5893 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5895 [delegate_ showKeyboard:show];
5898 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5899 [self _showKeyboard:YES];
5902 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5903 [self _showKeyboard:NO];
5906 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5908 NSString *text([field_ text]);
5909 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5915 - (void) textFieldClearButtonPressed:(UITextField *)field {
5919 - (void) keyboardInputShouldDelete:(id)input {
5923 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5924 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5928 [field_ resignFirstResponder];
5933 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5934 if ((self = [super initWithBook:book]) != nil) {
5935 CGRect pageBounds = [book_ pageBounds];
5937 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5938 [self addSubview:transition_];
5940 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5942 [advanced_ setReusesTableCells:YES];
5943 [advanced_ setDataSource:self];
5944 [advanced_ reloadData];
5946 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5947 CGColor dimmed(space_, 0, 0, 0, 0.5);
5948 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5950 table_ = [[FilteredPackageTable alloc]
5954 filter:@selector(isUnfilteredAndSearchedForBy:)
5958 [table_ setShouldHideHeaderInShortLists:NO];
5959 [transition_ transition:0 toView:table_];
5968 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5975 [self bounds].size.width - area.origin.x - 18;
5977 area.size.height = [UISearchField defaultHeight];
5979 field_ = [[UISearchField alloc] initWithFrame:area];
5981 UIFont *font = [UIFont systemFontOfSize:16];
5982 [field_ setFont:font];
5984 [field_ setPlaceholder:@"Package Names & Descriptions"];
5985 [field_ setDelegate:self];
5987 [field_ setPaddingTop:5];
5989 UITextInputTraits *traits([field_ textInputTraits]);
5990 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5991 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5992 [traits setReturnKeyType:UIReturnKeySearch];
5994 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5996 accessory_ = [[UIView alloc] initWithFrame:accrect];
5997 [accessory_ addSubview:field_];
5999 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6000 [configure setShowPressFeedback:YES];
6001 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6002 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6003 [accessory_ addSubview:configure];*/
6005 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6006 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6012 LKAnimation *animation = [LKTransition animation];
6013 [animation setType:@"oglFlip"];
6014 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6015 [animation setFillMode:@"extended"];
6016 [animation setTransitionFlags:3];
6017 [animation setDuration:10];
6018 [animation setSpeed:0.35];
6019 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6020 [[transition_ _layer] addAnimation:animation forKey:0];
6021 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6022 flipped_ = !flipped_;
6026 - (void) configurePushed {
6027 [field_ resignFirstResponder];
6031 - (void) resetViewAnimated:(BOOL)animated {
6034 [table_ resetViewAnimated:animated];
6037 - (void) _reloadData {
6040 - (void) reloadData {
6043 [table_ setObject:[field_ text]];
6044 _profile(SearchView$reloadData)
6045 [table_ reloadData];
6048 [table_ resetCursor];
6051 - (UIView *) accessoryView {
6055 - (NSString *) title {
6059 - (NSString *) backButtonTitle {
6063 - (void) setDelegate:(id)delegate {
6064 [table_ setDelegate:delegate];
6065 [super setDelegate:delegate];
6071 @interface SettingsView : RVPage {
6072 _transient Database *database_;
6075 UIPreferencesTable *table_;
6076 _UISwitchSlider *subscribedSwitch_;
6077 _UISwitchSlider *ignoredSwitch_;
6078 UIPreferencesControlTableCell *subscribedCell_;
6079 UIPreferencesControlTableCell *ignoredCell_;
6082 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6086 @implementation SettingsView
6089 [table_ setDataSource:nil];
6092 if (package_ != nil)
6095 [subscribedSwitch_ release];
6096 [ignoredSwitch_ release];
6097 [subscribedCell_ release];
6098 [ignoredCell_ release];
6102 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6103 if (package_ == nil)
6109 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6110 if (package_ == nil)
6117 default: _assert(false);
6123 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6124 if (package_ == nil)
6131 default: _assert(false);
6137 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6138 if (package_ == nil)
6145 default: _assert(false);
6151 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6152 if (package_ == nil)
6155 _UISwitchSlider *slider([cell control]);
6156 BOOL value([slider value] != 0);
6157 NSMutableDictionary *metadata([package_ metadata]);
6160 if (NSNumber *number = [metadata objectForKey:key])
6161 before = [number boolValue];
6165 if (value != before) {
6166 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6168 [delegate_ updateData];
6172 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6173 [self onSomething:cell withKey:@"IsSubscribed"];
6176 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6177 [self onSomething:cell withKey:@"IsIgnored"];
6180 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6181 if (package_ == nil)
6185 case 0: switch (row) {
6187 return subscribedCell_;
6189 return ignoredCell_;
6190 default: _assert(false);
6193 case 1: switch (row) {
6195 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6196 [cell setShowSelection:NO];
6197 [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."];
6201 default: _assert(false);
6204 default: _assert(false);
6210 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6211 if ((self = [super initWithBook:book])) {
6212 database_ = database;
6213 name_ = [package retain];
6215 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6216 [self addSubview:table_];
6218 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6219 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6221 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6222 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6224 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6225 [subscribedCell_ setShowSelection:NO];
6226 [subscribedCell_ setTitle:@"Show All Changes"];
6227 [subscribedCell_ setControl:subscribedSwitch_];
6229 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6230 [ignoredCell_ setShowSelection:NO];
6231 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6232 [ignoredCell_ setControl:ignoredSwitch_];
6234 [table_ setDataSource:self];
6239 - (void) resetViewAnimated:(BOOL)animated {
6240 [table_ resetViewAnimated:animated];
6243 - (void) reloadData {
6244 if (package_ != nil)
6245 [package_ autorelease];
6246 package_ = [database_ packageWithName:name_];
6247 if (package_ != nil) {
6249 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6250 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6253 [table_ reloadData];
6256 - (NSString *) title {
6262 /* Signature View {{{ */
6263 @interface SignatureView : BrowserView {
6264 _transient Database *database_;
6268 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6272 @implementation SignatureView
6279 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6281 [super webView:sender didClearWindowObject:window forFrame:frame];
6284 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6285 if ((self = [super initWithBook:book]) != nil) {
6286 database_ = database;
6287 package_ = [package retain];
6292 - (void) resetViewAnimated:(BOOL)animated {
6295 - (void) reloadData {
6296 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6302 @interface Cydia : UIApplication <
6303 ConfirmationViewDelegate,
6304 ProgressViewDelegate,
6313 UIToolbar *buttonbar_;
6317 NSMutableArray *essential_;
6318 NSMutableArray *broken_;
6320 Database *database_;
6321 ProgressView *progress_;
6325 UIKeyboard *keyboard_;
6326 UIProgressHUD *hud_;
6328 SectionsView *sections_;
6329 ChangesView *changes_;
6330 ManageView *manage_;
6331 SearchView *search_;
6336 @implementation Cydia
6339 if ([broken_ count] != 0) {
6340 int count = [broken_ count];
6342 UIActionSheet *sheet = [[[UIActionSheet alloc]
6343 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6344 buttons:[NSArray arrayWithObjects:
6346 @"Ignore (Temporary)",
6348 defaultButtonIndex:0
6353 [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."];
6354 [sheet popupAlertAnimated:YES];
6355 } else if (!Ignored_ && [essential_ count] != 0) {
6356 int count = [essential_ count];
6358 UIActionSheet *sheet = [[[UIActionSheet alloc]
6359 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6360 buttons:[NSArray arrayWithObjects:
6361 @"Upgrade Essential",
6362 @"Complete Upgrade",
6363 @"Ignore (Temporary)",
6365 defaultButtonIndex:0
6370 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6371 [sheet popupAlertAnimated:YES];
6375 - (void) _reloadData {
6378 UIProgressHUD *hud([self addProgressHUD]);
6379 [hud setText:@"Reloading Data"];
6381 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6384 [self removeProgressHUD:hud];
6388 [essential_ removeAllObjects];
6389 [broken_ removeAllObjects];
6391 NSArray *packages = [database_ packages];
6392 for (Package *package in packages) {
6394 [broken_ addObject:package];
6395 if ([package upgradableAndEssential:NO]) {
6396 if ([package essential])
6397 [essential_ addObject:package];
6403 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6404 [buttonbar_ setBadgeValue:badge forButton:3];
6405 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6406 [buttonbar_ setBadgeAnimated:YES forButton:3];
6407 [self setApplicationBadge:badge];
6409 [buttonbar_ setBadgeValue:nil forButton:3];
6410 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6411 [buttonbar_ setBadgeAnimated:NO forButton:3];
6412 [self removeApplicationBadge];
6417 // XXX: what is this line of code for?
6418 if ([packages count] == 0);
6419 else if (Loaded_) loaded:
6424 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6425 NSTimeInterval interval([update timeIntervalSinceNow]);
6426 if (interval <= 0 && interval > -600)
6434 - (void) _saveConfig {
6437 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6443 - (void) updateData {
6446 /* XXX: this is just stupid */
6447 if (tag_ != 2 && sections_ != nil)
6448 [sections_ reloadData];
6449 if (tag_ != 3 && changes_ != nil)
6450 [changes_ reloadData];
6451 if (tag_ != 5 && search_ != nil)
6452 [search_ reloadData];
6462 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6463 _assert(file != NULL);
6465 NSArray *keys = [Sources_ allKeys];
6467 for (int i(0), e([keys count]); i != e; ++i) {
6468 NSString *key = [keys objectAtIndex:i];
6469 NSDictionary *source = [Sources_ objectForKey:key];
6471 fprintf(file, "%s %s %s\n",
6472 [[source objectForKey:@"Type"] UTF8String],
6473 [[source objectForKey:@"URI"] UTF8String],
6474 [[source objectForKey:@"Distribution"] UTF8String]
6483 detachNewThreadSelector:@selector(update_)
6486 title:@"Updating Sources"
6490 - (void) reloadData {
6491 @synchronized (self) {
6492 if (confirm_ == nil)
6498 pkgProblemResolver *resolver = [database_ resolver];
6500 resolver->InstallProtect();
6501 if (!resolver->Resolve(true))
6505 - (void) popUpBook:(RVBook *)book {
6506 [underlay_ popSubview:book];
6509 - (CGRect) popUpBounds {
6510 return [underlay_ bounds];
6514 [database_ prepare];
6516 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6517 [confirm_ setDelegate:self];
6519 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6520 [page setDelegate:self];
6522 [confirm_ setPage:page];
6523 [self popUpBook:confirm_];
6526 - (void) installPackage:(Package *)package {
6527 @synchronized (self) {
6534 - (void) removePackage:(Package *)package {
6535 @synchronized (self) {
6542 - (void) distUpgrade {
6543 @synchronized (self) {
6544 [database_ upgrade];
6550 @synchronized (self) {
6552 if (confirm_ != nil) {
6560 [overlay_ removeFromSuperview];
6564 detachNewThreadSelector:@selector(perform)
6571 - (void) bootstrap_ {
6573 [database_ upgrade];
6574 [database_ prepare];
6575 [database_ perform];
6578 - (void) bootstrap {
6580 detachNewThreadSelector:@selector(bootstrap_)
6583 title:@"Bootstrap Install"
6587 - (void) progressViewIsComplete:(ProgressView *)progress {
6588 if (confirm_ != nil) {
6589 [underlay_ addSubview:overlay_];
6590 [confirm_ popFromSuperviewAnimated:NO];
6596 - (void) setPage:(RVPage *)page {
6597 [page resetViewAnimated:NO];
6598 [page setDelegate:self];
6599 [book_ setPage:page];
6602 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6603 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6604 [browser loadURL:url];
6608 - (void) _setHomePage {
6609 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6612 - (void) buttonBarItemTapped:(id)sender {
6613 unsigned tag = [sender tag];
6615 [book_ resetViewAnimated:YES];
6617 } else if (tag_ == 2 && tag != 2)
6618 [sections_ resetView];
6621 case 1: [self _setHomePage]; break;
6623 case 2: [self setPage:sections_]; break;
6624 case 3: [self setPage:changes_]; break;
6625 case 4: [self setPage:manage_]; break;
6626 case 5: [self setPage:search_]; break;
6628 default: _assert(false);
6634 - (void) applicationWillSuspend {
6636 [super applicationWillSuspend];
6639 - (void) askForSettings {
6640 UIActionSheet *role = [[[UIActionSheet alloc]
6641 initWithTitle:@"Who Are You?"
6642 buttons:[NSArray arrayWithObjects:
6643 @"User (Graphical Only)",
6644 @"Hacker (+ Command Line)",
6645 @"Developer (No Filters)",
6647 defaultButtonIndex:-1
6652 [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."];
6653 [role popupAlertAnimated:YES];
6658 [self setStatusBarShowsProgress:NO];
6659 [self removeProgressHUD:hud_];
6664 pid_t pid = ExecFork();
6666 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6667 perror("launchctl stop");
6674 [self askForSettings];
6679 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6681 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6682 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6683 0, 0, screenrect.size.width, screenrect.size.height - 48
6684 ) database:database_];
6686 [book_ setDelegate:self];
6688 [overlay_ addSubview:book_];
6690 NSArray *buttonitems = [NSArray arrayWithObjects:
6691 [NSDictionary dictionaryWithObjectsAndKeys:
6692 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6693 @"home-up.png", kUIButtonBarButtonInfo,
6694 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6695 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6696 self, kUIButtonBarButtonTarget,
6697 @"Home", kUIButtonBarButtonTitle,
6698 @"0", kUIButtonBarButtonType,
6701 [NSDictionary dictionaryWithObjectsAndKeys:
6702 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6703 @"install-up.png", kUIButtonBarButtonInfo,
6704 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6705 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6706 self, kUIButtonBarButtonTarget,
6707 @"Sections", kUIButtonBarButtonTitle,
6708 @"0", kUIButtonBarButtonType,
6711 [NSDictionary dictionaryWithObjectsAndKeys:
6712 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6713 @"changes-up.png", kUIButtonBarButtonInfo,
6714 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6715 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6716 self, kUIButtonBarButtonTarget,
6717 @"Changes", kUIButtonBarButtonTitle,
6718 @"0", kUIButtonBarButtonType,
6721 [NSDictionary dictionaryWithObjectsAndKeys:
6722 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6723 @"manage-up.png", kUIButtonBarButtonInfo,
6724 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6725 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6726 self, kUIButtonBarButtonTarget,
6727 @"Manage", kUIButtonBarButtonTitle,
6728 @"0", kUIButtonBarButtonType,
6731 [NSDictionary dictionaryWithObjectsAndKeys:
6732 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6733 @"search-up.png", kUIButtonBarButtonInfo,
6734 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6735 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6736 self, kUIButtonBarButtonTarget,
6737 @"Search", kUIButtonBarButtonTitle,
6738 @"0", kUIButtonBarButtonType,
6742 buttonbar_ = [[UIToolbar alloc]
6744 withFrame:CGRectMake(
6745 0, screenrect.size.height - ButtonBarHeight_,
6746 screenrect.size.width, ButtonBarHeight_
6748 withItemList:buttonitems
6751 [buttonbar_ setDelegate:self];
6752 [buttonbar_ setBarStyle:1];
6753 [buttonbar_ setButtonBarTrackingMode:2];
6755 int buttons[5] = {1, 2, 3, 4, 5};
6756 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6757 [buttonbar_ showButtonGroup:0 withDuration:0];
6759 for (int i = 0; i != 5; ++i)
6760 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6761 i * 64 + 2, 1, 60, ButtonBarHeight_
6764 [buttonbar_ showSelectionForButton:1];
6765 [overlay_ addSubview:buttonbar_];
6767 [UIKeyboard initImplementationNow];
6768 CGSize keysize = [UIKeyboard defaultSize];
6769 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6770 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6771 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6772 [overlay_ addSubview:keyboard_];
6775 [underlay_ addSubview:overlay_];
6779 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6780 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6781 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6783 manage_ = (ManageView *) [[self
6784 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6785 withClass:[ManageView class]
6793 [self _setHomePage];
6796 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6797 NSString *context([sheet context]);
6799 if ([context isEqualToString:@"missing"])
6801 else if ([context isEqualToString:@"fixhalf"]) {
6804 @synchronized (self) {
6805 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6806 Package *broken = [broken_ objectAtIndex:i];
6809 NSString *id = [broken id];
6810 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6811 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6812 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6813 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6822 [broken_ removeAllObjects];
6831 } else if ([context isEqualToString:@"role"]) {
6833 case 1: Role_ = @"User"; break;
6834 case 2: Role_ = @"Hacker"; break;
6835 case 3: Role_ = @"Developer"; break;
6842 bool reset = Settings_ != nil;
6844 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6848 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6858 } else if ([context isEqualToString:@"upgrade"]) {
6861 @synchronized (self) {
6862 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6863 Package *essential = [essential_ objectAtIndex:i];
6864 [essential install];
6888 - (void) reorganize { _pooled
6889 system("/usr/libexec/cydia/free.sh");
6890 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6893 - (void) applicationSuspend:(__GSEvent *)event {
6894 if (hud_ == nil && ![progress_ isRunning])
6895 [super applicationSuspend:event];
6898 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6900 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6903 - (void) _setSuspended:(BOOL)value {
6905 [super _setSuspended:value];
6908 - (UIProgressHUD *) addProgressHUD {
6909 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
6910 [window_ setUserInteractionEnabled:NO];
6912 [progress_ addSubview:hud];
6916 - (void) removeProgressHUD:(UIProgressHUD *)hud {
6918 [hud removeFromSuperview];
6919 [window_ setUserInteractionEnabled:YES];
6922 - (void) openMailToURL:(NSURL *)url {
6923 // XXX: this makes me sad
6925 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6927 [UIApp openURL:url];// asPanel:YES];
6931 - (void) clearFirstResponder {
6932 if (id responder = [window_ firstResponder])
6933 [responder resignFirstResponder];
6936 - (RVPage *) pageForPackage:(NSString *)name {
6937 if (Package *package = [database_ packageWithName:name]) {
6938 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6939 [view setPackage:package];
6942 UIActionSheet *sheet = [[[UIActionSheet alloc]
6943 initWithTitle:@"Cannot Locate Package"
6944 buttons:[NSArray arrayWithObjects:@"Close", nil]
6945 defaultButtonIndex:0
6950 [sheet setBodyText:[NSString stringWithFormat:
6951 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6954 [sheet popupAlertAnimated:YES];
6959 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6963 NSString *scheme([[url scheme] lowercaseString]);
6964 if (![scheme isEqualToString:@"cydia"])
6966 NSString *path([url absoluteString]);
6967 if ([path length] < 8)
6969 path = [path substringFromIndex:8];
6970 if (![path hasPrefix:@"/"])
6971 path = [@"/" stringByAppendingString:path];
6973 if ([path isEqualToString:@"/add-source"])
6974 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6975 else if ([path isEqualToString:@"/storage"])
6976 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6977 else if ([path isEqualToString:@"/sources"])
6978 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6979 else if ([path isEqualToString:@"/packages"])
6980 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6981 else if ([path hasPrefix:@"/url/"])
6982 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6983 else if ([path hasPrefix:@"/launch/"])
6984 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6985 else if ([path hasPrefix:@"/package-settings/"])
6986 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6987 else if ([path hasPrefix:@"/package-signature/"])
6988 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6989 else if ([path hasPrefix:@"/package/"])
6990 return [self pageForPackage:[path substringFromIndex:9]];
6991 else if ([path hasPrefix:@"/files/"]) {
6992 NSString *name = [path substringFromIndex:7];
6994 if (Package *package = [database_ packageWithName:name]) {
6995 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6996 [files setPackage:package];
7004 - (void) applicationOpenURL:(NSURL *)url {
7005 [super applicationOpenURL:url];
7007 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7008 [self setPage:page];
7009 [buttonbar_ showSelectionForButton:tag];
7014 - (void) applicationDidFinishLaunching:(id)unused {
7016 Font12_ = [[UIFont systemFontOfSize:12] retain];
7017 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7018 Font14_ = [[UIFont systemFontOfSize:14] retain];
7019 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7020 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7022 _assert(pkgInitConfig(*_config));
7023 _assert(pkgInitSystem(*_config, _system));
7027 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7028 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7030 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7032 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7033 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7035 [window_ orderFront:self];
7036 [window_ makeKey:self];
7037 [window_ setHidden:NO];
7039 database_ = [Database sharedInstance];
7040 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7041 [database_ setDelegate:progress_];
7042 [window_ setContentView:progress_];
7044 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7045 [progress_ setContentView:underlay_];
7047 [progress_ resetView];
7050 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7051 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7052 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7053 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7054 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7055 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7056 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7058 [self setIdleTimerDisabled:YES];
7060 hud_ = [[self addProgressHUD] retain];
7061 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7063 [self setStatusBarShowsProgress:YES];
7066 detachNewThreadSelector:@selector(reorganize)
7074 - (void) showKeyboard:(BOOL)show {
7075 CGSize keysize = [UIKeyboard defaultSize];
7076 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7077 CGRect keyup = keydown;
7078 keyup.origin.y -= keysize.height;
7080 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7081 [animation setSignificantRectFields:2];
7084 [animation setStartFrame:keydown];
7085 [animation setEndFrame:keyup];
7086 [keyboard_ activate];
7088 [animation setStartFrame:keyup];
7089 [animation setEndFrame:keydown];
7090 [keyboard_ deactivate];
7093 [[UIAnimator sharedAnimator]
7094 addAnimations:[NSArray arrayWithObjects:animation, nil]
7095 withDuration:KeyboardTime_
7100 - (void) slideUp:(UIActionSheet *)alert {
7102 [alert presentSheetFromButtonBar:buttonbar_];
7104 [alert presentSheetInView:overlay_];
7109 void AddPreferences(NSString *plist) { _pooled
7110 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7111 _assert(settings != NULL);
7112 NSMutableArray *items = [settings objectForKey:@"items"];
7116 for (size_t i(0); i != [items count]; ++i) {
7117 NSMutableDictionary *item([items objectAtIndex:i]);
7118 NSString *label = [item objectForKey:@"label"];
7119 if (label != nil && [label isEqualToString:@"Cydia"]) {
7126 for (size_t i(0); i != [items count]; ++i) {
7127 NSDictionary *item([items objectAtIndex:i]);
7128 NSString *label = [item objectForKey:@"label"];
7129 if (label != nil && [label isEqualToString:@"General"]) {
7130 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7131 @"CydiaSettings", @"bundle",
7132 @"PSLinkCell", @"cell",
7133 [NSNumber numberWithBool:YES], @"hasIcon",
7134 [NSNumber numberWithBool:YES], @"isController",
7136 nil] atIndex:(i + 1)];
7142 _assert([settings writeToFile:plist atomically:YES] == YES);
7147 id Alloc_(id self, SEL selector) {
7148 id object = alloc_(self, selector);
7149 lprintf("[%s]A-%p\n", self->isa->name, object);
7154 id Dealloc_(id self, SEL selector) {
7155 id object = dealloc_(self, selector);
7156 lprintf("[%s]D-%p\n", self->isa->name, object);
7160 int main(int argc, char *argv[]) { _pooled
7162 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7164 bool substrate(false);
7170 for (int argi(1); argi != argc; ++argi)
7171 if (strcmp(argv[argi], "--") == 0) {
7173 argv[argi] = argv[0];
7179 for (int argi(1); argi != arge; ++argi)
7180 if (strcmp(args[argi], "--bootstrap") == 0)
7182 else if (strcmp(args[argi], "--substrate") == 0)
7185 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7188 App_ = [[NSBundle mainBundle] bundlePath];
7189 Home_ = NSHomeDirectory();
7190 Locale_ = CFLocaleCopyCurrent();
7193 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7194 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7195 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7196 Sounds_Keyboard_ = [keyboard boolValue];
7202 #if 0 /* XXX: this costs 1.4s of startup performance */
7203 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7204 _assert(errno == ENOENT);
7205 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7206 _assert(errno == ENOENT);
7209 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7210 alloc_ = alloc->method_imp;
7211 alloc->method_imp = (IMP) &Alloc_;*/
7213 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7214 dealloc_ = dealloc->method_imp;
7215 dealloc->method_imp = (IMP) &Dealloc_;*/
7220 size = sizeof(maxproc);
7221 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7222 perror("sysctlbyname(\"kern.maxproc\", ?)");
7223 else if (maxproc < 64) {
7225 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7226 perror("sysctlbyname(\"kern.maxproc\", #)");
7229 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7230 char *machine = new char[size];
7231 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7232 perror("sysctlbyname(\"hw.machine\", ?)");
7236 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7238 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7239 Build_ = [system objectForKey:@"ProductBuildVersion"];
7241 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7242 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7245 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7248 if (Metadata_ == NULL)
7249 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7251 Settings_ = [Metadata_ objectForKey:@"Settings"];
7253 Packages_ = [Metadata_ objectForKey:@"Packages"];
7254 Sections_ = [Metadata_ objectForKey:@"Sections"];
7255 Sources_ = [Metadata_ objectForKey:@"Sources"];
7258 if (Settings_ != nil)
7259 Role_ = [Settings_ objectForKey:@"Role"];
7261 if (Packages_ == nil) {
7262 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7263 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7266 if (Sections_ == nil) {
7267 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7268 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7271 if (Sources_ == nil) {
7272 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7273 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7277 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7280 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7281 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7282 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7283 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7285 if (access("/User", F_OK) != 0) {
7287 system("/usr/libexec/cydia/firmware.sh");
7291 _assert([[NSFileManager defaultManager]
7292 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7293 withIntermediateDirectories:YES
7298 space_ = CGColorSpaceCreateDeviceRGB();
7300 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7301 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7302 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7303 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7304 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7305 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7307 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7309 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7311 UIApplicationUseLegacyEvents(YES);
7312 UIKeyboardDisableAutomaticAppearance();
7315 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7317 CGColorSpaceRelease(space_);