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 @implementation UIView (PopUpView)
241 - (void) popFromSuperviewAnimated:(BOOL)animated {
242 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
245 - (void) popSubview:(UIView *)view {
246 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
247 [transition setDelegate:transition];
248 [self addSubview:transition];
250 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
251 [transition transition:UITransitionNone toView:blank];
252 [transition transition:UITransitionPushFromBottom toView:view];
258 #define lprintf(args...) fprintf(stderr, args)
261 #define ForSaurik (1 && !ForRelease)
262 #define IgnoreInstall (0 && !ForRelease)
263 #define RecycleWebViews 0
264 #define AlwaysReload (1 && !ForRelease)
268 #define _trace(args...)
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_;
621 static BOOL Ignored_;
623 static UIFont *Font12_;
624 static UIFont *Font12Bold_;
625 static UIFont *Font14_;
626 static UIFont *Font18Bold_;
627 static UIFont *Font22Bold_;
629 static const char *Machine_ = NULL;
630 static const NSString *UniqueID_ = nil;
631 static const NSString *Build_ = nil;
634 CGColorSpaceRef space_;
639 static NSDictionary *SectionMap_;
640 static NSMutableDictionary *Metadata_;
641 static NSMutableDictionary *Indices_;
642 static _transient NSMutableDictionary *Settings_;
643 static _transient NSString *Role_;
644 static _transient NSMutableDictionary *Packages_;
645 static _transient NSMutableDictionary *Sections_;
646 static _transient NSMutableDictionary *Sources_;
647 static bool Changed_;
651 static NSMutableArray *Documents_;
654 NSString *GetLastUpdate() {
655 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
658 return @"Never or Unknown";
660 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
661 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
663 CFRelease(formatter);
665 return [(NSString *) formatted autorelease];
668 /* Display Helpers {{{ */
669 inline float Interpolate(float begin, float end, float fraction) {
670 return (end - begin) * fraction + begin;
673 NSString *SizeString(double size) {
674 bool negative = size < 0;
679 while (size > 1024) {
684 static const char *powers_[] = {"B", "kB", "MB", "GB"};
686 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
689 NSString *StripVersion(NSString *version) {
690 NSRange colon = [version rangeOfString:@":"];
691 if (colon.location != NSNotFound)
692 version = [version substringFromIndex:(colon.location + 1)];
696 NSString *Simplify(NSString *title) {
697 const char *data = [title UTF8String];
698 size_t size = [title length];
700 static Pcre square_r("^\\[(.*)\\]$");
701 if (square_r(data, size))
702 return Simplify(square_r[1]);
704 static Pcre paren_r("^\\((.*)\\)$");
705 if (paren_r(data, size))
706 return Simplify(paren_r[1]);
708 static Pcre title_r("^(.*?) \\(.*\\)$");
709 if (title_r(data, size))
710 return Simplify(title_r[1]);
716 bool isSectionVisible(NSString *section) {
717 NSDictionary *metadata = [Sections_ objectForKey:section];
718 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
719 return hidden == nil || ![hidden boolValue];
722 /* Delegate Prototypes {{{ */
726 @interface NSObject (ProgressDelegate)
729 @implementation NSObject(ProgressDelegate)
731 - (void) _setProgressError:(NSArray *)args {
732 [self performSelector:@selector(setProgressError:forPackage:)
733 withObject:[args objectAtIndex:0]
734 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
740 @protocol ProgressDelegate
741 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
742 - (void) setProgressTitle:(NSString *)title;
743 - (void) setProgressPercent:(float)percent;
744 - (void) startProgress;
745 - (void) addProgressOutput:(NSString *)output;
746 - (bool) isCancelling:(size_t)received;
749 @protocol ConfigurationDelegate
750 - (void) repairWithSelector:(SEL)selector;
751 - (void) setConfigurationData:(NSString *)data;
754 @protocol CydiaDelegate
755 - (void) installPackage:(Package *)package;
756 - (void) removePackage:(Package *)package;
757 - (void) slideUp:(UIActionSheet *)alert;
758 - (void) distUpgrade;
761 - (void) askForSettings;
762 - (UIProgressHUD *) addProgressHUD;
763 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
764 - (RVPage *) pageForPackage:(NSString *)name;
765 - (void) openMailToURL:(NSURL *)url;
766 - (void) clearFirstResponder;
770 /* Status Delegation {{{ */
772 public pkgAcquireStatus
775 _transient NSObject<ProgressDelegate> *delegate_;
783 void setDelegate(id delegate) {
784 delegate_ = delegate;
787 virtual bool MediaChange(std::string media, std::string drive) {
791 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
794 virtual void Fetch(pkgAcquire::ItemDesc &item) {
795 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
796 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
799 virtual void Done(pkgAcquire::ItemDesc &item) {
802 virtual void Fail(pkgAcquire::ItemDesc &item) {
804 item.Owner->Status == pkgAcquire::Item::StatIdle ||
805 item.Owner->Status == pkgAcquire::Item::StatDone
809 std::string &error(item.Owner->ErrorText);
813 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
814 NSArray *fields([description componentsSeparatedByString:@" "]);
815 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
817 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
818 withObject:[NSArray arrayWithObjects:
819 [NSString stringWithUTF8String:error.c_str()],
826 virtual bool Pulse(pkgAcquire *Owner) {
827 bool value = pkgAcquireStatus::Pulse(Owner);
830 double(CurrentBytes + CurrentItems) /
831 double(TotalBytes + TotalItems)
834 [delegate_ setProgressPercent:percent];
835 return [delegate_ isCancelling:CurrentBytes] ? false : value;
838 virtual void Start() {
839 [delegate_ startProgress];
842 virtual void Stop() {
846 /* Progress Delegation {{{ */
851 _transient id<ProgressDelegate> delegate_;
854 virtual void Update() {
855 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
856 [delegate_ setProgressPercent:(Percent / 100)];
865 void setDelegate(id delegate) {
866 delegate_ = delegate;
869 virtual void Done() {
870 [delegate_ setProgressPercent:1];
875 /* Database Interface {{{ */
876 @interface Database : NSObject {
878 pkgDepCache::Policy *policy_;
879 pkgRecords *records_;
880 pkgProblemResolver *resolver_;
881 pkgAcquire *fetcher_;
883 SPtr<pkgPackageManager> manager_;
884 pkgSourceList *list_;
886 NSMutableDictionary *sources_;
887 NSMutableArray *packages_;
889 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
898 + (Database *) sharedInstance;
900 - (void) _readCydia:(NSNumber *)fd;
901 - (void) _readStatus:(NSNumber *)fd;
902 - (void) _readOutput:(NSNumber *)fd;
906 - (Package *) packageWithName:(NSString *)name;
908 - (pkgCacheFile &) cache;
909 - (pkgDepCache::Policy *) policy;
910 - (pkgRecords *) records;
911 - (pkgProblemResolver *) resolver;
912 - (pkgAcquire &) fetcher;
913 - (pkgSourceList &) list;
914 - (NSArray *) packages;
915 - (NSArray *) sources;
924 - (void) updateWithStatus:(Status &)status;
926 - (void) setDelegate:(id)delegate;
927 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
931 /* Source Class {{{ */
932 @interface Source : NSObject {
933 NSString *description_;
938 NSString *distribution_;
942 NSString *defaultIcon_;
944 NSDictionary *record_;
948 - (Source *) initWithMetaIndex:(metaIndex *)index;
950 - (NSComparisonResult) compareByNameAndType:(Source *)source;
952 - (NSDictionary *) record;
956 - (NSString *) distribution;
962 - (NSString *) description;
963 - (NSString *) label;
964 - (NSString *) origin;
965 - (NSString *) version;
967 - (NSString *) defaultIcon;
971 @implementation Source
973 #define _clear(field) \
980 _clear(distribution_)
996 + (NSArray *) _attributeKeys {
997 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1000 - (NSArray *) attributeKeys {
1001 return [[self class] _attributeKeys];
1004 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1005 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1008 - (void) setMetaIndex:(metaIndex *)index {
1011 trusted_ = index->IsTrusted();
1013 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1014 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1015 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1017 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1018 if (dindex != NULL) {
1019 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1021 while (std::getline(release, line)) {
1022 std::string::size_type colon(line.find(':'));
1023 if (colon == std::string::npos)
1026 std::string name(line.substr(0, colon));
1027 std::string value(line.substr(colon + 1));
1028 while (!value.empty() && value[0] == ' ')
1029 value = value.substr(1);
1031 if (name == "Default-Icon")
1032 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1033 else if (name == "Description")
1034 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1035 else if (name == "Label")
1036 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1037 else if (name == "Origin")
1038 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1039 else if (name == "Version")
1040 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1044 record_ = [Sources_ objectForKey:[self key]];
1046 record_ = [record_ retain];
1049 - (Source *) initWithMetaIndex:(metaIndex *)index {
1050 if ((self = [super init]) != nil) {
1051 [self setMetaIndex:index];
1055 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1056 NSDictionary *lhr = [self record];
1057 NSDictionary *rhr = [source record];
1060 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1062 NSString *lhs = [self name];
1063 NSString *rhs = [source name];
1065 if ([lhs length] != 0 && [rhs length] != 0) {
1066 unichar lhc = [lhs characterAtIndex:0];
1067 unichar rhc = [rhs characterAtIndex:0];
1069 if (isalpha(lhc) && !isalpha(rhc))
1070 return NSOrderedAscending;
1071 else if (!isalpha(lhc) && isalpha(rhc))
1072 return NSOrderedDescending;
1075 return [lhs compare:rhs options:LaxCompareOptions_];
1078 - (NSDictionary *) record {
1086 - (NSString *) uri {
1090 - (NSString *) distribution {
1091 return distribution_;
1094 - (NSString *) type {
1098 - (NSString *) key {
1099 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1102 - (NSString *) host {
1103 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1106 - (NSString *) name {
1107 return origin_ == nil ? [self host] : origin_;
1110 - (NSString *) description {
1111 return description_;
1114 - (NSString *) label {
1115 return label_ == nil ? [self host] : label_;
1118 - (NSString *) origin {
1122 - (NSString *) version {
1126 - (NSString *) defaultIcon {
1127 return defaultIcon_;
1132 /* Relationship Class {{{ */
1133 @interface Relationship : NSObject {
1138 - (NSString *) type;
1140 - (NSString *) name;
1144 @implementation Relationship
1152 - (NSString *) type {
1160 - (NSString *) name {
1167 /* Package Class {{{ */
1168 @interface Package : NSObject {
1169 pkgCache::PkgIterator iterator_;
1170 _transient Database *database_;
1171 pkgCache::VerIterator version_;
1172 pkgCache::VerFileIterator file_;
1180 NSString *installed_;
1186 NSString *depiction_;
1187 NSString *homepage_;
1193 NSArray *relationships_;
1196 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1197 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1199 - (pkgCache::PkgIterator) iterator;
1201 - (NSString *) section;
1202 - (NSString *) simpleSection;
1206 - (Address *) maintainer;
1208 - (NSString *) description;
1209 - (NSString *) index;
1211 - (NSMutableDictionary *) metadata;
1213 - (BOOL) subscribed;
1216 - (NSString *) latest;
1217 - (NSString *) installed;
1220 - (BOOL) upgradableAndEssential:(BOOL)essential;
1223 - (BOOL) unfiltered;
1227 - (BOOL) halfConfigured;
1228 - (BOOL) halfInstalled;
1230 - (NSString *) mode;
1233 - (NSString *) name;
1234 - (NSString *) tagline;
1236 - (NSString *) homepage;
1237 - (NSString *) depiction;
1238 - (Address *) author;
1240 - (NSArray *) files;
1241 - (NSArray *) relationships;
1242 - (NSArray *) warnings;
1243 - (NSArray *) applications;
1245 - (Source *) source;
1246 - (NSString *) role;
1247 - (NSString *) rating;
1249 - (BOOL) matches:(NSString *)text;
1251 - (bool) hasSupportingRole;
1252 - (BOOL) hasTag:(NSString *)tag;
1253 - (NSString *) primaryPurpose;
1254 - (NSArray *) purposes;
1256 - (NSComparisonResult) compareByName:(Package *)package;
1257 - (NSComparisonResult) compareBySection:(Package *)package;
1259 - (uint32_t) compareForChanges;
1264 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1265 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1266 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1267 - (NSNumber *) isVisibleInSource:(Source *)source;
1271 @implementation Package
1277 if (section_ != nil)
1281 if (installed_ != nil)
1282 [installed_ release];
1290 if (depiction_ != nil)
1291 [depiction_ release];
1292 if (homepage_ != nil)
1293 [homepage_ release];
1294 if (sponsor_ != nil)
1303 if (relationships_ != nil)
1304 [relationships_ release];
1309 + (NSArray *) _attributeKeys {
1310 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1313 - (NSArray *) attributeKeys {
1314 return [[self class] _attributeKeys];
1317 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1318 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1321 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1322 if ((self = [super init]) != nil) {
1323 iterator_ = iterator;
1324 database_ = database;
1326 version_ = [database_ policy]->GetCandidateVer(iterator_);
1327 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1328 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1330 pkgCache::VerIterator current = iterator_.CurrentVer();
1331 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1332 installed_ = [StripVersion(installed) retain];
1334 if (!version_.end())
1335 file_ = version_.FileList();
1337 pkgCache &cache([database_ cache]);
1338 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1341 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1344 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1346 const char *begin, *end;
1347 parser->GetRec(begin, end);
1349 NSString *website(nil);
1350 NSString *sponsor(nil);
1351 NSString *author(nil);
1360 {"depiction", &depiction_},
1361 {"homepage", &homepage_},
1362 {"website", &website},
1363 {"sponsor", &sponsor},
1364 {"author", &author},
1368 while (begin != end)
1369 if (*begin == '\n') {
1372 } else if (isblank(*begin)) next: {
1373 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1376 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1377 const char *name(begin);
1378 size_t size(colon - begin);
1380 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1383 const char *stop(begin == NULL ? end : begin);
1384 while (stop[-1] == '\r')
1386 while (++colon != stop && isblank(*colon));
1388 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1389 if (strncasecmp(names[i].name_, name, size) == 0) {
1390 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1391 *names[i].value_ = value;
1402 name_ = [name_ retain];
1403 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1405 icon_ = [icon_ retain];
1406 if (depiction_ != nil)
1407 depiction_ = [depiction_ retain];
1408 if (homepage_ == nil)
1409 homepage_ = website;
1410 if ([homepage_ isEqualToString:depiction_])
1412 if (homepage_ != nil)
1413 homepage_ = [homepage_ retain];
1415 sponsor_ = [[Address addressWithString:sponsor] retain];
1417 author_ = [[Address addressWithString:author] retain];
1419 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1423 for (int i(0), e([tags_ count]); i != e; ++i) {
1424 NSString *tag = [tags_ objectAtIndex:i];
1425 if ([tag hasPrefix:@"role::"]) {
1426 role_ = [[tag substringFromIndex:6] retain];
1431 NSString *solid(latest == nil ? installed : latest);
1432 bool changed(false);
1434 NSString *key([id_ lowercaseString]);
1436 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1437 if (metadata == nil) {
1438 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1443 [metadata setObject:solid forKey:@"LastVersion"];
1446 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1447 NSDate *last([metadata objectForKey:@"LastSeen"]);
1448 NSString *version([metadata objectForKey:@"LastVersion"]);
1451 first = last == nil ? now_ : last;
1452 [metadata setObject:first forKey:@"FirstSeen"];
1457 if (version == nil) {
1458 [metadata setObject:solid forKey:@"LastVersion"];
1460 } else if (![version isEqualToString:solid]) {
1461 [metadata setObject:solid forKey:@"LastVersion"];
1463 [metadata setObject:last forKey:@"LastSeen"];
1469 [Packages_ setObject:metadata forKey:key];
1475 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1476 return [[[Package alloc]
1477 initWithIterator:iterator
1482 - (pkgCache::PkgIterator) iterator {
1486 - (NSString *) section {
1487 if (section_ != nil)
1490 const char *section = iterator_.Section();
1491 if (section == NULL)
1494 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1497 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1498 if (NSString *rename = [value objectForKey:@"Rename"]) {
1503 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1507 - (NSString *) simpleSection {
1508 if (NSString *section = [self section])
1509 return Simplify(section);
1514 - (NSString *) uri {
1517 pkgIndexFile *index;
1518 pkgCache::PkgFileIterator file(file_.File());
1519 if (![database_ list].FindIndex(file, index))
1521 return [NSString stringWithUTF8String:iterator_->Path];
1522 //return [NSString stringWithUTF8String:file.Site()];
1523 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1527 - (Address *) maintainer {
1530 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1531 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1535 return version_.end() ? 0 : version_->InstalledSize;
1538 - (NSString *) description {
1541 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1542 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1544 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1545 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1546 if ([lines count] < 2)
1549 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1550 for (size_t i(1); i != [lines count]; ++i) {
1551 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1552 [trimmed addObject:trim];
1555 return [trimmed componentsJoinedByString:@"\n"];
1558 - (NSString *) index {
1559 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1560 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1563 - (NSMutableDictionary *) metadata {
1564 return [Packages_ objectForKey:[id_ lowercaseString]];
1568 NSDictionary *metadata([self metadata]);
1569 if ([self subscribed])
1570 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1572 return [metadata objectForKey:@"FirstSeen"];
1575 - (BOOL) subscribed {
1576 NSDictionary *metadata([self metadata]);
1577 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1578 return [subscribed boolValue];
1584 NSDictionary *metadata([self metadata]);
1585 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1586 return [ignored boolValue];
1591 - (NSString *) latest {
1595 - (NSString *) installed {
1600 return !version_.end();
1603 - (BOOL) upgradableAndEssential:(BOOL)essential {
1604 pkgCache::VerIterator current = iterator_.CurrentVer();
1608 value = essential && [self essential];
1610 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1614 - (BOOL) essential {
1615 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1619 return [database_ cache][iterator_].InstBroken();
1622 - (BOOL) unfiltered {
1623 NSString *section = [self section];
1624 return section == nil || isSectionVisible(section);
1628 return [self hasSupportingRole] && [self unfiltered];
1632 unsigned char current = iterator_->CurrentState;
1633 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1636 - (BOOL) halfConfigured {
1637 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1640 - (BOOL) halfInstalled {
1641 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1645 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1646 return state.Mode != pkgDepCache::ModeKeep;
1649 - (NSString *) mode {
1650 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1652 switch (state.Mode) {
1653 case pkgDepCache::ModeDelete:
1654 if ((state.iFlags & pkgDepCache::Purge) != 0)
1658 case pkgDepCache::ModeKeep:
1659 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1663 case pkgDepCache::ModeInstall:
1664 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1665 return @"Reinstall";
1666 else switch (state.Status) {
1668 return @"Downgrade";
1674 return @"New Install";
1687 - (NSString *) name {
1688 return name_ == nil ? id_ : name_;
1691 - (NSString *) tagline {
1695 - (UIImage *) icon {
1696 NSString *section = [self simpleSection];
1700 if ([icon_ hasPrefix:@"file:///"])
1701 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1702 if (icon == nil) if (section != nil)
1703 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1704 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1705 if ([dicon hasPrefix:@"file:///"])
1706 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1708 icon = [UIImage applicationImageNamed:@"unknown.png"];
1712 - (NSString *) homepage {
1716 - (NSString *) depiction {
1720 - (Address *) sponsor {
1724 - (Address *) author {
1728 - (NSArray *) files {
1729 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1730 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1733 fin.open([path UTF8String]);
1738 while (std::getline(fin, line))
1739 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1744 - (NSArray *) relationships {
1745 return relationships_;
1748 - (NSArray *) warnings {
1749 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1750 const char *name(iterator_.Name());
1752 size_t length(strlen(name));
1753 if (length < 2) invalid:
1754 [warnings addObject:@"illegal package identifier"];
1755 else for (size_t i(0); i != length; ++i)
1757 /* XXX: technically this is not allowed */
1758 (name[i] < 'A' || name[i] > 'Z') &&
1759 (name[i] < 'a' || name[i] > 'z') &&
1760 (name[i] < '0' || name[i] > '9') &&
1761 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1764 if (strcmp(name, "cydia") != 0) {
1766 bool _private = false;
1769 bool repository = [[self section] isEqualToString:@"Repositories"];
1771 if (NSArray *files = [self files])
1772 for (NSString *file in files)
1773 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1775 else if (!_private && [file isEqualToString:@"/private"])
1777 else if (!stash && [file isEqualToString:@"/var/stash"])
1780 /* XXX: this is not sensitive enough. only some folders are valid. */
1781 if (cydia && !repository)
1782 [warnings addObject:@"files installed into Cydia.app"];
1784 [warnings addObject:@"files installed with /private/*"];
1786 [warnings addObject:@"files installed to /var/stash"];
1789 return [warnings count] == 0 ? nil : warnings;
1792 - (NSArray *) applications {
1793 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1795 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1797 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1798 if (NSArray *files = [self files])
1799 for (NSString *file in files)
1800 if (application_r(file)) {
1801 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1802 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1803 if ([id isEqualToString:me])
1806 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1808 display = application_r[1];
1810 NSString *bundle([file stringByDeletingLastPathComponent]);
1811 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1812 if (icon == nil || [icon length] == 0)
1814 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1816 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1817 [applications addObject:application];
1819 [application addObject:id];
1820 [application addObject:display];
1821 [application addObject:url];
1824 return [applications count] == 0 ? nil : applications;
1827 - (Source *) source {
1829 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1836 - (NSString *) role {
1840 - (NSString *) rating {
1841 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1842 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1847 - (BOOL) matches:(NSString *)text {
1853 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1854 if (range.location != NSNotFound)
1857 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1858 if (range.location != NSNotFound)
1861 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1862 if (range.location != NSNotFound)
1868 - (bool) hasSupportingRole {
1871 if ([role_ isEqualToString:@"enduser"])
1873 if ([Role_ isEqualToString:@"User"])
1875 if ([role_ isEqualToString:@"hacker"])
1877 if ([Role_ isEqualToString:@"Hacker"])
1879 if ([role_ isEqualToString:@"developer"])
1881 if ([Role_ isEqualToString:@"Developer"])
1886 - (BOOL) hasTag:(NSString *)tag {
1887 return tags_ == nil ? NO : [tags_ containsObject:tag];
1890 - (NSString *) primaryPurpose {
1891 for (NSString *tag in tags_)
1892 if ([tag hasPrefix:@"purpose::"])
1893 return [tag substringFromIndex:9];
1897 - (NSArray *) purposes {
1898 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1899 for (NSString *tag in tags_)
1900 if ([tag hasPrefix:@"purpose::"])
1901 [purposes addObject:[tag substringFromIndex:9]];
1902 return [purposes count] == 0 ? nil : purposes;
1905 - (NSComparisonResult) compareByName:(Package *)package {
1906 NSString *lhs = [self name];
1907 NSString *rhs = [package name];
1909 if ([lhs length] != 0 && [rhs length] != 0) {
1910 unichar lhc = [lhs characterAtIndex:0];
1911 unichar rhc = [rhs characterAtIndex:0];
1913 if (isalpha(lhc) && !isalpha(rhc))
1914 return NSOrderedAscending;
1915 else if (!isalpha(lhc) && isalpha(rhc))
1916 return NSOrderedDescending;
1919 return [lhs compare:rhs options:LaxCompareOptions_];
1922 - (NSComparisonResult) compareBySection:(Package *)package {
1923 NSString *lhs = [self section];
1924 NSString *rhs = [package section];
1926 if (lhs == NULL && rhs != NULL)
1927 return NSOrderedAscending;
1928 else if (lhs != NULL && rhs == NULL)
1929 return NSOrderedDescending;
1930 else if (lhs != NULL && rhs != NULL) {
1931 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1932 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1935 return NSOrderedSame;
1938 - (uint32_t) compareForChanges {
1943 uint32_t timestamp : 30;
1944 uint32_t ignored : 1;
1945 uint32_t upgradable : 1;
1949 bool upgradable([self upgradableAndEssential:YES]);
1950 value.bits.upgradable = upgradable ? 1 : 0;
1953 value.bits.timestamp = 0;
1954 value.bits.ignored = [self ignored] ? 0 : 1;
1955 value.bits.upgradable = 1;
1957 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1958 value.bits.ignored = 0;
1959 value.bits.upgradable = 0;
1962 return _not(uint32_t) - value.key;
1966 pkgProblemResolver *resolver = [database_ resolver];
1967 resolver->Clear(iterator_);
1968 resolver->Protect(iterator_);
1969 pkgCacheFile &cache([database_ cache]);
1970 cache->MarkInstall(iterator_, false);
1971 pkgDepCache::StateCache &state((*cache)[iterator_]);
1972 if (!state.Install())
1973 cache->SetReInstall(iterator_, true);
1977 pkgProblemResolver *resolver = [database_ resolver];
1978 resolver->Clear(iterator_);
1979 resolver->Protect(iterator_);
1980 resolver->Remove(iterator_);
1981 [database_ cache]->MarkDelete(iterator_, true);
1984 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1985 return [NSNumber numberWithBool:(
1986 [self unfiltered] && [self matches:search]
1990 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1991 return [NSNumber numberWithBool:(
1992 (![number boolValue] || [self visible]) && [self installed] != nil
1996 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1997 NSString *section = [self section];
1999 return [NSNumber numberWithBool:(
2001 [self installed] == nil && (
2003 section == nil && [name length] == 0 ||
2004 [name isEqualToString:section]
2009 - (NSNumber *) isVisibleInSource:(Source *)source {
2010 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2015 /* Section Class {{{ */
2016 @interface Section : NSObject {
2022 - (NSComparisonResult) compareByName:(Section *)section;
2023 - (Section *) initWithName:(NSString *)name;
2024 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2025 - (NSString *) name;
2028 - (void) addToCount;
2032 @implementation Section
2039 - (NSComparisonResult) compareByName:(Section *)section {
2040 NSString *lhs = [self name];
2041 NSString *rhs = [section name];
2043 if ([lhs length] != 0 && [rhs length] != 0) {
2044 unichar lhc = [lhs characterAtIndex:0];
2045 unichar rhc = [rhs characterAtIndex:0];
2047 if (isalpha(lhc) && !isalpha(rhc))
2048 return NSOrderedAscending;
2049 else if (!isalpha(lhc) && isalpha(rhc))
2050 return NSOrderedDescending;
2053 return [lhs compare:rhs options:LaxCompareOptions_];
2056 - (Section *) initWithName:(NSString *)name {
2057 return [self initWithName:name row:0];
2060 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2061 if ((self = [super init]) != nil) {
2062 name_ = [name retain];
2067 - (NSString *) name {
2079 - (void) addToCount {
2087 static NSArray *Finishes_;
2089 /* Database Implementation {{{ */
2090 @implementation Database
2092 + (Database *) sharedInstance {
2093 static Database *instance;
2094 if (instance == nil)
2095 instance = [[Database alloc] init];
2104 - (void) _readCydia:(NSNumber *)fd { _pooled
2105 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2106 std::istream is(&ib);
2109 static Pcre finish_r("^finish:([^:]*)$");
2111 while (std::getline(is, line)) {
2112 const char *data(line.c_str());
2113 size_t size = line.size();
2114 lprintf("C:%s\n", data);
2116 if (finish_r(data, size)) {
2117 NSString *finish = finish_r[1];
2118 int index = [Finishes_ indexOfObject:finish];
2119 if (index != INT_MAX && index > Finish_)
2127 - (void) _readStatus:(NSNumber *)fd { _pooled
2128 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2129 std::istream is(&ib);
2132 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2133 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2135 while (std::getline(is, line)) {
2136 const char *data(line.c_str());
2137 size_t size = line.size();
2138 lprintf("S:%s\n", data);
2140 if (conffile_r(data, size)) {
2141 [delegate_ setConfigurationData:conffile_r[1]];
2142 } else if (strncmp(data, "status: ", 8) == 0) {
2143 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2144 [delegate_ setProgressTitle:string];
2145 } else if (pmstatus_r(data, size)) {
2146 std::string type([pmstatus_r[1] UTF8String]);
2147 NSString *id = pmstatus_r[2];
2149 float percent([pmstatus_r[3] floatValue]);
2150 [delegate_ setProgressPercent:(percent / 100)];
2152 NSString *string = pmstatus_r[4];
2154 if (type == "pmerror")
2155 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2156 withObject:[NSArray arrayWithObjects:string, id, nil]
2159 else if (type == "pmstatus") {
2160 [delegate_ setProgressTitle:string];
2161 } else if (type == "pmconffile")
2162 [delegate_ setConfigurationData:string];
2163 else _assert(false);
2164 } else _assert(false);
2170 - (void) _readOutput:(NSNumber *)fd { _pooled
2171 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2172 std::istream is(&ib);
2175 while (std::getline(is, line)) {
2176 lprintf("O:%s\n", line.c_str());
2177 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2187 - (Package *) packageWithName:(NSString *)name {
2188 if (static_cast<pkgDepCache *>(cache_) == NULL)
2190 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2191 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2194 - (Database *) init {
2195 if ((self = [super init]) != nil) {
2202 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2203 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2207 _assert(pipe(fds) != -1);
2210 _config->Set("APT::Keep-Fds::", cydiafd_);
2211 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2214 detachNewThreadSelector:@selector(_readCydia:)
2216 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2219 _assert(pipe(fds) != -1);
2223 detachNewThreadSelector:@selector(_readStatus:)
2225 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2228 _assert(pipe(fds) != -1);
2229 _assert(dup2(fds[0], 0) != -1);
2230 _assert(close(fds[0]) != -1);
2232 input_ = fdopen(fds[1], "a");
2234 _assert(pipe(fds) != -1);
2235 _assert(dup2(fds[1], 1) != -1);
2236 _assert(close(fds[1]) != -1);
2239 detachNewThreadSelector:@selector(_readOutput:)
2241 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2246 - (pkgCacheFile &) cache {
2250 - (pkgDepCache::Policy *) policy {
2254 - (pkgRecords *) records {
2258 - (pkgProblemResolver *) resolver {
2262 - (pkgAcquire &) fetcher {
2266 - (pkgSourceList &) list {
2270 - (NSArray *) packages {
2274 - (NSArray *) sources {
2275 return [sources_ allValues];
2278 - (NSArray *) issues {
2279 if (cache_->BrokenCount() == 0)
2282 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2284 for (Package *package in packages_) {
2285 if (![package broken])
2287 pkgCache::PkgIterator pkg([package iterator]);
2289 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2290 [entry addObject:[package name]];
2291 [issues addObject:entry];
2293 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2297 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2298 pkgCache::DepIterator start;
2299 pkgCache::DepIterator end;
2300 dep.GlobOr(start, end); // ++dep
2302 if (!cache_->IsImportantDep(end))
2304 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2307 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2308 [entry addObject:failure];
2309 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2311 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2312 [failure addObject:[package name]];
2314 pkgCache::PkgIterator target(start.TargetPkg());
2315 if (target->ProvidesList != 0)
2316 [failure addObject:@"?"];
2318 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2320 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2321 else if (!cache_[target].CandidateVerIter(cache_).end())
2322 [failure addObject:@"-"];
2323 else if (target->ProvidesList == 0)
2324 [failure addObject:@"!"];
2326 [failure addObject:@"%"];
2330 if (start.TargetVer() != 0)
2331 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2342 - (void) reloadData {
2362 if (!cache_.Open(progress_, true)) {
2364 if (!_error->PopMessage(error))
2367 lprintf("cache_.Open():[%s]\n", error.c_str());
2369 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2370 [delegate_ repairWithSelector:@selector(configure)];
2371 else if (error == "The package lists or status file could not be parsed or opened.")
2372 [delegate_ repairWithSelector:@selector(update)];
2373 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2374 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2375 // else if (error == "The list of sources could not be read.")
2376 else _assert(false);
2382 now_ = [[NSDate date] retain];
2384 policy_ = new pkgDepCache::Policy();
2385 records_ = new pkgRecords(cache_);
2386 resolver_ = new pkgProblemResolver(cache_);
2387 fetcher_ = new pkgAcquire(&status_);
2390 list_ = new pkgSourceList();
2391 _assert(list_->ReadMainList());
2393 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2394 _assert(pkgApplyStatus(cache_));
2396 if (cache_->BrokenCount() != 0) {
2397 _assert(pkgFixBroken(cache_));
2398 _assert(cache_->BrokenCount() == 0);
2399 _assert(pkgMinimizeUpgrade(cache_));
2402 [sources_ removeAllObjects];
2403 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2404 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2405 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2407 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2408 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2412 [packages_ removeAllObjects];
2415 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2416 if (Package *package = [Package packageWithIterator:iterator database:self])
2417 [packages_ addObject:package];
2419 [packages_ sortUsingSelector:@selector(compareByName:)];
2423 - (void) configure {
2424 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2425 system([dpkg UTF8String]);
2433 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2434 _assert(!_error->PendingError());
2437 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2440 public pkgArchiveCleaner
2443 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2448 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2450 while (_error->PopMessage(error))
2451 lprintf("ArchiveCleaner: %s\n", error.c_str());
2456 pkgRecords records(cache_);
2458 lock_ = new FileFd();
2459 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2460 _assert(!_error->PendingError());
2463 // XXX: explain this with an error message
2464 _assert(list.ReadMainList());
2466 manager_ = (_system->CreatePM(cache_));
2467 _assert(manager_->GetArchives(fetcher_, &list, &records));
2468 _assert(!_error->PendingError());
2472 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2474 _assert(list.ReadMainList());
2475 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2476 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2479 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2484 bool failed = false;
2485 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2486 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2489 std::string uri = (*item)->DescURI();
2490 std::string error = (*item)->ErrorText;
2492 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2495 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2496 withObject:[NSArray arrayWithObjects:
2497 [NSString stringWithUTF8String:error.c_str()],
2509 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2511 if (_error->PendingError()) {
2516 if (result == pkgPackageManager::Failed) {
2521 if (result != pkgPackageManager::Completed) {
2526 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2528 _assert(list.ReadMainList());
2529 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2530 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2533 if (![before isEqualToArray:after])
2538 _assert(pkgDistUpgrade(cache_));
2542 [self updateWithStatus:status_];
2545 - (void) updateWithStatus:(Status &)status {
2547 _assert(list.ReadMainList());
2550 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2551 _assert(!_error->PendingError());
2553 pkgAcquire fetcher(&status);
2554 _assert(list.GetIndexes(&fetcher));
2556 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2557 bool failed = false;
2558 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2559 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2560 (*item)->Finished();
2564 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2565 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2566 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2569 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2574 - (void) setDelegate:(id)delegate {
2575 delegate_ = delegate;
2576 status_.setDelegate(delegate);
2577 progress_.setDelegate(delegate);
2580 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2581 pkgIndexFile *index(NULL);
2582 list_->FindIndex(file, index);
2583 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2589 /* PopUp Windows {{{ */
2590 @interface PopUpView : UIView {
2591 _transient id delegate_;
2592 UITransitionView *transition_;
2597 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2601 @implementation PopUpView
2604 [transition_ setDelegate:nil];
2605 [transition_ release];
2611 [transition_ transition:UITransitionPushFromTop toView:nil];
2614 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2615 if (from != nil && to == nil)
2616 [self removeFromSuperview];
2619 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2620 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2621 delegate_ = delegate;
2623 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2624 [self addSubview:transition_];
2626 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2628 [view addSubview:self];
2630 [transition_ setDelegate:self];
2632 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2633 [transition_ transition:UITransitionNone toView:blank];
2634 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2641 /* Mail Composition {{{ */
2642 @interface MailToView : PopUpView {
2643 MailComposeController *controller_;
2646 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2650 @implementation MailToView
2653 [controller_ release];
2657 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2661 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2662 NSLog(@"did:%@", delivery);
2663 // [UIApp setStatusBarShowsProgress:NO];
2664 if ([controller error]){
2665 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2666 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2667 [mailAlertSheet setBodyText:[controller error]];
2668 [mailAlertSheet popupAlertAnimated:YES];
2672 - (void) showError {
2673 NSLog(@"%@", [controller_ error]);
2674 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2675 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2676 [mailAlertSheet setBodyText:[controller_ error]];
2677 [mailAlertSheet popupAlertAnimated:YES];
2680 - (void) deliverMessage { _pooled
2684 if (![controller_ deliverMessage])
2685 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2688 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2689 if ([controller_ needsDelivery])
2690 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2695 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2696 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2697 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2698 [controller_ setDelegate:self];
2699 [controller_ initializeUI];
2700 [controller_ setupForURL:url];
2702 UIView *view([controller_ view]);
2703 [overlay_ addSubview:view];
2709 /* Confirmation View {{{ */
2710 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2711 if (!iterator.end())
2712 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2713 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2715 pkgCache::PkgIterator package(dep.TargetPkg());
2718 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2725 @protocol ConfirmationViewDelegate
2730 @interface ConfirmationView : BrowserView {
2731 _transient Database *database_;
2732 UIActionSheet *essential_;
2739 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2743 @implementation ConfirmationView
2750 if (essential_ != nil)
2751 [essential_ release];
2757 [book_ popFromSuperviewAnimated:YES];
2760 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2761 NSString *context([sheet context]);
2763 if ([context isEqualToString:@"remove"]) {
2771 [delegate_ confirm];
2778 } else if ([context isEqualToString:@"unable"]) {
2782 [super alertSheet:sheet buttonClicked:button];
2785 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2786 [window setValue:changes_ forKey:@"changes"];
2787 [window setValue:issues_ forKey:@"issues"];
2788 [window setValue:sizes_ forKey:@"sizes"];
2789 [super webView:sender didClearWindowObject:window forFrame:frame];
2792 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2793 if ((self = [super initWithBook:book]) != nil) {
2794 database_ = database;
2796 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2797 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2798 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2799 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2800 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2804 pkgDepCache::Policy *policy([database_ policy]);
2806 pkgCacheFile &cache([database_ cache]);
2807 NSArray *packages = [database_ packages];
2808 for (size_t i(0), e = [packages count]; i != e; ++i) {
2809 Package *package = [packages objectAtIndex:i];
2810 pkgCache::PkgIterator iterator = [package iterator];
2811 pkgDepCache::StateCache &state(cache[iterator]);
2813 NSString *name([package name]);
2815 if (state.NewInstall())
2816 [installing addObject:name];
2817 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2818 [reinstalling addObject:name];
2819 else if (state.Upgrade())
2820 [upgrading addObject:name];
2821 else if (state.Downgrade())
2822 [downgrading addObject:name];
2823 else if (state.Delete()) {
2824 if ([package essential])
2826 [removing addObject:name];
2829 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2830 substrate_ |= DepSubstrate(iterator.CurrentVer());
2835 else if (Advanced_ || true) {
2836 essential_ = [[UIActionSheet alloc]
2837 initWithTitle:@"Removing Essentials"
2838 buttons:[NSArray arrayWithObjects:
2839 @"Cancel Operation (Safe)",
2840 @"Force Removal (Unsafe)",
2842 defaultButtonIndex:0
2848 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2850 [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."];
2852 essential_ = [[UIActionSheet alloc]
2853 initWithTitle:@"Unable to Comply"
2854 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2855 defaultButtonIndex:0
2860 [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."];
2863 changes_ = [[NSArray alloc] initWithObjects:
2871 issues_ = [database_ issues];
2873 issues_ = [issues_ retain];
2875 sizes_ = [[NSArray alloc] initWithObjects:
2876 SizeString([database_ fetcher].FetchNeeded()),
2877 SizeString([database_ fetcher].PartialPresent()),
2878 SizeString([database_ cache]->UsrSize()),
2881 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2885 - (NSString *) backButtonTitle {
2889 - (NSString *) leftButtonTitle {
2893 - (id) _rightButtonTitle {
2894 #if AlwaysReload || IgnoreInstall
2897 return issues_ == nil ? @"Confirm" : nil;
2901 - (void) _leftButtonClicked {
2906 - (void) _rightButtonClicked {
2908 return [super _rightButtonClicked];
2910 if (essential_ != nil)
2911 [essential_ popupAlertAnimated:YES];
2915 [delegate_ confirm];
2923 /* Progress Data {{{ */
2924 @interface ProgressData : NSObject {
2930 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2937 @implementation ProgressData
2939 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2940 if ((self = [super init]) != nil) {
2941 selector_ = selector;
2961 /* Progress View {{{ */
2962 @interface ProgressView : UIView <
2963 ConfigurationDelegate,
2966 _transient Database *database_;
2968 UIView *background_;
2969 UITransitionView *transition_;
2971 UINavigationBar *navbar_;
2972 UIProgressBar *progress_;
2973 UITextView *output_;
2974 UITextLabel *status_;
2975 UIPushButton *close_;
2978 SHA1SumValue springlist_;
2979 SHA1SumValue notifyconf_;
2980 SHA1SumValue sandplate_;
2982 NSTimeInterval last_;
2985 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2987 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2988 - (void) setContentView:(UIView *)view;
2991 - (void) _retachThread;
2992 - (void) _detachNewThreadData:(ProgressData *)data;
2993 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2999 @protocol ProgressViewDelegate
3000 - (void) progressViewIsComplete:(ProgressView *)sender;
3003 @implementation ProgressView
3006 [transition_ setDelegate:nil];
3007 [navbar_ setDelegate:nil];
3010 if (background_ != nil)
3011 [background_ release];
3012 [transition_ release];
3015 [progress_ release];
3022 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3023 if (bootstrap_ && from == overlay_ && to == view_)
3027 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3028 if ((self = [super initWithFrame:frame]) != nil) {
3029 database_ = database;
3030 delegate_ = delegate;
3032 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3033 [transition_ setDelegate:self];
3035 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3038 [overlay_ setBackgroundColor:[UIColor blackColor]];
3040 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3041 [background_ setBackgroundColor:[UIColor blackColor]];
3042 [self addSubview:background_];
3045 [self addSubview:transition_];
3047 CGSize navsize = [UINavigationBar defaultSize];
3048 CGRect navrect = {{0, 0}, navsize};
3050 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3051 [overlay_ addSubview:navbar_];
3053 [navbar_ setBarStyle:1];
3054 [navbar_ setDelegate:self];
3056 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3057 [navbar_ pushNavigationItem:navitem];
3059 CGRect bounds = [overlay_ bounds];
3060 CGSize prgsize = [UIProgressBar defaultSize];
3063 (bounds.size.width - prgsize.width) / 2,
3064 bounds.size.height - prgsize.height - 20
3067 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3068 [progress_ setStyle:0];
3070 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3072 bounds.size.height - prgsize.height - 50,
3073 bounds.size.width - 20,
3077 [status_ setColor:[UIColor whiteColor]];
3078 [status_ setBackgroundColor:[UIColor clearColor]];
3080 [status_ setCentersHorizontally:YES];
3081 //[status_ setFont:font];
3083 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3085 navrect.size.height + 20,
3086 bounds.size.width - 20,
3087 bounds.size.height - navsize.height - 62 - navrect.size.height
3090 //[output_ setTextFont:@"Courier New"];
3091 [output_ setTextSize:12];
3093 [output_ setTextColor:[UIColor whiteColor]];
3094 [output_ setBackgroundColor:[UIColor clearColor]];
3096 [output_ setMarginTop:0];
3097 [output_ setAllowsRubberBanding:YES];
3098 [output_ setEditable:NO];
3100 [overlay_ addSubview:output_];
3102 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3104 bounds.size.height - prgsize.height - 50,
3105 bounds.size.width - 20,
3109 [close_ setAutosizesToFit:NO];
3110 [close_ setDrawsShadow:YES];
3111 [close_ setStretchBackground:YES];
3112 [close_ setEnabled:YES];
3114 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3115 [close_ setTitleFont:bold];
3117 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3118 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3119 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3123 - (void) setContentView:(UIView *)view {
3124 view_ = [view retain];
3127 - (void) resetView {
3128 [transition_ transition:6 toView:view_];
3131 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3132 NSString *context([sheet context]);
3134 if ([context isEqualToString:@"error"])
3136 else if ([context isEqualToString:@"conffile"]) {
3137 FILE *input = [database_ input];
3141 fprintf(input, "N\n");
3145 fprintf(input, "Y\n");
3156 - (void) closeButtonPushed {
3165 [delegate_ suspendWithAnimation:YES];
3169 system("launchctl stop com.apple.SpringBoard");
3173 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3182 - (void) _retachThread {
3183 UINavigationItem *item = [navbar_ topItem];
3184 [item setTitle:@"Complete"];
3186 [overlay_ addSubview:close_];
3187 [progress_ removeFromSuperview];
3188 [status_ removeFromSuperview];
3190 [delegate_ progressViewIsComplete:self];
3193 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3194 MMap mmap(file, MMap::ReadOnly);
3196 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3197 if (!(sandplate_ == sha1.Result()))
3202 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3203 MMap mmap(file, MMap::ReadOnly);
3205 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3206 if (!(notifyconf_ == sha1.Result()))
3211 FileFd file(SpringBoard_, FileFd::ReadOnly);
3212 MMap mmap(file, MMap::ReadOnly);
3214 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3215 if (!(springlist_ == sha1.Result()))
3220 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3221 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3222 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3223 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3224 case 4: [close_ setTitle:@"Reboot Device"]; break;
3227 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3229 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3230 [cache autorelease];
3232 NSFileManager *manager = [NSFileManager defaultManager];
3233 NSError *error = nil;
3235 id system = [cache objectForKey:@"System"];
3240 if (stat(Cache_, &info) == -1)
3243 [system removeAllObjects];
3245 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3246 for (NSString *app in apps)
3247 if ([app hasSuffix:@".app"]) {
3248 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3249 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3250 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3252 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3253 [info setObject:path forKey:@"Path"];
3254 [info setObject:@"System" forKey:@"ApplicationType"];
3255 [system addInfoDictionary:info];
3261 [cache writeToFile:@Cache_ atomically:YES];
3263 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3265 if (chmod(Cache_, info.st_mode) == -1)
3269 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3272 notify_post("com.apple.mobile.application_installed");
3274 [delegate_ setStatusBarShowsProgress:NO];
3277 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3278 [[data target] performSelector:[data selector] withObject:[data object]];
3281 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3284 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3285 UINavigationItem *item = [navbar_ topItem];
3286 [item setTitle:title];
3288 [status_ setText:nil];
3289 [output_ setText:@""];
3290 [progress_ setProgress:0];
3293 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3295 [close_ removeFromSuperview];
3296 [overlay_ addSubview:progress_];
3297 [overlay_ addSubview:status_];
3299 [delegate_ setStatusBarShowsProgress:YES];
3303 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3304 MMap mmap(file, MMap::ReadOnly);
3306 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3307 sandplate_ = sha1.Result();
3311 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3312 MMap mmap(file, MMap::ReadOnly);
3314 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3315 notifyconf_ = sha1.Result();
3319 FileFd file(SpringBoard_, FileFd::ReadOnly);
3320 MMap mmap(file, MMap::ReadOnly);
3322 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3323 springlist_ = sha1.Result();
3326 [transition_ transition:6 toView:overlay_];
3329 detachNewThreadSelector:@selector(_detachNewThreadData:)
3331 withObject:[[ProgressData alloc]
3332 initWithSelector:selector
3339 - (void) repairWithSelector:(SEL)selector {
3341 detachNewThreadSelector:selector
3348 - (void) setConfigurationData:(NSString *)data {
3350 performSelectorOnMainThread:@selector(_setConfigurationData:)
3356 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3357 Package *package = id == nil ? nil : [database_ packageWithName:id];
3359 UIActionSheet *sheet = [[[UIActionSheet alloc]
3360 initWithTitle:(package == nil ? id : [package name])
3361 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3362 defaultButtonIndex:0
3367 [sheet setBodyText:error];
3368 [sheet popupAlertAnimated:YES];
3371 - (void) setProgressTitle:(NSString *)title {
3373 performSelectorOnMainThread:@selector(_setProgressTitle:)
3379 - (void) setProgressPercent:(float)percent {
3381 performSelectorOnMainThread:@selector(_setProgressPercent:)
3382 withObject:[NSNumber numberWithFloat:percent]
3387 - (void) startProgress {
3388 last_ = [NSDate timeIntervalSinceReferenceDate];
3391 - (void) addProgressOutput:(NSString *)output {
3393 performSelectorOnMainThread:@selector(_addProgressOutput:)
3399 - (bool) isCancelling:(size_t)received {
3401 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3402 if (received_ != received) {
3403 received_ = received;
3405 } else if (now - last_ > 30)
3412 - (void) _setConfigurationData:(NSString *)data {
3413 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3415 _assert(conffile_r(data));
3417 NSString *ofile = conffile_r[1];
3418 //NSString *nfile = conffile_r[2];
3420 UIActionSheet *sheet = [[[UIActionSheet alloc]
3421 initWithTitle:@"Configuration Upgrade"
3422 buttons:[NSArray arrayWithObjects:
3423 @"Keep My Old Copy",
3424 @"Accept The New Copy",
3425 // XXX: @"See What Changed",
3427 defaultButtonIndex:0
3432 [sheet setBodyText:[NSString stringWithFormat:
3433 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3436 [sheet popupAlertAnimated:YES];
3439 - (void) _setProgressTitle:(NSString *)title {
3440 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3441 for (size_t i(0), e([words count]); i != e; ++i) {
3442 NSString *word([words objectAtIndex:i]);
3443 if (Package *package = [database_ packageWithName:word])
3444 [words replaceObjectAtIndex:i withObject:[package name]];
3447 [status_ setText:[words componentsJoinedByString:@" "]];
3450 - (void) _setProgressPercent:(NSNumber *)percent {
3451 [progress_ setProgress:[percent floatValue]];
3454 - (void) _addProgressOutput:(NSString *)output {
3455 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3456 CGSize size = [output_ contentSize];
3457 CGRect rect = {{0, size.height}, {size.width, 0}};
3458 [output_ scrollRectToVisible:rect animated:YES];
3461 - (BOOL) isRunning {
3468 /* Package Cell {{{ */
3469 @interface PackageCell : UISimpleTableCell {
3472 NSString *description_;
3476 UITextLabel *status_;
3480 - (PackageCell *) init;
3481 - (void) setPackage:(Package *)package;
3483 + (int) heightForPackage:(Package *)package;
3487 @implementation PackageCell
3489 - (void) clearPackage {
3500 if (description_ != nil) {
3501 [description_ release];
3505 if (source_ != nil) {
3510 if (badge_ != nil) {
3517 [self clearPackage];
3524 - (PackageCell *) init {
3525 if ((self = [super init]) != nil) {
3527 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3528 [status_ setBackgroundColor:[UIColor clearColor]];
3529 [status_ setFont:small];
3534 - (void) setPackage:(Package *)package {
3535 [self clearPackage];
3537 Source *source = [package source];
3538 NSString *section = [package simpleSection];
3540 icon_ = [[package icon] retain];
3542 name_ = [[package name] retain];
3543 description_ = [[package tagline] retain];
3545 NSString *label = nil;
3546 bool trusted = false;
3548 if (source != nil) {
3549 label = [source label];
3550 trusted = [source trusted];
3551 } else if ([[package id] isEqualToString:@"firmware"])
3554 label = @"Unknown/Local";
3556 NSString *from = [NSString stringWithFormat:@"from %@", label];
3558 if (section != nil && ![section isEqualToString:label])
3559 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3561 source_ = [from retain];
3563 if (NSString *purpose = [package primaryPurpose])
3564 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3565 badge_ = [badge_ retain];
3568 if (NSString *mode = [package mode]) {
3569 [badge_ setImage:[UIImage applicationImageNamed:
3570 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3573 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3574 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3575 } else if ([package half]) {
3576 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3577 [status_ setText:@"Package Damaged"];
3578 [status_ setColor:[UIColor redColor]];
3580 [badge_ setImage:nil];
3581 [status_ setText:nil];
3586 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3589 rect.size = [icon_ size];
3591 rect.size.width /= 2;
3592 rect.size.height /= 2;
3594 rect.origin.x = 25 - rect.size.width / 2;
3595 rect.origin.y = 25 - rect.size.height / 2;
3597 [icon_ drawInRect:rect];
3600 if (badge_ != nil) {
3601 CGSize size = [badge_ size];
3603 [badge_ drawAtPoint:CGPointMake(
3604 36 - size.width / 2,
3605 36 - size.height / 2
3614 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3615 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3619 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3621 [super drawContentInRect:rect selected:selected];
3624 + (int) heightForPackage:(Package *)package {
3625 NSString *tagline([package tagline]);
3626 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3628 if ([package hasMode] || [package half])
3637 /* Section Cell {{{ */
3638 @interface SectionCell : UISimpleTableCell {
3643 _UISwitchSlider *switch_;
3648 - (void) setSection:(Section *)section editing:(BOOL)editing;
3652 @implementation SectionCell
3654 - (void) clearSection {
3655 if (section_ != nil) {
3665 if (count_ != nil) {
3672 [self clearSection];
3679 if ((self = [super init]) != nil) {
3680 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3682 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3683 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3687 - (void) onSwitch:(id)sender {
3688 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3689 if (metadata == nil) {
3690 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3691 [Sections_ setObject:metadata forKey:section_];
3695 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3698 - (void) setSection:(Section *)section editing:(BOOL)editing {
3699 if (editing != editing_) {
3701 [switch_ removeFromSuperview];
3703 [self addSubview:switch_];
3707 [self clearSection];
3709 if (section == nil) {
3710 name_ = [@"All Packages" retain];
3713 section_ = [section name];
3714 if (section_ != nil)
3715 section_ = [section_ retain];
3716 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3717 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3720 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3724 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3725 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3732 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3734 CGSize size = [count_ sizeWithFont:Font14_];
3738 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3740 [super drawContentInRect:rect selected:selected];
3746 /* File Table {{{ */
3747 @interface FileTable : RVPage {
3748 _transient Database *database_;
3751 NSMutableArray *files_;
3755 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3756 - (void) setPackage:(Package *)package;
3760 @implementation FileTable
3763 if (package_ != nil)
3772 - (int) numberOfRowsInTable:(UITable *)table {
3773 return files_ == nil ? 0 : [files_ count];
3776 - (float) table:(UITable *)table heightForRow:(int)row {
3780 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3781 if (reusing == nil) {
3782 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3783 UIFont *font = [UIFont systemFontOfSize:16];
3784 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3786 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3790 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3794 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3795 if ((self = [super initWithBook:book]) != nil) {
3796 database_ = database;
3798 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3800 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3801 [self addSubview:list_];
3803 UITableColumn *column = [[[UITableColumn alloc]
3804 initWithTitle:@"Name"
3806 width:[self frame].size.width
3809 [list_ setDataSource:self];
3810 [list_ setSeparatorStyle:1];
3811 [list_ addTableColumn:column];
3812 [list_ setDelegate:self];
3813 [list_ setReusesTableCells:YES];
3817 - (void) setPackage:(Package *)package {
3818 if (package_ != nil) {
3819 [package_ autorelease];
3828 [files_ removeAllObjects];
3830 if (package != nil) {
3831 package_ = [package retain];
3832 name_ = [[package id] retain];
3834 if (NSArray *files = [package files])
3835 [files_ addObjectsFromArray:files];
3837 if ([files_ count] != 0) {
3838 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3839 [files_ removeObjectAtIndex:0];
3840 [files_ sortUsingSelector:@selector(compareByPath:)];
3842 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3843 [stack addObject:@"/"];
3845 for (int i(0), e([files_ count]); i != e; ++i) {
3846 NSString *file = [files_ objectAtIndex:i];
3847 while (![file hasPrefix:[stack lastObject]])
3848 [stack removeLastObject];
3849 NSString *directory = [stack lastObject];
3850 [stack addObject:[file stringByAppendingString:@"/"]];
3851 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3852 ([stack count] - 2) * 3, "",
3853 [file substringFromIndex:[directory length]]
3862 - (void) resetViewAnimated:(BOOL)animated {
3863 [list_ resetViewAnimated:animated];
3866 - (void) reloadData {
3867 [self setPackage:[database_ packageWithName:name_]];
3868 [self reloadButtons];
3871 - (NSString *) title {
3872 return @"Installed Files";
3875 - (NSString *) backButtonTitle {
3881 /* Package View {{{ */
3882 @interface PackageView : BrowserView {
3883 _transient Database *database_;
3886 NSMutableArray *buttons_;
3889 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3890 - (void) setPackage:(Package *)package;
3894 @implementation PackageView
3897 if (package_ != nil)
3905 - (void) _clickButtonWithName:(NSString *)name {
3906 if ([name isEqualToString:@"Install"])
3907 [delegate_ installPackage:package_];
3908 else if ([name isEqualToString:@"Reinstall"])
3909 [delegate_ installPackage:package_];
3910 else if ([name isEqualToString:@"Remove"])
3911 [delegate_ removePackage:package_];
3912 else if ([name isEqualToString:@"Upgrade"])
3913 [delegate_ installPackage:package_];
3914 else _assert(false);
3917 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3918 NSString *context([sheet context]);
3920 if ([context isEqualToString:@"modify"]) {
3921 int count = [buttons_ count];
3922 _assert(count != 0);
3923 _assert(button <= count + 1);
3925 if (count != button - 1)
3926 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3930 [super alertSheet:sheet buttonClicked:button];
3933 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3934 return [super webView:sender didFinishLoadForFrame:frame];
3937 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3938 [window setValue:package_ forKey:@"package"];
3939 [super webView:sender didClearWindowObject:window forFrame:frame];
3943 - (void) _rightButtonClicked {
3944 /*[super _rightButtonClicked];
3947 int count = [buttons_ count];
3948 _assert(count != 0);
3951 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3953 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3954 [buttons addObjectsFromArray:buttons_];
3955 [buttons addObject:@"Cancel"];
3957 [delegate_ slideUp:[[[UIActionSheet alloc]
3960 defaultButtonIndex:2
3968 - (id) _rightButtonTitle {
3969 int count = [buttons_ count];
3970 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3973 - (NSString *) backButtonTitle {
3977 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3978 if ((self = [super initWithBook:book]) != nil) {
3979 database_ = database;
3980 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3984 - (void) setPackage:(Package *)package {
3985 if (package_ != nil) {
3986 [package_ autorelease];
3995 [buttons_ removeAllObjects];
3997 if (package != nil) {
3998 package_ = [package retain];
3999 name_ = [[package id] retain];
4001 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4003 if ([package_ source] == nil);
4004 else if ([package_ upgradableAndEssential:NO])
4005 [buttons_ addObject:@"Upgrade"];
4006 else if ([package_ installed] == nil)
4007 [buttons_ addObject:@"Install"];
4009 [buttons_ addObject:@"Reinstall"];
4010 if ([package_ installed] != nil)
4011 [buttons_ addObject:@"Remove"];
4019 - (void) reloadData {
4020 [self setPackage:[database_ packageWithName:name_]];
4021 [self reloadButtons];
4026 /* Package Table {{{ */
4027 @interface PackageTable : RVPage {
4028 _transient Database *database_;
4030 NSMutableArray *packages_;
4031 NSMutableArray *sections_;
4032 UISectionList *list_;
4035 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4037 - (void) setDelegate:(id)delegate;
4039 - (void) reloadData;
4040 - (void) resetCursor;
4042 - (UISectionList *) list;
4044 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4048 @implementation PackageTable
4051 [list_ setDataSource:nil];
4054 [packages_ release];
4055 [sections_ release];
4060 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4061 return [sections_ count];
4064 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4065 return [[sections_ objectAtIndex:section] name];
4068 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4069 return [[sections_ objectAtIndex:section] row];
4072 - (int) numberOfRowsInTable:(UITable *)table {
4073 return [packages_ count];
4076 - (float) table:(UITable *)table heightForRow:(int)row {
4077 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4080 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4082 reusing = [[[PackageCell alloc] init] autorelease];
4083 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4087 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4091 - (void) tableRowSelected:(NSNotification *)notification {
4092 int row = [[notification object] selectedRow];
4096 Package *package = [packages_ objectAtIndex:row];
4097 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4098 [view setDelegate:delegate_];
4099 [view setPackage:package];
4100 [book_ pushPage:view];
4103 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4104 if ((self = [super initWithBook:book]) != nil) {
4105 database_ = database;
4106 title_ = [title retain];
4108 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4109 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4111 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4112 [list_ setDataSource:self];
4114 UITableColumn *column = [[[UITableColumn alloc]
4115 initWithTitle:@"Name"
4117 width:[self frame].size.width
4120 UITable *table = [list_ table];
4121 [table setSeparatorStyle:1];
4122 [table addTableColumn:column];
4123 [table setDelegate:self];
4124 [table setReusesTableCells:YES];
4126 [self addSubview:list_];
4128 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4129 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4133 - (void) setDelegate:(id)delegate {
4134 delegate_ = delegate;
4137 - (bool) hasPackage:(Package *)package {
4141 - (void) reloadData {
4142 NSArray *packages = [database_ packages];
4144 [packages_ removeAllObjects];
4145 [sections_ removeAllObjects];
4147 for (size_t i(0); i != [packages count]; ++i) {
4148 Package *package([packages objectAtIndex:i]);
4149 if ([self hasPackage:package])
4150 [packages_ addObject:package];
4153 Section *section = nil;
4155 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4156 Package *package = [packages_ objectAtIndex:offset];
4157 NSString *name = [package index];
4159 if (section == nil || ![[section name] isEqualToString:name]) {
4160 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4161 [sections_ addObject:section];
4164 [section addToCount];
4170 - (NSString *) title {
4174 - (void) resetViewAnimated:(BOOL)animated {
4175 [list_ resetViewAnimated:animated];
4178 - (void) resetCursor {
4179 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4182 - (UISectionList *) list {
4186 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4187 [list_ setShouldHideHeaderInShortLists:hide];
4192 /* Filtered Package Table {{{ */
4193 @interface FilteredPackageTable : PackageTable {
4198 - (void) setObject:(id)object;
4200 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4204 @implementation FilteredPackageTable
4212 - (void) setObject:(id)object {
4218 object_ = [object retain];
4221 - (bool) hasPackage:(Package *)package {
4222 return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
4225 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4226 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4228 object_ = object == nil ? nil : [object retain];
4237 /* Add Source View {{{ */
4238 @interface AddSourceView : RVPage {
4239 _transient Database *database_;
4242 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4246 @implementation AddSourceView
4248 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4249 if ((self = [super initWithBook:book]) != nil) {
4250 database_ = database;
4256 /* Source Cell {{{ */
4257 @interface SourceCell : UITableCell {
4260 NSString *description_;
4266 - (SourceCell *) initWithSource:(Source *)source;
4270 @implementation SourceCell
4275 [description_ release];
4280 - (SourceCell *) initWithSource:(Source *)source {
4281 if ((self = [super init]) != nil) {
4283 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4285 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4286 icon_ = [icon_ retain];
4288 origin_ = [[source name] retain];
4289 label_ = [[source uri] retain];
4290 description_ = [[source description] retain];
4294 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4296 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4303 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4307 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4311 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4313 [super drawContentInRect:rect selected:selected];
4318 /* Source Table {{{ */
4319 @interface SourceTable : RVPage {
4320 _transient Database *database_;
4321 UISectionList *list_;
4322 NSMutableArray *sources_;
4323 UIActionSheet *alert_;
4327 UIProgressHUD *hud_;
4330 //NSURLConnection *installer_;
4331 NSURLConnection *trivial_bz2_;
4332 NSURLConnection *trivial_gz_;
4333 //NSURLConnection *automatic_;
4338 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4342 @implementation SourceTable
4344 - (void) _deallocConnection:(NSURLConnection *)connection {
4345 if (connection != nil) {
4346 [connection cancel];
4347 //[connection setDelegate:nil];
4348 [connection release];
4353 [[list_ table] setDelegate:nil];
4354 [list_ setDataSource:nil];
4363 //[self _deallocConnection:installer_];
4364 [self _deallocConnection:trivial_gz_];
4365 [self _deallocConnection:trivial_bz2_];
4366 //[self _deallocConnection:automatic_];
4373 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4374 return offset_ == 0 ? 1 : 2;
4377 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4378 switch (section + (offset_ == 0 ? 1 : 0)) {
4379 case 0: return @"Entered by User";
4380 case 1: return @"Installed by Packages";
4388 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4389 switch (section + (offset_ == 0 ? 1 : 0)) {
4391 case 1: return offset_;
4399 - (int) numberOfRowsInTable:(UITable *)table {
4400 return [sources_ count];
4403 - (float) table:(UITable *)table heightForRow:(int)row {
4404 Source *source = [sources_ objectAtIndex:row];
4405 return [source description] == nil ? 56 : 73;
4408 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4409 Source *source = [sources_ objectAtIndex:row];
4410 // XXX: weird warning, stupid selectors ;P
4411 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4414 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4418 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4422 - (void) tableRowSelected:(NSNotification*)notification {
4423 UITable *table([list_ table]);
4424 int row([table selectedRow]);
4428 Source *source = [sources_ objectAtIndex:row];
4430 PackageTable *packages = [[[FilteredPackageTable alloc]
4433 title:[source label]
4434 filter:@selector(isVisibleInSource:)
4438 [packages setDelegate:delegate_];
4440 [book_ pushPage:packages];
4443 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4444 Source *source = [sources_ objectAtIndex:row];
4445 return [source record] != nil;
4448 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4449 [[list_ table] setDeleteConfirmationRow:row];
4452 - (void) table:(UITable *)table deleteRow:(int)row {
4453 Source *source = [sources_ objectAtIndex:row];
4454 [Sources_ removeObjectForKey:[source key]];
4455 [delegate_ syncData];
4458 - (void) _endConnection:(NSURLConnection *)connection {
4459 NSURLConnection **field = NULL;
4460 if (connection == trivial_bz2_)
4461 field = &trivial_bz2_;
4462 else if (connection == trivial_gz_)
4463 field = &trivial_gz_;
4464 _assert(field != NULL);
4465 [connection release];
4469 trivial_bz2_ == nil &&
4472 [delegate_ setStatusBarShowsProgress:NO];
4475 [hud_ removeFromSuperview];
4480 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4483 @"./", @"Distribution",
4484 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4486 [delegate_ syncData];
4487 } else if (error_ != nil) {
4488 UIActionSheet *sheet = [[[UIActionSheet alloc]
4489 initWithTitle:@"Verification Error"
4490 buttons:[NSArray arrayWithObjects:@"OK", nil]
4491 defaultButtonIndex:0
4496 [sheet setBodyText:[error_ localizedDescription]];
4497 [sheet popupAlertAnimated:YES];
4499 UIActionSheet *sheet = [[[UIActionSheet alloc]
4500 initWithTitle:@"Did not Find Repository"
4501 buttons:[NSArray arrayWithObjects:@"OK", nil]
4502 defaultButtonIndex:0
4507 [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."];
4508 [sheet popupAlertAnimated:YES];
4514 if (error_ != nil) {
4521 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4522 switch ([response statusCode]) {
4528 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4529 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4531 error_ = [error retain];
4532 [self _endConnection:connection];
4535 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4536 [self _endConnection:connection];
4539 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4540 NSMutableURLRequest *request = [NSMutableURLRequest
4541 requestWithURL:[NSURL URLWithString:href]
4542 cachePolicy:NSURLRequestUseProtocolCachePolicy
4543 timeoutInterval:20.0
4546 [request setHTTPMethod:method];
4548 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4551 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4552 NSString *context([sheet context]);
4554 if ([context isEqualToString:@"source"]) {
4557 NSString *href = [[sheet textField] text];
4559 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4561 if (![href hasSuffix:@"/"])
4562 href_ = [href stringByAppendingString:@"/"];
4565 href_ = [href_ retain];
4567 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4568 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4569 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4573 hud_ = [delegate_ addProgressHUD];
4574 [hud_ setText:@"Verifying URL"];
4585 } else if ([context isEqualToString:@"trivial"])
4587 else if ([context isEqualToString:@"urlerror"])
4591 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4592 if ((self = [super initWithBook:book]) != nil) {
4593 database_ = database;
4594 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4596 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4597 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4598 [list_ setShouldHideHeaderInShortLists:NO];
4600 [self addSubview:list_];
4601 [list_ setDataSource:self];
4603 UITableColumn *column = [[UITableColumn alloc]
4604 initWithTitle:@"Name"
4606 width:[self frame].size.width
4609 UITable *table = [list_ table];
4610 [table setSeparatorStyle:1];
4611 [table addTableColumn:column];
4612 [table setDelegate:self];
4616 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4617 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4621 - (void) reloadData {
4623 _assert(list.ReadMainList());
4625 [sources_ removeAllObjects];
4626 [sources_ addObjectsFromArray:[database_ sources]];
4628 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4631 int count = [sources_ count];
4632 for (offset_ = 0; offset_ != count; ++offset_) {
4633 Source *source = [sources_ objectAtIndex:offset_];
4634 if ([source record] == nil)
4641 - (void) resetViewAnimated:(BOOL)animated {
4642 [list_ resetViewAnimated:animated];
4645 - (void) _leftButtonClicked {
4646 /*[book_ pushPage:[[[AddSourceView alloc]
4651 UIActionSheet *sheet = [[[UIActionSheet alloc]
4652 initWithTitle:@"Enter Cydia/APT URL"
4653 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4654 defaultButtonIndex:0
4659 [sheet setNumberOfRows:1];
4661 [sheet addTextFieldWithValue:@"http://" label:@""];
4663 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4664 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4665 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4666 [traits setKeyboardType:UIKeyboardTypeURL];
4667 // XXX: UIReturnKeyDone
4668 [traits setReturnKeyType:UIReturnKeyNext];
4670 [sheet popupAlertAnimated:YES];
4673 - (void) _rightButtonClicked {
4674 UITable *table = [list_ table];
4675 BOOL editing = [table isRowDeletionEnabled];
4676 [table enableRowDeletion:!editing animated:YES];
4677 [book_ reloadButtonsForPage:self];
4680 - (NSString *) title {
4684 - (NSString *) leftButtonTitle {
4685 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4688 - (id) rightButtonTitle {
4689 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4692 - (UINavigationButtonStyle) rightButtonStyle {
4693 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4699 /* Installed View {{{ */
4700 @interface InstalledView : RVPage {
4701 _transient Database *database_;
4702 FilteredPackageTable *packages_;
4706 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4710 @implementation InstalledView
4713 [packages_ release];
4717 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4718 if ((self = [super initWithBook:book]) != nil) {
4719 database_ = database;
4721 packages_ = [[FilteredPackageTable alloc]
4725 filter:@selector(isInstalledAndVisible:)
4726 with:[NSNumber numberWithBool:YES]
4729 [self addSubview:packages_];
4731 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4732 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4736 - (void) resetViewAnimated:(BOOL)animated {
4737 [packages_ resetViewAnimated:animated];
4740 - (void) reloadData {
4741 [packages_ reloadData];
4744 - (void) _rightButtonClicked {
4745 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4746 [packages_ reloadData];
4748 [book_ reloadButtonsForPage:self];
4751 - (NSString *) title {
4752 return @"Installed";
4755 - (NSString *) backButtonTitle {
4759 - (id) rightButtonTitle {
4760 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4763 - (UINavigationButtonStyle) rightButtonStyle {
4764 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4767 - (void) setDelegate:(id)delegate {
4768 [super setDelegate:delegate];
4769 [packages_ setDelegate:delegate];
4776 @interface HomeView : BrowserView {
4781 @implementation HomeView
4783 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4784 NSString *context([sheet context]);
4786 if ([context isEqualToString:@"about"])
4789 [super alertSheet:sheet buttonClicked:button];
4792 - (void) _leftButtonClicked {
4793 UIActionSheet *sheet = [[[UIActionSheet alloc]
4794 initWithTitle:@"About Cydia Installer"
4795 buttons:[NSArray arrayWithObjects:@"Close", nil]
4796 defaultButtonIndex:0
4802 @"Copyright (C) 2008\n"
4803 "Jay Freeman (saurik)\n"
4804 "saurik@saurik.com\n"
4805 "http://www.saurik.com/\n"
4808 "http://www.theokorigroup.com/\n"
4810 "College of Creative Studies,\n"
4811 "University of California,\n"
4813 "http://www.ccs.ucsb.edu/"
4816 [sheet popupAlertAnimated:YES];
4819 - (NSString *) leftButtonTitle {
4825 /* Manage View {{{ */
4826 @interface ManageView : BrowserView {
4831 @implementation ManageView
4833 - (NSString *) title {
4837 - (void) _leftButtonClicked {
4838 [delegate_ askForSettings];
4841 - (NSString *) leftButtonTitle {
4846 - (id) _rightButtonTitle {
4858 #include <BrowserView.m>
4860 /* Cydia Book {{{ */
4861 @interface CYBook : RVBook <
4864 _transient Database *database_;
4865 UINavigationBar *overlay_;
4866 UINavigationBar *underlay_;
4867 UIProgressIndicator *indicator_;
4868 UITextLabel *prompt_;
4869 UIProgressBar *progress_;
4870 UINavigationButton *cancel_;
4873 NSTimeInterval last_;
4876 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4882 @implementation CYBook
4886 [indicator_ release];
4888 [progress_ release];
4893 - (NSString *) getTitleForPage:(RVPage *)page {
4894 return Simplify([super getTitleForPage:page]);
4902 [UIView beginAnimations:nil context:NULL];
4904 CGRect ovrframe = [overlay_ frame];
4905 ovrframe.origin.y = 0;
4906 [overlay_ setFrame:ovrframe];
4908 CGRect barframe = [navbar_ frame];
4909 barframe.origin.y += ovrframe.size.height;
4910 [navbar_ setFrame:barframe];
4912 CGRect trnframe = [transition_ frame];
4913 trnframe.origin.y += ovrframe.size.height;
4914 trnframe.size.height -= ovrframe.size.height;
4915 [transition_ setFrame:trnframe];
4917 [UIView endAnimations];
4919 [indicator_ startAnimation];
4920 [prompt_ setText:@"Updating Database"];
4921 [progress_ setProgress:0];
4924 last_ = [NSDate timeIntervalSinceReferenceDate];
4926 [overlay_ addSubview:cancel_];
4929 detachNewThreadSelector:@selector(_update)
4938 [indicator_ stopAnimation];
4940 [UIView beginAnimations:nil context:NULL];
4942 CGRect ovrframe = [overlay_ frame];
4943 ovrframe.origin.y = -ovrframe.size.height;
4944 [overlay_ setFrame:ovrframe];
4946 CGRect barframe = [navbar_ frame];
4947 barframe.origin.y -= ovrframe.size.height;
4948 [navbar_ setFrame:barframe];
4950 CGRect trnframe = [transition_ frame];
4951 trnframe.origin.y -= ovrframe.size.height;
4952 trnframe.size.height += ovrframe.size.height;
4953 [transition_ setFrame:trnframe];
4955 [UIView commitAnimations];
4957 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4960 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4961 if ((self = [super initWithFrame:frame]) != nil) {
4962 database_ = database;
4964 CGRect ovrrect = [navbar_ bounds];
4965 ovrrect.size.height = [UINavigationBar defaultSize].height;
4966 ovrrect.origin.y = -ovrrect.size.height;
4968 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4969 [self addSubview:overlay_];
4971 ovrrect.origin.y = frame.size.height;
4972 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4973 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4974 [self addSubview:underlay_];
4976 [overlay_ setBarStyle:1];
4977 [underlay_ setBarStyle:1];
4979 int barstyle = [overlay_ _barStyle:NO];
4980 bool ugly = barstyle == 0;
4982 UIProgressIndicatorStyle style = ugly ?
4983 UIProgressIndicatorStyleMediumBrown :
4984 UIProgressIndicatorStyleMediumWhite;
4986 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4987 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4988 CGRect indrect = {{indoffset, indoffset}, indsize};
4990 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4991 [indicator_ setStyle:style];
4992 [overlay_ addSubview:indicator_];
4994 CGSize prmsize = {215, indsize.height + 4};
4997 indoffset * 2 + indsize.width,
5001 unsigned(ovrrect.size.height - prmsize.height) / 2
5004 UIFont *font = [UIFont systemFontOfSize:15];
5006 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5008 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5009 [prompt_ setBackgroundColor:[UIColor clearColor]];
5010 [prompt_ setFont:font];
5012 [overlay_ addSubview:prompt_];
5014 CGSize prgsize = {75, 100};
5017 ovrrect.size.width - prgsize.width - 10,
5018 (ovrrect.size.height - prgsize.height) / 2
5021 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5022 [progress_ setStyle:0];
5023 [overlay_ addSubview:progress_];
5025 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5026 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5028 CGRect frame = [cancel_ frame];
5029 frame.size.width = 65;
5030 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5031 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5032 [cancel_ setFrame:frame];
5034 [cancel_ setBarStyle:barstyle];
5038 - (void) _onCancel {
5040 [cancel_ removeFromSuperview];
5043 - (void) _update { _pooled
5045 status.setDelegate(self);
5047 [database_ updateWithStatus:status];
5050 performSelectorOnMainThread:@selector(_update_)
5056 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5057 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5060 - (void) setProgressTitle:(NSString *)title {
5062 performSelectorOnMainThread:@selector(_setProgressTitle:)
5068 - (void) setProgressPercent:(float)percent {
5070 performSelectorOnMainThread:@selector(_setProgressPercent:)
5071 withObject:[NSNumber numberWithFloat:percent]
5076 - (void) startProgress {
5079 - (void) addProgressOutput:(NSString *)output {
5081 performSelectorOnMainThread:@selector(_addProgressOutput:)
5087 - (bool) isCancelling:(size_t)received {
5088 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5089 if (received_ != received) {
5090 received_ = received;
5092 } else if (now - last_ > 15)
5097 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5101 - (void) _setProgressTitle:(NSString *)title {
5102 [prompt_ setText:title];
5105 - (void) _setProgressPercent:(NSNumber *)percent {
5106 [progress_ setProgress:[percent floatValue]];
5109 - (void) _addProgressOutput:(NSString *)output {
5114 /* Cydia:// Protocol {{{ */
5115 @interface CydiaURLProtocol : NSURLProtocol {
5120 @implementation CydiaURLProtocol
5122 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5123 NSURL *url([request URL]);
5126 NSString *scheme([[url scheme] lowercaseString]);
5127 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5132 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5136 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5137 id<NSURLProtocolClient> client([self client]);
5139 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5141 NSData *data(UIImagePNGRepresentation(icon));
5143 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5144 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5145 [client URLProtocol:self didLoadData:data];
5146 [client URLProtocolDidFinishLoading:self];
5150 - (void) startLoading {
5151 id<NSURLProtocolClient> client([self client]);
5152 NSURLRequest *request([self request]);
5154 NSURL *url([request URL]);
5155 NSString *href([url absoluteString]);
5157 NSString *path([href substringFromIndex:8]);
5158 NSRange slash([path rangeOfString:@"/"]);
5161 if (slash.location == NSNotFound) {
5165 command = [path substringToIndex:slash.location];
5166 path = [path substringFromIndex:(slash.location + 1)];
5169 Database *database([Database sharedInstance]);
5171 if ([command isEqualToString:@"package-icon"]) {
5174 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5175 Package *package([database packageWithName:path]);
5178 UIImage *icon([package icon]);
5179 [self _returnPNGWithImage:icon forRequest:request];
5180 } else if ([command isEqualToString:@"source-icon"]) {
5183 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5184 NSString *source(Simplify(path));
5185 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5187 icon = [UIImage applicationImageNamed:@"unknown.png"];
5188 [self _returnPNGWithImage:icon forRequest:request];
5189 } else if ([command isEqualToString:@"uikit-image"]) {
5192 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5193 UIImage *icon(_UIImageWithName(path));
5194 [self _returnPNGWithImage:icon forRequest:request];
5195 } else if ([command isEqualToString:@"section-icon"]) {
5198 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5199 NSString *section(Simplify(path));
5200 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5202 icon = [UIImage applicationImageNamed:@"unknown.png"];
5203 [self _returnPNGWithImage:icon forRequest:request];
5205 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5209 - (void) stopLoading {
5215 /* Sections View {{{ */
5216 @interface SectionsView : RVPage {
5217 _transient Database *database_;
5218 NSMutableArray *sections_;
5219 NSMutableArray *filtered_;
5220 UITransitionView *transition_;
5226 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5227 - (void) reloadData;
5232 @implementation SectionsView
5235 [list_ setDataSource:nil];
5236 [list_ setDelegate:nil];
5238 [sections_ release];
5239 [filtered_ release];
5240 [transition_ release];
5242 [accessory_ release];
5246 - (int) numberOfRowsInTable:(UITable *)table {
5247 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5250 - (float) table:(UITable *)table heightForRow:(int)row {
5254 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5256 reusing = [[[SectionCell alloc] init] autorelease];
5257 [(SectionCell *)reusing setSection:(editing_ ?
5258 [sections_ objectAtIndex:row] :
5259 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5260 ) editing:editing_];
5264 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5268 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5272 - (void) tableRowSelected:(NSNotification *)notification {
5273 int row = [[notification object] selectedRow];
5284 title = @"All Packages";
5286 section = [filtered_ objectAtIndex:(row - 1)];
5287 name = [section name];
5293 title = @"(No Section)";
5297 PackageTable *table = [[[FilteredPackageTable alloc]
5301 filter:@selector(isVisiblyUninstalledInSection:)
5305 [table setDelegate:delegate_];
5307 [book_ pushPage:table];
5310 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5311 if ((self = [super initWithBook:book]) != nil) {
5312 database_ = database;
5314 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5315 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5317 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5318 [self addSubview:transition_];
5320 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5321 [transition_ transition:0 toView:list_];
5323 UITableColumn *column = [[[UITableColumn alloc]
5324 initWithTitle:@"Name"
5326 width:[self frame].size.width
5329 [list_ setDataSource:self];
5330 [list_ setSeparatorStyle:1];
5331 [list_ addTableColumn:column];
5332 [list_ setDelegate:self];
5333 [list_ setReusesTableCells:YES];
5337 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5338 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5342 - (void) reloadData {
5343 NSArray *packages = [database_ packages];
5345 [sections_ removeAllObjects];
5346 [filtered_ removeAllObjects];
5348 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5349 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5352 for (size_t i(0); i != [packages count]; ++i) {
5353 Package *package([packages objectAtIndex:i]);
5354 NSString *name([package section]);
5357 Section *section([sections objectForKey:name]);
5358 if (section == nil) {
5359 section = [[[Section alloc] initWithName:name] autorelease];
5360 [sections setObject:section forKey:name];
5364 if ([package valid] && [package installed] == nil && [package visible])
5365 [filtered addObject:package];
5369 [sections_ addObjectsFromArray:[sections allValues]];
5370 [sections_ sortUsingSelector:@selector(compareByName:)];
5373 [filtered sortUsingSelector:@selector(compareBySection:)];
5376 Section *section = nil;
5377 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5378 Package *package = [filtered objectAtIndex:offset];
5379 NSString *name = [package section];
5381 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5382 section = name == nil ?
5383 [[[Section alloc] initWithName:nil] autorelease] :
5384 [sections objectForKey:name];
5385 [filtered_ addObject:section];
5388 [section addToCount];
5396 - (void) resetView {
5398 [self _rightButtonClicked];
5401 - (void) resetViewAnimated:(BOOL)animated {
5402 [list_ resetViewAnimated:animated];
5405 - (void) _rightButtonClicked {
5406 if ((editing_ = !editing_))
5409 [delegate_ updateData];
5410 [book_ reloadTitleForPage:self];
5411 [book_ reloadButtonsForPage:self];
5414 - (NSString *) title {
5415 return editing_ ? @"Section Visibility" : @"Install by Section";
5418 - (NSString *) backButtonTitle {
5422 - (id) rightButtonTitle {
5423 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5426 - (UINavigationButtonStyle) rightButtonStyle {
5427 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5430 - (UIView *) accessoryView {
5436 /* Changes View {{{ */
5437 @interface ChangesView : RVPage {
5438 _transient Database *database_;
5439 NSMutableArray *packages_;
5440 NSMutableArray *sections_;
5441 UISectionList *list_;
5445 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5446 - (void) reloadData;
5450 @implementation ChangesView
5453 [[list_ table] setDelegate:nil];
5454 [list_ setDataSource:nil];
5456 [packages_ release];
5457 [sections_ release];
5462 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5463 return [sections_ count];
5466 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5467 return [[sections_ objectAtIndex:section] name];
5470 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5471 return [[sections_ objectAtIndex:section] row];
5474 - (int) numberOfRowsInTable:(UITable *)table {
5475 return [packages_ count];
5478 - (float) table:(UITable *)table heightForRow:(int)row {
5479 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5482 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5484 reusing = [[[PackageCell alloc] init] autorelease];
5485 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5489 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5493 - (void) tableRowSelected:(NSNotification *)notification {
5494 int row = [[notification object] selectedRow];
5497 Package *package = [packages_ objectAtIndex:row];
5498 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5499 [view setDelegate:delegate_];
5500 [view setPackage:package];
5501 [book_ pushPage:view];
5504 - (void) _leftButtonClicked {
5505 [(CYBook *)book_ update];
5506 [self reloadButtons];
5509 - (void) _rightButtonClicked {
5510 [delegate_ distUpgrade];
5513 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5514 if ((self = [super initWithBook:book]) != nil) {
5515 database_ = database;
5517 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5518 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5520 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5521 [self addSubview:list_];
5523 [list_ setShouldHideHeaderInShortLists:NO];
5524 [list_ setDataSource:self];
5525 //[list_ setSectionListStyle:1];
5527 UITableColumn *column = [[[UITableColumn alloc]
5528 initWithTitle:@"Name"
5530 width:[self frame].size.width
5533 UITable *table = [list_ table];
5534 [table setSeparatorStyle:1];
5535 [table addTableColumn:column];
5536 [table setDelegate:self];
5537 [table setReusesTableCells:YES];
5541 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5542 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5546 - (void) reloadData {
5547 NSArray *packages = [database_ packages];
5549 [packages_ removeAllObjects];
5550 [sections_ removeAllObjects];
5553 for (size_t i(0); i != [packages count]; ++i) {
5554 Package *package([packages objectAtIndex:i]);
5557 [package installed] == nil && [package valid] && [package visible] ||
5558 [package upgradableAndEssential:YES]
5560 [packages_ addObject:package];
5564 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5567 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5568 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5569 Section *section = nil;
5573 bool unseens = false;
5575 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5578 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5579 Package *package = [packages_ objectAtIndex:offset];
5581 if (![package upgradableAndEssential:YES]) {
5583 NSDate *seen = [package seen];
5585 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5588 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5589 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5590 [sections_ addObject:section];
5594 [section addToCount];
5595 } else if ([package ignored])
5596 [ignored addToCount];
5599 [upgradable addToCount];
5604 CFRelease(formatter);
5607 Section *last = [sections_ lastObject];
5608 size_t count = [last count];
5609 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5610 [sections_ removeLastObject];
5613 if ([ignored count] != 0)
5614 [sections_ insertObject:ignored atIndex:0];
5616 [sections_ insertObject:upgradable atIndex:0];
5619 [self reloadButtons];
5622 - (void) resetViewAnimated:(BOOL)animated {
5623 [list_ resetViewAnimated:animated];
5626 - (NSString *) leftButtonTitle {
5627 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5630 - (id) rightButtonTitle {
5631 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5634 - (NSString *) title {
5640 /* Search View {{{ */
5641 @protocol SearchViewDelegate
5642 - (void) showKeyboard:(BOOL)show;
5645 @interface SearchView : RVPage {
5647 UISearchField *field_;
5648 UITransitionView *transition_;
5649 FilteredPackageTable *table_;
5650 UIPreferencesTable *advanced_;
5656 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5657 - (void) reloadData;
5661 @implementation SearchView
5664 [field_ setDelegate:nil];
5666 [accessory_ release];
5668 [transition_ release];
5670 [advanced_ release];
5675 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5679 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5681 case 0: return @"Advanced Search (Coming Soon!)";
5683 default: _assert(false);
5687 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5691 default: _assert(false);
5695 - (void) _showKeyboard:(BOOL)show {
5696 CGSize keysize = [UIKeyboard defaultSize];
5697 CGRect keydown = [book_ pageBounds];
5698 CGRect keyup = keydown;
5699 keyup.size.height -= keysize.height - ButtonBarHeight_;
5701 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5703 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5704 [animation setSignificantRectFields:8];
5707 [animation setStartFrame:keydown];
5708 [animation setEndFrame:keyup];
5710 [animation setStartFrame:keyup];
5711 [animation setEndFrame:keydown];
5714 UIAnimator *animator = [UIAnimator sharedAnimator];
5717 addAnimations:[NSArray arrayWithObjects:animation, nil]
5718 withDuration:(KeyboardTime_ - delay)
5723 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5725 [delegate_ showKeyboard:show];
5728 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5729 [self _showKeyboard:YES];
5732 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5733 [self _showKeyboard:NO];
5736 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5738 NSString *text([field_ text]);
5739 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5745 - (void) textFieldClearButtonPressed:(UITextField *)field {
5749 - (void) keyboardInputShouldDelete:(id)input {
5753 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5754 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5758 [field_ resignFirstResponder];
5763 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5764 if ((self = [super initWithBook:book]) != nil) {
5765 CGRect pageBounds = [book_ pageBounds];
5767 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5768 [self addSubview:transition_];
5770 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5772 [advanced_ setReusesTableCells:YES];
5773 [advanced_ setDataSource:self];
5774 [advanced_ reloadData];
5776 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5777 CGColor dimmed(space_, 0, 0, 0, 0.5);
5778 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5780 table_ = [[FilteredPackageTable alloc]
5784 filter:@selector(isUnfilteredAndSearchedForBy:)
5788 [table_ setShouldHideHeaderInShortLists:NO];
5789 [transition_ transition:0 toView:table_];
5798 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5805 [self bounds].size.width - area.origin.x - 18;
5807 area.size.height = [UISearchField defaultHeight];
5809 field_ = [[UISearchField alloc] initWithFrame:area];
5811 UIFont *font = [UIFont systemFontOfSize:16];
5812 [field_ setFont:font];
5814 [field_ setPlaceholder:@"Package Names & Descriptions"];
5815 [field_ setDelegate:self];
5817 [field_ setPaddingTop:5];
5819 UITextInputTraits *traits([field_ textInputTraits]);
5820 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5821 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5822 [traits setReturnKeyType:UIReturnKeySearch];
5824 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5826 accessory_ = [[UIView alloc] initWithFrame:accrect];
5827 [accessory_ addSubview:field_];
5829 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5830 [configure setShowPressFeedback:YES];
5831 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5832 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5833 [accessory_ addSubview:configure];*/
5835 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5836 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5842 LKAnimation *animation = [LKTransition animation];
5843 [animation setType:@"oglFlip"];
5844 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5845 [animation setFillMode:@"extended"];
5846 [animation setTransitionFlags:3];
5847 [animation setDuration:10];
5848 [animation setSpeed:0.35];
5849 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5850 [[transition_ _layer] addAnimation:animation forKey:0];
5851 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5852 flipped_ = !flipped_;
5856 - (void) configurePushed {
5857 [field_ resignFirstResponder];
5861 - (void) resetViewAnimated:(BOOL)animated {
5864 [table_ resetViewAnimated:animated];
5867 - (void) _reloadData {
5870 - (void) reloadData {
5873 [table_ setObject:[field_ text]];
5874 [table_ reloadData];
5875 [table_ resetCursor];
5878 - (UIView *) accessoryView {
5882 - (NSString *) title {
5886 - (NSString *) backButtonTitle {
5890 - (void) setDelegate:(id)delegate {
5891 [table_ setDelegate:delegate];
5892 [super setDelegate:delegate];
5898 @interface SettingsView : RVPage {
5899 _transient Database *database_;
5902 UIPreferencesTable *table_;
5903 _UISwitchSlider *subscribedSwitch_;
5904 _UISwitchSlider *ignoredSwitch_;
5905 UIPreferencesControlTableCell *subscribedCell_;
5906 UIPreferencesControlTableCell *ignoredCell_;
5909 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5913 @implementation SettingsView
5916 [table_ setDataSource:nil];
5919 if (package_ != nil)
5922 [subscribedSwitch_ release];
5923 [ignoredSwitch_ release];
5924 [subscribedCell_ release];
5925 [ignoredCell_ release];
5929 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5930 if (package_ == nil)
5936 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5937 if (package_ == nil)
5944 default: _assert(false);
5950 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5951 if (package_ == nil)
5958 default: _assert(false);
5964 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5965 if (package_ == nil)
5972 default: _assert(false);
5978 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5979 if (package_ == nil)
5982 _UISwitchSlider *slider([cell control]);
5983 BOOL value([slider value] != 0);
5984 NSMutableDictionary *metadata([package_ metadata]);
5987 if (NSNumber *number = [metadata objectForKey:key])
5988 before = [number boolValue];
5992 if (value != before) {
5993 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5995 [delegate_ updateData];
5999 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6000 [self onSomething:cell withKey:@"IsSubscribed"];
6003 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6004 [self onSomething:cell withKey:@"IsIgnored"];
6007 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6008 if (package_ == nil)
6012 case 0: switch (row) {
6014 return subscribedCell_;
6016 return ignoredCell_;
6017 default: _assert(false);
6020 case 1: switch (row) {
6022 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6023 [cell setShowSelection:NO];
6024 [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."];
6028 default: _assert(false);
6031 default: _assert(false);
6037 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6038 if ((self = [super initWithBook:book])) {
6039 database_ = database;
6040 name_ = [package retain];
6042 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6043 [self addSubview:table_];
6045 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6046 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6048 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6049 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6051 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6052 [subscribedCell_ setShowSelection:NO];
6053 [subscribedCell_ setTitle:@"Show All Changes"];
6054 [subscribedCell_ setControl:subscribedSwitch_];
6056 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6057 [ignoredCell_ setShowSelection:NO];
6058 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6059 [ignoredCell_ setControl:ignoredSwitch_];
6061 [table_ setDataSource:self];
6066 - (void) resetViewAnimated:(BOOL)animated {
6067 [table_ resetViewAnimated:animated];
6070 - (void) reloadData {
6071 if (package_ != nil)
6072 [package_ autorelease];
6073 package_ = [database_ packageWithName:name_];
6074 if (package_ != nil) {
6076 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6077 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6080 [table_ reloadData];
6083 - (NSString *) title {
6089 /* Signature View {{{ */
6090 @interface SignatureView : BrowserView {
6091 _transient Database *database_;
6095 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6099 @implementation SignatureView
6106 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6108 [super webView:sender didClearWindowObject:window forFrame:frame];
6111 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6112 if ((self = [super initWithBook:book]) != nil) {
6113 database_ = database;
6114 package_ = [package retain];
6119 - (void) resetViewAnimated:(BOOL)animated {
6122 - (void) reloadData {
6123 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6129 @interface Cydia : UIApplication <
6130 ConfirmationViewDelegate,
6131 ProgressViewDelegate,
6140 UIToolbar *buttonbar_;
6144 NSMutableArray *essential_;
6145 NSMutableArray *broken_;
6147 Database *database_;
6148 ProgressView *progress_;
6152 UIKeyboard *keyboard_;
6153 UIProgressHUD *hud_;
6155 SectionsView *sections_;
6156 ChangesView *changes_;
6157 ManageView *manage_;
6158 SearchView *search_;
6163 @implementation Cydia
6166 if ([broken_ count] != 0) {
6167 int count = [broken_ count];
6169 UIActionSheet *sheet = [[[UIActionSheet alloc]
6170 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6171 buttons:[NSArray arrayWithObjects:
6173 @"Ignore (Temporary)",
6175 defaultButtonIndex:0
6180 [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."];
6181 [sheet popupAlertAnimated:YES];
6182 } else if (!Ignored_ && [essential_ count] != 0) {
6183 int count = [essential_ count];
6185 UIActionSheet *sheet = [[[UIActionSheet alloc]
6186 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6187 buttons:[NSArray arrayWithObjects:
6188 @"Upgrade Essential",
6189 @"Complete Upgrade",
6190 @"Ignore (Temporary)",
6192 defaultButtonIndex:0
6197 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6198 [sheet popupAlertAnimated:YES];
6202 - (void) _reloadData {
6203 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6204 [hud setText:@"Reloading Data"];
6205 [overlay_ addSubview:hud];
6208 [database_ reloadData];
6212 [essential_ removeAllObjects];
6213 [broken_ removeAllObjects];
6215 NSArray *packages = [database_ packages];
6216 for (Package *package in packages) {
6218 [broken_ addObject:package];
6219 if ([package upgradableAndEssential:NO]) {
6220 if ([package essential])
6221 [essential_ addObject:package];
6227 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6228 [buttonbar_ setBadgeValue:badge forButton:3];
6229 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6230 [buttonbar_ setBadgeAnimated:YES forButton:3];
6231 [self setApplicationBadge:badge];
6233 [buttonbar_ setBadgeValue:nil forButton:3];
6234 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6235 [buttonbar_ setBadgeAnimated:NO forButton:3];
6236 [self removeApplicationBadge];
6241 // XXX: what is this line of code for?
6242 if ([packages count] == 0);
6243 else if (Loaded_) loaded:
6248 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6249 NSTimeInterval interval([update timeIntervalSinceNow]);
6250 if (interval <= 0 && interval > -600)
6258 [hud removeFromSuperview];*/
6261 - (void) _saveConfig {
6264 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6270 - (void) updateData {
6273 /* XXX: this is just stupid */
6274 if (tag_ != 2 && sections_ != nil)
6275 [sections_ reloadData];
6276 if (tag_ != 3 && changes_ != nil)
6277 [changes_ reloadData];
6278 if (tag_ != 5 && search_ != nil)
6279 [search_ reloadData];
6289 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6290 _assert(file != NULL);
6292 NSArray *keys = [Sources_ allKeys];
6294 for (int i(0), e([keys count]); i != e; ++i) {
6295 NSString *key = [keys objectAtIndex:i];
6296 NSDictionary *source = [Sources_ objectForKey:key];
6298 fprintf(file, "%s %s %s\n",
6299 [[source objectForKey:@"Type"] UTF8String],
6300 [[source objectForKey:@"URI"] UTF8String],
6301 [[source objectForKey:@"Distribution"] UTF8String]
6310 detachNewThreadSelector:@selector(update_)
6313 title:@"Updating Sources"
6317 - (void) reloadData {
6318 @synchronized (self) {
6319 if (confirm_ == nil)
6325 pkgProblemResolver *resolver = [database_ resolver];
6327 resolver->InstallProtect();
6328 if (!resolver->Resolve(true))
6332 - (void) popUpBook:(RVBook *)book {
6333 [underlay_ popSubview:book];
6336 - (CGRect) popUpBounds {
6337 return [underlay_ bounds];
6341 [database_ prepare];
6343 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6344 [confirm_ setDelegate:self];
6346 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6347 [page setDelegate:self];
6349 [confirm_ setPage:page];
6350 [self popUpBook:confirm_];
6353 - (void) installPackage:(Package *)package {
6354 @synchronized (self) {
6361 - (void) removePackage:(Package *)package {
6362 @synchronized (self) {
6369 - (void) distUpgrade {
6370 @synchronized (self) {
6371 [database_ upgrade];
6377 @synchronized (self) {
6379 if (confirm_ != nil) {
6387 [overlay_ removeFromSuperview];
6391 detachNewThreadSelector:@selector(perform)
6398 - (void) bootstrap_ {
6400 [database_ upgrade];
6401 [database_ prepare];
6402 [database_ perform];
6405 - (void) bootstrap {
6407 detachNewThreadSelector:@selector(bootstrap_)
6410 title:@"Bootstrap Install"
6414 - (void) progressViewIsComplete:(ProgressView *)progress {
6415 if (confirm_ != nil) {
6416 [underlay_ addSubview:overlay_];
6417 [confirm_ popFromSuperviewAnimated:NO];
6423 - (void) setPage:(RVPage *)page {
6424 [page resetViewAnimated:NO];
6425 [page setDelegate:self];
6426 [book_ setPage:page];
6429 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6430 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6431 [browser loadURL:url];
6435 - (void) _setHomePage {
6436 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6439 - (void) buttonBarItemTapped:(id)sender {
6440 unsigned tag = [sender tag];
6442 [book_ resetViewAnimated:YES];
6444 } else if (tag_ == 2 && tag != 2)
6445 [sections_ resetView];
6448 case 1: [self _setHomePage]; break;
6450 case 2: [self setPage:sections_]; break;
6451 case 3: [self setPage:changes_]; break;
6452 case 4: [self setPage:manage_]; break;
6453 case 5: [self setPage:search_]; break;
6455 default: _assert(false);
6461 - (void) applicationWillSuspend {
6463 [super applicationWillSuspend];
6466 - (void) askForSettings {
6467 UIActionSheet *role = [[[UIActionSheet alloc]
6468 initWithTitle:@"Who Are You?"
6469 buttons:[NSArray arrayWithObjects:
6470 @"User (Graphical Only)",
6471 @"Hacker (+ Command Line)",
6472 @"Developer (No Filters)",
6474 defaultButtonIndex:-1
6479 [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."];
6480 [role popupAlertAnimated:YES];
6485 [self setStatusBarShowsProgress:NO];
6488 [hud_ removeFromSuperview];
6492 pid_t pid = ExecFork();
6494 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6495 perror("launchctl stop");
6502 [self askForSettings];
6506 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6508 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6509 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6510 0, 0, screenrect.size.width, screenrect.size.height - 48
6511 ) database:database_];
6513 [book_ setDelegate:self];
6515 [overlay_ addSubview:book_];
6517 NSArray *buttonitems = [NSArray arrayWithObjects:
6518 [NSDictionary dictionaryWithObjectsAndKeys:
6519 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6520 @"home-up.png", kUIButtonBarButtonInfo,
6521 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6522 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6523 self, kUIButtonBarButtonTarget,
6524 @"Home", kUIButtonBarButtonTitle,
6525 @"0", kUIButtonBarButtonType,
6528 [NSDictionary dictionaryWithObjectsAndKeys:
6529 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6530 @"install-up.png", kUIButtonBarButtonInfo,
6531 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6532 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6533 self, kUIButtonBarButtonTarget,
6534 @"Sections", kUIButtonBarButtonTitle,
6535 @"0", kUIButtonBarButtonType,
6538 [NSDictionary dictionaryWithObjectsAndKeys:
6539 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6540 @"changes-up.png", kUIButtonBarButtonInfo,
6541 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6542 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6543 self, kUIButtonBarButtonTarget,
6544 @"Changes", kUIButtonBarButtonTitle,
6545 @"0", kUIButtonBarButtonType,
6548 [NSDictionary dictionaryWithObjectsAndKeys:
6549 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6550 @"manage-up.png", kUIButtonBarButtonInfo,
6551 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6552 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6553 self, kUIButtonBarButtonTarget,
6554 @"Manage", kUIButtonBarButtonTitle,
6555 @"0", kUIButtonBarButtonType,
6558 [NSDictionary dictionaryWithObjectsAndKeys:
6559 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6560 @"search-up.png", kUIButtonBarButtonInfo,
6561 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6562 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6563 self, kUIButtonBarButtonTarget,
6564 @"Search", kUIButtonBarButtonTitle,
6565 @"0", kUIButtonBarButtonType,
6569 buttonbar_ = [[UIToolbar alloc]
6571 withFrame:CGRectMake(
6572 0, screenrect.size.height - ButtonBarHeight_,
6573 screenrect.size.width, ButtonBarHeight_
6575 withItemList:buttonitems
6578 [buttonbar_ setDelegate:self];
6579 [buttonbar_ setBarStyle:1];
6580 [buttonbar_ setButtonBarTrackingMode:2];
6582 int buttons[5] = {1, 2, 3, 4, 5};
6583 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6584 [buttonbar_ showButtonGroup:0 withDuration:0];
6586 for (int i = 0; i != 5; ++i)
6587 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6588 i * 64 + 2, 1, 60, ButtonBarHeight_
6591 [buttonbar_ showSelectionForButton:1];
6592 [overlay_ addSubview:buttonbar_];
6594 [UIKeyboard initImplementationNow];
6595 CGSize keysize = [UIKeyboard defaultSize];
6596 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6597 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6598 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6599 [overlay_ addSubview:keyboard_];
6602 [underlay_ addSubview:overlay_];
6606 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6607 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6608 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6610 manage_ = (ManageView *) [[self
6611 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6612 withClass:[ManageView class]
6618 [self _setHomePage];
6621 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6622 NSString *context([sheet context]);
6624 if ([context isEqualToString:@"missing"])
6626 else if ([context isEqualToString:@"fixhalf"]) {
6629 @synchronized (self) {
6630 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6631 Package *broken = [broken_ objectAtIndex:i];
6634 NSString *id = [broken id];
6635 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6636 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6637 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6638 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6647 [broken_ removeAllObjects];
6656 } else if ([context isEqualToString:@"role"]) {
6658 case 1: Role_ = @"User"; break;
6659 case 2: Role_ = @"Hacker"; break;
6660 case 3: Role_ = @"Developer"; break;
6667 bool reset = Settings_ != nil;
6669 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6673 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6683 } else if ([context isEqualToString:@"upgrade"]) {
6686 @synchronized (self) {
6687 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6688 Package *essential = [essential_ objectAtIndex:i];
6689 [essential install];
6713 - (void) reorganize { _pooled
6714 system("/usr/libexec/cydia/free.sh");
6715 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6718 - (void) applicationSuspend:(__GSEvent *)event {
6719 if (hud_ == nil && ![progress_ isRunning])
6720 [super applicationSuspend:event];
6723 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6725 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6728 - (void) _setSuspended:(BOOL)value {
6730 [super _setSuspended:value];
6733 - (UIProgressHUD *) addProgressHUD {
6734 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6736 [underlay_ addSubview:hud];
6740 - (void) openMailToURL:(NSURL *)url {
6741 // XXX: this makes me sad
6743 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6745 [UIApp openURL:url];// asPanel:YES];
6749 - (void) clearFirstResponder {
6750 if (id responder = [window_ firstResponder])
6751 [responder resignFirstResponder];
6754 - (RVPage *) pageForPackage:(NSString *)name {
6755 if (Package *package = [database_ packageWithName:name]) {
6756 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6757 [view setPackage:package];
6760 UIActionSheet *sheet = [[[UIActionSheet alloc]
6761 initWithTitle:@"Cannot Locate Package"
6762 buttons:[NSArray arrayWithObjects:@"Close", nil]
6763 defaultButtonIndex:0
6768 [sheet setBodyText:[NSString stringWithFormat:
6769 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6772 [sheet popupAlertAnimated:YES];
6777 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6781 NSString *scheme([[url scheme] lowercaseString]);
6782 if (![scheme isEqualToString:@"cydia"])
6784 NSString *path([url absoluteString]);
6785 if ([path length] < 8)
6787 path = [path substringFromIndex:8];
6788 if (![path hasPrefix:@"/"])
6789 path = [@"/" stringByAppendingString:path];
6791 if ([path isEqualToString:@"/add-source"])
6792 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6793 else if ([path isEqualToString:@"/storage"])
6794 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6795 else if ([path isEqualToString:@"/sources"])
6796 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6797 else if ([path isEqualToString:@"/packages"])
6798 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6799 else if ([path hasPrefix:@"/url/"])
6800 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6801 else if ([path hasPrefix:@"/launch/"])
6802 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6803 else if ([path hasPrefix:@"/package-settings/"])
6804 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6805 else if ([path hasPrefix:@"/package-signature/"])
6806 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6807 else if ([path hasPrefix:@"/package/"])
6808 return [self pageForPackage:[path substringFromIndex:9]];
6809 else if ([path hasPrefix:@"/files/"]) {
6810 NSString *name = [path substringFromIndex:7];
6812 if (Package *package = [database_ packageWithName:name]) {
6813 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6814 [files setPackage:package];
6822 - (void) applicationOpenURL:(NSURL *)url {
6823 [super applicationOpenURL:url];
6825 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6826 [self setPage:page];
6827 [buttonbar_ showSelectionForButton:tag];
6832 - (void) applicationDidFinishLaunching:(id)unused {
6833 Font12_ = [[UIFont systemFontOfSize:12] retain];
6834 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6835 Font14_ = [[UIFont systemFontOfSize:14] retain];
6836 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6837 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6839 _assert(pkgInitConfig(*_config));
6840 _assert(pkgInitSystem(*_config, _system));
6844 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6845 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6847 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6849 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6850 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6852 [window_ orderFront:self];
6853 [window_ makeKey:self];
6854 [window_ setHidden:NO];
6856 database_ = [Database sharedInstance];
6857 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6858 [database_ setDelegate:progress_];
6859 [window_ setContentView:progress_];
6861 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6862 [progress_ setContentView:underlay_];
6864 [progress_ resetView];
6867 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6868 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6869 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6870 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6871 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6872 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6873 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6875 [self setIdleTimerDisabled:YES];
6877 hud_ = [self addProgressHUD];
6878 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6880 [self setStatusBarShowsProgress:YES];
6883 detachNewThreadSelector:@selector(reorganize)
6891 - (void) showKeyboard:(BOOL)show {
6892 CGSize keysize = [UIKeyboard defaultSize];
6893 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6894 CGRect keyup = keydown;
6895 keyup.origin.y -= keysize.height;
6897 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6898 [animation setSignificantRectFields:2];
6901 [animation setStartFrame:keydown];
6902 [animation setEndFrame:keyup];
6903 [keyboard_ activate];
6905 [animation setStartFrame:keyup];
6906 [animation setEndFrame:keydown];
6907 [keyboard_ deactivate];
6910 [[UIAnimator sharedAnimator]
6911 addAnimations:[NSArray arrayWithObjects:animation, nil]
6912 withDuration:KeyboardTime_
6917 - (void) slideUp:(UIActionSheet *)alert {
6919 [alert presentSheetFromButtonBar:buttonbar_];
6921 [alert presentSheetInView:overlay_];
6926 void AddPreferences(NSString *plist) { _pooled
6927 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6928 _assert(settings != NULL);
6929 NSMutableArray *items = [settings objectForKey:@"items"];
6933 for (size_t i(0); i != [items count]; ++i) {
6934 NSMutableDictionary *item([items objectAtIndex:i]);
6935 NSString *label = [item objectForKey:@"label"];
6936 if (label != nil && [label isEqualToString:@"Cydia"]) {
6943 for (size_t i(0); i != [items count]; ++i) {
6944 NSDictionary *item([items objectAtIndex:i]);
6945 NSString *label = [item objectForKey:@"label"];
6946 if (label != nil && [label isEqualToString:@"General"]) {
6947 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6948 @"CydiaSettings", @"bundle",
6949 @"PSLinkCell", @"cell",
6950 [NSNumber numberWithBool:YES], @"hasIcon",
6951 [NSNumber numberWithBool:YES], @"isController",
6953 nil] atIndex:(i + 1)];
6959 _assert([settings writeToFile:plist atomically:YES] == YES);
6964 id Alloc_(id self, SEL selector) {
6965 id object = alloc_(self, selector);
6966 lprintf("[%s]A-%p\n", self->isa->name, object);
6971 id Dealloc_(id self, SEL selector) {
6972 id object = dealloc_(self, selector);
6973 lprintf("[%s]D-%p\n", self->isa->name, object);
6977 int main(int argc, char *argv[]) { _pooled
6978 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6980 bool substrate(false);
6986 for (int argi(1); argi != argc; ++argi)
6987 if (strcmp(argv[argi], "--") == 0) {
6989 argv[argi] = argv[0];
6995 for (int argi(1); argi != arge; ++argi)
6996 if (strcmp(args[argi], "--bootstrap") == 0)
6998 else if (strcmp(args[argi], "--substrate") == 0)
7001 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7004 App_ = [[NSBundle mainBundle] bundlePath];
7005 Home_ = NSHomeDirectory();
7006 Locale_ = CFLocaleCopyCurrent();
7009 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7010 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7011 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7012 Sounds_Keyboard_ = [keyboard boolValue];
7018 #if 1 /* XXX: this costs 1.4s of startup performance */
7019 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7020 _assert(errno == ENOENT);
7021 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7022 _assert(errno == ENOENT);
7025 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7026 alloc_ = alloc->method_imp;
7027 alloc->method_imp = (IMP) &Alloc_;*/
7029 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7030 dealloc_ = dealloc->method_imp;
7031 dealloc->method_imp = (IMP) &Dealloc_;*/
7036 size = sizeof(maxproc);
7037 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7038 perror("sysctlbyname(\"kern.maxproc\", ?)");
7039 else if (maxproc < 64) {
7041 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7042 perror("sysctlbyname(\"kern.maxproc\", #)");
7045 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7046 char *machine = new char[size];
7047 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7048 perror("sysctlbyname(\"hw.machine\", ?)");
7052 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7054 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7055 Build_ = [system objectForKey:@"ProductBuildVersion"];
7057 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7058 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7060 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7061 Indices_ = [[NSMutableDictionary alloc] init];*/
7063 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7064 //@"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
7065 //@"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7068 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7069 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7071 Settings_ = [Metadata_ objectForKey:@"Settings"];
7073 Packages_ = [Metadata_ objectForKey:@"Packages"];
7074 Sections_ = [Metadata_ objectForKey:@"Sections"];
7075 Sources_ = [Metadata_ objectForKey:@"Sources"];
7078 if (Settings_ != nil)
7079 Role_ = [Settings_ objectForKey:@"Role"];
7081 if (Packages_ == nil) {
7082 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7083 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7086 if (Sections_ == nil) {
7087 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7088 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7091 if (Sources_ == nil) {
7092 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7093 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7097 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7100 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7101 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7102 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7103 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7105 if (access("/User", F_OK) != 0)
7106 system("/usr/libexec/cydia/firmware.sh");
7108 _assert([[NSFileManager defaultManager]
7109 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7110 withIntermediateDirectories:YES
7115 space_ = CGColorSpaceCreateDeviceRGB();
7117 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7118 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7119 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7120 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7121 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7122 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7124 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7126 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7128 UIApplicationUseLegacyEvents(YES);
7129 UIKeyboardDisableAutomaticAppearance();
7131 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7133 CGColorSpaceRelease(space_);