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();
1587 value = essential && [self essential];
1589 value = !version_.end() && version_ != current && (!essential || ![database_ cache][iterator_].Keep());
1593 - (BOOL) essential {
1594 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1598 return [database_ cache][iterator_].InstBroken();
1601 - (BOOL) unfiltered {
1602 NSString *section = [self section];
1603 return section == nil || isSectionVisible(section);
1607 return [self hasSupportingRole] && [self unfiltered];
1611 unsigned char current = iterator_->CurrentState;
1612 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1615 - (BOOL) halfConfigured {
1616 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1619 - (BOOL) halfInstalled {
1620 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1624 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1625 return state.Mode != pkgDepCache::ModeKeep;
1628 - (NSString *) mode {
1629 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1631 switch (state.Mode) {
1632 case pkgDepCache::ModeDelete:
1633 if ((state.iFlags & pkgDepCache::Purge) != 0)
1637 case pkgDepCache::ModeKeep:
1638 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1642 case pkgDepCache::ModeInstall:
1643 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1644 return @"Reinstall";
1645 else switch (state.Status) {
1647 return @"Downgrade";
1653 return @"New Install";
1666 - (NSString *) name {
1667 return name_ == nil ? id_ : name_;
1670 - (NSString *) tagline {
1674 - (UIImage *) icon {
1675 NSString *section = [self simpleSection];
1678 if (NSString *icon = icon_)
1679 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1680 if (icon == nil) if (section != nil)
1681 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1682 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1683 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1685 icon = [UIImage applicationImageNamed:@"unknown.png"];
1689 - (NSString *) homepage {
1693 - (NSString *) depiction {
1697 - (Address *) sponsor {
1701 - (Address *) author {
1705 - (NSArray *) files {
1706 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1707 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1710 fin.open([path UTF8String]);
1715 while (std::getline(fin, line))
1716 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1721 - (NSArray *) relationships {
1722 return relationships_;
1725 - (NSArray *) warnings {
1726 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1727 const char *name(iterator_.Name());
1729 size_t length(strlen(name));
1730 if (length < 2) invalid:
1731 [warnings addObject:@"illegal package identifier"];
1732 else for (size_t i(0); i != length; ++i)
1734 /* XXX: technically this is not allowed */
1735 (name[i] < 'A' || name[i] > 'Z') &&
1736 (name[i] < 'a' || name[i] > 'z') &&
1737 (name[i] < '0' || name[i] > '9') &&
1738 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1741 if (strcmp(name, "cydia") != 0) {
1743 bool _private = false;
1746 bool repository = [[self section] isEqualToString:@"Repositories"];
1748 if (NSArray *files = [self files])
1749 for (NSString *file in files)
1750 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1752 else if (!_private && [file isEqualToString:@"/private"])
1754 else if (!stash && [file isEqualToString:@"/var/stash"])
1757 /* XXX: this is not sensitive enough. only some folders are valid. */
1758 if (cydia && !repository)
1759 [warnings addObject:@"files installed into Cydia.app"];
1761 [warnings addObject:@"files installed with /private/*"];
1763 [warnings addObject:@"files installed to /var/stash"];
1766 return [warnings count] == 0 ? nil : warnings;
1769 - (NSArray *) applications {
1770 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1772 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1774 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1775 if (NSArray *files = [self files])
1776 for (NSString *file in files)
1777 if (application_r(file)) {
1778 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1779 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1780 if ([id isEqualToString:me])
1783 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1785 display = application_r[1];
1787 NSString *bundle([file stringByDeletingLastPathComponent]);
1788 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1789 if (icon == nil || [icon length] == 0)
1791 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1793 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1794 [applications addObject:application];
1796 [application addObject:id];
1797 [application addObject:display];
1798 [application addObject:url];
1801 return [applications count] == 0 ? nil : applications;
1804 - (Source *) source {
1806 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1813 - (NSString *) role {
1817 - (NSString *) rating {
1818 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1819 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1824 - (BOOL) matches:(NSString *)text {
1830 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1831 if (range.location != NSNotFound)
1834 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1835 if (range.location != NSNotFound)
1838 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1839 if (range.location != NSNotFound)
1845 - (bool) hasSupportingRole {
1848 if ([role_ isEqualToString:@"enduser"])
1850 if ([Role_ isEqualToString:@"User"])
1852 if ([role_ isEqualToString:@"hacker"])
1854 if ([Role_ isEqualToString:@"Hacker"])
1856 if ([role_ isEqualToString:@"developer"])
1858 if ([Role_ isEqualToString:@"Developer"])
1863 - (BOOL) hasTag:(NSString *)tag {
1864 return tags_ == nil ? NO : [tags_ containsObject:tag];
1867 - (NSString *) primaryPurpose {
1868 for (NSString *tag in tags_)
1869 if ([tag hasPrefix:@"purpose::"])
1870 return [tag substringFromIndex:9];
1874 - (NSArray *) purposes {
1875 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1876 for (NSString *tag in tags_)
1877 if ([tag hasPrefix:@"purpose::"])
1878 [purposes addObject:[tag substringFromIndex:9]];
1879 return [purposes count] == 0 ? nil : purposes;
1882 - (NSComparisonResult) compareByName:(Package *)package {
1883 NSString *lhs = [self name];
1884 NSString *rhs = [package name];
1886 if ([lhs length] != 0 && [rhs length] != 0) {
1887 unichar lhc = [lhs characterAtIndex:0];
1888 unichar rhc = [rhs characterAtIndex:0];
1890 if (isalpha(lhc) && !isalpha(rhc))
1891 return NSOrderedAscending;
1892 else if (!isalpha(lhc) && isalpha(rhc))
1893 return NSOrderedDescending;
1896 return [lhs compare:rhs options:LaxCompareOptions_];
1899 - (NSComparisonResult) compareBySection:(Package *)package {
1900 NSString *lhs = [self section];
1901 NSString *rhs = [package section];
1903 if (lhs == NULL && rhs != NULL)
1904 return NSOrderedAscending;
1905 else if (lhs != NULL && rhs == NULL)
1906 return NSOrderedDescending;
1907 else if (lhs != NULL && rhs != NULL) {
1908 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1909 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1912 return NSOrderedSame;
1915 - (uint32_t) compareForChanges {
1920 uint32_t timestamp : 30;
1921 uint32_t ignored : 1;
1922 uint32_t upgradable : 1;
1926 bool upgradable([self upgradableAndEssential:YES]);
1927 value.bits.upgradable = upgradable ? 1 : 0;
1930 value.bits.timestamp = 0;
1931 value.bits.ignored = [self ignored] ? 0 : 1;
1932 value.bits.upgradable = 1;
1934 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1935 value.bits.ignored = 0;
1936 value.bits.upgradable = 0;
1939 return _not(uint32_t) - value.key;
1943 pkgProblemResolver *resolver = [database_ resolver];
1944 resolver->Clear(iterator_);
1945 resolver->Protect(iterator_);
1946 pkgCacheFile &cache([database_ cache]);
1947 cache->MarkInstall(iterator_, false);
1948 pkgDepCache::StateCache &state((*cache)[iterator_]);
1949 if (!state.Install())
1950 cache->SetReInstall(iterator_, true);
1954 pkgProblemResolver *resolver = [database_ resolver];
1955 resolver->Clear(iterator_);
1956 resolver->Protect(iterator_);
1957 resolver->Remove(iterator_);
1958 [database_ cache]->MarkDelete(iterator_, true);
1961 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1962 return [NSNumber numberWithBool:(
1963 [self unfiltered] && [self matches:search]
1967 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1968 return [NSNumber numberWithBool:(
1969 (![number boolValue] || [self visible]) && [self installed] != nil
1973 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1974 NSString *section = [self section];
1976 return [NSNumber numberWithBool:(
1978 [self installed] == nil && (
1980 section == nil && [name length] == 0 ||
1981 [name isEqualToString:section]
1986 - (NSNumber *) isVisibleInSource:(Source *)source {
1987 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1992 /* Section Class {{{ */
1993 @interface Section : NSObject {
1999 - (NSComparisonResult) compareByName:(Section *)section;
2000 - (Section *) initWithName:(NSString *)name;
2001 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2002 - (NSString *) name;
2005 - (void) addToCount;
2009 @implementation Section
2016 - (NSComparisonResult) compareByName:(Section *)section {
2017 NSString *lhs = [self name];
2018 NSString *rhs = [section name];
2020 if ([lhs length] != 0 && [rhs length] != 0) {
2021 unichar lhc = [lhs characterAtIndex:0];
2022 unichar rhc = [rhs characterAtIndex:0];
2024 if (isalpha(lhc) && !isalpha(rhc))
2025 return NSOrderedAscending;
2026 else if (!isalpha(lhc) && isalpha(rhc))
2027 return NSOrderedDescending;
2030 return [lhs compare:rhs options:LaxCompareOptions_];
2033 - (Section *) initWithName:(NSString *)name {
2034 return [self initWithName:name row:0];
2037 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2038 if ((self = [super init]) != nil) {
2039 name_ = [name retain];
2044 - (NSString *) name {
2056 - (void) addToCount {
2064 static NSArray *Finishes_;
2066 /* Database Implementation {{{ */
2067 @implementation Database
2069 + (Database *) sharedInstance {
2070 static Database *instance;
2071 if (instance == nil)
2072 instance = [[Database alloc] init];
2081 - (void) _readCydia:(NSNumber *)fd { _pooled
2082 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2083 std::istream is(&ib);
2086 static Pcre finish_r("^finish:([^:]*)$");
2088 while (std::getline(is, line)) {
2089 const char *data(line.c_str());
2090 size_t size = line.size();
2091 lprintf("C:%s\n", data);
2093 if (finish_r(data, size)) {
2094 NSString *finish = finish_r[1];
2095 int index = [Finishes_ indexOfObject:finish];
2096 if (index != INT_MAX && index > Finish_)
2104 - (void) _readStatus:(NSNumber *)fd { _pooled
2105 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2106 std::istream is(&ib);
2109 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2110 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2112 while (std::getline(is, line)) {
2113 const char *data(line.c_str());
2114 size_t size = line.size();
2115 lprintf("S:%s\n", data);
2117 if (conffile_r(data, size)) {
2118 [delegate_ setConfigurationData:conffile_r[1]];
2119 } else if (strncmp(data, "status: ", 8) == 0) {
2120 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2121 [delegate_ setProgressTitle:string];
2122 } else if (pmstatus_r(data, size)) {
2123 std::string type([pmstatus_r[1] UTF8String]);
2124 NSString *id = pmstatus_r[2];
2126 float percent([pmstatus_r[3] floatValue]);
2127 [delegate_ setProgressPercent:(percent / 100)];
2129 NSString *string = pmstatus_r[4];
2131 if (type == "pmerror")
2132 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2133 withObject:[NSArray arrayWithObjects:string, id, nil]
2136 else if (type == "pmstatus") {
2137 [delegate_ setProgressTitle:string];
2138 } else if (type == "pmconffile")
2139 [delegate_ setConfigurationData:string];
2140 else _assert(false);
2141 } else _assert(false);
2147 - (void) _readOutput:(NSNumber *)fd { _pooled
2148 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2149 std::istream is(&ib);
2152 while (std::getline(is, line)) {
2153 lprintf("O:%s\n", line.c_str());
2154 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2164 - (Package *) packageWithName:(NSString *)name {
2165 if (static_cast<pkgDepCache *>(cache_) == NULL)
2167 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2168 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2171 - (Database *) init {
2172 if ((self = [super init]) != nil) {
2179 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2180 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2184 _assert(pipe(fds) != -1);
2187 _config->Set("APT::Keep-Fds::", cydiafd_);
2188 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2191 detachNewThreadSelector:@selector(_readCydia:)
2193 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2196 _assert(pipe(fds) != -1);
2200 detachNewThreadSelector:@selector(_readStatus:)
2202 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2205 _assert(pipe(fds) != -1);
2206 _assert(dup2(fds[0], 0) != -1);
2207 _assert(close(fds[0]) != -1);
2209 input_ = fdopen(fds[1], "a");
2211 _assert(pipe(fds) != -1);
2212 _assert(dup2(fds[1], 1) != -1);
2213 _assert(close(fds[1]) != -1);
2216 detachNewThreadSelector:@selector(_readOutput:)
2218 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2223 - (pkgCacheFile &) cache {
2227 - (pkgDepCache::Policy *) policy {
2231 - (pkgRecords *) records {
2235 - (pkgProblemResolver *) resolver {
2239 - (pkgAcquire &) fetcher {
2243 - (NSArray *) packages {
2247 - (NSArray *) sources {
2248 return [sources_ allValues];
2251 - (NSArray *) issues {
2252 if (cache_->BrokenCount() == 0)
2255 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2257 for (Package *package in packages_) {
2258 if (![package broken])
2260 pkgCache::PkgIterator pkg([package iterator]);
2262 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2263 [entry addObject:[package name]];
2264 [issues addObject:entry];
2266 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2270 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2271 pkgCache::DepIterator start;
2272 pkgCache::DepIterator end;
2273 dep.GlobOr(start, end); // ++dep
2275 if (!cache_->IsImportantDep(end))
2277 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2280 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2281 [entry addObject:failure];
2282 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2284 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2285 [failure addObject:[package name]];
2287 pkgCache::PkgIterator target(start.TargetPkg());
2288 if (target->ProvidesList != 0)
2289 [failure addObject:@"?"];
2291 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2293 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2294 else if (!cache_[target].CandidateVerIter(cache_).end())
2295 [failure addObject:@"-"];
2296 else if (target->ProvidesList == 0)
2297 [failure addObject:@"!"];
2299 [failure addObject:@"%"];
2303 if (start.TargetVer() != 0)
2304 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2315 - (void) reloadData {
2335 if (!cache_.Open(progress_, true)) {
2337 if (!_error->PopMessage(error))
2340 lprintf("cache_.Open():[%s]\n", error.c_str());
2342 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2343 [delegate_ repairWithSelector:@selector(configure)];
2344 else if (error == "The package lists or status file could not be parsed or opened.")
2345 [delegate_ repairWithSelector:@selector(update)];
2346 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2347 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2348 // else if (error == "The list of sources could not be read.")
2349 else _assert(false);
2355 now_ = [[NSDate date] retain];
2357 policy_ = new pkgDepCache::Policy();
2358 records_ = new pkgRecords(cache_);
2359 resolver_ = new pkgProblemResolver(cache_);
2360 fetcher_ = new pkgAcquire(&status_);
2363 list_ = new pkgSourceList();
2364 _assert(list_->ReadMainList());
2366 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2367 _assert(pkgApplyStatus(cache_));
2369 if (cache_->BrokenCount() != 0) {
2370 _assert(pkgFixBroken(cache_));
2371 _assert(cache_->BrokenCount() == 0);
2372 _assert(pkgMinimizeUpgrade(cache_));
2375 [sources_ removeAllObjects];
2376 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2377 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2378 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2380 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2381 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2385 [packages_ removeAllObjects];
2388 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2389 if (Package *package = [Package packageWithIterator:iterator database:self])
2390 [packages_ addObject:package];
2392 [packages_ sortUsingSelector:@selector(compareByName:)];
2396 - (void) configure {
2397 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2398 system([dpkg UTF8String]);
2406 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2407 _assert(!_error->PendingError());
2410 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2413 public pkgArchiveCleaner
2416 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2421 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2423 while (_error->PopMessage(error))
2424 lprintf("ArchiveCleaner: %s\n", error.c_str());
2429 pkgRecords records(cache_);
2431 lock_ = new FileFd();
2432 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2433 _assert(!_error->PendingError());
2436 // XXX: explain this with an error message
2437 _assert(list.ReadMainList());
2439 manager_ = (_system->CreatePM(cache_));
2440 _assert(manager_->GetArchives(fetcher_, &list, &records));
2441 _assert(!_error->PendingError());
2445 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2447 _assert(list.ReadMainList());
2448 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2449 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2452 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2457 bool failed = false;
2458 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2459 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2462 std::string uri = (*item)->DescURI();
2463 std::string error = (*item)->ErrorText;
2465 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2468 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2469 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2480 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2482 if (_error->PendingError()) {
2487 if (result == pkgPackageManager::Failed) {
2492 if (result != pkgPackageManager::Completed) {
2497 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2499 _assert(list.ReadMainList());
2500 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2501 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2504 if (![before isEqualToArray:after])
2509 _assert(pkgDistUpgrade(cache_));
2513 [self updateWithStatus:status_];
2516 - (void) updateWithStatus:(Status &)status {
2518 _assert(list.ReadMainList());
2521 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2522 _assert(!_error->PendingError());
2524 pkgAcquire fetcher(&status);
2525 _assert(list.GetIndexes(&fetcher));
2527 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2528 bool failed = false;
2529 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2530 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2531 (*item)->Finished();
2535 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2536 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2537 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2540 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2545 - (void) setDelegate:(id)delegate {
2546 delegate_ = delegate;
2547 status_.setDelegate(delegate);
2548 progress_.setDelegate(delegate);
2551 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2552 pkgIndexFile *index(NULL);
2553 list_->FindIndex(file, index);
2554 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2560 /* PopUp Windows {{{ */
2561 @interface PopUpView : UIView {
2562 _transient id delegate_;
2563 UITransitionView *transition_;
2568 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2572 @implementation PopUpView
2575 [transition_ setDelegate:nil];
2576 [transition_ release];
2582 [transition_ transition:UITransitionPushFromTop toView:nil];
2585 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2586 if (from != nil && to == nil)
2587 [self removeFromSuperview];
2590 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2591 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2592 delegate_ = delegate;
2594 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2595 [self addSubview:transition_];
2597 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2599 [view addSubview:self];
2601 [transition_ setDelegate:self];
2603 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2604 [transition_ transition:UITransitionNone toView:blank];
2605 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2612 /* Mail Composition {{{ */
2613 @interface MailToView : PopUpView {
2614 MailComposeController *controller_;
2617 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2621 @implementation MailToView
2624 [controller_ release];
2628 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2632 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2633 NSLog(@"did:%@", delivery);
2634 // [UIApp setStatusBarShowsProgress:NO];
2635 if ([controller error]){
2636 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2637 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2638 [mailAlertSheet setBodyText:[controller error]];
2639 [mailAlertSheet popupAlertAnimated:YES];
2643 - (void) showError {
2644 NSLog(@"%@", [controller_ error]);
2645 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2646 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2647 [mailAlertSheet setBodyText:[controller_ error]];
2648 [mailAlertSheet popupAlertAnimated:YES];
2651 - (void) deliverMessage { _pooled
2655 if (![controller_ deliverMessage])
2656 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2659 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2660 if ([controller_ needsDelivery])
2661 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2666 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2667 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2668 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2669 [controller_ setDelegate:self];
2670 [controller_ initializeUI];
2671 [controller_ setupForURL:url];
2673 UIView *view([controller_ view]);
2674 [overlay_ addSubview:view];
2680 /* Confirmation View {{{ */
2681 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2682 if (!iterator.end())
2683 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2684 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2686 pkgCache::PkgIterator package(dep.TargetPkg());
2689 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2696 @protocol ConfirmationViewDelegate
2701 @interface ConfirmationView : BrowserView {
2702 _transient Database *database_;
2703 UIActionSheet *essential_;
2710 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2714 @implementation ConfirmationView
2721 if (essential_ != nil)
2722 [essential_ release];
2728 [book_ popFromSuperviewAnimated:YES];
2731 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2732 NSString *context([sheet context]);
2734 if ([context isEqualToString:@"remove"]) {
2742 [delegate_ confirm];
2749 } else if ([context isEqualToString:@"unable"]) {
2753 [super alertSheet:sheet buttonClicked:button];
2756 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2757 [window setValue:changes_ forKey:@"changes"];
2758 [window setValue:issues_ forKey:@"issues"];
2759 [window setValue:sizes_ forKey:@"sizes"];
2760 [super webView:sender didClearWindowObject:window forFrame:frame];
2763 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2764 if ((self = [super initWithBook:book]) != nil) {
2765 database_ = database;
2767 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2768 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2769 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2770 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2771 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2775 pkgDepCache::Policy *policy([database_ policy]);
2777 pkgCacheFile &cache([database_ cache]);
2778 NSArray *packages = [database_ packages];
2779 for (size_t i(0), e = [packages count]; i != e; ++i) {
2780 Package *package = [packages objectAtIndex:i];
2781 pkgCache::PkgIterator iterator = [package iterator];
2782 pkgDepCache::StateCache &state(cache[iterator]);
2784 NSString *name([package name]);
2786 if (state.NewInstall())
2787 [installing addObject:name];
2788 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2789 [reinstalling addObject:name];
2790 else if (state.Upgrade())
2791 [upgrading addObject:name];
2792 else if (state.Downgrade())
2793 [downgrading addObject:name];
2794 else if (state.Delete()) {
2795 if ([package essential])
2797 [removing addObject:name];
2800 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2801 substrate_ |= DepSubstrate(iterator.CurrentVer());
2806 else if (Advanced_ || true) {
2807 essential_ = [[UIActionSheet alloc]
2808 initWithTitle:@"Removing Essentials"
2809 buttons:[NSArray arrayWithObjects:
2810 @"Cancel Operation (Safe)",
2811 @"Force Removal (Unsafe)",
2813 defaultButtonIndex:0
2819 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2821 [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."];
2823 essential_ = [[UIActionSheet alloc]
2824 initWithTitle:@"Unable to Comply"
2825 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2826 defaultButtonIndex:0
2831 [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."];
2834 changes_ = [[NSArray alloc] initWithObjects:
2842 issues_ = [database_ issues];
2844 issues_ = [issues_ retain];
2846 sizes_ = [[NSArray alloc] initWithObjects:
2847 SizeString([database_ fetcher].FetchNeeded()),
2848 SizeString([database_ fetcher].PartialPresent()),
2849 SizeString([database_ cache]->UsrSize()),
2852 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2856 - (NSString *) backButtonTitle {
2860 - (NSString *) leftButtonTitle {
2864 - (id) _rightButtonTitle {
2865 #if AlwaysReload || IgnoreInstall
2868 return issues_ == nil ? @"Confirm" : nil;
2872 - (void) _leftButtonClicked {
2877 - (void) _rightButtonClicked {
2879 return [super _rightButtonClicked];
2881 if (essential_ != nil)
2882 [essential_ popupAlertAnimated:YES];
2886 [delegate_ confirm];
2894 /* Progress Data {{{ */
2895 @interface ProgressData : NSObject {
2901 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2908 @implementation ProgressData
2910 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2911 if ((self = [super init]) != nil) {
2912 selector_ = selector;
2932 /* Progress View {{{ */
2933 @interface ProgressView : UIView <
2934 ConfigurationDelegate,
2937 _transient Database *database_;
2939 UIView *background_;
2940 UITransitionView *transition_;
2942 UINavigationBar *navbar_;
2943 UIProgressBar *progress_;
2944 UITextView *output_;
2945 UITextLabel *status_;
2946 UIPushButton *close_;
2949 SHA1SumValue springlist_;
2950 SHA1SumValue notifyconf_;
2951 SHA1SumValue sandplate_;
2953 NSTimeInterval last_;
2956 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2958 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2959 - (void) setContentView:(UIView *)view;
2962 - (void) _retachThread;
2963 - (void) _detachNewThreadData:(ProgressData *)data;
2964 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2970 @protocol ProgressViewDelegate
2971 - (void) progressViewIsComplete:(ProgressView *)sender;
2974 @implementation ProgressView
2977 [transition_ setDelegate:nil];
2978 [navbar_ setDelegate:nil];
2981 if (background_ != nil)
2982 [background_ release];
2983 [transition_ release];
2986 [progress_ release];
2993 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2994 if (bootstrap_ && from == overlay_ && to == view_)
2998 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2999 if ((self = [super initWithFrame:frame]) != nil) {
3000 database_ = database;
3001 delegate_ = delegate;
3003 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3004 [transition_ setDelegate:self];
3006 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3009 [overlay_ setBackgroundColor:[UIColor blackColor]];
3011 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3012 [background_ setBackgroundColor:[UIColor blackColor]];
3013 [self addSubview:background_];
3016 [self addSubview:transition_];
3018 CGSize navsize = [UINavigationBar defaultSize];
3019 CGRect navrect = {{0, 0}, navsize};
3021 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3022 [overlay_ addSubview:navbar_];
3024 [navbar_ setBarStyle:1];
3025 [navbar_ setDelegate:self];
3027 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3028 [navbar_ pushNavigationItem:navitem];
3030 CGRect bounds = [overlay_ bounds];
3031 CGSize prgsize = [UIProgressBar defaultSize];
3034 (bounds.size.width - prgsize.width) / 2,
3035 bounds.size.height - prgsize.height - 20
3038 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3039 [progress_ setStyle:0];
3041 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3043 bounds.size.height - prgsize.height - 50,
3044 bounds.size.width - 20,
3048 [status_ setColor:[UIColor whiteColor]];
3049 [status_ setBackgroundColor:[UIColor clearColor]];
3051 [status_ setCentersHorizontally:YES];
3052 //[status_ setFont:font];
3054 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3056 navrect.size.height + 20,
3057 bounds.size.width - 20,
3058 bounds.size.height - navsize.height - 62 - navrect.size.height
3061 //[output_ setTextFont:@"Courier New"];
3062 [output_ setTextSize:12];
3064 [output_ setTextColor:[UIColor whiteColor]];
3065 [output_ setBackgroundColor:[UIColor clearColor]];
3067 [output_ setMarginTop:0];
3068 [output_ setAllowsRubberBanding:YES];
3069 [output_ setEditable:NO];
3071 [overlay_ addSubview:output_];
3073 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3075 bounds.size.height - prgsize.height - 50,
3076 bounds.size.width - 20,
3080 [close_ setAutosizesToFit:NO];
3081 [close_ setDrawsShadow:YES];
3082 [close_ setStretchBackground:YES];
3083 [close_ setEnabled:YES];
3085 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3086 [close_ setTitleFont:bold];
3088 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3089 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3090 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3094 - (void) setContentView:(UIView *)view {
3095 view_ = [view retain];
3098 - (void) resetView {
3099 [transition_ transition:6 toView:view_];
3102 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3103 NSString *context([sheet context]);
3105 if ([context isEqualToString:@"conffile"]) {
3106 FILE *input = [database_ input];
3110 fprintf(input, "N\n");
3114 fprintf(input, "Y\n");
3125 - (void) closeButtonPushed {
3134 [delegate_ suspendWithAnimation:YES];
3138 system("launchctl stop com.apple.SpringBoard");
3142 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3151 - (void) _retachThread {
3152 UINavigationItem *item = [navbar_ topItem];
3153 [item setTitle:@"Complete"];
3155 [overlay_ addSubview:close_];
3156 [progress_ removeFromSuperview];
3157 [status_ removeFromSuperview];
3159 [delegate_ progressViewIsComplete:self];
3162 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3163 MMap mmap(file, MMap::ReadOnly);
3165 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3166 if (!(sandplate_ == sha1.Result()))
3171 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3172 MMap mmap(file, MMap::ReadOnly);
3174 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3175 if (!(notifyconf_ == sha1.Result()))
3180 FileFd file(SpringBoard_, FileFd::ReadOnly);
3181 MMap mmap(file, MMap::ReadOnly);
3183 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3184 if (!(springlist_ == sha1.Result()))
3189 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3190 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3191 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3192 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3193 case 4: [close_ setTitle:@"Reboot Device"]; break;
3196 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3198 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3199 [cache autorelease];
3201 NSFileManager *manager = [NSFileManager defaultManager];
3202 NSError *error = nil;
3204 id system = [cache objectForKey:@"System"];
3209 if (stat(Cache_, &info) == -1)
3212 [system removeAllObjects];
3214 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3215 for (NSString *app in apps)
3216 if ([app hasSuffix:@".app"]) {
3217 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3218 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3219 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3221 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3222 [info setObject:path forKey:@"Path"];
3223 [info setObject:@"System" forKey:@"ApplicationType"];
3224 [system addInfoDictionary:info];
3230 [cache writeToFile:@Cache_ atomically:YES];
3232 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3234 if (chmod(Cache_, info.st_mode) == -1)
3238 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3241 notify_post("com.apple.mobile.application_installed");
3243 [delegate_ setStatusBarShowsProgress:NO];
3246 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3247 [[data target] performSelector:[data selector] withObject:[data object]];
3250 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3253 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3254 UINavigationItem *item = [navbar_ topItem];
3255 [item setTitle:title];
3257 [status_ setText:nil];
3258 [output_ setText:@""];
3259 [progress_ setProgress:0];
3262 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3264 [close_ removeFromSuperview];
3265 [overlay_ addSubview:progress_];
3266 [overlay_ addSubview:status_];
3268 [delegate_ setStatusBarShowsProgress:YES];
3272 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3273 MMap mmap(file, MMap::ReadOnly);
3275 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3276 sandplate_ = sha1.Result();
3280 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3281 MMap mmap(file, MMap::ReadOnly);
3283 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3284 notifyconf_ = sha1.Result();
3288 FileFd file(SpringBoard_, FileFd::ReadOnly);
3289 MMap mmap(file, MMap::ReadOnly);
3291 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3292 springlist_ = sha1.Result();
3295 [transition_ transition:6 toView:overlay_];
3298 detachNewThreadSelector:@selector(_detachNewThreadData:)
3300 withObject:[[ProgressData alloc]
3301 initWithSelector:selector
3308 - (void) repairWithSelector:(SEL)selector {
3310 detachNewThreadSelector:selector
3317 - (void) setConfigurationData:(NSString *)data {
3319 performSelectorOnMainThread:@selector(_setConfigurationData:)
3325 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3326 Package *package = id == nil ? nil : [database_ packageWithName:id];
3328 UIActionSheet *sheet = [[[UIActionSheet alloc]
3329 initWithTitle:(package == nil ? id : [package name])
3330 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3331 defaultButtonIndex:0
3336 [sheet setBodyText:error];
3337 [sheet popupAlertAnimated:YES];
3340 - (void) setProgressTitle:(NSString *)title {
3342 performSelectorOnMainThread:@selector(_setProgressTitle:)
3348 - (void) setProgressPercent:(float)percent {
3350 performSelectorOnMainThread:@selector(_setProgressPercent:)
3351 withObject:[NSNumber numberWithFloat:percent]
3356 - (void) startProgress {
3357 last_ = [NSDate timeIntervalSinceReferenceDate];
3360 - (void) addProgressOutput:(NSString *)output {
3362 performSelectorOnMainThread:@selector(_addProgressOutput:)
3368 - (bool) isCancelling:(size_t)received {
3370 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3371 if (received_ != received) {
3372 received_ = received;
3374 } else if (now - last_ > 30)
3381 - (void) _setConfigurationData:(NSString *)data {
3382 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3384 _assert(conffile_r(data));
3386 NSString *ofile = conffile_r[1];
3387 //NSString *nfile = conffile_r[2];
3389 UIActionSheet *sheet = [[[UIActionSheet alloc]
3390 initWithTitle:@"Configuration Upgrade"
3391 buttons:[NSArray arrayWithObjects:
3392 @"Keep My Old Copy",
3393 @"Accept The New Copy",
3394 // XXX: @"See What Changed",
3396 defaultButtonIndex:0
3401 [sheet setBodyText:[NSString stringWithFormat:
3402 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3405 [sheet popupAlertAnimated:YES];
3408 - (void) _setProgressTitle:(NSString *)title {
3409 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3410 for (size_t i(0), e([words count]); i != e; ++i) {
3411 NSString *word([words objectAtIndex:i]);
3412 if (Package *package = [database_ packageWithName:word])
3413 [words replaceObjectAtIndex:i withObject:[package name]];
3416 [status_ setText:[words componentsJoinedByString:@" "]];
3419 - (void) _setProgressPercent:(NSNumber *)percent {
3420 [progress_ setProgress:[percent floatValue]];
3423 - (void) _addProgressOutput:(NSString *)output {
3424 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3425 CGSize size = [output_ contentSize];
3426 CGRect rect = {{0, size.height}, {size.width, 0}};
3427 [output_ scrollRectToVisible:rect animated:YES];
3430 - (BOOL) isRunning {
3437 /* Package Cell {{{ */
3438 @interface PackageCell : UISimpleTableCell {
3441 NSString *description_;
3445 UITextLabel *status_;
3449 - (PackageCell *) init;
3450 - (void) setPackage:(Package *)package;
3452 + (int) heightForPackage:(Package *)package;
3456 @implementation PackageCell
3458 - (void) clearPackage {
3469 if (description_ != nil) {
3470 [description_ release];
3474 if (source_ != nil) {
3479 if (badge_ != nil) {
3486 [self clearPackage];
3493 - (PackageCell *) init {
3494 if ((self = [super init]) != nil) {
3496 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3497 [status_ setBackgroundColor:[UIColor clearColor]];
3498 [status_ setFont:small];
3503 - (void) setPackage:(Package *)package {
3504 [self clearPackage];
3506 Source *source = [package source];
3507 NSString *section = [package simpleSection];
3509 icon_ = [[package icon] retain];
3511 name_ = [[package name] retain];
3512 description_ = [[package tagline] retain];
3514 NSString *label = nil;
3515 bool trusted = false;
3517 if (source != nil) {
3518 label = [source label];
3519 trusted = [source trusted];
3520 } else if ([[package id] isEqualToString:@"firmware"])
3523 label = @"Unknown/Local";
3525 NSString *from = [NSString stringWithFormat:@"from %@", label];
3527 if (section != nil && ![section isEqualToString:label])
3528 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3530 source_ = [from retain];
3532 if (NSString *purpose = [package primaryPurpose])
3533 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3534 badge_ = [badge_ retain];
3537 if (NSString *mode = [package mode]) {
3538 [badge_ setImage:[UIImage applicationImageNamed:
3539 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3542 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3543 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3544 } else if ([package half]) {
3545 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3546 [status_ setText:@"Package Damaged"];
3547 [status_ setColor:[UIColor redColor]];
3549 [badge_ setImage:nil];
3550 [status_ setText:nil];
3555 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3558 rect.size = [icon_ size];
3560 rect.size.width /= 2;
3561 rect.size.height /= 2;
3563 rect.origin.x = 25 - rect.size.width / 2;
3564 rect.origin.y = 25 - rect.size.height / 2;
3566 [icon_ drawInRect:rect];
3569 if (badge_ != nil) {
3570 CGSize size = [badge_ size];
3572 [badge_ drawAtPoint:CGPointMake(
3573 36 - size.width / 2,
3574 36 - size.height / 2
3583 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3584 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3588 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3590 [super drawContentInRect:rect selected:selected];
3593 + (int) heightForPackage:(Package *)package {
3594 NSString *tagline([package tagline]);
3595 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3597 if ([package hasMode] || [package half])
3606 /* Section Cell {{{ */
3607 @interface SectionCell : UISimpleTableCell {
3612 _UISwitchSlider *switch_;
3617 - (void) setSection:(Section *)section editing:(BOOL)editing;
3621 @implementation SectionCell
3623 - (void) clearSection {
3624 if (section_ != nil) {
3634 if (count_ != nil) {
3641 [self clearSection];
3648 if ((self = [super init]) != nil) {
3649 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3651 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3652 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3656 - (void) onSwitch:(id)sender {
3657 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3658 if (metadata == nil) {
3659 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3660 [Sections_ setObject:metadata forKey:section_];
3664 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3667 - (void) setSection:(Section *)section editing:(BOOL)editing {
3668 if (editing != editing_) {
3670 [switch_ removeFromSuperview];
3672 [self addSubview:switch_];
3676 [self clearSection];
3678 if (section == nil) {
3679 name_ = [@"All Packages" retain];
3682 section_ = [section name];
3683 if (section_ != nil)
3684 section_ = [section_ retain];
3685 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3686 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3689 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3693 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3694 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3701 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3703 CGSize size = [count_ sizeWithFont:Font14_];
3707 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3709 [super drawContentInRect:rect selected:selected];
3715 /* File Table {{{ */
3716 @interface FileTable : RVPage {
3717 _transient Database *database_;
3720 NSMutableArray *files_;
3724 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3725 - (void) setPackage:(Package *)package;
3729 @implementation FileTable
3732 if (package_ != nil)
3741 - (int) numberOfRowsInTable:(UITable *)table {
3742 return files_ == nil ? 0 : [files_ count];
3745 - (float) table:(UITable *)table heightForRow:(int)row {
3749 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3750 if (reusing == nil) {
3751 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3752 UIFont *font = [UIFont systemFontOfSize:16];
3753 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3755 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3759 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3763 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3764 if ((self = [super initWithBook:book]) != nil) {
3765 database_ = database;
3767 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3769 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3770 [self addSubview:list_];
3772 UITableColumn *column = [[[UITableColumn alloc]
3773 initWithTitle:@"Name"
3775 width:[self frame].size.width
3778 [list_ setDataSource:self];
3779 [list_ setSeparatorStyle:1];
3780 [list_ addTableColumn:column];
3781 [list_ setDelegate:self];
3782 [list_ setReusesTableCells:YES];
3786 - (void) setPackage:(Package *)package {
3787 if (package_ != nil) {
3788 [package_ autorelease];
3797 [files_ removeAllObjects];
3799 if (package != nil) {
3800 package_ = [package retain];
3801 name_ = [[package id] retain];
3803 if (NSArray *files = [package files])
3804 [files_ addObjectsFromArray:files];
3806 if ([files_ count] != 0) {
3807 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3808 [files_ removeObjectAtIndex:0];
3809 [files_ sortUsingSelector:@selector(compareByPath:)];
3811 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3812 [stack addObject:@"/"];
3814 for (int i(0), e([files_ count]); i != e; ++i) {
3815 NSString *file = [files_ objectAtIndex:i];
3816 while (![file hasPrefix:[stack lastObject]])
3817 [stack removeLastObject];
3818 NSString *directory = [stack lastObject];
3819 [stack addObject:[file stringByAppendingString:@"/"]];
3820 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3821 ([stack count] - 2) * 3, "",
3822 [file substringFromIndex:[directory length]]
3831 - (void) resetViewAnimated:(BOOL)animated {
3832 [list_ resetViewAnimated:animated];
3835 - (void) reloadData {
3836 [self setPackage:[database_ packageWithName:name_]];
3837 [self reloadButtons];
3840 - (NSString *) title {
3841 return @"Installed Files";
3844 - (NSString *) backButtonTitle {
3850 /* Package View {{{ */
3851 @interface PackageView : BrowserView {
3852 _transient Database *database_;
3855 NSMutableArray *buttons_;
3858 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3859 - (void) setPackage:(Package *)package;
3863 @implementation PackageView
3866 if (package_ != nil)
3874 - (void) _clickButtonWithName:(NSString *)name {
3875 if ([name isEqualToString:@"Install"])
3876 [delegate_ installPackage:package_];
3877 else if ([name isEqualToString:@"Reinstall"])
3878 [delegate_ installPackage:package_];
3879 else if ([name isEqualToString:@"Remove"])
3880 [delegate_ removePackage:package_];
3881 else if ([name isEqualToString:@"Upgrade"])
3882 [delegate_ installPackage:package_];
3883 else _assert(false);
3886 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3887 NSString *context([sheet context]);
3889 if ([context isEqualToString:@"modify"]) {
3890 int count = [buttons_ count];
3891 _assert(count != 0);
3892 _assert(button <= count + 1);
3894 if (count != button - 1)
3895 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3899 [super alertSheet:sheet buttonClicked:button];
3902 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3903 return [super webView:sender didFinishLoadForFrame:frame];
3906 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3907 [window setValue:package_ forKey:@"package"];
3908 [super webView:sender didClearWindowObject:window forFrame:frame];
3912 - (void) _rightButtonClicked {
3913 /*[super _rightButtonClicked];
3916 int count = [buttons_ count];
3917 _assert(count != 0);
3920 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3922 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3923 [buttons addObjectsFromArray:buttons_];
3924 [buttons addObject:@"Cancel"];
3926 [delegate_ slideUp:[[[UIActionSheet alloc]
3929 defaultButtonIndex:2
3937 - (id) _rightButtonTitle {
3938 int count = [buttons_ count];
3939 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3942 - (NSString *) backButtonTitle {
3946 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3947 if ((self = [super initWithBook:book]) != nil) {
3948 database_ = database;
3949 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3953 - (void) setPackage:(Package *)package {
3954 if (package_ != nil) {
3955 [package_ autorelease];
3964 [buttons_ removeAllObjects];
3966 if (package != nil) {
3967 package_ = [package retain];
3968 name_ = [[package id] retain];
3970 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3972 if ([package_ source] == nil);
3973 else if ([package_ upgradableAndEssential:NO])
3974 [buttons_ addObject:@"Upgrade"];
3975 else if ([package_ installed] == nil)
3976 [buttons_ addObject:@"Install"];
3978 [buttons_ addObject:@"Reinstall"];
3979 if ([package_ installed] != nil)
3980 [buttons_ addObject:@"Remove"];
3988 - (void) reloadData {
3989 [self setPackage:[database_ packageWithName:name_]];
3990 [self reloadButtons];
3995 /* Package Table {{{ */
3996 @interface PackageTable : RVPage {
3997 _transient Database *database_;
4001 NSMutableArray *packages_;
4002 NSMutableArray *sections_;
4003 UISectionList *list_;
4006 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4008 - (void) setDelegate:(id)delegate;
4009 - (void) setObject:(id)object;
4011 - (void) reloadData;
4012 - (void) resetCursor;
4014 - (UISectionList *) list;
4016 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4020 @implementation PackageTable
4023 [list_ setDataSource:nil];
4028 [packages_ release];
4029 [sections_ release];
4034 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4035 return [sections_ count];
4038 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4039 return [[sections_ objectAtIndex:section] name];
4042 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4043 return [[sections_ objectAtIndex:section] row];
4046 - (int) numberOfRowsInTable:(UITable *)table {
4047 return [packages_ count];
4050 - (float) table:(UITable *)table heightForRow:(int)row {
4051 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4054 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4056 reusing = [[[PackageCell alloc] init] autorelease];
4057 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4061 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4065 - (void) tableRowSelected:(NSNotification *)notification {
4066 int row = [[notification object] selectedRow];
4070 Package *package = [packages_ objectAtIndex:row];
4071 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4072 [view setDelegate:delegate_];
4073 [view setPackage:package];
4074 [book_ pushPage:view];
4077 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4078 if ((self = [super initWithBook:book]) != nil) {
4079 database_ = database;
4080 title_ = [title retain];
4082 object_ = object == nil ? nil : [object retain];
4084 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4085 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4087 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4088 [list_ setDataSource:self];
4090 UITableColumn *column = [[[UITableColumn alloc]
4091 initWithTitle:@"Name"
4093 width:[self frame].size.width
4096 UITable *table = [list_ table];
4097 [table setSeparatorStyle:1];
4098 [table addTableColumn:column];
4099 [table setDelegate:self];
4100 [table setReusesTableCells:YES];
4102 [self addSubview:list_];
4105 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4106 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4110 - (void) setDelegate:(id)delegate {
4111 delegate_ = delegate;
4114 - (void) setObject:(id)object {
4120 object_ = [object retain];
4123 - (void) reloadData {
4124 NSArray *packages = [database_ packages];
4126 [packages_ removeAllObjects];
4127 [sections_ removeAllObjects];
4129 for (size_t i(0); i != [packages count]; ++i) {
4130 Package *package([packages objectAtIndex:i]);
4131 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4132 [packages_ addObject:package];
4135 Section *section = nil;
4137 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4138 Package *package = [packages_ objectAtIndex:offset];
4139 NSString *name = [package index];
4141 if (section == nil || ![[section name] isEqualToString:name]) {
4142 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4143 [sections_ addObject:section];
4146 [section addToCount];
4152 - (NSString *) title {
4156 - (void) resetViewAnimated:(BOOL)animated {
4157 [list_ resetViewAnimated:animated];
4160 - (void) resetCursor {
4161 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4164 - (UISectionList *) list {
4168 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4169 [list_ setShouldHideHeaderInShortLists:hide];
4175 /* Add Source View {{{ */
4176 @interface AddSourceView : RVPage {
4177 _transient Database *database_;
4180 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4184 @implementation AddSourceView
4186 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4187 if ((self = [super initWithBook:book]) != nil) {
4188 database_ = database;
4194 /* Source Cell {{{ */
4195 @interface SourceCell : UITableCell {
4198 NSString *description_;
4204 - (SourceCell *) initWithSource:(Source *)source;
4208 @implementation SourceCell
4213 [description_ release];
4218 - (SourceCell *) initWithSource:(Source *)source {
4219 if ((self = [super init]) != nil) {
4221 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4223 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4224 icon_ = [icon_ retain];
4226 origin_ = [[source name] retain];
4227 label_ = [[source uri] retain];
4228 description_ = [[source description] retain];
4232 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4234 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4241 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4245 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4249 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4251 [super drawContentInRect:rect selected:selected];
4256 /* Source Table {{{ */
4257 @interface SourceTable : RVPage {
4258 _transient Database *database_;
4259 UISectionList *list_;
4260 NSMutableArray *sources_;
4261 UIActionSheet *alert_;
4265 UIProgressHUD *hud_;
4268 //NSURLConnection *installer_;
4269 NSURLConnection *trivial_bz2_;
4270 NSURLConnection *trivial_gz_;
4271 //NSURLConnection *automatic_;
4276 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4280 @implementation SourceTable
4282 - (void) _deallocConnection:(NSURLConnection *)connection {
4283 if (connection != nil) {
4284 [connection cancel];
4285 //[connection setDelegate:nil];
4286 [connection release];
4291 [[list_ table] setDelegate:nil];
4292 [list_ setDataSource:nil];
4301 //[self _deallocConnection:installer_];
4302 [self _deallocConnection:trivial_gz_];
4303 [self _deallocConnection:trivial_bz2_];
4304 //[self _deallocConnection:automatic_];
4311 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4312 return offset_ == 0 ? 1 : 2;
4315 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4316 switch (section + (offset_ == 0 ? 1 : 0)) {
4317 case 0: return @"Entered by User";
4318 case 1: return @"Installed by Packages";
4326 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4327 switch (section + (offset_ == 0 ? 1 : 0)) {
4329 case 1: return offset_;
4337 - (int) numberOfRowsInTable:(UITable *)table {
4338 return [sources_ count];
4341 - (float) table:(UITable *)table heightForRow:(int)row {
4342 Source *source = [sources_ objectAtIndex:row];
4343 return [source description] == nil ? 56 : 73;
4346 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4347 Source *source = [sources_ objectAtIndex:row];
4348 // XXX: weird warning, stupid selectors ;P
4349 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4352 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4356 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4360 - (void) tableRowSelected:(NSNotification*)notification {
4361 UITable *table([list_ table]);
4362 int row([table selectedRow]);
4366 Source *source = [sources_ objectAtIndex:row];
4368 PackageTable *packages = [[[PackageTable alloc]
4371 title:[source label]
4372 filter:@selector(isVisibleInSource:)
4376 [packages setDelegate:delegate_];
4378 [book_ pushPage:packages];
4381 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4382 Source *source = [sources_ objectAtIndex:row];
4383 return [source record] != nil;
4386 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4387 [[list_ table] setDeleteConfirmationRow:row];
4390 - (void) table:(UITable *)table deleteRow:(int)row {
4391 Source *source = [sources_ objectAtIndex:row];
4392 [Sources_ removeObjectForKey:[source key]];
4393 [delegate_ syncData];
4396 - (void) _endConnection:(NSURLConnection *)connection {
4397 NSURLConnection **field = NULL;
4398 if (connection == trivial_bz2_)
4399 field = &trivial_bz2_;
4400 else if (connection == trivial_gz_)
4401 field = &trivial_gz_;
4402 _assert(field != NULL);
4403 [connection release];
4407 trivial_bz2_ == nil &&
4410 [delegate_ setStatusBarShowsProgress:NO];
4413 [hud_ removeFromSuperview];
4418 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4421 @"./", @"Distribution",
4422 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4424 [delegate_ syncData];
4425 } else if (error_ != nil) {
4426 UIActionSheet *sheet = [[[UIActionSheet alloc]
4427 initWithTitle:@"Verification Error"
4428 buttons:[NSArray arrayWithObjects:@"OK", nil]
4429 defaultButtonIndex:0
4434 [sheet setBodyText:[error_ localizedDescription]];
4435 [sheet popupAlertAnimated:YES];
4437 UIActionSheet *sheet = [[[UIActionSheet alloc]
4438 initWithTitle:@"Did not Find Repository"
4439 buttons:[NSArray arrayWithObjects:@"OK", nil]
4440 defaultButtonIndex:0
4445 [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."];
4446 [sheet popupAlertAnimated:YES];
4452 if (error_ != nil) {
4459 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4460 switch ([response statusCode]) {
4466 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4467 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4469 error_ = [error retain];
4470 [self _endConnection:connection];
4473 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4474 [self _endConnection:connection];
4477 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4478 NSMutableURLRequest *request = [NSMutableURLRequest
4479 requestWithURL:[NSURL URLWithString:href]
4480 cachePolicy:NSURLRequestUseProtocolCachePolicy
4481 timeoutInterval:20.0
4484 [request setHTTPMethod:method];
4486 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4489 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4490 NSString *context([sheet context]);
4492 if ([context isEqualToString:@"source"]) {
4495 NSString *href = [[sheet textField] text];
4497 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4499 if (![href hasSuffix:@"/"])
4500 href_ = [href stringByAppendingString:@"/"];
4503 href_ = [href_ retain];
4505 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4506 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4507 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4511 hud_ = [delegate_ addProgressHUD];
4512 [hud_ setText:@"Verifying URL"];
4523 } else if ([context isEqualToString:@"trivial"])
4525 else if ([context isEqualToString:@"urlerror"])
4529 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4530 if ((self = [super initWithBook:book]) != nil) {
4531 database_ = database;
4532 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4534 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4535 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4536 [list_ setShouldHideHeaderInShortLists:NO];
4538 [self addSubview:list_];
4539 [list_ setDataSource:self];
4541 UITableColumn *column = [[UITableColumn alloc]
4542 initWithTitle:@"Name"
4544 width:[self frame].size.width
4547 UITable *table = [list_ table];
4548 [table setSeparatorStyle:1];
4549 [table addTableColumn:column];
4550 [table setDelegate:self];
4554 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4555 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4559 - (void) reloadData {
4561 _assert(list.ReadMainList());
4563 [sources_ removeAllObjects];
4564 [sources_ addObjectsFromArray:[database_ sources]];
4566 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4569 int count = [sources_ count];
4570 for (offset_ = 0; offset_ != count; ++offset_) {
4571 Source *source = [sources_ objectAtIndex:offset_];
4572 if ([source record] == nil)
4579 - (void) resetViewAnimated:(BOOL)animated {
4580 [list_ resetViewAnimated:animated];
4583 - (void) _leftButtonClicked {
4584 /*[book_ pushPage:[[[AddSourceView alloc]
4589 UIActionSheet *sheet = [[[UIActionSheet alloc]
4590 initWithTitle:@"Enter Cydia/APT URL"
4591 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4592 defaultButtonIndex:0
4597 [sheet setNumberOfRows:1];
4599 [sheet addTextFieldWithValue:@"http://" label:@""];
4601 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4602 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4603 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4604 [traits setKeyboardType:UIKeyboardTypeURL];
4605 // XXX: UIReturnKeyDone
4606 [traits setReturnKeyType:UIReturnKeyNext];
4608 [sheet popupAlertAnimated:YES];
4611 - (void) _rightButtonClicked {
4612 UITable *table = [list_ table];
4613 BOOL editing = [table isRowDeletionEnabled];
4614 [table enableRowDeletion:!editing animated:YES];
4615 [book_ reloadButtonsForPage:self];
4618 - (NSString *) title {
4622 - (NSString *) leftButtonTitle {
4623 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4626 - (id) rightButtonTitle {
4627 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4630 - (UINavigationButtonStyle) rightButtonStyle {
4631 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4637 /* Installed View {{{ */
4638 @interface InstalledView : RVPage {
4639 _transient Database *database_;
4640 PackageTable *packages_;
4644 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4648 @implementation InstalledView
4651 [packages_ release];
4655 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4656 if ((self = [super initWithBook:book]) != nil) {
4657 database_ = database;
4659 packages_ = [[PackageTable alloc]
4663 filter:@selector(isInstalledAndVisible:)
4664 with:[NSNumber numberWithBool:YES]
4667 [self addSubview:packages_];
4669 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4670 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4674 - (void) resetViewAnimated:(BOOL)animated {
4675 [packages_ resetViewAnimated:animated];
4678 - (void) reloadData {
4679 [packages_ reloadData];
4682 - (void) _rightButtonClicked {
4683 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4684 [packages_ reloadData];
4686 [book_ reloadButtonsForPage:self];
4689 - (NSString *) title {
4690 return @"Installed";
4693 - (NSString *) backButtonTitle {
4697 - (id) rightButtonTitle {
4698 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4701 - (UINavigationButtonStyle) rightButtonStyle {
4702 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4705 - (void) setDelegate:(id)delegate {
4706 [super setDelegate:delegate];
4707 [packages_ setDelegate:delegate];
4714 @interface HomeView : BrowserView {
4719 @implementation HomeView
4721 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4722 NSString *context([sheet context]);
4724 if ([context isEqualToString:@"about"])
4727 [super alertSheet:sheet buttonClicked:button];
4730 - (void) _leftButtonClicked {
4731 UIActionSheet *sheet = [[[UIActionSheet alloc]
4732 initWithTitle:@"About Cydia Installer"
4733 buttons:[NSArray arrayWithObjects:@"Close", nil]
4734 defaultButtonIndex:0
4740 @"Copyright (C) 2008\n"
4741 "Jay Freeman (saurik)\n"
4742 "saurik@saurik.com\n"
4743 "http://www.saurik.com/\n"
4746 "http://www.theokorigroup.com/\n"
4748 "College of Creative Studies,\n"
4749 "University of California,\n"
4751 "http://www.ccs.ucsb.edu/"
4754 [sheet popupAlertAnimated:YES];
4757 - (NSString *) leftButtonTitle {
4763 /* Manage View {{{ */
4764 @interface ManageView : BrowserView {
4769 @implementation ManageView
4771 - (NSString *) title {
4775 - (void) _leftButtonClicked {
4776 [delegate_ askForSettings];
4779 - (NSString *) leftButtonTitle {
4784 - (id) _rightButtonTitle {
4796 /* Indirect Delegate {{{ */
4797 @interface IndirectDelegate : NSProxy {
4798 _transient volatile id delegate_;
4801 - (void) setDelegate:(id)delegate;
4802 - (id) initWithDelegate:(id)delegate;
4805 @implementation IndirectDelegate
4807 - (void) setDelegate:(id)delegate {
4808 delegate_ = delegate;
4811 - (id) initWithDelegate:(id)delegate {
4812 delegate_ = delegate;
4816 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4817 if (delegate_ != nil)
4818 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4820 // XXX: I fucking hate Apple so very very bad
4821 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4824 - (void) forwardInvocation:(NSInvocation *)inv {
4825 SEL sel = [inv selector];
4826 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4827 [inv invokeWithTarget:delegate_];
4833 #include <BrowserView.m>
4835 /* Cydia Book {{{ */
4836 @interface CYBook : RVBook <
4839 _transient Database *database_;
4840 UINavigationBar *overlay_;
4841 UINavigationBar *underlay_;
4842 UIProgressIndicator *indicator_;
4843 UITextLabel *prompt_;
4844 UIProgressBar *progress_;
4845 UINavigationButton *cancel_;
4848 NSTimeInterval last_;
4851 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4857 @implementation CYBook
4861 [indicator_ release];
4863 [progress_ release];
4868 - (NSString *) getTitleForPage:(RVPage *)page {
4869 return Simplify([super getTitleForPage:page]);
4877 [UIView beginAnimations:nil context:NULL];
4879 CGRect ovrframe = [overlay_ frame];
4880 ovrframe.origin.y = 0;
4881 [overlay_ setFrame:ovrframe];
4883 CGRect barframe = [navbar_ frame];
4884 barframe.origin.y += ovrframe.size.height;
4885 [navbar_ setFrame:barframe];
4887 CGRect trnframe = [transition_ frame];
4888 trnframe.origin.y += ovrframe.size.height;
4889 trnframe.size.height -= ovrframe.size.height;
4890 [transition_ setFrame:trnframe];
4892 [UIView endAnimations];
4894 [indicator_ startAnimation];
4895 [prompt_ setText:@"Updating Database"];
4896 [progress_ setProgress:0];
4899 last_ = [NSDate timeIntervalSinceReferenceDate];
4901 [overlay_ addSubview:cancel_];
4904 detachNewThreadSelector:@selector(_update)
4913 [indicator_ stopAnimation];
4915 [UIView beginAnimations:nil context:NULL];
4917 CGRect ovrframe = [overlay_ frame];
4918 ovrframe.origin.y = -ovrframe.size.height;
4919 [overlay_ setFrame:ovrframe];
4921 CGRect barframe = [navbar_ frame];
4922 barframe.origin.y -= ovrframe.size.height;
4923 [navbar_ setFrame:barframe];
4925 CGRect trnframe = [transition_ frame];
4926 trnframe.origin.y -= ovrframe.size.height;
4927 trnframe.size.height += ovrframe.size.height;
4928 [transition_ setFrame:trnframe];
4930 [UIView commitAnimations];
4932 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4935 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4936 if ((self = [super initWithFrame:frame]) != nil) {
4937 database_ = database;
4939 CGRect ovrrect = [navbar_ bounds];
4940 ovrrect.size.height = [UINavigationBar defaultSize].height;
4941 ovrrect.origin.y = -ovrrect.size.height;
4943 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4944 [self addSubview:overlay_];
4946 ovrrect.origin.y = frame.size.height;
4947 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4948 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4949 [self addSubview:underlay_];
4951 [overlay_ setBarStyle:1];
4952 [underlay_ setBarStyle:1];
4954 int barstyle = [overlay_ _barStyle:NO];
4955 bool ugly = barstyle == 0;
4957 UIProgressIndicatorStyle style = ugly ?
4958 UIProgressIndicatorStyleMediumBrown :
4959 UIProgressIndicatorStyleMediumWhite;
4961 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4962 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4963 CGRect indrect = {{indoffset, indoffset}, indsize};
4965 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4966 [indicator_ setStyle:style];
4967 [overlay_ addSubview:indicator_];
4969 CGSize prmsize = {215, indsize.height + 4};
4972 indoffset * 2 + indsize.width,
4976 unsigned(ovrrect.size.height - prmsize.height) / 2
4979 UIFont *font = [UIFont systemFontOfSize:15];
4981 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4983 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
4984 [prompt_ setBackgroundColor:[UIColor clearColor]];
4985 [prompt_ setFont:font];
4987 [overlay_ addSubview:prompt_];
4989 CGSize prgsize = {75, 100};
4992 ovrrect.size.width - prgsize.width - 10,
4993 (ovrrect.size.height - prgsize.height) / 2
4996 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4997 [progress_ setStyle:0];
4998 [overlay_ addSubview:progress_];
5000 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5001 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5003 CGRect frame = [cancel_ frame];
5004 frame.size.width = 65;
5005 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5006 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5007 [cancel_ setFrame:frame];
5009 [cancel_ setBarStyle:barstyle];
5013 - (void) _onCancel {
5015 [cancel_ removeFromSuperview];
5018 - (void) _update { _pooled
5020 status.setDelegate(self);
5022 [database_ updateWithStatus:status];
5025 performSelectorOnMainThread:@selector(_update_)
5031 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5032 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5035 - (void) setProgressTitle:(NSString *)title {
5037 performSelectorOnMainThread:@selector(_setProgressTitle:)
5043 - (void) setProgressPercent:(float)percent {
5045 performSelectorOnMainThread:@selector(_setProgressPercent:)
5046 withObject:[NSNumber numberWithFloat:percent]
5051 - (void) startProgress {
5054 - (void) addProgressOutput:(NSString *)output {
5056 performSelectorOnMainThread:@selector(_addProgressOutput:)
5062 - (bool) isCancelling:(size_t)received {
5063 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5064 if (received_ != received) {
5065 received_ = received;
5067 } else if (now - last_ > 15)
5072 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5076 - (void) _setProgressTitle:(NSString *)title {
5077 [prompt_ setText:title];
5080 - (void) _setProgressPercent:(NSNumber *)percent {
5081 [progress_ setProgress:[percent floatValue]];
5084 - (void) _addProgressOutput:(NSString *)output {
5089 /* Cydia:// Protocol {{{ */
5090 @interface CydiaURLProtocol : NSURLProtocol {
5095 @implementation CydiaURLProtocol
5097 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5098 NSURL *url([request URL]);
5101 NSString *scheme([[url scheme] lowercaseString]);
5102 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5107 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5111 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5112 id<NSURLProtocolClient> client([self client]);
5113 NSData *data(UIImagePNGRepresentation(icon));
5115 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5116 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5117 [client URLProtocol:self didLoadData:data];
5118 [client URLProtocolDidFinishLoading:self];
5121 - (void) startLoading {
5122 id<NSURLProtocolClient> client([self client]);
5123 NSURLRequest *request([self request]);
5125 NSURL *url([request URL]);
5126 NSString *href([url absoluteString]);
5128 NSString *path([href substringFromIndex:8]);
5129 NSRange slash([path rangeOfString:@"/"]);
5132 if (slash.location == NSNotFound) {
5136 command = [path substringToIndex:slash.location];
5137 path = [path substringFromIndex:(slash.location + 1)];
5140 Database *database([Database sharedInstance]);
5142 if ([command isEqualToString:@"package-icon"]) {
5145 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5146 Package *package([database packageWithName:path]);
5149 UIImage *icon([package icon]);
5150 [self _returnPNGWithImage:icon forRequest:request];
5151 } else if ([command isEqualToString:@"source-icon"]) {
5154 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5155 NSString *source(Simplify(path));
5156 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5158 icon = [UIImage applicationImageNamed:@"unknown.png"];
5159 [self _returnPNGWithImage:icon forRequest:request];
5160 } else if ([command isEqualToString:@"uikit-image"]) {
5163 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5164 UIImage *icon(_UIImageWithName(path));
5165 [self _returnPNGWithImage:icon forRequest:request];
5166 } else if ([command isEqualToString:@"section-icon"]) {
5169 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5170 NSString *section(Simplify(path));
5171 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5173 icon = [UIImage applicationImageNamed:@"unknown.png"];
5174 [self _returnPNGWithImage:icon forRequest:request];
5176 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5180 - (void) stopLoading {
5186 /* Install View {{{ */
5187 @interface InstallView : RVPage {
5188 _transient Database *database_;
5189 NSMutableArray *sections_;
5190 NSMutableArray *filtered_;
5191 UITransitionView *transition_;
5197 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5198 - (void) reloadData;
5203 @implementation InstallView
5206 [list_ setDataSource:nil];
5207 [list_ setDelegate:nil];
5209 [sections_ release];
5210 [filtered_ release];
5211 [transition_ release];
5213 [accessory_ release];
5217 - (int) numberOfRowsInTable:(UITable *)table {
5218 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5221 - (float) table:(UITable *)table heightForRow:(int)row {
5225 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5227 reusing = [[[SectionCell alloc] init] autorelease];
5228 [(SectionCell *)reusing setSection:(editing_ ?
5229 [sections_ objectAtIndex:row] :
5230 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5231 ) editing:editing_];
5235 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5239 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5243 - (void) tableRowSelected:(NSNotification *)notification {
5244 int row = [[notification object] selectedRow];
5255 title = @"All Packages";
5257 section = [filtered_ objectAtIndex:(row - 1)];
5258 name = [section name];
5264 title = @"(No Section)";
5268 PackageTable *table = [[[PackageTable alloc]
5272 filter:@selector(isVisiblyUninstalledInSection:)
5276 [table setDelegate:delegate_];
5278 [book_ pushPage:table];
5281 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5282 if ((self = [super initWithBook:book]) != nil) {
5283 database_ = database;
5285 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5286 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5288 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5289 [self addSubview:transition_];
5291 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5292 [transition_ transition:0 toView:list_];
5294 UITableColumn *column = [[[UITableColumn alloc]
5295 initWithTitle:@"Name"
5297 width:[self frame].size.width
5300 [list_ setDataSource:self];
5301 [list_ setSeparatorStyle:1];
5302 [list_ addTableColumn:column];
5303 [list_ setDelegate:self];
5304 [list_ setReusesTableCells:YES];
5308 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5309 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5313 - (void) reloadData {
5314 NSArray *packages = [database_ packages];
5316 [sections_ removeAllObjects];
5317 [filtered_ removeAllObjects];
5319 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5320 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5323 for (size_t i(0); i != [packages count]; ++i) {
5324 Package *package([packages objectAtIndex:i]);
5325 NSString *name([package section]);
5328 Section *section([sections objectForKey:name]);
5329 if (section == nil) {
5330 section = [[[Section alloc] initWithName:name] autorelease];
5331 [sections setObject:section forKey:name];
5335 if ([package valid] && [package installed] == nil && [package visible])
5336 [filtered addObject:package];
5340 [sections_ addObjectsFromArray:[sections allValues]];
5341 [sections_ sortUsingSelector:@selector(compareByName:)];
5344 [filtered sortUsingSelector:@selector(compareBySection:)];
5347 Section *section = nil;
5348 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5349 Package *package = [filtered objectAtIndex:offset];
5350 NSString *name = [package section];
5352 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5353 section = name == nil ?
5354 [[[Section alloc] initWithName:nil] autorelease] :
5355 [sections objectForKey:name];
5356 [filtered_ addObject:section];
5359 [section addToCount];
5367 - (void) resetView {
5369 [self _rightButtonClicked];
5372 - (void) resetViewAnimated:(BOOL)animated {
5373 [list_ resetViewAnimated:animated];
5376 - (void) _rightButtonClicked {
5377 if ((editing_ = !editing_))
5380 [delegate_ updateData];
5381 [book_ reloadTitleForPage:self];
5382 [book_ reloadButtonsForPage:self];
5385 - (NSString *) title {
5386 return editing_ ? @"Section Visibility" : @"Install by Section";
5389 - (NSString *) backButtonTitle {
5393 - (id) rightButtonTitle {
5394 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5397 - (UINavigationButtonStyle) rightButtonStyle {
5398 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5401 - (UIView *) accessoryView {
5407 /* Changes View {{{ */
5408 @interface ChangesView : RVPage {
5409 _transient Database *database_;
5410 NSMutableArray *packages_;
5411 NSMutableArray *sections_;
5412 UISectionList *list_;
5416 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5417 - (void) reloadData;
5421 @implementation ChangesView
5424 [[list_ table] setDelegate:nil];
5425 [list_ setDataSource:nil];
5427 [packages_ release];
5428 [sections_ release];
5433 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5434 return [sections_ count];
5437 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5438 return [[sections_ objectAtIndex:section] name];
5441 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5442 return [[sections_ objectAtIndex:section] row];
5445 - (int) numberOfRowsInTable:(UITable *)table {
5446 return [packages_ count];
5449 - (float) table:(UITable *)table heightForRow:(int)row {
5450 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5453 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5455 reusing = [[[PackageCell alloc] init] autorelease];
5456 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5460 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5464 - (void) tableRowSelected:(NSNotification *)notification {
5465 int row = [[notification object] selectedRow];
5468 Package *package = [packages_ objectAtIndex:row];
5469 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5470 [view setDelegate:delegate_];
5471 [view setPackage:package];
5472 [book_ pushPage:view];
5475 - (void) _leftButtonClicked {
5476 [(CYBook *)book_ update];
5477 [self reloadButtons];
5480 - (void) _rightButtonClicked {
5481 [delegate_ distUpgrade];
5484 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5485 if ((self = [super initWithBook:book]) != nil) {
5486 database_ = database;
5488 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5489 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5491 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5492 [self addSubview:list_];
5494 [list_ setShouldHideHeaderInShortLists:NO];
5495 [list_ setDataSource:self];
5496 //[list_ setSectionListStyle:1];
5498 UITableColumn *column = [[[UITableColumn alloc]
5499 initWithTitle:@"Name"
5501 width:[self frame].size.width
5504 UITable *table = [list_ table];
5505 [table setSeparatorStyle:1];
5506 [table addTableColumn:column];
5507 [table setDelegate:self];
5508 [table setReusesTableCells:YES];
5512 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5513 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5517 - (void) reloadData {
5518 NSArray *packages = [database_ packages];
5520 [packages_ removeAllObjects];
5521 [sections_ removeAllObjects];
5524 for (size_t i(0); i != [packages count]; ++i) {
5525 Package *package([packages objectAtIndex:i]);
5528 [package installed] == nil && [package valid] && [package visible] ||
5529 [package upgradableAndEssential:NO]
5531 [packages_ addObject:package];
5535 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5538 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5539 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5540 Section *section = nil;
5544 bool unseens = false;
5546 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5549 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5550 Package *package = [packages_ objectAtIndex:offset];
5552 if (![package upgradableAndEssential:YES]) {
5554 NSDate *seen = [package seen];
5556 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5559 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5560 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5561 [sections_ addObject:section];
5565 [section addToCount];
5566 } else if ([package ignored])
5567 [ignored addToCount];
5570 [upgradable addToCount];
5575 CFRelease(formatter);
5578 Section *last = [sections_ lastObject];
5579 size_t count = [last count];
5580 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5581 [sections_ removeLastObject];
5584 if ([ignored count] != 0)
5585 [sections_ insertObject:ignored atIndex:0];
5587 [sections_ insertObject:upgradable atIndex:0];
5590 [self reloadButtons];
5593 - (void) resetViewAnimated:(BOOL)animated {
5594 [list_ resetViewAnimated:animated];
5597 - (NSString *) leftButtonTitle {
5598 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5601 - (id) rightButtonTitle {
5602 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5605 - (NSString *) title {
5611 /* Search View {{{ */
5612 @protocol SearchViewDelegate
5613 - (void) showKeyboard:(BOOL)show;
5616 @interface SearchView : RVPage {
5618 UISearchField *field_;
5619 UITransitionView *transition_;
5620 PackageTable *table_;
5621 UIPreferencesTable *advanced_;
5627 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5628 - (void) reloadData;
5632 @implementation SearchView
5635 [field_ setDelegate:nil];
5637 [accessory_ release];
5639 [transition_ release];
5641 [advanced_ release];
5646 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5650 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5652 case 0: return @"Advanced Search (Coming Soon!)";
5654 default: _assert(false);
5658 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5662 default: _assert(false);
5666 - (void) _showKeyboard:(BOOL)show {
5667 CGSize keysize = [UIKeyboard defaultSize];
5668 CGRect keydown = [book_ pageBounds];
5669 CGRect keyup = keydown;
5670 keyup.size.height -= keysize.height - ButtonBarHeight_;
5672 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5674 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5675 [animation setSignificantRectFields:8];
5678 [animation setStartFrame:keydown];
5679 [animation setEndFrame:keyup];
5681 [animation setStartFrame:keyup];
5682 [animation setEndFrame:keydown];
5685 UIAnimator *animator = [UIAnimator sharedAnimator];
5688 addAnimations:[NSArray arrayWithObjects:animation, nil]
5689 withDuration:(KeyboardTime_ - delay)
5694 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5696 [delegate_ showKeyboard:show];
5699 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5700 [self _showKeyboard:YES];
5703 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5704 [self _showKeyboard:NO];
5707 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5709 NSString *text([field_ text]);
5710 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5716 - (void) textFieldClearButtonPressed:(UITextField *)field {
5720 - (void) keyboardInputShouldDelete:(id)input {
5724 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5725 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5729 [field_ resignFirstResponder];
5734 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5735 if ((self = [super initWithBook:book]) != nil) {
5736 CGRect pageBounds = [book_ pageBounds];
5738 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5739 [self addSubview:transition_];
5741 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5743 [advanced_ setReusesTableCells:YES];
5744 [advanced_ setDataSource:self];
5745 [advanced_ reloadData];
5747 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5748 CGColor dimmed(space_, 0, 0, 0, 0.5);
5749 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5751 table_ = [[PackageTable alloc]
5755 filter:@selector(isUnfilteredAndSearchedForBy:)
5759 [table_ setShouldHideHeaderInShortLists:NO];
5760 [transition_ transition:0 toView:table_];
5769 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5776 [self bounds].size.width - area.origin.x - 18;
5778 area.size.height = [UISearchField defaultHeight];
5780 field_ = [[UISearchField alloc] initWithFrame:area];
5782 UIFont *font = [UIFont systemFontOfSize:16];
5783 [field_ setFont:font];
5785 [field_ setPlaceholder:@"Package Names & Descriptions"];
5786 [field_ setDelegate:self];
5788 [field_ setPaddingTop:5];
5790 UITextInputTraits *traits([field_ textInputTraits]);
5791 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5792 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5793 [traits setReturnKeyType:UIReturnKeySearch];
5795 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5797 accessory_ = [[UIView alloc] initWithFrame:accrect];
5798 [accessory_ addSubview:field_];
5800 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5801 [configure setShowPressFeedback:YES];
5802 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5803 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5804 [accessory_ addSubview:configure];*/
5806 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5807 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5813 LKAnimation *animation = [LKTransition animation];
5814 [animation setType:@"oglFlip"];
5815 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5816 [animation setFillMode:@"extended"];
5817 [animation setTransitionFlags:3];
5818 [animation setDuration:10];
5819 [animation setSpeed:0.35];
5820 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5821 [[transition_ _layer] addAnimation:animation forKey:0];
5822 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5823 flipped_ = !flipped_;
5827 - (void) configurePushed {
5828 [field_ resignFirstResponder];
5832 - (void) resetViewAnimated:(BOOL)animated {
5835 [table_ resetViewAnimated:animated];
5838 - (void) _reloadData {
5841 - (void) reloadData {
5844 [table_ setObject:[field_ text]];
5845 [table_ reloadData];
5846 [table_ resetCursor];
5849 - (UIView *) accessoryView {
5853 - (NSString *) title {
5857 - (NSString *) backButtonTitle {
5861 - (void) setDelegate:(id)delegate {
5862 [table_ setDelegate:delegate];
5863 [super setDelegate:delegate];
5869 @interface SettingsView : RVPage {
5870 _transient Database *database_;
5873 UIPreferencesTable *table_;
5874 _UISwitchSlider *subscribedSwitch_;
5875 _UISwitchSlider *ignoredSwitch_;
5876 UIPreferencesControlTableCell *subscribedCell_;
5877 UIPreferencesControlTableCell *ignoredCell_;
5880 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5884 @implementation SettingsView
5887 [table_ setDataSource:nil];
5890 if (package_ != nil)
5893 [subscribedSwitch_ release];
5894 [ignoredSwitch_ release];
5895 [subscribedCell_ release];
5896 [ignoredCell_ release];
5900 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5901 if (package_ == nil)
5907 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5908 if (package_ == nil)
5915 default: _assert(false);
5921 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5922 if (package_ == nil)
5929 default: _assert(false);
5935 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5936 if (package_ == nil)
5943 default: _assert(false);
5949 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5950 if (package_ == nil)
5953 _UISwitchSlider *slider([cell control]);
5954 BOOL value([slider value] != 0);
5955 NSMutableDictionary *metadata([package_ metadata]);
5958 if (NSNumber *number = [metadata objectForKey:key])
5959 before = [number boolValue];
5963 if (value != before) {
5964 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5966 [delegate_ updateData];
5970 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
5971 [self onSomething:cell withKey:@"IsSubscribed"];
5974 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
5975 [self onSomething:cell withKey:@"IsIgnored"];
5978 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
5979 if (package_ == nil)
5983 case 0: switch (row) {
5985 return subscribedCell_;
5987 return ignoredCell_;
5988 default: _assert(false);
5991 case 1: switch (row) {
5993 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
5994 [cell setShowSelection:NO];
5995 [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."];
5999 default: _assert(false);
6002 default: _assert(false);
6008 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6009 if ((self = [super initWithBook:book])) {
6010 database_ = database;
6011 name_ = [package retain];
6013 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6014 [self addSubview:table_];
6016 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6017 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6019 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6020 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6022 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6023 [subscribedCell_ setShowSelection:NO];
6024 [subscribedCell_ setTitle:@"Show All Changes"];
6025 [subscribedCell_ setControl:subscribedSwitch_];
6027 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6028 [ignoredCell_ setShowSelection:NO];
6029 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6030 [ignoredCell_ setControl:ignoredSwitch_];
6032 [table_ setDataSource:self];
6037 - (void) resetViewAnimated:(BOOL)animated {
6038 [table_ resetViewAnimated:animated];
6041 - (void) reloadData {
6042 if (package_ != nil)
6043 [package_ autorelease];
6044 package_ = [database_ packageWithName:name_];
6045 if (package_ != nil) {
6047 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6048 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6051 [table_ reloadData];
6054 - (NSString *) title {
6060 /* Signature View {{{ */
6061 @interface SignatureView : BrowserView {
6062 _transient Database *database_;
6066 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6070 @implementation SignatureView
6077 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6079 [super webView:sender didClearWindowObject:window forFrame:frame];
6082 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6083 if ((self = [super initWithBook:book]) != nil) {
6084 database_ = database;
6085 package_ = [package retain];
6090 - (void) resetViewAnimated:(BOOL)animated {
6093 - (void) reloadData {
6094 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6100 @interface Cydia : UIApplication <
6101 ConfirmationViewDelegate,
6102 ProgressViewDelegate,
6111 UIToolbar *buttonbar_;
6115 NSMutableArray *essential_;
6116 NSMutableArray *broken_;
6118 Database *database_;
6119 ProgressView *progress_;
6123 UIKeyboard *keyboard_;
6124 UIProgressHUD *hud_;
6126 InstallView *install_;
6127 ChangesView *changes_;
6128 ManageView *manage_;
6129 SearchView *search_;
6134 @implementation Cydia
6137 if ([broken_ count] != 0) {
6138 int count = [broken_ count];
6140 UIActionSheet *sheet = [[[UIActionSheet alloc]
6141 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6142 buttons:[NSArray arrayWithObjects:
6144 @"Ignore (Temporary)",
6146 defaultButtonIndex:0
6151 [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."];
6152 [sheet popupAlertAnimated:YES];
6153 } else if (!Ignored_ && [essential_ count] != 0) {
6154 int count = [essential_ count];
6156 UIActionSheet *sheet = [[[UIActionSheet alloc]
6157 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6158 buttons:[NSArray arrayWithObjects:
6159 @"Upgrade Essential",
6160 @"Complete Upgrade",
6161 @"Ignore (Temporary)",
6163 defaultButtonIndex:0
6168 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6169 [sheet popupAlertAnimated:YES];
6173 - (void) _reloadData {
6174 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6175 [hud setText:@"Reloading Data"];
6176 [overlay_ addSubview:hud];
6179 [database_ reloadData];
6183 [essential_ removeAllObjects];
6184 [broken_ removeAllObjects];
6186 NSArray *packages = [database_ packages];
6187 for (Package *package in packages) {
6189 [broken_ addObject:package];
6190 if ([package upgradableAndEssential:NO]) {
6191 if ([package essential])
6192 [essential_ addObject:package];
6198 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6199 [buttonbar_ setBadgeValue:badge forButton:3];
6200 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6201 [buttonbar_ setBadgeAnimated:YES forButton:3];
6202 [self setApplicationBadge:badge];
6204 [buttonbar_ setBadgeValue:nil forButton:3];
6205 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6206 [buttonbar_ setBadgeAnimated:NO forButton:3];
6207 [self removeApplicationBadge];
6213 if ([packages count] == 0);
6225 [hud removeFromSuperview];*/
6228 - (void) _saveConfig {
6231 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6237 - (void) updateData {
6240 /* XXX: this is just stupid */
6241 if (tag_ != 2 && install_ != nil)
6242 [install_ reloadData];
6243 if (tag_ != 3 && changes_ != nil)
6244 [changes_ reloadData];
6245 if (tag_ != 5 && search_ != nil)
6246 [search_ reloadData];
6256 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6257 _assert(file != NULL);
6259 NSArray *keys = [Sources_ allKeys];
6261 for (int i(0), e([keys count]); i != e; ++i) {
6262 NSString *key = [keys objectAtIndex:i];
6263 NSDictionary *source = [Sources_ objectForKey:key];
6265 fprintf(file, "%s %s %s\n",
6266 [[source objectForKey:@"Type"] UTF8String],
6267 [[source objectForKey:@"URI"] UTF8String],
6268 [[source objectForKey:@"Distribution"] UTF8String]
6277 detachNewThreadSelector:@selector(update_)
6280 title:@"Updating Sources"
6284 - (void) reloadData {
6285 @synchronized (self) {
6286 if (confirm_ == nil)
6292 pkgProblemResolver *resolver = [database_ resolver];
6294 resolver->InstallProtect();
6295 if (!resolver->Resolve(true))
6300 [database_ prepare];
6302 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6303 [confirm_ setDelegate:self];
6305 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6306 [page setDelegate:self];
6308 [confirm_ setPage:page];
6309 [underlay_ popSubview:confirm_];
6312 - (void) installPackage:(Package *)package {
6313 @synchronized (self) {
6320 - (void) removePackage:(Package *)package {
6321 @synchronized (self) {
6328 - (void) distUpgrade {
6329 @synchronized (self) {
6330 [database_ upgrade];
6336 @synchronized (self) {
6338 if (confirm_ != nil) {
6346 [overlay_ removeFromSuperview];
6350 detachNewThreadSelector:@selector(perform)
6357 - (void) bootstrap_ {
6359 [database_ upgrade];
6360 [database_ prepare];
6361 [database_ perform];
6364 - (void) bootstrap {
6366 detachNewThreadSelector:@selector(bootstrap_)
6369 title:@"Bootstrap Install"
6373 - (void) progressViewIsComplete:(ProgressView *)progress {
6374 if (confirm_ != nil) {
6375 [underlay_ addSubview:overlay_];
6376 [confirm_ popFromSuperviewAnimated:NO];
6382 - (void) setPage:(RVPage *)page {
6383 [page resetViewAnimated:NO];
6384 [page setDelegate:self];
6385 [book_ setPage:page];
6388 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6389 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6390 [browser loadURL:url];
6394 - (void) _setHomePage {
6395 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6398 - (void) buttonBarItemTapped:(id)sender {
6399 unsigned tag = [sender tag];
6401 [book_ resetViewAnimated:YES];
6403 } else if (tag_ == 2 && tag != 2)
6404 [install_ resetView];
6407 case 1: [self _setHomePage]; break;
6409 case 2: [self setPage:install_]; break;
6410 case 3: [self setPage:changes_]; break;
6411 case 4: [self setPage:manage_]; break;
6412 case 5: [self setPage:search_]; break;
6414 default: _assert(false);
6420 - (void) applicationWillSuspend {
6422 [super applicationWillSuspend];
6425 - (void) askForSettings {
6426 UIActionSheet *role = [[[UIActionSheet alloc]
6427 initWithTitle:@"Who Are You?"
6428 buttons:[NSArray arrayWithObjects:
6429 @"User (Graphical Only)",
6430 @"Hacker (+ Command Line)",
6431 @"Developer (No Filters)",
6433 defaultButtonIndex:-1
6438 [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."];
6439 [role popupAlertAnimated:YES];
6444 [self setStatusBarShowsProgress:NO];
6447 [hud_ removeFromSuperview];
6451 pid_t pid = ExecFork();
6453 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6454 perror("launchctl stop");
6461 [self askForSettings];
6465 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6467 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6468 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6469 0, 0, screenrect.size.width, screenrect.size.height - 48
6470 ) database:database_];
6472 [book_ setDelegate:self];
6474 [overlay_ addSubview:book_];
6476 NSArray *buttonitems = [NSArray arrayWithObjects:
6477 [NSDictionary dictionaryWithObjectsAndKeys:
6478 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6479 @"home-up.png", kUIButtonBarButtonInfo,
6480 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6481 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6482 self, kUIButtonBarButtonTarget,
6483 @"Home", kUIButtonBarButtonTitle,
6484 @"0", kUIButtonBarButtonType,
6487 [NSDictionary dictionaryWithObjectsAndKeys:
6488 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6489 @"install-up.png", kUIButtonBarButtonInfo,
6490 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6491 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6492 self, kUIButtonBarButtonTarget,
6493 @"Sections", kUIButtonBarButtonTitle,
6494 @"0", kUIButtonBarButtonType,
6497 [NSDictionary dictionaryWithObjectsAndKeys:
6498 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6499 @"changes-up.png", kUIButtonBarButtonInfo,
6500 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6501 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6502 self, kUIButtonBarButtonTarget,
6503 @"Changes", kUIButtonBarButtonTitle,
6504 @"0", kUIButtonBarButtonType,
6507 [NSDictionary dictionaryWithObjectsAndKeys:
6508 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6509 @"manage-up.png", kUIButtonBarButtonInfo,
6510 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6511 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6512 self, kUIButtonBarButtonTarget,
6513 @"Manage", kUIButtonBarButtonTitle,
6514 @"0", kUIButtonBarButtonType,
6517 [NSDictionary dictionaryWithObjectsAndKeys:
6518 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6519 @"search-up.png", kUIButtonBarButtonInfo,
6520 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6521 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6522 self, kUIButtonBarButtonTarget,
6523 @"Search", kUIButtonBarButtonTitle,
6524 @"0", kUIButtonBarButtonType,
6528 buttonbar_ = [[UIToolbar alloc]
6530 withFrame:CGRectMake(
6531 0, screenrect.size.height - ButtonBarHeight_,
6532 screenrect.size.width, ButtonBarHeight_
6534 withItemList:buttonitems
6537 [buttonbar_ setDelegate:self];
6538 [buttonbar_ setBarStyle:1];
6539 [buttonbar_ setButtonBarTrackingMode:2];
6541 int buttons[5] = {1, 2, 3, 4, 5};
6542 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6543 [buttonbar_ showButtonGroup:0 withDuration:0];
6545 for (int i = 0; i != 5; ++i)
6546 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6547 i * 64 + 2, 1, 60, ButtonBarHeight_
6550 [buttonbar_ showSelectionForButton:1];
6551 [overlay_ addSubview:buttonbar_];
6553 [UIKeyboard initImplementationNow];
6554 CGSize keysize = [UIKeyboard defaultSize];
6555 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6556 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6557 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6558 [overlay_ addSubview:keyboard_];
6561 [underlay_ addSubview:overlay_];
6565 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6566 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6567 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6569 manage_ = (ManageView *) [[self
6570 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6571 withClass:[ManageView class]
6577 [self _setHomePage];
6580 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6581 NSString *context([sheet context]);
6583 if ([context isEqualToString:@"missing"])
6585 else if ([context isEqualToString:@"fixhalf"]) {
6588 @synchronized (self) {
6589 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6590 Package *broken = [broken_ objectAtIndex:i];
6593 NSString *id = [broken id];
6594 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6595 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6596 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6597 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6606 [broken_ removeAllObjects];
6615 } else if ([context isEqualToString:@"role"]) {
6617 case 1: Role_ = @"User"; break;
6618 case 2: Role_ = @"Hacker"; break;
6619 case 3: Role_ = @"Developer"; break;
6626 bool reset = Settings_ != nil;
6628 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6632 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6642 } else if ([context isEqualToString:@"upgrade"]) {
6645 @synchronized (self) {
6646 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6647 Package *essential = [essential_ objectAtIndex:i];
6648 [essential install];
6672 - (void) reorganize { _pooled
6673 system("/usr/libexec/cydia/free.sh");
6674 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6677 - (void) applicationSuspend:(__GSEvent *)event {
6678 if (hud_ == nil && ![progress_ isRunning])
6679 [super applicationSuspend:event];
6682 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6684 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6687 - (void) _setSuspended:(BOOL)value {
6689 [super _setSuspended:value];
6692 - (UIProgressHUD *) addProgressHUD {
6693 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6695 [underlay_ addSubview:hud];
6699 - (void) openMailToURL:(NSURL *)url {
6700 // XXX: this makes me sad
6702 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6704 [UIApp openURL:url];// asPanel:YES];
6708 - (void) clearFirstResponder {
6709 if (id responder = [window_ firstResponder])
6710 [responder resignFirstResponder];
6713 - (RVPage *) pageForPackage:(NSString *)name {
6714 if (Package *package = [database_ packageWithName:name]) {
6715 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6716 [view setPackage:package];
6719 UIActionSheet *sheet = [[[UIActionSheet alloc]
6720 initWithTitle:@"Cannot Locate Package"
6721 buttons:[NSArray arrayWithObjects:@"Close", nil]
6722 defaultButtonIndex:0
6727 [sheet setBodyText:[NSString stringWithFormat:
6728 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6731 [sheet popupAlertAnimated:YES];
6736 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6740 NSString *scheme([[url scheme] lowercaseString]);
6741 if (![scheme isEqualToString:@"cydia"])
6743 NSString *path([url absoluteString]);
6744 if ([path length] < 8)
6746 path = [path substringFromIndex:8];
6747 if (![path hasPrefix:@"/"])
6748 path = [@"/" stringByAppendingString:path];
6750 if ([path isEqualToString:@"/add-source"])
6751 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6752 else if ([path isEqualToString:@"/storage"])
6753 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6754 else if ([path isEqualToString:@"/sources"])
6755 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6756 else if ([path isEqualToString:@"/packages"])
6757 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6758 else if ([path hasPrefix:@"/url/"])
6759 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6760 else if ([path hasPrefix:@"/launch/"])
6761 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6762 else if ([path hasPrefix:@"/package-settings/"])
6763 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6764 else if ([path hasPrefix:@"/package-signature/"])
6765 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6766 else if ([path hasPrefix:@"/package/"])
6767 return [self pageForPackage:[path substringFromIndex:9]];
6768 else if ([path hasPrefix:@"/files/"]) {
6769 NSString *name = [path substringFromIndex:7];
6771 if (Package *package = [database_ packageWithName:name]) {
6772 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6773 [files setPackage:package];
6781 - (void) applicationOpenURL:(NSURL *)url {
6782 [super applicationOpenURL:url];
6784 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6785 [self setPage:page];
6786 [buttonbar_ showSelectionForButton:tag];
6791 - (void) applicationDidFinishLaunching:(id)unused {
6792 Font12_ = [[UIFont systemFontOfSize:12] retain];
6793 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6794 Font14_ = [[UIFont systemFontOfSize:14] retain];
6795 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6796 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6798 _assert(pkgInitConfig(*_config));
6799 _assert(pkgInitSystem(*_config, _system));
6803 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6804 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6806 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6808 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6809 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6811 [window_ orderFront:self];
6812 [window_ makeKey:self];
6813 [window_ setHidden:NO];
6815 database_ = [Database sharedInstance];
6816 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6817 [database_ setDelegate:progress_];
6818 [window_ setContentView:progress_];
6820 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6821 [progress_ setContentView:underlay_];
6823 [progress_ resetView];
6826 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6827 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6828 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6829 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6830 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6831 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6832 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6834 [self setIdleTimerDisabled:YES];
6836 hud_ = [self addProgressHUD];
6837 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6839 [self setStatusBarShowsProgress:YES];
6842 detachNewThreadSelector:@selector(reorganize)
6850 - (void) showKeyboard:(BOOL)show {
6851 CGSize keysize = [UIKeyboard defaultSize];
6852 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6853 CGRect keyup = keydown;
6854 keyup.origin.y -= keysize.height;
6856 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6857 [animation setSignificantRectFields:2];
6860 [animation setStartFrame:keydown];
6861 [animation setEndFrame:keyup];
6862 [keyboard_ activate];
6864 [animation setStartFrame:keyup];
6865 [animation setEndFrame:keydown];
6866 [keyboard_ deactivate];
6869 [[UIAnimator sharedAnimator]
6870 addAnimations:[NSArray arrayWithObjects:animation, nil]
6871 withDuration:KeyboardTime_
6876 - (void) slideUp:(UIActionSheet *)alert {
6878 [alert presentSheetFromButtonBar:buttonbar_];
6880 [alert presentSheetInView:overlay_];
6885 void AddPreferences(NSString *plist) { _pooled
6886 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6887 _assert(settings != NULL);
6888 NSMutableArray *items = [settings objectForKey:@"items"];
6892 for (size_t i(0); i != [items count]; ++i) {
6893 NSMutableDictionary *item([items objectAtIndex:i]);
6894 NSString *label = [item objectForKey:@"label"];
6895 if (label != nil && [label isEqualToString:@"Cydia"]) {
6902 for (size_t i(0); i != [items count]; ++i) {
6903 NSDictionary *item([items objectAtIndex:i]);
6904 NSString *label = [item objectForKey:@"label"];
6905 if (label != nil && [label isEqualToString:@"General"]) {
6906 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6907 @"CydiaSettings", @"bundle",
6908 @"PSLinkCell", @"cell",
6909 [NSNumber numberWithBool:YES], @"hasIcon",
6910 [NSNumber numberWithBool:YES], @"isController",
6912 nil] atIndex:(i + 1)];
6918 _assert([settings writeToFile:plist atomically:YES] == YES);
6923 id Alloc_(id self, SEL selector) {
6924 id object = alloc_(self, selector);
6925 lprintf("[%s]A-%p\n", self->isa->name, object);
6930 id Dealloc_(id self, SEL selector) {
6931 id object = dealloc_(self, selector);
6932 lprintf("[%s]D-%p\n", self->isa->name, object);
6936 int main(int argc, char *argv[]) { _pooled
6937 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6939 bool substrate(false);
6945 for (int argi(1); argi != argc; ++argi)
6946 if (strcmp(argv[argi], "--") == 0) {
6948 argv[argi] = argv[0];
6954 for (int argi(1); argi != arge; ++argi)
6955 if (strcmp(args[argi], "--bootstrap") == 0)
6957 else if (strcmp(args[argi], "--substrate") == 0)
6960 fprintf(stderr, "unknown argument: %s\n", args[argi]);
6963 App_ = [[NSBundle mainBundle] bundlePath];
6964 Home_ = NSHomeDirectory();
6965 Locale_ = CFLocaleCopyCurrent();
6968 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6969 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6970 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6971 Sounds_Keyboard_ = [keyboard boolValue];
6977 #if 1 /* XXX: this costs 1.4s of startup performance */
6978 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6979 _assert(errno == ENOENT);
6980 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6981 _assert(errno == ENOENT);
6984 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6985 alloc_ = alloc->method_imp;
6986 alloc->method_imp = (IMP) &Alloc_;*/
6988 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6989 dealloc_ = dealloc->method_imp;
6990 dealloc->method_imp = (IMP) &Dealloc_;*/
6995 size = sizeof(maxproc);
6996 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6997 perror("sysctlbyname(\"kern.maxproc\", ?)");
6998 else if (maxproc < 64) {
7000 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7001 perror("sysctlbyname(\"kern.maxproc\", #)");
7004 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7005 char *machine = new char[size];
7006 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7007 perror("sysctlbyname(\"hw.machine\", ?)");
7011 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7013 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7014 Build_ = [system objectForKey:@"ProductBuildVersion"];
7016 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7017 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7019 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7020 Indices_ = [[NSMutableDictionary alloc] init];*/
7022 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7023 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
7024 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7027 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7028 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7030 Settings_ = [Metadata_ objectForKey:@"Settings"];
7032 Packages_ = [Metadata_ objectForKey:@"Packages"];
7033 Sections_ = [Metadata_ objectForKey:@"Sections"];
7034 Sources_ = [Metadata_ objectForKey:@"Sources"];
7037 if (Settings_ != nil)
7038 Role_ = [Settings_ objectForKey:@"Role"];
7040 if (Packages_ == nil) {
7041 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7042 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7045 if (Sections_ == nil) {
7046 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7047 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7050 if (Sources_ == nil) {
7051 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7052 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7056 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7059 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7060 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7061 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7062 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7064 if (access("/User", F_OK) != 0)
7065 system("/usr/libexec/cydia/firmware.sh");
7067 _assert([[NSFileManager defaultManager]
7068 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7069 withIntermediateDirectories:YES
7074 space_ = CGColorSpaceCreateDeviceRGB();
7076 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7077 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7078 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7079 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7080 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7081 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7083 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7085 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7087 UIApplicationUseLegacyEvents(YES);
7088 UIKeyboardDisableAutomaticAppearance();
7090 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7092 CGColorSpaceRelease(space_);