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>
61 #include <ext/stdio_filebuf.h>
63 #include <apt-pkg/acquire.h>
64 #include <apt-pkg/acquire-item.h>
65 #include <apt-pkg/algorithms.h>
66 #include <apt-pkg/cachefile.h>
67 #include <apt-pkg/clean.h>
68 #include <apt-pkg/configuration.h>
69 #include <apt-pkg/debmetaindex.h>
70 #include <apt-pkg/error.h>
71 #include <apt-pkg/init.h>
72 #include <apt-pkg/mmap.h>
73 #include <apt-pkg/pkgrecords.h>
74 #include <apt-pkg/sha1.h>
75 #include <apt-pkg/sourcelist.h>
76 #include <apt-pkg/sptr.h>
77 #include <apt-pkg/strutl.h>
79 #include <sys/types.h>
81 #include <sys/sysctl.h>
82 #include <sys/param.h>
83 #include <sys/mount.h>
89 #include <mach-o/nlist.h>
99 #import "BrowserView.h"
100 #import "ResetView.h"
102 #import "substrate.h"
105 //#define _finline __attribute__((force_inline))
106 #define _finline inline
111 #define _limit(count) do { \
112 static size_t _count(0); \
113 if (++_count == count) \
117 static uint64_t profile_;
119 #define _timestamp ({ \
121 gettimeofday(&tv, NULL); \
122 tv.tv_sec * 1000000 + tv.tv_usec; \
125 /* Objective-C Handle<> {{{ */
126 template <typename Type_>
128 typedef _H<Type_> This_;
133 _finline void Retain_() {
138 _finline void Clear_() {
144 _finline _H(Type_ *value = NULL, bool mended = false) :
155 _finline This_ &operator =(Type_ *value) {
156 if (value_ != value) {
165 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
167 void NSLogPoint(const char *fix, const CGPoint &point) {
168 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
171 void NSLogRect(const char *fix, const CGRect &rect) {
172 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
175 /* NSForcedOrderingSearch doesn't work on the iPhone */
176 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
177 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
178 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
180 /* iPhoneOS 2.0 Compatibility {{{ */
182 @interface UITextView (iPhoneOS)
183 - (void) setTextSize:(float)size;
186 @implementation UITextView (iPhoneOS)
188 - (void) setTextSize:(float)size {
189 [self setFont:[[self font] fontWithSize:size]];
196 extern NSString * const kCAFilterNearest;
198 /* Information Dictionaries {{{ */
199 @interface NSMutableArray (Cydia)
200 - (void) addInfoDictionary:(NSDictionary *)info;
203 @implementation NSMutableArray (Cydia)
205 - (void) addInfoDictionary:(NSDictionary *)info {
206 [self addObject:info];
211 @interface NSMutableDictionary (Cydia)
212 - (void) addInfoDictionary:(NSDictionary *)info;
215 @implementation NSMutableDictionary (Cydia)
217 - (void) addInfoDictionary:(NSDictionary *)info {
218 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
219 [self setObject:info forKey:bundle];
224 /* Pop Transitions {{{ */
225 @interface PopTransitionView : UITransitionView {
230 @implementation PopTransitionView
232 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
233 if (from != nil && to == nil)
234 [self removeFromSuperview];
239 @interface UIView (PopUpView)
240 - (void) popFromSuperviewAnimated:(BOOL)animated;
241 - (void) popSubview:(UIView *)view;
244 @implementation UIView (PopUpView)
246 - (void) popFromSuperviewAnimated:(BOOL)animated {
247 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
250 - (void) popSubview:(UIView *)view {
251 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
252 [transition setDelegate:transition];
253 [self addSubview:transition];
255 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
256 [transition transition:UITransitionNone toView:blank];
257 [transition transition:UITransitionPushFromBottom toView:view];
263 #define lprintf(args...) fprintf(stderr, args)
266 #define ForSaurik (1 && !ForRelease)
267 #define IgnoreInstall (0 && !ForRelease)
268 #define RecycleWebViews 0
269 #define AlwaysReload (1 && !ForRelease)
272 @interface NSMutableArray (Radix)
273 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
276 @implementation NSMutableArray (Radix)
278 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
279 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
280 [invocation setSelector:selector];
281 [invocation setArgument:&object atIndex:2];
283 size_t count([self count]);
288 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
290 for (size_t i(0); i != count; ++i) {
291 RadixItem &item(lhs[i]);
294 id object([self objectAtIndex:i]);
295 [invocation setTarget:object];
298 [invocation getReturnValue:&item.key];
301 static const size_t width = 32;
302 static const size_t bits = 11;
303 static const size_t slots = 1 << bits;
304 static const size_t passes = (width + (bits - 1)) / bits;
306 size_t *hist(new size_t[slots]);
308 for (size_t pass(0); pass != passes; ++pass) {
309 memset(hist, 0, sizeof(size_t) * slots);
311 for (size_t i(0); i != count; ++i) {
312 uint32_t key(lhs[i].key);
314 key &= _not(uint32_t) >> width - bits;
319 for (size_t i(0); i != slots; ++i) {
320 size_t local(offset);
325 for (size_t i(0); i != count; ++i) {
326 uint32_t key(lhs[i].key);
328 key &= _not(uint32_t) >> width - bits;
329 rhs[hist[key]++] = lhs[i];
339 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
340 for (size_t i(0); i != count; ++i)
341 [values addObject:[self objectAtIndex:lhs[i].index]];
342 [self setArray:values];
350 /* Apple Bug Fixes {{{ */
351 @implementation UIWebDocumentView (Cydia)
353 - (void) _setScrollerOffset:(CGPoint)offset {
354 UIScroller *scroller([self _scroller]);
356 CGSize size([scroller contentSize]);
357 CGSize bounds([scroller bounds].size);
360 max.x = size.width - bounds.width;
361 max.y = size.height - bounds.height;
369 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
370 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
372 [scroller setOffset:offset];
379 kUIControlEventMouseDown = 1 << 0,
380 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
381 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
382 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
383 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
384 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
385 } UIControlEventMasks;
387 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
388 size_t length([self length] - state->state);
391 else if (length > count)
393 for (size_t i(0); i != length; ++i)
394 objects[i] = [self item:state->state++];
395 state->itemsPtr = objects;
396 state->mutationsPtr = (unsigned long *) self;
400 @interface NSString (UIKit)
401 - (NSString *) stringByAddingPercentEscapes;
402 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
405 @interface NSString (Cydia)
406 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
407 - (NSComparisonResult) compareByPath:(NSString *)other;
410 @implementation NSString (Cydia)
412 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
413 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
416 - (NSComparisonResult) compareByPath:(NSString *)other {
417 NSString *prefix = [self commonPrefixWithString:other options:0];
418 size_t length = [prefix length];
420 NSRange lrange = NSMakeRange(length, [self length] - length);
421 NSRange rrange = NSMakeRange(length, [other length] - length);
423 lrange = [self rangeOfString:@"/" options:0 range:lrange];
424 rrange = [other rangeOfString:@"/" options:0 range:rrange];
426 NSComparisonResult value;
428 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
429 value = NSOrderedSame;
430 else if (lrange.location == NSNotFound)
431 value = NSOrderedAscending;
432 else if (rrange.location == NSNotFound)
433 value = NSOrderedDescending;
435 value = NSOrderedSame;
437 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
438 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
439 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
440 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
442 NSComparisonResult result = [lpath compare:rpath];
443 return result == NSOrderedSame ? value : result;
448 /* Perl-Compatible RegEx {{{ */
458 Pcre(const char *regex) :
463 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
466 lprintf("%d:%s\n", offset, error);
470 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
471 matches_ = new int[(capture_ + 1) * 3];
479 NSString *operator [](size_t match) {
480 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
483 bool operator ()(NSString *data) {
484 // XXX: length is for characters, not for bytes
485 return operator ()([data UTF8String], [data length]);
488 bool operator ()(const char *data, size_t size) {
490 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
494 /* Mime Addresses {{{ */
495 @interface Address : NSObject {
501 - (NSString *) address;
503 + (Address *) addressWithString:(NSString *)string;
504 - (Address *) initWithString:(NSString *)string;
507 @implementation Address
516 - (NSString *) name {
520 - (NSString *) address {
524 + (Address *) addressWithString:(NSString *)string {
525 return [[[Address alloc] initWithString:string] autorelease];
528 + (NSArray *) _attributeKeys {
529 return [NSArray arrayWithObjects:@"address", @"name", nil];
532 - (NSArray *) attributeKeys {
533 return [[self class] _attributeKeys];
536 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
537 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
540 - (Address *) initWithString:(NSString *)string {
541 if ((self = [super init]) != nil) {
542 const char *data = [string UTF8String];
543 size_t size = [string length];
545 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
547 if (address_r(data, size)) {
548 name_ = [address_r[1] retain];
549 address_ = [address_r[2] retain];
551 name_ = [string retain];
559 /* CoreGraphics Primitives {{{ */
570 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
573 Set(space, red, green, blue, alpha);
578 CGColorRelease(color_);
585 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
587 float color[] = {red, green, blue, alpha};
588 color_ = CGColorCreate(space, color);
591 operator CGColorRef() {
597 extern "C" void UISetColor(CGColorRef color);
599 /* Random Global Variables {{{ */
600 static const int PulseInterval_ = 50000;
601 static const int ButtonBarHeight_ = 48;
602 static const float KeyboardTime_ = 0.3f;
604 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
605 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
606 #define NotifyConfig_ "/etc/notify.conf"
608 static CGColor Blue_;
609 static CGColor Blueish_;
610 static CGColor Black_;
612 static CGColor White_;
613 static CGColor Gray_;
615 static NSString *App_;
616 static NSString *Home_;
617 static BOOL Sounds_Keyboard_;
619 static BOOL Advanced_;
623 static BOOL Ignored_;
625 static UIFont *Font12_;
626 static UIFont *Font12Bold_;
627 static UIFont *Font14_;
628 static UIFont *Font18Bold_;
629 static UIFont *Font22Bold_;
631 static const char *Machine_ = NULL;
632 static const NSString *UniqueID_ = nil;
633 static const NSString *Build_ = nil;
636 CGColorSpaceRef space_;
641 static NSDictionary *SectionMap_;
642 static NSMutableDictionary *Metadata_;
643 static NSMutableDictionary *Indices_;
644 static _transient NSMutableDictionary *Settings_;
645 static _transient NSString *Role_;
646 static _transient NSMutableDictionary *Packages_;
647 static _transient NSMutableDictionary *Sections_;
648 static _transient NSMutableDictionary *Sources_;
649 static bool Changed_;
653 static NSMutableArray *Documents_;
656 NSString *GetLastUpdate() {
657 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
660 return @"Never or Unknown";
662 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
663 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
665 CFRelease(formatter);
667 return [(NSString *) formatted autorelease];
670 /* Display Helpers {{{ */
671 inline float Interpolate(float begin, float end, float fraction) {
672 return (end - begin) * fraction + begin;
675 NSString *SizeString(double size) {
676 bool negative = size < 0;
681 while (size > 1024) {
686 static const char *powers_[] = {"B", "kB", "MB", "GB"};
688 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
691 NSString *StripVersion(NSString *version) {
692 NSRange colon = [version rangeOfString:@":"];
693 if (colon.location != NSNotFound)
694 version = [version substringFromIndex:(colon.location + 1)];
698 NSString *Simplify(NSString *title) {
699 const char *data = [title UTF8String];
700 size_t size = [title length];
702 static Pcre square_r("^\\[(.*)\\]$");
703 if (square_r(data, size))
704 return Simplify(square_r[1]);
706 static Pcre paren_r("^\\((.*)\\)$");
707 if (paren_r(data, size))
708 return Simplify(paren_r[1]);
710 static Pcre title_r("^(.*?) \\(.*\\)$");
711 if (title_r(data, size))
712 return Simplify(title_r[1]);
718 bool isSectionVisible(NSString *section) {
719 NSDictionary *metadata = [Sections_ objectForKey:section];
720 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
721 return hidden == nil || ![hidden boolValue];
724 /* Delegate Prototypes {{{ */
728 @interface NSObject (ProgressDelegate)
731 @implementation NSObject(ProgressDelegate)
733 - (void) _setProgressError:(NSArray *)args {
734 [self performSelector:@selector(setProgressError:forPackage:)
735 withObject:[args objectAtIndex:0]
736 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
742 @protocol ProgressDelegate
743 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
744 - (void) setProgressTitle:(NSString *)title;
745 - (void) setProgressPercent:(float)percent;
746 - (void) startProgress;
747 - (void) addProgressOutput:(NSString *)output;
748 - (bool) isCancelling:(size_t)received;
751 @protocol ConfigurationDelegate
752 - (void) repairWithSelector:(SEL)selector;
753 - (void) setConfigurationData:(NSString *)data;
756 @protocol CydiaDelegate
757 - (void) installPackage:(Package *)package;
758 - (void) removePackage:(Package *)package;
759 - (void) slideUp:(UIActionSheet *)alert;
760 - (void) distUpgrade;
763 - (void) askForSettings;
764 - (UIProgressHUD *) addProgressHUD;
765 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
766 - (RVPage *) pageForPackage:(NSString *)name;
767 - (void) openMailToURL:(NSURL *)url;
768 - (void) clearFirstResponder;
772 /* Status Delegation {{{ */
774 public pkgAcquireStatus
777 _transient NSObject<ProgressDelegate> *delegate_;
785 void setDelegate(id delegate) {
786 delegate_ = delegate;
789 virtual bool MediaChange(std::string media, std::string drive) {
793 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
796 virtual void Fetch(pkgAcquire::ItemDesc &item) {
797 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
798 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
801 virtual void Done(pkgAcquire::ItemDesc &item) {
804 virtual void Fail(pkgAcquire::ItemDesc &item) {
806 item.Owner->Status == pkgAcquire::Item::StatIdle ||
807 item.Owner->Status == pkgAcquire::Item::StatDone
811 std::string &error(item.Owner->ErrorText);
815 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
816 NSArray *fields([description componentsSeparatedByString:@" "]);
817 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
819 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
820 withObject:[NSArray arrayWithObjects:
821 [NSString stringWithUTF8String:error.c_str()],
828 virtual bool Pulse(pkgAcquire *Owner) {
829 bool value = pkgAcquireStatus::Pulse(Owner);
832 double(CurrentBytes + CurrentItems) /
833 double(TotalBytes + TotalItems)
836 [delegate_ setProgressPercent:percent];
837 return [delegate_ isCancelling:CurrentBytes] ? false : value;
840 virtual void Start() {
841 [delegate_ startProgress];
844 virtual void Stop() {
848 /* Progress Delegation {{{ */
853 _transient id<ProgressDelegate> delegate_;
856 virtual void Update() {
857 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
858 [delegate_ setProgressPercent:(Percent / 100)];
867 void setDelegate(id delegate) {
868 delegate_ = delegate;
871 virtual void Done() {
872 [delegate_ setProgressPercent:1];
877 /* Database Interface {{{ */
878 @interface Database : NSObject {
880 pkgDepCache::Policy *policy_;
881 pkgRecords *records_;
882 pkgProblemResolver *resolver_;
883 pkgAcquire *fetcher_;
885 SPtr<pkgPackageManager> manager_;
886 pkgSourceList *list_;
888 NSMutableDictionary *sources_;
889 NSMutableArray *packages_;
891 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
900 + (Database *) sharedInstance;
902 - (void) _readCydia:(NSNumber *)fd;
903 - (void) _readStatus:(NSNumber *)fd;
904 - (void) _readOutput:(NSNumber *)fd;
908 - (Package *) packageWithName:(NSString *)name;
910 - (pkgCacheFile &) cache;
911 - (pkgDepCache::Policy *) policy;
912 - (pkgRecords *) records;
913 - (pkgProblemResolver *) resolver;
914 - (pkgAcquire &) fetcher;
915 - (NSArray *) packages;
916 - (NSArray *) sources;
925 - (void) updateWithStatus:(Status &)status;
927 - (void) setDelegate:(id)delegate;
928 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
932 /* Source Class {{{ */
933 @interface Source : NSObject {
934 NSString *description_;
939 NSString *distribution_;
943 NSString *defaultIcon_;
945 NSDictionary *record_;
949 - (Source *) initWithMetaIndex:(metaIndex *)index;
951 - (NSComparisonResult) compareByNameAndType:(Source *)source;
953 - (NSDictionary *) record;
957 - (NSString *) distribution;
963 - (NSString *) description;
964 - (NSString *) label;
965 - (NSString *) origin;
966 - (NSString *) version;
968 - (NSString *) defaultIcon;
972 @implementation Source
976 [distribution_ release];
979 if (description_ != nil)
980 [description_ release];
987 if (defaultIcon_ != nil)
988 [defaultIcon_ release];
995 + (NSArray *) _attributeKeys {
996 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
999 - (NSArray *) attributeKeys {
1000 return [[self class] _attributeKeys];
1003 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1004 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1007 - (Source *) initWithMetaIndex:(metaIndex *)index {
1008 if ((self = [super init]) != nil) {
1009 trusted_ = index->IsTrusted();
1011 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1012 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1013 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1015 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1016 if (dindex != NULL) {
1017 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1019 while (std::getline(release, line)) {
1020 std::string::size_type colon(line.find(':'));
1021 if (colon == std::string::npos)
1024 std::string name(line.substr(0, colon));
1025 std::string value(line.substr(colon + 1));
1026 while (!value.empty() && value[0] == ' ')
1027 value = value.substr(1);
1029 if (name == "Default-Icon")
1030 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1031 else if (name == "Description")
1032 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1033 else if (name == "Label")
1034 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1035 else if (name == "Origin")
1036 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1037 else if (name == "Version")
1038 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1042 record_ = [Sources_ objectForKey:[self key]];
1044 record_ = [record_ retain];
1048 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1049 NSDictionary *lhr = [self record];
1050 NSDictionary *rhr = [source record];
1053 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1055 NSString *lhs = [self name];
1056 NSString *rhs = [source name];
1058 if ([lhs length] != 0 && [rhs length] != 0) {
1059 unichar lhc = [lhs characterAtIndex:0];
1060 unichar rhc = [rhs characterAtIndex:0];
1062 if (isalpha(lhc) && !isalpha(rhc))
1063 return NSOrderedAscending;
1064 else if (!isalpha(lhc) && isalpha(rhc))
1065 return NSOrderedDescending;
1068 return [lhs compare:rhs options:LaxCompareOptions_];
1071 - (NSDictionary *) record {
1079 - (NSString *) uri {
1083 - (NSString *) distribution {
1084 return distribution_;
1087 - (NSString *) type {
1091 - (NSString *) key {
1092 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1095 - (NSString *) host {
1096 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1099 - (NSString *) name {
1100 return origin_ == nil ? [self host] : origin_;
1103 - (NSString *) description {
1104 return description_;
1107 - (NSString *) label {
1108 return label_ == nil ? [self host] : label_;
1111 - (NSString *) origin {
1115 - (NSString *) version {
1119 - (NSString *) defaultIcon {
1120 return defaultIcon_;
1125 /* Relationship Class {{{ */
1126 @interface Relationship : NSObject {
1131 - (NSString *) type;
1133 - (NSString *) name;
1137 @implementation Relationship
1145 - (NSString *) type {
1153 - (NSString *) name {
1160 /* Package Class {{{ */
1161 @interface Package : NSObject {
1162 pkgCache::PkgIterator iterator_;
1163 _transient Database *database_;
1164 pkgCache::VerIterator version_;
1165 pkgCache::VerFileIterator file_;
1173 NSString *installed_;
1179 NSString *depiction_;
1180 NSString *homepage_;
1186 NSArray *relationships_;
1189 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1190 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1192 - (pkgCache::PkgIterator) iterator;
1194 - (NSString *) section;
1195 - (NSString *) simpleSection;
1197 - (Address *) maintainer;
1199 - (NSString *) description;
1200 - (NSString *) index;
1202 - (NSMutableDictionary *) metadata;
1204 - (BOOL) subscribed;
1207 - (NSString *) latest;
1208 - (NSString *) installed;
1211 - (BOOL) upgradableAndEssential:(BOOL)essential;
1214 - (BOOL) unfiltered;
1218 - (BOOL) halfConfigured;
1219 - (BOOL) halfInstalled;
1221 - (NSString *) mode;
1224 - (NSString *) name;
1225 - (NSString *) tagline;
1227 - (NSString *) homepage;
1228 - (NSString *) depiction;
1229 - (Address *) author;
1231 - (NSArray *) files;
1232 - (NSArray *) relationships;
1233 - (NSArray *) warnings;
1234 - (NSArray *) applications;
1236 - (Source *) source;
1237 - (NSString *) role;
1238 - (NSString *) rating;
1240 - (BOOL) matches:(NSString *)text;
1242 - (bool) hasSupportingRole;
1243 - (BOOL) hasTag:(NSString *)tag;
1244 - (NSString *) primaryPurpose;
1245 - (NSArray *) purposes;
1247 - (NSComparisonResult) compareByName:(Package *)package;
1248 - (NSComparisonResult) compareBySection:(Package *)package;
1250 - (uint32_t) compareForChanges;
1255 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1256 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1257 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1258 - (NSNumber *) isVisibleInSource:(Source *)source;
1262 @implementation Package
1268 if (section_ != nil)
1272 if (installed_ != nil)
1273 [installed_ release];
1281 if (depiction_ != nil)
1282 [depiction_ release];
1283 if (homepage_ != nil)
1284 [homepage_ release];
1285 if (sponsor_ != nil)
1294 if (relationships_ != nil)
1295 [relationships_ release];
1300 + (NSArray *) _attributeKeys {
1301 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1304 - (NSArray *) attributeKeys {
1305 return [[self class] _attributeKeys];
1308 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1309 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1312 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1313 if ((self = [super init]) != nil) {
1314 iterator_ = iterator;
1315 database_ = database;
1317 version_ = [database_ policy]->GetCandidateVer(iterator_);
1318 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1319 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1321 pkgCache::VerIterator current = iterator_.CurrentVer();
1322 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1323 installed_ = [StripVersion(installed) retain];
1325 if (!version_.end())
1326 file_ = version_.FileList();
1328 pkgCache &cache([database_ cache]);
1329 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1332 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1335 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1337 const char *begin, *end;
1338 parser->GetRec(begin, end);
1340 NSString *website(nil);
1341 NSString *sponsor(nil);
1342 NSString *author(nil);
1351 {"depiction", &depiction_},
1352 {"homepage", &homepage_},
1353 {"website", &website},
1354 {"sponsor", &sponsor},
1355 {"author", &author},
1359 while (begin != end)
1360 if (*begin == '\n') {
1363 } else if (isblank(*begin)) next: {
1364 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1367 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1368 const char *name(begin);
1369 size_t size(colon - begin);
1371 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1374 const char *stop(begin == NULL ? end : begin);
1375 while (stop[-1] == '\r')
1377 while (++colon != stop && isblank(*colon));
1379 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1380 if (strncasecmp(names[i].name_, name, size) == 0) {
1381 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1382 *names[i].value_ = value;
1393 name_ = [name_ retain];
1394 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1396 icon_ = [icon_ retain];
1397 if (depiction_ != nil)
1398 depiction_ = [depiction_ retain];
1399 if (homepage_ == nil)
1400 homepage_ = website;
1401 if ([homepage_ isEqualToString:depiction_])
1403 if (homepage_ != nil)
1404 homepage_ = [homepage_ retain];
1406 sponsor_ = [[Address addressWithString:sponsor] retain];
1408 author_ = [[Address addressWithString:author] retain];
1410 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1414 for (int i(0), e([tags_ count]); i != e; ++i) {
1415 NSString *tag = [tags_ objectAtIndex:i];
1416 if ([tag hasPrefix:@"role::"]) {
1417 role_ = [[tag substringFromIndex:6] retain];
1422 NSString *solid(latest == nil ? installed : latest);
1423 bool changed(false);
1425 NSString *key([id_ lowercaseString]);
1427 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1428 if (metadata == nil) {
1429 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1434 [metadata setObject:solid forKey:@"LastVersion"];
1437 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1438 NSDate *last([metadata objectForKey:@"LastSeen"]);
1439 NSString *version([metadata objectForKey:@"LastVersion"]);
1442 first = last == nil ? now_ : last;
1443 [metadata setObject:first forKey:@"FirstSeen"];
1448 if (version == nil) {
1449 [metadata setObject:solid forKey:@"LastVersion"];
1451 } else if (![version isEqualToString:solid]) {
1452 [metadata setObject:solid forKey:@"LastVersion"];
1454 [metadata setObject:last forKey:@"LastSeen"];
1460 [Packages_ setObject:metadata forKey:key];
1466 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1467 return [[[Package alloc]
1468 initWithIterator:iterator
1473 - (pkgCache::PkgIterator) iterator {
1477 - (NSString *) section {
1478 if (section_ != nil)
1481 const char *section = iterator_.Section();
1482 if (section == NULL)
1485 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1488 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1489 if (NSString *rename = [value objectForKey:@"Rename"]) {
1494 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1498 - (NSString *) simpleSection {
1499 if (NSString *section = [self section])
1500 return Simplify(section);
1506 - (Address *) maintainer {
1509 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1510 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1514 return version_.end() ? 0 : version_->InstalledSize;
1517 - (NSString *) description {
1520 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1521 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1523 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1524 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1525 if ([lines count] < 2)
1528 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1529 for (size_t i(1); i != [lines count]; ++i) {
1530 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1531 [trimmed addObject:trim];
1534 return [trimmed componentsJoinedByString:@"\n"];
1537 - (NSString *) index {
1538 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1539 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1542 - (NSMutableDictionary *) metadata {
1543 return [Packages_ objectForKey:[id_ lowercaseString]];
1547 NSDictionary *metadata([self metadata]);
1548 if ([self subscribed])
1549 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1551 return [metadata objectForKey:@"FirstSeen"];
1554 - (BOOL) subscribed {
1555 NSDictionary *metadata([self metadata]);
1556 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1557 return [subscribed boolValue];
1563 NSDictionary *metadata([self metadata]);
1564 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1565 return [ignored boolValue];
1570 - (NSString *) latest {
1574 - (NSString *) installed {
1579 return !version_.end();
1582 - (BOOL) upgradableAndEssential:(BOOL)essential {
1583 pkgCache::VerIterator current = iterator_.CurrentVer();
1586 return essential && [self essential];
1588 return !version_.end() && version_ != current;
1591 - (BOOL) essential {
1592 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1596 return [database_ cache][iterator_].InstBroken();
1599 - (BOOL) unfiltered {
1600 NSString *section = [self section];
1601 return section == nil || isSectionVisible(section);
1605 return [self hasSupportingRole] && [self unfiltered];
1609 unsigned char current = iterator_->CurrentState;
1610 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1613 - (BOOL) halfConfigured {
1614 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1617 - (BOOL) halfInstalled {
1618 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1622 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1623 return state.Mode != pkgDepCache::ModeKeep;
1626 - (NSString *) mode {
1627 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1629 switch (state.Mode) {
1630 case pkgDepCache::ModeDelete:
1631 if ((state.iFlags & pkgDepCache::Purge) != 0)
1635 case pkgDepCache::ModeKeep:
1636 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1640 case pkgDepCache::ModeInstall:
1641 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1642 return @"Reinstall";
1643 else switch (state.Status) {
1645 return @"Downgrade";
1651 return @"New Install";
1664 - (NSString *) name {
1665 return name_ == nil ? id_ : name_;
1668 - (NSString *) tagline {
1672 - (UIImage *) icon {
1673 NSString *section = [self simpleSection];
1676 if (NSString *icon = icon_)
1677 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1678 if (icon == nil) if (section != nil)
1679 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1680 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1681 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1683 icon = [UIImage applicationImageNamed:@"unknown.png"];
1687 - (NSString *) homepage {
1691 - (NSString *) depiction {
1695 - (Address *) sponsor {
1699 - (Address *) author {
1703 - (NSArray *) files {
1704 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1705 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1708 fin.open([path UTF8String]);
1713 while (std::getline(fin, line))
1714 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1719 - (NSArray *) relationships {
1720 return relationships_;
1723 - (NSArray *) warnings {
1724 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1725 const char *name(iterator_.Name());
1727 size_t length(strlen(name));
1728 if (length < 2) invalid:
1729 [warnings addObject:@"illegal package identifier"];
1730 else for (size_t i(0); i != length; ++i)
1732 /* XXX: technically this is not allowed */
1733 (name[i] < 'A' || name[i] > 'Z') &&
1734 (name[i] < 'a' || name[i] > 'z') &&
1735 (name[i] < '0' || name[i] > '9') &&
1736 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1739 if (strcmp(name, "cydia") != 0) {
1741 bool _private = false;
1744 bool repository = [[self section] isEqualToString:@"Repositories"];
1746 if (NSArray *files = [self files])
1747 for (NSString *file in files)
1748 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1750 else if (!_private && [file isEqualToString:@"/private"])
1752 else if (!stash && [file isEqualToString:@"/var/stash"])
1755 /* XXX: this is not sensitive enough. only some folders are valid. */
1756 if (cydia && !repository)
1757 [warnings addObject:@"files installed into Cydia.app"];
1759 [warnings addObject:@"files installed with /private/*"];
1761 [warnings addObject:@"files installed to /var/stash"];
1764 return [warnings count] == 0 ? nil : warnings;
1767 - (NSArray *) applications {
1768 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1770 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1772 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1773 if (NSArray *files = [self files])
1774 for (NSString *file in files)
1775 if (application_r(file)) {
1776 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1777 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1778 if ([id isEqualToString:me])
1781 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1783 display = application_r[1];
1785 NSString *bundle([file stringByDeletingLastPathComponent]);
1786 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1787 if (icon == nil || [icon length] == 0)
1789 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1791 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1792 [applications addObject:application];
1794 [application addObject:id];
1795 [application addObject:display];
1796 [application addObject:url];
1799 return [applications count] == 0 ? nil : applications;
1802 - (Source *) source {
1804 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1811 - (NSString *) role {
1815 - (NSString *) rating {
1816 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1817 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1822 - (BOOL) matches:(NSString *)text {
1828 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1829 if (range.location != NSNotFound)
1832 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1833 if (range.location != NSNotFound)
1836 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1837 if (range.location != NSNotFound)
1843 - (bool) hasSupportingRole {
1846 if ([role_ isEqualToString:@"enduser"])
1848 if ([Role_ isEqualToString:@"User"])
1850 if ([role_ isEqualToString:@"hacker"])
1852 if ([Role_ isEqualToString:@"Hacker"])
1854 if ([role_ isEqualToString:@"developer"])
1856 if ([Role_ isEqualToString:@"Developer"])
1861 - (BOOL) hasTag:(NSString *)tag {
1862 return tags_ == nil ? NO : [tags_ containsObject:tag];
1865 - (NSString *) primaryPurpose {
1866 for (NSString *tag in tags_)
1867 if ([tag hasPrefix:@"purpose::"])
1868 return [tag substringFromIndex:9];
1872 - (NSArray *) purposes {
1873 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1874 for (NSString *tag in tags_)
1875 if ([tag hasPrefix:@"purpose::"])
1876 [purposes addObject:[tag substringFromIndex:9]];
1877 return [purposes count] == 0 ? nil : purposes;
1880 - (NSComparisonResult) compareByName:(Package *)package {
1881 NSString *lhs = [self name];
1882 NSString *rhs = [package name];
1884 if ([lhs length] != 0 && [rhs length] != 0) {
1885 unichar lhc = [lhs characterAtIndex:0];
1886 unichar rhc = [rhs characterAtIndex:0];
1888 if (isalpha(lhc) && !isalpha(rhc))
1889 return NSOrderedAscending;
1890 else if (!isalpha(lhc) && isalpha(rhc))
1891 return NSOrderedDescending;
1894 return [lhs compare:rhs options:LaxCompareOptions_];
1897 - (NSComparisonResult) compareBySection:(Package *)package {
1898 NSString *lhs = [self section];
1899 NSString *rhs = [package section];
1901 if (lhs == NULL && rhs != NULL)
1902 return NSOrderedAscending;
1903 else if (lhs != NULL && rhs == NULL)
1904 return NSOrderedDescending;
1905 else if (lhs != NULL && rhs != NULL) {
1906 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1907 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1910 return NSOrderedSame;
1913 - (uint32_t) compareForChanges {
1918 uint32_t timestamp : 30;
1919 uint32_t ignored : 1;
1920 uint32_t upgradable : 1;
1924 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1926 if ([self upgradableAndEssential:YES]) {
1927 value.bits.timestamp = 0;
1928 value.bits.ignored = [self ignored] ? 0 : 1;
1929 value.bits.upgradable = 1;
1931 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1932 value.bits.ignored = 0;
1933 value.bits.upgradable = 0;
1936 return _not(uint32_t) - value.key;
1940 pkgProblemResolver *resolver = [database_ resolver];
1941 resolver->Clear(iterator_);
1942 resolver->Protect(iterator_);
1943 pkgCacheFile &cache([database_ cache]);
1944 cache->MarkInstall(iterator_, false);
1945 pkgDepCache::StateCache &state((*cache)[iterator_]);
1946 if (!state.Install())
1947 cache->SetReInstall(iterator_, true);
1951 pkgProblemResolver *resolver = [database_ resolver];
1952 resolver->Clear(iterator_);
1953 resolver->Protect(iterator_);
1954 resolver->Remove(iterator_);
1955 [database_ cache]->MarkDelete(iterator_, true);
1958 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1959 return [NSNumber numberWithBool:(
1960 [self unfiltered] && [self matches:search]
1964 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1965 return [NSNumber numberWithBool:(
1966 (![number boolValue] || [self visible]) && [self installed] != nil
1970 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1971 NSString *section = [self section];
1973 return [NSNumber numberWithBool:(
1975 [self installed] == nil && (
1977 section == nil && [name length] == 0 ||
1978 [name isEqualToString:section]
1983 - (NSNumber *) isVisibleInSource:(Source *)source {
1984 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1989 /* Section Class {{{ */
1990 @interface Section : NSObject {
1996 - (NSComparisonResult) compareByName:(Section *)section;
1997 - (Section *) initWithName:(NSString *)name;
1998 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1999 - (NSString *) name;
2002 - (void) addToCount;
2006 @implementation Section
2013 - (NSComparisonResult) compareByName:(Section *)section {
2014 NSString *lhs = [self name];
2015 NSString *rhs = [section name];
2017 if ([lhs length] != 0 && [rhs length] != 0) {
2018 unichar lhc = [lhs characterAtIndex:0];
2019 unichar rhc = [rhs characterAtIndex:0];
2021 if (isalpha(lhc) && !isalpha(rhc))
2022 return NSOrderedAscending;
2023 else if (!isalpha(lhc) && isalpha(rhc))
2024 return NSOrderedDescending;
2027 return [lhs compare:rhs options:LaxCompareOptions_];
2030 - (Section *) initWithName:(NSString *)name {
2031 return [self initWithName:name row:0];
2034 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2035 if ((self = [super init]) != nil) {
2036 name_ = [name retain];
2041 - (NSString *) name {
2053 - (void) addToCount {
2061 static NSArray *Finishes_;
2063 /* Database Implementation {{{ */
2064 @implementation Database
2066 + (Database *) sharedInstance {
2067 static Database *instance;
2068 if (instance == nil)
2069 instance = [[Database alloc] init];
2078 - (void) _readCydia:(NSNumber *)fd { _pooled
2079 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2080 std::istream is(&ib);
2083 static Pcre finish_r("^finish:([^:]*)$");
2085 while (std::getline(is, line)) {
2086 const char *data(line.c_str());
2087 size_t size = line.size();
2088 lprintf("C:%s\n", data);
2090 if (finish_r(data, size)) {
2091 NSString *finish = finish_r[1];
2092 int index = [Finishes_ indexOfObject:finish];
2093 if (index != INT_MAX && index > Finish_)
2101 - (void) _readStatus:(NSNumber *)fd { _pooled
2102 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2103 std::istream is(&ib);
2106 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2107 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2109 while (std::getline(is, line)) {
2110 const char *data(line.c_str());
2111 size_t size = line.size();
2112 lprintf("S:%s\n", data);
2114 if (conffile_r(data, size)) {
2115 [delegate_ setConfigurationData:conffile_r[1]];
2116 } else if (strncmp(data, "status: ", 8) == 0) {
2117 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2118 [delegate_ setProgressTitle:string];
2119 } else if (pmstatus_r(data, size)) {
2120 std::string type([pmstatus_r[1] UTF8String]);
2121 NSString *id = pmstatus_r[2];
2123 float percent([pmstatus_r[3] floatValue]);
2124 [delegate_ setProgressPercent:(percent / 100)];
2126 NSString *string = pmstatus_r[4];
2128 if (type == "pmerror")
2129 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2130 withObject:[NSArray arrayWithObjects:string, id, nil]
2133 else if (type == "pmstatus") {
2134 [delegate_ setProgressTitle:string];
2135 } else if (type == "pmconffile")
2136 [delegate_ setConfigurationData:string];
2137 else _assert(false);
2138 } else _assert(false);
2144 - (void) _readOutput:(NSNumber *)fd { _pooled
2145 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2146 std::istream is(&ib);
2149 while (std::getline(is, line)) {
2150 lprintf("O:%s\n", line.c_str());
2151 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2161 - (Package *) packageWithName:(NSString *)name {
2162 if (static_cast<pkgDepCache *>(cache_) == NULL)
2164 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2165 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2168 - (Database *) init {
2169 if ((self = [super init]) != nil) {
2176 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2177 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2181 _assert(pipe(fds) != -1);
2184 _config->Set("APT::Keep-Fds::", cydiafd_);
2185 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2188 detachNewThreadSelector:@selector(_readCydia:)
2190 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2193 _assert(pipe(fds) != -1);
2197 detachNewThreadSelector:@selector(_readStatus:)
2199 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2202 _assert(pipe(fds) != -1);
2203 _assert(dup2(fds[0], 0) != -1);
2204 _assert(close(fds[0]) != -1);
2206 input_ = fdopen(fds[1], "a");
2208 _assert(pipe(fds) != -1);
2209 _assert(dup2(fds[1], 1) != -1);
2210 _assert(close(fds[1]) != -1);
2213 detachNewThreadSelector:@selector(_readOutput:)
2215 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2220 - (pkgCacheFile &) cache {
2224 - (pkgDepCache::Policy *) policy {
2228 - (pkgRecords *) records {
2232 - (pkgProblemResolver *) resolver {
2236 - (pkgAcquire &) fetcher {
2240 - (NSArray *) packages {
2244 - (NSArray *) sources {
2245 return [sources_ allValues];
2248 - (NSArray *) issues {
2249 if (cache_->BrokenCount() == 0)
2252 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2254 for (Package *package in packages_) {
2255 if (![package broken])
2257 pkgCache::PkgIterator pkg([package iterator]);
2259 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2260 [entry addObject:[package name]];
2261 [issues addObject:entry];
2263 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2267 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2268 pkgCache::DepIterator start;
2269 pkgCache::DepIterator end;
2270 dep.GlobOr(start, end); // ++dep
2272 if (!cache_->IsImportantDep(end))
2274 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2277 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2278 [entry addObject:failure];
2279 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2281 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2282 [failure addObject:[package name]];
2284 pkgCache::PkgIterator target(start.TargetPkg());
2285 if (target->ProvidesList != 0)
2286 [failure addObject:@"?"];
2288 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2290 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2291 else if (!cache_[target].CandidateVerIter(cache_).end())
2292 [failure addObject:@"-"];
2293 else if (target->ProvidesList == 0)
2294 [failure addObject:@"!"];
2296 [failure addObject:@"%"];
2300 if (start.TargetVer() != 0)
2301 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2312 - (void) reloadData {
2332 if (!cache_.Open(progress_, true)) {
2334 if (!_error->PopMessage(error))
2337 lprintf("cache_.Open():[%s]\n", error.c_str());
2339 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2340 [delegate_ repairWithSelector:@selector(configure)];
2341 else if (error == "The package lists or status file could not be parsed or opened.")
2342 [delegate_ repairWithSelector:@selector(update)];
2343 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2344 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2345 // else if (error == "The list of sources could not be read.")
2346 else _assert(false);
2352 now_ = [[NSDate date] retain];
2354 policy_ = new pkgDepCache::Policy();
2355 records_ = new pkgRecords(cache_);
2356 resolver_ = new pkgProblemResolver(cache_);
2357 fetcher_ = new pkgAcquire(&status_);
2360 list_ = new pkgSourceList();
2361 _assert(list_->ReadMainList());
2363 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2364 _assert(pkgApplyStatus(cache_));
2366 if (cache_->BrokenCount() != 0) {
2367 _assert(pkgFixBroken(cache_));
2368 _assert(cache_->BrokenCount() == 0);
2369 _assert(pkgMinimizeUpgrade(cache_));
2372 [sources_ removeAllObjects];
2373 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2374 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2375 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2377 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2378 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2382 [packages_ removeAllObjects];
2385 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2386 if (Package *package = [Package packageWithIterator:iterator database:self])
2387 [packages_ addObject:package];
2389 [packages_ sortUsingSelector:@selector(compareByName:)];
2393 - (void) configure {
2394 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2395 system([dpkg UTF8String]);
2403 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2404 _assert(!_error->PendingError());
2407 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2410 public pkgArchiveCleaner
2413 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2418 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2420 while (_error->PopMessage(error))
2421 lprintf("ArchiveCleaner: %s\n", error.c_str());
2426 pkgRecords records(cache_);
2428 lock_ = new FileFd();
2429 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2430 _assert(!_error->PendingError());
2433 // XXX: explain this with an error message
2434 _assert(list.ReadMainList());
2436 manager_ = (_system->CreatePM(cache_));
2437 _assert(manager_->GetArchives(fetcher_, &list, &records));
2438 _assert(!_error->PendingError());
2442 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2444 _assert(list.ReadMainList());
2445 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2446 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2449 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2454 bool failed = false;
2455 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2456 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2459 std::string uri = (*item)->DescURI();
2460 std::string error = (*item)->ErrorText;
2462 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2465 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2466 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2477 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2479 if (_error->PendingError()) {
2484 if (result == pkgPackageManager::Failed) {
2489 if (result != pkgPackageManager::Completed) {
2494 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2496 _assert(list.ReadMainList());
2497 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2498 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2501 if (![before isEqualToArray:after])
2506 _assert(pkgDistUpgrade(cache_));
2510 [self updateWithStatus:status_];
2513 - (void) updateWithStatus:(Status &)status {
2515 _assert(list.ReadMainList());
2518 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2519 _assert(!_error->PendingError());
2521 pkgAcquire fetcher(&status);
2522 _assert(list.GetIndexes(&fetcher));
2524 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2525 bool failed = false;
2526 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2527 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2528 (*item)->Finished();
2532 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2533 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2534 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2537 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2542 - (void) setDelegate:(id)delegate {
2543 delegate_ = delegate;
2544 status_.setDelegate(delegate);
2545 progress_.setDelegate(delegate);
2548 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2549 pkgIndexFile *index(NULL);
2550 list_->FindIndex(file, index);
2551 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2557 /* PopUp Windows {{{ */
2558 @interface PopUpView : UIView {
2559 _transient id delegate_;
2560 UITransitionView *transition_;
2565 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2569 @implementation PopUpView
2572 [transition_ setDelegate:nil];
2573 [transition_ release];
2579 [transition_ transition:UITransitionPushFromTop toView:nil];
2582 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2583 if (from != nil && to == nil)
2584 [self removeFromSuperview];
2587 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2588 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2589 delegate_ = delegate;
2591 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2592 [self addSubview:transition_];
2594 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2596 [view addSubview:self];
2598 [transition_ setDelegate:self];
2600 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2601 [transition_ transition:UITransitionNone toView:blank];
2602 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2609 /* Mail Composition {{{ */
2610 @interface MailToView : PopUpView {
2611 MailComposeController *controller_;
2614 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2618 @implementation MailToView
2621 [controller_ release];
2625 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2629 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2630 NSLog(@"did:%@", delivery);
2631 // [UIApp setStatusBarShowsProgress:NO];
2632 if ([controller error]){
2633 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2634 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2635 [mailAlertSheet setBodyText:[controller error]];
2636 [mailAlertSheet popupAlertAnimated:YES];
2640 - (void) showError {
2641 NSLog(@"%@", [controller_ error]);
2642 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2643 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2644 [mailAlertSheet setBodyText:[controller_ error]];
2645 [mailAlertSheet popupAlertAnimated:YES];
2648 - (void) deliverMessage { _pooled
2652 if (![controller_ deliverMessage])
2653 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2656 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2657 if ([controller_ needsDelivery])
2658 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2663 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2664 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2665 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2666 [controller_ setDelegate:self];
2667 [controller_ initializeUI];
2668 [controller_ setupForURL:url];
2670 UIView *view([controller_ view]);
2671 [overlay_ addSubview:view];
2677 /* Confirmation View {{{ */
2678 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2679 if (!iterator.end())
2680 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2681 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2683 pkgCache::PkgIterator package(dep.TargetPkg());
2686 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2693 @protocol ConfirmationViewDelegate
2698 @interface ConfirmationView : BrowserView {
2699 _transient Database *database_;
2700 UIActionSheet *essential_;
2707 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2711 @implementation ConfirmationView
2718 if (essential_ != nil)
2719 [essential_ release];
2725 [book_ popFromSuperviewAnimated:YES];
2728 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2729 NSString *context([sheet context]);
2731 if ([context isEqualToString:@"remove"]) {
2739 [delegate_ confirm];
2746 } else if ([context isEqualToString:@"unable"]) {
2750 [super alertSheet:sheet buttonClicked:button];
2753 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2754 [window setValue:changes_ forKey:@"changes"];
2755 [window setValue:issues_ forKey:@"issues"];
2756 [window setValue:sizes_ forKey:@"sizes"];
2757 [super webView:sender didClearWindowObject:window forFrame:frame];
2760 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2761 if ((self = [super initWithBook:book]) != nil) {
2762 database_ = database;
2764 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2765 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2766 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2767 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2768 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2772 pkgDepCache::Policy *policy([database_ policy]);
2774 pkgCacheFile &cache([database_ cache]);
2775 NSArray *packages = [database_ packages];
2776 for (size_t i(0), e = [packages count]; i != e; ++i) {
2777 Package *package = [packages objectAtIndex:i];
2778 pkgCache::PkgIterator iterator = [package iterator];
2779 pkgDepCache::StateCache &state(cache[iterator]);
2781 NSString *name([package name]);
2783 if (state.NewInstall())
2784 [installing addObject:name];
2785 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2786 [reinstalling addObject:name];
2787 else if (state.Upgrade())
2788 [upgrading addObject:name];
2789 else if (state.Downgrade())
2790 [downgrading addObject:name];
2791 else if (state.Delete()) {
2792 if ([package essential])
2794 [removing addObject:name];
2797 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2798 substrate_ |= DepSubstrate(iterator.CurrentVer());
2803 else if (Advanced_ || true) {
2804 essential_ = [[UIActionSheet alloc]
2805 initWithTitle:@"Removing Essentials"
2806 buttons:[NSArray arrayWithObjects:
2807 @"Cancel Operation (Safe)",
2808 @"Force Removal (Unsafe)",
2810 defaultButtonIndex:0
2816 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2818 [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."];
2820 essential_ = [[UIActionSheet alloc]
2821 initWithTitle:@"Unable to Comply"
2822 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2823 defaultButtonIndex:0
2828 [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."];
2831 changes_ = [[NSArray alloc] initWithObjects:
2839 issues_ = [database_ issues];
2841 issues_ = [issues_ retain];
2843 sizes_ = [[NSArray alloc] initWithObjects:
2844 SizeString([database_ fetcher].FetchNeeded()),
2845 SizeString([database_ fetcher].PartialPresent()),
2846 SizeString([database_ cache]->UsrSize()),
2849 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2853 - (NSString *) backButtonTitle {
2857 - (NSString *) leftButtonTitle {
2861 - (id) _rightButtonTitle {
2862 #if AlwaysReload || IgnoreInstall
2865 return issues_ == nil ? @"Confirm" : nil;
2869 - (void) _leftButtonClicked {
2874 - (void) _rightButtonClicked {
2876 return [super _rightButtonClicked];
2878 if (essential_ != nil)
2879 [essential_ popupAlertAnimated:YES];
2883 [delegate_ confirm];
2891 /* Progress Data {{{ */
2892 @interface ProgressData : NSObject {
2898 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2905 @implementation ProgressData
2907 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2908 if ((self = [super init]) != nil) {
2909 selector_ = selector;
2929 /* Progress View {{{ */
2930 @interface ProgressView : UIView <
2931 ConfigurationDelegate,
2934 _transient Database *database_;
2936 UIView *background_;
2937 UITransitionView *transition_;
2939 UINavigationBar *navbar_;
2940 UIProgressBar *progress_;
2941 UITextView *output_;
2942 UITextLabel *status_;
2943 UIPushButton *close_;
2946 SHA1SumValue springlist_;
2947 SHA1SumValue notifyconf_;
2948 SHA1SumValue sandplate_;
2950 NSTimeInterval last_;
2953 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2955 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2956 - (void) setContentView:(UIView *)view;
2959 - (void) _retachThread;
2960 - (void) _detachNewThreadData:(ProgressData *)data;
2961 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2967 @protocol ProgressViewDelegate
2968 - (void) progressViewIsComplete:(ProgressView *)sender;
2971 @implementation ProgressView
2974 [transition_ setDelegate:nil];
2975 [navbar_ setDelegate:nil];
2978 if (background_ != nil)
2979 [background_ release];
2980 [transition_ release];
2983 [progress_ release];
2990 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2991 if (bootstrap_ && from == overlay_ && to == view_)
2995 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2996 if ((self = [super initWithFrame:frame]) != nil) {
2997 database_ = database;
2998 delegate_ = delegate;
3000 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3001 [transition_ setDelegate:self];
3003 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3006 [overlay_ setBackgroundColor:[UIColor blackColor]];
3008 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3009 [background_ setBackgroundColor:[UIColor blackColor]];
3010 [self addSubview:background_];
3013 [self addSubview:transition_];
3015 CGSize navsize = [UINavigationBar defaultSize];
3016 CGRect navrect = {{0, 0}, navsize};
3018 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3019 [overlay_ addSubview:navbar_];
3021 [navbar_ setBarStyle:1];
3022 [navbar_ setDelegate:self];
3024 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3025 [navbar_ pushNavigationItem:navitem];
3027 CGRect bounds = [overlay_ bounds];
3028 CGSize prgsize = [UIProgressBar defaultSize];
3031 (bounds.size.width - prgsize.width) / 2,
3032 bounds.size.height - prgsize.height - 20
3035 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3036 [progress_ setStyle:0];
3038 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3040 bounds.size.height - prgsize.height - 50,
3041 bounds.size.width - 20,
3045 [status_ setColor:[UIColor whiteColor]];
3046 [status_ setBackgroundColor:[UIColor clearColor]];
3048 [status_ setCentersHorizontally:YES];
3049 //[status_ setFont:font];
3051 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3053 navrect.size.height + 20,
3054 bounds.size.width - 20,
3055 bounds.size.height - navsize.height - 62 - navrect.size.height
3058 //[output_ setTextFont:@"Courier New"];
3059 [output_ setTextSize:12];
3061 [output_ setTextColor:[UIColor whiteColor]];
3062 [output_ setBackgroundColor:[UIColor clearColor]];
3064 [output_ setMarginTop:0];
3065 [output_ setAllowsRubberBanding:YES];
3066 [output_ setEditable:NO];
3068 [overlay_ addSubview:output_];
3070 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3072 bounds.size.height - prgsize.height - 50,
3073 bounds.size.width - 20,
3077 [close_ setAutosizesToFit:NO];
3078 [close_ setDrawsShadow:YES];
3079 [close_ setStretchBackground:YES];
3080 [close_ setEnabled:YES];
3082 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3083 [close_ setTitleFont:bold];
3085 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3086 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3087 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3091 - (void) setContentView:(UIView *)view {
3092 view_ = [view retain];
3095 - (void) resetView {
3096 [transition_ transition:6 toView:view_];
3099 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3100 NSString *context([sheet context]);
3102 if ([context isEqualToString:@"conffile"]) {
3103 FILE *input = [database_ input];
3107 fprintf(input, "N\n");
3111 fprintf(input, "Y\n");
3122 - (void) closeButtonPushed {
3131 [delegate_ suspendWithAnimation:YES];
3135 system("launchctl stop com.apple.SpringBoard");
3139 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3148 - (void) _retachThread {
3149 UINavigationItem *item = [navbar_ topItem];
3150 [item setTitle:@"Complete"];
3152 [overlay_ addSubview:close_];
3153 [progress_ removeFromSuperview];
3154 [status_ removeFromSuperview];
3156 [delegate_ progressViewIsComplete:self];
3159 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3160 MMap mmap(file, MMap::ReadOnly);
3162 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3163 if (!(sandplate_ == sha1.Result()))
3168 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3169 MMap mmap(file, MMap::ReadOnly);
3171 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3172 if (!(notifyconf_ == sha1.Result()))
3177 FileFd file(SpringBoard_, FileFd::ReadOnly);
3178 MMap mmap(file, MMap::ReadOnly);
3180 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3181 if (!(springlist_ == sha1.Result()))
3186 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3187 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3188 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3189 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3190 case 4: [close_ setTitle:@"Reboot Device"]; break;
3193 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3195 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3196 [cache autorelease];
3198 NSFileManager *manager = [NSFileManager defaultManager];
3199 NSError *error = nil;
3201 id system = [cache objectForKey:@"System"];
3206 if (stat(Cache_, &info) == -1)
3209 [system removeAllObjects];
3211 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3212 for (NSString *app in apps)
3213 if ([app hasSuffix:@".app"]) {
3214 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3215 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3216 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3218 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3219 [info setObject:path forKey:@"Path"];
3220 [info setObject:@"System" forKey:@"ApplicationType"];
3221 [system addInfoDictionary:info];
3227 [cache writeToFile:@Cache_ atomically:YES];
3229 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3231 if (chmod(Cache_, info.st_mode) == -1)
3235 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3238 notify_post("com.apple.mobile.application_installed");
3240 [delegate_ setStatusBarShowsProgress:NO];
3243 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3244 [[data target] performSelector:[data selector] withObject:[data object]];
3247 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3250 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3251 UINavigationItem *item = [navbar_ topItem];
3252 [item setTitle:title];
3254 [status_ setText:nil];
3255 [output_ setText:@""];
3256 [progress_ setProgress:0];
3259 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3261 [close_ removeFromSuperview];
3262 [overlay_ addSubview:progress_];
3263 [overlay_ addSubview:status_];
3265 [delegate_ setStatusBarShowsProgress:YES];
3269 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3270 MMap mmap(file, MMap::ReadOnly);
3272 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3273 sandplate_ = sha1.Result();
3277 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3278 MMap mmap(file, MMap::ReadOnly);
3280 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3281 notifyconf_ = sha1.Result();
3285 FileFd file(SpringBoard_, FileFd::ReadOnly);
3286 MMap mmap(file, MMap::ReadOnly);
3288 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3289 springlist_ = sha1.Result();
3292 [transition_ transition:6 toView:overlay_];
3295 detachNewThreadSelector:@selector(_detachNewThreadData:)
3297 withObject:[[ProgressData alloc]
3298 initWithSelector:selector
3305 - (void) repairWithSelector:(SEL)selector {
3307 detachNewThreadSelector:selector
3314 - (void) setConfigurationData:(NSString *)data {
3316 performSelectorOnMainThread:@selector(_setConfigurationData:)
3322 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3323 Package *package = id == nil ? nil : [database_ packageWithName:id];
3325 UIActionSheet *sheet = [[[UIActionSheet alloc]
3326 initWithTitle:(package == nil ? id : [package name])
3327 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3328 defaultButtonIndex:0
3333 [sheet setBodyText:error];
3334 [sheet popupAlertAnimated:YES];
3337 - (void) setProgressTitle:(NSString *)title {
3339 performSelectorOnMainThread:@selector(_setProgressTitle:)
3345 - (void) setProgressPercent:(float)percent {
3347 performSelectorOnMainThread:@selector(_setProgressPercent:)
3348 withObject:[NSNumber numberWithFloat:percent]
3353 - (void) startProgress {
3354 last_ = [NSDate timeIntervalSinceReferenceDate];
3357 - (void) addProgressOutput:(NSString *)output {
3359 performSelectorOnMainThread:@selector(_addProgressOutput:)
3365 - (bool) isCancelling:(size_t)received {
3367 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3368 if (received_ != received) {
3369 received_ = received;
3371 } else if (now - last_ > 30)
3378 - (void) _setConfigurationData:(NSString *)data {
3379 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3381 _assert(conffile_r(data));
3383 NSString *ofile = conffile_r[1];
3384 //NSString *nfile = conffile_r[2];
3386 UIActionSheet *sheet = [[[UIActionSheet alloc]
3387 initWithTitle:@"Configuration Upgrade"
3388 buttons:[NSArray arrayWithObjects:
3389 @"Keep My Old Copy",
3390 @"Accept The New Copy",
3391 // XXX: @"See What Changed",
3393 defaultButtonIndex:0
3398 [sheet setBodyText:[NSString stringWithFormat:
3399 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3402 [sheet popupAlertAnimated:YES];
3405 - (void) _setProgressTitle:(NSString *)title {
3406 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3407 for (size_t i(0), e([words count]); i != e; ++i) {
3408 NSString *word([words objectAtIndex:i]);
3409 if (Package *package = [database_ packageWithName:word])
3410 [words replaceObjectAtIndex:i withObject:[package name]];
3413 [status_ setText:[words componentsJoinedByString:@" "]];
3416 - (void) _setProgressPercent:(NSNumber *)percent {
3417 [progress_ setProgress:[percent floatValue]];
3420 - (void) _addProgressOutput:(NSString *)output {
3421 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3422 CGSize size = [output_ contentSize];
3423 CGRect rect = {{0, size.height}, {size.width, 0}};
3424 [output_ scrollRectToVisible:rect animated:YES];
3427 - (BOOL) isRunning {
3434 /* Package Cell {{{ */
3435 @interface PackageCell : UISimpleTableCell {
3438 NSString *description_;
3442 UITextLabel *status_;
3446 - (PackageCell *) init;
3447 - (void) setPackage:(Package *)package;
3449 + (int) heightForPackage:(Package *)package;
3453 @implementation PackageCell
3455 - (void) clearPackage {
3466 if (description_ != nil) {
3467 [description_ release];
3471 if (source_ != nil) {
3476 if (badge_ != nil) {
3483 [self clearPackage];
3490 - (PackageCell *) init {
3491 if ((self = [super init]) != nil) {
3493 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3494 [status_ setBackgroundColor:[UIColor clearColor]];
3495 [status_ setFont:small];
3500 - (void) setPackage:(Package *)package {
3501 [self clearPackage];
3503 Source *source = [package source];
3504 NSString *section = [package simpleSection];
3506 icon_ = [[package icon] retain];
3508 name_ = [[package name] retain];
3509 description_ = [[package tagline] retain];
3511 NSString *label = nil;
3512 bool trusted = false;
3514 if (source != nil) {
3515 label = [source label];
3516 trusted = [source trusted];
3517 } else if ([[package id] isEqualToString:@"firmware"])
3520 label = @"Unknown/Local";
3522 NSString *from = [NSString stringWithFormat:@"from %@", label];
3524 if (section != nil && ![section isEqualToString:label])
3525 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3527 source_ = [from retain];
3529 if (NSString *purpose = [package primaryPurpose])
3530 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3531 badge_ = [badge_ retain];
3534 if (NSString *mode = [package mode]) {
3535 [badge_ setImage:[UIImage applicationImageNamed:
3536 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3539 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3540 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3541 } else if ([package half]) {
3542 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3543 [status_ setText:@"Package Damaged"];
3544 [status_ setColor:[UIColor redColor]];
3546 [badge_ setImage:nil];
3547 [status_ setText:nil];
3552 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3555 rect.size = [icon_ size];
3557 rect.size.width /= 2;
3558 rect.size.height /= 2;
3560 rect.origin.x = 25 - rect.size.width / 2;
3561 rect.origin.y = 25 - rect.size.height / 2;
3563 [icon_ drawInRect:rect];
3566 if (badge_ != nil) {
3567 CGSize size = [badge_ size];
3569 [badge_ drawAtPoint:CGPointMake(
3570 36 - size.width / 2,
3571 36 - size.height / 2
3580 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3581 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3585 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3587 [super drawContentInRect:rect selected:selected];
3590 + (int) heightForPackage:(Package *)package {
3591 NSString *tagline([package tagline]);
3592 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3594 if ([package hasMode] || [package half])
3603 /* Section Cell {{{ */
3604 @interface SectionCell : UISimpleTableCell {
3609 _UISwitchSlider *switch_;
3614 - (void) setSection:(Section *)section editing:(BOOL)editing;
3618 @implementation SectionCell
3620 - (void) clearSection {
3621 if (section_ != nil) {
3631 if (count_ != nil) {
3638 [self clearSection];
3645 if ((self = [super init]) != nil) {
3646 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3648 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3649 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3653 - (void) onSwitch:(id)sender {
3654 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3655 if (metadata == nil) {
3656 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3657 [Sections_ setObject:metadata forKey:section_];
3661 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3664 - (void) setSection:(Section *)section editing:(BOOL)editing {
3665 if (editing != editing_) {
3667 [switch_ removeFromSuperview];
3669 [self addSubview:switch_];
3673 [self clearSection];
3675 if (section == nil) {
3676 name_ = [@"All Packages" retain];
3679 section_ = [section name];
3680 if (section_ != nil)
3681 section_ = [section_ retain];
3682 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3683 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3686 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3690 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3691 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3698 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3700 CGSize size = [count_ sizeWithFont:Font14_];
3704 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3706 [super drawContentInRect:rect selected:selected];
3712 /* File Table {{{ */
3713 @interface FileTable : RVPage {
3714 _transient Database *database_;
3717 NSMutableArray *files_;
3721 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3722 - (void) setPackage:(Package *)package;
3726 @implementation FileTable
3729 if (package_ != nil)
3738 - (int) numberOfRowsInTable:(UITable *)table {
3739 return files_ == nil ? 0 : [files_ count];
3742 - (float) table:(UITable *)table heightForRow:(int)row {
3746 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3747 if (reusing == nil) {
3748 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3749 UIFont *font = [UIFont systemFontOfSize:16];
3750 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3752 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3756 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3760 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3761 if ((self = [super initWithBook:book]) != nil) {
3762 database_ = database;
3764 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3766 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3767 [self addSubview:list_];
3769 UITableColumn *column = [[[UITableColumn alloc]
3770 initWithTitle:@"Name"
3772 width:[self frame].size.width
3775 [list_ setDataSource:self];
3776 [list_ setSeparatorStyle:1];
3777 [list_ addTableColumn:column];
3778 [list_ setDelegate:self];
3779 [list_ setReusesTableCells:YES];
3783 - (void) setPackage:(Package *)package {
3784 if (package_ != nil) {
3785 [package_ autorelease];
3794 [files_ removeAllObjects];
3796 if (package != nil) {
3797 package_ = [package retain];
3798 name_ = [[package id] retain];
3800 if (NSArray *files = [package files])
3801 [files_ addObjectsFromArray:files];
3803 if ([files_ count] != 0) {
3804 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3805 [files_ removeObjectAtIndex:0];
3806 [files_ sortUsingSelector:@selector(compareByPath:)];
3808 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3809 [stack addObject:@"/"];
3811 for (int i(0), e([files_ count]); i != e; ++i) {
3812 NSString *file = [files_ objectAtIndex:i];
3813 while (![file hasPrefix:[stack lastObject]])
3814 [stack removeLastObject];
3815 NSString *directory = [stack lastObject];
3816 [stack addObject:[file stringByAppendingString:@"/"]];
3817 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3818 ([stack count] - 2) * 3, "",
3819 [file substringFromIndex:[directory length]]
3828 - (void) resetViewAnimated:(BOOL)animated {
3829 [list_ resetViewAnimated:animated];
3832 - (void) reloadData {
3833 [self setPackage:[database_ packageWithName:name_]];
3834 [self reloadButtons];
3837 - (NSString *) title {
3838 return @"Installed Files";
3841 - (NSString *) backButtonTitle {
3847 /* Package View {{{ */
3848 @interface PackageView : BrowserView {
3849 _transient Database *database_;
3852 NSMutableArray *buttons_;
3855 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3856 - (void) setPackage:(Package *)package;
3860 @implementation PackageView
3863 if (package_ != nil)
3871 - (void) _clickButtonWithName:(NSString *)name {
3872 if ([name isEqualToString:@"Install"])
3873 [delegate_ installPackage:package_];
3874 else if ([name isEqualToString:@"Reinstall"])
3875 [delegate_ installPackage:package_];
3876 else if ([name isEqualToString:@"Remove"])
3877 [delegate_ removePackage:package_];
3878 else if ([name isEqualToString:@"Upgrade"])
3879 [delegate_ installPackage:package_];
3880 else _assert(false);
3883 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3884 NSString *context([sheet context]);
3886 if ([context isEqualToString:@"modify"]) {
3887 int count = [buttons_ count];
3888 _assert(count != 0);
3889 _assert(button <= count + 1);
3891 if (count != button - 1)
3892 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3896 [super alertSheet:sheet buttonClicked:button];
3899 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3900 return [super webView:sender didFinishLoadForFrame:frame];
3903 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3904 [window setValue:package_ forKey:@"package"];
3905 [super webView:sender didClearWindowObject:window forFrame:frame];
3909 - (void) _rightButtonClicked {
3910 /*[super _rightButtonClicked];
3913 int count = [buttons_ count];
3914 _assert(count != 0);
3917 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3919 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3920 [buttons addObjectsFromArray:buttons_];
3921 [buttons addObject:@"Cancel"];
3923 [delegate_ slideUp:[[[UIActionSheet alloc]
3926 defaultButtonIndex:2
3934 - (id) _rightButtonTitle {
3935 int count = [buttons_ count];
3936 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3939 - (NSString *) backButtonTitle {
3943 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3944 if ((self = [super initWithBook:book]) != nil) {
3945 database_ = database;
3946 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3950 - (void) setPackage:(Package *)package {
3951 if (package_ != nil) {
3952 [package_ autorelease];
3961 [buttons_ removeAllObjects];
3963 if (package != nil) {
3964 package_ = [package retain];
3965 name_ = [[package id] retain];
3967 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3969 if ([package_ source] == nil);
3970 else if ([package_ upgradableAndEssential:NO])
3971 [buttons_ addObject:@"Upgrade"];
3972 else if ([package_ installed] == nil)
3973 [buttons_ addObject:@"Install"];
3975 [buttons_ addObject:@"Reinstall"];
3976 if ([package_ installed] != nil)
3977 [buttons_ addObject:@"Remove"];
3985 - (void) reloadData {
3986 [self setPackage:[database_ packageWithName:name_]];
3987 [self reloadButtons];
3992 /* Package Table {{{ */
3993 @interface PackageTable : RVPage {
3994 _transient Database *database_;
3998 NSMutableArray *packages_;
3999 NSMutableArray *sections_;
4000 UISectionList *list_;
4003 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4005 - (void) setDelegate:(id)delegate;
4006 - (void) setObject:(id)object;
4008 - (void) reloadData;
4009 - (void) resetCursor;
4011 - (UISectionList *) list;
4013 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4017 @implementation PackageTable
4020 [list_ setDataSource:nil];
4025 [packages_ release];
4026 [sections_ release];
4031 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4032 return [sections_ count];
4035 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4036 return [[sections_ objectAtIndex:section] name];
4039 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4040 return [[sections_ objectAtIndex:section] row];
4043 - (int) numberOfRowsInTable:(UITable *)table {
4044 return [packages_ count];
4047 - (float) table:(UITable *)table heightForRow:(int)row {
4048 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4051 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4053 reusing = [[[PackageCell alloc] init] autorelease];
4054 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4058 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4062 - (void) tableRowSelected:(NSNotification *)notification {
4063 int row = [[notification object] selectedRow];
4067 Package *package = [packages_ objectAtIndex:row];
4068 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4069 [view setDelegate:delegate_];
4070 [view setPackage:package];
4071 [book_ pushPage:view];
4074 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4075 if ((self = [super initWithBook:book]) != nil) {
4076 database_ = database;
4077 title_ = [title retain];
4079 object_ = object == nil ? nil : [object retain];
4081 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4082 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4084 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4085 [list_ setDataSource:self];
4087 UITableColumn *column = [[[UITableColumn alloc]
4088 initWithTitle:@"Name"
4090 width:[self frame].size.width
4093 UITable *table = [list_ table];
4094 [table setSeparatorStyle:1];
4095 [table addTableColumn:column];
4096 [table setDelegate:self];
4097 [table setReusesTableCells:YES];
4099 [self addSubview:list_];
4102 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4103 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4107 - (void) setDelegate:(id)delegate {
4108 delegate_ = delegate;
4111 - (void) setObject:(id)object {
4117 object_ = [object retain];
4120 - (void) reloadData {
4121 NSArray *packages = [database_ packages];
4123 [packages_ removeAllObjects];
4124 [sections_ removeAllObjects];
4126 for (size_t i(0); i != [packages count]; ++i) {
4127 Package *package([packages objectAtIndex:i]);
4128 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4129 [packages_ addObject:package];
4132 Section *section = nil;
4134 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4135 Package *package = [packages_ objectAtIndex:offset];
4136 NSString *name = [package index];
4138 if (section == nil || ![[section name] isEqualToString:name]) {
4139 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4140 [sections_ addObject:section];
4143 [section addToCount];
4149 - (NSString *) title {
4153 - (void) resetViewAnimated:(BOOL)animated {
4154 [list_ resetViewAnimated:animated];
4157 - (void) resetCursor {
4158 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4161 - (UISectionList *) list {
4165 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4166 [list_ setShouldHideHeaderInShortLists:hide];
4172 /* Add Source View {{{ */
4173 @interface AddSourceView : RVPage {
4174 _transient Database *database_;
4177 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4181 @implementation AddSourceView
4183 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4184 if ((self = [super initWithBook:book]) != nil) {
4185 database_ = database;
4191 /* Source Cell {{{ */
4192 @interface SourceCell : UITableCell {
4195 NSString *description_;
4201 - (SourceCell *) initWithSource:(Source *)source;
4205 @implementation SourceCell
4210 [description_ release];
4215 - (SourceCell *) initWithSource:(Source *)source {
4216 if ((self = [super init]) != nil) {
4218 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4220 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4221 icon_ = [icon_ retain];
4223 origin_ = [[source name] retain];
4224 label_ = [[source uri] retain];
4225 description_ = [[source description] retain];
4229 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4231 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4238 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4242 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4246 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4248 [super drawContentInRect:rect selected:selected];
4253 /* Source Table {{{ */
4254 @interface SourceTable : RVPage {
4255 _transient Database *database_;
4256 UISectionList *list_;
4257 NSMutableArray *sources_;
4258 UIActionSheet *alert_;
4262 UIProgressHUD *hud_;
4265 //NSURLConnection *installer_;
4266 NSURLConnection *trivial_bz2_;
4267 NSURLConnection *trivial_gz_;
4268 //NSURLConnection *automatic_;
4273 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4277 @implementation SourceTable
4279 - (void) _deallocConnection:(NSURLConnection *)connection {
4280 if (connection != nil) {
4281 [connection cancel];
4282 //[connection setDelegate:nil];
4283 [connection release];
4288 [[list_ table] setDelegate:nil];
4289 [list_ setDataSource:nil];
4298 //[self _deallocConnection:installer_];
4299 [self _deallocConnection:trivial_gz_];
4300 [self _deallocConnection:trivial_bz2_];
4301 //[self _deallocConnection:automatic_];
4308 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4309 return offset_ == 0 ? 1 : 2;
4312 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4313 switch (section + (offset_ == 0 ? 1 : 0)) {
4314 case 0: return @"Entered by User";
4315 case 1: return @"Installed by Packages";
4323 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4324 switch (section + (offset_ == 0 ? 1 : 0)) {
4326 case 1: return offset_;
4334 - (int) numberOfRowsInTable:(UITable *)table {
4335 return [sources_ count];
4338 - (float) table:(UITable *)table heightForRow:(int)row {
4339 Source *source = [sources_ objectAtIndex:row];
4340 return [source description] == nil ? 56 : 73;
4343 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4344 Source *source = [sources_ objectAtIndex:row];
4345 // XXX: weird warning, stupid selectors ;P
4346 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4349 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4353 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4357 - (void) tableRowSelected:(NSNotification*)notification {
4358 UITable *table([list_ table]);
4359 int row([table selectedRow]);
4363 Source *source = [sources_ objectAtIndex:row];
4365 PackageTable *packages = [[[PackageTable alloc]
4368 title:[source label]
4369 filter:@selector(isVisibleInSource:)
4373 [packages setDelegate:delegate_];
4375 [book_ pushPage:packages];
4378 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4379 Source *source = [sources_ objectAtIndex:row];
4380 return [source record] != nil;
4383 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4384 [[list_ table] setDeleteConfirmationRow:row];
4387 - (void) table:(UITable *)table deleteRow:(int)row {
4388 Source *source = [sources_ objectAtIndex:row];
4389 [Sources_ removeObjectForKey:[source key]];
4390 [delegate_ syncData];
4393 - (void) _endConnection:(NSURLConnection *)connection {
4394 NSURLConnection **field = NULL;
4395 if (connection == trivial_bz2_)
4396 field = &trivial_bz2_;
4397 else if (connection == trivial_gz_)
4398 field = &trivial_gz_;
4399 _assert(field != NULL);
4400 [connection release];
4404 trivial_bz2_ == nil &&
4407 [delegate_ setStatusBarShowsProgress:NO];
4410 [hud_ removeFromSuperview];
4415 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4418 @"./", @"Distribution",
4419 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4421 [delegate_ syncData];
4422 } else if (error_ != nil) {
4423 UIActionSheet *sheet = [[[UIActionSheet alloc]
4424 initWithTitle:@"Verification Error"
4425 buttons:[NSArray arrayWithObjects:@"OK", nil]
4426 defaultButtonIndex:0
4431 [sheet setBodyText:[error_ localizedDescription]];
4432 [sheet popupAlertAnimated:YES];
4434 UIActionSheet *sheet = [[[UIActionSheet alloc]
4435 initWithTitle:@"Did not Find Repository"
4436 buttons:[NSArray arrayWithObjects:@"OK", nil]
4437 defaultButtonIndex:0
4442 [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."];
4443 [sheet popupAlertAnimated:YES];
4449 if (error_ != nil) {
4456 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4457 switch ([response statusCode]) {
4463 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4464 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4466 error_ = [error retain];
4467 [self _endConnection:connection];
4470 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4471 [self _endConnection:connection];
4474 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4475 NSMutableURLRequest *request = [NSMutableURLRequest
4476 requestWithURL:[NSURL URLWithString:href]
4477 cachePolicy:NSURLRequestUseProtocolCachePolicy
4478 timeoutInterval:20.0
4481 [request setHTTPMethod:method];
4483 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4486 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4487 NSString *context([sheet context]);
4489 if ([context isEqualToString:@"source"]) {
4492 NSString *href = [[sheet textField] text];
4494 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4496 if (![href hasSuffix:@"/"])
4497 href_ = [href stringByAppendingString:@"/"];
4500 href_ = [href_ retain];
4502 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4503 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4504 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4508 hud_ = [delegate_ addProgressHUD];
4509 [hud_ setText:@"Verifying URL"];
4520 } else if ([context isEqualToString:@"trivial"])
4522 else if ([context isEqualToString:@"urlerror"])
4526 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4527 if ((self = [super initWithBook:book]) != nil) {
4528 database_ = database;
4529 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4531 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4532 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4533 [list_ setShouldHideHeaderInShortLists:NO];
4535 [self addSubview:list_];
4536 [list_ setDataSource:self];
4538 UITableColumn *column = [[UITableColumn alloc]
4539 initWithTitle:@"Name"
4541 width:[self frame].size.width
4544 UITable *table = [list_ table];
4545 [table setSeparatorStyle:1];
4546 [table addTableColumn:column];
4547 [table setDelegate:self];
4551 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4552 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4556 - (void) reloadData {
4558 _assert(list.ReadMainList());
4560 [sources_ removeAllObjects];
4561 [sources_ addObjectsFromArray:[database_ sources]];
4563 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4566 int count = [sources_ count];
4567 for (offset_ = 0; offset_ != count; ++offset_) {
4568 Source *source = [sources_ objectAtIndex:offset_];
4569 if ([source record] == nil)
4576 - (void) resetViewAnimated:(BOOL)animated {
4577 [list_ resetViewAnimated:animated];
4580 - (void) _leftButtonClicked {
4581 /*[book_ pushPage:[[[AddSourceView alloc]
4586 UIActionSheet *sheet = [[[UIActionSheet alloc]
4587 initWithTitle:@"Enter Cydia/APT URL"
4588 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4589 defaultButtonIndex:0
4594 [sheet setNumberOfRows:1];
4596 [sheet addTextFieldWithValue:@"http://" label:@""];
4598 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4599 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4600 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4601 [traits setKeyboardType:UIKeyboardTypeURL];
4602 // XXX: UIReturnKeyDone
4603 [traits setReturnKeyType:UIReturnKeyNext];
4605 [sheet popupAlertAnimated:YES];
4608 - (void) _rightButtonClicked {
4609 UITable *table = [list_ table];
4610 BOOL editing = [table isRowDeletionEnabled];
4611 [table enableRowDeletion:!editing animated:YES];
4612 [book_ reloadButtonsForPage:self];
4615 - (NSString *) title {
4619 - (NSString *) leftButtonTitle {
4620 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4623 - (id) rightButtonTitle {
4624 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4627 - (UINavigationButtonStyle) rightButtonStyle {
4628 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4634 /* Installed View {{{ */
4635 @interface InstalledView : RVPage {
4636 _transient Database *database_;
4637 PackageTable *packages_;
4641 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4645 @implementation InstalledView
4648 [packages_ release];
4652 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4653 if ((self = [super initWithBook:book]) != nil) {
4654 database_ = database;
4656 packages_ = [[PackageTable alloc]
4660 filter:@selector(isInstalledAndVisible:)
4661 with:[NSNumber numberWithBool:YES]
4664 [self addSubview:packages_];
4666 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4667 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4671 - (void) resetViewAnimated:(BOOL)animated {
4672 [packages_ resetViewAnimated:animated];
4675 - (void) reloadData {
4676 [packages_ reloadData];
4679 - (void) _rightButtonClicked {
4680 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4681 [packages_ reloadData];
4683 [book_ reloadButtonsForPage:self];
4686 - (NSString *) title {
4687 return @"Installed";
4690 - (NSString *) backButtonTitle {
4694 - (id) rightButtonTitle {
4695 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4698 - (UINavigationButtonStyle) rightButtonStyle {
4699 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4702 - (void) setDelegate:(id)delegate {
4703 [super setDelegate:delegate];
4704 [packages_ setDelegate:delegate];
4711 @interface HomeView : BrowserView {
4716 @implementation HomeView
4718 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4719 NSString *context([sheet context]);
4721 if ([context isEqualToString:@"about"])
4724 [super alertSheet:sheet buttonClicked:button];
4727 - (void) _leftButtonClicked {
4728 UIActionSheet *sheet = [[[UIActionSheet alloc]
4729 initWithTitle:@"About Cydia Installer"
4730 buttons:[NSArray arrayWithObjects:@"Close", nil]
4731 defaultButtonIndex:0
4737 @"Copyright (C) 2008\n"
4738 "Jay Freeman (saurik)\n"
4739 "saurik@saurik.com\n"
4740 "http://www.saurik.com/\n"
4743 "http://www.theokorigroup.com/\n"
4745 "College of Creative Studies,\n"
4746 "University of California,\n"
4748 "http://www.ccs.ucsb.edu/"
4751 [sheet popupAlertAnimated:YES];
4754 - (NSString *) leftButtonTitle {
4760 /* Manage View {{{ */
4761 @interface ManageView : BrowserView {
4766 @implementation ManageView
4768 - (NSString *) title {
4772 - (void) _leftButtonClicked {
4773 [delegate_ askForSettings];
4776 - (NSString *) leftButtonTitle {
4781 - (id) _rightButtonTitle {
4793 /* Indirect Delegate {{{ */
4794 @interface IndirectDelegate : NSProxy {
4795 _transient volatile id delegate_;
4798 - (void) setDelegate:(id)delegate;
4799 - (id) initWithDelegate:(id)delegate;
4802 @implementation IndirectDelegate
4804 - (void) setDelegate:(id)delegate {
4805 delegate_ = delegate;
4808 - (id) initWithDelegate:(id)delegate {
4809 delegate_ = delegate;
4813 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4814 if (delegate_ != nil)
4815 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4817 // XXX: I fucking hate Apple so very very bad
4818 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4821 - (void) forwardInvocation:(NSInvocation *)inv {
4822 SEL sel = [inv selector];
4823 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4824 [inv invokeWithTarget:delegate_];
4830 #include <BrowserView.m>
4832 /* Cydia Book {{{ */
4833 @interface CYBook : RVBook <
4836 _transient Database *database_;
4837 UINavigationBar *overlay_;
4838 UINavigationBar *underlay_;
4839 UIProgressIndicator *indicator_;
4840 UITextLabel *prompt_;
4841 UIProgressBar *progress_;
4842 UINavigationButton *cancel_;
4845 NSTimeInterval last_;
4848 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4854 @implementation CYBook
4858 [indicator_ release];
4860 [progress_ release];
4865 - (NSString *) getTitleForPage:(RVPage *)page {
4866 return Simplify([super getTitleForPage:page]);
4874 [UIView beginAnimations:nil context:NULL];
4876 CGRect ovrframe = [overlay_ frame];
4877 ovrframe.origin.y = 0;
4878 [overlay_ setFrame:ovrframe];
4880 CGRect barframe = [navbar_ frame];
4881 barframe.origin.y += ovrframe.size.height;
4882 [navbar_ setFrame:barframe];
4884 CGRect trnframe = [transition_ frame];
4885 trnframe.origin.y += ovrframe.size.height;
4886 trnframe.size.height -= ovrframe.size.height;
4887 [transition_ setFrame:trnframe];
4889 [UIView endAnimations];
4891 [indicator_ startAnimation];
4892 [prompt_ setText:@"Updating Database"];
4893 [progress_ setProgress:0];
4896 last_ = [NSDate timeIntervalSinceReferenceDate];
4898 [overlay_ addSubview:cancel_];
4901 detachNewThreadSelector:@selector(_update)
4910 [indicator_ stopAnimation];
4912 [UIView beginAnimations:nil context:NULL];
4914 CGRect ovrframe = [overlay_ frame];
4915 ovrframe.origin.y = -ovrframe.size.height;
4916 [overlay_ setFrame:ovrframe];
4918 CGRect barframe = [navbar_ frame];
4919 barframe.origin.y -= ovrframe.size.height;
4920 [navbar_ setFrame:barframe];
4922 CGRect trnframe = [transition_ frame];
4923 trnframe.origin.y -= ovrframe.size.height;
4924 trnframe.size.height += ovrframe.size.height;
4925 [transition_ setFrame:trnframe];
4927 [UIView commitAnimations];
4929 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4932 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4933 if ((self = [super initWithFrame:frame]) != nil) {
4934 database_ = database;
4936 CGRect ovrrect = [navbar_ bounds];
4937 ovrrect.size.height = [UINavigationBar defaultSize].height;
4938 ovrrect.origin.y = -ovrrect.size.height;
4940 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4941 [self addSubview:overlay_];
4943 ovrrect.origin.y = frame.size.height;
4944 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4945 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4946 [self addSubview:underlay_];
4948 [overlay_ setBarStyle:1];
4949 [underlay_ setBarStyle:1];
4951 int barstyle = [overlay_ _barStyle:NO];
4952 bool ugly = barstyle == 0;
4954 UIProgressIndicatorStyle style = ugly ?
4955 UIProgressIndicatorStyleMediumBrown :
4956 UIProgressIndicatorStyleMediumWhite;
4958 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4959 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4960 CGRect indrect = {{indoffset, indoffset}, indsize};
4962 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4963 [indicator_ setStyle:style];
4964 [overlay_ addSubview:indicator_];
4966 CGSize prmsize = {215, indsize.height + 4};
4969 indoffset * 2 + indsize.width,
4973 unsigned(ovrrect.size.height - prmsize.height) / 2
4976 UIFont *font = [UIFont systemFontOfSize:15];
4978 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4980 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
4981 [prompt_ setBackgroundColor:[UIColor clearColor]];
4982 [prompt_ setFont:font];
4984 [overlay_ addSubview:prompt_];
4986 CGSize prgsize = {75, 100};
4989 ovrrect.size.width - prgsize.width - 10,
4990 (ovrrect.size.height - prgsize.height) / 2
4993 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4994 [progress_ setStyle:0];
4995 [overlay_ addSubview:progress_];
4997 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
4998 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5000 CGRect frame = [cancel_ frame];
5001 frame.size.width = 65;
5002 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5003 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5004 [cancel_ setFrame:frame];
5006 [cancel_ setBarStyle:barstyle];
5010 - (void) _onCancel {
5012 [cancel_ removeFromSuperview];
5015 - (void) _update { _pooled
5017 status.setDelegate(self);
5019 [database_ updateWithStatus:status];
5022 performSelectorOnMainThread:@selector(_update_)
5028 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5029 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5032 - (void) setProgressTitle:(NSString *)title {
5034 performSelectorOnMainThread:@selector(_setProgressTitle:)
5040 - (void) setProgressPercent:(float)percent {
5042 performSelectorOnMainThread:@selector(_setProgressPercent:)
5043 withObject:[NSNumber numberWithFloat:percent]
5048 - (void) startProgress {
5051 - (void) addProgressOutput:(NSString *)output {
5053 performSelectorOnMainThread:@selector(_addProgressOutput:)
5059 - (bool) isCancelling:(size_t)received {
5060 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5061 if (received_ != received) {
5062 received_ = received;
5064 } else if (now - last_ > 15)
5069 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5073 - (void) _setProgressTitle:(NSString *)title {
5074 [prompt_ setText:title];
5077 - (void) _setProgressPercent:(NSNumber *)percent {
5078 [progress_ setProgress:[percent floatValue]];
5081 - (void) _addProgressOutput:(NSString *)output {
5086 /* Cydia:// Protocol {{{ */
5087 @interface CydiaURLProtocol : NSURLProtocol {
5092 @implementation CydiaURLProtocol
5094 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5095 NSURL *url([request URL]);
5098 NSString *scheme([[url scheme] lowercaseString]);
5099 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5104 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5108 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5109 id<NSURLProtocolClient> client([self client]);
5110 NSData *data(UIImagePNGRepresentation(icon));
5112 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5113 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5114 [client URLProtocol:self didLoadData:data];
5115 [client URLProtocolDidFinishLoading:self];
5118 - (void) startLoading {
5119 id<NSURLProtocolClient> client([self client]);
5120 NSURLRequest *request([self request]);
5122 NSURL *url([request URL]);
5123 NSString *href([url absoluteString]);
5125 NSString *path([href substringFromIndex:8]);
5126 NSRange slash([path rangeOfString:@"/"]);
5129 if (slash.location == NSNotFound) {
5133 command = [path substringToIndex:slash.location];
5134 path = [path substringFromIndex:(slash.location + 1)];
5137 Database *database([Database sharedInstance]);
5139 if ([command isEqualToString:@"package-icon"]) {
5142 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5143 Package *package([database packageWithName:path]);
5146 UIImage *icon([package icon]);
5147 [self _returnPNGWithImage:icon forRequest:request];
5148 } else if ([command isEqualToString:@"source-icon"]) {
5151 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5152 NSString *source(Simplify(path));
5153 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5155 icon = [UIImage applicationImageNamed:@"unknown.png"];
5156 [self _returnPNGWithImage:icon forRequest:request];
5157 } else if ([command isEqualToString:@"uikit-image"]) {
5160 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5161 UIImage *icon(_UIImageWithName(path));
5162 [self _returnPNGWithImage:icon forRequest:request];
5163 } else if ([command isEqualToString:@"section-icon"]) {
5166 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5167 NSString *section(Simplify(path));
5168 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5170 icon = [UIImage applicationImageNamed:@"unknown.png"];
5171 [self _returnPNGWithImage:icon forRequest:request];
5173 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5177 - (void) stopLoading {
5183 /* Install View {{{ */
5184 @interface InstallView : RVPage {
5185 _transient Database *database_;
5186 NSMutableArray *sections_;
5187 NSMutableArray *filtered_;
5188 UITransitionView *transition_;
5194 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5195 - (void) reloadData;
5200 @implementation InstallView
5203 [list_ setDataSource:nil];
5204 [list_ setDelegate:nil];
5206 [sections_ release];
5207 [filtered_ release];
5208 [transition_ release];
5210 [accessory_ release];
5214 - (int) numberOfRowsInTable:(UITable *)table {
5215 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5218 - (float) table:(UITable *)table heightForRow:(int)row {
5222 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5224 reusing = [[[SectionCell alloc] init] autorelease];
5225 [(SectionCell *)reusing setSection:(editing_ ?
5226 [sections_ objectAtIndex:row] :
5227 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5228 ) editing:editing_];
5232 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5236 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5240 - (void) tableRowSelected:(NSNotification *)notification {
5241 int row = [[notification object] selectedRow];
5252 title = @"All Packages";
5254 section = [filtered_ objectAtIndex:(row - 1)];
5255 name = [section name];
5261 title = @"(No Section)";
5265 PackageTable *table = [[[PackageTable alloc]
5269 filter:@selector(isVisiblyUninstalledInSection:)
5273 [table setDelegate:delegate_];
5275 [book_ pushPage:table];
5278 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5279 if ((self = [super initWithBook:book]) != nil) {
5280 database_ = database;
5282 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5283 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5285 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5286 [self addSubview:transition_];
5288 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5289 [transition_ transition:0 toView:list_];
5291 UITableColumn *column = [[[UITableColumn alloc]
5292 initWithTitle:@"Name"
5294 width:[self frame].size.width
5297 [list_ setDataSource:self];
5298 [list_ setSeparatorStyle:1];
5299 [list_ addTableColumn:column];
5300 [list_ setDelegate:self];
5301 [list_ setReusesTableCells:YES];
5305 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5306 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5310 - (void) reloadData {
5311 NSArray *packages = [database_ packages];
5313 [sections_ removeAllObjects];
5314 [filtered_ removeAllObjects];
5316 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5317 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5320 for (size_t i(0); i != [packages count]; ++i) {
5321 Package *package([packages objectAtIndex:i]);
5322 NSString *name([package section]);
5325 Section *section([sections objectForKey:name]);
5326 if (section == nil) {
5327 section = [[[Section alloc] initWithName:name] autorelease];
5328 [sections setObject:section forKey:name];
5332 if ([package valid] && [package installed] == nil && [package visible])
5333 [filtered addObject:package];
5337 [sections_ addObjectsFromArray:[sections allValues]];
5338 [sections_ sortUsingSelector:@selector(compareByName:)];
5341 [filtered sortUsingSelector:@selector(compareBySection:)];
5344 Section *section = nil;
5345 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5346 Package *package = [filtered objectAtIndex:offset];
5347 NSString *name = [package section];
5349 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5350 section = name == nil ?
5351 [[[Section alloc] initWithName:nil] autorelease] :
5352 [sections objectForKey:name];
5353 [filtered_ addObject:section];
5356 [section addToCount];
5364 - (void) resetView {
5366 [self _rightButtonClicked];
5369 - (void) resetViewAnimated:(BOOL)animated {
5370 [list_ resetViewAnimated:animated];
5373 - (void) _rightButtonClicked {
5374 if ((editing_ = !editing_))
5377 [delegate_ updateData];
5378 [book_ reloadTitleForPage:self];
5379 [book_ reloadButtonsForPage:self];
5382 - (NSString *) title {
5383 return editing_ ? @"Section Visibility" : @"Install by Section";
5386 - (NSString *) backButtonTitle {
5390 - (id) rightButtonTitle {
5391 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5394 - (UINavigationButtonStyle) rightButtonStyle {
5395 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5398 - (UIView *) accessoryView {
5404 /* Changes View {{{ */
5405 @interface ChangesView : RVPage {
5406 _transient Database *database_;
5407 NSMutableArray *packages_;
5408 NSMutableArray *sections_;
5409 UISectionList *list_;
5413 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5414 - (void) reloadData;
5418 @implementation ChangesView
5421 [[list_ table] setDelegate:nil];
5422 [list_ setDataSource:nil];
5424 [packages_ release];
5425 [sections_ release];
5430 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5431 return [sections_ count];
5434 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5435 return [[sections_ objectAtIndex:section] name];
5438 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5439 return [[sections_ objectAtIndex:section] row];
5442 - (int) numberOfRowsInTable:(UITable *)table {
5443 return [packages_ count];
5446 - (float) table:(UITable *)table heightForRow:(int)row {
5447 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5450 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5452 reusing = [[[PackageCell alloc] init] autorelease];
5453 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5457 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5461 - (void) tableRowSelected:(NSNotification *)notification {
5462 int row = [[notification object] selectedRow];
5465 Package *package = [packages_ objectAtIndex:row];
5466 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5467 [view setDelegate:delegate_];
5468 [view setPackage:package];
5469 [book_ pushPage:view];
5472 - (void) _leftButtonClicked {
5473 [(CYBook *)book_ update];
5474 [self reloadButtons];
5477 - (void) _rightButtonClicked {
5478 [delegate_ distUpgrade];
5481 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5482 if ((self = [super initWithBook:book]) != nil) {
5483 database_ = database;
5485 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5486 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5488 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5489 [self addSubview:list_];
5491 [list_ setShouldHideHeaderInShortLists:NO];
5492 [list_ setDataSource:self];
5493 //[list_ setSectionListStyle:1];
5495 UITableColumn *column = [[[UITableColumn alloc]
5496 initWithTitle:@"Name"
5498 width:[self frame].size.width
5501 UITable *table = [list_ table];
5502 [table setSeparatorStyle:1];
5503 [table addTableColumn:column];
5504 [table setDelegate:self];
5505 [table setReusesTableCells:YES];
5509 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5510 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5514 - (void) reloadData {
5515 NSArray *packages = [database_ packages];
5517 [packages_ removeAllObjects];
5518 [sections_ removeAllObjects];
5521 for (size_t i(0); i != [packages count]; ++i) {
5522 Package *package([packages objectAtIndex:i]);
5525 [package installed] == nil && [package valid] && [package visible] ||
5526 [package upgradableAndEssential:NO]
5528 [packages_ addObject:package];
5532 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5535 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5536 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5537 Section *section = nil;
5541 bool unseens = false;
5543 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5546 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5547 Package *package = [packages_ objectAtIndex:offset];
5549 if (![package upgradableAndEssential:YES]) {
5551 NSDate *seen = [package seen];
5553 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5556 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5557 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5558 [sections_ addObject:section];
5562 [section addToCount];
5563 } else if ([package ignored])
5564 [ignored addToCount];
5567 [upgradable addToCount];
5572 CFRelease(formatter);
5575 Section *last = [sections_ lastObject];
5576 size_t count = [last count];
5577 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5578 [sections_ removeLastObject];
5581 if ([ignored count] != 0)
5582 [sections_ insertObject:ignored atIndex:0];
5584 [sections_ insertObject:upgradable atIndex:0];
5587 [self reloadButtons];
5590 - (void) resetViewAnimated:(BOOL)animated {
5591 [list_ resetViewAnimated:animated];
5594 - (NSString *) leftButtonTitle {
5595 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5598 - (id) rightButtonTitle {
5599 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5602 - (NSString *) title {
5608 /* Search View {{{ */
5609 @protocol SearchViewDelegate
5610 - (void) showKeyboard:(BOOL)show;
5613 @interface SearchView : RVPage {
5615 UISearchField *field_;
5616 UITransitionView *transition_;
5617 PackageTable *table_;
5618 UIPreferencesTable *advanced_;
5624 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5625 - (void) reloadData;
5629 @implementation SearchView
5632 [field_ setDelegate:nil];
5634 [accessory_ release];
5636 [transition_ release];
5638 [advanced_ release];
5643 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5647 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5649 case 0: return @"Advanced Search (Coming Soon!)";
5651 default: _assert(false);
5655 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5659 default: _assert(false);
5663 - (void) _showKeyboard:(BOOL)show {
5664 CGSize keysize = [UIKeyboard defaultSize];
5665 CGRect keydown = [book_ pageBounds];
5666 CGRect keyup = keydown;
5667 keyup.size.height -= keysize.height - ButtonBarHeight_;
5669 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5671 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5672 [animation setSignificantRectFields:8];
5675 [animation setStartFrame:keydown];
5676 [animation setEndFrame:keyup];
5678 [animation setStartFrame:keyup];
5679 [animation setEndFrame:keydown];
5682 UIAnimator *animator = [UIAnimator sharedAnimator];
5685 addAnimations:[NSArray arrayWithObjects:animation, nil]
5686 withDuration:(KeyboardTime_ - delay)
5691 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5693 [delegate_ showKeyboard:show];
5696 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5697 [self _showKeyboard:YES];
5700 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5701 [self _showKeyboard:NO];
5704 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5706 NSString *text([field_ text]);
5707 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5713 - (void) textFieldClearButtonPressed:(UITextField *)field {
5717 - (void) keyboardInputShouldDelete:(id)input {
5721 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5722 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5726 [field_ resignFirstResponder];
5731 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5732 if ((self = [super initWithBook:book]) != nil) {
5733 CGRect pageBounds = [book_ pageBounds];
5735 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5736 [self addSubview:transition_];
5738 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5740 [advanced_ setReusesTableCells:YES];
5741 [advanced_ setDataSource:self];
5742 [advanced_ reloadData];
5744 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5745 CGColor dimmed(space_, 0, 0, 0, 0.5);
5746 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5748 table_ = [[PackageTable alloc]
5752 filter:@selector(isUnfilteredAndSearchedForBy:)
5756 [table_ setShouldHideHeaderInShortLists:NO];
5757 [transition_ transition:0 toView:table_];
5766 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5773 [self bounds].size.width - area.origin.x - 18;
5775 area.size.height = [UISearchField defaultHeight];
5777 field_ = [[UISearchField alloc] initWithFrame:area];
5779 UIFont *font = [UIFont systemFontOfSize:16];
5780 [field_ setFont:font];
5782 [field_ setPlaceholder:@"Package Names & Descriptions"];
5783 [field_ setDelegate:self];
5785 [field_ setPaddingTop:5];
5787 UITextInputTraits *traits([field_ textInputTraits]);
5788 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5789 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5790 [traits setReturnKeyType:UIReturnKeySearch];
5792 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5794 accessory_ = [[UIView alloc] initWithFrame:accrect];
5795 [accessory_ addSubview:field_];
5797 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5798 [configure setShowPressFeedback:YES];
5799 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5800 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5801 [accessory_ addSubview:configure];*/
5803 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5804 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5810 LKAnimation *animation = [LKTransition animation];
5811 [animation setType:@"oglFlip"];
5812 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5813 [animation setFillMode:@"extended"];
5814 [animation setTransitionFlags:3];
5815 [animation setDuration:10];
5816 [animation setSpeed:0.35];
5817 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5818 [[transition_ _layer] addAnimation:animation forKey:0];
5819 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5820 flipped_ = !flipped_;
5824 - (void) configurePushed {
5825 [field_ resignFirstResponder];
5829 - (void) resetViewAnimated:(BOOL)animated {
5832 [table_ resetViewAnimated:animated];
5835 - (void) _reloadData {
5838 - (void) reloadData {
5841 [table_ setObject:[field_ text]];
5842 [table_ reloadData];
5843 [table_ resetCursor];
5846 - (UIView *) accessoryView {
5850 - (NSString *) title {
5854 - (NSString *) backButtonTitle {
5858 - (void) setDelegate:(id)delegate {
5859 [table_ setDelegate:delegate];
5860 [super setDelegate:delegate];
5866 @interface SettingsView : RVPage {
5867 _transient Database *database_;
5870 UIPreferencesTable *table_;
5871 _UISwitchSlider *subscribedSwitch_;
5872 _UISwitchSlider *ignoredSwitch_;
5873 UIPreferencesControlTableCell *subscribedCell_;
5874 UIPreferencesControlTableCell *ignoredCell_;
5877 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5881 @implementation SettingsView
5884 [table_ setDataSource:nil];
5887 if (package_ != nil)
5890 [subscribedSwitch_ release];
5891 [ignoredSwitch_ release];
5892 [subscribedCell_ release];
5893 [ignoredCell_ release];
5897 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5898 if (package_ == nil)
5904 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5905 if (package_ == nil)
5912 default: _assert(false);
5918 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5919 if (package_ == nil)
5926 default: _assert(false);
5932 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5933 if (package_ == nil)
5940 default: _assert(false);
5946 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5947 if (package_ == nil)
5950 _UISwitchSlider *slider([cell control]);
5951 BOOL value([slider value] != 0);
5952 NSMutableDictionary *metadata([package_ metadata]);
5955 if (NSNumber *number = [metadata objectForKey:key])
5956 before = [number boolValue];
5960 if (value != before) {
5961 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5963 [delegate_ updateData];
5967 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
5968 [self onSomething:cell withKey:@"IsSubscribed"];
5971 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
5972 [self onSomething:cell withKey:@"IsIgnored"];
5975 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
5976 if (package_ == nil)
5980 case 0: switch (row) {
5982 return subscribedCell_;
5984 return ignoredCell_;
5985 default: _assert(false);
5988 case 1: switch (row) {
5990 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
5991 [cell setShowSelection:NO];
5992 [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."];
5996 default: _assert(false);
5999 default: _assert(false);
6005 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6006 if ((self = [super initWithBook:book])) {
6007 database_ = database;
6008 name_ = [package retain];
6010 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6011 [self addSubview:table_];
6013 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6014 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6016 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6017 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6019 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6020 [subscribedCell_ setShowSelection:NO];
6021 [subscribedCell_ setTitle:@"Show All Changes"];
6022 [subscribedCell_ setControl:subscribedSwitch_];
6024 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6025 [ignoredCell_ setShowSelection:NO];
6026 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6027 [ignoredCell_ setControl:ignoredSwitch_];
6029 [table_ setDataSource:self];
6034 - (void) resetViewAnimated:(BOOL)animated {
6035 [table_ resetViewAnimated:animated];
6038 - (void) reloadData {
6039 if (package_ != nil)
6040 [package_ autorelease];
6041 package_ = [database_ packageWithName:name_];
6042 if (package_ != nil) {
6044 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6045 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6048 [table_ reloadData];
6051 - (NSString *) title {
6057 /* Signature View {{{ */
6058 @interface SignatureView : BrowserView {
6059 _transient Database *database_;
6063 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6067 @implementation SignatureView
6074 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6076 [super webView:sender didClearWindowObject:window forFrame:frame];
6079 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6080 if ((self = [super initWithBook:book]) != nil) {
6081 database_ = database;
6082 package_ = [package retain];
6087 - (void) resetViewAnimated:(BOOL)animated {
6090 - (void) reloadData {
6091 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6097 @interface Cydia : UIApplication <
6098 ConfirmationViewDelegate,
6099 ProgressViewDelegate,
6108 UIToolbar *buttonbar_;
6112 NSMutableArray *essential_;
6113 NSMutableArray *broken_;
6115 Database *database_;
6116 ProgressView *progress_;
6120 UIKeyboard *keyboard_;
6121 UIProgressHUD *hud_;
6123 InstallView *install_;
6124 ChangesView *changes_;
6125 ManageView *manage_;
6126 SearchView *search_;
6131 @implementation Cydia
6134 if ([broken_ count] != 0) {
6135 int count = [broken_ count];
6137 UIActionSheet *sheet = [[[UIActionSheet alloc]
6138 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6139 buttons:[NSArray arrayWithObjects:
6141 @"Ignore (Temporary)",
6143 defaultButtonIndex:0
6148 [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."];
6149 [sheet popupAlertAnimated:YES];
6150 } else if (!Ignored_ && [essential_ count] != 0) {
6151 int count = [essential_ count];
6153 UIActionSheet *sheet = [[[UIActionSheet alloc]
6154 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6155 buttons:[NSArray arrayWithObjects:
6156 @"Upgrade Essential",
6157 @"Complete Upgrade",
6158 @"Ignore (Temporary)",
6160 defaultButtonIndex:0
6165 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6166 [sheet popupAlertAnimated:YES];
6170 - (void) _reloadData {
6171 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6172 [hud setText:@"Reloading Data"];
6173 [overlay_ addSubview:hud];
6176 [database_ reloadData];
6180 [essential_ removeAllObjects];
6181 [broken_ removeAllObjects];
6183 NSArray *packages = [database_ packages];
6184 for (Package *package in packages) {
6186 [broken_ addObject:package];
6187 if ([package upgradableAndEssential:NO]) {
6188 if ([package essential])
6189 [essential_ addObject:package];
6195 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6196 [buttonbar_ setBadgeValue:badge forButton:3];
6197 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6198 [buttonbar_ setBadgeAnimated:YES forButton:3];
6199 [self setApplicationBadge:badge];
6201 [buttonbar_ setBadgeValue:nil forButton:3];
6202 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6203 [buttonbar_ setBadgeAnimated:NO forButton:3];
6204 [self removeApplicationBadge];
6210 if ([packages count] == 0);
6222 [hud removeFromSuperview];*/
6225 - (void) _saveConfig {
6228 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6234 - (void) updateData {
6237 /* XXX: this is just stupid */
6238 if (tag_ != 2 && install_ != nil)
6239 [install_ reloadData];
6240 if (tag_ != 3 && changes_ != nil)
6241 [changes_ reloadData];
6242 if (tag_ != 5 && search_ != nil)
6243 [search_ reloadData];
6253 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6254 _assert(file != NULL);
6256 NSArray *keys = [Sources_ allKeys];
6258 for (int i(0), e([keys count]); i != e; ++i) {
6259 NSString *key = [keys objectAtIndex:i];
6260 NSDictionary *source = [Sources_ objectForKey:key];
6262 fprintf(file, "%s %s %s\n",
6263 [[source objectForKey:@"Type"] UTF8String],
6264 [[source objectForKey:@"URI"] UTF8String],
6265 [[source objectForKey:@"Distribution"] UTF8String]
6274 detachNewThreadSelector:@selector(update_)
6277 title:@"Updating Sources"
6281 - (void) reloadData {
6282 @synchronized (self) {
6283 if (confirm_ == nil)
6289 pkgProblemResolver *resolver = [database_ resolver];
6291 resolver->InstallProtect();
6292 if (!resolver->Resolve(true))
6297 [database_ prepare];
6299 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6300 [confirm_ setDelegate:self];
6302 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6303 [page setDelegate:self];
6305 [confirm_ setPage:page];
6306 [underlay_ popSubview:confirm_];
6309 - (void) installPackage:(Package *)package {
6310 @synchronized (self) {
6317 - (void) removePackage:(Package *)package {
6318 @synchronized (self) {
6325 - (void) distUpgrade {
6326 @synchronized (self) {
6327 [database_ upgrade];
6333 @synchronized (self) {
6335 if (confirm_ != nil) {
6343 [overlay_ removeFromSuperview];
6347 detachNewThreadSelector:@selector(perform)
6354 - (void) bootstrap_ {
6356 [database_ upgrade];
6357 [database_ prepare];
6358 [database_ perform];
6361 - (void) bootstrap {
6363 detachNewThreadSelector:@selector(bootstrap_)
6366 title:@"Bootstrap Install"
6370 - (void) progressViewIsComplete:(ProgressView *)progress {
6371 if (confirm_ != nil) {
6372 [underlay_ addSubview:overlay_];
6373 [confirm_ popFromSuperviewAnimated:NO];
6379 - (void) setPage:(RVPage *)page {
6380 [page resetViewAnimated:NO];
6381 [page setDelegate:self];
6382 [book_ setPage:page];
6385 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6386 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6387 [browser loadURL:url];
6391 - (void) _setHomePage {
6392 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6395 - (void) buttonBarItemTapped:(id)sender {
6396 unsigned tag = [sender tag];
6398 [book_ resetViewAnimated:YES];
6400 } else if (tag_ == 2 && tag != 2)
6401 [install_ resetView];
6404 case 1: [self _setHomePage]; break;
6406 case 2: [self setPage:install_]; break;
6407 case 3: [self setPage:changes_]; break;
6408 case 4: [self setPage:manage_]; break;
6409 case 5: [self setPage:search_]; break;
6411 default: _assert(false);
6417 - (void) applicationWillSuspend {
6419 [super applicationWillSuspend];
6422 - (void) askForSettings {
6423 UIActionSheet *role = [[[UIActionSheet alloc]
6424 initWithTitle:@"Who Are You?"
6425 buttons:[NSArray arrayWithObjects:
6426 @"User (Graphical Only)",
6427 @"Hacker (+ Command Line)",
6428 @"Developer (No Filters)",
6430 defaultButtonIndex:-1
6435 [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."];
6436 [role popupAlertAnimated:YES];
6441 [self setStatusBarShowsProgress:NO];
6444 [hud_ removeFromSuperview];
6448 pid_t pid = ExecFork();
6450 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6451 perror("launchctl stop");
6458 [self askForSettings];
6462 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6464 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6465 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6466 0, 0, screenrect.size.width, screenrect.size.height - 48
6467 ) database:database_];
6469 [book_ setDelegate:self];
6471 [overlay_ addSubview:book_];
6473 NSArray *buttonitems = [NSArray arrayWithObjects:
6474 [NSDictionary dictionaryWithObjectsAndKeys:
6475 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6476 @"home-up.png", kUIButtonBarButtonInfo,
6477 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6478 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6479 self, kUIButtonBarButtonTarget,
6480 @"Home", kUIButtonBarButtonTitle,
6481 @"0", kUIButtonBarButtonType,
6484 [NSDictionary dictionaryWithObjectsAndKeys:
6485 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6486 @"install-up.png", kUIButtonBarButtonInfo,
6487 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6488 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6489 self, kUIButtonBarButtonTarget,
6490 @"Sections", kUIButtonBarButtonTitle,
6491 @"0", kUIButtonBarButtonType,
6494 [NSDictionary dictionaryWithObjectsAndKeys:
6495 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6496 @"changes-up.png", kUIButtonBarButtonInfo,
6497 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6498 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6499 self, kUIButtonBarButtonTarget,
6500 @"Changes", kUIButtonBarButtonTitle,
6501 @"0", kUIButtonBarButtonType,
6504 [NSDictionary dictionaryWithObjectsAndKeys:
6505 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6506 @"manage-up.png", kUIButtonBarButtonInfo,
6507 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6508 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6509 self, kUIButtonBarButtonTarget,
6510 @"Manage", kUIButtonBarButtonTitle,
6511 @"0", kUIButtonBarButtonType,
6514 [NSDictionary dictionaryWithObjectsAndKeys:
6515 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6516 @"search-up.png", kUIButtonBarButtonInfo,
6517 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6518 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6519 self, kUIButtonBarButtonTarget,
6520 @"Search", kUIButtonBarButtonTitle,
6521 @"0", kUIButtonBarButtonType,
6525 buttonbar_ = [[UIToolbar alloc]
6527 withFrame:CGRectMake(
6528 0, screenrect.size.height - ButtonBarHeight_,
6529 screenrect.size.width, ButtonBarHeight_
6531 withItemList:buttonitems
6534 [buttonbar_ setDelegate:self];
6535 [buttonbar_ setBarStyle:1];
6536 [buttonbar_ setButtonBarTrackingMode:2];
6538 int buttons[5] = {1, 2, 3, 4, 5};
6539 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6540 [buttonbar_ showButtonGroup:0 withDuration:0];
6542 for (int i = 0; i != 5; ++i)
6543 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6544 i * 64 + 2, 1, 60, ButtonBarHeight_
6547 [buttonbar_ showSelectionForButton:1];
6548 [overlay_ addSubview:buttonbar_];
6550 [UIKeyboard initImplementationNow];
6551 CGSize keysize = [UIKeyboard defaultSize];
6552 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6553 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6554 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6555 [overlay_ addSubview:keyboard_];
6558 [underlay_ addSubview:overlay_];
6562 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6563 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6564 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6566 manage_ = (ManageView *) [[self
6567 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6568 withClass:[ManageView class]
6574 [self _setHomePage];
6577 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6578 NSString *context([sheet context]);
6580 if ([context isEqualToString:@"missing"])
6582 else if ([context isEqualToString:@"fixhalf"]) {
6585 @synchronized (self) {
6586 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6587 Package *broken = [broken_ objectAtIndex:i];
6590 NSString *id = [broken id];
6591 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6592 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6593 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6594 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6603 [broken_ removeAllObjects];
6612 } else if ([context isEqualToString:@"role"]) {
6614 case 1: Role_ = @"User"; break;
6615 case 2: Role_ = @"Hacker"; break;
6616 case 3: Role_ = @"Developer"; break;
6623 bool reset = Settings_ != nil;
6625 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6629 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6639 } else if ([context isEqualToString:@"upgrade"]) {
6642 @synchronized (self) {
6643 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6644 Package *essential = [essential_ objectAtIndex:i];
6645 [essential install];
6669 - (void) reorganize { _pooled
6670 system("/usr/libexec/cydia/free.sh");
6671 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6674 - (void) applicationSuspend:(__GSEvent *)event {
6675 if (hud_ == nil && ![progress_ isRunning])
6676 [super applicationSuspend:event];
6679 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6681 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6684 - (void) _setSuspended:(BOOL)value {
6686 [super _setSuspended:value];
6689 - (UIProgressHUD *) addProgressHUD {
6690 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6692 [underlay_ addSubview:hud];
6696 - (void) openMailToURL:(NSURL *)url {
6697 // XXX: this makes me sad
6699 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6701 [UIApp openURL:url];// asPanel:YES];
6705 - (void) clearFirstResponder {
6706 if (id responder = [window_ firstResponder])
6707 [responder resignFirstResponder];
6710 - (RVPage *) pageForPackage:(NSString *)name {
6711 if (Package *package = [database_ packageWithName:name]) {
6712 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6713 [view setPackage:package];
6716 UIActionSheet *sheet = [[[UIActionSheet alloc]
6717 initWithTitle:@"Cannot Locate Package"
6718 buttons:[NSArray arrayWithObjects:@"Close", nil]
6719 defaultButtonIndex:0
6724 [sheet setBodyText:[NSString stringWithFormat:
6725 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6728 [sheet popupAlertAnimated:YES];
6733 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6737 NSString *scheme([[url scheme] lowercaseString]);
6738 if (![scheme isEqualToString:@"cydia"])
6740 NSString *path([url absoluteString]);
6741 if ([path length] < 8)
6743 path = [path substringFromIndex:8];
6744 if (![path hasPrefix:@"/"])
6745 path = [@"/" stringByAppendingString:path];
6747 if ([path isEqualToString:@"/add-source"])
6748 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6749 else if ([path isEqualToString:@"/storage"])
6750 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6751 else if ([path isEqualToString:@"/sources"])
6752 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6753 else if ([path isEqualToString:@"/packages"])
6754 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6755 else if ([path hasPrefix:@"/url/"])
6756 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6757 else if ([path hasPrefix:@"/launch/"])
6758 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6759 else if ([path hasPrefix:@"/package-settings/"])
6760 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6761 else if ([path hasPrefix:@"/package-signature/"])
6762 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6763 else if ([path hasPrefix:@"/package/"])
6764 return [self pageForPackage:[path substringFromIndex:9]];
6765 else if ([path hasPrefix:@"/files/"]) {
6766 NSString *name = [path substringFromIndex:7];
6768 if (Package *package = [database_ packageWithName:name]) {
6769 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6770 [files setPackage:package];
6778 - (void) applicationOpenURL:(NSURL *)url {
6779 [super applicationOpenURL:url];
6781 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6782 [self setPage:page];
6783 [buttonbar_ showSelectionForButton:tag];
6788 - (void) applicationDidFinishLaunching:(id)unused {
6789 Font12_ = [[UIFont systemFontOfSize:12] retain];
6790 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6791 Font14_ = [[UIFont systemFontOfSize:14] retain];
6792 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6793 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6795 _assert(pkgInitConfig(*_config));
6796 _assert(pkgInitSystem(*_config, _system));
6800 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6801 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6803 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6805 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6806 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6808 [window_ orderFront:self];
6809 [window_ makeKey:self];
6810 [window_ setHidden:NO];
6812 database_ = [Database sharedInstance];
6813 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6814 [database_ setDelegate:progress_];
6815 [window_ setContentView:progress_];
6817 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6818 [progress_ setContentView:underlay_];
6820 [progress_ resetView];
6823 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6824 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6825 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6826 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6827 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6828 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6829 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6831 [self setIdleTimerDisabled:YES];
6833 hud_ = [self addProgressHUD];
6834 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6836 [self setStatusBarShowsProgress:YES];
6839 detachNewThreadSelector:@selector(reorganize)
6847 - (void) showKeyboard:(BOOL)show {
6848 CGSize keysize = [UIKeyboard defaultSize];
6849 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6850 CGRect keyup = keydown;
6851 keyup.origin.y -= keysize.height;
6853 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6854 [animation setSignificantRectFields:2];
6857 [animation setStartFrame:keydown];
6858 [animation setEndFrame:keyup];
6859 [keyboard_ activate];
6861 [animation setStartFrame:keyup];
6862 [animation setEndFrame:keydown];
6863 [keyboard_ deactivate];
6866 [[UIAnimator sharedAnimator]
6867 addAnimations:[NSArray arrayWithObjects:animation, nil]
6868 withDuration:KeyboardTime_
6873 - (void) slideUp:(UIActionSheet *)alert {
6875 [alert presentSheetFromButtonBar:buttonbar_];
6877 [alert presentSheetInView:overlay_];
6882 void AddPreferences(NSString *plist) { _pooled
6883 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6884 _assert(settings != NULL);
6885 NSMutableArray *items = [settings objectForKey:@"items"];
6889 for (size_t i(0); i != [items count]; ++i) {
6890 NSMutableDictionary *item([items objectAtIndex:i]);
6891 NSString *label = [item objectForKey:@"label"];
6892 if (label != nil && [label isEqualToString:@"Cydia"]) {
6899 for (size_t i(0); i != [items count]; ++i) {
6900 NSDictionary *item([items objectAtIndex:i]);
6901 NSString *label = [item objectForKey:@"label"];
6902 if (label != nil && [label isEqualToString:@"General"]) {
6903 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6904 @"CydiaSettings", @"bundle",
6905 @"PSLinkCell", @"cell",
6906 [NSNumber numberWithBool:YES], @"hasIcon",
6907 [NSNumber numberWithBool:YES], @"isController",
6909 nil] atIndex:(i + 1)];
6915 _assert([settings writeToFile:plist atomically:YES] == YES);
6920 id Alloc_(id self, SEL selector) {
6921 id object = alloc_(self, selector);
6922 lprintf("[%s]A-%p\n", self->isa->name, object);
6927 id Dealloc_(id self, SEL selector) {
6928 id object = dealloc_(self, selector);
6929 lprintf("[%s]D-%p\n", self->isa->name, object);
6933 int main(int argc, char *argv[]) { _pooled
6934 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6936 bool substrate(false);
6942 for (int argi(1); argi != argc; ++argi)
6943 if (strcmp(argv[argi], "--") == 0) {
6945 argv[argi] = argv[0];
6951 for (int argi(1); argi != arge; ++argi)
6952 if (strcmp(args[argi], "--bootstrap") == 0)
6954 else if (strcmp(args[argi], "--substrate") == 0)
6957 fprintf(stderr, "unknown argument: %s\n", args[argi]);
6960 App_ = [[NSBundle mainBundle] bundlePath];
6961 Home_ = NSHomeDirectory();
6962 Locale_ = CFLocaleCopyCurrent();
6965 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6966 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6967 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6968 Sounds_Keyboard_ = [keyboard boolValue];
6974 #if 1 /* XXX: this costs 1.4s of startup performance */
6975 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6976 _assert(errno == ENOENT);
6977 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6978 _assert(errno == ENOENT);
6981 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6982 alloc_ = alloc->method_imp;
6983 alloc->method_imp = (IMP) &Alloc_;*/
6985 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6986 dealloc_ = dealloc->method_imp;
6987 dealloc->method_imp = (IMP) &Dealloc_;*/
6992 size = sizeof(maxproc);
6993 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6994 perror("sysctlbyname(\"kern.maxproc\", ?)");
6995 else if (maxproc < 64) {
6997 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
6998 perror("sysctlbyname(\"kern.maxproc\", #)");
7001 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7002 char *machine = new char[size];
7003 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7004 perror("sysctlbyname(\"hw.machine\", ?)");
7008 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7010 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7011 Build_ = [system objectForKey:@"ProductBuildVersion"];
7013 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7014 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7016 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7017 Indices_ = [[NSMutableDictionary alloc] init];*/
7019 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7020 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
7021 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7024 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7025 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7027 Settings_ = [Metadata_ objectForKey:@"Settings"];
7029 Packages_ = [Metadata_ objectForKey:@"Packages"];
7030 Sections_ = [Metadata_ objectForKey:@"Sections"];
7031 Sources_ = [Metadata_ objectForKey:@"Sources"];
7034 if (Settings_ != nil)
7035 Role_ = [Settings_ objectForKey:@"Role"];
7037 if (Packages_ == nil) {
7038 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7039 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7042 if (Sections_ == nil) {
7043 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7044 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7047 if (Sources_ == nil) {
7048 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7049 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7053 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7056 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7057 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7058 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7059 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7061 if (access("/User", F_OK) != 0)
7062 system("/usr/libexec/cydia/firmware.sh");
7064 _assert([[NSFileManager defaultManager]
7065 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7066 withIntermediateDirectories:YES
7071 space_ = CGColorSpaceCreateDeviceRGB();
7073 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7074 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7075 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7076 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7077 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7078 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7080 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7082 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7084 UIApplicationUseLegacyEvents(YES);
7085 UIKeyboardDisableAutomaticAppearance();
7087 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7089 CGColorSpaceRelease(space_);