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 (NSString *tag in tags_)
1578 if ([tag hasPrefix:@"role::"]) {
1579 role_ = [[tag substringFromIndex:6] retain];
1584 NSString *solid(latest == nil ? installed : latest);
1585 bool changed(false);
1587 NSString *key([id_ lowercaseString]);
1589 _profile(Package$initWithIterator$Metadata)
1590 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1591 if (metadata == nil) {
1592 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1597 [metadata setObject:solid forKey:@"LastVersion"];
1600 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1601 NSDate *last([metadata objectForKey:@"LastSeen"]);
1602 NSString *version([metadata objectForKey:@"LastVersion"]);
1605 first = last == nil ? now_ : last;
1606 [metadata setObject:first forKey:@"FirstSeen"];
1611 if (version == nil) {
1612 [metadata setObject:solid forKey:@"LastVersion"];
1614 } else if (![version isEqualToString:solid]) {
1615 [metadata setObject:solid forKey:@"LastVersion"];
1617 [metadata setObject:last forKey:@"LastSeen"];
1623 [Packages_ setObject:metadata forKey:key];
1630 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1631 return [[[Package alloc]
1632 initWithIterator:iterator
1637 - (pkgCache::PkgIterator) iterator {
1641 - (NSString *) section {
1642 if (section_ != nil)
1645 const char *section = iterator_.Section();
1646 if (section == NULL)
1649 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1652 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1653 if (NSString *rename = [value objectForKey:@"Rename"]) {
1658 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1662 - (NSString *) simpleSection {
1663 if (NSString *section = [self section])
1664 return Simplify(section);
1669 - (NSString *) uri {
1672 pkgIndexFile *index;
1673 pkgCache::PkgFileIterator file(file_.File());
1674 if (![database_ list].FindIndex(file, index))
1676 return [NSString stringWithUTF8String:iterator_->Path];
1677 //return [NSString stringWithUTF8String:file.Site()];
1678 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1682 - (Address *) maintainer {
1685 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1686 const std::string &maintainer(parser->Maintainer());
1687 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1691 return version_.end() ? 0 : version_->InstalledSize;
1694 - (NSString *) description {
1697 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1698 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1700 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1701 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1702 if ([lines count] < 2)
1705 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1706 for (size_t i(1), e([lines count]); i != e; ++i) {
1707 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1708 [trimmed addObject:trim];
1711 return [trimmed componentsJoinedByString:@"\n"];
1715 _profile(Package$index)
1716 NSString *name([self name]);
1717 if ([name length] == 0)
1719 unichar character([name characterAtIndex:0]);
1720 if (!isalpha(character))
1726 - (NSMutableDictionary *) metadata {
1727 return [Packages_ objectForKey:[id_ lowercaseString]];
1731 NSDictionary *metadata([self metadata]);
1732 if ([self subscribed])
1733 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1735 return [metadata objectForKey:@"FirstSeen"];
1738 - (BOOL) subscribed {
1739 NSDictionary *metadata([self metadata]);
1740 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1741 return [subscribed boolValue];
1747 NSDictionary *metadata([self metadata]);
1748 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1749 return [ignored boolValue];
1754 - (NSString *) latest {
1758 - (NSString *) installed {
1763 return !version_.end();
1766 - (BOOL) upgradableAndEssential:(BOOL)essential {
1767 pkgCache::VerIterator current = iterator_.CurrentVer();
1771 value = essential && [self essential] && [self visible];
1773 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1777 - (BOOL) essential {
1778 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1782 return [database_ cache][iterator_].InstBroken();
1785 - (BOOL) unfiltered {
1786 NSString *section = [self section];
1787 return section == nil || isSectionVisible(section);
1791 return [self hasSupportingRole] && [self unfiltered];
1795 unsigned char current = iterator_->CurrentState;
1796 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1799 - (BOOL) halfConfigured {
1800 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1803 - (BOOL) halfInstalled {
1804 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1808 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1809 return state.Mode != pkgDepCache::ModeKeep;
1812 - (NSString *) mode {
1813 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1815 switch (state.Mode) {
1816 case pkgDepCache::ModeDelete:
1817 if ((state.iFlags & pkgDepCache::Purge) != 0)
1821 case pkgDepCache::ModeKeep:
1822 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1826 case pkgDepCache::ModeInstall:
1827 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1828 return @"Reinstall";
1829 else switch (state.Status) {
1831 return @"Downgrade";
1837 return @"New Install";
1850 - (NSString *) name {
1851 return name_ == nil ? id_ : name_;
1854 - (NSString *) tagline {
1858 - (UIImage *) icon {
1859 NSString *section = [self simpleSection];
1863 if ([icon_ hasPrefix:@"file:///"])
1864 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1865 if (icon == nil) if (section != nil)
1866 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1867 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1868 if ([dicon hasPrefix:@"file:///"])
1869 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1871 icon = [UIImage applicationImageNamed:@"unknown.png"];
1875 - (NSString *) homepage {
1879 - (NSString *) depiction {
1883 - (Address *) sponsor {
1887 - (Address *) author {
1891 - (NSArray *) files {
1892 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1893 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1896 fin.open([path UTF8String]);
1901 while (std::getline(fin, line))
1902 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1907 - (NSArray *) relationships {
1908 return relationships_;
1911 - (NSArray *) warnings {
1912 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1913 const char *name(iterator_.Name());
1915 size_t length(strlen(name));
1916 if (length < 2) invalid:
1917 [warnings addObject:@"illegal package identifier"];
1918 else for (size_t i(0); i != length; ++i)
1920 /* XXX: technically this is not allowed */
1921 (name[i] < 'A' || name[i] > 'Z') &&
1922 (name[i] < 'a' || name[i] > 'z') &&
1923 (name[i] < '0' || name[i] > '9') &&
1924 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1927 if (strcmp(name, "cydia") != 0) {
1929 bool _private = false;
1932 bool repository = [[self section] isEqualToString:@"Repositories"];
1934 if (NSArray *files = [self files])
1935 for (NSString *file in files)
1936 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1938 else if (!_private && [file isEqualToString:@"/private"])
1940 else if (!stash && [file isEqualToString:@"/var/stash"])
1943 /* XXX: this is not sensitive enough. only some folders are valid. */
1944 if (cydia && !repository)
1945 [warnings addObject:@"files installed into Cydia.app"];
1947 [warnings addObject:@"files installed with /private/*"];
1949 [warnings addObject:@"files installed to /var/stash"];
1952 return [warnings count] == 0 ? nil : warnings;
1955 - (NSArray *) applications {
1956 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1958 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1960 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1961 if (NSArray *files = [self files])
1962 for (NSString *file in files)
1963 if (application_r(file)) {
1964 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1965 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1966 if ([id isEqualToString:me])
1969 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1971 display = application_r[1];
1973 NSString *bundle([file stringByDeletingLastPathComponent]);
1974 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1975 if (icon == nil || [icon length] == 0)
1977 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1979 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1980 [applications addObject:application];
1982 [application addObject:id];
1983 [application addObject:display];
1984 [application addObject:url];
1987 return [applications count] == 0 ? nil : applications;
1990 - (Source *) source {
1992 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1999 - (NSString *) role {
2003 - (BOOL) matches:(NSString *)text {
2009 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2010 if (range.location != NSNotFound)
2013 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2014 if (range.location != NSNotFound)
2017 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2018 if (range.location != NSNotFound)
2024 - (bool) hasSupportingRole {
2027 if ([role_ isEqualToString:@"enduser"])
2029 if ([Role_ isEqualToString:@"User"])
2031 if ([role_ isEqualToString:@"hacker"])
2033 if ([Role_ isEqualToString:@"Hacker"])
2035 if ([role_ isEqualToString:@"developer"])
2037 if ([Role_ isEqualToString:@"Developer"])
2042 - (BOOL) hasTag:(NSString *)tag {
2043 return tags_ == nil ? NO : [tags_ containsObject:tag];
2046 - (NSString *) primaryPurpose {
2047 for (NSString *tag in tags_)
2048 if ([tag hasPrefix:@"purpose::"])
2049 return [tag substringFromIndex:9];
2053 - (NSArray *) purposes {
2054 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2055 for (NSString *tag in tags_)
2056 if ([tag hasPrefix:@"purpose::"])
2057 [purposes addObject:[tag substringFromIndex:9]];
2058 return [purposes count] == 0 ? nil : purposes;
2061 - (NSComparisonResult) compareByName:(Package *)package {
2062 NSString *lhs = [self name];
2063 NSString *rhs = [package name];
2065 if ([lhs length] != 0 && [rhs length] != 0) {
2066 unichar lhc = [lhs characterAtIndex:0];
2067 unichar rhc = [rhs characterAtIndex:0];
2069 if (isalpha(lhc) && !isalpha(rhc))
2070 return NSOrderedAscending;
2071 else if (!isalpha(lhc) && isalpha(rhc))
2072 return NSOrderedDescending;
2075 return [lhs compare:rhs options:LaxCompareOptions_];
2078 - (NSComparisonResult) compareBySection:(Package *)package {
2079 NSString *lhs = [self section];
2080 NSString *rhs = [package section];
2082 if (lhs == NULL && rhs != NULL)
2083 return NSOrderedAscending;
2084 else if (lhs != NULL && rhs == NULL)
2085 return NSOrderedDescending;
2086 else if (lhs != NULL && rhs != NULL) {
2087 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2088 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2091 return NSOrderedSame;
2094 - (uint32_t) compareForChanges {
2099 uint32_t timestamp : 30;
2100 uint32_t ignored : 1;
2101 uint32_t upgradable : 1;
2105 bool upgradable([self upgradableAndEssential:YES]);
2106 value.bits.upgradable = upgradable ? 1 : 0;
2109 value.bits.timestamp = 0;
2110 value.bits.ignored = [self ignored] ? 0 : 1;
2111 value.bits.upgradable = 1;
2113 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2114 value.bits.ignored = 0;
2115 value.bits.upgradable = 0;
2118 return _not(uint32_t) - value.key;
2122 pkgProblemResolver *resolver = [database_ resolver];
2123 resolver->Clear(iterator_);
2124 resolver->Protect(iterator_);
2125 pkgCacheFile &cache([database_ cache]);
2126 cache->MarkInstall(iterator_, false);
2127 pkgDepCache::StateCache &state((*cache)[iterator_]);
2128 if (!state.Install())
2129 cache->SetReInstall(iterator_, true);
2133 pkgProblemResolver *resolver = [database_ resolver];
2134 resolver->Clear(iterator_);
2135 resolver->Protect(iterator_);
2136 resolver->Remove(iterator_);
2137 [database_ cache]->MarkDelete(iterator_, true);
2140 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
2141 _profile(Package$isUnfilteredAndSearchedForBy)
2144 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2145 value &= [self unfiltered];
2148 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2149 value &= [self matches:search];
2152 return [NSNumber numberWithBool:value];
2156 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
2157 return [NSNumber numberWithBool:(
2158 (![number boolValue] || [self visible]) && [self installed] != nil
2162 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
2163 NSString *section = [self section];
2165 return [NSNumber numberWithBool:(
2167 [self installed] == nil && (
2169 section == nil && [name length] == 0 ||
2170 [name isEqualToString:section]
2175 - (NSNumber *) isVisibleInSource:(Source *)source {
2176 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2181 /* Section Class {{{ */
2182 @interface Section : NSObject {
2189 - (NSComparisonResult) compareByName:(Section *)section;
2190 - (Section *) initWithName:(NSString *)name;
2191 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2192 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2193 - (NSString *) name;
2197 - (void) addToCount;
2201 @implementation Section
2208 - (NSComparisonResult) compareByName:(Section *)section {
2209 NSString *lhs = [self name];
2210 NSString *rhs = [section name];
2212 if ([lhs length] != 0 && [rhs length] != 0) {
2213 unichar lhc = [lhs characterAtIndex:0];
2214 unichar rhc = [rhs characterAtIndex:0];
2216 if (isalpha(lhc) && !isalpha(rhc))
2217 return NSOrderedAscending;
2218 else if (!isalpha(lhc) && isalpha(rhc))
2219 return NSOrderedDescending;
2222 return [lhs compare:rhs options:LaxCompareOptions_];
2225 - (Section *) initWithName:(NSString *)name {
2226 return [self initWithName:name row:0];
2229 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2230 if ((self = [super init]) != nil) {
2231 name_ = [name retain];
2237 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2238 if ((self = [super init]) != nil) {
2239 name_ = [[NSString stringWithCharacters:&index length:1] retain];
2245 - (NSString *) name {
2261 - (void) addToCount {
2269 static NSArray *Finishes_;
2271 /* Database Implementation {{{ */
2272 @implementation Database
2274 + (Database *) sharedInstance {
2275 static Database *instance;
2276 if (instance == nil)
2277 instance = [[Database alloc] init];
2286 - (void) _readCydia:(NSNumber *)fd { _pooled
2287 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2288 std::istream is(&ib);
2291 static Pcre finish_r("^finish:([^:]*)$");
2293 while (std::getline(is, line)) {
2294 const char *data(line.c_str());
2295 size_t size = line.size();
2296 lprintf("C:%s\n", data);
2298 if (finish_r(data, size)) {
2299 NSString *finish = finish_r[1];
2300 int index = [Finishes_ indexOfObject:finish];
2301 if (index != INT_MAX && index > Finish_)
2309 - (void) _readStatus:(NSNumber *)fd { _pooled
2310 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2311 std::istream is(&ib);
2314 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2315 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2317 while (std::getline(is, line)) {
2318 const char *data(line.c_str());
2319 size_t size = line.size();
2320 lprintf("S:%s\n", data);
2322 if (conffile_r(data, size)) {
2323 [delegate_ setConfigurationData:conffile_r[1]];
2324 } else if (strncmp(data, "status: ", 8) == 0) {
2325 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2326 [delegate_ setProgressTitle:string];
2327 } else if (pmstatus_r(data, size)) {
2328 std::string type([pmstatus_r[1] UTF8String]);
2329 NSString *id = pmstatus_r[2];
2331 float percent([pmstatus_r[3] floatValue]);
2332 [delegate_ setProgressPercent:(percent / 100)];
2334 NSString *string = pmstatus_r[4];
2336 if (type == "pmerror")
2337 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2338 withObject:[NSArray arrayWithObjects:string, id, nil]
2341 else if (type == "pmstatus") {
2342 [delegate_ setProgressTitle:string];
2343 } else if (type == "pmconffile")
2344 [delegate_ setConfigurationData:string];
2345 else _assert(false);
2346 } else _assert(false);
2352 - (void) _readOutput:(NSNumber *)fd { _pooled
2353 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2354 std::istream is(&ib);
2357 while (std::getline(is, line)) {
2358 lprintf("O:%s\n", line.c_str());
2359 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2369 - (Package *) packageWithName:(NSString *)name {
2370 if (static_cast<pkgDepCache *>(cache_) == NULL)
2372 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2373 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2376 - (Database *) init {
2377 if ((self = [super init]) != nil) {
2384 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2385 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2389 _assert(pipe(fds) != -1);
2392 _config->Set("APT::Keep-Fds::", cydiafd_);
2393 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2396 detachNewThreadSelector:@selector(_readCydia:)
2398 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2401 _assert(pipe(fds) != -1);
2405 detachNewThreadSelector:@selector(_readStatus:)
2407 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2410 _assert(pipe(fds) != -1);
2411 _assert(dup2(fds[0], 0) != -1);
2412 _assert(close(fds[0]) != -1);
2414 input_ = fdopen(fds[1], "a");
2416 _assert(pipe(fds) != -1);
2417 _assert(dup2(fds[1], 1) != -1);
2418 _assert(close(fds[1]) != -1);
2421 detachNewThreadSelector:@selector(_readOutput:)
2423 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2428 - (pkgCacheFile &) cache {
2432 - (pkgDepCache::Policy *) policy {
2436 - (pkgRecords *) records {
2440 - (pkgProblemResolver *) resolver {
2444 - (pkgAcquire &) fetcher {
2448 - (pkgSourceList &) list {
2452 - (NSArray *) packages {
2456 - (NSArray *) sources {
2457 return [sources_ allValues];
2460 - (NSArray *) issues {
2461 if (cache_->BrokenCount() == 0)
2464 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2466 for (Package *package in packages_) {
2467 if (![package broken])
2469 pkgCache::PkgIterator pkg([package iterator]);
2471 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2472 [entry addObject:[package name]];
2473 [issues addObject:entry];
2475 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2479 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2480 pkgCache::DepIterator start;
2481 pkgCache::DepIterator end;
2482 dep.GlobOr(start, end); // ++dep
2484 if (!cache_->IsImportantDep(end))
2486 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2489 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2490 [entry addObject:failure];
2491 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2493 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2494 [failure addObject:[package name]];
2496 pkgCache::PkgIterator target(start.TargetPkg());
2497 if (target->ProvidesList != 0)
2498 [failure addObject:@"?"];
2500 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2502 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2503 else if (!cache_[target].CandidateVerIter(cache_).end())
2504 [failure addObject:@"-"];
2505 else if (target->ProvidesList == 0)
2506 [failure addObject:@"!"];
2508 [failure addObject:@"%"];
2512 if (start.TargetVer() != 0)
2513 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2524 - (void) reloadData { _pooled
2544 if (!cache_.Open(progress_, true)) {
2546 if (!_error->PopMessage(error))
2549 lprintf("cache_.Open():[%s]\n", error.c_str());
2551 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2552 [delegate_ repairWithSelector:@selector(configure)];
2553 else if (error == "The package lists or status file could not be parsed or opened.")
2554 [delegate_ repairWithSelector:@selector(update)];
2555 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2556 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2557 // else if (error == "The list of sources could not be read.")
2558 else _assert(false);
2564 now_ = [[NSDate date] retain];
2566 policy_ = new pkgDepCache::Policy();
2567 records_ = new pkgRecords(cache_);
2568 resolver_ = new pkgProblemResolver(cache_);
2569 fetcher_ = new pkgAcquire(&status_);
2572 list_ = new pkgSourceList();
2573 _assert(list_->ReadMainList());
2575 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2576 _assert(pkgApplyStatus(cache_));
2578 if (cache_->BrokenCount() != 0) {
2579 _assert(pkgFixBroken(cache_));
2580 _assert(cache_->BrokenCount() == 0);
2581 _assert(pkgMinimizeUpgrade(cache_));
2584 [sources_ removeAllObjects];
2585 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2586 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2587 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2589 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2590 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2594 [packages_ removeAllObjects];
2596 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2597 if (Package *package = [Package packageWithIterator:iterator database:self])
2598 [packages_ addObject:package];
2600 [packages_ sortUsingSelector:@selector(compareByName:)];
2603 _config->Set("Acquire::http::Timeout", 15);
2604 _config->Set("Acquire::http::MaxParallel", 4);
2607 - (void) configure {
2608 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2609 system([dpkg UTF8String]);
2617 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2618 _assert(!_error->PendingError());
2621 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2624 public pkgArchiveCleaner
2627 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2632 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2634 while (_error->PopMessage(error))
2635 lprintf("ArchiveCleaner: %s\n", error.c_str());
2640 pkgRecords records(cache_);
2642 lock_ = new FileFd();
2643 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2644 _assert(!_error->PendingError());
2647 // XXX: explain this with an error message
2648 _assert(list.ReadMainList());
2650 manager_ = (_system->CreatePM(cache_));
2651 _assert(manager_->GetArchives(fetcher_, &list, &records));
2652 _assert(!_error->PendingError());
2656 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2658 _assert(list.ReadMainList());
2659 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2660 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2663 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2668 bool failed = false;
2669 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2670 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2673 std::string uri = (*item)->DescURI();
2674 std::string error = (*item)->ErrorText;
2676 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2679 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2680 withObject:[NSArray arrayWithObjects:
2681 [NSString stringWithUTF8String:error.c_str()],
2693 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2695 if (_error->PendingError()) {
2700 if (result == pkgPackageManager::Failed) {
2705 if (result != pkgPackageManager::Completed) {
2710 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2712 _assert(list.ReadMainList());
2713 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2714 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2717 if (![before isEqualToArray:after])
2722 _assert(pkgDistUpgrade(cache_));
2726 [self updateWithStatus:status_];
2729 - (void) updateWithStatus:(Status &)status {
2731 _assert(list.ReadMainList());
2734 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2735 _assert(!_error->PendingError());
2737 pkgAcquire fetcher(&status);
2738 _assert(list.GetIndexes(&fetcher));
2740 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2741 bool failed = false;
2742 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2743 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2744 (*item)->Finished();
2748 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2749 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2750 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2753 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2758 - (void) setDelegate:(id)delegate {
2759 delegate_ = delegate;
2760 status_.setDelegate(delegate);
2761 progress_.setDelegate(delegate);
2764 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2765 pkgIndexFile *index(NULL);
2766 list_->FindIndex(file, index);
2767 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2773 /* PopUp Windows {{{ */
2774 @interface PopUpView : UIView {
2775 _transient id delegate_;
2776 UITransitionView *transition_;
2781 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2785 @implementation PopUpView
2788 [transition_ setDelegate:nil];
2789 [transition_ release];
2795 [transition_ transition:UITransitionPushFromTop toView:nil];
2798 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2799 if (from != nil && to == nil)
2800 [self removeFromSuperview];
2803 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2804 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2805 delegate_ = delegate;
2807 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2808 [self addSubview:transition_];
2810 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2812 [view addSubview:self];
2814 [transition_ setDelegate:self];
2816 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2817 [transition_ transition:UITransitionNone toView:blank];
2818 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2825 /* Mail Composition {{{ */
2826 @interface MailToView : PopUpView {
2827 MailComposeController *controller_;
2830 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2834 @implementation MailToView
2837 [controller_ release];
2841 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2845 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2846 NSLog(@"did:%@", delivery);
2847 // [UIApp setStatusBarShowsProgress:NO];
2848 if ([controller error]){
2849 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2850 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2851 [mailAlertSheet setBodyText:[controller error]];
2852 [mailAlertSheet popupAlertAnimated:YES];
2856 - (void) showError {
2857 NSLog(@"%@", [controller_ error]);
2858 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2859 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2860 [mailAlertSheet setBodyText:[controller_ error]];
2861 [mailAlertSheet popupAlertAnimated:YES];
2864 - (void) deliverMessage { _pooled
2868 if (![controller_ deliverMessage])
2869 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2872 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2873 if ([controller_ needsDelivery])
2874 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2879 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2880 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2881 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2882 [controller_ setDelegate:self];
2883 [controller_ initializeUI];
2884 [controller_ setupForURL:url];
2886 UIView *view([controller_ view]);
2887 [overlay_ addSubview:view];
2893 /* Confirmation View {{{ */
2894 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2895 if (!iterator.end())
2896 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2897 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2899 pkgCache::PkgIterator package(dep.TargetPkg());
2902 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2909 @protocol ConfirmationViewDelegate
2914 @interface ConfirmationView : BrowserView {
2915 _transient Database *database_;
2916 UIActionSheet *essential_;
2923 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2927 @implementation ConfirmationView
2934 if (essential_ != nil)
2935 [essential_ release];
2941 [book_ popFromSuperviewAnimated:YES];
2944 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2945 NSString *context([sheet context]);
2947 if ([context isEqualToString:@"remove"]) {
2955 [delegate_ confirm];
2962 } else if ([context isEqualToString:@"unable"]) {
2966 [super alertSheet:sheet buttonClicked:button];
2969 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2970 [window setValue:changes_ forKey:@"changes"];
2971 [window setValue:issues_ forKey:@"issues"];
2972 [window setValue:sizes_ forKey:@"sizes"];
2973 [super webView:sender didClearWindowObject:window forFrame:frame];
2976 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2977 if ((self = [super initWithBook:book]) != nil) {
2978 database_ = database;
2980 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2981 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2982 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2983 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2984 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2988 pkgDepCache::Policy *policy([database_ policy]);
2990 pkgCacheFile &cache([database_ cache]);
2991 NSArray *packages = [database_ packages];
2992 for (Package *package in packages) {
2993 pkgCache::PkgIterator iterator = [package iterator];
2994 pkgDepCache::StateCache &state(cache[iterator]);
2996 NSString *name([package name]);
2998 if (state.NewInstall())
2999 [installing addObject:name];
3000 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
3001 [reinstalling addObject:name];
3002 else if (state.Upgrade())
3003 [upgrading addObject:name];
3004 else if (state.Downgrade())
3005 [downgrading addObject:name];
3006 else if (state.Delete()) {
3007 if ([package essential])
3009 [removing addObject:name];
3012 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3013 substrate_ |= DepSubstrate(iterator.CurrentVer());
3018 else if (Advanced_ || true) {
3019 essential_ = [[UIActionSheet alloc]
3020 initWithTitle:@"Removing Essentials"
3021 buttons:[NSArray arrayWithObjects:
3022 @"Cancel Operation (Safe)",
3023 @"Force Removal (Unsafe)",
3025 defaultButtonIndex:0
3031 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3033 [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."];
3035 essential_ = [[UIActionSheet alloc]
3036 initWithTitle:@"Unable to Comply"
3037 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3038 defaultButtonIndex:0
3043 [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."];
3046 changes_ = [[NSArray alloc] initWithObjects:
3054 issues_ = [database_ issues];
3056 issues_ = [issues_ retain];
3058 sizes_ = [[NSArray alloc] initWithObjects:
3059 SizeString([database_ fetcher].FetchNeeded()),
3060 SizeString([database_ fetcher].PartialPresent()),
3061 SizeString([database_ cache]->UsrSize()),
3064 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3068 - (NSString *) backButtonTitle {
3072 - (NSString *) leftButtonTitle {
3076 - (id) _rightButtonTitle {
3077 #if AlwaysReload || IgnoreInstall
3080 return issues_ == nil ? @"Confirm" : nil;
3084 - (void) _leftButtonClicked {
3089 - (void) _rightButtonClicked {
3091 return [super _rightButtonClicked];
3093 if (essential_ != nil)
3094 [essential_ popupAlertAnimated:YES];
3098 [delegate_ confirm];
3106 /* Progress Data {{{ */
3107 @interface ProgressData : NSObject {
3113 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3120 @implementation ProgressData
3122 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3123 if ((self = [super init]) != nil) {
3124 selector_ = selector;
3144 /* Progress View {{{ */
3145 @interface ProgressView : UIView <
3146 ConfigurationDelegate,
3149 _transient Database *database_;
3151 UIView *background_;
3152 UITransitionView *transition_;
3154 UINavigationBar *navbar_;
3155 UIProgressBar *progress_;
3156 UITextView *output_;
3157 UITextLabel *status_;
3158 UIPushButton *close_;
3161 SHA1SumValue springlist_;
3162 SHA1SumValue notifyconf_;
3163 SHA1SumValue sandplate_;
3166 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3168 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3169 - (void) setContentView:(UIView *)view;
3172 - (void) _retachThread;
3173 - (void) _detachNewThreadData:(ProgressData *)data;
3174 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3180 @protocol ProgressViewDelegate
3181 - (void) progressViewIsComplete:(ProgressView *)sender;
3184 @implementation ProgressView
3187 [transition_ setDelegate:nil];
3188 [navbar_ setDelegate:nil];
3191 if (background_ != nil)
3192 [background_ release];
3193 [transition_ release];
3196 [progress_ release];
3203 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3204 if (bootstrap_ && from == overlay_ && to == view_)
3208 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3209 if ((self = [super initWithFrame:frame]) != nil) {
3210 database_ = database;
3211 delegate_ = delegate;
3213 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3214 [transition_ setDelegate:self];
3216 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3219 [overlay_ setBackgroundColor:[UIColor blackColor]];
3221 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3222 [background_ setBackgroundColor:[UIColor blackColor]];
3223 [self addSubview:background_];
3226 [self addSubview:transition_];
3228 CGSize navsize = [UINavigationBar defaultSize];
3229 CGRect navrect = {{0, 0}, navsize};
3231 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3232 [overlay_ addSubview:navbar_];
3234 [navbar_ setBarStyle:1];
3235 [navbar_ setDelegate:self];
3237 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3238 [navbar_ pushNavigationItem:navitem];
3240 CGRect bounds = [overlay_ bounds];
3241 CGSize prgsize = [UIProgressBar defaultSize];
3244 (bounds.size.width - prgsize.width) / 2,
3245 bounds.size.height - prgsize.height - 20
3248 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3249 [progress_ setStyle:0];
3251 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3253 bounds.size.height - prgsize.height - 50,
3254 bounds.size.width - 20,
3258 [status_ setColor:[UIColor whiteColor]];
3259 [status_ setBackgroundColor:[UIColor clearColor]];
3261 [status_ setCentersHorizontally:YES];
3262 //[status_ setFont:font];
3265 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3267 navrect.size.height + 20,
3268 bounds.size.width - 20,
3269 bounds.size.height - navsize.height - 62 - navrect.size.height
3273 //[output_ setTextFont:@"Courier New"];
3274 [output_ setTextSize:12];
3276 [output_ setTextColor:[UIColor whiteColor]];
3277 [output_ setBackgroundColor:[UIColor clearColor]];
3279 [output_ setMarginTop:0];
3280 [output_ setAllowsRubberBanding:YES];
3281 [output_ setEditable:NO];
3283 [overlay_ addSubview:output_];
3285 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3287 bounds.size.height - prgsize.height - 50,
3288 bounds.size.width - 20,
3292 [close_ setAutosizesToFit:NO];
3293 [close_ setDrawsShadow:YES];
3294 [close_ setStretchBackground:YES];
3295 [close_ setEnabled:YES];
3297 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3298 [close_ setTitleFont:bold];
3300 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3301 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3302 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3306 - (void) setContentView:(UIView *)view {
3307 view_ = [view retain];
3310 - (void) resetView {
3311 [transition_ transition:6 toView:view_];
3314 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3315 NSString *context([sheet context]);
3317 if ([context isEqualToString:@"error"])
3319 else if ([context isEqualToString:@"conffile"]) {
3320 FILE *input = [database_ input];
3324 fprintf(input, "N\n");
3328 fprintf(input, "Y\n");
3339 - (void) closeButtonPushed {
3348 [delegate_ suspendWithAnimation:YES];
3352 system("launchctl stop com.apple.SpringBoard");
3356 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3365 - (void) _retachThread {
3366 UINavigationItem *item = [navbar_ topItem];
3367 [item setTitle:@"Complete"];
3369 [overlay_ addSubview:close_];
3370 [progress_ removeFromSuperview];
3371 [status_ removeFromSuperview];
3373 [delegate_ progressViewIsComplete:self];
3376 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3377 MMap mmap(file, MMap::ReadOnly);
3379 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3380 if (!(sandplate_ == sha1.Result()))
3385 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3386 MMap mmap(file, MMap::ReadOnly);
3388 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3389 if (!(notifyconf_ == sha1.Result()))
3394 FileFd file(SpringBoard_, FileFd::ReadOnly);
3395 MMap mmap(file, MMap::ReadOnly);
3397 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3398 if (!(springlist_ == sha1.Result()))
3403 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3404 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3405 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3406 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3407 case 4: [close_ setTitle:@"Reboot Device"]; break;
3410 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3412 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3413 [cache autorelease];
3415 NSFileManager *manager = [NSFileManager defaultManager];
3416 NSError *error = nil;
3418 id system = [cache objectForKey:@"System"];
3423 if (stat(Cache_, &info) == -1)
3426 [system removeAllObjects];
3428 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3429 for (NSString *app in apps)
3430 if ([app hasSuffix:@".app"]) {
3431 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3432 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3433 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3435 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3436 [info setObject:path forKey:@"Path"];
3437 [info setObject:@"System" forKey:@"ApplicationType"];
3438 [system addInfoDictionary:info];
3444 [cache writeToFile:@Cache_ atomically:YES];
3446 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3448 if (chmod(Cache_, info.st_mode) == -1)
3452 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3455 notify_post("com.apple.mobile.application_installed");
3457 [delegate_ setStatusBarShowsProgress:NO];
3460 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3461 [[data target] performSelector:[data selector] withObject:[data object]];
3464 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3467 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3468 UINavigationItem *item = [navbar_ topItem];
3469 [item setTitle:title];
3471 [status_ setText:nil];
3472 [output_ setText:@""];
3473 [progress_ setProgress:0];
3475 [close_ removeFromSuperview];
3476 [overlay_ addSubview:progress_];
3477 [overlay_ addSubview:status_];
3479 [delegate_ setStatusBarShowsProgress:YES];
3483 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3484 MMap mmap(file, MMap::ReadOnly);
3486 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3487 sandplate_ = sha1.Result();
3491 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3492 MMap mmap(file, MMap::ReadOnly);
3494 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3495 notifyconf_ = sha1.Result();
3499 FileFd file(SpringBoard_, FileFd::ReadOnly);
3500 MMap mmap(file, MMap::ReadOnly);
3502 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3503 springlist_ = sha1.Result();
3506 [transition_ transition:6 toView:overlay_];
3509 detachNewThreadSelector:@selector(_detachNewThreadData:)
3511 withObject:[[ProgressData alloc]
3512 initWithSelector:selector
3519 - (void) repairWithSelector:(SEL)selector {
3521 detachNewThreadSelector:selector
3528 - (void) setConfigurationData:(NSString *)data {
3530 performSelectorOnMainThread:@selector(_setConfigurationData:)
3536 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3537 Package *package = id == nil ? nil : [database_ packageWithName:id];
3539 UIActionSheet *sheet = [[[UIActionSheet alloc]
3540 initWithTitle:(package == nil ? id : [package name])
3541 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3542 defaultButtonIndex:0
3547 [sheet setBodyText:error];
3548 [sheet popupAlertAnimated:YES];
3551 - (void) setProgressTitle:(NSString *)title {
3553 performSelectorOnMainThread:@selector(_setProgressTitle:)
3559 - (void) setProgressPercent:(float)percent {
3561 performSelectorOnMainThread:@selector(_setProgressPercent:)
3562 withObject:[NSNumber numberWithFloat:percent]
3567 - (void) startProgress {
3570 - (void) addProgressOutput:(NSString *)output {
3572 performSelectorOnMainThread:@selector(_addProgressOutput:)
3578 - (bool) isCancelling:(size_t)received {
3582 - (void) _setConfigurationData:(NSString *)data {
3583 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3585 _assert(conffile_r(data));
3587 NSString *ofile = conffile_r[1];
3588 //NSString *nfile = conffile_r[2];
3590 UIActionSheet *sheet = [[[UIActionSheet alloc]
3591 initWithTitle:@"Configuration Upgrade"
3592 buttons:[NSArray arrayWithObjects:
3593 @"Keep My Old Copy",
3594 @"Accept The New Copy",
3595 // XXX: @"See What Changed",
3597 defaultButtonIndex:0
3602 [sheet setBodyText:[NSString stringWithFormat:
3603 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3606 [sheet popupAlertAnimated:YES];
3609 - (void) _setProgressTitle:(NSString *)title {
3610 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3611 for (size_t i(0), e([words count]); i != e; ++i) {
3612 NSString *word([words objectAtIndex:i]);
3613 if (Package *package = [database_ packageWithName:word])
3614 [words replaceObjectAtIndex:i withObject:[package name]];
3617 [status_ setText:[words componentsJoinedByString:@" "]];
3620 - (void) _setProgressPercent:(NSNumber *)percent {
3621 [progress_ setProgress:[percent floatValue]];
3624 - (void) _addProgressOutput:(NSString *)output {
3625 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3626 CGSize size = [output_ contentSize];
3627 CGRect rect = {{0, size.height}, {size.width, 0}};
3628 [output_ scrollRectToVisible:rect animated:YES];
3631 - (BOOL) isRunning {
3638 /* Package Cell {{{ */
3639 @interface PackageCell : UISimpleTableCell {
3642 NSString *description_;
3646 UITextLabel *status_;
3650 - (PackageCell *) init;
3651 - (void) setPackage:(Package *)package;
3653 + (int) heightForPackage:(Package *)package;
3657 @implementation PackageCell
3659 - (void) clearPackage {
3670 if (description_ != nil) {
3671 [description_ release];
3675 if (source_ != nil) {
3680 if (badge_ != nil) {
3687 [self clearPackage];
3694 - (PackageCell *) init {
3695 if ((self = [super init]) != nil) {
3697 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3698 [status_ setBackgroundColor:[UIColor clearColor]];
3699 [status_ setFont:small];
3704 - (void) setPackage:(Package *)package {
3705 [self clearPackage];
3707 Source *source = [package source];
3708 NSString *section = [package simpleSection];
3710 icon_ = [[package icon] retain];
3712 name_ = [[package name] retain];
3713 description_ = [[package tagline] retain];
3715 NSString *label = nil;
3716 bool trusted = false;
3718 if (source != nil) {
3719 label = [source label];
3720 trusted = [source trusted];
3721 } else if ([[package id] isEqualToString:@"firmware"])
3724 label = @"Unknown/Local";
3726 NSString *from = [NSString stringWithFormat:@"from %@", label];
3728 if (section != nil && ![section isEqualToString:label])
3729 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3731 source_ = [from retain];
3733 if (NSString *purpose = [package primaryPurpose])
3734 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3735 badge_ = [badge_ retain];
3738 if (NSString *mode = [package mode]) {
3739 [badge_ setImage:[UIImage applicationImageNamed:
3740 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3743 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3744 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3745 } else if ([package half]) {
3746 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3747 [status_ setText:@"Package Damaged"];
3748 [status_ setColor:[UIColor redColor]];
3750 [badge_ setImage:nil];
3751 [status_ setText:nil];
3756 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3759 rect.size = [icon_ size];
3761 rect.size.width /= 2;
3762 rect.size.height /= 2;
3764 rect.origin.x = 25 - rect.size.width / 2;
3765 rect.origin.y = 25 - rect.size.height / 2;
3767 [icon_ drawInRect:rect];
3770 if (badge_ != nil) {
3771 CGSize size = [badge_ size];
3773 [badge_ drawAtPoint:CGPointMake(
3774 36 - size.width / 2,
3775 36 - size.height / 2
3784 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3785 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3789 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3791 [super drawContentInRect:rect selected:selected];
3794 + (int) heightForPackage:(Package *)package {
3795 NSString *tagline([package tagline]);
3796 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3798 if ([package hasMode] || [package half])
3807 /* Section Cell {{{ */
3808 @interface SectionCell : UISimpleTableCell {
3813 _UISwitchSlider *switch_;
3818 - (void) setSection:(Section *)section editing:(BOOL)editing;
3822 @implementation SectionCell
3824 - (void) clearSection {
3825 if (section_ != nil) {
3835 if (count_ != nil) {
3842 [self clearSection];
3849 if ((self = [super init]) != nil) {
3850 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3852 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3853 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3857 - (void) onSwitch:(id)sender {
3858 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3859 if (metadata == nil) {
3860 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3861 [Sections_ setObject:metadata forKey:section_];
3865 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3868 - (void) setSection:(Section *)section editing:(BOOL)editing {
3869 if (editing != editing_) {
3871 [switch_ removeFromSuperview];
3873 [self addSubview:switch_];
3877 [self clearSection];
3879 if (section == nil) {
3880 name_ = [@"All Packages" retain];
3883 section_ = [section name];
3884 if (section_ != nil)
3885 section_ = [section_ retain];
3886 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3887 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3890 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3894 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3895 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3902 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3904 CGSize size = [count_ sizeWithFont:Font14_];
3908 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3910 [super drawContentInRect:rect selected:selected];
3916 /* File Table {{{ */
3917 @interface FileTable : RVPage {
3918 _transient Database *database_;
3921 NSMutableArray *files_;
3925 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3926 - (void) setPackage:(Package *)package;
3930 @implementation FileTable
3933 if (package_ != nil)
3942 - (int) numberOfRowsInTable:(UITable *)table {
3943 return files_ == nil ? 0 : [files_ count];
3946 - (float) table:(UITable *)table heightForRow:(int)row {
3950 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3951 if (reusing == nil) {
3952 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3953 UIFont *font = [UIFont systemFontOfSize:16];
3954 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3956 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3960 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3964 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3965 if ((self = [super initWithBook:book]) != nil) {
3966 database_ = database;
3968 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3970 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3971 [self addSubview:list_];
3973 UITableColumn *column = [[[UITableColumn alloc]
3974 initWithTitle:@"Name"
3976 width:[self frame].size.width
3979 [list_ setDataSource:self];
3980 [list_ setSeparatorStyle:1];
3981 [list_ addTableColumn:column];
3982 [list_ setDelegate:self];
3983 [list_ setReusesTableCells:YES];
3987 - (void) setPackage:(Package *)package {
3988 if (package_ != nil) {
3989 [package_ autorelease];
3998 [files_ removeAllObjects];
4000 if (package != nil) {
4001 package_ = [package retain];
4002 name_ = [[package id] retain];
4004 if (NSArray *files = [package files])
4005 [files_ addObjectsFromArray:files];
4007 if ([files_ count] != 0) {
4008 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4009 [files_ removeObjectAtIndex:0];
4010 [files_ sortUsingSelector:@selector(compareByPath:)];
4012 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4013 [stack addObject:@"/"];
4015 for (int i(0), e([files_ count]); i != e; ++i) {
4016 NSString *file = [files_ objectAtIndex:i];
4017 while (![file hasPrefix:[stack lastObject]])
4018 [stack removeLastObject];
4019 NSString *directory = [stack lastObject];
4020 [stack addObject:[file stringByAppendingString:@"/"]];
4021 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4022 ([stack count] - 2) * 3, "",
4023 [file substringFromIndex:[directory length]]
4032 - (void) resetViewAnimated:(BOOL)animated {
4033 [list_ resetViewAnimated:animated];
4036 - (void) reloadData {
4037 [self setPackage:[database_ packageWithName:name_]];
4038 [self reloadButtons];
4041 - (NSString *) title {
4042 return @"Installed Files";
4045 - (NSString *) backButtonTitle {
4051 /* Package View {{{ */
4052 @interface PackageView : BrowserView {
4053 _transient Database *database_;
4056 NSMutableArray *buttons_;
4059 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4060 - (void) setPackage:(Package *)package;
4064 @implementation PackageView
4067 if (package_ != nil)
4075 - (void) _clickButtonWithName:(NSString *)name {
4076 if ([name isEqualToString:@"Install"])
4077 [delegate_ installPackage:package_];
4078 else if ([name isEqualToString:@"Reinstall"])
4079 [delegate_ installPackage:package_];
4080 else if ([name isEqualToString:@"Remove"])
4081 [delegate_ removePackage:package_];
4082 else if ([name isEqualToString:@"Upgrade"])
4083 [delegate_ installPackage:package_];
4084 else _assert(false);
4087 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4088 NSString *context([sheet context]);
4090 if ([context isEqualToString:@"modify"]) {
4091 int count = [buttons_ count];
4092 _assert(count != 0);
4093 _assert(button <= count + 1);
4095 if (count != button - 1)
4096 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4100 [super alertSheet:sheet buttonClicked:button];
4103 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4104 return [super webView:sender didFinishLoadForFrame:frame];
4107 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4108 [window setValue:package_ forKey:@"package"];
4109 [super webView:sender didClearWindowObject:window forFrame:frame];
4113 - (void) _rightButtonClicked {
4114 /*[super _rightButtonClicked];
4117 int count = [buttons_ count];
4118 _assert(count != 0);
4121 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4123 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4124 [buttons addObjectsFromArray:buttons_];
4125 [buttons addObject:@"Cancel"];
4127 [delegate_ slideUp:[[[UIActionSheet alloc]
4130 defaultButtonIndex:2
4138 - (id) _rightButtonTitle {
4139 int count = [buttons_ count];
4140 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4143 - (NSString *) backButtonTitle {
4147 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4148 if ((self = [super initWithBook:book]) != nil) {
4149 database_ = database;
4150 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4154 - (void) setPackage:(Package *)package {
4155 if (package_ != nil) {
4156 [package_ autorelease];
4165 [buttons_ removeAllObjects];
4167 if (package != nil) {
4168 package_ = [package retain];
4169 name_ = [[package id] retain];
4171 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4173 if ([package_ source] == nil);
4174 else if ([package_ upgradableAndEssential:NO])
4175 [buttons_ addObject:@"Upgrade"];
4176 else if ([package_ installed] == nil)
4177 [buttons_ addObject:@"Install"];
4179 [buttons_ addObject:@"Reinstall"];
4180 if ([package_ installed] != nil)
4181 [buttons_ addObject:@"Remove"];
4189 - (void) reloadData {
4190 [self setPackage:[database_ packageWithName:name_]];
4191 [self reloadButtons];
4196 /* Package Table {{{ */
4197 @interface PackageTable : RVPage {
4198 _transient Database *database_;
4200 NSMutableArray *packages_;
4201 NSMutableArray *sections_;
4202 UISectionList *list_;
4205 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4207 - (void) setDelegate:(id)delegate;
4209 - (void) reloadData;
4210 - (void) resetCursor;
4212 - (UISectionList *) list;
4214 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4218 @implementation PackageTable
4221 [list_ setDataSource:nil];
4224 [packages_ release];
4225 [sections_ release];
4230 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4231 return [sections_ count];
4234 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4235 return [[sections_ objectAtIndex:section] name];
4238 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4239 return [[sections_ objectAtIndex:section] row];
4242 - (int) numberOfRowsInTable:(UITable *)table {
4243 return [packages_ count];
4246 - (float) table:(UITable *)table heightForRow:(int)row {
4247 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4250 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4252 reusing = [[[PackageCell alloc] init] autorelease];
4253 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4257 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4261 - (void) tableRowSelected:(NSNotification *)notification {
4262 int row = [[notification object] selectedRow];
4266 Package *package = [packages_ objectAtIndex:row];
4267 package = [database_ packageWithName:[package id]];
4268 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4269 [view setPackage:package];
4270 [view setDelegate:delegate_];
4271 [book_ pushPage:view];
4274 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4275 if ((self = [super initWithBook:book]) != nil) {
4276 database_ = database;
4277 title_ = [title retain];
4279 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4280 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4282 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4283 [list_ setDataSource:self];
4285 UITableColumn *column = [[[UITableColumn alloc]
4286 initWithTitle:@"Name"
4288 width:[self frame].size.width
4291 UITable *table = [list_ table];
4292 [table setSeparatorStyle:1];
4293 [table addTableColumn:column];
4294 [table setDelegate:self];
4295 [table setReusesTableCells:YES];
4297 [self addSubview:list_];
4299 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4300 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4304 - (void) setDelegate:(id)delegate {
4305 delegate_ = delegate;
4308 - (bool) hasPackage:(Package *)package {
4312 - (void) reloadData {
4313 NSArray *packages = [database_ packages];
4315 [packages_ removeAllObjects];
4316 [sections_ removeAllObjects];
4318 _profile(PackageTable$reloadData$Filter)
4319 for (Package *package in packages)
4320 if ([self hasPackage:package])
4321 [packages_ addObject:package];
4324 Section *section = nil;
4326 _profile(PackageTable$reloadData$Section)
4327 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4331 _profile(PackageTable$reloadData$Section$Package)
4332 package = [packages_ objectAtIndex:offset];
4333 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 _profile(PackageTable$reloadData$Section$Add)
4342 [sections_ addObject:section];
4346 [section addToCount];
4350 _profile(PackageTable$reloadData$List)
4355 - (NSString *) title {
4359 - (void) resetViewAnimated:(BOOL)animated {
4360 [list_ resetViewAnimated:animated];
4363 - (void) resetCursor {
4364 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4367 - (UISectionList *) list {
4371 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4372 [list_ setShouldHideHeaderInShortLists:hide];
4377 /* Filtered Package Table {{{ */
4378 @interface FilteredPackageTable : PackageTable {
4383 - (void) setObject:(id)object;
4385 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4389 @implementation FilteredPackageTable
4397 - (void) setObject:(id)object {
4403 object_ = [object retain];
4406 - (bool) hasPackage:(Package *)package {
4407 return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
4410 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4411 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4413 object_ = object == nil ? nil : [object retain];
4422 /* Add Source View {{{ */
4423 @interface AddSourceView : RVPage {
4424 _transient Database *database_;
4427 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4431 @implementation AddSourceView
4433 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4434 if ((self = [super initWithBook:book]) != nil) {
4435 database_ = database;
4441 /* Source Cell {{{ */
4442 @interface SourceCell : UITableCell {
4445 NSString *description_;
4451 - (SourceCell *) initWithSource:(Source *)source;
4455 @implementation SourceCell
4460 [description_ release];
4465 - (SourceCell *) initWithSource:(Source *)source {
4466 if ((self = [super init]) != nil) {
4468 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4470 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4471 icon_ = [icon_ retain];
4473 origin_ = [[source name] retain];
4474 label_ = [[source uri] retain];
4475 description_ = [[source description] retain];
4479 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4481 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4488 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4492 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4496 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4498 [super drawContentInRect:rect selected:selected];
4503 /* Source Table {{{ */
4504 @interface SourceTable : RVPage {
4505 _transient Database *database_;
4506 UISectionList *list_;
4507 NSMutableArray *sources_;
4508 UIActionSheet *alert_;
4512 UIProgressHUD *hud_;
4515 //NSURLConnection *installer_;
4516 NSURLConnection *trivial_bz2_;
4517 NSURLConnection *trivial_gz_;
4518 //NSURLConnection *automatic_;
4523 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4527 @implementation SourceTable
4529 - (void) _deallocConnection:(NSURLConnection *)connection {
4530 if (connection != nil) {
4531 [connection cancel];
4532 //[connection setDelegate:nil];
4533 [connection release];
4538 [[list_ table] setDelegate:nil];
4539 [list_ setDataSource:nil];
4548 //[self _deallocConnection:installer_];
4549 [self _deallocConnection:trivial_gz_];
4550 [self _deallocConnection:trivial_bz2_];
4551 //[self _deallocConnection:automatic_];
4558 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4559 return offset_ == 0 ? 1 : 2;
4562 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4563 switch (section + (offset_ == 0 ? 1 : 0)) {
4564 case 0: return @"Entered by User";
4565 case 1: return @"Installed by Packages";
4573 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4574 switch (section + (offset_ == 0 ? 1 : 0)) {
4576 case 1: return offset_;
4584 - (int) numberOfRowsInTable:(UITable *)table {
4585 return [sources_ count];
4588 - (float) table:(UITable *)table heightForRow:(int)row {
4589 Source *source = [sources_ objectAtIndex:row];
4590 return [source description] == nil ? 56 : 73;
4593 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4594 Source *source = [sources_ objectAtIndex:row];
4595 // XXX: weird warning, stupid selectors ;P
4596 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4599 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4603 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4607 - (void) tableRowSelected:(NSNotification*)notification {
4608 UITable *table([list_ table]);
4609 int row([table selectedRow]);
4613 Source *source = [sources_ objectAtIndex:row];
4615 PackageTable *packages = [[[FilteredPackageTable alloc]
4618 title:[source label]
4619 filter:@selector(isVisibleInSource:)
4623 [packages setDelegate:delegate_];
4625 [book_ pushPage:packages];
4628 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4629 Source *source = [sources_ objectAtIndex:row];
4630 return [source record] != nil;
4633 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4634 [[list_ table] setDeleteConfirmationRow:row];
4637 - (void) table:(UITable *)table deleteRow:(int)row {
4638 Source *source = [sources_ objectAtIndex:row];
4639 [Sources_ removeObjectForKey:[source key]];
4640 [delegate_ syncData];
4643 - (void) _endConnection:(NSURLConnection *)connection {
4644 NSURLConnection **field = NULL;
4645 if (connection == trivial_bz2_)
4646 field = &trivial_bz2_;
4647 else if (connection == trivial_gz_)
4648 field = &trivial_gz_;
4649 _assert(field != NULL);
4650 [connection release];
4654 trivial_bz2_ == nil &&
4657 [delegate_ setStatusBarShowsProgress:NO];
4658 [delegate_ removeProgressHUD:hud_];
4664 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4667 @"./", @"Distribution",
4668 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4670 [delegate_ syncData];
4671 } else if (error_ != nil) {
4672 UIActionSheet *sheet = [[[UIActionSheet alloc]
4673 initWithTitle:@"Verification Error"
4674 buttons:[NSArray arrayWithObjects:@"OK", nil]
4675 defaultButtonIndex:0
4680 [sheet setBodyText:[error_ localizedDescription]];
4681 [sheet popupAlertAnimated:YES];
4683 UIActionSheet *sheet = [[[UIActionSheet alloc]
4684 initWithTitle:@"Did not Find Repository"
4685 buttons:[NSArray arrayWithObjects:@"OK", nil]
4686 defaultButtonIndex:0
4691 [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."];
4692 [sheet popupAlertAnimated:YES];
4698 if (error_ != nil) {
4705 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4706 switch ([response statusCode]) {
4712 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4713 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4715 error_ = [error retain];
4716 [self _endConnection:connection];
4719 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4720 [self _endConnection:connection];
4723 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4724 NSMutableURLRequest *request = [NSMutableURLRequest
4725 requestWithURL:[NSURL URLWithString:href]
4726 cachePolicy:NSURLRequestUseProtocolCachePolicy
4727 timeoutInterval:20.0
4730 [request setHTTPMethod:method];
4732 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4735 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4736 NSString *context([sheet context]);
4738 if ([context isEqualToString:@"source"]) {
4741 NSString *href = [[sheet textField] text];
4743 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4745 if (![href hasSuffix:@"/"])
4746 href_ = [href stringByAppendingString:@"/"];
4749 href_ = [href_ retain];
4751 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4752 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4753 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4757 hud_ = [[delegate_ addProgressHUD] retain];
4758 [hud_ setText:@"Verifying URL"];
4769 } else if ([context isEqualToString:@"trivial"])
4771 else if ([context isEqualToString:@"urlerror"])
4775 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4776 if ((self = [super initWithBook:book]) != nil) {
4777 database_ = database;
4778 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4780 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4781 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4782 [list_ setShouldHideHeaderInShortLists:NO];
4784 [self addSubview:list_];
4785 [list_ setDataSource:self];
4787 UITableColumn *column = [[UITableColumn alloc]
4788 initWithTitle:@"Name"
4790 width:[self frame].size.width
4793 UITable *table = [list_ table];
4794 [table setSeparatorStyle:1];
4795 [table addTableColumn:column];
4796 [table setDelegate:self];
4800 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4801 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4805 - (void) reloadData {
4807 _assert(list.ReadMainList());
4809 [sources_ removeAllObjects];
4810 [sources_ addObjectsFromArray:[database_ sources]];
4812 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4815 int count = [sources_ count];
4816 for (offset_ = 0; offset_ != count; ++offset_) {
4817 Source *source = [sources_ objectAtIndex:offset_];
4818 if ([source record] == nil)
4825 - (void) resetViewAnimated:(BOOL)animated {
4826 [list_ resetViewAnimated:animated];
4829 - (void) _leftButtonClicked {
4830 /*[book_ pushPage:[[[AddSourceView alloc]
4835 UIActionSheet *sheet = [[[UIActionSheet alloc]
4836 initWithTitle:@"Enter Cydia/APT URL"
4837 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4838 defaultButtonIndex:0
4843 [sheet setNumberOfRows:1];
4845 [sheet addTextFieldWithValue:@"http://" label:@""];
4847 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4848 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4849 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4850 [traits setKeyboardType:UIKeyboardTypeURL];
4851 // XXX: UIReturnKeyDone
4852 [traits setReturnKeyType:UIReturnKeyNext];
4854 [sheet popupAlertAnimated:YES];
4857 - (void) _rightButtonClicked {
4858 UITable *table = [list_ table];
4859 BOOL editing = [table isRowDeletionEnabled];
4860 [table enableRowDeletion:!editing animated:YES];
4861 [book_ reloadButtonsForPage:self];
4864 - (NSString *) title {
4868 - (NSString *) leftButtonTitle {
4869 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4872 - (id) rightButtonTitle {
4873 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4876 - (UINavigationButtonStyle) rightButtonStyle {
4877 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4883 /* Installed View {{{ */
4884 @interface InstalledView : RVPage {
4885 _transient Database *database_;
4886 FilteredPackageTable *packages_;
4890 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4894 @implementation InstalledView
4897 [packages_ release];
4901 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4902 if ((self = [super initWithBook:book]) != nil) {
4903 database_ = database;
4905 packages_ = [[FilteredPackageTable alloc]
4909 filter:@selector(isInstalledAndVisible:)
4910 with:[NSNumber numberWithBool:YES]
4913 [self addSubview:packages_];
4915 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4916 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4920 - (void) resetViewAnimated:(BOOL)animated {
4921 [packages_ resetViewAnimated:animated];
4924 - (void) reloadData {
4925 [packages_ reloadData];
4928 - (void) _rightButtonClicked {
4929 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4930 [packages_ reloadData];
4932 [book_ reloadButtonsForPage:self];
4935 - (NSString *) title {
4936 return @"Installed";
4939 - (NSString *) backButtonTitle {
4943 - (id) rightButtonTitle {
4944 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4947 - (UINavigationButtonStyle) rightButtonStyle {
4948 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4951 - (void) setDelegate:(id)delegate {
4952 [super setDelegate:delegate];
4953 [packages_ setDelegate:delegate];
4960 @interface HomeView : BrowserView {
4965 @implementation HomeView
4967 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4968 NSString *context([sheet context]);
4970 if ([context isEqualToString:@"about"])
4973 [super alertSheet:sheet buttonClicked:button];
4976 - (void) _leftButtonClicked {
4977 UIActionSheet *sheet = [[[UIActionSheet alloc]
4978 initWithTitle:@"About Cydia Installer"
4979 buttons:[NSArray arrayWithObjects:@"Close", nil]
4980 defaultButtonIndex:0
4986 @"Copyright (C) 2008\n"
4987 "Jay Freeman (saurik)\n"
4988 "saurik@saurik.com\n"
4989 "http://www.saurik.com/\n"
4992 "http://www.theokorigroup.com/\n"
4994 "College of Creative Studies,\n"
4995 "University of California,\n"
4997 "http://www.ccs.ucsb.edu/"
5000 [sheet popupAlertAnimated:YES];
5003 - (NSString *) leftButtonTitle {
5009 /* Manage View {{{ */
5010 @interface ManageView : BrowserView {
5015 @implementation ManageView
5017 - (NSString *) title {
5021 - (void) _leftButtonClicked {
5022 [delegate_ askForSettings];
5025 - (NSString *) leftButtonTitle {
5030 - (id) _rightButtonTitle {
5042 #include <BrowserView.m>
5044 /* Cydia Book {{{ */
5045 @interface CYBook : RVBook <
5048 _transient Database *database_;
5049 UINavigationBar *overlay_;
5050 UINavigationBar *underlay_;
5051 UIProgressIndicator *indicator_;
5052 UITextLabel *prompt_;
5053 UIProgressBar *progress_;
5054 UINavigationButton *cancel_;
5058 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5064 @implementation CYBook
5068 [indicator_ release];
5070 [progress_ release];
5075 - (NSString *) getTitleForPage:(RVPage *)page {
5076 return Simplify([super getTitleForPage:page]);
5084 [UIView beginAnimations:nil context:NULL];
5086 CGRect ovrframe = [overlay_ frame];
5087 ovrframe.origin.y = 0;
5088 [overlay_ setFrame:ovrframe];
5090 CGRect barframe = [navbar_ frame];
5091 barframe.origin.y += ovrframe.size.height;
5092 [navbar_ setFrame:barframe];
5094 CGRect trnframe = [transition_ frame];
5095 trnframe.origin.y += ovrframe.size.height;
5096 trnframe.size.height -= ovrframe.size.height;
5097 [transition_ setFrame:trnframe];
5099 [UIView endAnimations];
5101 [indicator_ startAnimation];
5102 [prompt_ setText:@"Updating Database"];
5103 [progress_ setProgress:0];
5106 [overlay_ addSubview:cancel_];
5109 detachNewThreadSelector:@selector(_update)
5118 [indicator_ stopAnimation];
5120 [UIView beginAnimations:nil context:NULL];
5122 CGRect ovrframe = [overlay_ frame];
5123 ovrframe.origin.y = -ovrframe.size.height;
5124 [overlay_ setFrame:ovrframe];
5126 CGRect barframe = [navbar_ frame];
5127 barframe.origin.y -= ovrframe.size.height;
5128 [navbar_ setFrame:barframe];
5130 CGRect trnframe = [transition_ frame];
5131 trnframe.origin.y -= ovrframe.size.height;
5132 trnframe.size.height += ovrframe.size.height;
5133 [transition_ setFrame:trnframe];
5135 [UIView commitAnimations];
5137 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5140 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5141 if ((self = [super initWithFrame:frame]) != nil) {
5142 database_ = database;
5144 CGRect ovrrect = [navbar_ bounds];
5145 ovrrect.size.height = [UINavigationBar defaultSize].height;
5146 ovrrect.origin.y = -ovrrect.size.height;
5148 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5149 [self addSubview:overlay_];
5151 ovrrect.origin.y = frame.size.height;
5152 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5153 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5154 [self addSubview:underlay_];
5156 [overlay_ setBarStyle:1];
5157 [underlay_ setBarStyle:1];
5159 int barstyle = [overlay_ _barStyle:NO];
5160 bool ugly = barstyle == 0;
5162 UIProgressIndicatorStyle style = ugly ?
5163 UIProgressIndicatorStyleMediumBrown :
5164 UIProgressIndicatorStyleMediumWhite;
5166 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5167 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5168 CGRect indrect = {{indoffset, indoffset}, indsize};
5170 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5171 [indicator_ setStyle:style];
5172 [overlay_ addSubview:indicator_];
5174 CGSize prmsize = {215, indsize.height + 4};
5177 indoffset * 2 + indsize.width,
5181 unsigned(ovrrect.size.height - prmsize.height) / 2
5184 UIFont *font = [UIFont systemFontOfSize:15];
5186 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5188 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5189 [prompt_ setBackgroundColor:[UIColor clearColor]];
5190 [prompt_ setFont:font];
5192 [overlay_ addSubview:prompt_];
5194 CGSize prgsize = {75, 100};
5197 ovrrect.size.width - prgsize.width - 10,
5198 (ovrrect.size.height - prgsize.height) / 2
5201 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5202 [progress_ setStyle:0];
5203 [overlay_ addSubview:progress_];
5205 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5206 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5208 CGRect frame = [cancel_ frame];
5209 frame.size.width = 65;
5210 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5211 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5212 [cancel_ setFrame:frame];
5214 [cancel_ setBarStyle:barstyle];
5218 - (void) _onCancel {
5220 [cancel_ removeFromSuperview];
5223 - (void) _update { _pooled
5225 status.setDelegate(self);
5227 [database_ updateWithStatus:status];
5230 performSelectorOnMainThread:@selector(_update_)
5236 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5237 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5240 - (void) setProgressTitle:(NSString *)title {
5242 performSelectorOnMainThread:@selector(_setProgressTitle:)
5248 - (void) setProgressPercent:(float)percent {
5250 performSelectorOnMainThread:@selector(_setProgressPercent:)
5251 withObject:[NSNumber numberWithFloat:percent]
5256 - (void) startProgress {
5259 - (void) addProgressOutput:(NSString *)output {
5261 performSelectorOnMainThread:@selector(_addProgressOutput:)
5267 - (bool) isCancelling:(size_t)received {
5271 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5275 - (void) _setProgressTitle:(NSString *)title {
5276 [prompt_ setText:title];
5279 - (void) _setProgressPercent:(NSNumber *)percent {
5280 [progress_ setProgress:[percent floatValue]];
5283 - (void) _addProgressOutput:(NSString *)output {
5288 /* Cydia:// Protocol {{{ */
5289 @interface CydiaURLProtocol : NSURLProtocol {
5294 @implementation CydiaURLProtocol
5296 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5297 NSURL *url([request URL]);
5300 NSString *scheme([[url scheme] lowercaseString]);
5301 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5306 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5310 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5311 id<NSURLProtocolClient> client([self client]);
5313 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5315 NSData *data(UIImagePNGRepresentation(icon));
5317 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5318 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5319 [client URLProtocol:self didLoadData:data];
5320 [client URLProtocolDidFinishLoading:self];
5324 - (void) startLoading {
5325 id<NSURLProtocolClient> client([self client]);
5326 NSURLRequest *request([self request]);
5328 NSURL *url([request URL]);
5329 NSString *href([url absoluteString]);
5331 NSString *path([href substringFromIndex:8]);
5332 NSRange slash([path rangeOfString:@"/"]);
5335 if (slash.location == NSNotFound) {
5339 command = [path substringToIndex:slash.location];
5340 path = [path substringFromIndex:(slash.location + 1)];
5343 Database *database([Database sharedInstance]);
5345 if ([command isEqualToString:@"package-icon"]) {
5348 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5349 Package *package([database packageWithName:path]);
5352 UIImage *icon([package icon]);
5353 [self _returnPNGWithImage:icon forRequest:request];
5354 } else if ([command isEqualToString:@"source-icon"]) {
5357 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5358 NSString *source(Simplify(path));
5359 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5361 icon = [UIImage applicationImageNamed:@"unknown.png"];
5362 [self _returnPNGWithImage:icon forRequest:request];
5363 } else if ([command isEqualToString:@"uikit-image"]) {
5366 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5367 UIImage *icon(_UIImageWithName(path));
5368 [self _returnPNGWithImage:icon forRequest:request];
5369 } else if ([command isEqualToString:@"section-icon"]) {
5372 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5373 NSString *section(Simplify(path));
5374 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5376 icon = [UIImage applicationImageNamed:@"unknown.png"];
5377 [self _returnPNGWithImage:icon forRequest:request];
5379 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5383 - (void) stopLoading {
5389 /* Sections View {{{ */
5390 @interface SectionsView : RVPage {
5391 _transient Database *database_;
5392 NSMutableArray *sections_;
5393 NSMutableArray *filtered_;
5394 UITransitionView *transition_;
5400 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5401 - (void) reloadData;
5406 @implementation SectionsView
5409 [list_ setDataSource:nil];
5410 [list_ setDelegate:nil];
5412 [sections_ release];
5413 [filtered_ release];
5414 [transition_ release];
5416 [accessory_ release];
5420 - (int) numberOfRowsInTable:(UITable *)table {
5421 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5424 - (float) table:(UITable *)table heightForRow:(int)row {
5428 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5430 reusing = [[[SectionCell alloc] init] autorelease];
5431 [(SectionCell *)reusing setSection:(editing_ ?
5432 [sections_ objectAtIndex:row] :
5433 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5434 ) editing:editing_];
5438 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5442 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5446 - (void) tableRowSelected:(NSNotification *)notification {
5447 int row = [[notification object] selectedRow];
5458 title = @"All Packages";
5460 section = [filtered_ objectAtIndex:(row - 1)];
5461 name = [section name];
5467 title = @"(No Section)";
5471 PackageTable *table = [[[FilteredPackageTable alloc]
5475 filter:@selector(isVisiblyUninstalledInSection:)
5479 [table setDelegate:delegate_];
5481 [book_ pushPage:table];
5484 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5485 if ((self = [super initWithBook:book]) != nil) {
5486 database_ = database;
5488 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5489 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5491 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5492 [self addSubview:transition_];
5494 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5495 [transition_ transition:0 toView:list_];
5497 UITableColumn *column = [[[UITableColumn alloc]
5498 initWithTitle:@"Name"
5500 width:[self frame].size.width
5503 [list_ setDataSource:self];
5504 [list_ setSeparatorStyle:1];
5505 [list_ addTableColumn:column];
5506 [list_ setDelegate:self];
5507 [list_ setReusesTableCells:YES];
5511 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5512 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5516 - (void) reloadData {
5517 NSArray *packages = [database_ packages];
5519 [sections_ removeAllObjects];
5520 [filtered_ removeAllObjects];
5522 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5523 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5526 for (Package *package in packages) {
5527 NSString *name([package section]);
5530 Section *section([sections objectForKey:name]);
5531 if (section == nil) {
5532 section = [[[Section alloc] initWithName:name] autorelease];
5533 [sections setObject:section forKey:name];
5537 if ([package valid] && [package installed] == nil && [package visible])
5538 [filtered addObject:package];
5542 [sections_ addObjectsFromArray:[sections allValues]];
5543 [sections_ sortUsingSelector:@selector(compareByName:)];
5546 [filtered sortUsingSelector:@selector(compareBySection:)];
5549 Section *section = nil;
5550 for (Package *package in filtered) {
5551 NSString *name = [package section];
5553 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5554 section = name == nil ?
5555 [[[Section alloc] initWithName:nil] autorelease] :
5556 [sections objectForKey:name];
5557 [filtered_ addObject:section];
5560 [section addToCount];
5568 - (void) resetView {
5570 [self _rightButtonClicked];
5573 - (void) resetViewAnimated:(BOOL)animated {
5574 [list_ resetViewAnimated:animated];
5577 - (void) _rightButtonClicked {
5578 if ((editing_ = !editing_))
5581 [delegate_ updateData];
5582 [book_ reloadTitleForPage:self];
5583 [book_ reloadButtonsForPage:self];
5586 - (NSString *) title {
5587 return editing_ ? @"Section Visibility" : @"Install by Section";
5590 - (NSString *) backButtonTitle {
5594 - (id) rightButtonTitle {
5595 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5598 - (UINavigationButtonStyle) rightButtonStyle {
5599 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5602 - (UIView *) accessoryView {
5608 /* Changes View {{{ */
5609 @interface ChangesView : RVPage {
5610 _transient Database *database_;
5611 NSMutableArray *packages_;
5612 NSMutableArray *sections_;
5613 UISectionList *list_;
5617 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5618 - (void) reloadData;
5622 @implementation ChangesView
5625 [[list_ table] setDelegate:nil];
5626 [list_ setDataSource:nil];
5628 [packages_ release];
5629 [sections_ release];
5634 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5635 return [sections_ count];
5638 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5639 return [[sections_ objectAtIndex:section] name];
5642 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5643 return [[sections_ objectAtIndex:section] row];
5646 - (int) numberOfRowsInTable:(UITable *)table {
5647 return [packages_ count];
5650 - (float) table:(UITable *)table heightForRow:(int)row {
5651 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5654 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5656 reusing = [[[PackageCell alloc] init] autorelease];
5657 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5661 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5665 - (void) tableRowSelected:(NSNotification *)notification {
5666 int row = [[notification object] selectedRow];
5669 Package *package = [packages_ objectAtIndex:row];
5670 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5671 [view setDelegate:delegate_];
5672 [view setPackage:package];
5673 [book_ pushPage:view];
5676 - (void) _leftButtonClicked {
5677 [(CYBook *)book_ update];
5678 [self reloadButtons];
5681 - (void) _rightButtonClicked {
5682 [delegate_ distUpgrade];
5685 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5686 if ((self = [super initWithBook:book]) != nil) {
5687 database_ = database;
5689 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5690 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5692 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5693 [self addSubview:list_];
5695 [list_ setShouldHideHeaderInShortLists:NO];
5696 [list_ setDataSource:self];
5697 //[list_ setSectionListStyle:1];
5699 UITableColumn *column = [[[UITableColumn alloc]
5700 initWithTitle:@"Name"
5702 width:[self frame].size.width
5705 UITable *table = [list_ table];
5706 [table setSeparatorStyle:1];
5707 [table addTableColumn:column];
5708 [table setDelegate:self];
5709 [table setReusesTableCells:YES];
5713 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5714 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5718 - (void) reloadData {
5719 NSArray *packages = [database_ packages];
5721 [packages_ removeAllObjects];
5722 [sections_ removeAllObjects];
5725 for (Package *package in packages)
5727 [package installed] == nil && [package valid] && [package visible] ||
5728 [package upgradableAndEssential:YES]
5730 [packages_ addObject:package];
5733 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5736 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5737 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5738 Section *section = nil;
5742 bool unseens = false;
5744 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5747 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5748 Package *package = [packages_ objectAtIndex:offset];
5750 if (![package upgradableAndEssential:YES]) {
5752 NSDate *seen = [package seen];
5754 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5757 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5758 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5759 [sections_ addObject:section];
5763 [section addToCount];
5764 } else if ([package ignored])
5765 [ignored addToCount];
5768 [upgradable addToCount];
5773 CFRelease(formatter);
5776 Section *last = [sections_ lastObject];
5777 size_t count = [last count];
5778 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5779 [sections_ removeLastObject];
5782 if ([ignored count] != 0)
5783 [sections_ insertObject:ignored atIndex:0];
5785 [sections_ insertObject:upgradable atIndex:0];
5788 [self reloadButtons];
5791 - (void) resetViewAnimated:(BOOL)animated {
5792 [list_ resetViewAnimated:animated];
5795 - (NSString *) leftButtonTitle {
5796 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5799 - (id) rightButtonTitle {
5800 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5803 - (NSString *) title {
5809 /* Search View {{{ */
5810 @protocol SearchViewDelegate
5811 - (void) showKeyboard:(BOOL)show;
5814 @interface SearchView : RVPage {
5816 UISearchField *field_;
5817 UITransitionView *transition_;
5818 FilteredPackageTable *table_;
5819 UIPreferencesTable *advanced_;
5825 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5826 - (void) reloadData;
5830 @implementation SearchView
5833 [field_ setDelegate:nil];
5835 [accessory_ release];
5837 [transition_ release];
5839 [advanced_ release];
5844 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5848 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5850 case 0: return @"Advanced Search (Coming Soon!)";
5852 default: _assert(false);
5856 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5860 default: _assert(false);
5864 - (void) _showKeyboard:(BOOL)show {
5865 CGSize keysize = [UIKeyboard defaultSize];
5866 CGRect keydown = [book_ pageBounds];
5867 CGRect keyup = keydown;
5868 keyup.size.height -= keysize.height - ButtonBarHeight_;
5870 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5872 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5873 [animation setSignificantRectFields:8];
5876 [animation setStartFrame:keydown];
5877 [animation setEndFrame:keyup];
5879 [animation setStartFrame:keyup];
5880 [animation setEndFrame:keydown];
5883 UIAnimator *animator = [UIAnimator sharedAnimator];
5886 addAnimations:[NSArray arrayWithObjects:animation, nil]
5887 withDuration:(KeyboardTime_ - delay)
5892 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5894 [delegate_ showKeyboard:show];
5897 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5898 [self _showKeyboard:YES];
5901 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5902 [self _showKeyboard:NO];
5905 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5907 NSString *text([field_ text]);
5908 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5914 - (void) textFieldClearButtonPressed:(UITextField *)field {
5918 - (void) keyboardInputShouldDelete:(id)input {
5922 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5923 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5927 [field_ resignFirstResponder];
5932 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5933 if ((self = [super initWithBook:book]) != nil) {
5934 CGRect pageBounds = [book_ pageBounds];
5936 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5937 [self addSubview:transition_];
5939 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5941 [advanced_ setReusesTableCells:YES];
5942 [advanced_ setDataSource:self];
5943 [advanced_ reloadData];
5945 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5946 CGColor dimmed(space_, 0, 0, 0, 0.5);
5947 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5949 table_ = [[FilteredPackageTable alloc]
5953 filter:@selector(isUnfilteredAndSearchedForBy:)
5957 [table_ setShouldHideHeaderInShortLists:NO];
5958 [transition_ transition:0 toView:table_];
5967 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5974 [self bounds].size.width - area.origin.x - 18;
5976 area.size.height = [UISearchField defaultHeight];
5978 field_ = [[UISearchField alloc] initWithFrame:area];
5980 UIFont *font = [UIFont systemFontOfSize:16];
5981 [field_ setFont:font];
5983 [field_ setPlaceholder:@"Package Names & Descriptions"];
5984 [field_ setDelegate:self];
5986 [field_ setPaddingTop:5];
5988 UITextInputTraits *traits([field_ textInputTraits]);
5989 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5990 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5991 [traits setReturnKeyType:UIReturnKeySearch];
5993 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5995 accessory_ = [[UIView alloc] initWithFrame:accrect];
5996 [accessory_ addSubview:field_];
5998 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5999 [configure setShowPressFeedback:YES];
6000 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6001 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6002 [accessory_ addSubview:configure];*/
6004 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6005 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6011 LKAnimation *animation = [LKTransition animation];
6012 [animation setType:@"oglFlip"];
6013 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6014 [animation setFillMode:@"extended"];
6015 [animation setTransitionFlags:3];
6016 [animation setDuration:10];
6017 [animation setSpeed:0.35];
6018 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6019 [[transition_ _layer] addAnimation:animation forKey:0];
6020 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6021 flipped_ = !flipped_;
6025 - (void) configurePushed {
6026 [field_ resignFirstResponder];
6030 - (void) resetViewAnimated:(BOOL)animated {
6033 [table_ resetViewAnimated:animated];
6036 - (void) _reloadData {
6039 - (void) reloadData {
6042 [table_ setObject:[field_ text]];
6043 _profile(SearchView$reloadData)
6044 [table_ reloadData];
6047 [table_ resetCursor];
6050 - (UIView *) accessoryView {
6054 - (NSString *) title {
6058 - (NSString *) backButtonTitle {
6062 - (void) setDelegate:(id)delegate {
6063 [table_ setDelegate:delegate];
6064 [super setDelegate:delegate];
6070 @interface SettingsView : RVPage {
6071 _transient Database *database_;
6074 UIPreferencesTable *table_;
6075 _UISwitchSlider *subscribedSwitch_;
6076 _UISwitchSlider *ignoredSwitch_;
6077 UIPreferencesControlTableCell *subscribedCell_;
6078 UIPreferencesControlTableCell *ignoredCell_;
6081 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6085 @implementation SettingsView
6088 [table_ setDataSource:nil];
6091 if (package_ != nil)
6094 [subscribedSwitch_ release];
6095 [ignoredSwitch_ release];
6096 [subscribedCell_ release];
6097 [ignoredCell_ release];
6101 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6102 if (package_ == nil)
6108 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6109 if (package_ == nil)
6116 default: _assert(false);
6122 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6123 if (package_ == nil)
6130 default: _assert(false);
6136 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6137 if (package_ == nil)
6144 default: _assert(false);
6150 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6151 if (package_ == nil)
6154 _UISwitchSlider *slider([cell control]);
6155 BOOL value([slider value] != 0);
6156 NSMutableDictionary *metadata([package_ metadata]);
6159 if (NSNumber *number = [metadata objectForKey:key])
6160 before = [number boolValue];
6164 if (value != before) {
6165 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6167 [delegate_ updateData];
6171 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6172 [self onSomething:cell withKey:@"IsSubscribed"];
6175 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6176 [self onSomething:cell withKey:@"IsIgnored"];
6179 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6180 if (package_ == nil)
6184 case 0: switch (row) {
6186 return subscribedCell_;
6188 return ignoredCell_;
6189 default: _assert(false);
6192 case 1: switch (row) {
6194 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6195 [cell setShowSelection:NO];
6196 [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."];
6200 default: _assert(false);
6203 default: _assert(false);
6209 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6210 if ((self = [super initWithBook:book])) {
6211 database_ = database;
6212 name_ = [package retain];
6214 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6215 [self addSubview:table_];
6217 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6218 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6220 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6221 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6223 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6224 [subscribedCell_ setShowSelection:NO];
6225 [subscribedCell_ setTitle:@"Show All Changes"];
6226 [subscribedCell_ setControl:subscribedSwitch_];
6228 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6229 [ignoredCell_ setShowSelection:NO];
6230 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6231 [ignoredCell_ setControl:ignoredSwitch_];
6233 [table_ setDataSource:self];
6238 - (void) resetViewAnimated:(BOOL)animated {
6239 [table_ resetViewAnimated:animated];
6242 - (void) reloadData {
6243 if (package_ != nil)
6244 [package_ autorelease];
6245 package_ = [database_ packageWithName:name_];
6246 if (package_ != nil) {
6248 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6249 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6252 [table_ reloadData];
6255 - (NSString *) title {
6261 /* Signature View {{{ */
6262 @interface SignatureView : BrowserView {
6263 _transient Database *database_;
6267 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6271 @implementation SignatureView
6278 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6280 [super webView:sender didClearWindowObject:window forFrame:frame];
6283 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6284 if ((self = [super initWithBook:book]) != nil) {
6285 database_ = database;
6286 package_ = [package retain];
6291 - (void) resetViewAnimated:(BOOL)animated {
6294 - (void) reloadData {
6295 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6301 @interface Cydia : UIApplication <
6302 ConfirmationViewDelegate,
6303 ProgressViewDelegate,
6312 UIToolbar *buttonbar_;
6316 NSMutableArray *essential_;
6317 NSMutableArray *broken_;
6319 Database *database_;
6320 ProgressView *progress_;
6324 UIKeyboard *keyboard_;
6325 UIProgressHUD *hud_;
6327 SectionsView *sections_;
6328 ChangesView *changes_;
6329 ManageView *manage_;
6330 SearchView *search_;
6335 @implementation Cydia
6338 if ([broken_ count] != 0) {
6339 int count = [broken_ count];
6341 UIActionSheet *sheet = [[[UIActionSheet alloc]
6342 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6343 buttons:[NSArray arrayWithObjects:
6345 @"Ignore (Temporary)",
6347 defaultButtonIndex:0
6352 [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."];
6353 [sheet popupAlertAnimated:YES];
6354 } else if (!Ignored_ && [essential_ count] != 0) {
6355 int count = [essential_ count];
6357 UIActionSheet *sheet = [[[UIActionSheet alloc]
6358 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6359 buttons:[NSArray arrayWithObjects:
6360 @"Upgrade Essential",
6361 @"Complete Upgrade",
6362 @"Ignore (Temporary)",
6364 defaultButtonIndex:0
6369 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6370 [sheet popupAlertAnimated:YES];
6374 - (void) _reloadData {
6377 UIProgressHUD *hud([self addProgressHUD]);
6378 [hud setText:@"Reloading Data"];
6380 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6383 [self removeProgressHUD:hud];
6387 [essential_ removeAllObjects];
6388 [broken_ removeAllObjects];
6390 NSArray *packages = [database_ packages];
6391 for (Package *package in packages) {
6393 [broken_ addObject:package];
6394 if ([package upgradableAndEssential:NO]) {
6395 if ([package essential])
6396 [essential_ addObject:package];
6402 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6403 [buttonbar_ setBadgeValue:badge forButton:3];
6404 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6405 [buttonbar_ setBadgeAnimated:YES forButton:3];
6406 [self setApplicationBadge:badge];
6408 [buttonbar_ setBadgeValue:nil forButton:3];
6409 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6410 [buttonbar_ setBadgeAnimated:NO forButton:3];
6411 [self removeApplicationBadge];
6416 // XXX: what is this line of code for?
6417 if ([packages count] == 0);
6418 else if (Loaded_) loaded:
6423 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6424 NSTimeInterval interval([update timeIntervalSinceNow]);
6425 if (interval <= 0 && interval > -600)
6433 - (void) _saveConfig {
6436 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6442 - (void) updateData {
6445 /* XXX: this is just stupid */
6446 if (tag_ != 2 && sections_ != nil)
6447 [sections_ reloadData];
6448 if (tag_ != 3 && changes_ != nil)
6449 [changes_ reloadData];
6450 if (tag_ != 5 && search_ != nil)
6451 [search_ reloadData];
6461 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6462 _assert(file != NULL);
6464 NSArray *keys = [Sources_ allKeys];
6466 for (NSString *key in keys) {
6467 NSDictionary *source = [Sources_ objectForKey:key];
6469 fprintf(file, "%s %s %s\n",
6470 [[source objectForKey:@"Type"] UTF8String],
6471 [[source objectForKey:@"URI"] UTF8String],
6472 [[source objectForKey:@"Distribution"] UTF8String]
6481 detachNewThreadSelector:@selector(update_)
6484 title:@"Updating Sources"
6488 - (void) reloadData {
6489 @synchronized (self) {
6490 if (confirm_ == nil)
6496 pkgProblemResolver *resolver = [database_ resolver];
6498 resolver->InstallProtect();
6499 if (!resolver->Resolve(true))
6503 - (void) popUpBook:(RVBook *)book {
6504 [underlay_ popSubview:book];
6507 - (CGRect) popUpBounds {
6508 return [underlay_ bounds];
6512 [database_ prepare];
6514 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6515 [confirm_ setDelegate:self];
6517 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6518 [page setDelegate:self];
6520 [confirm_ setPage:page];
6521 [self popUpBook:confirm_];
6524 - (void) installPackage:(Package *)package {
6525 @synchronized (self) {
6532 - (void) removePackage:(Package *)package {
6533 @synchronized (self) {
6540 - (void) distUpgrade {
6541 @synchronized (self) {
6542 [database_ upgrade];
6548 @synchronized (self) {
6550 if (confirm_ != nil) {
6558 [overlay_ removeFromSuperview];
6562 detachNewThreadSelector:@selector(perform)
6569 - (void) bootstrap_ {
6571 [database_ upgrade];
6572 [database_ prepare];
6573 [database_ perform];
6576 - (void) bootstrap {
6578 detachNewThreadSelector:@selector(bootstrap_)
6581 title:@"Bootstrap Install"
6585 - (void) progressViewIsComplete:(ProgressView *)progress {
6586 if (confirm_ != nil) {
6587 [underlay_ addSubview:overlay_];
6588 [confirm_ popFromSuperviewAnimated:NO];
6594 - (void) setPage:(RVPage *)page {
6595 [page resetViewAnimated:NO];
6596 [page setDelegate:self];
6597 [book_ setPage:page];
6600 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6601 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6602 [browser loadURL:url];
6606 - (void) _setHomePage {
6607 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6610 - (void) buttonBarItemTapped:(id)sender {
6611 unsigned tag = [sender tag];
6613 [book_ resetViewAnimated:YES];
6615 } else if (tag_ == 2 && tag != 2)
6616 [sections_ resetView];
6619 case 1: [self _setHomePage]; break;
6621 case 2: [self setPage:sections_]; break;
6622 case 3: [self setPage:changes_]; break;
6623 case 4: [self setPage:manage_]; break;
6624 case 5: [self setPage:search_]; break;
6626 default: _assert(false);
6632 - (void) applicationWillSuspend {
6634 [super applicationWillSuspend];
6637 - (void) askForSettings {
6638 UIActionSheet *role = [[[UIActionSheet alloc]
6639 initWithTitle:@"Who Are You?"
6640 buttons:[NSArray arrayWithObjects:
6641 @"User (Graphical Only)",
6642 @"Hacker (+ Command Line)",
6643 @"Developer (No Filters)",
6645 defaultButtonIndex:-1
6650 [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."];
6651 [role popupAlertAnimated:YES];
6656 [self setStatusBarShowsProgress:NO];
6657 [self removeProgressHUD:hud_];
6662 pid_t pid = ExecFork();
6664 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6665 perror("launchctl stop");
6672 [self askForSettings];
6677 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6679 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6680 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6681 0, 0, screenrect.size.width, screenrect.size.height - 48
6682 ) database:database_];
6684 [book_ setDelegate:self];
6686 [overlay_ addSubview:book_];
6688 NSArray *buttonitems = [NSArray arrayWithObjects:
6689 [NSDictionary dictionaryWithObjectsAndKeys:
6690 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6691 @"home-up.png", kUIButtonBarButtonInfo,
6692 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6693 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6694 self, kUIButtonBarButtonTarget,
6695 @"Home", kUIButtonBarButtonTitle,
6696 @"0", kUIButtonBarButtonType,
6699 [NSDictionary dictionaryWithObjectsAndKeys:
6700 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6701 @"install-up.png", kUIButtonBarButtonInfo,
6702 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6703 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6704 self, kUIButtonBarButtonTarget,
6705 @"Sections", kUIButtonBarButtonTitle,
6706 @"0", kUIButtonBarButtonType,
6709 [NSDictionary dictionaryWithObjectsAndKeys:
6710 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6711 @"changes-up.png", kUIButtonBarButtonInfo,
6712 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6713 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6714 self, kUIButtonBarButtonTarget,
6715 @"Changes", kUIButtonBarButtonTitle,
6716 @"0", kUIButtonBarButtonType,
6719 [NSDictionary dictionaryWithObjectsAndKeys:
6720 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6721 @"manage-up.png", kUIButtonBarButtonInfo,
6722 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6723 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6724 self, kUIButtonBarButtonTarget,
6725 @"Manage", kUIButtonBarButtonTitle,
6726 @"0", kUIButtonBarButtonType,
6729 [NSDictionary dictionaryWithObjectsAndKeys:
6730 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6731 @"search-up.png", kUIButtonBarButtonInfo,
6732 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6733 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6734 self, kUIButtonBarButtonTarget,
6735 @"Search", kUIButtonBarButtonTitle,
6736 @"0", kUIButtonBarButtonType,
6740 buttonbar_ = [[UIToolbar alloc]
6742 withFrame:CGRectMake(
6743 0, screenrect.size.height - ButtonBarHeight_,
6744 screenrect.size.width, ButtonBarHeight_
6746 withItemList:buttonitems
6749 [buttonbar_ setDelegate:self];
6750 [buttonbar_ setBarStyle:1];
6751 [buttonbar_ setButtonBarTrackingMode:2];
6753 int buttons[5] = {1, 2, 3, 4, 5};
6754 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6755 [buttonbar_ showButtonGroup:0 withDuration:0];
6757 for (int i = 0; i != 5; ++i)
6758 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6759 i * 64 + 2, 1, 60, ButtonBarHeight_
6762 [buttonbar_ showSelectionForButton:1];
6763 [overlay_ addSubview:buttonbar_];
6765 [UIKeyboard initImplementationNow];
6766 CGSize keysize = [UIKeyboard defaultSize];
6767 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6768 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6769 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6770 [overlay_ addSubview:keyboard_];
6773 [underlay_ addSubview:overlay_];
6777 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6778 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6779 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6781 manage_ = (ManageView *) [[self
6782 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6783 withClass:[ManageView class]
6791 [self _setHomePage];
6794 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6795 NSString *context([sheet context]);
6797 if ([context isEqualToString:@"missing"])
6799 else if ([context isEqualToString:@"fixhalf"]) {
6802 @synchronized (self) {
6803 for (Package *broken in broken_) {
6806 NSString *id = [broken id];
6807 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6808 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6809 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6810 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6819 [broken_ removeAllObjects];
6828 } else if ([context isEqualToString:@"role"]) {
6830 case 1: Role_ = @"User"; break;
6831 case 2: Role_ = @"Hacker"; break;
6832 case 3: Role_ = @"Developer"; break;
6839 bool reset = Settings_ != nil;
6841 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6845 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6855 } else if ([context isEqualToString:@"upgrade"]) {
6858 @synchronized (self) {
6859 for (Package *essential in essential_)
6860 [essential install];
6883 - (void) reorganize { _pooled
6884 system("/usr/libexec/cydia/free.sh");
6885 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6888 - (void) applicationSuspend:(__GSEvent *)event {
6889 if (hud_ == nil && ![progress_ isRunning])
6890 [super applicationSuspend:event];
6893 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6895 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6898 - (void) _setSuspended:(BOOL)value {
6900 [super _setSuspended:value];
6903 - (UIProgressHUD *) addProgressHUD {
6904 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
6905 [window_ setUserInteractionEnabled:NO];
6907 [progress_ addSubview:hud];
6911 - (void) removeProgressHUD:(UIProgressHUD *)hud {
6913 [hud removeFromSuperview];
6914 [window_ setUserInteractionEnabled:YES];
6917 - (void) openMailToURL:(NSURL *)url {
6918 // XXX: this makes me sad
6920 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6922 [UIApp openURL:url];// asPanel:YES];
6926 - (void) clearFirstResponder {
6927 if (id responder = [window_ firstResponder])
6928 [responder resignFirstResponder];
6931 - (RVPage *) pageForPackage:(NSString *)name {
6932 if (Package *package = [database_ packageWithName:name]) {
6933 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6934 [view setPackage:package];
6937 UIActionSheet *sheet = [[[UIActionSheet alloc]
6938 initWithTitle:@"Cannot Locate Package"
6939 buttons:[NSArray arrayWithObjects:@"Close", nil]
6940 defaultButtonIndex:0
6945 [sheet setBodyText:[NSString stringWithFormat:
6946 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6949 [sheet popupAlertAnimated:YES];
6954 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6958 NSString *scheme([[url scheme] lowercaseString]);
6959 if (![scheme isEqualToString:@"cydia"])
6961 NSString *path([url absoluteString]);
6962 if ([path length] < 8)
6964 path = [path substringFromIndex:8];
6965 if (![path hasPrefix:@"/"])
6966 path = [@"/" stringByAppendingString:path];
6968 if ([path isEqualToString:@"/add-source"])
6969 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6970 else if ([path isEqualToString:@"/storage"])
6971 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6972 else if ([path isEqualToString:@"/sources"])
6973 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6974 else if ([path isEqualToString:@"/packages"])
6975 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6976 else if ([path hasPrefix:@"/url/"])
6977 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6978 else if ([path hasPrefix:@"/launch/"])
6979 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6980 else if ([path hasPrefix:@"/package-settings/"])
6981 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6982 else if ([path hasPrefix:@"/package-signature/"])
6983 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6984 else if ([path hasPrefix:@"/package/"])
6985 return [self pageForPackage:[path substringFromIndex:9]];
6986 else if ([path hasPrefix:@"/files/"]) {
6987 NSString *name = [path substringFromIndex:7];
6989 if (Package *package = [database_ packageWithName:name]) {
6990 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6991 [files setPackage:package];
6999 - (void) applicationOpenURL:(NSURL *)url {
7000 [super applicationOpenURL:url];
7002 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7003 [self setPage:page];
7004 [buttonbar_ showSelectionForButton:tag];
7009 - (void) applicationDidFinishLaunching:(id)unused {
7011 Font12_ = [[UIFont systemFontOfSize:12] retain];
7012 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7013 Font14_ = [[UIFont systemFontOfSize:14] retain];
7014 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7015 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7017 _assert(pkgInitConfig(*_config));
7018 _assert(pkgInitSystem(*_config, _system));
7022 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7023 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7025 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7027 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7028 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7030 [window_ orderFront:self];
7031 [window_ makeKey:self];
7032 [window_ setHidden:NO];
7034 database_ = [Database sharedInstance];
7035 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7036 [database_ setDelegate:progress_];
7037 [window_ setContentView:progress_];
7039 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7040 [progress_ setContentView:underlay_];
7042 [progress_ resetView];
7045 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7046 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7047 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7048 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7049 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7050 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7051 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7053 [self setIdleTimerDisabled:YES];
7055 hud_ = [[self addProgressHUD] retain];
7056 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7058 [self setStatusBarShowsProgress:YES];
7061 detachNewThreadSelector:@selector(reorganize)
7069 - (void) showKeyboard:(BOOL)show {
7070 CGSize keysize = [UIKeyboard defaultSize];
7071 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7072 CGRect keyup = keydown;
7073 keyup.origin.y -= keysize.height;
7075 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7076 [animation setSignificantRectFields:2];
7079 [animation setStartFrame:keydown];
7080 [animation setEndFrame:keyup];
7081 [keyboard_ activate];
7083 [animation setStartFrame:keyup];
7084 [animation setEndFrame:keydown];
7085 [keyboard_ deactivate];
7088 [[UIAnimator sharedAnimator]
7089 addAnimations:[NSArray arrayWithObjects:animation, nil]
7090 withDuration:KeyboardTime_
7095 - (void) slideUp:(UIActionSheet *)alert {
7097 [alert presentSheetFromButtonBar:buttonbar_];
7099 [alert presentSheetInView:overlay_];
7104 void AddPreferences(NSString *plist) { _pooled
7105 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7106 _assert(settings != NULL);
7107 NSMutableArray *items = [settings objectForKey:@"items"];
7111 for (NSMutableDictionary *item in items) {
7112 NSString *label = [item objectForKey:@"label"];
7113 if (label != nil && [label isEqualToString:@"Cydia"]) {
7120 for (size_t i(0); i != [items count]; ++i) {
7121 NSDictionary *item([items objectAtIndex:i]);
7122 NSString *label = [item objectForKey:@"label"];
7123 if (label != nil && [label isEqualToString:@"General"]) {
7124 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7125 @"CydiaSettings", @"bundle",
7126 @"PSLinkCell", @"cell",
7127 [NSNumber numberWithBool:YES], @"hasIcon",
7128 [NSNumber numberWithBool:YES], @"isController",
7130 nil] atIndex:(i + 1)];
7136 _assert([settings writeToFile:plist atomically:YES] == YES);
7141 id Alloc_(id self, SEL selector) {
7142 id object = alloc_(self, selector);
7143 lprintf("[%s]A-%p\n", self->isa->name, object);
7148 id Dealloc_(id self, SEL selector) {
7149 id object = dealloc_(self, selector);
7150 lprintf("[%s]D-%p\n", self->isa->name, object);
7154 int main(int argc, char *argv[]) { _pooled
7156 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7158 bool substrate(false);
7164 for (int argi(1); argi != argc; ++argi)
7165 if (strcmp(argv[argi], "--") == 0) {
7167 argv[argi] = argv[0];
7173 for (int argi(1); argi != arge; ++argi)
7174 if (strcmp(args[argi], "--bootstrap") == 0)
7176 else if (strcmp(args[argi], "--substrate") == 0)
7179 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7182 App_ = [[NSBundle mainBundle] bundlePath];
7183 Home_ = NSHomeDirectory();
7184 Locale_ = CFLocaleCopyCurrent();
7187 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7188 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7189 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7190 Sounds_Keyboard_ = [keyboard boolValue];
7196 #if 0 /* XXX: this costs 1.4s of startup performance */
7197 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7198 _assert(errno == ENOENT);
7199 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7200 _assert(errno == ENOENT);
7203 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7204 alloc_ = alloc->method_imp;
7205 alloc->method_imp = (IMP) &Alloc_;*/
7207 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7208 dealloc_ = dealloc->method_imp;
7209 dealloc->method_imp = (IMP) &Dealloc_;*/
7214 size = sizeof(maxproc);
7215 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7216 perror("sysctlbyname(\"kern.maxproc\", ?)");
7217 else if (maxproc < 64) {
7219 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7220 perror("sysctlbyname(\"kern.maxproc\", #)");
7223 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7224 char *machine = new char[size];
7225 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7226 perror("sysctlbyname(\"hw.machine\", ?)");
7230 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7232 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7233 Build_ = [system objectForKey:@"ProductBuildVersion"];
7235 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7236 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7239 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7242 if (Metadata_ == NULL)
7243 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7245 Settings_ = [Metadata_ objectForKey:@"Settings"];
7247 Packages_ = [Metadata_ objectForKey:@"Packages"];
7248 Sections_ = [Metadata_ objectForKey:@"Sections"];
7249 Sources_ = [Metadata_ objectForKey:@"Sources"];
7252 if (Settings_ != nil)
7253 Role_ = [Settings_ objectForKey:@"Role"];
7255 if (Packages_ == nil) {
7256 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7257 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7260 if (Sections_ == nil) {
7261 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7262 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7265 if (Sources_ == nil) {
7266 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7267 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7271 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7274 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7275 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7276 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7277 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7279 if (access("/User", F_OK) != 0) {
7281 system("/usr/libexec/cydia/firmware.sh");
7285 _assert([[NSFileManager defaultManager]
7286 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7287 withIntermediateDirectories:YES
7292 space_ = CGColorSpaceCreateDeviceRGB();
7294 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7295 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7296 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7297 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7298 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7299 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7301 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7303 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7305 UIApplicationUseLegacyEvents(YES);
7306 UIKeyboardDisableAutomaticAppearance();
7309 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7311 CGColorSpaceRelease(space_);