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 _transient NSMutableDictionary *Settings_;
642 static _transient NSString *Role_;
643 static _transient NSMutableDictionary *Packages_;
644 static _transient NSMutableDictionary *Sections_;
645 static _transient NSMutableDictionary *Sources_;
646 static bool Changed_;
650 static NSMutableArray *Documents_;
653 NSString *GetLastUpdate() {
654 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
657 return @"Never or Unknown";
659 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
660 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
662 CFRelease(formatter);
664 return [(NSString *) formatted autorelease];
667 /* Display Helpers {{{ */
668 inline float Interpolate(float begin, float end, float fraction) {
669 return (end - begin) * fraction + begin;
672 NSString *SizeString(double size) {
673 bool negative = size < 0;
678 while (size > 1024) {
683 static const char *powers_[] = {"B", "kB", "MB", "GB"};
685 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
688 NSString *StripVersion(NSString *version) {
689 NSRange colon = [version rangeOfString:@":"];
690 if (colon.location != NSNotFound)
691 version = [version substringFromIndex:(colon.location + 1)];
695 NSString *Simplify(NSString *title) {
696 const char *data = [title UTF8String];
697 size_t size = [title length];
699 static Pcre square_r("^\\[(.*)\\]$");
700 if (square_r(data, size))
701 return Simplify(square_r[1]);
703 static Pcre paren_r("^\\((.*)\\)$");
704 if (paren_r(data, size))
705 return Simplify(paren_r[1]);
707 static Pcre title_r("^(.*?) \\(.*\\)$");
708 if (title_r(data, size))
709 return Simplify(title_r[1]);
715 bool isSectionVisible(NSString *section) {
716 NSDictionary *metadata = [Sections_ objectForKey:section];
717 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
718 return hidden == nil || ![hidden boolValue];
721 /* Delegate Prototypes {{{ */
725 @interface NSObject (ProgressDelegate)
728 @implementation NSObject(ProgressDelegate)
730 - (void) _setProgressError:(NSArray *)args {
731 [self performSelector:@selector(setProgressError:forPackage:)
732 withObject:[args objectAtIndex:0]
733 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
739 @protocol ProgressDelegate
740 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
741 - (void) setProgressTitle:(NSString *)title;
742 - (void) setProgressPercent:(float)percent;
743 - (void) startProgress;
744 - (void) addProgressOutput:(NSString *)output;
745 - (bool) isCancelling:(size_t)received;
748 @protocol ConfigurationDelegate
749 - (void) repairWithSelector:(SEL)selector;
750 - (void) setConfigurationData:(NSString *)data;
753 @protocol CydiaDelegate
754 - (void) installPackage:(Package *)package;
755 - (void) removePackage:(Package *)package;
756 - (void) slideUp:(UIActionSheet *)alert;
757 - (void) distUpgrade;
760 - (void) askForSettings;
761 - (UIProgressHUD *) addProgressHUD;
762 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
763 - (RVPage *) pageForPackage:(NSString *)name;
764 - (void) openMailToURL:(NSURL *)url;
765 - (void) clearFirstResponder;
769 /* Status Delegation {{{ */
771 public pkgAcquireStatus
774 _transient NSObject<ProgressDelegate> *delegate_;
782 void setDelegate(id delegate) {
783 delegate_ = delegate;
786 virtual bool MediaChange(std::string media, std::string drive) {
790 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
793 virtual void Fetch(pkgAcquire::ItemDesc &item) {
794 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
795 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
798 virtual void Done(pkgAcquire::ItemDesc &item) {
801 virtual void Fail(pkgAcquire::ItemDesc &item) {
803 item.Owner->Status == pkgAcquire::Item::StatIdle ||
804 item.Owner->Status == pkgAcquire::Item::StatDone
808 std::string &error(item.Owner->ErrorText);
812 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
813 NSArray *fields([description componentsSeparatedByString:@" "]);
814 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
816 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
817 withObject:[NSArray arrayWithObjects:
818 [NSString stringWithUTF8String:error.c_str()],
825 virtual bool Pulse(pkgAcquire *Owner) {
826 bool value = pkgAcquireStatus::Pulse(Owner);
829 double(CurrentBytes + CurrentItems) /
830 double(TotalBytes + TotalItems)
833 [delegate_ setProgressPercent:percent];
834 return [delegate_ isCancelling:CurrentBytes] ? false : value;
837 virtual void Start() {
838 [delegate_ startProgress];
841 virtual void Stop() {
845 /* Progress Delegation {{{ */
850 _transient id<ProgressDelegate> delegate_;
853 virtual void Update() {
854 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
855 [delegate_ setProgressPercent:(Percent / 100)];
864 void setDelegate(id delegate) {
865 delegate_ = delegate;
868 virtual void Done() {
869 [delegate_ setProgressPercent:1];
874 /* Database Interface {{{ */
875 @interface Database : NSObject {
877 pkgDepCache::Policy *policy_;
878 pkgRecords *records_;
879 pkgProblemResolver *resolver_;
880 pkgAcquire *fetcher_;
882 SPtr<pkgPackageManager> manager_;
883 pkgSourceList *list_;
885 NSMutableDictionary *sources_;
886 NSMutableArray *packages_;
888 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
897 + (Database *) sharedInstance;
899 - (void) _readCydia:(NSNumber *)fd;
900 - (void) _readStatus:(NSNumber *)fd;
901 - (void) _readOutput:(NSNumber *)fd;
905 - (Package *) packageWithName:(NSString *)name;
907 - (pkgCacheFile &) cache;
908 - (pkgDepCache::Policy *) policy;
909 - (pkgRecords *) records;
910 - (pkgProblemResolver *) resolver;
911 - (pkgAcquire &) fetcher;
912 - (pkgSourceList &) list;
913 - (NSArray *) packages;
914 - (NSArray *) sources;
923 - (void) updateWithStatus:(Status &)status;
925 - (void) setDelegate:(id)delegate;
926 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
930 /* Source Class {{{ */
931 @interface Source : NSObject {
932 NSString *description_;
937 NSString *distribution_;
941 NSString *defaultIcon_;
943 NSDictionary *record_;
947 - (Source *) initWithMetaIndex:(metaIndex *)index;
949 - (NSComparisonResult) compareByNameAndType:(Source *)source;
951 - (NSDictionary *) record;
955 - (NSString *) distribution;
961 - (NSString *) description;
962 - (NSString *) label;
963 - (NSString *) origin;
964 - (NSString *) version;
966 - (NSString *) defaultIcon;
970 @implementation Source
972 #define _clear(field) \
979 _clear(distribution_)
995 + (NSArray *) _attributeKeys {
996 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
999 - (NSArray *) attributeKeys {
1000 return [[self class] _attributeKeys];
1003 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1004 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1007 - (void) setMetaIndex:(metaIndex *)index {
1010 trusted_ = index->IsTrusted();
1012 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1013 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1014 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1016 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1017 if (dindex != NULL) {
1018 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1020 while (std::getline(release, line)) {
1021 std::string::size_type colon(line.find(':'));
1022 if (colon == std::string::npos)
1025 std::string name(line.substr(0, colon));
1026 std::string value(line.substr(colon + 1));
1027 while (!value.empty() && value[0] == ' ')
1028 value = value.substr(1);
1030 if (name == "Default-Icon")
1031 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1032 else if (name == "Description")
1033 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1034 else if (name == "Label")
1035 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1036 else if (name == "Origin")
1037 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1038 else if (name == "Version")
1039 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1043 record_ = [Sources_ objectForKey:[self key]];
1045 record_ = [record_ retain];
1048 - (Source *) initWithMetaIndex:(metaIndex *)index {
1049 if ((self = [super init]) != nil) {
1050 [self setMetaIndex:index];
1054 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1055 NSDictionary *lhr = [self record];
1056 NSDictionary *rhr = [source record];
1059 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1061 NSString *lhs = [self name];
1062 NSString *rhs = [source name];
1064 if ([lhs length] != 0 && [rhs length] != 0) {
1065 unichar lhc = [lhs characterAtIndex:0];
1066 unichar rhc = [rhs characterAtIndex:0];
1068 if (isalpha(lhc) && !isalpha(rhc))
1069 return NSOrderedAscending;
1070 else if (!isalpha(lhc) && isalpha(rhc))
1071 return NSOrderedDescending;
1074 return [lhs compare:rhs options:LaxCompareOptions_];
1077 - (NSDictionary *) record {
1085 - (NSString *) uri {
1089 - (NSString *) distribution {
1090 return distribution_;
1093 - (NSString *) type {
1097 - (NSString *) key {
1098 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1101 - (NSString *) host {
1102 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1105 - (NSString *) name {
1106 return origin_ == nil ? [self host] : origin_;
1109 - (NSString *) description {
1110 return description_;
1113 - (NSString *) label {
1114 return label_ == nil ? [self host] : label_;
1117 - (NSString *) origin {
1121 - (NSString *) version {
1125 - (NSString *) defaultIcon {
1126 return defaultIcon_;
1131 /* Relationship Class {{{ */
1132 @interface Relationship : NSObject {
1137 - (NSString *) type;
1139 - (NSString *) name;
1143 @implementation Relationship
1151 - (NSString *) type {
1159 - (NSString *) name {
1166 /* Package Class {{{ */
1167 @interface Package : NSObject {
1168 pkgCache::PkgIterator iterator_;
1169 _transient Database *database_;
1170 pkgCache::VerIterator version_;
1171 pkgCache::VerFileIterator file_;
1179 NSString *installed_;
1185 NSString *depiction_;
1186 NSString *homepage_;
1192 NSArray *relationships_;
1195 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1196 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1198 - (pkgCache::PkgIterator) iterator;
1200 - (NSString *) section;
1201 - (NSString *) simpleSection;
1205 - (Address *) maintainer;
1207 - (NSString *) description;
1208 - (NSString *) index;
1210 - (NSMutableDictionary *) metadata;
1212 - (BOOL) subscribed;
1215 - (NSString *) latest;
1216 - (NSString *) installed;
1219 - (BOOL) upgradableAndEssential:(BOOL)essential;
1222 - (BOOL) unfiltered;
1226 - (BOOL) halfConfigured;
1227 - (BOOL) halfInstalled;
1229 - (NSString *) mode;
1232 - (NSString *) name;
1233 - (NSString *) tagline;
1235 - (NSString *) homepage;
1236 - (NSString *) depiction;
1237 - (Address *) author;
1239 - (NSArray *) files;
1240 - (NSArray *) relationships;
1241 - (NSArray *) warnings;
1242 - (NSArray *) applications;
1244 - (Source *) source;
1245 - (NSString *) role;
1247 - (BOOL) matches:(NSString *)text;
1249 - (bool) hasSupportingRole;
1250 - (BOOL) hasTag:(NSString *)tag;
1251 - (NSString *) primaryPurpose;
1252 - (NSArray *) purposes;
1254 - (NSComparisonResult) compareByName:(Package *)package;
1255 - (NSComparisonResult) compareBySection:(Package *)package;
1257 - (uint32_t) compareForChanges;
1262 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1263 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1264 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1265 - (NSNumber *) isVisibleInSource:(Source *)source;
1269 @implementation Package
1275 if (section_ != nil)
1279 if (installed_ != nil)
1280 [installed_ release];
1288 if (depiction_ != nil)
1289 [depiction_ release];
1290 if (homepage_ != nil)
1291 [homepage_ release];
1292 if (sponsor_ != nil)
1301 if (relationships_ != nil)
1302 [relationships_ release];
1307 + (NSArray *) _attributeKeys {
1308 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1311 - (NSArray *) attributeKeys {
1312 return [[self class] _attributeKeys];
1315 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1316 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1319 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1320 if ((self = [super init]) != nil) {
1321 iterator_ = iterator;
1322 database_ = database;
1324 version_ = [database_ policy]->GetCandidateVer(iterator_);
1325 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1326 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1328 pkgCache::VerIterator current = iterator_.CurrentVer();
1329 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1330 installed_ = [StripVersion(installed) retain];
1332 if (!version_.end())
1333 file_ = version_.FileList();
1335 pkgCache &cache([database_ cache]);
1336 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1339 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1342 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1344 const char *begin, *end;
1345 parser->GetRec(begin, end);
1347 NSString *website(nil);
1348 NSString *sponsor(nil);
1349 NSString *author(nil);
1358 {"depiction", &depiction_},
1359 {"homepage", &homepage_},
1360 {"website", &website},
1361 {"sponsor", &sponsor},
1362 {"author", &author},
1366 while (begin != end)
1367 if (*begin == '\n') {
1370 } else if (isblank(*begin)) next: {
1371 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1374 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1375 const char *name(begin);
1376 size_t size(colon - begin);
1378 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1381 const char *stop(begin == NULL ? end : begin);
1382 while (stop[-1] == '\r')
1384 while (++colon != stop && isblank(*colon));
1386 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1387 if (strncasecmp(names[i].name_, name, size) == 0) {
1388 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1389 *names[i].value_ = value;
1400 name_ = [name_ retain];
1401 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1403 icon_ = [icon_ retain];
1404 if (depiction_ != nil)
1405 depiction_ = [depiction_ retain];
1406 if (homepage_ == nil)
1407 homepage_ = website;
1408 if ([homepage_ isEqualToString:depiction_])
1410 if (homepage_ != nil)
1411 homepage_ = [homepage_ retain];
1413 sponsor_ = [[Address addressWithString:sponsor] retain];
1415 author_ = [[Address addressWithString:author] retain];
1417 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1421 for (int i(0), e([tags_ count]); i != e; ++i) {
1422 NSString *tag = [tags_ objectAtIndex:i];
1423 if ([tag hasPrefix:@"role::"]) {
1424 role_ = [[tag substringFromIndex:6] retain];
1429 NSString *solid(latest == nil ? installed : latest);
1430 bool changed(false);
1432 NSString *key([id_ lowercaseString]);
1434 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1435 if (metadata == nil) {
1436 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1441 [metadata setObject:solid forKey:@"LastVersion"];
1444 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1445 NSDate *last([metadata objectForKey:@"LastSeen"]);
1446 NSString *version([metadata objectForKey:@"LastVersion"]);
1449 first = last == nil ? now_ : last;
1450 [metadata setObject:first forKey:@"FirstSeen"];
1455 if (version == nil) {
1456 [metadata setObject:solid forKey:@"LastVersion"];
1458 } else if (![version isEqualToString:solid]) {
1459 [metadata setObject:solid forKey:@"LastVersion"];
1461 [metadata setObject:last forKey:@"LastSeen"];
1467 [Packages_ setObject:metadata forKey:key];
1473 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1474 return [[[Package alloc]
1475 initWithIterator:iterator
1480 - (pkgCache::PkgIterator) iterator {
1484 - (NSString *) section {
1485 if (section_ != nil)
1488 const char *section = iterator_.Section();
1489 if (section == NULL)
1492 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1495 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1496 if (NSString *rename = [value objectForKey:@"Rename"]) {
1501 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1505 - (NSString *) simpleSection {
1506 if (NSString *section = [self section])
1507 return Simplify(section);
1512 - (NSString *) uri {
1515 pkgIndexFile *index;
1516 pkgCache::PkgFileIterator file(file_.File());
1517 if (![database_ list].FindIndex(file, index))
1519 return [NSString stringWithUTF8String:iterator_->Path];
1520 //return [NSString stringWithUTF8String:file.Site()];
1521 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1525 - (Address *) maintainer {
1528 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1529 const std::string &maintainer(parser->Maintainer());
1530 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1534 return version_.end() ? 0 : version_->InstalledSize;
1537 - (NSString *) description {
1540 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1541 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1543 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1544 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1545 if ([lines count] < 2)
1548 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1549 for (size_t i(1); i != [lines count]; ++i) {
1550 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1551 [trimmed addObject:trim];
1554 return [trimmed componentsJoinedByString:@"\n"];
1557 - (NSString *) index {
1558 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1559 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1562 - (NSMutableDictionary *) metadata {
1563 return [Packages_ objectForKey:[id_ lowercaseString]];
1567 NSDictionary *metadata([self metadata]);
1568 if ([self subscribed])
1569 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1571 return [metadata objectForKey:@"FirstSeen"];
1574 - (BOOL) subscribed {
1575 NSDictionary *metadata([self metadata]);
1576 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1577 return [subscribed boolValue];
1583 NSDictionary *metadata([self metadata]);
1584 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1585 return [ignored boolValue];
1590 - (NSString *) latest {
1594 - (NSString *) installed {
1599 return !version_.end();
1602 - (BOOL) upgradableAndEssential:(BOOL)essential {
1603 pkgCache::VerIterator current = iterator_.CurrentVer();
1607 value = essential && [self essential];
1609 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1613 - (BOOL) essential {
1614 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1618 return [database_ cache][iterator_].InstBroken();
1621 - (BOOL) unfiltered {
1622 NSString *section = [self section];
1623 return section == nil || isSectionVisible(section);
1627 return [self hasSupportingRole] && [self unfiltered];
1631 unsigned char current = iterator_->CurrentState;
1632 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1635 - (BOOL) halfConfigured {
1636 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1639 - (BOOL) halfInstalled {
1640 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1644 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1645 return state.Mode != pkgDepCache::ModeKeep;
1648 - (NSString *) mode {
1649 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1651 switch (state.Mode) {
1652 case pkgDepCache::ModeDelete:
1653 if ((state.iFlags & pkgDepCache::Purge) != 0)
1657 case pkgDepCache::ModeKeep:
1658 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1662 case pkgDepCache::ModeInstall:
1663 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1664 return @"Reinstall";
1665 else switch (state.Status) {
1667 return @"Downgrade";
1673 return @"New Install";
1686 - (NSString *) name {
1687 return name_ == nil ? id_ : name_;
1690 - (NSString *) tagline {
1694 - (UIImage *) icon {
1695 NSString *section = [self simpleSection];
1699 if ([icon_ hasPrefix:@"file:///"])
1700 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1701 if (icon == nil) if (section != nil)
1702 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1703 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1704 if ([dicon hasPrefix:@"file:///"])
1705 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1707 icon = [UIImage applicationImageNamed:@"unknown.png"];
1711 - (NSString *) homepage {
1715 - (NSString *) depiction {
1719 - (Address *) sponsor {
1723 - (Address *) author {
1727 - (NSArray *) files {
1728 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1729 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1732 fin.open([path UTF8String]);
1737 while (std::getline(fin, line))
1738 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1743 - (NSArray *) relationships {
1744 return relationships_;
1747 - (NSArray *) warnings {
1748 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1749 const char *name(iterator_.Name());
1751 size_t length(strlen(name));
1752 if (length < 2) invalid:
1753 [warnings addObject:@"illegal package identifier"];
1754 else for (size_t i(0); i != length; ++i)
1756 /* XXX: technically this is not allowed */
1757 (name[i] < 'A' || name[i] > 'Z') &&
1758 (name[i] < 'a' || name[i] > 'z') &&
1759 (name[i] < '0' || name[i] > '9') &&
1760 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1763 if (strcmp(name, "cydia") != 0) {
1765 bool _private = false;
1768 bool repository = [[self section] isEqualToString:@"Repositories"];
1770 if (NSArray *files = [self files])
1771 for (NSString *file in files)
1772 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1774 else if (!_private && [file isEqualToString:@"/private"])
1776 else if (!stash && [file isEqualToString:@"/var/stash"])
1779 /* XXX: this is not sensitive enough. only some folders are valid. */
1780 if (cydia && !repository)
1781 [warnings addObject:@"files installed into Cydia.app"];
1783 [warnings addObject:@"files installed with /private/*"];
1785 [warnings addObject:@"files installed to /var/stash"];
1788 return [warnings count] == 0 ? nil : warnings;
1791 - (NSArray *) applications {
1792 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1794 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1796 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1797 if (NSArray *files = [self files])
1798 for (NSString *file in files)
1799 if (application_r(file)) {
1800 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1801 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1802 if ([id isEqualToString:me])
1805 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1807 display = application_r[1];
1809 NSString *bundle([file stringByDeletingLastPathComponent]);
1810 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1811 if (icon == nil || [icon length] == 0)
1813 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1815 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1816 [applications addObject:application];
1818 [application addObject:id];
1819 [application addObject:display];
1820 [application addObject:url];
1823 return [applications count] == 0 ? nil : applications;
1826 - (Source *) source {
1828 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1835 - (NSString *) role {
1839 - (BOOL) matches:(NSString *)text {
1845 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1846 if (range.location != NSNotFound)
1849 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1850 if (range.location != NSNotFound)
1853 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1854 if (range.location != NSNotFound)
1860 - (bool) hasSupportingRole {
1863 if ([role_ isEqualToString:@"enduser"])
1865 if ([Role_ isEqualToString:@"User"])
1867 if ([role_ isEqualToString:@"hacker"])
1869 if ([Role_ isEqualToString:@"Hacker"])
1871 if ([role_ isEqualToString:@"developer"])
1873 if ([Role_ isEqualToString:@"Developer"])
1878 - (BOOL) hasTag:(NSString *)tag {
1879 return tags_ == nil ? NO : [tags_ containsObject:tag];
1882 - (NSString *) primaryPurpose {
1883 for (NSString *tag in tags_)
1884 if ([tag hasPrefix:@"purpose::"])
1885 return [tag substringFromIndex:9];
1889 - (NSArray *) purposes {
1890 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1891 for (NSString *tag in tags_)
1892 if ([tag hasPrefix:@"purpose::"])
1893 [purposes addObject:[tag substringFromIndex:9]];
1894 return [purposes count] == 0 ? nil : purposes;
1897 - (NSComparisonResult) compareByName:(Package *)package {
1898 NSString *lhs = [self name];
1899 NSString *rhs = [package name];
1901 if ([lhs length] != 0 && [rhs length] != 0) {
1902 unichar lhc = [lhs characterAtIndex:0];
1903 unichar rhc = [rhs characterAtIndex:0];
1905 if (isalpha(lhc) && !isalpha(rhc))
1906 return NSOrderedAscending;
1907 else if (!isalpha(lhc) && isalpha(rhc))
1908 return NSOrderedDescending;
1911 return [lhs compare:rhs options:LaxCompareOptions_];
1914 - (NSComparisonResult) compareBySection:(Package *)package {
1915 NSString *lhs = [self section];
1916 NSString *rhs = [package section];
1918 if (lhs == NULL && rhs != NULL)
1919 return NSOrderedAscending;
1920 else if (lhs != NULL && rhs == NULL)
1921 return NSOrderedDescending;
1922 else if (lhs != NULL && rhs != NULL) {
1923 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1924 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1927 return NSOrderedSame;
1930 - (uint32_t) compareForChanges {
1935 uint32_t timestamp : 30;
1936 uint32_t ignored : 1;
1937 uint32_t upgradable : 1;
1941 bool upgradable([self upgradableAndEssential:YES]);
1942 value.bits.upgradable = upgradable ? 1 : 0;
1945 value.bits.timestamp = 0;
1946 value.bits.ignored = [self ignored] ? 0 : 1;
1947 value.bits.upgradable = 1;
1949 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1950 value.bits.ignored = 0;
1951 value.bits.upgradable = 0;
1954 return _not(uint32_t) - value.key;
1958 pkgProblemResolver *resolver = [database_ resolver];
1959 resolver->Clear(iterator_);
1960 resolver->Protect(iterator_);
1961 pkgCacheFile &cache([database_ cache]);
1962 cache->MarkInstall(iterator_, false);
1963 pkgDepCache::StateCache &state((*cache)[iterator_]);
1964 if (!state.Install())
1965 cache->SetReInstall(iterator_, true);
1969 pkgProblemResolver *resolver = [database_ resolver];
1970 resolver->Clear(iterator_);
1971 resolver->Protect(iterator_);
1972 resolver->Remove(iterator_);
1973 [database_ cache]->MarkDelete(iterator_, true);
1976 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1977 return [NSNumber numberWithBool:(
1978 [self unfiltered] && [self matches:search]
1982 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1983 return [NSNumber numberWithBool:(
1984 (![number boolValue] || [self visible]) && [self installed] != nil
1988 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1989 NSString *section = [self section];
1991 return [NSNumber numberWithBool:(
1993 [self installed] == nil && (
1995 section == nil && [name length] == 0 ||
1996 [name isEqualToString:section]
2001 - (NSNumber *) isVisibleInSource:(Source *)source {
2002 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2007 /* Section Class {{{ */
2008 @interface Section : NSObject {
2014 - (NSComparisonResult) compareByName:(Section *)section;
2015 - (Section *) initWithName:(NSString *)name;
2016 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2017 - (NSString *) name;
2020 - (void) addToCount;
2024 @implementation Section
2031 - (NSComparisonResult) compareByName:(Section *)section {
2032 NSString *lhs = [self name];
2033 NSString *rhs = [section name];
2035 if ([lhs length] != 0 && [rhs length] != 0) {
2036 unichar lhc = [lhs characterAtIndex:0];
2037 unichar rhc = [rhs characterAtIndex:0];
2039 if (isalpha(lhc) && !isalpha(rhc))
2040 return NSOrderedAscending;
2041 else if (!isalpha(lhc) && isalpha(rhc))
2042 return NSOrderedDescending;
2045 return [lhs compare:rhs options:LaxCompareOptions_];
2048 - (Section *) initWithName:(NSString *)name {
2049 return [self initWithName:name row:0];
2052 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2053 if ((self = [super init]) != nil) {
2054 name_ = [name retain];
2059 - (NSString *) name {
2071 - (void) addToCount {
2079 static NSArray *Finishes_;
2081 /* Database Implementation {{{ */
2082 @implementation Database
2084 + (Database *) sharedInstance {
2085 static Database *instance;
2086 if (instance == nil)
2087 instance = [[Database alloc] init];
2096 - (void) _readCydia:(NSNumber *)fd { _pooled
2097 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2098 std::istream is(&ib);
2101 static Pcre finish_r("^finish:([^:]*)$");
2103 while (std::getline(is, line)) {
2104 const char *data(line.c_str());
2105 size_t size = line.size();
2106 lprintf("C:%s\n", data);
2108 if (finish_r(data, size)) {
2109 NSString *finish = finish_r[1];
2110 int index = [Finishes_ indexOfObject:finish];
2111 if (index != INT_MAX && index > Finish_)
2119 - (void) _readStatus:(NSNumber *)fd { _pooled
2120 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2121 std::istream is(&ib);
2124 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2125 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2127 while (std::getline(is, line)) {
2128 const char *data(line.c_str());
2129 size_t size = line.size();
2130 lprintf("S:%s\n", data);
2132 if (conffile_r(data, size)) {
2133 [delegate_ setConfigurationData:conffile_r[1]];
2134 } else if (strncmp(data, "status: ", 8) == 0) {
2135 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2136 [delegate_ setProgressTitle:string];
2137 } else if (pmstatus_r(data, size)) {
2138 std::string type([pmstatus_r[1] UTF8String]);
2139 NSString *id = pmstatus_r[2];
2141 float percent([pmstatus_r[3] floatValue]);
2142 [delegate_ setProgressPercent:(percent / 100)];
2144 NSString *string = pmstatus_r[4];
2146 if (type == "pmerror")
2147 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2148 withObject:[NSArray arrayWithObjects:string, id, nil]
2151 else if (type == "pmstatus") {
2152 [delegate_ setProgressTitle:string];
2153 } else if (type == "pmconffile")
2154 [delegate_ setConfigurationData:string];
2155 else _assert(false);
2156 } else _assert(false);
2162 - (void) _readOutput:(NSNumber *)fd { _pooled
2163 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2164 std::istream is(&ib);
2167 while (std::getline(is, line)) {
2168 lprintf("O:%s\n", line.c_str());
2169 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2179 - (Package *) packageWithName:(NSString *)name {
2180 if (static_cast<pkgDepCache *>(cache_) == NULL)
2182 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2183 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2186 - (Database *) init {
2187 if ((self = [super init]) != nil) {
2194 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2195 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2199 _assert(pipe(fds) != -1);
2202 _config->Set("APT::Keep-Fds::", cydiafd_);
2203 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2206 detachNewThreadSelector:@selector(_readCydia:)
2208 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2211 _assert(pipe(fds) != -1);
2215 detachNewThreadSelector:@selector(_readStatus:)
2217 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2220 _assert(pipe(fds) != -1);
2221 _assert(dup2(fds[0], 0) != -1);
2222 _assert(close(fds[0]) != -1);
2224 input_ = fdopen(fds[1], "a");
2226 _assert(pipe(fds) != -1);
2227 _assert(dup2(fds[1], 1) != -1);
2228 _assert(close(fds[1]) != -1);
2231 detachNewThreadSelector:@selector(_readOutput:)
2233 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2238 - (pkgCacheFile &) cache {
2242 - (pkgDepCache::Policy *) policy {
2246 - (pkgRecords *) records {
2250 - (pkgProblemResolver *) resolver {
2254 - (pkgAcquire &) fetcher {
2258 - (pkgSourceList &) list {
2262 - (NSArray *) packages {
2266 - (NSArray *) sources {
2267 return [sources_ allValues];
2270 - (NSArray *) issues {
2271 if (cache_->BrokenCount() == 0)
2274 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2276 for (Package *package in packages_) {
2277 if (![package broken])
2279 pkgCache::PkgIterator pkg([package iterator]);
2281 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2282 [entry addObject:[package name]];
2283 [issues addObject:entry];
2285 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2289 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2290 pkgCache::DepIterator start;
2291 pkgCache::DepIterator end;
2292 dep.GlobOr(start, end); // ++dep
2294 if (!cache_->IsImportantDep(end))
2296 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2299 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2300 [entry addObject:failure];
2301 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2303 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2304 [failure addObject:[package name]];
2306 pkgCache::PkgIterator target(start.TargetPkg());
2307 if (target->ProvidesList != 0)
2308 [failure addObject:@"?"];
2310 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2312 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2313 else if (!cache_[target].CandidateVerIter(cache_).end())
2314 [failure addObject:@"-"];
2315 else if (target->ProvidesList == 0)
2316 [failure addObject:@"!"];
2318 [failure addObject:@"%"];
2322 if (start.TargetVer() != 0)
2323 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2334 - (void) reloadData {
2354 if (!cache_.Open(progress_, true)) {
2356 if (!_error->PopMessage(error))
2359 lprintf("cache_.Open():[%s]\n", error.c_str());
2361 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2362 [delegate_ repairWithSelector:@selector(configure)];
2363 else if (error == "The package lists or status file could not be parsed or opened.")
2364 [delegate_ repairWithSelector:@selector(update)];
2365 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2366 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2367 // else if (error == "The list of sources could not be read.")
2368 else _assert(false);
2374 now_ = [[NSDate date] retain];
2376 policy_ = new pkgDepCache::Policy();
2377 records_ = new pkgRecords(cache_);
2378 resolver_ = new pkgProblemResolver(cache_);
2379 fetcher_ = new pkgAcquire(&status_);
2382 list_ = new pkgSourceList();
2383 _assert(list_->ReadMainList());
2385 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2386 _assert(pkgApplyStatus(cache_));
2388 if (cache_->BrokenCount() != 0) {
2389 _assert(pkgFixBroken(cache_));
2390 _assert(cache_->BrokenCount() == 0);
2391 _assert(pkgMinimizeUpgrade(cache_));
2394 [sources_ removeAllObjects];
2395 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2396 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2397 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2399 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2400 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2404 [packages_ removeAllObjects];
2407 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2408 if (Package *package = [Package packageWithIterator:iterator database:self])
2409 [packages_ addObject:package];
2411 [packages_ sortUsingSelector:@selector(compareByName:)];
2415 - (void) configure {
2416 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2417 system([dpkg UTF8String]);
2425 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2426 _assert(!_error->PendingError());
2429 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2432 public pkgArchiveCleaner
2435 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2440 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2442 while (_error->PopMessage(error))
2443 lprintf("ArchiveCleaner: %s\n", error.c_str());
2448 pkgRecords records(cache_);
2450 lock_ = new FileFd();
2451 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2452 _assert(!_error->PendingError());
2455 // XXX: explain this with an error message
2456 _assert(list.ReadMainList());
2458 manager_ = (_system->CreatePM(cache_));
2459 _assert(manager_->GetArchives(fetcher_, &list, &records));
2460 _assert(!_error->PendingError());
2464 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2466 _assert(list.ReadMainList());
2467 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2468 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2471 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2476 bool failed = false;
2477 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2478 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2481 std::string uri = (*item)->DescURI();
2482 std::string error = (*item)->ErrorText;
2484 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2487 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2488 withObject:[NSArray arrayWithObjects:
2489 [NSString stringWithUTF8String:error.c_str()],
2501 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2503 if (_error->PendingError()) {
2508 if (result == pkgPackageManager::Failed) {
2513 if (result != pkgPackageManager::Completed) {
2518 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2520 _assert(list.ReadMainList());
2521 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2522 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2525 if (![before isEqualToArray:after])
2530 _assert(pkgDistUpgrade(cache_));
2534 [self updateWithStatus:status_];
2537 - (void) updateWithStatus:(Status &)status {
2539 _assert(list.ReadMainList());
2542 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2543 _assert(!_error->PendingError());
2545 pkgAcquire fetcher(&status);
2546 _assert(list.GetIndexes(&fetcher));
2548 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2549 bool failed = false;
2550 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2551 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2552 (*item)->Finished();
2556 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2557 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2558 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2561 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2566 - (void) setDelegate:(id)delegate {
2567 delegate_ = delegate;
2568 status_.setDelegate(delegate);
2569 progress_.setDelegate(delegate);
2572 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2573 pkgIndexFile *index(NULL);
2574 list_->FindIndex(file, index);
2575 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2581 /* PopUp Windows {{{ */
2582 @interface PopUpView : UIView {
2583 _transient id delegate_;
2584 UITransitionView *transition_;
2589 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2593 @implementation PopUpView
2596 [transition_ setDelegate:nil];
2597 [transition_ release];
2603 [transition_ transition:UITransitionPushFromTop toView:nil];
2606 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2607 if (from != nil && to == nil)
2608 [self removeFromSuperview];
2611 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2612 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2613 delegate_ = delegate;
2615 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2616 [self addSubview:transition_];
2618 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2620 [view addSubview:self];
2622 [transition_ setDelegate:self];
2624 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2625 [transition_ transition:UITransitionNone toView:blank];
2626 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2633 /* Mail Composition {{{ */
2634 @interface MailToView : PopUpView {
2635 MailComposeController *controller_;
2638 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2642 @implementation MailToView
2645 [controller_ release];
2649 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2653 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2654 NSLog(@"did:%@", delivery);
2655 // [UIApp setStatusBarShowsProgress:NO];
2656 if ([controller error]){
2657 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2658 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2659 [mailAlertSheet setBodyText:[controller error]];
2660 [mailAlertSheet popupAlertAnimated:YES];
2664 - (void) showError {
2665 NSLog(@"%@", [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];
2672 - (void) deliverMessage { _pooled
2676 if (![controller_ deliverMessage])
2677 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2680 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2681 if ([controller_ needsDelivery])
2682 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2687 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2688 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2689 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2690 [controller_ setDelegate:self];
2691 [controller_ initializeUI];
2692 [controller_ setupForURL:url];
2694 UIView *view([controller_ view]);
2695 [overlay_ addSubview:view];
2701 /* Confirmation View {{{ */
2702 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2703 if (!iterator.end())
2704 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2705 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2707 pkgCache::PkgIterator package(dep.TargetPkg());
2710 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2717 @protocol ConfirmationViewDelegate
2722 @interface ConfirmationView : BrowserView {
2723 _transient Database *database_;
2724 UIActionSheet *essential_;
2731 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2735 @implementation ConfirmationView
2742 if (essential_ != nil)
2743 [essential_ release];
2749 [book_ popFromSuperviewAnimated:YES];
2752 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2753 NSString *context([sheet context]);
2755 if ([context isEqualToString:@"remove"]) {
2763 [delegate_ confirm];
2770 } else if ([context isEqualToString:@"unable"]) {
2774 [super alertSheet:sheet buttonClicked:button];
2777 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2778 [window setValue:changes_ forKey:@"changes"];
2779 [window setValue:issues_ forKey:@"issues"];
2780 [window setValue:sizes_ forKey:@"sizes"];
2781 [super webView:sender didClearWindowObject:window forFrame:frame];
2784 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2785 if ((self = [super initWithBook:book]) != nil) {
2786 database_ = database;
2788 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2789 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2790 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2791 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2792 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2796 pkgDepCache::Policy *policy([database_ policy]);
2798 pkgCacheFile &cache([database_ cache]);
2799 NSArray *packages = [database_ packages];
2800 for (size_t i(0), e = [packages count]; i != e; ++i) {
2801 Package *package = [packages objectAtIndex:i];
2802 pkgCache::PkgIterator iterator = [package iterator];
2803 pkgDepCache::StateCache &state(cache[iterator]);
2805 NSString *name([package name]);
2807 if (state.NewInstall())
2808 [installing addObject:name];
2809 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2810 [reinstalling addObject:name];
2811 else if (state.Upgrade())
2812 [upgrading addObject:name];
2813 else if (state.Downgrade())
2814 [downgrading addObject:name];
2815 else if (state.Delete()) {
2816 if ([package essential])
2818 [removing addObject:name];
2821 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2822 substrate_ |= DepSubstrate(iterator.CurrentVer());
2827 else if (Advanced_ || true) {
2828 essential_ = [[UIActionSheet alloc]
2829 initWithTitle:@"Removing Essentials"
2830 buttons:[NSArray arrayWithObjects:
2831 @"Cancel Operation (Safe)",
2832 @"Force Removal (Unsafe)",
2834 defaultButtonIndex:0
2840 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2842 [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."];
2844 essential_ = [[UIActionSheet alloc]
2845 initWithTitle:@"Unable to Comply"
2846 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2847 defaultButtonIndex:0
2852 [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."];
2855 changes_ = [[NSArray alloc] initWithObjects:
2863 issues_ = [database_ issues];
2865 issues_ = [issues_ retain];
2867 sizes_ = [[NSArray alloc] initWithObjects:
2868 SizeString([database_ fetcher].FetchNeeded()),
2869 SizeString([database_ fetcher].PartialPresent()),
2870 SizeString([database_ cache]->UsrSize()),
2873 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2877 - (NSString *) backButtonTitle {
2881 - (NSString *) leftButtonTitle {
2885 - (id) _rightButtonTitle {
2886 #if AlwaysReload || IgnoreInstall
2889 return issues_ == nil ? @"Confirm" : nil;
2893 - (void) _leftButtonClicked {
2898 - (void) _rightButtonClicked {
2900 return [super _rightButtonClicked];
2902 if (essential_ != nil)
2903 [essential_ popupAlertAnimated:YES];
2907 [delegate_ confirm];
2915 /* Progress Data {{{ */
2916 @interface ProgressData : NSObject {
2922 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2929 @implementation ProgressData
2931 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2932 if ((self = [super init]) != nil) {
2933 selector_ = selector;
2953 /* Progress View {{{ */
2954 @interface ProgressView : UIView <
2955 ConfigurationDelegate,
2958 _transient Database *database_;
2960 UIView *background_;
2961 UITransitionView *transition_;
2963 UINavigationBar *navbar_;
2964 UIProgressBar *progress_;
2965 UITextView *output_;
2966 UITextLabel *status_;
2967 UIPushButton *close_;
2970 SHA1SumValue springlist_;
2971 SHA1SumValue notifyconf_;
2972 SHA1SumValue sandplate_;
2974 NSTimeInterval last_;
2977 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2979 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2980 - (void) setContentView:(UIView *)view;
2983 - (void) _retachThread;
2984 - (void) _detachNewThreadData:(ProgressData *)data;
2985 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2991 @protocol ProgressViewDelegate
2992 - (void) progressViewIsComplete:(ProgressView *)sender;
2995 @implementation ProgressView
2998 [transition_ setDelegate:nil];
2999 [navbar_ setDelegate:nil];
3002 if (background_ != nil)
3003 [background_ release];
3004 [transition_ release];
3007 [progress_ release];
3014 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3015 if (bootstrap_ && from == overlay_ && to == view_)
3019 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3020 if ((self = [super initWithFrame:frame]) != nil) {
3021 database_ = database;
3022 delegate_ = delegate;
3024 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3025 [transition_ setDelegate:self];
3027 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3030 [overlay_ setBackgroundColor:[UIColor blackColor]];
3032 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3033 [background_ setBackgroundColor:[UIColor blackColor]];
3034 [self addSubview:background_];
3037 [self addSubview:transition_];
3039 CGSize navsize = [UINavigationBar defaultSize];
3040 CGRect navrect = {{0, 0}, navsize};
3042 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3043 [overlay_ addSubview:navbar_];
3045 [navbar_ setBarStyle:1];
3046 [navbar_ setDelegate:self];
3048 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3049 [navbar_ pushNavigationItem:navitem];
3051 CGRect bounds = [overlay_ bounds];
3052 CGSize prgsize = [UIProgressBar defaultSize];
3055 (bounds.size.width - prgsize.width) / 2,
3056 bounds.size.height - prgsize.height - 20
3059 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3060 [progress_ setStyle:0];
3062 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3064 bounds.size.height - prgsize.height - 50,
3065 bounds.size.width - 20,
3069 [status_ setColor:[UIColor whiteColor]];
3070 [status_ setBackgroundColor:[UIColor clearColor]];
3072 [status_ setCentersHorizontally:YES];
3073 //[status_ setFont:font];
3075 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3077 navrect.size.height + 20,
3078 bounds.size.width - 20,
3079 bounds.size.height - navsize.height - 62 - navrect.size.height
3082 //[output_ setTextFont:@"Courier New"];
3083 [output_ setTextSize:12];
3085 [output_ setTextColor:[UIColor whiteColor]];
3086 [output_ setBackgroundColor:[UIColor clearColor]];
3088 [output_ setMarginTop:0];
3089 [output_ setAllowsRubberBanding:YES];
3090 [output_ setEditable:NO];
3092 [overlay_ addSubview:output_];
3094 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3096 bounds.size.height - prgsize.height - 50,
3097 bounds.size.width - 20,
3101 [close_ setAutosizesToFit:NO];
3102 [close_ setDrawsShadow:YES];
3103 [close_ setStretchBackground:YES];
3104 [close_ setEnabled:YES];
3106 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3107 [close_ setTitleFont:bold];
3109 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3110 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3111 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3115 - (void) setContentView:(UIView *)view {
3116 view_ = [view retain];
3119 - (void) resetView {
3120 [transition_ transition:6 toView:view_];
3123 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3124 NSString *context([sheet context]);
3126 if ([context isEqualToString:@"error"])
3128 else if ([context isEqualToString:@"conffile"]) {
3129 FILE *input = [database_ input];
3133 fprintf(input, "N\n");
3137 fprintf(input, "Y\n");
3148 - (void) closeButtonPushed {
3157 [delegate_ suspendWithAnimation:YES];
3161 system("launchctl stop com.apple.SpringBoard");
3165 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3174 - (void) _retachThread {
3175 UINavigationItem *item = [navbar_ topItem];
3176 [item setTitle:@"Complete"];
3178 [overlay_ addSubview:close_];
3179 [progress_ removeFromSuperview];
3180 [status_ removeFromSuperview];
3182 [delegate_ progressViewIsComplete:self];
3185 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3186 MMap mmap(file, MMap::ReadOnly);
3188 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3189 if (!(sandplate_ == sha1.Result()))
3194 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3195 MMap mmap(file, MMap::ReadOnly);
3197 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3198 if (!(notifyconf_ == sha1.Result()))
3203 FileFd file(SpringBoard_, FileFd::ReadOnly);
3204 MMap mmap(file, MMap::ReadOnly);
3206 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3207 if (!(springlist_ == sha1.Result()))
3212 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3213 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3214 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3215 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3216 case 4: [close_ setTitle:@"Reboot Device"]; break;
3219 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3221 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3222 [cache autorelease];
3224 NSFileManager *manager = [NSFileManager defaultManager];
3225 NSError *error = nil;
3227 id system = [cache objectForKey:@"System"];
3232 if (stat(Cache_, &info) == -1)
3235 [system removeAllObjects];
3237 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3238 for (NSString *app in apps)
3239 if ([app hasSuffix:@".app"]) {
3240 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3241 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3242 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3244 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3245 [info setObject:path forKey:@"Path"];
3246 [info setObject:@"System" forKey:@"ApplicationType"];
3247 [system addInfoDictionary:info];
3253 [cache writeToFile:@Cache_ atomically:YES];
3255 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3257 if (chmod(Cache_, info.st_mode) == -1)
3261 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3264 notify_post("com.apple.mobile.application_installed");
3266 [delegate_ setStatusBarShowsProgress:NO];
3269 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3270 [[data target] performSelector:[data selector] withObject:[data object]];
3273 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3276 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3277 UINavigationItem *item = [navbar_ topItem];
3278 [item setTitle:title];
3280 [status_ setText:nil];
3281 [output_ setText:@""];
3282 [progress_ setProgress:0];
3285 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3287 [close_ removeFromSuperview];
3288 [overlay_ addSubview:progress_];
3289 [overlay_ addSubview:status_];
3291 [delegate_ setStatusBarShowsProgress:YES];
3295 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3296 MMap mmap(file, MMap::ReadOnly);
3298 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3299 sandplate_ = sha1.Result();
3303 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3304 MMap mmap(file, MMap::ReadOnly);
3306 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3307 notifyconf_ = sha1.Result();
3311 FileFd file(SpringBoard_, FileFd::ReadOnly);
3312 MMap mmap(file, MMap::ReadOnly);
3314 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3315 springlist_ = sha1.Result();
3318 [transition_ transition:6 toView:overlay_];
3321 detachNewThreadSelector:@selector(_detachNewThreadData:)
3323 withObject:[[ProgressData alloc]
3324 initWithSelector:selector
3331 - (void) repairWithSelector:(SEL)selector {
3333 detachNewThreadSelector:selector
3340 - (void) setConfigurationData:(NSString *)data {
3342 performSelectorOnMainThread:@selector(_setConfigurationData:)
3348 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3349 Package *package = id == nil ? nil : [database_ packageWithName:id];
3351 UIActionSheet *sheet = [[[UIActionSheet alloc]
3352 initWithTitle:(package == nil ? id : [package name])
3353 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3354 defaultButtonIndex:0
3359 [sheet setBodyText:error];
3360 [sheet popupAlertAnimated:YES];
3363 - (void) setProgressTitle:(NSString *)title {
3365 performSelectorOnMainThread:@selector(_setProgressTitle:)
3371 - (void) setProgressPercent:(float)percent {
3373 performSelectorOnMainThread:@selector(_setProgressPercent:)
3374 withObject:[NSNumber numberWithFloat:percent]
3379 - (void) startProgress {
3380 last_ = [NSDate timeIntervalSinceReferenceDate];
3383 - (void) addProgressOutput:(NSString *)output {
3385 performSelectorOnMainThread:@selector(_addProgressOutput:)
3391 - (bool) isCancelling:(size_t)received {
3393 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3394 if (received_ != received) {
3395 received_ = received;
3397 } else if (now - last_ > 30)
3404 - (void) _setConfigurationData:(NSString *)data {
3405 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3407 _assert(conffile_r(data));
3409 NSString *ofile = conffile_r[1];
3410 //NSString *nfile = conffile_r[2];
3412 UIActionSheet *sheet = [[[UIActionSheet alloc]
3413 initWithTitle:@"Configuration Upgrade"
3414 buttons:[NSArray arrayWithObjects:
3415 @"Keep My Old Copy",
3416 @"Accept The New Copy",
3417 // XXX: @"See What Changed",
3419 defaultButtonIndex:0
3424 [sheet setBodyText:[NSString stringWithFormat:
3425 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3428 [sheet popupAlertAnimated:YES];
3431 - (void) _setProgressTitle:(NSString *)title {
3432 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3433 for (size_t i(0), e([words count]); i != e; ++i) {
3434 NSString *word([words objectAtIndex:i]);
3435 if (Package *package = [database_ packageWithName:word])
3436 [words replaceObjectAtIndex:i withObject:[package name]];
3439 [status_ setText:[words componentsJoinedByString:@" "]];
3442 - (void) _setProgressPercent:(NSNumber *)percent {
3443 [progress_ setProgress:[percent floatValue]];
3446 - (void) _addProgressOutput:(NSString *)output {
3447 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3448 CGSize size = [output_ contentSize];
3449 CGRect rect = {{0, size.height}, {size.width, 0}};
3450 [output_ scrollRectToVisible:rect animated:YES];
3453 - (BOOL) isRunning {
3460 /* Package Cell {{{ */
3461 @interface PackageCell : UISimpleTableCell {
3464 NSString *description_;
3468 UITextLabel *status_;
3472 - (PackageCell *) init;
3473 - (void) setPackage:(Package *)package;
3475 + (int) heightForPackage:(Package *)package;
3479 @implementation PackageCell
3481 - (void) clearPackage {
3492 if (description_ != nil) {
3493 [description_ release];
3497 if (source_ != nil) {
3502 if (badge_ != nil) {
3509 [self clearPackage];
3516 - (PackageCell *) init {
3517 if ((self = [super init]) != nil) {
3519 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3520 [status_ setBackgroundColor:[UIColor clearColor]];
3521 [status_ setFont:small];
3526 - (void) setPackage:(Package *)package {
3527 [self clearPackage];
3529 Source *source = [package source];
3530 NSString *section = [package simpleSection];
3532 icon_ = [[package icon] retain];
3534 name_ = [[package name] retain];
3535 description_ = [[package tagline] retain];
3537 NSString *label = nil;
3538 bool trusted = false;
3540 if (source != nil) {
3541 label = [source label];
3542 trusted = [source trusted];
3543 } else if ([[package id] isEqualToString:@"firmware"])
3546 label = @"Unknown/Local";
3548 NSString *from = [NSString stringWithFormat:@"from %@", label];
3550 if (section != nil && ![section isEqualToString:label])
3551 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3553 source_ = [from retain];
3555 if (NSString *purpose = [package primaryPurpose])
3556 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3557 badge_ = [badge_ retain];
3560 if (NSString *mode = [package mode]) {
3561 [badge_ setImage:[UIImage applicationImageNamed:
3562 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3565 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3566 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3567 } else if ([package half]) {
3568 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3569 [status_ setText:@"Package Damaged"];
3570 [status_ setColor:[UIColor redColor]];
3572 [badge_ setImage:nil];
3573 [status_ setText:nil];
3578 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3581 rect.size = [icon_ size];
3583 rect.size.width /= 2;
3584 rect.size.height /= 2;
3586 rect.origin.x = 25 - rect.size.width / 2;
3587 rect.origin.y = 25 - rect.size.height / 2;
3589 [icon_ drawInRect:rect];
3592 if (badge_ != nil) {
3593 CGSize size = [badge_ size];
3595 [badge_ drawAtPoint:CGPointMake(
3596 36 - size.width / 2,
3597 36 - size.height / 2
3606 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3607 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3611 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3613 [super drawContentInRect:rect selected:selected];
3616 + (int) heightForPackage:(Package *)package {
3617 NSString *tagline([package tagline]);
3618 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3620 if ([package hasMode] || [package half])
3629 /* Section Cell {{{ */
3630 @interface SectionCell : UISimpleTableCell {
3635 _UISwitchSlider *switch_;
3640 - (void) setSection:(Section *)section editing:(BOOL)editing;
3644 @implementation SectionCell
3646 - (void) clearSection {
3647 if (section_ != nil) {
3657 if (count_ != nil) {
3664 [self clearSection];
3671 if ((self = [super init]) != nil) {
3672 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3674 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3675 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3679 - (void) onSwitch:(id)sender {
3680 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3681 if (metadata == nil) {
3682 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3683 [Sections_ setObject:metadata forKey:section_];
3687 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3690 - (void) setSection:(Section *)section editing:(BOOL)editing {
3691 if (editing != editing_) {
3693 [switch_ removeFromSuperview];
3695 [self addSubview:switch_];
3699 [self clearSection];
3701 if (section == nil) {
3702 name_ = [@"All Packages" retain];
3705 section_ = [section name];
3706 if (section_ != nil)
3707 section_ = [section_ retain];
3708 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3709 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3712 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3716 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3717 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3724 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3726 CGSize size = [count_ sizeWithFont:Font14_];
3730 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3732 [super drawContentInRect:rect selected:selected];
3738 /* File Table {{{ */
3739 @interface FileTable : RVPage {
3740 _transient Database *database_;
3743 NSMutableArray *files_;
3747 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3748 - (void) setPackage:(Package *)package;
3752 @implementation FileTable
3755 if (package_ != nil)
3764 - (int) numberOfRowsInTable:(UITable *)table {
3765 return files_ == nil ? 0 : [files_ count];
3768 - (float) table:(UITable *)table heightForRow:(int)row {
3772 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3773 if (reusing == nil) {
3774 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3775 UIFont *font = [UIFont systemFontOfSize:16];
3776 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3778 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3782 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3786 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3787 if ((self = [super initWithBook:book]) != nil) {
3788 database_ = database;
3790 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3792 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3793 [self addSubview:list_];
3795 UITableColumn *column = [[[UITableColumn alloc]
3796 initWithTitle:@"Name"
3798 width:[self frame].size.width
3801 [list_ setDataSource:self];
3802 [list_ setSeparatorStyle:1];
3803 [list_ addTableColumn:column];
3804 [list_ setDelegate:self];
3805 [list_ setReusesTableCells:YES];
3809 - (void) setPackage:(Package *)package {
3810 if (package_ != nil) {
3811 [package_ autorelease];
3820 [files_ removeAllObjects];
3822 if (package != nil) {
3823 package_ = [package retain];
3824 name_ = [[package id] retain];
3826 if (NSArray *files = [package files])
3827 [files_ addObjectsFromArray:files];
3829 if ([files_ count] != 0) {
3830 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3831 [files_ removeObjectAtIndex:0];
3832 [files_ sortUsingSelector:@selector(compareByPath:)];
3834 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3835 [stack addObject:@"/"];
3837 for (int i(0), e([files_ count]); i != e; ++i) {
3838 NSString *file = [files_ objectAtIndex:i];
3839 while (![file hasPrefix:[stack lastObject]])
3840 [stack removeLastObject];
3841 NSString *directory = [stack lastObject];
3842 [stack addObject:[file stringByAppendingString:@"/"]];
3843 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3844 ([stack count] - 2) * 3, "",
3845 [file substringFromIndex:[directory length]]
3854 - (void) resetViewAnimated:(BOOL)animated {
3855 [list_ resetViewAnimated:animated];
3858 - (void) reloadData {
3859 [self setPackage:[database_ packageWithName:name_]];
3860 [self reloadButtons];
3863 - (NSString *) title {
3864 return @"Installed Files";
3867 - (NSString *) backButtonTitle {
3873 /* Package View {{{ */
3874 @interface PackageView : BrowserView {
3875 _transient Database *database_;
3878 NSMutableArray *buttons_;
3881 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3882 - (void) setPackage:(Package *)package;
3886 @implementation PackageView
3889 if (package_ != nil)
3897 - (void) _clickButtonWithName:(NSString *)name {
3898 if ([name isEqualToString:@"Install"])
3899 [delegate_ installPackage:package_];
3900 else if ([name isEqualToString:@"Reinstall"])
3901 [delegate_ installPackage:package_];
3902 else if ([name isEqualToString:@"Remove"])
3903 [delegate_ removePackage:package_];
3904 else if ([name isEqualToString:@"Upgrade"])
3905 [delegate_ installPackage:package_];
3906 else _assert(false);
3909 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3910 NSString *context([sheet context]);
3912 if ([context isEqualToString:@"modify"]) {
3913 int count = [buttons_ count];
3914 _assert(count != 0);
3915 _assert(button <= count + 1);
3917 if (count != button - 1)
3918 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3922 [super alertSheet:sheet buttonClicked:button];
3925 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3926 return [super webView:sender didFinishLoadForFrame:frame];
3929 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3930 [window setValue:package_ forKey:@"package"];
3931 [super webView:sender didClearWindowObject:window forFrame:frame];
3935 - (void) _rightButtonClicked {
3936 /*[super _rightButtonClicked];
3939 int count = [buttons_ count];
3940 _assert(count != 0);
3943 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3945 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3946 [buttons addObjectsFromArray:buttons_];
3947 [buttons addObject:@"Cancel"];
3949 [delegate_ slideUp:[[[UIActionSheet alloc]
3952 defaultButtonIndex:2
3960 - (id) _rightButtonTitle {
3961 int count = [buttons_ count];
3962 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3965 - (NSString *) backButtonTitle {
3969 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3970 if ((self = [super initWithBook:book]) != nil) {
3971 database_ = database;
3972 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3976 - (void) setPackage:(Package *)package {
3977 if (package_ != nil) {
3978 [package_ autorelease];
3987 [buttons_ removeAllObjects];
3989 if (package != nil) {
3990 package_ = [package retain];
3991 name_ = [[package id] retain];
3993 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3995 if ([package_ source] == nil);
3996 else if ([package_ upgradableAndEssential:NO])
3997 [buttons_ addObject:@"Upgrade"];
3998 else if ([package_ installed] == nil)
3999 [buttons_ addObject:@"Install"];
4001 [buttons_ addObject:@"Reinstall"];
4002 if ([package_ installed] != nil)
4003 [buttons_ addObject:@"Remove"];
4011 - (void) reloadData {
4012 [self setPackage:[database_ packageWithName:name_]];
4013 [self reloadButtons];
4018 /* Package Table {{{ */
4019 @interface PackageTable : RVPage {
4020 _transient Database *database_;
4022 NSMutableArray *packages_;
4023 NSMutableArray *sections_;
4024 UISectionList *list_;
4027 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4029 - (void) setDelegate:(id)delegate;
4031 - (void) reloadData;
4032 - (void) resetCursor;
4034 - (UISectionList *) list;
4036 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4040 @implementation PackageTable
4043 [list_ setDataSource:nil];
4046 [packages_ release];
4047 [sections_ release];
4052 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4053 return [sections_ count];
4056 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4057 return [[sections_ objectAtIndex:section] name];
4060 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4061 return [[sections_ objectAtIndex:section] row];
4064 - (int) numberOfRowsInTable:(UITable *)table {
4065 return [packages_ count];
4068 - (float) table:(UITable *)table heightForRow:(int)row {
4069 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4072 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4074 reusing = [[[PackageCell alloc] init] autorelease];
4075 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4079 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4083 - (void) tableRowSelected:(NSNotification *)notification {
4084 int row = [[notification object] selectedRow];
4088 Package *package = [packages_ objectAtIndex:row];
4089 package = [database_ packageWithName:[package id]];
4090 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4091 [view setPackage:package];
4092 [view setDelegate:delegate_];
4093 [book_ pushPage:view];
4096 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4097 if ((self = [super initWithBook:book]) != nil) {
4098 database_ = database;
4099 title_ = [title retain];
4101 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4102 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4104 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4105 [list_ setDataSource:self];
4107 UITableColumn *column = [[[UITableColumn alloc]
4108 initWithTitle:@"Name"
4110 width:[self frame].size.width
4113 UITable *table = [list_ table];
4114 [table setSeparatorStyle:1];
4115 [table addTableColumn:column];
4116 [table setDelegate:self];
4117 [table setReusesTableCells:YES];
4119 [self addSubview:list_];
4121 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4122 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4126 - (void) setDelegate:(id)delegate {
4127 delegate_ = delegate;
4130 - (bool) hasPackage:(Package *)package {
4134 - (void) reloadData {
4135 NSArray *packages = [database_ packages];
4137 [packages_ removeAllObjects];
4138 [sections_ removeAllObjects];
4140 for (size_t i(0); i != [packages count]; ++i) {
4141 Package *package([packages objectAtIndex:i]);
4142 if ([self hasPackage:package])
4143 [packages_ addObject:package];
4146 Section *section = nil;
4148 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4149 Package *package = [packages_ objectAtIndex:offset];
4150 NSString *name = [package index];
4152 if (section == nil || ![[section name] isEqualToString:name]) {
4153 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4154 [sections_ addObject:section];
4157 [section addToCount];
4163 - (NSString *) title {
4167 - (void) resetViewAnimated:(BOOL)animated {
4168 [list_ resetViewAnimated:animated];
4171 - (void) resetCursor {
4172 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4175 - (UISectionList *) list {
4179 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4180 [list_ setShouldHideHeaderInShortLists:hide];
4185 /* Filtered Package Table {{{ */
4186 @interface FilteredPackageTable : PackageTable {
4191 - (void) setObject:(id)object;
4193 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4197 @implementation FilteredPackageTable
4205 - (void) setObject:(id)object {
4211 object_ = [object retain];
4214 - (bool) hasPackage:(Package *)package {
4215 return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
4218 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4219 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4221 object_ = object == nil ? nil : [object retain];
4230 /* Add Source View {{{ */
4231 @interface AddSourceView : RVPage {
4232 _transient Database *database_;
4235 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4239 @implementation AddSourceView
4241 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4242 if ((self = [super initWithBook:book]) != nil) {
4243 database_ = database;
4249 /* Source Cell {{{ */
4250 @interface SourceCell : UITableCell {
4253 NSString *description_;
4259 - (SourceCell *) initWithSource:(Source *)source;
4263 @implementation SourceCell
4268 [description_ release];
4273 - (SourceCell *) initWithSource:(Source *)source {
4274 if ((self = [super init]) != nil) {
4276 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4278 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4279 icon_ = [icon_ retain];
4281 origin_ = [[source name] retain];
4282 label_ = [[source uri] retain];
4283 description_ = [[source description] retain];
4287 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4289 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4296 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4300 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4304 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4306 [super drawContentInRect:rect selected:selected];
4311 /* Source Table {{{ */
4312 @interface SourceTable : RVPage {
4313 _transient Database *database_;
4314 UISectionList *list_;
4315 NSMutableArray *sources_;
4316 UIActionSheet *alert_;
4320 UIProgressHUD *hud_;
4323 //NSURLConnection *installer_;
4324 NSURLConnection *trivial_bz2_;
4325 NSURLConnection *trivial_gz_;
4326 //NSURLConnection *automatic_;
4331 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4335 @implementation SourceTable
4337 - (void) _deallocConnection:(NSURLConnection *)connection {
4338 if (connection != nil) {
4339 [connection cancel];
4340 //[connection setDelegate:nil];
4341 [connection release];
4346 [[list_ table] setDelegate:nil];
4347 [list_ setDataSource:nil];
4356 //[self _deallocConnection:installer_];
4357 [self _deallocConnection:trivial_gz_];
4358 [self _deallocConnection:trivial_bz2_];
4359 //[self _deallocConnection:automatic_];
4366 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4367 return offset_ == 0 ? 1 : 2;
4370 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4371 switch (section + (offset_ == 0 ? 1 : 0)) {
4372 case 0: return @"Entered by User";
4373 case 1: return @"Installed by Packages";
4381 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4382 switch (section + (offset_ == 0 ? 1 : 0)) {
4384 case 1: return offset_;
4392 - (int) numberOfRowsInTable:(UITable *)table {
4393 return [sources_ count];
4396 - (float) table:(UITable *)table heightForRow:(int)row {
4397 Source *source = [sources_ objectAtIndex:row];
4398 return [source description] == nil ? 56 : 73;
4401 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4402 Source *source = [sources_ objectAtIndex:row];
4403 // XXX: weird warning, stupid selectors ;P
4404 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4407 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4411 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4415 - (void) tableRowSelected:(NSNotification*)notification {
4416 UITable *table([list_ table]);
4417 int row([table selectedRow]);
4421 Source *source = [sources_ objectAtIndex:row];
4423 PackageTable *packages = [[[FilteredPackageTable alloc]
4426 title:[source label]
4427 filter:@selector(isVisibleInSource:)
4431 [packages setDelegate:delegate_];
4433 [book_ pushPage:packages];
4436 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4437 Source *source = [sources_ objectAtIndex:row];
4438 return [source record] != nil;
4441 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4442 [[list_ table] setDeleteConfirmationRow:row];
4445 - (void) table:(UITable *)table deleteRow:(int)row {
4446 Source *source = [sources_ objectAtIndex:row];
4447 [Sources_ removeObjectForKey:[source key]];
4448 [delegate_ syncData];
4451 - (void) _endConnection:(NSURLConnection *)connection {
4452 NSURLConnection **field = NULL;
4453 if (connection == trivial_bz2_)
4454 field = &trivial_bz2_;
4455 else if (connection == trivial_gz_)
4456 field = &trivial_gz_;
4457 _assert(field != NULL);
4458 [connection release];
4462 trivial_bz2_ == nil &&
4465 [delegate_ setStatusBarShowsProgress:NO];
4468 [hud_ removeFromSuperview];
4473 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4476 @"./", @"Distribution",
4477 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4479 [delegate_ syncData];
4480 } else if (error_ != nil) {
4481 UIActionSheet *sheet = [[[UIActionSheet alloc]
4482 initWithTitle:@"Verification Error"
4483 buttons:[NSArray arrayWithObjects:@"OK", nil]
4484 defaultButtonIndex:0
4489 [sheet setBodyText:[error_ localizedDescription]];
4490 [sheet popupAlertAnimated:YES];
4492 UIActionSheet *sheet = [[[UIActionSheet alloc]
4493 initWithTitle:@"Did not Find Repository"
4494 buttons:[NSArray arrayWithObjects:@"OK", nil]
4495 defaultButtonIndex:0
4500 [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."];
4501 [sheet popupAlertAnimated:YES];
4507 if (error_ != nil) {
4514 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4515 switch ([response statusCode]) {
4521 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4522 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4524 error_ = [error retain];
4525 [self _endConnection:connection];
4528 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4529 [self _endConnection:connection];
4532 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4533 NSMutableURLRequest *request = [NSMutableURLRequest
4534 requestWithURL:[NSURL URLWithString:href]
4535 cachePolicy:NSURLRequestUseProtocolCachePolicy
4536 timeoutInterval:20.0
4539 [request setHTTPMethod:method];
4541 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4544 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4545 NSString *context([sheet context]);
4547 if ([context isEqualToString:@"source"]) {
4550 NSString *href = [[sheet textField] text];
4552 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4554 if (![href hasSuffix:@"/"])
4555 href_ = [href stringByAppendingString:@"/"];
4558 href_ = [href_ retain];
4560 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4561 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4562 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4566 hud_ = [delegate_ addProgressHUD];
4567 [hud_ setText:@"Verifying URL"];
4578 } else if ([context isEqualToString:@"trivial"])
4580 else if ([context isEqualToString:@"urlerror"])
4584 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4585 if ((self = [super initWithBook:book]) != nil) {
4586 database_ = database;
4587 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4589 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4590 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4591 [list_ setShouldHideHeaderInShortLists:NO];
4593 [self addSubview:list_];
4594 [list_ setDataSource:self];
4596 UITableColumn *column = [[UITableColumn alloc]
4597 initWithTitle:@"Name"
4599 width:[self frame].size.width
4602 UITable *table = [list_ table];
4603 [table setSeparatorStyle:1];
4604 [table addTableColumn:column];
4605 [table setDelegate:self];
4609 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4610 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4614 - (void) reloadData {
4616 _assert(list.ReadMainList());
4618 [sources_ removeAllObjects];
4619 [sources_ addObjectsFromArray:[database_ sources]];
4621 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4624 int count = [sources_ count];
4625 for (offset_ = 0; offset_ != count; ++offset_) {
4626 Source *source = [sources_ objectAtIndex:offset_];
4627 if ([source record] == nil)
4634 - (void) resetViewAnimated:(BOOL)animated {
4635 [list_ resetViewAnimated:animated];
4638 - (void) _leftButtonClicked {
4639 /*[book_ pushPage:[[[AddSourceView alloc]
4644 UIActionSheet *sheet = [[[UIActionSheet alloc]
4645 initWithTitle:@"Enter Cydia/APT URL"
4646 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4647 defaultButtonIndex:0
4652 [sheet setNumberOfRows:1];
4654 [sheet addTextFieldWithValue:@"http://" label:@""];
4656 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4657 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4658 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4659 [traits setKeyboardType:UIKeyboardTypeURL];
4660 // XXX: UIReturnKeyDone
4661 [traits setReturnKeyType:UIReturnKeyNext];
4663 [sheet popupAlertAnimated:YES];
4666 - (void) _rightButtonClicked {
4667 UITable *table = [list_ table];
4668 BOOL editing = [table isRowDeletionEnabled];
4669 [table enableRowDeletion:!editing animated:YES];
4670 [book_ reloadButtonsForPage:self];
4673 - (NSString *) title {
4677 - (NSString *) leftButtonTitle {
4678 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4681 - (id) rightButtonTitle {
4682 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4685 - (UINavigationButtonStyle) rightButtonStyle {
4686 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4692 /* Installed View {{{ */
4693 @interface InstalledView : RVPage {
4694 _transient Database *database_;
4695 FilteredPackageTable *packages_;
4699 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4703 @implementation InstalledView
4706 [packages_ release];
4710 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4711 if ((self = [super initWithBook:book]) != nil) {
4712 database_ = database;
4714 packages_ = [[FilteredPackageTable alloc]
4718 filter:@selector(isInstalledAndVisible:)
4719 with:[NSNumber numberWithBool:YES]
4722 [self addSubview:packages_];
4724 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4725 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4729 - (void) resetViewAnimated:(BOOL)animated {
4730 [packages_ resetViewAnimated:animated];
4733 - (void) reloadData {
4734 [packages_ reloadData];
4737 - (void) _rightButtonClicked {
4738 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4739 [packages_ reloadData];
4741 [book_ reloadButtonsForPage:self];
4744 - (NSString *) title {
4745 return @"Installed";
4748 - (NSString *) backButtonTitle {
4752 - (id) rightButtonTitle {
4753 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4756 - (UINavigationButtonStyle) rightButtonStyle {
4757 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4760 - (void) setDelegate:(id)delegate {
4761 [super setDelegate:delegate];
4762 [packages_ setDelegate:delegate];
4769 @interface HomeView : BrowserView {
4774 @implementation HomeView
4776 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4777 NSString *context([sheet context]);
4779 if ([context isEqualToString:@"about"])
4782 [super alertSheet:sheet buttonClicked:button];
4785 - (void) _leftButtonClicked {
4786 UIActionSheet *sheet = [[[UIActionSheet alloc]
4787 initWithTitle:@"About Cydia Installer"
4788 buttons:[NSArray arrayWithObjects:@"Close", nil]
4789 defaultButtonIndex:0
4795 @"Copyright (C) 2008\n"
4796 "Jay Freeman (saurik)\n"
4797 "saurik@saurik.com\n"
4798 "http://www.saurik.com/\n"
4801 "http://www.theokorigroup.com/\n"
4803 "College of Creative Studies,\n"
4804 "University of California,\n"
4806 "http://www.ccs.ucsb.edu/"
4809 [sheet popupAlertAnimated:YES];
4812 - (NSString *) leftButtonTitle {
4818 /* Manage View {{{ */
4819 @interface ManageView : BrowserView {
4824 @implementation ManageView
4826 - (NSString *) title {
4830 - (void) _leftButtonClicked {
4831 [delegate_ askForSettings];
4834 - (NSString *) leftButtonTitle {
4839 - (id) _rightButtonTitle {
4851 #include <BrowserView.m>
4853 /* Cydia Book {{{ */
4854 @interface CYBook : RVBook <
4857 _transient Database *database_;
4858 UINavigationBar *overlay_;
4859 UINavigationBar *underlay_;
4860 UIProgressIndicator *indicator_;
4861 UITextLabel *prompt_;
4862 UIProgressBar *progress_;
4863 UINavigationButton *cancel_;
4866 NSTimeInterval last_;
4869 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4875 @implementation CYBook
4879 [indicator_ release];
4881 [progress_ release];
4886 - (NSString *) getTitleForPage:(RVPage *)page {
4887 return Simplify([super getTitleForPage:page]);
4895 [UIView beginAnimations:nil context:NULL];
4897 CGRect ovrframe = [overlay_ frame];
4898 ovrframe.origin.y = 0;
4899 [overlay_ setFrame:ovrframe];
4901 CGRect barframe = [navbar_ frame];
4902 barframe.origin.y += ovrframe.size.height;
4903 [navbar_ setFrame:barframe];
4905 CGRect trnframe = [transition_ frame];
4906 trnframe.origin.y += ovrframe.size.height;
4907 trnframe.size.height -= ovrframe.size.height;
4908 [transition_ setFrame:trnframe];
4910 [UIView endAnimations];
4912 [indicator_ startAnimation];
4913 [prompt_ setText:@"Updating Database"];
4914 [progress_ setProgress:0];
4917 last_ = [NSDate timeIntervalSinceReferenceDate];
4919 [overlay_ addSubview:cancel_];
4922 detachNewThreadSelector:@selector(_update)
4931 [indicator_ stopAnimation];
4933 [UIView beginAnimations:nil context:NULL];
4935 CGRect ovrframe = [overlay_ frame];
4936 ovrframe.origin.y = -ovrframe.size.height;
4937 [overlay_ setFrame:ovrframe];
4939 CGRect barframe = [navbar_ frame];
4940 barframe.origin.y -= ovrframe.size.height;
4941 [navbar_ setFrame:barframe];
4943 CGRect trnframe = [transition_ frame];
4944 trnframe.origin.y -= ovrframe.size.height;
4945 trnframe.size.height += ovrframe.size.height;
4946 [transition_ setFrame:trnframe];
4948 [UIView commitAnimations];
4950 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4953 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4954 if ((self = [super initWithFrame:frame]) != nil) {
4955 database_ = database;
4957 CGRect ovrrect = [navbar_ bounds];
4958 ovrrect.size.height = [UINavigationBar defaultSize].height;
4959 ovrrect.origin.y = -ovrrect.size.height;
4961 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4962 [self addSubview:overlay_];
4964 ovrrect.origin.y = frame.size.height;
4965 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4966 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4967 [self addSubview:underlay_];
4969 [overlay_ setBarStyle:1];
4970 [underlay_ setBarStyle:1];
4972 int barstyle = [overlay_ _barStyle:NO];
4973 bool ugly = barstyle == 0;
4975 UIProgressIndicatorStyle style = ugly ?
4976 UIProgressIndicatorStyleMediumBrown :
4977 UIProgressIndicatorStyleMediumWhite;
4979 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4980 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4981 CGRect indrect = {{indoffset, indoffset}, indsize};
4983 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4984 [indicator_ setStyle:style];
4985 [overlay_ addSubview:indicator_];
4987 CGSize prmsize = {215, indsize.height + 4};
4990 indoffset * 2 + indsize.width,
4994 unsigned(ovrrect.size.height - prmsize.height) / 2
4997 UIFont *font = [UIFont systemFontOfSize:15];
4999 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5001 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5002 [prompt_ setBackgroundColor:[UIColor clearColor]];
5003 [prompt_ setFont:font];
5005 [overlay_ addSubview:prompt_];
5007 CGSize prgsize = {75, 100};
5010 ovrrect.size.width - prgsize.width - 10,
5011 (ovrrect.size.height - prgsize.height) / 2
5014 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5015 [progress_ setStyle:0];
5016 [overlay_ addSubview:progress_];
5018 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5019 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5021 CGRect frame = [cancel_ frame];
5022 frame.size.width = 65;
5023 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5024 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5025 [cancel_ setFrame:frame];
5027 [cancel_ setBarStyle:barstyle];
5031 - (void) _onCancel {
5033 [cancel_ removeFromSuperview];
5036 - (void) _update { _pooled
5038 status.setDelegate(self);
5040 [database_ updateWithStatus:status];
5043 performSelectorOnMainThread:@selector(_update_)
5049 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5050 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5053 - (void) setProgressTitle:(NSString *)title {
5055 performSelectorOnMainThread:@selector(_setProgressTitle:)
5061 - (void) setProgressPercent:(float)percent {
5063 performSelectorOnMainThread:@selector(_setProgressPercent:)
5064 withObject:[NSNumber numberWithFloat:percent]
5069 - (void) startProgress {
5072 - (void) addProgressOutput:(NSString *)output {
5074 performSelectorOnMainThread:@selector(_addProgressOutput:)
5080 - (bool) isCancelling:(size_t)received {
5081 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5082 if (received_ != received) {
5083 received_ = received;
5085 } else if (now - last_ > 15)
5090 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5094 - (void) _setProgressTitle:(NSString *)title {
5095 [prompt_ setText:title];
5098 - (void) _setProgressPercent:(NSNumber *)percent {
5099 [progress_ setProgress:[percent floatValue]];
5102 - (void) _addProgressOutput:(NSString *)output {
5107 /* Cydia:// Protocol {{{ */
5108 @interface CydiaURLProtocol : NSURLProtocol {
5113 @implementation CydiaURLProtocol
5115 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5116 NSURL *url([request URL]);
5119 NSString *scheme([[url scheme] lowercaseString]);
5120 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5125 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5129 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5130 id<NSURLProtocolClient> client([self client]);
5132 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5134 NSData *data(UIImagePNGRepresentation(icon));
5136 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5137 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5138 [client URLProtocol:self didLoadData:data];
5139 [client URLProtocolDidFinishLoading:self];
5143 - (void) startLoading {
5144 id<NSURLProtocolClient> client([self client]);
5145 NSURLRequest *request([self request]);
5147 NSURL *url([request URL]);
5148 NSString *href([url absoluteString]);
5150 NSString *path([href substringFromIndex:8]);
5151 NSRange slash([path rangeOfString:@"/"]);
5154 if (slash.location == NSNotFound) {
5158 command = [path substringToIndex:slash.location];
5159 path = [path substringFromIndex:(slash.location + 1)];
5162 Database *database([Database sharedInstance]);
5164 if ([command isEqualToString:@"package-icon"]) {
5167 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5168 Package *package([database packageWithName:path]);
5171 UIImage *icon([package icon]);
5172 [self _returnPNGWithImage:icon forRequest:request];
5173 } else if ([command isEqualToString:@"source-icon"]) {
5176 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5177 NSString *source(Simplify(path));
5178 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5180 icon = [UIImage applicationImageNamed:@"unknown.png"];
5181 [self _returnPNGWithImage:icon forRequest:request];
5182 } else if ([command isEqualToString:@"uikit-image"]) {
5185 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5186 UIImage *icon(_UIImageWithName(path));
5187 [self _returnPNGWithImage:icon forRequest:request];
5188 } else if ([command isEqualToString:@"section-icon"]) {
5191 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5192 NSString *section(Simplify(path));
5193 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5195 icon = [UIImage applicationImageNamed:@"unknown.png"];
5196 [self _returnPNGWithImage:icon forRequest:request];
5198 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5202 - (void) stopLoading {
5208 /* Sections View {{{ */
5209 @interface SectionsView : RVPage {
5210 _transient Database *database_;
5211 NSMutableArray *sections_;
5212 NSMutableArray *filtered_;
5213 UITransitionView *transition_;
5219 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5220 - (void) reloadData;
5225 @implementation SectionsView
5228 [list_ setDataSource:nil];
5229 [list_ setDelegate:nil];
5231 [sections_ release];
5232 [filtered_ release];
5233 [transition_ release];
5235 [accessory_ release];
5239 - (int) numberOfRowsInTable:(UITable *)table {
5240 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5243 - (float) table:(UITable *)table heightForRow:(int)row {
5247 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5249 reusing = [[[SectionCell alloc] init] autorelease];
5250 [(SectionCell *)reusing setSection:(editing_ ?
5251 [sections_ objectAtIndex:row] :
5252 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5253 ) editing:editing_];
5257 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5261 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5265 - (void) tableRowSelected:(NSNotification *)notification {
5266 int row = [[notification object] selectedRow];
5277 title = @"All Packages";
5279 section = [filtered_ objectAtIndex:(row - 1)];
5280 name = [section name];
5286 title = @"(No Section)";
5290 PackageTable *table = [[[FilteredPackageTable alloc]
5294 filter:@selector(isVisiblyUninstalledInSection:)
5298 [table setDelegate:delegate_];
5300 [book_ pushPage:table];
5303 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5304 if ((self = [super initWithBook:book]) != nil) {
5305 database_ = database;
5307 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5308 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5310 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5311 [self addSubview:transition_];
5313 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5314 [transition_ transition:0 toView:list_];
5316 UITableColumn *column = [[[UITableColumn alloc]
5317 initWithTitle:@"Name"
5319 width:[self frame].size.width
5322 [list_ setDataSource:self];
5323 [list_ setSeparatorStyle:1];
5324 [list_ addTableColumn:column];
5325 [list_ setDelegate:self];
5326 [list_ setReusesTableCells:YES];
5330 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5331 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5335 - (void) reloadData {
5336 NSArray *packages = [database_ packages];
5338 [sections_ removeAllObjects];
5339 [filtered_ removeAllObjects];
5341 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5342 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5345 for (size_t i(0); i != [packages count]; ++i) {
5346 Package *package([packages objectAtIndex:i]);
5347 NSString *name([package section]);
5350 Section *section([sections objectForKey:name]);
5351 if (section == nil) {
5352 section = [[[Section alloc] initWithName:name] autorelease];
5353 [sections setObject:section forKey:name];
5357 if ([package valid] && [package installed] == nil && [package visible])
5358 [filtered addObject:package];
5362 [sections_ addObjectsFromArray:[sections allValues]];
5363 [sections_ sortUsingSelector:@selector(compareByName:)];
5366 [filtered sortUsingSelector:@selector(compareBySection:)];
5369 Section *section = nil;
5370 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5371 Package *package = [filtered objectAtIndex:offset];
5372 NSString *name = [package section];
5374 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5375 section = name == nil ?
5376 [[[Section alloc] initWithName:nil] autorelease] :
5377 [sections objectForKey:name];
5378 [filtered_ addObject:section];
5381 [section addToCount];
5389 - (void) resetView {
5391 [self _rightButtonClicked];
5394 - (void) resetViewAnimated:(BOOL)animated {
5395 [list_ resetViewAnimated:animated];
5398 - (void) _rightButtonClicked {
5399 if ((editing_ = !editing_))
5402 [delegate_ updateData];
5403 [book_ reloadTitleForPage:self];
5404 [book_ reloadButtonsForPage:self];
5407 - (NSString *) title {
5408 return editing_ ? @"Section Visibility" : @"Install by Section";
5411 - (NSString *) backButtonTitle {
5415 - (id) rightButtonTitle {
5416 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5419 - (UINavigationButtonStyle) rightButtonStyle {
5420 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5423 - (UIView *) accessoryView {
5429 /* Changes View {{{ */
5430 @interface ChangesView : RVPage {
5431 _transient Database *database_;
5432 NSMutableArray *packages_;
5433 NSMutableArray *sections_;
5434 UISectionList *list_;
5438 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5439 - (void) reloadData;
5443 @implementation ChangesView
5446 [[list_ table] setDelegate:nil];
5447 [list_ setDataSource:nil];
5449 [packages_ release];
5450 [sections_ release];
5455 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5456 return [sections_ count];
5459 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5460 return [[sections_ objectAtIndex:section] name];
5463 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5464 return [[sections_ objectAtIndex:section] row];
5467 - (int) numberOfRowsInTable:(UITable *)table {
5468 return [packages_ count];
5471 - (float) table:(UITable *)table heightForRow:(int)row {
5472 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5475 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5477 reusing = [[[PackageCell alloc] init] autorelease];
5478 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5482 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5486 - (void) tableRowSelected:(NSNotification *)notification {
5487 int row = [[notification object] selectedRow];
5490 Package *package = [packages_ objectAtIndex:row];
5491 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5492 [view setDelegate:delegate_];
5493 [view setPackage:package];
5494 [book_ pushPage:view];
5497 - (void) _leftButtonClicked {
5498 [(CYBook *)book_ update];
5499 [self reloadButtons];
5502 - (void) _rightButtonClicked {
5503 [delegate_ distUpgrade];
5506 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5507 if ((self = [super initWithBook:book]) != nil) {
5508 database_ = database;
5510 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5511 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5513 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5514 [self addSubview:list_];
5516 [list_ setShouldHideHeaderInShortLists:NO];
5517 [list_ setDataSource:self];
5518 //[list_ setSectionListStyle:1];
5520 UITableColumn *column = [[[UITableColumn alloc]
5521 initWithTitle:@"Name"
5523 width:[self frame].size.width
5526 UITable *table = [list_ table];
5527 [table setSeparatorStyle:1];
5528 [table addTableColumn:column];
5529 [table setDelegate:self];
5530 [table setReusesTableCells:YES];
5534 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5535 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5539 - (void) reloadData {
5540 NSArray *packages = [database_ packages];
5542 [packages_ removeAllObjects];
5543 [sections_ removeAllObjects];
5546 for (size_t i(0); i != [packages count]; ++i) {
5547 Package *package([packages objectAtIndex:i]);
5550 [package installed] == nil && [package valid] && [package visible] ||
5551 [package upgradableAndEssential:YES]
5553 [packages_ addObject:package];
5557 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5560 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5561 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5562 Section *section = nil;
5566 bool unseens = false;
5568 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5571 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5572 Package *package = [packages_ objectAtIndex:offset];
5574 if (![package upgradableAndEssential:YES]) {
5576 NSDate *seen = [package seen];
5578 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5581 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5582 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5583 [sections_ addObject:section];
5587 [section addToCount];
5588 } else if ([package ignored])
5589 [ignored addToCount];
5592 [upgradable addToCount];
5597 CFRelease(formatter);
5600 Section *last = [sections_ lastObject];
5601 size_t count = [last count];
5602 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5603 [sections_ removeLastObject];
5606 if ([ignored count] != 0)
5607 [sections_ insertObject:ignored atIndex:0];
5609 [sections_ insertObject:upgradable atIndex:0];
5612 [self reloadButtons];
5615 - (void) resetViewAnimated:(BOOL)animated {
5616 [list_ resetViewAnimated:animated];
5619 - (NSString *) leftButtonTitle {
5620 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5623 - (id) rightButtonTitle {
5624 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5627 - (NSString *) title {
5633 /* Search View {{{ */
5634 @protocol SearchViewDelegate
5635 - (void) showKeyboard:(BOOL)show;
5638 @interface SearchView : RVPage {
5640 UISearchField *field_;
5641 UITransitionView *transition_;
5642 FilteredPackageTable *table_;
5643 UIPreferencesTable *advanced_;
5649 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5650 - (void) reloadData;
5654 @implementation SearchView
5657 [field_ setDelegate:nil];
5659 [accessory_ release];
5661 [transition_ release];
5663 [advanced_ release];
5668 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5672 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5674 case 0: return @"Advanced Search (Coming Soon!)";
5676 default: _assert(false);
5680 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5684 default: _assert(false);
5688 - (void) _showKeyboard:(BOOL)show {
5689 CGSize keysize = [UIKeyboard defaultSize];
5690 CGRect keydown = [book_ pageBounds];
5691 CGRect keyup = keydown;
5692 keyup.size.height -= keysize.height - ButtonBarHeight_;
5694 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5696 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5697 [animation setSignificantRectFields:8];
5700 [animation setStartFrame:keydown];
5701 [animation setEndFrame:keyup];
5703 [animation setStartFrame:keyup];
5704 [animation setEndFrame:keydown];
5707 UIAnimator *animator = [UIAnimator sharedAnimator];
5710 addAnimations:[NSArray arrayWithObjects:animation, nil]
5711 withDuration:(KeyboardTime_ - delay)
5716 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5718 [delegate_ showKeyboard:show];
5721 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5722 [self _showKeyboard:YES];
5725 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5726 [self _showKeyboard:NO];
5729 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5731 NSString *text([field_ text]);
5732 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5738 - (void) textFieldClearButtonPressed:(UITextField *)field {
5742 - (void) keyboardInputShouldDelete:(id)input {
5746 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5747 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5751 [field_ resignFirstResponder];
5756 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5757 if ((self = [super initWithBook:book]) != nil) {
5758 CGRect pageBounds = [book_ pageBounds];
5760 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5761 [self addSubview:transition_];
5763 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5765 [advanced_ setReusesTableCells:YES];
5766 [advanced_ setDataSource:self];
5767 [advanced_ reloadData];
5769 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5770 CGColor dimmed(space_, 0, 0, 0, 0.5);
5771 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5773 table_ = [[FilteredPackageTable alloc]
5777 filter:@selector(isUnfilteredAndSearchedForBy:)
5781 [table_ setShouldHideHeaderInShortLists:NO];
5782 [transition_ transition:0 toView:table_];
5791 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5798 [self bounds].size.width - area.origin.x - 18;
5800 area.size.height = [UISearchField defaultHeight];
5802 field_ = [[UISearchField alloc] initWithFrame:area];
5804 UIFont *font = [UIFont systemFontOfSize:16];
5805 [field_ setFont:font];
5807 [field_ setPlaceholder:@"Package Names & Descriptions"];
5808 [field_ setDelegate:self];
5810 [field_ setPaddingTop:5];
5812 UITextInputTraits *traits([field_ textInputTraits]);
5813 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5814 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5815 [traits setReturnKeyType:UIReturnKeySearch];
5817 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5819 accessory_ = [[UIView alloc] initWithFrame:accrect];
5820 [accessory_ addSubview:field_];
5822 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5823 [configure setShowPressFeedback:YES];
5824 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5825 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5826 [accessory_ addSubview:configure];*/
5828 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5829 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5835 LKAnimation *animation = [LKTransition animation];
5836 [animation setType:@"oglFlip"];
5837 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5838 [animation setFillMode:@"extended"];
5839 [animation setTransitionFlags:3];
5840 [animation setDuration:10];
5841 [animation setSpeed:0.35];
5842 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5843 [[transition_ _layer] addAnimation:animation forKey:0];
5844 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5845 flipped_ = !flipped_;
5849 - (void) configurePushed {
5850 [field_ resignFirstResponder];
5854 - (void) resetViewAnimated:(BOOL)animated {
5857 [table_ resetViewAnimated:animated];
5860 - (void) _reloadData {
5863 - (void) reloadData {
5866 [table_ setObject:[field_ text]];
5867 [table_ reloadData];
5868 [table_ resetCursor];
5871 - (UIView *) accessoryView {
5875 - (NSString *) title {
5879 - (NSString *) backButtonTitle {
5883 - (void) setDelegate:(id)delegate {
5884 [table_ setDelegate:delegate];
5885 [super setDelegate:delegate];
5891 @interface SettingsView : RVPage {
5892 _transient Database *database_;
5895 UIPreferencesTable *table_;
5896 _UISwitchSlider *subscribedSwitch_;
5897 _UISwitchSlider *ignoredSwitch_;
5898 UIPreferencesControlTableCell *subscribedCell_;
5899 UIPreferencesControlTableCell *ignoredCell_;
5902 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5906 @implementation SettingsView
5909 [table_ setDataSource:nil];
5912 if (package_ != nil)
5915 [subscribedSwitch_ release];
5916 [ignoredSwitch_ release];
5917 [subscribedCell_ release];
5918 [ignoredCell_ release];
5922 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5923 if (package_ == nil)
5929 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5930 if (package_ == nil)
5937 default: _assert(false);
5943 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5944 if (package_ == nil)
5951 default: _assert(false);
5957 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5958 if (package_ == nil)
5965 default: _assert(false);
5971 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5972 if (package_ == nil)
5975 _UISwitchSlider *slider([cell control]);
5976 BOOL value([slider value] != 0);
5977 NSMutableDictionary *metadata([package_ metadata]);
5980 if (NSNumber *number = [metadata objectForKey:key])
5981 before = [number boolValue];
5985 if (value != before) {
5986 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5988 [delegate_ updateData];
5992 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
5993 [self onSomething:cell withKey:@"IsSubscribed"];
5996 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
5997 [self onSomething:cell withKey:@"IsIgnored"];
6000 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6001 if (package_ == nil)
6005 case 0: switch (row) {
6007 return subscribedCell_;
6009 return ignoredCell_;
6010 default: _assert(false);
6013 case 1: switch (row) {
6015 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6016 [cell setShowSelection:NO];
6017 [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."];
6021 default: _assert(false);
6024 default: _assert(false);
6030 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6031 if ((self = [super initWithBook:book])) {
6032 database_ = database;
6033 name_ = [package retain];
6035 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6036 [self addSubview:table_];
6038 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6039 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6041 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6042 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6044 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6045 [subscribedCell_ setShowSelection:NO];
6046 [subscribedCell_ setTitle:@"Show All Changes"];
6047 [subscribedCell_ setControl:subscribedSwitch_];
6049 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6050 [ignoredCell_ setShowSelection:NO];
6051 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6052 [ignoredCell_ setControl:ignoredSwitch_];
6054 [table_ setDataSource:self];
6059 - (void) resetViewAnimated:(BOOL)animated {
6060 [table_ resetViewAnimated:animated];
6063 - (void) reloadData {
6064 if (package_ != nil)
6065 [package_ autorelease];
6066 package_ = [database_ packageWithName:name_];
6067 if (package_ != nil) {
6069 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6070 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6073 [table_ reloadData];
6076 - (NSString *) title {
6082 /* Signature View {{{ */
6083 @interface SignatureView : BrowserView {
6084 _transient Database *database_;
6088 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6092 @implementation SignatureView
6099 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6101 [super webView:sender didClearWindowObject:window forFrame:frame];
6104 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6105 if ((self = [super initWithBook:book]) != nil) {
6106 database_ = database;
6107 package_ = [package retain];
6112 - (void) resetViewAnimated:(BOOL)animated {
6115 - (void) reloadData {
6116 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6122 @interface Cydia : UIApplication <
6123 ConfirmationViewDelegate,
6124 ProgressViewDelegate,
6133 UIToolbar *buttonbar_;
6137 NSMutableArray *essential_;
6138 NSMutableArray *broken_;
6140 Database *database_;
6141 ProgressView *progress_;
6145 UIKeyboard *keyboard_;
6146 UIProgressHUD *hud_;
6148 SectionsView *sections_;
6149 ChangesView *changes_;
6150 ManageView *manage_;
6151 SearchView *search_;
6156 @implementation Cydia
6159 if ([broken_ count] != 0) {
6160 int count = [broken_ count];
6162 UIActionSheet *sheet = [[[UIActionSheet alloc]
6163 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6164 buttons:[NSArray arrayWithObjects:
6166 @"Ignore (Temporary)",
6168 defaultButtonIndex:0
6173 [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."];
6174 [sheet popupAlertAnimated:YES];
6175 } else if (!Ignored_ && [essential_ count] != 0) {
6176 int count = [essential_ count];
6178 UIActionSheet *sheet = [[[UIActionSheet alloc]
6179 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6180 buttons:[NSArray arrayWithObjects:
6181 @"Upgrade Essential",
6182 @"Complete Upgrade",
6183 @"Ignore (Temporary)",
6185 defaultButtonIndex:0
6190 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6191 [sheet popupAlertAnimated:YES];
6195 - (void) _reloadData {
6196 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6197 [hud setText:@"Reloading Data"];
6198 [overlay_ addSubview:hud];
6201 [database_ reloadData];
6205 [essential_ removeAllObjects];
6206 [broken_ removeAllObjects];
6208 NSArray *packages = [database_ packages];
6209 for (Package *package in packages) {
6211 [broken_ addObject:package];
6212 if ([package upgradableAndEssential:NO]) {
6213 if ([package essential])
6214 [essential_ addObject:package];
6220 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6221 [buttonbar_ setBadgeValue:badge forButton:3];
6222 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6223 [buttonbar_ setBadgeAnimated:YES forButton:3];
6224 [self setApplicationBadge:badge];
6226 [buttonbar_ setBadgeValue:nil forButton:3];
6227 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6228 [buttonbar_ setBadgeAnimated:NO forButton:3];
6229 [self removeApplicationBadge];
6234 // XXX: what is this line of code for?
6235 if ([packages count] == 0);
6236 else if (Loaded_) loaded:
6241 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6242 NSTimeInterval interval([update timeIntervalSinceNow]);
6243 if (interval <= 0 && interval > -600)
6251 [hud removeFromSuperview];*/
6254 - (void) _saveConfig {
6257 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6263 - (void) updateData {
6266 /* XXX: this is just stupid */
6267 if (tag_ != 2 && sections_ != nil)
6268 [sections_ reloadData];
6269 if (tag_ != 3 && changes_ != nil)
6270 [changes_ reloadData];
6271 if (tag_ != 5 && search_ != nil)
6272 [search_ reloadData];
6282 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6283 _assert(file != NULL);
6285 NSArray *keys = [Sources_ allKeys];
6287 for (int i(0), e([keys count]); i != e; ++i) {
6288 NSString *key = [keys objectAtIndex:i];
6289 NSDictionary *source = [Sources_ objectForKey:key];
6291 fprintf(file, "%s %s %s\n",
6292 [[source objectForKey:@"Type"] UTF8String],
6293 [[source objectForKey:@"URI"] UTF8String],
6294 [[source objectForKey:@"Distribution"] UTF8String]
6303 detachNewThreadSelector:@selector(update_)
6306 title:@"Updating Sources"
6310 - (void) reloadData {
6311 @synchronized (self) {
6312 if (confirm_ == nil)
6318 pkgProblemResolver *resolver = [database_ resolver];
6320 resolver->InstallProtect();
6321 if (!resolver->Resolve(true))
6325 - (void) popUpBook:(RVBook *)book {
6326 [underlay_ popSubview:book];
6329 - (CGRect) popUpBounds {
6330 return [underlay_ bounds];
6334 [database_ prepare];
6336 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6337 [confirm_ setDelegate:self];
6339 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6340 [page setDelegate:self];
6342 [confirm_ setPage:page];
6343 [self popUpBook:confirm_];
6346 - (void) installPackage:(Package *)package {
6347 @synchronized (self) {
6354 - (void) removePackage:(Package *)package {
6355 @synchronized (self) {
6362 - (void) distUpgrade {
6363 @synchronized (self) {
6364 [database_ upgrade];
6370 @synchronized (self) {
6372 if (confirm_ != nil) {
6380 [overlay_ removeFromSuperview];
6384 detachNewThreadSelector:@selector(perform)
6391 - (void) bootstrap_ {
6393 [database_ upgrade];
6394 [database_ prepare];
6395 [database_ perform];
6398 - (void) bootstrap {
6400 detachNewThreadSelector:@selector(bootstrap_)
6403 title:@"Bootstrap Install"
6407 - (void) progressViewIsComplete:(ProgressView *)progress {
6408 if (confirm_ != nil) {
6409 [underlay_ addSubview:overlay_];
6410 [confirm_ popFromSuperviewAnimated:NO];
6416 - (void) setPage:(RVPage *)page {
6417 [page resetViewAnimated:NO];
6418 [page setDelegate:self];
6419 [book_ setPage:page];
6422 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6423 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6424 [browser loadURL:url];
6428 - (void) _setHomePage {
6429 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6432 - (void) buttonBarItemTapped:(id)sender {
6433 unsigned tag = [sender tag];
6435 [book_ resetViewAnimated:YES];
6437 } else if (tag_ == 2 && tag != 2)
6438 [sections_ resetView];
6441 case 1: [self _setHomePage]; break;
6443 case 2: [self setPage:sections_]; break;
6444 case 3: [self setPage:changes_]; break;
6445 case 4: [self setPage:manage_]; break;
6446 case 5: [self setPage:search_]; break;
6448 default: _assert(false);
6454 - (void) applicationWillSuspend {
6456 [super applicationWillSuspend];
6459 - (void) askForSettings {
6460 UIActionSheet *role = [[[UIActionSheet alloc]
6461 initWithTitle:@"Who Are You?"
6462 buttons:[NSArray arrayWithObjects:
6463 @"User (Graphical Only)",
6464 @"Hacker (+ Command Line)",
6465 @"Developer (No Filters)",
6467 defaultButtonIndex:-1
6472 [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."];
6473 [role popupAlertAnimated:YES];
6478 [self setStatusBarShowsProgress:NO];
6481 [hud_ removeFromSuperview];
6485 pid_t pid = ExecFork();
6487 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6488 perror("launchctl stop");
6495 [self askForSettings];
6499 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6501 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6502 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6503 0, 0, screenrect.size.width, screenrect.size.height - 48
6504 ) database:database_];
6506 [book_ setDelegate:self];
6508 [overlay_ addSubview:book_];
6510 NSArray *buttonitems = [NSArray arrayWithObjects:
6511 [NSDictionary dictionaryWithObjectsAndKeys:
6512 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6513 @"home-up.png", kUIButtonBarButtonInfo,
6514 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6515 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6516 self, kUIButtonBarButtonTarget,
6517 @"Home", kUIButtonBarButtonTitle,
6518 @"0", kUIButtonBarButtonType,
6521 [NSDictionary dictionaryWithObjectsAndKeys:
6522 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6523 @"install-up.png", kUIButtonBarButtonInfo,
6524 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6525 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6526 self, kUIButtonBarButtonTarget,
6527 @"Sections", kUIButtonBarButtonTitle,
6528 @"0", kUIButtonBarButtonType,
6531 [NSDictionary dictionaryWithObjectsAndKeys:
6532 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6533 @"changes-up.png", kUIButtonBarButtonInfo,
6534 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6535 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6536 self, kUIButtonBarButtonTarget,
6537 @"Changes", kUIButtonBarButtonTitle,
6538 @"0", kUIButtonBarButtonType,
6541 [NSDictionary dictionaryWithObjectsAndKeys:
6542 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6543 @"manage-up.png", kUIButtonBarButtonInfo,
6544 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6545 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6546 self, kUIButtonBarButtonTarget,
6547 @"Manage", kUIButtonBarButtonTitle,
6548 @"0", kUIButtonBarButtonType,
6551 [NSDictionary dictionaryWithObjectsAndKeys:
6552 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6553 @"search-up.png", kUIButtonBarButtonInfo,
6554 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6555 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6556 self, kUIButtonBarButtonTarget,
6557 @"Search", kUIButtonBarButtonTitle,
6558 @"0", kUIButtonBarButtonType,
6562 buttonbar_ = [[UIToolbar alloc]
6564 withFrame:CGRectMake(
6565 0, screenrect.size.height - ButtonBarHeight_,
6566 screenrect.size.width, ButtonBarHeight_
6568 withItemList:buttonitems
6571 [buttonbar_ setDelegate:self];
6572 [buttonbar_ setBarStyle:1];
6573 [buttonbar_ setButtonBarTrackingMode:2];
6575 int buttons[5] = {1, 2, 3, 4, 5};
6576 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6577 [buttonbar_ showButtonGroup:0 withDuration:0];
6579 for (int i = 0; i != 5; ++i)
6580 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6581 i * 64 + 2, 1, 60, ButtonBarHeight_
6584 [buttonbar_ showSelectionForButton:1];
6585 [overlay_ addSubview:buttonbar_];
6587 [UIKeyboard initImplementationNow];
6588 CGSize keysize = [UIKeyboard defaultSize];
6589 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6590 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6591 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6592 [overlay_ addSubview:keyboard_];
6595 [underlay_ addSubview:overlay_];
6599 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6600 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6601 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6603 manage_ = (ManageView *) [[self
6604 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6605 withClass:[ManageView class]
6611 [self _setHomePage];
6614 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6615 NSString *context([sheet context]);
6617 if ([context isEqualToString:@"missing"])
6619 else if ([context isEqualToString:@"fixhalf"]) {
6622 @synchronized (self) {
6623 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6624 Package *broken = [broken_ objectAtIndex:i];
6627 NSString *id = [broken id];
6628 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6629 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6630 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6631 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6640 [broken_ removeAllObjects];
6649 } else if ([context isEqualToString:@"role"]) {
6651 case 1: Role_ = @"User"; break;
6652 case 2: Role_ = @"Hacker"; break;
6653 case 3: Role_ = @"Developer"; break;
6660 bool reset = Settings_ != nil;
6662 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6666 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6676 } else if ([context isEqualToString:@"upgrade"]) {
6679 @synchronized (self) {
6680 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6681 Package *essential = [essential_ objectAtIndex:i];
6682 [essential install];
6706 - (void) reorganize { _pooled
6707 system("/usr/libexec/cydia/free.sh");
6708 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6711 - (void) applicationSuspend:(__GSEvent *)event {
6712 if (hud_ == nil && ![progress_ isRunning])
6713 [super applicationSuspend:event];
6716 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6718 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6721 - (void) _setSuspended:(BOOL)value {
6723 [super _setSuspended:value];
6726 - (UIProgressHUD *) addProgressHUD {
6727 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6729 [underlay_ addSubview:hud];
6733 - (void) openMailToURL:(NSURL *)url {
6734 // XXX: this makes me sad
6736 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6738 [UIApp openURL:url];// asPanel:YES];
6742 - (void) clearFirstResponder {
6743 if (id responder = [window_ firstResponder])
6744 [responder resignFirstResponder];
6747 - (RVPage *) pageForPackage:(NSString *)name {
6748 if (Package *package = [database_ packageWithName:name]) {
6749 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6750 [view setPackage:package];
6753 UIActionSheet *sheet = [[[UIActionSheet alloc]
6754 initWithTitle:@"Cannot Locate Package"
6755 buttons:[NSArray arrayWithObjects:@"Close", nil]
6756 defaultButtonIndex:0
6761 [sheet setBodyText:[NSString stringWithFormat:
6762 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6765 [sheet popupAlertAnimated:YES];
6770 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6774 NSString *scheme([[url scheme] lowercaseString]);
6775 if (![scheme isEqualToString:@"cydia"])
6777 NSString *path([url absoluteString]);
6778 if ([path length] < 8)
6780 path = [path substringFromIndex:8];
6781 if (![path hasPrefix:@"/"])
6782 path = [@"/" stringByAppendingString:path];
6784 if ([path isEqualToString:@"/add-source"])
6785 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6786 else if ([path isEqualToString:@"/storage"])
6787 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6788 else if ([path isEqualToString:@"/sources"])
6789 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6790 else if ([path isEqualToString:@"/packages"])
6791 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6792 else if ([path hasPrefix:@"/url/"])
6793 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6794 else if ([path hasPrefix:@"/launch/"])
6795 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6796 else if ([path hasPrefix:@"/package-settings/"])
6797 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6798 else if ([path hasPrefix:@"/package-signature/"])
6799 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6800 else if ([path hasPrefix:@"/package/"])
6801 return [self pageForPackage:[path substringFromIndex:9]];
6802 else if ([path hasPrefix:@"/files/"]) {
6803 NSString *name = [path substringFromIndex:7];
6805 if (Package *package = [database_ packageWithName:name]) {
6806 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6807 [files setPackage:package];
6815 - (void) applicationOpenURL:(NSURL *)url {
6816 [super applicationOpenURL:url];
6818 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6819 [self setPage:page];
6820 [buttonbar_ showSelectionForButton:tag];
6825 - (void) applicationDidFinishLaunching:(id)unused {
6826 Font12_ = [[UIFont systemFontOfSize:12] retain];
6827 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6828 Font14_ = [[UIFont systemFontOfSize:14] retain];
6829 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6830 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6832 _assert(pkgInitConfig(*_config));
6833 _assert(pkgInitSystem(*_config, _system));
6837 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6838 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6840 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6842 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6843 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6845 [window_ orderFront:self];
6846 [window_ makeKey:self];
6847 [window_ setHidden:NO];
6849 database_ = [Database sharedInstance];
6850 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6851 [database_ setDelegate:progress_];
6852 [window_ setContentView:progress_];
6854 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6855 [progress_ setContentView:underlay_];
6857 [progress_ resetView];
6860 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6861 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6862 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6863 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6864 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6865 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6866 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6868 [self setIdleTimerDisabled:YES];
6870 hud_ = [self addProgressHUD];
6871 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6873 [self setStatusBarShowsProgress:YES];
6876 detachNewThreadSelector:@selector(reorganize)
6884 - (void) showKeyboard:(BOOL)show {
6885 CGSize keysize = [UIKeyboard defaultSize];
6886 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6887 CGRect keyup = keydown;
6888 keyup.origin.y -= keysize.height;
6890 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6891 [animation setSignificantRectFields:2];
6894 [animation setStartFrame:keydown];
6895 [animation setEndFrame:keyup];
6896 [keyboard_ activate];
6898 [animation setStartFrame:keyup];
6899 [animation setEndFrame:keydown];
6900 [keyboard_ deactivate];
6903 [[UIAnimator sharedAnimator]
6904 addAnimations:[NSArray arrayWithObjects:animation, nil]
6905 withDuration:KeyboardTime_
6910 - (void) slideUp:(UIActionSheet *)alert {
6912 [alert presentSheetFromButtonBar:buttonbar_];
6914 [alert presentSheetInView:overlay_];
6919 void AddPreferences(NSString *plist) { _pooled
6920 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6921 _assert(settings != NULL);
6922 NSMutableArray *items = [settings objectForKey:@"items"];
6926 for (size_t i(0); i != [items count]; ++i) {
6927 NSMutableDictionary *item([items objectAtIndex:i]);
6928 NSString *label = [item objectForKey:@"label"];
6929 if (label != nil && [label isEqualToString:@"Cydia"]) {
6936 for (size_t i(0); i != [items count]; ++i) {
6937 NSDictionary *item([items objectAtIndex:i]);
6938 NSString *label = [item objectForKey:@"label"];
6939 if (label != nil && [label isEqualToString:@"General"]) {
6940 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6941 @"CydiaSettings", @"bundle",
6942 @"PSLinkCell", @"cell",
6943 [NSNumber numberWithBool:YES], @"hasIcon",
6944 [NSNumber numberWithBool:YES], @"isController",
6946 nil] atIndex:(i + 1)];
6952 _assert([settings writeToFile:plist atomically:YES] == YES);
6957 id Alloc_(id self, SEL selector) {
6958 id object = alloc_(self, selector);
6959 lprintf("[%s]A-%p\n", self->isa->name, object);
6964 id Dealloc_(id self, SEL selector) {
6965 id object = dealloc_(self, selector);
6966 lprintf("[%s]D-%p\n", self->isa->name, object);
6970 int main(int argc, char *argv[]) { _pooled
6972 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6974 bool substrate(false);
6980 for (int argi(1); argi != argc; ++argi)
6981 if (strcmp(argv[argi], "--") == 0) {
6983 argv[argi] = argv[0];
6989 for (int argi(1); argi != arge; ++argi)
6990 if (strcmp(args[argi], "--bootstrap") == 0)
6992 else if (strcmp(args[argi], "--substrate") == 0)
6995 fprintf(stderr, "unknown argument: %s\n", args[argi]);
6998 App_ = [[NSBundle mainBundle] bundlePath];
6999 Home_ = NSHomeDirectory();
7000 Locale_ = CFLocaleCopyCurrent();
7003 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7004 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7005 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7006 Sounds_Keyboard_ = [keyboard boolValue];
7012 #if 1 /* XXX: this costs 1.4s of startup performance */
7013 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7014 _assert(errno == ENOENT);
7015 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7016 _assert(errno == ENOENT);
7019 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7020 alloc_ = alloc->method_imp;
7021 alloc->method_imp = (IMP) &Alloc_;*/
7023 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7024 dealloc_ = dealloc->method_imp;
7025 dealloc->method_imp = (IMP) &Dealloc_;*/
7030 size = sizeof(maxproc);
7031 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7032 perror("sysctlbyname(\"kern.maxproc\", ?)");
7033 else if (maxproc < 64) {
7035 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7036 perror("sysctlbyname(\"kern.maxproc\", #)");
7039 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7040 char *machine = new char[size];
7041 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7042 perror("sysctlbyname(\"hw.machine\", ?)");
7046 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7048 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7049 Build_ = [system objectForKey:@"ProductBuildVersion"];
7051 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7052 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7055 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7058 if (Metadata_ == NULL)
7059 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7061 Settings_ = [Metadata_ objectForKey:@"Settings"];
7063 Packages_ = [Metadata_ objectForKey:@"Packages"];
7064 Sections_ = [Metadata_ objectForKey:@"Sections"];
7065 Sources_ = [Metadata_ objectForKey:@"Sources"];
7068 if (Settings_ != nil)
7069 Role_ = [Settings_ objectForKey:@"Role"];
7071 if (Packages_ == nil) {
7072 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7073 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7076 if (Sections_ == nil) {
7077 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7078 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7081 if (Sources_ == nil) {
7082 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7083 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7087 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7090 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7091 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7092 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7093 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7095 if (access("/User", F_OK) != 0) {
7097 system("/usr/libexec/cydia/firmware.sh");
7101 _assert([[NSFileManager defaultManager]
7102 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7103 withIntermediateDirectories:YES
7108 space_ = CGColorSpaceCreateDeviceRGB();
7110 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7111 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7112 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7113 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7114 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7115 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7117 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7119 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7121 UIApplicationUseLegacyEvents(YES);
7122 UIKeyboardDisableAutomaticAppearance();
7125 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7127 CGColorSpaceRelease(space_);