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 const std::string &maintainer(parser->Maintainer());
1532 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1536 return version_.end() ? 0 : version_->InstalledSize;
1539 - (NSString *) description {
1542 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1543 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1545 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1546 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1547 if ([lines count] < 2)
1550 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1551 for (size_t i(1); i != [lines count]; ++i) {
1552 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1553 [trimmed addObject:trim];
1556 return [trimmed componentsJoinedByString:@"\n"];
1559 - (NSString *) index {
1560 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1561 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1564 - (NSMutableDictionary *) metadata {
1565 return [Packages_ objectForKey:[id_ lowercaseString]];
1569 NSDictionary *metadata([self metadata]);
1570 if ([self subscribed])
1571 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1573 return [metadata objectForKey:@"FirstSeen"];
1576 - (BOOL) subscribed {
1577 NSDictionary *metadata([self metadata]);
1578 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1579 return [subscribed boolValue];
1585 NSDictionary *metadata([self metadata]);
1586 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1587 return [ignored boolValue];
1592 - (NSString *) latest {
1596 - (NSString *) installed {
1601 return !version_.end();
1604 - (BOOL) upgradableAndEssential:(BOOL)essential {
1605 pkgCache::VerIterator current = iterator_.CurrentVer();
1609 value = essential && [self essential];
1611 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1615 - (BOOL) essential {
1616 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1620 return [database_ cache][iterator_].InstBroken();
1623 - (BOOL) unfiltered {
1624 NSString *section = [self section];
1625 return section == nil || isSectionVisible(section);
1629 return [self hasSupportingRole] && [self unfiltered];
1633 unsigned char current = iterator_->CurrentState;
1634 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1637 - (BOOL) halfConfigured {
1638 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1641 - (BOOL) halfInstalled {
1642 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1646 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1647 return state.Mode != pkgDepCache::ModeKeep;
1650 - (NSString *) mode {
1651 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1653 switch (state.Mode) {
1654 case pkgDepCache::ModeDelete:
1655 if ((state.iFlags & pkgDepCache::Purge) != 0)
1659 case pkgDepCache::ModeKeep:
1660 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1664 case pkgDepCache::ModeInstall:
1665 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1666 return @"Reinstall";
1667 else switch (state.Status) {
1669 return @"Downgrade";
1675 return @"New Install";
1688 - (NSString *) name {
1689 return name_ == nil ? id_ : name_;
1692 - (NSString *) tagline {
1696 - (UIImage *) icon {
1697 NSString *section = [self simpleSection];
1701 if ([icon_ hasPrefix:@"file:///"])
1702 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1703 if (icon == nil) if (section != nil)
1704 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1705 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1706 if ([dicon hasPrefix:@"file:///"])
1707 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1709 icon = [UIImage applicationImageNamed:@"unknown.png"];
1713 - (NSString *) homepage {
1717 - (NSString *) depiction {
1721 - (Address *) sponsor {
1725 - (Address *) author {
1729 - (NSArray *) files {
1730 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1731 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1734 fin.open([path UTF8String]);
1739 while (std::getline(fin, line))
1740 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1745 - (NSArray *) relationships {
1746 return relationships_;
1749 - (NSArray *) warnings {
1750 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1751 const char *name(iterator_.Name());
1753 size_t length(strlen(name));
1754 if (length < 2) invalid:
1755 [warnings addObject:@"illegal package identifier"];
1756 else for (size_t i(0); i != length; ++i)
1758 /* XXX: technically this is not allowed */
1759 (name[i] < 'A' || name[i] > 'Z') &&
1760 (name[i] < 'a' || name[i] > 'z') &&
1761 (name[i] < '0' || name[i] > '9') &&
1762 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1765 if (strcmp(name, "cydia") != 0) {
1767 bool _private = false;
1770 bool repository = [[self section] isEqualToString:@"Repositories"];
1772 if (NSArray *files = [self files])
1773 for (NSString *file in files)
1774 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1776 else if (!_private && [file isEqualToString:@"/private"])
1778 else if (!stash && [file isEqualToString:@"/var/stash"])
1781 /* XXX: this is not sensitive enough. only some folders are valid. */
1782 if (cydia && !repository)
1783 [warnings addObject:@"files installed into Cydia.app"];
1785 [warnings addObject:@"files installed with /private/*"];
1787 [warnings addObject:@"files installed to /var/stash"];
1790 return [warnings count] == 0 ? nil : warnings;
1793 - (NSArray *) applications {
1794 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1796 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1798 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1799 if (NSArray *files = [self files])
1800 for (NSString *file in files)
1801 if (application_r(file)) {
1802 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1803 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1804 if ([id isEqualToString:me])
1807 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1809 display = application_r[1];
1811 NSString *bundle([file stringByDeletingLastPathComponent]);
1812 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1813 if (icon == nil || [icon length] == 0)
1815 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1817 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1818 [applications addObject:application];
1820 [application addObject:id];
1821 [application addObject:display];
1822 [application addObject:url];
1825 return [applications count] == 0 ? nil : applications;
1828 - (Source *) source {
1830 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1837 - (NSString *) role {
1841 - (NSString *) rating {
1842 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1843 return [rating stringByReplacingOccurrencesOfString:@"@" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1848 - (BOOL) matches:(NSString *)text {
1854 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1855 if (range.location != NSNotFound)
1858 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1859 if (range.location != NSNotFound)
1862 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1863 if (range.location != NSNotFound)
1869 - (bool) hasSupportingRole {
1872 if ([role_ isEqualToString:@"enduser"])
1874 if ([Role_ isEqualToString:@"User"])
1876 if ([role_ isEqualToString:@"hacker"])
1878 if ([Role_ isEqualToString:@"Hacker"])
1880 if ([role_ isEqualToString:@"developer"])
1882 if ([Role_ isEqualToString:@"Developer"])
1887 - (BOOL) hasTag:(NSString *)tag {
1888 return tags_ == nil ? NO : [tags_ containsObject:tag];
1891 - (NSString *) primaryPurpose {
1892 for (NSString *tag in tags_)
1893 if ([tag hasPrefix:@"purpose::"])
1894 return [tag substringFromIndex:9];
1898 - (NSArray *) purposes {
1899 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1900 for (NSString *tag in tags_)
1901 if ([tag hasPrefix:@"purpose::"])
1902 [purposes addObject:[tag substringFromIndex:9]];
1903 return [purposes count] == 0 ? nil : purposes;
1906 - (NSComparisonResult) compareByName:(Package *)package {
1907 NSString *lhs = [self name];
1908 NSString *rhs = [package name];
1910 if ([lhs length] != 0 && [rhs length] != 0) {
1911 unichar lhc = [lhs characterAtIndex:0];
1912 unichar rhc = [rhs characterAtIndex:0];
1914 if (isalpha(lhc) && !isalpha(rhc))
1915 return NSOrderedAscending;
1916 else if (!isalpha(lhc) && isalpha(rhc))
1917 return NSOrderedDescending;
1920 return [lhs compare:rhs options:LaxCompareOptions_];
1923 - (NSComparisonResult) compareBySection:(Package *)package {
1924 NSString *lhs = [self section];
1925 NSString *rhs = [package section];
1927 if (lhs == NULL && rhs != NULL)
1928 return NSOrderedAscending;
1929 else if (lhs != NULL && rhs == NULL)
1930 return NSOrderedDescending;
1931 else if (lhs != NULL && rhs != NULL) {
1932 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1933 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1936 return NSOrderedSame;
1939 - (uint32_t) compareForChanges {
1944 uint32_t timestamp : 30;
1945 uint32_t ignored : 1;
1946 uint32_t upgradable : 1;
1950 bool upgradable([self upgradableAndEssential:YES]);
1951 value.bits.upgradable = upgradable ? 1 : 0;
1954 value.bits.timestamp = 0;
1955 value.bits.ignored = [self ignored] ? 0 : 1;
1956 value.bits.upgradable = 1;
1958 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1959 value.bits.ignored = 0;
1960 value.bits.upgradable = 0;
1963 return _not(uint32_t) - value.key;
1967 pkgProblemResolver *resolver = [database_ resolver];
1968 resolver->Clear(iterator_);
1969 resolver->Protect(iterator_);
1970 pkgCacheFile &cache([database_ cache]);
1971 cache->MarkInstall(iterator_, false);
1972 pkgDepCache::StateCache &state((*cache)[iterator_]);
1973 if (!state.Install())
1974 cache->SetReInstall(iterator_, true);
1978 pkgProblemResolver *resolver = [database_ resolver];
1979 resolver->Clear(iterator_);
1980 resolver->Protect(iterator_);
1981 resolver->Remove(iterator_);
1982 [database_ cache]->MarkDelete(iterator_, true);
1985 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1986 return [NSNumber numberWithBool:(
1987 [self unfiltered] && [self matches:search]
1991 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1992 return [NSNumber numberWithBool:(
1993 (![number boolValue] || [self visible]) && [self installed] != nil
1997 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1998 NSString *section = [self section];
2000 return [NSNumber numberWithBool:(
2002 [self installed] == nil && (
2004 section == nil && [name length] == 0 ||
2005 [name isEqualToString:section]
2010 - (NSNumber *) isVisibleInSource:(Source *)source {
2011 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2016 /* Section Class {{{ */
2017 @interface Section : NSObject {
2023 - (NSComparisonResult) compareByName:(Section *)section;
2024 - (Section *) initWithName:(NSString *)name;
2025 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2026 - (NSString *) name;
2029 - (void) addToCount;
2033 @implementation Section
2040 - (NSComparisonResult) compareByName:(Section *)section {
2041 NSString *lhs = [self name];
2042 NSString *rhs = [section name];
2044 if ([lhs length] != 0 && [rhs length] != 0) {
2045 unichar lhc = [lhs characterAtIndex:0];
2046 unichar rhc = [rhs characterAtIndex:0];
2048 if (isalpha(lhc) && !isalpha(rhc))
2049 return NSOrderedAscending;
2050 else if (!isalpha(lhc) && isalpha(rhc))
2051 return NSOrderedDescending;
2054 return [lhs compare:rhs options:LaxCompareOptions_];
2057 - (Section *) initWithName:(NSString *)name {
2058 return [self initWithName:name row:0];
2061 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2062 if ((self = [super init]) != nil) {
2063 name_ = [name retain];
2068 - (NSString *) name {
2080 - (void) addToCount {
2088 static NSArray *Finishes_;
2090 /* Database Implementation {{{ */
2091 @implementation Database
2093 + (Database *) sharedInstance {
2094 static Database *instance;
2095 if (instance == nil)
2096 instance = [[Database alloc] init];
2105 - (void) _readCydia:(NSNumber *)fd { _pooled
2106 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2107 std::istream is(&ib);
2110 static Pcre finish_r("^finish:([^:]*)$");
2112 while (std::getline(is, line)) {
2113 const char *data(line.c_str());
2114 size_t size = line.size();
2115 lprintf("C:%s\n", data);
2117 if (finish_r(data, size)) {
2118 NSString *finish = finish_r[1];
2119 int index = [Finishes_ indexOfObject:finish];
2120 if (index != INT_MAX && index > Finish_)
2128 - (void) _readStatus:(NSNumber *)fd { _pooled
2129 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2130 std::istream is(&ib);
2133 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2134 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2136 while (std::getline(is, line)) {
2137 const char *data(line.c_str());
2138 size_t size = line.size();
2139 lprintf("S:%s\n", data);
2141 if (conffile_r(data, size)) {
2142 [delegate_ setConfigurationData:conffile_r[1]];
2143 } else if (strncmp(data, "status: ", 8) == 0) {
2144 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2145 [delegate_ setProgressTitle:string];
2146 } else if (pmstatus_r(data, size)) {
2147 std::string type([pmstatus_r[1] UTF8String]);
2148 NSString *id = pmstatus_r[2];
2150 float percent([pmstatus_r[3] floatValue]);
2151 [delegate_ setProgressPercent:(percent / 100)];
2153 NSString *string = pmstatus_r[4];
2155 if (type == "pmerror")
2156 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2157 withObject:[NSArray arrayWithObjects:string, id, nil]
2160 else if (type == "pmstatus") {
2161 [delegate_ setProgressTitle:string];
2162 } else if (type == "pmconffile")
2163 [delegate_ setConfigurationData:string];
2164 else _assert(false);
2165 } else _assert(false);
2171 - (void) _readOutput:(NSNumber *)fd { _pooled
2172 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2173 std::istream is(&ib);
2176 while (std::getline(is, line)) {
2177 lprintf("O:%s\n", line.c_str());
2178 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2188 - (Package *) packageWithName:(NSString *)name {
2189 if (static_cast<pkgDepCache *>(cache_) == NULL)
2191 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2192 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2195 - (Database *) init {
2196 if ((self = [super init]) != nil) {
2203 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2204 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2208 _assert(pipe(fds) != -1);
2211 _config->Set("APT::Keep-Fds::", cydiafd_);
2212 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2215 detachNewThreadSelector:@selector(_readCydia:)
2217 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2220 _assert(pipe(fds) != -1);
2224 detachNewThreadSelector:@selector(_readStatus:)
2226 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2229 _assert(pipe(fds) != -1);
2230 _assert(dup2(fds[0], 0) != -1);
2231 _assert(close(fds[0]) != -1);
2233 input_ = fdopen(fds[1], "a");
2235 _assert(pipe(fds) != -1);
2236 _assert(dup2(fds[1], 1) != -1);
2237 _assert(close(fds[1]) != -1);
2240 detachNewThreadSelector:@selector(_readOutput:)
2242 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2247 - (pkgCacheFile &) cache {
2251 - (pkgDepCache::Policy *) policy {
2255 - (pkgRecords *) records {
2259 - (pkgProblemResolver *) resolver {
2263 - (pkgAcquire &) fetcher {
2267 - (pkgSourceList &) list {
2271 - (NSArray *) packages {
2275 - (NSArray *) sources {
2276 return [sources_ allValues];
2279 - (NSArray *) issues {
2280 if (cache_->BrokenCount() == 0)
2283 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2285 for (Package *package in packages_) {
2286 if (![package broken])
2288 pkgCache::PkgIterator pkg([package iterator]);
2290 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2291 [entry addObject:[package name]];
2292 [issues addObject:entry];
2294 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2298 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2299 pkgCache::DepIterator start;
2300 pkgCache::DepIterator end;
2301 dep.GlobOr(start, end); // ++dep
2303 if (!cache_->IsImportantDep(end))
2305 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2308 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2309 [entry addObject:failure];
2310 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2312 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2313 [failure addObject:[package name]];
2315 pkgCache::PkgIterator target(start.TargetPkg());
2316 if (target->ProvidesList != 0)
2317 [failure addObject:@"?"];
2319 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2321 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2322 else if (!cache_[target].CandidateVerIter(cache_).end())
2323 [failure addObject:@"-"];
2324 else if (target->ProvidesList == 0)
2325 [failure addObject:@"!"];
2327 [failure addObject:@"%"];
2331 if (start.TargetVer() != 0)
2332 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2343 - (void) reloadData {
2363 if (!cache_.Open(progress_, true)) {
2365 if (!_error->PopMessage(error))
2368 lprintf("cache_.Open():[%s]\n", error.c_str());
2370 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2371 [delegate_ repairWithSelector:@selector(configure)];
2372 else if (error == "The package lists or status file could not be parsed or opened.")
2373 [delegate_ repairWithSelector:@selector(update)];
2374 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2375 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2376 // else if (error == "The list of sources could not be read.")
2377 else _assert(false);
2383 now_ = [[NSDate date] retain];
2385 policy_ = new pkgDepCache::Policy();
2386 records_ = new pkgRecords(cache_);
2387 resolver_ = new pkgProblemResolver(cache_);
2388 fetcher_ = new pkgAcquire(&status_);
2391 list_ = new pkgSourceList();
2392 _assert(list_->ReadMainList());
2394 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2395 _assert(pkgApplyStatus(cache_));
2397 if (cache_->BrokenCount() != 0) {
2398 _assert(pkgFixBroken(cache_));
2399 _assert(cache_->BrokenCount() == 0);
2400 _assert(pkgMinimizeUpgrade(cache_));
2403 [sources_ removeAllObjects];
2404 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2405 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2406 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2408 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2409 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2413 [packages_ removeAllObjects];
2416 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2417 if (Package *package = [Package packageWithIterator:iterator database:self])
2418 [packages_ addObject:package];
2420 [packages_ sortUsingSelector:@selector(compareByName:)];
2424 - (void) configure {
2425 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2426 system([dpkg UTF8String]);
2434 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2435 _assert(!_error->PendingError());
2438 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2441 public pkgArchiveCleaner
2444 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2449 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2451 while (_error->PopMessage(error))
2452 lprintf("ArchiveCleaner: %s\n", error.c_str());
2457 pkgRecords records(cache_);
2459 lock_ = new FileFd();
2460 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2461 _assert(!_error->PendingError());
2464 // XXX: explain this with an error message
2465 _assert(list.ReadMainList());
2467 manager_ = (_system->CreatePM(cache_));
2468 _assert(manager_->GetArchives(fetcher_, &list, &records));
2469 _assert(!_error->PendingError());
2473 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2475 _assert(list.ReadMainList());
2476 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2477 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2480 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2485 bool failed = false;
2486 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2487 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2490 std::string uri = (*item)->DescURI();
2491 std::string error = (*item)->ErrorText;
2493 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2496 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2497 withObject:[NSArray arrayWithObjects:
2498 [NSString stringWithUTF8String:error.c_str()],
2510 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2512 if (_error->PendingError()) {
2517 if (result == pkgPackageManager::Failed) {
2522 if (result != pkgPackageManager::Completed) {
2527 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2529 _assert(list.ReadMainList());
2530 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2531 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2534 if (![before isEqualToArray:after])
2539 _assert(pkgDistUpgrade(cache_));
2543 [self updateWithStatus:status_];
2546 - (void) updateWithStatus:(Status &)status {
2548 _assert(list.ReadMainList());
2551 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2552 _assert(!_error->PendingError());
2554 pkgAcquire fetcher(&status);
2555 _assert(list.GetIndexes(&fetcher));
2557 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2558 bool failed = false;
2559 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2560 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2561 (*item)->Finished();
2565 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2566 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2567 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2570 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2575 - (void) setDelegate:(id)delegate {
2576 delegate_ = delegate;
2577 status_.setDelegate(delegate);
2578 progress_.setDelegate(delegate);
2581 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2582 pkgIndexFile *index(NULL);
2583 list_->FindIndex(file, index);
2584 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2590 /* PopUp Windows {{{ */
2591 @interface PopUpView : UIView {
2592 _transient id delegate_;
2593 UITransitionView *transition_;
2598 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2602 @implementation PopUpView
2605 [transition_ setDelegate:nil];
2606 [transition_ release];
2612 [transition_ transition:UITransitionPushFromTop toView:nil];
2615 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2616 if (from != nil && to == nil)
2617 [self removeFromSuperview];
2620 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2621 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2622 delegate_ = delegate;
2624 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2625 [self addSubview:transition_];
2627 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2629 [view addSubview:self];
2631 [transition_ setDelegate:self];
2633 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2634 [transition_ transition:UITransitionNone toView:blank];
2635 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2642 /* Mail Composition {{{ */
2643 @interface MailToView : PopUpView {
2644 MailComposeController *controller_;
2647 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2651 @implementation MailToView
2654 [controller_ release];
2658 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2662 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2663 NSLog(@"did:%@", delivery);
2664 // [UIApp setStatusBarShowsProgress:NO];
2665 if ([controller error]){
2666 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2667 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2668 [mailAlertSheet setBodyText:[controller error]];
2669 [mailAlertSheet popupAlertAnimated:YES];
2673 - (void) showError {
2674 NSLog(@"%@", [controller_ error]);
2675 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2676 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2677 [mailAlertSheet setBodyText:[controller_ error]];
2678 [mailAlertSheet popupAlertAnimated:YES];
2681 - (void) deliverMessage { _pooled
2685 if (![controller_ deliverMessage])
2686 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2689 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2690 if ([controller_ needsDelivery])
2691 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2696 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2697 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2698 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2699 [controller_ setDelegate:self];
2700 [controller_ initializeUI];
2701 [controller_ setupForURL:url];
2703 UIView *view([controller_ view]);
2704 [overlay_ addSubview:view];
2710 /* Confirmation View {{{ */
2711 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2712 if (!iterator.end())
2713 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2714 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2716 pkgCache::PkgIterator package(dep.TargetPkg());
2719 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2726 @protocol ConfirmationViewDelegate
2731 @interface ConfirmationView : BrowserView {
2732 _transient Database *database_;
2733 UIActionSheet *essential_;
2740 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2744 @implementation ConfirmationView
2751 if (essential_ != nil)
2752 [essential_ release];
2758 [book_ popFromSuperviewAnimated:YES];
2761 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2762 NSString *context([sheet context]);
2764 if ([context isEqualToString:@"remove"]) {
2772 [delegate_ confirm];
2779 } else if ([context isEqualToString:@"unable"]) {
2783 [super alertSheet:sheet buttonClicked:button];
2786 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2787 [window setValue:changes_ forKey:@"changes"];
2788 [window setValue:issues_ forKey:@"issues"];
2789 [window setValue:sizes_ forKey:@"sizes"];
2790 [super webView:sender didClearWindowObject:window forFrame:frame];
2793 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2794 if ((self = [super initWithBook:book]) != nil) {
2795 database_ = database;
2797 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2798 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2799 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2800 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2801 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2805 pkgDepCache::Policy *policy([database_ policy]);
2807 pkgCacheFile &cache([database_ cache]);
2808 NSArray *packages = [database_ packages];
2809 for (size_t i(0), e = [packages count]; i != e; ++i) {
2810 Package *package = [packages objectAtIndex:i];
2811 pkgCache::PkgIterator iterator = [package iterator];
2812 pkgDepCache::StateCache &state(cache[iterator]);
2814 NSString *name([package name]);
2816 if (state.NewInstall())
2817 [installing addObject:name];
2818 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2819 [reinstalling addObject:name];
2820 else if (state.Upgrade())
2821 [upgrading addObject:name];
2822 else if (state.Downgrade())
2823 [downgrading addObject:name];
2824 else if (state.Delete()) {
2825 if ([package essential])
2827 [removing addObject:name];
2830 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2831 substrate_ |= DepSubstrate(iterator.CurrentVer());
2836 else if (Advanced_ || true) {
2837 essential_ = [[UIActionSheet alloc]
2838 initWithTitle:@"Removing Essentials"
2839 buttons:[NSArray arrayWithObjects:
2840 @"Cancel Operation (Safe)",
2841 @"Force Removal (Unsafe)",
2843 defaultButtonIndex:0
2849 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2851 [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."];
2853 essential_ = [[UIActionSheet alloc]
2854 initWithTitle:@"Unable to Comply"
2855 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2856 defaultButtonIndex:0
2861 [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."];
2864 changes_ = [[NSArray alloc] initWithObjects:
2872 issues_ = [database_ issues];
2874 issues_ = [issues_ retain];
2876 sizes_ = [[NSArray alloc] initWithObjects:
2877 SizeString([database_ fetcher].FetchNeeded()),
2878 SizeString([database_ fetcher].PartialPresent()),
2879 SizeString([database_ cache]->UsrSize()),
2882 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2886 - (NSString *) backButtonTitle {
2890 - (NSString *) leftButtonTitle {
2894 - (id) _rightButtonTitle {
2895 #if AlwaysReload || IgnoreInstall
2898 return issues_ == nil ? @"Confirm" : nil;
2902 - (void) _leftButtonClicked {
2907 - (void) _rightButtonClicked {
2909 return [super _rightButtonClicked];
2911 if (essential_ != nil)
2912 [essential_ popupAlertAnimated:YES];
2916 [delegate_ confirm];
2924 /* Progress Data {{{ */
2925 @interface ProgressData : NSObject {
2931 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2938 @implementation ProgressData
2940 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2941 if ((self = [super init]) != nil) {
2942 selector_ = selector;
2962 /* Progress View {{{ */
2963 @interface ProgressView : UIView <
2964 ConfigurationDelegate,
2967 _transient Database *database_;
2969 UIView *background_;
2970 UITransitionView *transition_;
2972 UINavigationBar *navbar_;
2973 UIProgressBar *progress_;
2974 UITextView *output_;
2975 UITextLabel *status_;
2976 UIPushButton *close_;
2979 SHA1SumValue springlist_;
2980 SHA1SumValue notifyconf_;
2981 SHA1SumValue sandplate_;
2983 NSTimeInterval last_;
2986 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2988 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2989 - (void) setContentView:(UIView *)view;
2992 - (void) _retachThread;
2993 - (void) _detachNewThreadData:(ProgressData *)data;
2994 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3000 @protocol ProgressViewDelegate
3001 - (void) progressViewIsComplete:(ProgressView *)sender;
3004 @implementation ProgressView
3007 [transition_ setDelegate:nil];
3008 [navbar_ setDelegate:nil];
3011 if (background_ != nil)
3012 [background_ release];
3013 [transition_ release];
3016 [progress_ release];
3023 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3024 if (bootstrap_ && from == overlay_ && to == view_)
3028 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3029 if ((self = [super initWithFrame:frame]) != nil) {
3030 database_ = database;
3031 delegate_ = delegate;
3033 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3034 [transition_ setDelegate:self];
3036 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3039 [overlay_ setBackgroundColor:[UIColor blackColor]];
3041 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3042 [background_ setBackgroundColor:[UIColor blackColor]];
3043 [self addSubview:background_];
3046 [self addSubview:transition_];
3048 CGSize navsize = [UINavigationBar defaultSize];
3049 CGRect navrect = {{0, 0}, navsize};
3051 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3052 [overlay_ addSubview:navbar_];
3054 [navbar_ setBarStyle:1];
3055 [navbar_ setDelegate:self];
3057 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3058 [navbar_ pushNavigationItem:navitem];
3060 CGRect bounds = [overlay_ bounds];
3061 CGSize prgsize = [UIProgressBar defaultSize];
3064 (bounds.size.width - prgsize.width) / 2,
3065 bounds.size.height - prgsize.height - 20
3068 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3069 [progress_ setStyle:0];
3071 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3073 bounds.size.height - prgsize.height - 50,
3074 bounds.size.width - 20,
3078 [status_ setColor:[UIColor whiteColor]];
3079 [status_ setBackgroundColor:[UIColor clearColor]];
3081 [status_ setCentersHorizontally:YES];
3082 //[status_ setFont:font];
3084 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3086 navrect.size.height + 20,
3087 bounds.size.width - 20,
3088 bounds.size.height - navsize.height - 62 - navrect.size.height
3091 //[output_ setTextFont:@"Courier New"];
3092 [output_ setTextSize:12];
3094 [output_ setTextColor:[UIColor whiteColor]];
3095 [output_ setBackgroundColor:[UIColor clearColor]];
3097 [output_ setMarginTop:0];
3098 [output_ setAllowsRubberBanding:YES];
3099 [output_ setEditable:NO];
3101 [overlay_ addSubview:output_];
3103 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3105 bounds.size.height - prgsize.height - 50,
3106 bounds.size.width - 20,
3110 [close_ setAutosizesToFit:NO];
3111 [close_ setDrawsShadow:YES];
3112 [close_ setStretchBackground:YES];
3113 [close_ setEnabled:YES];
3115 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3116 [close_ setTitleFont:bold];
3118 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3119 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3120 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3124 - (void) setContentView:(UIView *)view {
3125 view_ = [view retain];
3128 - (void) resetView {
3129 [transition_ transition:6 toView:view_];
3132 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3133 NSString *context([sheet context]);
3135 if ([context isEqualToString:@"error"])
3137 else if ([context isEqualToString:@"conffile"]) {
3138 FILE *input = [database_ input];
3142 fprintf(input, "N\n");
3146 fprintf(input, "Y\n");
3157 - (void) closeButtonPushed {
3166 [delegate_ suspendWithAnimation:YES];
3170 system("launchctl stop com.apple.SpringBoard");
3174 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3183 - (void) _retachThread {
3184 UINavigationItem *item = [navbar_ topItem];
3185 [item setTitle:@"Complete"];
3187 [overlay_ addSubview:close_];
3188 [progress_ removeFromSuperview];
3189 [status_ removeFromSuperview];
3191 [delegate_ progressViewIsComplete:self];
3194 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3195 MMap mmap(file, MMap::ReadOnly);
3197 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3198 if (!(sandplate_ == sha1.Result()))
3203 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3204 MMap mmap(file, MMap::ReadOnly);
3206 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3207 if (!(notifyconf_ == sha1.Result()))
3212 FileFd file(SpringBoard_, FileFd::ReadOnly);
3213 MMap mmap(file, MMap::ReadOnly);
3215 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3216 if (!(springlist_ == sha1.Result()))
3221 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3222 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3223 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3224 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3225 case 4: [close_ setTitle:@"Reboot Device"]; break;
3228 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3230 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3231 [cache autorelease];
3233 NSFileManager *manager = [NSFileManager defaultManager];
3234 NSError *error = nil;
3236 id system = [cache objectForKey:@"System"];
3241 if (stat(Cache_, &info) == -1)
3244 [system removeAllObjects];
3246 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3247 for (NSString *app in apps)
3248 if ([app hasSuffix:@".app"]) {
3249 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3250 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3251 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3253 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3254 [info setObject:path forKey:@"Path"];
3255 [info setObject:@"System" forKey:@"ApplicationType"];
3256 [system addInfoDictionary:info];
3262 [cache writeToFile:@Cache_ atomically:YES];
3264 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3266 if (chmod(Cache_, info.st_mode) == -1)
3270 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3273 notify_post("com.apple.mobile.application_installed");
3275 [delegate_ setStatusBarShowsProgress:NO];
3278 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3279 [[data target] performSelector:[data selector] withObject:[data object]];
3282 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3285 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3286 UINavigationItem *item = [navbar_ topItem];
3287 [item setTitle:title];
3289 [status_ setText:nil];
3290 [output_ setText:@""];
3291 [progress_ setProgress:0];
3294 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3296 [close_ removeFromSuperview];
3297 [overlay_ addSubview:progress_];
3298 [overlay_ addSubview:status_];
3300 [delegate_ setStatusBarShowsProgress:YES];
3304 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3305 MMap mmap(file, MMap::ReadOnly);
3307 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3308 sandplate_ = sha1.Result();
3312 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3313 MMap mmap(file, MMap::ReadOnly);
3315 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3316 notifyconf_ = sha1.Result();
3320 FileFd file(SpringBoard_, FileFd::ReadOnly);
3321 MMap mmap(file, MMap::ReadOnly);
3323 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3324 springlist_ = sha1.Result();
3327 [transition_ transition:6 toView:overlay_];
3330 detachNewThreadSelector:@selector(_detachNewThreadData:)
3332 withObject:[[ProgressData alloc]
3333 initWithSelector:selector
3340 - (void) repairWithSelector:(SEL)selector {
3342 detachNewThreadSelector:selector
3349 - (void) setConfigurationData:(NSString *)data {
3351 performSelectorOnMainThread:@selector(_setConfigurationData:)
3357 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3358 Package *package = id == nil ? nil : [database_ packageWithName:id];
3360 UIActionSheet *sheet = [[[UIActionSheet alloc]
3361 initWithTitle:(package == nil ? id : [package name])
3362 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3363 defaultButtonIndex:0
3368 [sheet setBodyText:error];
3369 [sheet popupAlertAnimated:YES];
3372 - (void) setProgressTitle:(NSString *)title {
3374 performSelectorOnMainThread:@selector(_setProgressTitle:)
3380 - (void) setProgressPercent:(float)percent {
3382 performSelectorOnMainThread:@selector(_setProgressPercent:)
3383 withObject:[NSNumber numberWithFloat:percent]
3388 - (void) startProgress {
3389 last_ = [NSDate timeIntervalSinceReferenceDate];
3392 - (void) addProgressOutput:(NSString *)output {
3394 performSelectorOnMainThread:@selector(_addProgressOutput:)
3400 - (bool) isCancelling:(size_t)received {
3402 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3403 if (received_ != received) {
3404 received_ = received;
3406 } else if (now - last_ > 30)
3413 - (void) _setConfigurationData:(NSString *)data {
3414 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3416 _assert(conffile_r(data));
3418 NSString *ofile = conffile_r[1];
3419 //NSString *nfile = conffile_r[2];
3421 UIActionSheet *sheet = [[[UIActionSheet alloc]
3422 initWithTitle:@"Configuration Upgrade"
3423 buttons:[NSArray arrayWithObjects:
3424 @"Keep My Old Copy",
3425 @"Accept The New Copy",
3426 // XXX: @"See What Changed",
3428 defaultButtonIndex:0
3433 [sheet setBodyText:[NSString stringWithFormat:
3434 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3437 [sheet popupAlertAnimated:YES];
3440 - (void) _setProgressTitle:(NSString *)title {
3441 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3442 for (size_t i(0), e([words count]); i != e; ++i) {
3443 NSString *word([words objectAtIndex:i]);
3444 if (Package *package = [database_ packageWithName:word])
3445 [words replaceObjectAtIndex:i withObject:[package name]];
3448 [status_ setText:[words componentsJoinedByString:@" "]];
3451 - (void) _setProgressPercent:(NSNumber *)percent {
3452 [progress_ setProgress:[percent floatValue]];
3455 - (void) _addProgressOutput:(NSString *)output {
3456 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3457 CGSize size = [output_ contentSize];
3458 CGRect rect = {{0, size.height}, {size.width, 0}};
3459 [output_ scrollRectToVisible:rect animated:YES];
3462 - (BOOL) isRunning {
3469 /* Package Cell {{{ */
3470 @interface PackageCell : UISimpleTableCell {
3473 NSString *description_;
3477 UITextLabel *status_;
3481 - (PackageCell *) init;
3482 - (void) setPackage:(Package *)package;
3484 + (int) heightForPackage:(Package *)package;
3488 @implementation PackageCell
3490 - (void) clearPackage {
3501 if (description_ != nil) {
3502 [description_ release];
3506 if (source_ != nil) {
3511 if (badge_ != nil) {
3518 [self clearPackage];
3525 - (PackageCell *) init {
3526 if ((self = [super init]) != nil) {
3528 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3529 [status_ setBackgroundColor:[UIColor clearColor]];
3530 [status_ setFont:small];
3535 - (void) setPackage:(Package *)package {
3536 [self clearPackage];
3538 Source *source = [package source];
3539 NSString *section = [package simpleSection];
3541 icon_ = [[package icon] retain];
3543 name_ = [[package name] retain];
3544 description_ = [[package tagline] retain];
3546 NSString *label = nil;
3547 bool trusted = false;
3549 if (source != nil) {
3550 label = [source label];
3551 trusted = [source trusted];
3552 } else if ([[package id] isEqualToString:@"firmware"])
3555 label = @"Unknown/Local";
3557 NSString *from = [NSString stringWithFormat:@"from %@", label];
3559 if (section != nil && ![section isEqualToString:label])
3560 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3562 source_ = [from retain];
3564 if (NSString *purpose = [package primaryPurpose])
3565 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3566 badge_ = [badge_ retain];
3569 if (NSString *mode = [package mode]) {
3570 [badge_ setImage:[UIImage applicationImageNamed:
3571 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3574 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3575 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3576 } else if ([package half]) {
3577 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3578 [status_ setText:@"Package Damaged"];
3579 [status_ setColor:[UIColor redColor]];
3581 [badge_ setImage:nil];
3582 [status_ setText:nil];
3587 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3590 rect.size = [icon_ size];
3592 rect.size.width /= 2;
3593 rect.size.height /= 2;
3595 rect.origin.x = 25 - rect.size.width / 2;
3596 rect.origin.y = 25 - rect.size.height / 2;
3598 [icon_ drawInRect:rect];
3601 if (badge_ != nil) {
3602 CGSize size = [badge_ size];
3604 [badge_ drawAtPoint:CGPointMake(
3605 36 - size.width / 2,
3606 36 - size.height / 2
3615 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3616 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3620 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3622 [super drawContentInRect:rect selected:selected];
3625 + (int) heightForPackage:(Package *)package {
3626 NSString *tagline([package tagline]);
3627 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3629 if ([package hasMode] || [package half])
3638 /* Section Cell {{{ */
3639 @interface SectionCell : UISimpleTableCell {
3644 _UISwitchSlider *switch_;
3649 - (void) setSection:(Section *)section editing:(BOOL)editing;
3653 @implementation SectionCell
3655 - (void) clearSection {
3656 if (section_ != nil) {
3666 if (count_ != nil) {
3673 [self clearSection];
3680 if ((self = [super init]) != nil) {
3681 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3683 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3684 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3688 - (void) onSwitch:(id)sender {
3689 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3690 if (metadata == nil) {
3691 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3692 [Sections_ setObject:metadata forKey:section_];
3696 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3699 - (void) setSection:(Section *)section editing:(BOOL)editing {
3700 if (editing != editing_) {
3702 [switch_ removeFromSuperview];
3704 [self addSubview:switch_];
3708 [self clearSection];
3710 if (section == nil) {
3711 name_ = [@"All Packages" retain];
3714 section_ = [section name];
3715 if (section_ != nil)
3716 section_ = [section_ retain];
3717 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3718 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3721 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3725 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3726 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3733 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3735 CGSize size = [count_ sizeWithFont:Font14_];
3739 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3741 [super drawContentInRect:rect selected:selected];
3747 /* File Table {{{ */
3748 @interface FileTable : RVPage {
3749 _transient Database *database_;
3752 NSMutableArray *files_;
3756 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3757 - (void) setPackage:(Package *)package;
3761 @implementation FileTable
3764 if (package_ != nil)
3773 - (int) numberOfRowsInTable:(UITable *)table {
3774 return files_ == nil ? 0 : [files_ count];
3777 - (float) table:(UITable *)table heightForRow:(int)row {
3781 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3782 if (reusing == nil) {
3783 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3784 UIFont *font = [UIFont systemFontOfSize:16];
3785 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3787 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3791 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3795 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3796 if ((self = [super initWithBook:book]) != nil) {
3797 database_ = database;
3799 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3801 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3802 [self addSubview:list_];
3804 UITableColumn *column = [[[UITableColumn alloc]
3805 initWithTitle:@"Name"
3807 width:[self frame].size.width
3810 [list_ setDataSource:self];
3811 [list_ setSeparatorStyle:1];
3812 [list_ addTableColumn:column];
3813 [list_ setDelegate:self];
3814 [list_ setReusesTableCells:YES];
3818 - (void) setPackage:(Package *)package {
3819 if (package_ != nil) {
3820 [package_ autorelease];
3829 [files_ removeAllObjects];
3831 if (package != nil) {
3832 package_ = [package retain];
3833 name_ = [[package id] retain];
3835 if (NSArray *files = [package files])
3836 [files_ addObjectsFromArray:files];
3838 if ([files_ count] != 0) {
3839 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3840 [files_ removeObjectAtIndex:0];
3841 [files_ sortUsingSelector:@selector(compareByPath:)];
3843 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3844 [stack addObject:@"/"];
3846 for (int i(0), e([files_ count]); i != e; ++i) {
3847 NSString *file = [files_ objectAtIndex:i];
3848 while (![file hasPrefix:[stack lastObject]])
3849 [stack removeLastObject];
3850 NSString *directory = [stack lastObject];
3851 [stack addObject:[file stringByAppendingString:@"/"]];
3852 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3853 ([stack count] - 2) * 3, "",
3854 [file substringFromIndex:[directory length]]
3863 - (void) resetViewAnimated:(BOOL)animated {
3864 [list_ resetViewAnimated:animated];
3867 - (void) reloadData {
3868 [self setPackage:[database_ packageWithName:name_]];
3869 [self reloadButtons];
3872 - (NSString *) title {
3873 return @"Installed Files";
3876 - (NSString *) backButtonTitle {
3882 /* Package View {{{ */
3883 @interface PackageView : BrowserView {
3884 _transient Database *database_;
3887 NSMutableArray *buttons_;
3890 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3891 - (void) setPackage:(Package *)package;
3895 @implementation PackageView
3898 if (package_ != nil)
3906 - (void) _clickButtonWithName:(NSString *)name {
3907 if ([name isEqualToString:@"Install"])
3908 [delegate_ installPackage:package_];
3909 else if ([name isEqualToString:@"Reinstall"])
3910 [delegate_ installPackage:package_];
3911 else if ([name isEqualToString:@"Remove"])
3912 [delegate_ removePackage:package_];
3913 else if ([name isEqualToString:@"Upgrade"])
3914 [delegate_ installPackage:package_];
3915 else _assert(false);
3918 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3919 NSString *context([sheet context]);
3921 if ([context isEqualToString:@"modify"]) {
3922 int count = [buttons_ count];
3923 _assert(count != 0);
3924 _assert(button <= count + 1);
3926 if (count != button - 1)
3927 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3931 [super alertSheet:sheet buttonClicked:button];
3934 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3935 return [super webView:sender didFinishLoadForFrame:frame];
3938 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3939 [window setValue:package_ forKey:@"package"];
3940 [super webView:sender didClearWindowObject:window forFrame:frame];
3944 - (void) _rightButtonClicked {
3945 /*[super _rightButtonClicked];
3948 int count = [buttons_ count];
3949 _assert(count != 0);
3952 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3954 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3955 [buttons addObjectsFromArray:buttons_];
3956 [buttons addObject:@"Cancel"];
3958 [delegate_ slideUp:[[[UIActionSheet alloc]
3961 defaultButtonIndex:2
3969 - (id) _rightButtonTitle {
3970 int count = [buttons_ count];
3971 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3974 - (NSString *) backButtonTitle {
3978 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3979 if ((self = [super initWithBook:book]) != nil) {
3980 database_ = database;
3981 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3985 - (void) setPackage:(Package *)package {
3986 if (package_ != nil) {
3987 [package_ autorelease];
3996 [buttons_ removeAllObjects];
3998 if (package != nil) {
3999 package_ = [package retain];
4000 name_ = [[package id] retain];
4002 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4004 if ([package_ source] == nil);
4005 else if ([package_ upgradableAndEssential:NO])
4006 [buttons_ addObject:@"Upgrade"];
4007 else if ([package_ installed] == nil)
4008 [buttons_ addObject:@"Install"];
4010 [buttons_ addObject:@"Reinstall"];
4011 if ([package_ installed] != nil)
4012 [buttons_ addObject:@"Remove"];
4020 - (void) reloadData {
4021 [self setPackage:[database_ packageWithName:name_]];
4022 [self reloadButtons];
4027 /* Package Table {{{ */
4028 @interface PackageTable : RVPage {
4029 _transient Database *database_;
4031 NSMutableArray *packages_;
4032 NSMutableArray *sections_;
4033 UISectionList *list_;
4036 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4038 - (void) setDelegate:(id)delegate;
4040 - (void) reloadData;
4041 - (void) resetCursor;
4043 - (UISectionList *) list;
4045 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4049 @implementation PackageTable
4052 [list_ setDataSource:nil];
4055 [packages_ release];
4056 [sections_ release];
4061 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4062 return [sections_ count];
4065 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4066 return [[sections_ objectAtIndex:section] name];
4069 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4070 return [[sections_ objectAtIndex:section] row];
4073 - (int) numberOfRowsInTable:(UITable *)table {
4074 return [packages_ count];
4077 - (float) table:(UITable *)table heightForRow:(int)row {
4078 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4081 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4083 reusing = [[[PackageCell alloc] init] autorelease];
4084 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4088 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4092 - (void) tableRowSelected:(NSNotification *)notification {
4093 int row = [[notification object] selectedRow];
4097 Package *package = [packages_ objectAtIndex:row];
4098 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4099 [view setDelegate:delegate_];
4100 [view setPackage:package];
4101 [book_ pushPage:view];
4104 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4105 if ((self = [super initWithBook:book]) != nil) {
4106 database_ = database;
4107 title_ = [title retain];
4109 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4110 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4112 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4113 [list_ setDataSource:self];
4115 UITableColumn *column = [[[UITableColumn alloc]
4116 initWithTitle:@"Name"
4118 width:[self frame].size.width
4121 UITable *table = [list_ table];
4122 [table setSeparatorStyle:1];
4123 [table addTableColumn:column];
4124 [table setDelegate:self];
4125 [table setReusesTableCells:YES];
4127 [self addSubview:list_];
4129 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4130 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4134 - (void) setDelegate:(id)delegate {
4135 delegate_ = delegate;
4138 - (bool) hasPackage:(Package *)package {
4142 - (void) reloadData {
4143 NSArray *packages = [database_ packages];
4145 [packages_ removeAllObjects];
4146 [sections_ removeAllObjects];
4148 for (size_t i(0); i != [packages count]; ++i) {
4149 Package *package([packages objectAtIndex:i]);
4150 if ([self hasPackage:package])
4151 [packages_ addObject:package];
4154 Section *section = nil;
4156 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4157 Package *package = [packages_ objectAtIndex:offset];
4158 NSString *name = [package index];
4160 if (section == nil || ![[section name] isEqualToString:name]) {
4161 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4162 [sections_ addObject:section];
4165 [section addToCount];
4171 - (NSString *) title {
4175 - (void) resetViewAnimated:(BOOL)animated {
4176 [list_ resetViewAnimated:animated];
4179 - (void) resetCursor {
4180 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4183 - (UISectionList *) list {
4187 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4188 [list_ setShouldHideHeaderInShortLists:hide];
4193 /* Filtered Package Table {{{ */
4194 @interface FilteredPackageTable : PackageTable {
4199 - (void) setObject:(id)object;
4201 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4205 @implementation FilteredPackageTable
4213 - (void) setObject:(id)object {
4219 object_ = [object retain];
4222 - (bool) hasPackage:(Package *)package {
4223 return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
4226 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4227 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4229 object_ = object == nil ? nil : [object retain];
4238 /* Add Source View {{{ */
4239 @interface AddSourceView : RVPage {
4240 _transient Database *database_;
4243 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4247 @implementation AddSourceView
4249 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4250 if ((self = [super initWithBook:book]) != nil) {
4251 database_ = database;
4257 /* Source Cell {{{ */
4258 @interface SourceCell : UITableCell {
4261 NSString *description_;
4267 - (SourceCell *) initWithSource:(Source *)source;
4271 @implementation SourceCell
4276 [description_ release];
4281 - (SourceCell *) initWithSource:(Source *)source {
4282 if ((self = [super init]) != nil) {
4284 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4286 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4287 icon_ = [icon_ retain];
4289 origin_ = [[source name] retain];
4290 label_ = [[source uri] retain];
4291 description_ = [[source description] retain];
4295 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4297 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4304 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4308 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4312 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4314 [super drawContentInRect:rect selected:selected];
4319 /* Source Table {{{ */
4320 @interface SourceTable : RVPage {
4321 _transient Database *database_;
4322 UISectionList *list_;
4323 NSMutableArray *sources_;
4324 UIActionSheet *alert_;
4328 UIProgressHUD *hud_;
4331 //NSURLConnection *installer_;
4332 NSURLConnection *trivial_bz2_;
4333 NSURLConnection *trivial_gz_;
4334 //NSURLConnection *automatic_;
4339 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4343 @implementation SourceTable
4345 - (void) _deallocConnection:(NSURLConnection *)connection {
4346 if (connection != nil) {
4347 [connection cancel];
4348 //[connection setDelegate:nil];
4349 [connection release];
4354 [[list_ table] setDelegate:nil];
4355 [list_ setDataSource:nil];
4364 //[self _deallocConnection:installer_];
4365 [self _deallocConnection:trivial_gz_];
4366 [self _deallocConnection:trivial_bz2_];
4367 //[self _deallocConnection:automatic_];
4374 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4375 return offset_ == 0 ? 1 : 2;
4378 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4379 switch (section + (offset_ == 0 ? 1 : 0)) {
4380 case 0: return @"Entered by User";
4381 case 1: return @"Installed by Packages";
4389 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4390 switch (section + (offset_ == 0 ? 1 : 0)) {
4392 case 1: return offset_;
4400 - (int) numberOfRowsInTable:(UITable *)table {
4401 return [sources_ count];
4404 - (float) table:(UITable *)table heightForRow:(int)row {
4405 Source *source = [sources_ objectAtIndex:row];
4406 return [source description] == nil ? 56 : 73;
4409 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4410 Source *source = [sources_ objectAtIndex:row];
4411 // XXX: weird warning, stupid selectors ;P
4412 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4415 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4419 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4423 - (void) tableRowSelected:(NSNotification*)notification {
4424 UITable *table([list_ table]);
4425 int row([table selectedRow]);
4429 Source *source = [sources_ objectAtIndex:row];
4431 PackageTable *packages = [[[FilteredPackageTable alloc]
4434 title:[source label]
4435 filter:@selector(isVisibleInSource:)
4439 [packages setDelegate:delegate_];
4441 [book_ pushPage:packages];
4444 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4445 Source *source = [sources_ objectAtIndex:row];
4446 return [source record] != nil;
4449 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4450 [[list_ table] setDeleteConfirmationRow:row];
4453 - (void) table:(UITable *)table deleteRow:(int)row {
4454 Source *source = [sources_ objectAtIndex:row];
4455 [Sources_ removeObjectForKey:[source key]];
4456 [delegate_ syncData];
4459 - (void) _endConnection:(NSURLConnection *)connection {
4460 NSURLConnection **field = NULL;
4461 if (connection == trivial_bz2_)
4462 field = &trivial_bz2_;
4463 else if (connection == trivial_gz_)
4464 field = &trivial_gz_;
4465 _assert(field != NULL);
4466 [connection release];
4470 trivial_bz2_ == nil &&
4473 [delegate_ setStatusBarShowsProgress:NO];
4476 [hud_ removeFromSuperview];
4481 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4484 @"./", @"Distribution",
4485 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4487 [delegate_ syncData];
4488 } else if (error_ != nil) {
4489 UIActionSheet *sheet = [[[UIActionSheet alloc]
4490 initWithTitle:@"Verification Error"
4491 buttons:[NSArray arrayWithObjects:@"OK", nil]
4492 defaultButtonIndex:0
4497 [sheet setBodyText:[error_ localizedDescription]];
4498 [sheet popupAlertAnimated:YES];
4500 UIActionSheet *sheet = [[[UIActionSheet alloc]
4501 initWithTitle:@"Did not Find Repository"
4502 buttons:[NSArray arrayWithObjects:@"OK", nil]
4503 defaultButtonIndex:0
4508 [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."];
4509 [sheet popupAlertAnimated:YES];
4515 if (error_ != nil) {
4522 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4523 switch ([response statusCode]) {
4529 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4530 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4532 error_ = [error retain];
4533 [self _endConnection:connection];
4536 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4537 [self _endConnection:connection];
4540 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4541 NSMutableURLRequest *request = [NSMutableURLRequest
4542 requestWithURL:[NSURL URLWithString:href]
4543 cachePolicy:NSURLRequestUseProtocolCachePolicy
4544 timeoutInterval:20.0
4547 [request setHTTPMethod:method];
4549 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4552 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4553 NSString *context([sheet context]);
4555 if ([context isEqualToString:@"source"]) {
4558 NSString *href = [[sheet textField] text];
4560 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4562 if (![href hasSuffix:@"/"])
4563 href_ = [href stringByAppendingString:@"/"];
4566 href_ = [href_ retain];
4568 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4569 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4570 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4574 hud_ = [delegate_ addProgressHUD];
4575 [hud_ setText:@"Verifying URL"];
4586 } else if ([context isEqualToString:@"trivial"])
4588 else if ([context isEqualToString:@"urlerror"])
4592 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4593 if ((self = [super initWithBook:book]) != nil) {
4594 database_ = database;
4595 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4597 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4598 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4599 [list_ setShouldHideHeaderInShortLists:NO];
4601 [self addSubview:list_];
4602 [list_ setDataSource:self];
4604 UITableColumn *column = [[UITableColumn alloc]
4605 initWithTitle:@"Name"
4607 width:[self frame].size.width
4610 UITable *table = [list_ table];
4611 [table setSeparatorStyle:1];
4612 [table addTableColumn:column];
4613 [table setDelegate:self];
4617 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4618 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4622 - (void) reloadData {
4624 _assert(list.ReadMainList());
4626 [sources_ removeAllObjects];
4627 [sources_ addObjectsFromArray:[database_ sources]];
4629 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4632 int count = [sources_ count];
4633 for (offset_ = 0; offset_ != count; ++offset_) {
4634 Source *source = [sources_ objectAtIndex:offset_];
4635 if ([source record] == nil)
4642 - (void) resetViewAnimated:(BOOL)animated {
4643 [list_ resetViewAnimated:animated];
4646 - (void) _leftButtonClicked {
4647 /*[book_ pushPage:[[[AddSourceView alloc]
4652 UIActionSheet *sheet = [[[UIActionSheet alloc]
4653 initWithTitle:@"Enter Cydia/APT URL"
4654 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4655 defaultButtonIndex:0
4660 [sheet setNumberOfRows:1];
4662 [sheet addTextFieldWithValue:@"http://" label:@""];
4664 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4665 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4666 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4667 [traits setKeyboardType:UIKeyboardTypeURL];
4668 // XXX: UIReturnKeyDone
4669 [traits setReturnKeyType:UIReturnKeyNext];
4671 [sheet popupAlertAnimated:YES];
4674 - (void) _rightButtonClicked {
4675 UITable *table = [list_ table];
4676 BOOL editing = [table isRowDeletionEnabled];
4677 [table enableRowDeletion:!editing animated:YES];
4678 [book_ reloadButtonsForPage:self];
4681 - (NSString *) title {
4685 - (NSString *) leftButtonTitle {
4686 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4689 - (id) rightButtonTitle {
4690 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4693 - (UINavigationButtonStyle) rightButtonStyle {
4694 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4700 /* Installed View {{{ */
4701 @interface InstalledView : RVPage {
4702 _transient Database *database_;
4703 FilteredPackageTable *packages_;
4707 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4711 @implementation InstalledView
4714 [packages_ release];
4718 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4719 if ((self = [super initWithBook:book]) != nil) {
4720 database_ = database;
4722 packages_ = [[FilteredPackageTable alloc]
4726 filter:@selector(isInstalledAndVisible:)
4727 with:[NSNumber numberWithBool:YES]
4730 [self addSubview:packages_];
4732 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4733 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4737 - (void) resetViewAnimated:(BOOL)animated {
4738 [packages_ resetViewAnimated:animated];
4741 - (void) reloadData {
4742 [packages_ reloadData];
4745 - (void) _rightButtonClicked {
4746 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4747 [packages_ reloadData];
4749 [book_ reloadButtonsForPage:self];
4752 - (NSString *) title {
4753 return @"Installed";
4756 - (NSString *) backButtonTitle {
4760 - (id) rightButtonTitle {
4761 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4764 - (UINavigationButtonStyle) rightButtonStyle {
4765 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4768 - (void) setDelegate:(id)delegate {
4769 [super setDelegate:delegate];
4770 [packages_ setDelegate:delegate];
4777 @interface HomeView : BrowserView {
4782 @implementation HomeView
4784 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4785 NSString *context([sheet context]);
4787 if ([context isEqualToString:@"about"])
4790 [super alertSheet:sheet buttonClicked:button];
4793 - (void) _leftButtonClicked {
4794 UIActionSheet *sheet = [[[UIActionSheet alloc]
4795 initWithTitle:@"About Cydia Installer"
4796 buttons:[NSArray arrayWithObjects:@"Close", nil]
4797 defaultButtonIndex:0
4803 @"Copyright (C) 2008\n"
4804 "Jay Freeman (saurik)\n"
4805 "saurik@saurik.com\n"
4806 "http://www.saurik.com/\n"
4809 "http://www.theokorigroup.com/\n"
4811 "College of Creative Studies,\n"
4812 "University of California,\n"
4814 "http://www.ccs.ucsb.edu/"
4817 [sheet popupAlertAnimated:YES];
4820 - (NSString *) leftButtonTitle {
4826 /* Manage View {{{ */
4827 @interface ManageView : BrowserView {
4832 @implementation ManageView
4834 - (NSString *) title {
4838 - (void) _leftButtonClicked {
4839 [delegate_ askForSettings];
4842 - (NSString *) leftButtonTitle {
4847 - (id) _rightButtonTitle {
4859 #include <BrowserView.m>
4861 /* Cydia Book {{{ */
4862 @interface CYBook : RVBook <
4865 _transient Database *database_;
4866 UINavigationBar *overlay_;
4867 UINavigationBar *underlay_;
4868 UIProgressIndicator *indicator_;
4869 UITextLabel *prompt_;
4870 UIProgressBar *progress_;
4871 UINavigationButton *cancel_;
4874 NSTimeInterval last_;
4877 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4883 @implementation CYBook
4887 [indicator_ release];
4889 [progress_ release];
4894 - (NSString *) getTitleForPage:(RVPage *)page {
4895 return Simplify([super getTitleForPage:page]);
4903 [UIView beginAnimations:nil context:NULL];
4905 CGRect ovrframe = [overlay_ frame];
4906 ovrframe.origin.y = 0;
4907 [overlay_ setFrame:ovrframe];
4909 CGRect barframe = [navbar_ frame];
4910 barframe.origin.y += ovrframe.size.height;
4911 [navbar_ setFrame:barframe];
4913 CGRect trnframe = [transition_ frame];
4914 trnframe.origin.y += ovrframe.size.height;
4915 trnframe.size.height -= ovrframe.size.height;
4916 [transition_ setFrame:trnframe];
4918 [UIView endAnimations];
4920 [indicator_ startAnimation];
4921 [prompt_ setText:@"Updating Database"];
4922 [progress_ setProgress:0];
4925 last_ = [NSDate timeIntervalSinceReferenceDate];
4927 [overlay_ addSubview:cancel_];
4930 detachNewThreadSelector:@selector(_update)
4939 [indicator_ stopAnimation];
4941 [UIView beginAnimations:nil context:NULL];
4943 CGRect ovrframe = [overlay_ frame];
4944 ovrframe.origin.y = -ovrframe.size.height;
4945 [overlay_ setFrame:ovrframe];
4947 CGRect barframe = [navbar_ frame];
4948 barframe.origin.y -= ovrframe.size.height;
4949 [navbar_ setFrame:barframe];
4951 CGRect trnframe = [transition_ frame];
4952 trnframe.origin.y -= ovrframe.size.height;
4953 trnframe.size.height += ovrframe.size.height;
4954 [transition_ setFrame:trnframe];
4956 [UIView commitAnimations];
4958 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4961 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4962 if ((self = [super initWithFrame:frame]) != nil) {
4963 database_ = database;
4965 CGRect ovrrect = [navbar_ bounds];
4966 ovrrect.size.height = [UINavigationBar defaultSize].height;
4967 ovrrect.origin.y = -ovrrect.size.height;
4969 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4970 [self addSubview:overlay_];
4972 ovrrect.origin.y = frame.size.height;
4973 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4974 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4975 [self addSubview:underlay_];
4977 [overlay_ setBarStyle:1];
4978 [underlay_ setBarStyle:1];
4980 int barstyle = [overlay_ _barStyle:NO];
4981 bool ugly = barstyle == 0;
4983 UIProgressIndicatorStyle style = ugly ?
4984 UIProgressIndicatorStyleMediumBrown :
4985 UIProgressIndicatorStyleMediumWhite;
4987 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4988 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4989 CGRect indrect = {{indoffset, indoffset}, indsize};
4991 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4992 [indicator_ setStyle:style];
4993 [overlay_ addSubview:indicator_];
4995 CGSize prmsize = {215, indsize.height + 4};
4998 indoffset * 2 + indsize.width,
5002 unsigned(ovrrect.size.height - prmsize.height) / 2
5005 UIFont *font = [UIFont systemFontOfSize:15];
5007 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5009 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5010 [prompt_ setBackgroundColor:[UIColor clearColor]];
5011 [prompt_ setFont:font];
5013 [overlay_ addSubview:prompt_];
5015 CGSize prgsize = {75, 100};
5018 ovrrect.size.width - prgsize.width - 10,
5019 (ovrrect.size.height - prgsize.height) / 2
5022 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5023 [progress_ setStyle:0];
5024 [overlay_ addSubview:progress_];
5026 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5027 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5029 CGRect frame = [cancel_ frame];
5030 frame.size.width = 65;
5031 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5032 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5033 [cancel_ setFrame:frame];
5035 [cancel_ setBarStyle:barstyle];
5039 - (void) _onCancel {
5041 [cancel_ removeFromSuperview];
5044 - (void) _update { _pooled
5046 status.setDelegate(self);
5048 [database_ updateWithStatus:status];
5051 performSelectorOnMainThread:@selector(_update_)
5057 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5058 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5061 - (void) setProgressTitle:(NSString *)title {
5063 performSelectorOnMainThread:@selector(_setProgressTitle:)
5069 - (void) setProgressPercent:(float)percent {
5071 performSelectorOnMainThread:@selector(_setProgressPercent:)
5072 withObject:[NSNumber numberWithFloat:percent]
5077 - (void) startProgress {
5080 - (void) addProgressOutput:(NSString *)output {
5082 performSelectorOnMainThread:@selector(_addProgressOutput:)
5088 - (bool) isCancelling:(size_t)received {
5089 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5090 if (received_ != received) {
5091 received_ = received;
5093 } else if (now - last_ > 15)
5098 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5102 - (void) _setProgressTitle:(NSString *)title {
5103 [prompt_ setText:title];
5106 - (void) _setProgressPercent:(NSNumber *)percent {
5107 [progress_ setProgress:[percent floatValue]];
5110 - (void) _addProgressOutput:(NSString *)output {
5115 /* Cydia:// Protocol {{{ */
5116 @interface CydiaURLProtocol : NSURLProtocol {
5121 @implementation CydiaURLProtocol
5123 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5124 NSURL *url([request URL]);
5127 NSString *scheme([[url scheme] lowercaseString]);
5128 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5133 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5137 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5138 id<NSURLProtocolClient> client([self client]);
5140 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5142 NSData *data(UIImagePNGRepresentation(icon));
5144 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5145 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5146 [client URLProtocol:self didLoadData:data];
5147 [client URLProtocolDidFinishLoading:self];
5151 - (void) startLoading {
5152 id<NSURLProtocolClient> client([self client]);
5153 NSURLRequest *request([self request]);
5155 NSURL *url([request URL]);
5156 NSString *href([url absoluteString]);
5158 NSString *path([href substringFromIndex:8]);
5159 NSRange slash([path rangeOfString:@"/"]);
5162 if (slash.location == NSNotFound) {
5166 command = [path substringToIndex:slash.location];
5167 path = [path substringFromIndex:(slash.location + 1)];
5170 Database *database([Database sharedInstance]);
5172 if ([command isEqualToString:@"package-icon"]) {
5175 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5176 Package *package([database packageWithName:path]);
5179 UIImage *icon([package icon]);
5180 [self _returnPNGWithImage:icon forRequest:request];
5181 } else if ([command isEqualToString:@"source-icon"]) {
5184 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5185 NSString *source(Simplify(path));
5186 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5188 icon = [UIImage applicationImageNamed:@"unknown.png"];
5189 [self _returnPNGWithImage:icon forRequest:request];
5190 } else if ([command isEqualToString:@"uikit-image"]) {
5193 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5194 UIImage *icon(_UIImageWithName(path));
5195 [self _returnPNGWithImage:icon forRequest:request];
5196 } else if ([command isEqualToString:@"section-icon"]) {
5199 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5200 NSString *section(Simplify(path));
5201 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5203 icon = [UIImage applicationImageNamed:@"unknown.png"];
5204 [self _returnPNGWithImage:icon forRequest:request];
5206 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5210 - (void) stopLoading {
5216 /* Sections View {{{ */
5217 @interface SectionsView : RVPage {
5218 _transient Database *database_;
5219 NSMutableArray *sections_;
5220 NSMutableArray *filtered_;
5221 UITransitionView *transition_;
5227 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5228 - (void) reloadData;
5233 @implementation SectionsView
5236 [list_ setDataSource:nil];
5237 [list_ setDelegate:nil];
5239 [sections_ release];
5240 [filtered_ release];
5241 [transition_ release];
5243 [accessory_ release];
5247 - (int) numberOfRowsInTable:(UITable *)table {
5248 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5251 - (float) table:(UITable *)table heightForRow:(int)row {
5255 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5257 reusing = [[[SectionCell alloc] init] autorelease];
5258 [(SectionCell *)reusing setSection:(editing_ ?
5259 [sections_ objectAtIndex:row] :
5260 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5261 ) editing:editing_];
5265 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5269 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5273 - (void) tableRowSelected:(NSNotification *)notification {
5274 int row = [[notification object] selectedRow];
5285 title = @"All Packages";
5287 section = [filtered_ objectAtIndex:(row - 1)];
5288 name = [section name];
5294 title = @"(No Section)";
5298 PackageTable *table = [[[FilteredPackageTable alloc]
5302 filter:@selector(isVisiblyUninstalledInSection:)
5306 [table setDelegate:delegate_];
5308 [book_ pushPage:table];
5311 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5312 if ((self = [super initWithBook:book]) != nil) {
5313 database_ = database;
5315 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5316 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5318 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5319 [self addSubview:transition_];
5321 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5322 [transition_ transition:0 toView:list_];
5324 UITableColumn *column = [[[UITableColumn alloc]
5325 initWithTitle:@"Name"
5327 width:[self frame].size.width
5330 [list_ setDataSource:self];
5331 [list_ setSeparatorStyle:1];
5332 [list_ addTableColumn:column];
5333 [list_ setDelegate:self];
5334 [list_ setReusesTableCells:YES];
5338 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5339 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5343 - (void) reloadData {
5344 NSArray *packages = [database_ packages];
5346 [sections_ removeAllObjects];
5347 [filtered_ removeAllObjects];
5349 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5350 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5353 for (size_t i(0); i != [packages count]; ++i) {
5354 Package *package([packages objectAtIndex:i]);
5355 NSString *name([package section]);
5358 Section *section([sections objectForKey:name]);
5359 if (section == nil) {
5360 section = [[[Section alloc] initWithName:name] autorelease];
5361 [sections setObject:section forKey:name];
5365 if ([package valid] && [package installed] == nil && [package visible])
5366 [filtered addObject:package];
5370 [sections_ addObjectsFromArray:[sections allValues]];
5371 [sections_ sortUsingSelector:@selector(compareByName:)];
5374 [filtered sortUsingSelector:@selector(compareBySection:)];
5377 Section *section = nil;
5378 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5379 Package *package = [filtered objectAtIndex:offset];
5380 NSString *name = [package section];
5382 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5383 section = name == nil ?
5384 [[[Section alloc] initWithName:nil] autorelease] :
5385 [sections objectForKey:name];
5386 [filtered_ addObject:section];
5389 [section addToCount];
5397 - (void) resetView {
5399 [self _rightButtonClicked];
5402 - (void) resetViewAnimated:(BOOL)animated {
5403 [list_ resetViewAnimated:animated];
5406 - (void) _rightButtonClicked {
5407 if ((editing_ = !editing_))
5410 [delegate_ updateData];
5411 [book_ reloadTitleForPage:self];
5412 [book_ reloadButtonsForPage:self];
5415 - (NSString *) title {
5416 return editing_ ? @"Section Visibility" : @"Install by Section";
5419 - (NSString *) backButtonTitle {
5423 - (id) rightButtonTitle {
5424 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5427 - (UINavigationButtonStyle) rightButtonStyle {
5428 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5431 - (UIView *) accessoryView {
5437 /* Changes View {{{ */
5438 @interface ChangesView : RVPage {
5439 _transient Database *database_;
5440 NSMutableArray *packages_;
5441 NSMutableArray *sections_;
5442 UISectionList *list_;
5446 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5447 - (void) reloadData;
5451 @implementation ChangesView
5454 [[list_ table] setDelegate:nil];
5455 [list_ setDataSource:nil];
5457 [packages_ release];
5458 [sections_ release];
5463 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5464 return [sections_ count];
5467 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5468 return [[sections_ objectAtIndex:section] name];
5471 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5472 return [[sections_ objectAtIndex:section] row];
5475 - (int) numberOfRowsInTable:(UITable *)table {
5476 return [packages_ count];
5479 - (float) table:(UITable *)table heightForRow:(int)row {
5480 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5483 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5485 reusing = [[[PackageCell alloc] init] autorelease];
5486 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5490 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5494 - (void) tableRowSelected:(NSNotification *)notification {
5495 int row = [[notification object] selectedRow];
5498 Package *package = [packages_ objectAtIndex:row];
5499 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5500 [view setDelegate:delegate_];
5501 [view setPackage:package];
5502 [book_ pushPage:view];
5505 - (void) _leftButtonClicked {
5506 [(CYBook *)book_ update];
5507 [self reloadButtons];
5510 - (void) _rightButtonClicked {
5511 [delegate_ distUpgrade];
5514 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5515 if ((self = [super initWithBook:book]) != nil) {
5516 database_ = database;
5518 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5519 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5521 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5522 [self addSubview:list_];
5524 [list_ setShouldHideHeaderInShortLists:NO];
5525 [list_ setDataSource:self];
5526 //[list_ setSectionListStyle:1];
5528 UITableColumn *column = [[[UITableColumn alloc]
5529 initWithTitle:@"Name"
5531 width:[self frame].size.width
5534 UITable *table = [list_ table];
5535 [table setSeparatorStyle:1];
5536 [table addTableColumn:column];
5537 [table setDelegate:self];
5538 [table setReusesTableCells:YES];
5542 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5543 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5547 - (void) reloadData {
5548 NSArray *packages = [database_ packages];
5550 [packages_ removeAllObjects];
5551 [sections_ removeAllObjects];
5554 for (size_t i(0); i != [packages count]; ++i) {
5555 Package *package([packages objectAtIndex:i]);
5558 [package installed] == nil && [package valid] && [package visible] ||
5559 [package upgradableAndEssential:YES]
5561 [packages_ addObject:package];
5565 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5568 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5569 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5570 Section *section = nil;
5574 bool unseens = false;
5576 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5579 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5580 Package *package = [packages_ objectAtIndex:offset];
5582 if (![package upgradableAndEssential:YES]) {
5584 NSDate *seen = [package seen];
5586 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5589 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5590 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5591 [sections_ addObject:section];
5595 [section addToCount];
5596 } else if ([package ignored])
5597 [ignored addToCount];
5600 [upgradable addToCount];
5605 CFRelease(formatter);
5608 Section *last = [sections_ lastObject];
5609 size_t count = [last count];
5610 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5611 [sections_ removeLastObject];
5614 if ([ignored count] != 0)
5615 [sections_ insertObject:ignored atIndex:0];
5617 [sections_ insertObject:upgradable atIndex:0];
5620 [self reloadButtons];
5623 - (void) resetViewAnimated:(BOOL)animated {
5624 [list_ resetViewAnimated:animated];
5627 - (NSString *) leftButtonTitle {
5628 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5631 - (id) rightButtonTitle {
5632 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5635 - (NSString *) title {
5641 /* Search View {{{ */
5642 @protocol SearchViewDelegate
5643 - (void) showKeyboard:(BOOL)show;
5646 @interface SearchView : RVPage {
5648 UISearchField *field_;
5649 UITransitionView *transition_;
5650 FilteredPackageTable *table_;
5651 UIPreferencesTable *advanced_;
5657 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5658 - (void) reloadData;
5662 @implementation SearchView
5665 [field_ setDelegate:nil];
5667 [accessory_ release];
5669 [transition_ release];
5671 [advanced_ release];
5676 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5680 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5682 case 0: return @"Advanced Search (Coming Soon!)";
5684 default: _assert(false);
5688 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5692 default: _assert(false);
5696 - (void) _showKeyboard:(BOOL)show {
5697 CGSize keysize = [UIKeyboard defaultSize];
5698 CGRect keydown = [book_ pageBounds];
5699 CGRect keyup = keydown;
5700 keyup.size.height -= keysize.height - ButtonBarHeight_;
5702 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5704 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5705 [animation setSignificantRectFields:8];
5708 [animation setStartFrame:keydown];
5709 [animation setEndFrame:keyup];
5711 [animation setStartFrame:keyup];
5712 [animation setEndFrame:keydown];
5715 UIAnimator *animator = [UIAnimator sharedAnimator];
5718 addAnimations:[NSArray arrayWithObjects:animation, nil]
5719 withDuration:(KeyboardTime_ - delay)
5724 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5726 [delegate_ showKeyboard:show];
5729 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5730 [self _showKeyboard:YES];
5733 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5734 [self _showKeyboard:NO];
5737 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5739 NSString *text([field_ text]);
5740 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5746 - (void) textFieldClearButtonPressed:(UITextField *)field {
5750 - (void) keyboardInputShouldDelete:(id)input {
5754 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5755 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5759 [field_ resignFirstResponder];
5764 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5765 if ((self = [super initWithBook:book]) != nil) {
5766 CGRect pageBounds = [book_ pageBounds];
5768 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5769 [self addSubview:transition_];
5771 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5773 [advanced_ setReusesTableCells:YES];
5774 [advanced_ setDataSource:self];
5775 [advanced_ reloadData];
5777 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5778 CGColor dimmed(space_, 0, 0, 0, 0.5);
5779 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5781 table_ = [[FilteredPackageTable alloc]
5785 filter:@selector(isUnfilteredAndSearchedForBy:)
5789 [table_ setShouldHideHeaderInShortLists:NO];
5790 [transition_ transition:0 toView:table_];
5799 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5806 [self bounds].size.width - area.origin.x - 18;
5808 area.size.height = [UISearchField defaultHeight];
5810 field_ = [[UISearchField alloc] initWithFrame:area];
5812 UIFont *font = [UIFont systemFontOfSize:16];
5813 [field_ setFont:font];
5815 [field_ setPlaceholder:@"Package Names & Descriptions"];
5816 [field_ setDelegate:self];
5818 [field_ setPaddingTop:5];
5820 UITextInputTraits *traits([field_ textInputTraits]);
5821 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5822 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5823 [traits setReturnKeyType:UIReturnKeySearch];
5825 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5827 accessory_ = [[UIView alloc] initWithFrame:accrect];
5828 [accessory_ addSubview:field_];
5830 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5831 [configure setShowPressFeedback:YES];
5832 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5833 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5834 [accessory_ addSubview:configure];*/
5836 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5837 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5843 LKAnimation *animation = [LKTransition animation];
5844 [animation setType:@"oglFlip"];
5845 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5846 [animation setFillMode:@"extended"];
5847 [animation setTransitionFlags:3];
5848 [animation setDuration:10];
5849 [animation setSpeed:0.35];
5850 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5851 [[transition_ _layer] addAnimation:animation forKey:0];
5852 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5853 flipped_ = !flipped_;
5857 - (void) configurePushed {
5858 [field_ resignFirstResponder];
5862 - (void) resetViewAnimated:(BOOL)animated {
5865 [table_ resetViewAnimated:animated];
5868 - (void) _reloadData {
5871 - (void) reloadData {
5874 [table_ setObject:[field_ text]];
5875 [table_ reloadData];
5876 [table_ resetCursor];
5879 - (UIView *) accessoryView {
5883 - (NSString *) title {
5887 - (NSString *) backButtonTitle {
5891 - (void) setDelegate:(id)delegate {
5892 [table_ setDelegate:delegate];
5893 [super setDelegate:delegate];
5899 @interface SettingsView : RVPage {
5900 _transient Database *database_;
5903 UIPreferencesTable *table_;
5904 _UISwitchSlider *subscribedSwitch_;
5905 _UISwitchSlider *ignoredSwitch_;
5906 UIPreferencesControlTableCell *subscribedCell_;
5907 UIPreferencesControlTableCell *ignoredCell_;
5910 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5914 @implementation SettingsView
5917 [table_ setDataSource:nil];
5920 if (package_ != nil)
5923 [subscribedSwitch_ release];
5924 [ignoredSwitch_ release];
5925 [subscribedCell_ release];
5926 [ignoredCell_ release];
5930 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5931 if (package_ == nil)
5937 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5938 if (package_ == nil)
5945 default: _assert(false);
5951 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5952 if (package_ == nil)
5959 default: _assert(false);
5965 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5966 if (package_ == nil)
5973 default: _assert(false);
5979 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5980 if (package_ == nil)
5983 _UISwitchSlider *slider([cell control]);
5984 BOOL value([slider value] != 0);
5985 NSMutableDictionary *metadata([package_ metadata]);
5988 if (NSNumber *number = [metadata objectForKey:key])
5989 before = [number boolValue];
5993 if (value != before) {
5994 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5996 [delegate_ updateData];
6000 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6001 [self onSomething:cell withKey:@"IsSubscribed"];
6004 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6005 [self onSomething:cell withKey:@"IsIgnored"];
6008 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6009 if (package_ == nil)
6013 case 0: switch (row) {
6015 return subscribedCell_;
6017 return ignoredCell_;
6018 default: _assert(false);
6021 case 1: switch (row) {
6023 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6024 [cell setShowSelection:NO];
6025 [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."];
6029 default: _assert(false);
6032 default: _assert(false);
6038 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6039 if ((self = [super initWithBook:book])) {
6040 database_ = database;
6041 name_ = [package retain];
6043 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6044 [self addSubview:table_];
6046 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6047 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6049 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6050 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6052 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6053 [subscribedCell_ setShowSelection:NO];
6054 [subscribedCell_ setTitle:@"Show All Changes"];
6055 [subscribedCell_ setControl:subscribedSwitch_];
6057 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6058 [ignoredCell_ setShowSelection:NO];
6059 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6060 [ignoredCell_ setControl:ignoredSwitch_];
6062 [table_ setDataSource:self];
6067 - (void) resetViewAnimated:(BOOL)animated {
6068 [table_ resetViewAnimated:animated];
6071 - (void) reloadData {
6072 if (package_ != nil)
6073 [package_ autorelease];
6074 package_ = [database_ packageWithName:name_];
6075 if (package_ != nil) {
6077 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6078 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6081 [table_ reloadData];
6084 - (NSString *) title {
6090 /* Signature View {{{ */
6091 @interface SignatureView : BrowserView {
6092 _transient Database *database_;
6096 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6100 @implementation SignatureView
6107 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6109 [super webView:sender didClearWindowObject:window forFrame:frame];
6112 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6113 if ((self = [super initWithBook:book]) != nil) {
6114 database_ = database;
6115 package_ = [package retain];
6120 - (void) resetViewAnimated:(BOOL)animated {
6123 - (void) reloadData {
6124 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6130 @interface Cydia : UIApplication <
6131 ConfirmationViewDelegate,
6132 ProgressViewDelegate,
6141 UIToolbar *buttonbar_;
6145 NSMutableArray *essential_;
6146 NSMutableArray *broken_;
6148 Database *database_;
6149 ProgressView *progress_;
6153 UIKeyboard *keyboard_;
6154 UIProgressHUD *hud_;
6156 SectionsView *sections_;
6157 ChangesView *changes_;
6158 ManageView *manage_;
6159 SearchView *search_;
6164 @implementation Cydia
6167 if ([broken_ count] != 0) {
6168 int count = [broken_ count];
6170 UIActionSheet *sheet = [[[UIActionSheet alloc]
6171 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6172 buttons:[NSArray arrayWithObjects:
6174 @"Ignore (Temporary)",
6176 defaultButtonIndex:0
6181 [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."];
6182 [sheet popupAlertAnimated:YES];
6183 } else if (!Ignored_ && [essential_ count] != 0) {
6184 int count = [essential_ count];
6186 UIActionSheet *sheet = [[[UIActionSheet alloc]
6187 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6188 buttons:[NSArray arrayWithObjects:
6189 @"Upgrade Essential",
6190 @"Complete Upgrade",
6191 @"Ignore (Temporary)",
6193 defaultButtonIndex:0
6198 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6199 [sheet popupAlertAnimated:YES];
6203 - (void) _reloadData {
6204 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6205 [hud setText:@"Reloading Data"];
6206 [overlay_ addSubview:hud];
6209 [database_ reloadData];
6213 [essential_ removeAllObjects];
6214 [broken_ removeAllObjects];
6216 NSArray *packages = [database_ packages];
6217 for (Package *package in packages) {
6219 [broken_ addObject:package];
6220 if ([package upgradableAndEssential:NO]) {
6221 if ([package essential])
6222 [essential_ addObject:package];
6228 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6229 [buttonbar_ setBadgeValue:badge forButton:3];
6230 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6231 [buttonbar_ setBadgeAnimated:YES forButton:3];
6232 [self setApplicationBadge:badge];
6234 [buttonbar_ setBadgeValue:nil forButton:3];
6235 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6236 [buttonbar_ setBadgeAnimated:NO forButton:3];
6237 [self removeApplicationBadge];
6242 // XXX: what is this line of code for?
6243 if ([packages count] == 0);
6244 else if (Loaded_) loaded:
6249 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6250 NSTimeInterval interval([update timeIntervalSinceNow]);
6251 if (interval <= 0 && interval > -600)
6259 [hud removeFromSuperview];*/
6262 - (void) _saveConfig {
6265 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6271 - (void) updateData {
6274 /* XXX: this is just stupid */
6275 if (tag_ != 2 && sections_ != nil)
6276 [sections_ reloadData];
6277 if (tag_ != 3 && changes_ != nil)
6278 [changes_ reloadData];
6279 if (tag_ != 5 && search_ != nil)
6280 [search_ reloadData];
6290 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6291 _assert(file != NULL);
6293 NSArray *keys = [Sources_ allKeys];
6295 for (int i(0), e([keys count]); i != e; ++i) {
6296 NSString *key = [keys objectAtIndex:i];
6297 NSDictionary *source = [Sources_ objectForKey:key];
6299 fprintf(file, "%s %s %s\n",
6300 [[source objectForKey:@"Type"] UTF8String],
6301 [[source objectForKey:@"URI"] UTF8String],
6302 [[source objectForKey:@"Distribution"] UTF8String]
6311 detachNewThreadSelector:@selector(update_)
6314 title:@"Updating Sources"
6318 - (void) reloadData {
6319 @synchronized (self) {
6320 if (confirm_ == nil)
6326 pkgProblemResolver *resolver = [database_ resolver];
6328 resolver->InstallProtect();
6329 if (!resolver->Resolve(true))
6333 - (void) popUpBook:(RVBook *)book {
6334 [underlay_ popSubview:book];
6337 - (CGRect) popUpBounds {
6338 return [underlay_ bounds];
6342 [database_ prepare];
6344 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6345 [confirm_ setDelegate:self];
6347 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6348 [page setDelegate:self];
6350 [confirm_ setPage:page];
6351 [self popUpBook:confirm_];
6354 - (void) installPackage:(Package *)package {
6355 @synchronized (self) {
6362 - (void) removePackage:(Package *)package {
6363 @synchronized (self) {
6370 - (void) distUpgrade {
6371 @synchronized (self) {
6372 [database_ upgrade];
6378 @synchronized (self) {
6380 if (confirm_ != nil) {
6388 [overlay_ removeFromSuperview];
6392 detachNewThreadSelector:@selector(perform)
6399 - (void) bootstrap_ {
6401 [database_ upgrade];
6402 [database_ prepare];
6403 [database_ perform];
6406 - (void) bootstrap {
6408 detachNewThreadSelector:@selector(bootstrap_)
6411 title:@"Bootstrap Install"
6415 - (void) progressViewIsComplete:(ProgressView *)progress {
6416 if (confirm_ != nil) {
6417 [underlay_ addSubview:overlay_];
6418 [confirm_ popFromSuperviewAnimated:NO];
6424 - (void) setPage:(RVPage *)page {
6425 [page resetViewAnimated:NO];
6426 [page setDelegate:self];
6427 [book_ setPage:page];
6430 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6431 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6432 [browser loadURL:url];
6436 - (void) _setHomePage {
6437 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6440 - (void) buttonBarItemTapped:(id)sender {
6441 unsigned tag = [sender tag];
6443 [book_ resetViewAnimated:YES];
6445 } else if (tag_ == 2 && tag != 2)
6446 [sections_ resetView];
6449 case 1: [self _setHomePage]; break;
6451 case 2: [self setPage:sections_]; break;
6452 case 3: [self setPage:changes_]; break;
6453 case 4: [self setPage:manage_]; break;
6454 case 5: [self setPage:search_]; break;
6456 default: _assert(false);
6462 - (void) applicationWillSuspend {
6464 [super applicationWillSuspend];
6467 - (void) askForSettings {
6468 UIActionSheet *role = [[[UIActionSheet alloc]
6469 initWithTitle:@"Who Are You?"
6470 buttons:[NSArray arrayWithObjects:
6471 @"User (Graphical Only)",
6472 @"Hacker (+ Command Line)",
6473 @"Developer (No Filters)",
6475 defaultButtonIndex:-1
6480 [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."];
6481 [role popupAlertAnimated:YES];
6486 [self setStatusBarShowsProgress:NO];
6489 [hud_ removeFromSuperview];
6493 pid_t pid = ExecFork();
6495 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6496 perror("launchctl stop");
6503 [self askForSettings];
6507 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6509 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6510 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6511 0, 0, screenrect.size.width, screenrect.size.height - 48
6512 ) database:database_];
6514 [book_ setDelegate:self];
6516 [overlay_ addSubview:book_];
6518 NSArray *buttonitems = [NSArray arrayWithObjects:
6519 [NSDictionary dictionaryWithObjectsAndKeys:
6520 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6521 @"home-up.png", kUIButtonBarButtonInfo,
6522 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6523 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6524 self, kUIButtonBarButtonTarget,
6525 @"Home", kUIButtonBarButtonTitle,
6526 @"0", kUIButtonBarButtonType,
6529 [NSDictionary dictionaryWithObjectsAndKeys:
6530 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6531 @"install-up.png", kUIButtonBarButtonInfo,
6532 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6533 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6534 self, kUIButtonBarButtonTarget,
6535 @"Sections", kUIButtonBarButtonTitle,
6536 @"0", kUIButtonBarButtonType,
6539 [NSDictionary dictionaryWithObjectsAndKeys:
6540 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6541 @"changes-up.png", kUIButtonBarButtonInfo,
6542 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6543 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6544 self, kUIButtonBarButtonTarget,
6545 @"Changes", kUIButtonBarButtonTitle,
6546 @"0", kUIButtonBarButtonType,
6549 [NSDictionary dictionaryWithObjectsAndKeys:
6550 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6551 @"manage-up.png", kUIButtonBarButtonInfo,
6552 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6553 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6554 self, kUIButtonBarButtonTarget,
6555 @"Manage", kUIButtonBarButtonTitle,
6556 @"0", kUIButtonBarButtonType,
6559 [NSDictionary dictionaryWithObjectsAndKeys:
6560 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6561 @"search-up.png", kUIButtonBarButtonInfo,
6562 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6563 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6564 self, kUIButtonBarButtonTarget,
6565 @"Search", kUIButtonBarButtonTitle,
6566 @"0", kUIButtonBarButtonType,
6570 buttonbar_ = [[UIToolbar alloc]
6572 withFrame:CGRectMake(
6573 0, screenrect.size.height - ButtonBarHeight_,
6574 screenrect.size.width, ButtonBarHeight_
6576 withItemList:buttonitems
6579 [buttonbar_ setDelegate:self];
6580 [buttonbar_ setBarStyle:1];
6581 [buttonbar_ setButtonBarTrackingMode:2];
6583 int buttons[5] = {1, 2, 3, 4, 5};
6584 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6585 [buttonbar_ showButtonGroup:0 withDuration:0];
6587 for (int i = 0; i != 5; ++i)
6588 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6589 i * 64 + 2, 1, 60, ButtonBarHeight_
6592 [buttonbar_ showSelectionForButton:1];
6593 [overlay_ addSubview:buttonbar_];
6595 [UIKeyboard initImplementationNow];
6596 CGSize keysize = [UIKeyboard defaultSize];
6597 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6598 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6599 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6600 [overlay_ addSubview:keyboard_];
6603 [underlay_ addSubview:overlay_];
6607 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6608 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6609 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6611 manage_ = (ManageView *) [[self
6612 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6613 withClass:[ManageView class]
6619 [self _setHomePage];
6622 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6623 NSString *context([sheet context]);
6625 if ([context isEqualToString:@"missing"])
6627 else if ([context isEqualToString:@"fixhalf"]) {
6630 @synchronized (self) {
6631 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6632 Package *broken = [broken_ objectAtIndex:i];
6635 NSString *id = [broken id];
6636 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6637 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6638 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6639 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6648 [broken_ removeAllObjects];
6657 } else if ([context isEqualToString:@"role"]) {
6659 case 1: Role_ = @"User"; break;
6660 case 2: Role_ = @"Hacker"; break;
6661 case 3: Role_ = @"Developer"; break;
6668 bool reset = Settings_ != nil;
6670 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6674 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6684 } else if ([context isEqualToString:@"upgrade"]) {
6687 @synchronized (self) {
6688 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6689 Package *essential = [essential_ objectAtIndex:i];
6690 [essential install];
6714 - (void) reorganize { _pooled
6715 system("/usr/libexec/cydia/free.sh");
6716 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6719 - (void) applicationSuspend:(__GSEvent *)event {
6720 if (hud_ == nil && ![progress_ isRunning])
6721 [super applicationSuspend:event];
6724 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6726 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6729 - (void) _setSuspended:(BOOL)value {
6731 [super _setSuspended:value];
6734 - (UIProgressHUD *) addProgressHUD {
6735 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6737 [underlay_ addSubview:hud];
6741 - (void) openMailToURL:(NSURL *)url {
6742 // XXX: this makes me sad
6744 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6746 [UIApp openURL:url];// asPanel:YES];
6750 - (void) clearFirstResponder {
6751 if (id responder = [window_ firstResponder])
6752 [responder resignFirstResponder];
6755 - (RVPage *) pageForPackage:(NSString *)name {
6756 if (Package *package = [database_ packageWithName:name]) {
6757 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6758 [view setPackage:package];
6761 UIActionSheet *sheet = [[[UIActionSheet alloc]
6762 initWithTitle:@"Cannot Locate Package"
6763 buttons:[NSArray arrayWithObjects:@"Close", nil]
6764 defaultButtonIndex:0
6769 [sheet setBodyText:[NSString stringWithFormat:
6770 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6773 [sheet popupAlertAnimated:YES];
6778 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6782 NSString *scheme([[url scheme] lowercaseString]);
6783 if (![scheme isEqualToString:@"cydia"])
6785 NSString *path([url absoluteString]);
6786 if ([path length] < 8)
6788 path = [path substringFromIndex:8];
6789 if (![path hasPrefix:@"/"])
6790 path = [@"/" stringByAppendingString:path];
6792 if ([path isEqualToString:@"/add-source"])
6793 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6794 else if ([path isEqualToString:@"/storage"])
6795 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6796 else if ([path isEqualToString:@"/sources"])
6797 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6798 else if ([path isEqualToString:@"/packages"])
6799 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6800 else if ([path hasPrefix:@"/url/"])
6801 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6802 else if ([path hasPrefix:@"/launch/"])
6803 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6804 else if ([path hasPrefix:@"/package-settings/"])
6805 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6806 else if ([path hasPrefix:@"/package-signature/"])
6807 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6808 else if ([path hasPrefix:@"/package/"])
6809 return [self pageForPackage:[path substringFromIndex:9]];
6810 else if ([path hasPrefix:@"/files/"]) {
6811 NSString *name = [path substringFromIndex:7];
6813 if (Package *package = [database_ packageWithName:name]) {
6814 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6815 [files setPackage:package];
6823 - (void) applicationOpenURL:(NSURL *)url {
6824 [super applicationOpenURL:url];
6826 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6827 [self setPage:page];
6828 [buttonbar_ showSelectionForButton:tag];
6833 - (void) applicationDidFinishLaunching:(id)unused {
6834 Font12_ = [[UIFont systemFontOfSize:12] retain];
6835 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6836 Font14_ = [[UIFont systemFontOfSize:14] retain];
6837 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6838 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6840 _assert(pkgInitConfig(*_config));
6841 _assert(pkgInitSystem(*_config, _system));
6845 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6846 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6848 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6850 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6851 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6853 [window_ orderFront:self];
6854 [window_ makeKey:self];
6855 [window_ setHidden:NO];
6857 database_ = [Database sharedInstance];
6858 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6859 [database_ setDelegate:progress_];
6860 [window_ setContentView:progress_];
6862 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6863 [progress_ setContentView:underlay_];
6865 [progress_ resetView];
6868 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6869 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6870 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6871 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6872 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6873 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6874 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6876 [self setIdleTimerDisabled:YES];
6878 hud_ = [self addProgressHUD];
6879 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6881 [self setStatusBarShowsProgress:YES];
6884 detachNewThreadSelector:@selector(reorganize)
6892 - (void) showKeyboard:(BOOL)show {
6893 CGSize keysize = [UIKeyboard defaultSize];
6894 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6895 CGRect keyup = keydown;
6896 keyup.origin.y -= keysize.height;
6898 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6899 [animation setSignificantRectFields:2];
6902 [animation setStartFrame:keydown];
6903 [animation setEndFrame:keyup];
6904 [keyboard_ activate];
6906 [animation setStartFrame:keyup];
6907 [animation setEndFrame:keydown];
6908 [keyboard_ deactivate];
6911 [[UIAnimator sharedAnimator]
6912 addAnimations:[NSArray arrayWithObjects:animation, nil]
6913 withDuration:KeyboardTime_
6918 - (void) slideUp:(UIActionSheet *)alert {
6920 [alert presentSheetFromButtonBar:buttonbar_];
6922 [alert presentSheetInView:overlay_];
6927 void AddPreferences(NSString *plist) { _pooled
6928 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6929 _assert(settings != NULL);
6930 NSMutableArray *items = [settings objectForKey:@"items"];
6934 for (size_t i(0); i != [items count]; ++i) {
6935 NSMutableDictionary *item([items objectAtIndex:i]);
6936 NSString *label = [item objectForKey:@"label"];
6937 if (label != nil && [label isEqualToString:@"Cydia"]) {
6944 for (size_t i(0); i != [items count]; ++i) {
6945 NSDictionary *item([items objectAtIndex:i]);
6946 NSString *label = [item objectForKey:@"label"];
6947 if (label != nil && [label isEqualToString:@"General"]) {
6948 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6949 @"CydiaSettings", @"bundle",
6950 @"PSLinkCell", @"cell",
6951 [NSNumber numberWithBool:YES], @"hasIcon",
6952 [NSNumber numberWithBool:YES], @"isController",
6954 nil] atIndex:(i + 1)];
6960 _assert([settings writeToFile:plist atomically:YES] == YES);
6965 id Alloc_(id self, SEL selector) {
6966 id object = alloc_(self, selector);
6967 lprintf("[%s]A-%p\n", self->isa->name, object);
6972 id Dealloc_(id self, SEL selector) {
6973 id object = dealloc_(self, selector);
6974 lprintf("[%s]D-%p\n", self->isa->name, object);
6978 int main(int argc, char *argv[]) { _pooled
6980 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6982 bool substrate(false);
6988 for (int argi(1); argi != argc; ++argi)
6989 if (strcmp(argv[argi], "--") == 0) {
6991 argv[argi] = argv[0];
6997 for (int argi(1); argi != arge; ++argi)
6998 if (strcmp(args[argi], "--bootstrap") == 0)
7000 else if (strcmp(args[argi], "--substrate") == 0)
7003 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7006 App_ = [[NSBundle mainBundle] bundlePath];
7007 Home_ = NSHomeDirectory();
7008 Locale_ = CFLocaleCopyCurrent();
7011 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7012 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7013 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7014 Sounds_Keyboard_ = [keyboard boolValue];
7020 #if 1 /* XXX: this costs 1.4s of startup performance */
7021 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7022 _assert(errno == ENOENT);
7023 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7024 _assert(errno == ENOENT);
7027 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7028 alloc_ = alloc->method_imp;
7029 alloc->method_imp = (IMP) &Alloc_;*/
7031 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7032 dealloc_ = dealloc->method_imp;
7033 dealloc->method_imp = (IMP) &Dealloc_;*/
7038 size = sizeof(maxproc);
7039 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7040 perror("sysctlbyname(\"kern.maxproc\", ?)");
7041 else if (maxproc < 64) {
7043 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7044 perror("sysctlbyname(\"kern.maxproc\", #)");
7047 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7048 char *machine = new char[size];
7049 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7050 perror("sysctlbyname(\"hw.machine\", ?)");
7054 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7056 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7057 Build_ = [system objectForKey:@"ProductBuildVersion"];
7059 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7060 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7062 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7063 Indices_ = [[NSMutableDictionary alloc] init];*/
7065 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7066 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/rating/@", @"Rating",
7067 //@"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7071 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7074 if (Metadata_ == NULL)
7075 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7077 Settings_ = [Metadata_ objectForKey:@"Settings"];
7079 Packages_ = [Metadata_ objectForKey:@"Packages"];
7080 Sections_ = [Metadata_ objectForKey:@"Sections"];
7081 Sources_ = [Metadata_ objectForKey:@"Sources"];
7084 if (Settings_ != nil)
7085 Role_ = [Settings_ objectForKey:@"Role"];
7087 if (Packages_ == nil) {
7088 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7089 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7092 if (Sections_ == nil) {
7093 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7094 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7097 if (Sources_ == nil) {
7098 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7099 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7103 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7106 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7107 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7108 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7109 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7111 if (access("/User", F_OK) != 0) {
7113 system("/usr/libexec/cydia/firmware.sh");
7117 _assert([[NSFileManager defaultManager]
7118 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7119 withIntermediateDirectories:YES
7124 space_ = CGColorSpaceCreateDeviceRGB();
7126 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7127 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7128 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7129 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7130 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7131 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7133 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7135 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7137 UIApplicationUseLegacyEvents(YES);
7138 UIKeyboardDisableAutomaticAppearance();
7141 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7143 CGColorSpaceRelease(space_);