1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 // XXX: wtf/FastMalloc.h... wtf?
39 #define USE_SYSTEM_MALLOC 1
41 /* #include Directives {{{ */
42 #import "UICaboodle.h"
44 #include <objc/objc.h>
45 #include <objc/runtime.h>
47 #include <CoreGraphics/CoreGraphics.h>
48 #include <GraphicsServices/GraphicsServices.h>
49 #include <Foundation/Foundation.h>
51 #import <QuartzCore/CALayer.h>
53 #import <UIKit/UIKit.h>
56 #import <MessageUI/MailComposeController.h>
61 #include <ext/stdio_filebuf.h>
63 #include <apt-pkg/acquire.h>
64 #include <apt-pkg/acquire-item.h>
65 #include <apt-pkg/algorithms.h>
66 #include <apt-pkg/cachefile.h>
67 #include <apt-pkg/clean.h>
68 #include <apt-pkg/configuration.h>
69 #include <apt-pkg/debmetaindex.h>
70 #include <apt-pkg/error.h>
71 #include <apt-pkg/init.h>
72 #include <apt-pkg/mmap.h>
73 #include <apt-pkg/pkgrecords.h>
74 #include <apt-pkg/sha1.h>
75 #include <apt-pkg/sourcelist.h>
76 #include <apt-pkg/sptr.h>
77 #include <apt-pkg/strutl.h>
79 #include <sys/types.h>
81 #include <sys/sysctl.h>
82 #include <sys/param.h>
83 #include <sys/mount.h>
89 #include <mach-o/nlist.h>
99 #import "BrowserView.h"
100 #import "ResetView.h"
102 #import "substrate.h"
105 //#define _finline __attribute__((force_inline))
106 #define _finline inline
111 #define _limit(count) do { \
112 static size_t _count(0); \
113 if (++_count == count) \
117 static uint64_t profile_;
119 #define _timestamp ({ \
121 gettimeofday(&tv, NULL); \
122 tv.tv_sec * 1000000 + tv.tv_usec; \
125 /* Objective-C Handle<> {{{ */
126 template <typename Type_>
128 typedef _H<Type_> This_;
133 _finline void Retain_() {
138 _finline void Clear_() {
144 _finline _H(Type_ *value = NULL, bool mended = false) :
155 _finline This_ &operator =(Type_ *value) {
156 if (value_ != value) {
165 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
167 void NSLogPoint(const char *fix, const CGPoint &point) {
168 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
171 void NSLogRect(const char *fix, const CGRect &rect) {
172 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
175 /* NSForcedOrderingSearch doesn't work on the iPhone */
176 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
177 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
178 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
180 /* iPhoneOS 2.0 Compatibility {{{ */
182 @interface UITextView (iPhoneOS)
183 - (void) setTextSize:(float)size;
186 @implementation UITextView (iPhoneOS)
188 - (void) setTextSize:(float)size {
189 [self setFont:[[self font] fontWithSize:size]];
196 extern NSString * const kCAFilterNearest;
198 /* Information Dictionaries {{{ */
199 @interface NSMutableArray (Cydia)
200 - (void) addInfoDictionary:(NSDictionary *)info;
203 @implementation NSMutableArray (Cydia)
205 - (void) addInfoDictionary:(NSDictionary *)info {
206 [self addObject:info];
211 @interface NSMutableDictionary (Cydia)
212 - (void) addInfoDictionary:(NSDictionary *)info;
215 @implementation NSMutableDictionary (Cydia)
217 - (void) addInfoDictionary:(NSDictionary *)info {
218 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
219 [self setObject:info forKey:bundle];
224 /* Pop Transitions {{{ */
225 @interface PopTransitionView : UITransitionView {
230 @implementation PopTransitionView
232 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
233 if (from != nil && to == nil)
234 [self removeFromSuperview];
239 @interface UIView (PopUpView)
240 - (void) popFromSuperviewAnimated:(BOOL)animated;
241 - (void) popSubview:(UIView *)view;
244 @implementation UIView (PopUpView)
246 - (void) popFromSuperviewAnimated:(BOOL)animated {
247 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
250 - (void) popSubview:(UIView *)view {
251 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
252 [transition setDelegate:transition];
253 [self addSubview:transition];
255 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
256 [transition transition:UITransitionNone toView:blank];
257 [transition transition:UITransitionPushFromBottom toView:view];
263 #define lprintf(args...) fprintf(stderr, args)
266 #define ForSaurik (1 && !ForRelease)
267 #define IgnoreInstall (0 && !ForRelease)
268 #define RecycleWebViews 0
269 #define AlwaysReload (1 && !ForRelease)
273 #define _trace(args...)
277 @interface NSMutableArray (Radix)
278 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
281 @implementation NSMutableArray (Radix)
283 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
284 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
285 [invocation setSelector:selector];
286 [invocation setArgument:&object atIndex:2];
288 size_t count([self count]);
293 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
295 for (size_t i(0); i != count; ++i) {
296 RadixItem &item(lhs[i]);
299 id object([self objectAtIndex:i]);
300 [invocation setTarget:object];
303 [invocation getReturnValue:&item.key];
306 static const size_t width = 32;
307 static const size_t bits = 11;
308 static const size_t slots = 1 << bits;
309 static const size_t passes = (width + (bits - 1)) / bits;
311 size_t *hist(new size_t[slots]);
313 for (size_t pass(0); pass != passes; ++pass) {
314 memset(hist, 0, sizeof(size_t) * slots);
316 for (size_t i(0); i != count; ++i) {
317 uint32_t key(lhs[i].key);
319 key &= _not(uint32_t) >> width - bits;
324 for (size_t i(0); i != slots; ++i) {
325 size_t local(offset);
330 for (size_t i(0); i != count; ++i) {
331 uint32_t key(lhs[i].key);
333 key &= _not(uint32_t) >> width - bits;
334 rhs[hist[key]++] = lhs[i];
344 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
345 for (size_t i(0); i != count; ++i)
346 [values addObject:[self objectAtIndex:lhs[i].index]];
347 [self setArray:values];
355 /* Apple Bug Fixes {{{ */
356 @implementation UIWebDocumentView (Cydia)
358 - (void) _setScrollerOffset:(CGPoint)offset {
359 UIScroller *scroller([self _scroller]);
361 CGSize size([scroller contentSize]);
362 CGSize bounds([scroller bounds].size);
365 max.x = size.width - bounds.width;
366 max.y = size.height - bounds.height;
374 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
375 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
377 [scroller setOffset:offset];
384 kUIControlEventMouseDown = 1 << 0,
385 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
386 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
387 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
388 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
389 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
390 } UIControlEventMasks;
392 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
393 size_t length([self length] - state->state);
396 else if (length > count)
398 for (size_t i(0); i != length; ++i)
399 objects[i] = [self item:state->state++];
400 state->itemsPtr = objects;
401 state->mutationsPtr = (unsigned long *) self;
405 @interface NSString (UIKit)
406 - (NSString *) stringByAddingPercentEscapes;
407 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
410 @interface NSString (Cydia)
411 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
412 - (NSComparisonResult) compareByPath:(NSString *)other;
415 @implementation NSString (Cydia)
417 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
418 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
421 - (NSComparisonResult) compareByPath:(NSString *)other {
422 NSString *prefix = [self commonPrefixWithString:other options:0];
423 size_t length = [prefix length];
425 NSRange lrange = NSMakeRange(length, [self length] - length);
426 NSRange rrange = NSMakeRange(length, [other length] - length);
428 lrange = [self rangeOfString:@"/" options:0 range:lrange];
429 rrange = [other rangeOfString:@"/" options:0 range:rrange];
431 NSComparisonResult value;
433 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
434 value = NSOrderedSame;
435 else if (lrange.location == NSNotFound)
436 value = NSOrderedAscending;
437 else if (rrange.location == NSNotFound)
438 value = NSOrderedDescending;
440 value = NSOrderedSame;
442 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
443 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
444 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
445 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
447 NSComparisonResult result = [lpath compare:rpath];
448 return result == NSOrderedSame ? value : result;
453 /* Perl-Compatible RegEx {{{ */
463 Pcre(const char *regex) :
468 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
471 lprintf("%d:%s\n", offset, error);
475 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
476 matches_ = new int[(capture_ + 1) * 3];
484 NSString *operator [](size_t match) {
485 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
488 bool operator ()(NSString *data) {
489 // XXX: length is for characters, not for bytes
490 return operator ()([data UTF8String], [data length]);
493 bool operator ()(const char *data, size_t size) {
495 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
499 /* Mime Addresses {{{ */
500 @interface Address : NSObject {
506 - (NSString *) address;
508 + (Address *) addressWithString:(NSString *)string;
509 - (Address *) initWithString:(NSString *)string;
512 @implementation Address
521 - (NSString *) name {
525 - (NSString *) address {
529 + (Address *) addressWithString:(NSString *)string {
530 return [[[Address alloc] initWithString:string] autorelease];
533 + (NSArray *) _attributeKeys {
534 return [NSArray arrayWithObjects:@"address", @"name", nil];
537 - (NSArray *) attributeKeys {
538 return [[self class] _attributeKeys];
541 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
542 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
545 - (Address *) initWithString:(NSString *)string {
546 if ((self = [super init]) != nil) {
547 const char *data = [string UTF8String];
548 size_t size = [string length];
550 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
552 if (address_r(data, size)) {
553 name_ = [address_r[1] retain];
554 address_ = [address_r[2] retain];
556 name_ = [string retain];
564 /* CoreGraphics Primitives {{{ */
575 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
578 Set(space, red, green, blue, alpha);
583 CGColorRelease(color_);
590 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
592 float color[] = {red, green, blue, alpha};
593 color_ = CGColorCreate(space, color);
596 operator CGColorRef() {
602 extern "C" void UISetColor(CGColorRef color);
604 /* Random Global Variables {{{ */
605 static const int PulseInterval_ = 50000;
606 static const int ButtonBarHeight_ = 48;
607 static const float KeyboardTime_ = 0.3f;
609 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
610 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
611 #define NotifyConfig_ "/etc/notify.conf"
613 static CGColor Blue_;
614 static CGColor Blueish_;
615 static CGColor Black_;
617 static CGColor White_;
618 static CGColor Gray_;
620 static NSString *App_;
621 static NSString *Home_;
622 static BOOL Sounds_Keyboard_;
624 static BOOL Advanced_;
628 static BOOL Ignored_;
630 static UIFont *Font12_;
631 static UIFont *Font12Bold_;
632 static UIFont *Font14_;
633 static UIFont *Font18Bold_;
634 static UIFont *Font22Bold_;
636 static const char *Machine_ = NULL;
637 static const NSString *UniqueID_ = nil;
638 static const NSString *Build_ = nil;
641 CGColorSpaceRef space_;
646 static NSDictionary *SectionMap_;
647 static NSMutableDictionary *Metadata_;
648 static NSMutableDictionary *Indices_;
649 static _transient NSMutableDictionary *Settings_;
650 static _transient NSString *Role_;
651 static _transient NSMutableDictionary *Packages_;
652 static _transient NSMutableDictionary *Sections_;
653 static _transient NSMutableDictionary *Sources_;
654 static bool Changed_;
658 static NSMutableArray *Documents_;
661 NSString *GetLastUpdate() {
662 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
665 return @"Never or Unknown";
667 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
668 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
670 CFRelease(formatter);
672 return [(NSString *) formatted autorelease];
675 /* Display Helpers {{{ */
676 inline float Interpolate(float begin, float end, float fraction) {
677 return (end - begin) * fraction + begin;
680 NSString *SizeString(double size) {
681 bool negative = size < 0;
686 while (size > 1024) {
691 static const char *powers_[] = {"B", "kB", "MB", "GB"};
693 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
696 NSString *StripVersion(NSString *version) {
697 NSRange colon = [version rangeOfString:@":"];
698 if (colon.location != NSNotFound)
699 version = [version substringFromIndex:(colon.location + 1)];
703 NSString *Simplify(NSString *title) {
704 const char *data = [title UTF8String];
705 size_t size = [title length];
707 static Pcre square_r("^\\[(.*)\\]$");
708 if (square_r(data, size))
709 return Simplify(square_r[1]);
711 static Pcre paren_r("^\\((.*)\\)$");
712 if (paren_r(data, size))
713 return Simplify(paren_r[1]);
715 static Pcre title_r("^(.*?) \\(.*\\)$");
716 if (title_r(data, size))
717 return Simplify(title_r[1]);
723 bool isSectionVisible(NSString *section) {
724 NSDictionary *metadata = [Sections_ objectForKey:section];
725 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
726 return hidden == nil || ![hidden boolValue];
729 /* Delegate Prototypes {{{ */
733 @interface NSObject (ProgressDelegate)
736 @implementation NSObject(ProgressDelegate)
738 - (void) _setProgressError:(NSArray *)args {
739 [self performSelector:@selector(setProgressError:forPackage:)
740 withObject:[args objectAtIndex:0]
741 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
747 @protocol ProgressDelegate
748 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
749 - (void) setProgressTitle:(NSString *)title;
750 - (void) setProgressPercent:(float)percent;
751 - (void) startProgress;
752 - (void) addProgressOutput:(NSString *)output;
753 - (bool) isCancelling:(size_t)received;
756 @protocol ConfigurationDelegate
757 - (void) repairWithSelector:(SEL)selector;
758 - (void) setConfigurationData:(NSString *)data;
761 @protocol CydiaDelegate
762 - (void) installPackage:(Package *)package;
763 - (void) removePackage:(Package *)package;
764 - (void) slideUp:(UIActionSheet *)alert;
765 - (void) distUpgrade;
768 - (void) askForSettings;
769 - (UIProgressHUD *) addProgressHUD;
770 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
771 - (RVPage *) pageForPackage:(NSString *)name;
772 - (void) openMailToURL:(NSURL *)url;
773 - (void) clearFirstResponder;
777 /* Status Delegation {{{ */
779 public pkgAcquireStatus
782 _transient NSObject<ProgressDelegate> *delegate_;
790 void setDelegate(id delegate) {
791 delegate_ = delegate;
794 virtual bool MediaChange(std::string media, std::string drive) {
798 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
801 virtual void Fetch(pkgAcquire::ItemDesc &item) {
802 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
803 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
806 virtual void Done(pkgAcquire::ItemDesc &item) {
809 virtual void Fail(pkgAcquire::ItemDesc &item) {
811 item.Owner->Status == pkgAcquire::Item::StatIdle ||
812 item.Owner->Status == pkgAcquire::Item::StatDone
816 std::string &error(item.Owner->ErrorText);
820 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
821 NSArray *fields([description componentsSeparatedByString:@" "]);
822 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
824 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
825 withObject:[NSArray arrayWithObjects:
826 [NSString stringWithUTF8String:error.c_str()],
833 virtual bool Pulse(pkgAcquire *Owner) {
834 bool value = pkgAcquireStatus::Pulse(Owner);
837 double(CurrentBytes + CurrentItems) /
838 double(TotalBytes + TotalItems)
841 [delegate_ setProgressPercent:percent];
842 return [delegate_ isCancelling:CurrentBytes] ? false : value;
845 virtual void Start() {
846 [delegate_ startProgress];
849 virtual void Stop() {
853 /* Progress Delegation {{{ */
858 _transient id<ProgressDelegate> delegate_;
861 virtual void Update() {
862 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
863 [delegate_ setProgressPercent:(Percent / 100)];
872 void setDelegate(id delegate) {
873 delegate_ = delegate;
876 virtual void Done() {
877 [delegate_ setProgressPercent:1];
882 /* Database Interface {{{ */
883 @interface Database : NSObject {
885 pkgDepCache::Policy *policy_;
886 pkgRecords *records_;
887 pkgProblemResolver *resolver_;
888 pkgAcquire *fetcher_;
890 SPtr<pkgPackageManager> manager_;
891 pkgSourceList *list_;
893 NSMutableDictionary *sources_;
894 NSMutableArray *packages_;
896 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
905 + (Database *) sharedInstance;
907 - (void) _readCydia:(NSNumber *)fd;
908 - (void) _readStatus:(NSNumber *)fd;
909 - (void) _readOutput:(NSNumber *)fd;
913 - (Package *) packageWithName:(NSString *)name;
915 - (pkgCacheFile &) cache;
916 - (pkgDepCache::Policy *) policy;
917 - (pkgRecords *) records;
918 - (pkgProblemResolver *) resolver;
919 - (pkgAcquire &) fetcher;
920 - (pkgSourceList &) list;
921 - (NSArray *) packages;
922 - (NSArray *) sources;
931 - (void) updateWithStatus:(Status &)status;
933 - (void) setDelegate:(id)delegate;
934 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
938 /* Source Class {{{ */
939 @interface Source : NSObject {
940 NSString *description_;
945 NSString *distribution_;
949 NSString *defaultIcon_;
951 NSDictionary *record_;
955 - (Source *) initWithMetaIndex:(metaIndex *)index;
957 - (NSComparisonResult) compareByNameAndType:(Source *)source;
959 - (NSDictionary *) record;
963 - (NSString *) distribution;
969 - (NSString *) description;
970 - (NSString *) label;
971 - (NSString *) origin;
972 - (NSString *) version;
974 - (NSString *) defaultIcon;
978 @implementation Source
980 #define _clear(field) \
987 _clear(distribution_)
1003 + (NSArray *) _attributeKeys {
1004 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1007 - (NSArray *) attributeKeys {
1008 return [[self class] _attributeKeys];
1011 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1012 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1015 - (void) setMetaIndex:(metaIndex *)index {
1018 trusted_ = index->IsTrusted();
1020 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1021 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1022 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1024 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1025 if (dindex != NULL) {
1026 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1028 while (std::getline(release, line)) {
1029 std::string::size_type colon(line.find(':'));
1030 if (colon == std::string::npos)
1033 std::string name(line.substr(0, colon));
1034 std::string value(line.substr(colon + 1));
1035 while (!value.empty() && value[0] == ' ')
1036 value = value.substr(1);
1038 if (name == "Default-Icon")
1039 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1040 else if (name == "Description")
1041 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1042 else if (name == "Label")
1043 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1044 else if (name == "Origin")
1045 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1046 else if (name == "Version")
1047 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1051 record_ = [Sources_ objectForKey:[self key]];
1053 record_ = [record_ retain];
1056 - (Source *) initWithMetaIndex:(metaIndex *)index {
1057 if ((self = [super init]) != nil) {
1058 [self setMetaIndex:index];
1062 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1063 NSDictionary *lhr = [self record];
1064 NSDictionary *rhr = [source record];
1067 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1069 NSString *lhs = [self name];
1070 NSString *rhs = [source name];
1072 if ([lhs length] != 0 && [rhs length] != 0) {
1073 unichar lhc = [lhs characterAtIndex:0];
1074 unichar rhc = [rhs characterAtIndex:0];
1076 if (isalpha(lhc) && !isalpha(rhc))
1077 return NSOrderedAscending;
1078 else if (!isalpha(lhc) && isalpha(rhc))
1079 return NSOrderedDescending;
1082 return [lhs compare:rhs options:LaxCompareOptions_];
1085 - (NSDictionary *) record {
1093 - (NSString *) uri {
1097 - (NSString *) distribution {
1098 return distribution_;
1101 - (NSString *) type {
1105 - (NSString *) key {
1106 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1109 - (NSString *) host {
1110 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1113 - (NSString *) name {
1114 return origin_ == nil ? [self host] : origin_;
1117 - (NSString *) description {
1118 return description_;
1121 - (NSString *) label {
1122 return label_ == nil ? [self host] : label_;
1125 - (NSString *) origin {
1129 - (NSString *) version {
1133 - (NSString *) defaultIcon {
1134 return defaultIcon_;
1139 /* Relationship Class {{{ */
1140 @interface Relationship : NSObject {
1145 - (NSString *) type;
1147 - (NSString *) name;
1151 @implementation Relationship
1159 - (NSString *) type {
1167 - (NSString *) name {
1174 /* Package Class {{{ */
1175 @interface Package : NSObject {
1176 pkgCache::PkgIterator iterator_;
1177 _transient Database *database_;
1178 pkgCache::VerIterator version_;
1179 pkgCache::VerFileIterator file_;
1187 NSString *installed_;
1193 NSString *depiction_;
1194 NSString *homepage_;
1200 NSArray *relationships_;
1203 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1204 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1206 - (pkgCache::PkgIterator) iterator;
1208 - (NSString *) section;
1209 - (NSString *) simpleSection;
1213 - (Address *) maintainer;
1215 - (NSString *) description;
1216 - (NSString *) index;
1218 - (NSMutableDictionary *) metadata;
1220 - (BOOL) subscribed;
1223 - (NSString *) latest;
1224 - (NSString *) installed;
1227 - (BOOL) upgradableAndEssential:(BOOL)essential;
1230 - (BOOL) unfiltered;
1234 - (BOOL) halfConfigured;
1235 - (BOOL) halfInstalled;
1237 - (NSString *) mode;
1240 - (NSString *) name;
1241 - (NSString *) tagline;
1243 - (NSString *) homepage;
1244 - (NSString *) depiction;
1245 - (Address *) author;
1247 - (NSArray *) files;
1248 - (NSArray *) relationships;
1249 - (NSArray *) warnings;
1250 - (NSArray *) applications;
1252 - (Source *) source;
1253 - (NSString *) role;
1254 - (NSString *) rating;
1256 - (BOOL) matches:(NSString *)text;
1258 - (bool) hasSupportingRole;
1259 - (BOOL) hasTag:(NSString *)tag;
1260 - (NSString *) primaryPurpose;
1261 - (NSArray *) purposes;
1263 - (NSComparisonResult) compareByName:(Package *)package;
1264 - (NSComparisonResult) compareBySection:(Package *)package;
1266 - (uint32_t) compareForChanges;
1271 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1272 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1273 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1274 - (NSNumber *) isVisibleInSource:(Source *)source;
1278 @implementation Package
1284 if (section_ != nil)
1288 if (installed_ != nil)
1289 [installed_ release];
1297 if (depiction_ != nil)
1298 [depiction_ release];
1299 if (homepage_ != nil)
1300 [homepage_ release];
1301 if (sponsor_ != nil)
1310 if (relationships_ != nil)
1311 [relationships_ release];
1316 + (NSArray *) _attributeKeys {
1317 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1320 - (NSArray *) attributeKeys {
1321 return [[self class] _attributeKeys];
1324 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1325 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1328 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1329 if ((self = [super init]) != nil) {
1330 iterator_ = iterator;
1331 database_ = database;
1333 version_ = [database_ policy]->GetCandidateVer(iterator_);
1334 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1335 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1337 pkgCache::VerIterator current = iterator_.CurrentVer();
1338 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1339 installed_ = [StripVersion(installed) retain];
1341 if (!version_.end())
1342 file_ = version_.FileList();
1344 pkgCache &cache([database_ cache]);
1345 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1348 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1351 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1353 const char *begin, *end;
1354 parser->GetRec(begin, end);
1356 NSString *website(nil);
1357 NSString *sponsor(nil);
1358 NSString *author(nil);
1367 {"depiction", &depiction_},
1368 {"homepage", &homepage_},
1369 {"website", &website},
1370 {"sponsor", &sponsor},
1371 {"author", &author},
1375 while (begin != end)
1376 if (*begin == '\n') {
1379 } else if (isblank(*begin)) next: {
1380 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1383 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1384 const char *name(begin);
1385 size_t size(colon - begin);
1387 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1390 const char *stop(begin == NULL ? end : begin);
1391 while (stop[-1] == '\r')
1393 while (++colon != stop && isblank(*colon));
1395 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1396 if (strncasecmp(names[i].name_, name, size) == 0) {
1397 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1398 *names[i].value_ = value;
1409 name_ = [name_ retain];
1410 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1412 icon_ = [icon_ retain];
1413 if (depiction_ != nil)
1414 depiction_ = [depiction_ retain];
1415 if (homepage_ == nil)
1416 homepage_ = website;
1417 if ([homepage_ isEqualToString:depiction_])
1419 if (homepage_ != nil)
1420 homepage_ = [homepage_ retain];
1422 sponsor_ = [[Address addressWithString:sponsor] retain];
1424 author_ = [[Address addressWithString:author] retain];
1426 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1430 for (int i(0), e([tags_ count]); i != e; ++i) {
1431 NSString *tag = [tags_ objectAtIndex:i];
1432 if ([tag hasPrefix:@"role::"]) {
1433 role_ = [[tag substringFromIndex:6] retain];
1438 NSString *solid(latest == nil ? installed : latest);
1439 bool changed(false);
1441 NSString *key([id_ lowercaseString]);
1443 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1444 if (metadata == nil) {
1445 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1450 [metadata setObject:solid forKey:@"LastVersion"];
1453 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1454 NSDate *last([metadata objectForKey:@"LastSeen"]);
1455 NSString *version([metadata objectForKey:@"LastVersion"]);
1458 first = last == nil ? now_ : last;
1459 [metadata setObject:first forKey:@"FirstSeen"];
1464 if (version == nil) {
1465 [metadata setObject:solid forKey:@"LastVersion"];
1467 } else if (![version isEqualToString:solid]) {
1468 [metadata setObject:solid forKey:@"LastVersion"];
1470 [metadata setObject:last forKey:@"LastSeen"];
1476 [Packages_ setObject:metadata forKey:key];
1482 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1483 return [[[Package alloc]
1484 initWithIterator:iterator
1489 - (pkgCache::PkgIterator) iterator {
1493 - (NSString *) section {
1494 if (section_ != nil)
1497 const char *section = iterator_.Section();
1498 if (section == NULL)
1501 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1504 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1505 if (NSString *rename = [value objectForKey:@"Rename"]) {
1510 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1514 - (NSString *) simpleSection {
1515 if (NSString *section = [self section])
1516 return Simplify(section);
1521 - (NSString *) uri {
1524 pkgIndexFile *index;
1525 pkgCache::PkgFileIterator file(file_.File());
1526 if (![database_ list].FindIndex(file, index))
1528 return [NSString stringWithUTF8String:iterator_->Path];
1529 //return [NSString stringWithUTF8String:file.Site()];
1530 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1534 - (Address *) maintainer {
1537 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1538 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1542 return version_.end() ? 0 : version_->InstalledSize;
1545 - (NSString *) description {
1548 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1549 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1551 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1552 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1553 if ([lines count] < 2)
1556 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1557 for (size_t i(1); i != [lines count]; ++i) {
1558 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1559 [trimmed addObject:trim];
1562 return [trimmed componentsJoinedByString:@"\n"];
1565 - (NSString *) index {
1566 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1567 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1570 - (NSMutableDictionary *) metadata {
1571 return [Packages_ objectForKey:[id_ lowercaseString]];
1575 NSDictionary *metadata([self metadata]);
1576 if ([self subscribed])
1577 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1579 return [metadata objectForKey:@"FirstSeen"];
1582 - (BOOL) subscribed {
1583 NSDictionary *metadata([self metadata]);
1584 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1585 return [subscribed boolValue];
1591 NSDictionary *metadata([self metadata]);
1592 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1593 return [ignored boolValue];
1598 - (NSString *) latest {
1602 - (NSString *) installed {
1607 return !version_.end();
1610 - (BOOL) upgradableAndEssential:(BOOL)essential {
1611 pkgCache::VerIterator current = iterator_.CurrentVer();
1615 value = essential && [self essential];
1617 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1621 - (BOOL) essential {
1622 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1626 return [database_ cache][iterator_].InstBroken();
1629 - (BOOL) unfiltered {
1630 NSString *section = [self section];
1631 return section == nil || isSectionVisible(section);
1635 return [self hasSupportingRole] && [self unfiltered];
1639 unsigned char current = iterator_->CurrentState;
1640 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1643 - (BOOL) halfConfigured {
1644 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1647 - (BOOL) halfInstalled {
1648 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1652 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1653 return state.Mode != pkgDepCache::ModeKeep;
1656 - (NSString *) mode {
1657 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1659 switch (state.Mode) {
1660 case pkgDepCache::ModeDelete:
1661 if ((state.iFlags & pkgDepCache::Purge) != 0)
1665 case pkgDepCache::ModeKeep:
1666 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1670 case pkgDepCache::ModeInstall:
1671 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1672 return @"Reinstall";
1673 else switch (state.Status) {
1675 return @"Downgrade";
1681 return @"New Install";
1694 - (NSString *) name {
1695 return name_ == nil ? id_ : name_;
1698 - (NSString *) tagline {
1702 - (UIImage *) icon {
1703 NSString *section = [self simpleSection];
1707 if ([icon_ hasPrefix:@"file:///"])
1708 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1709 if (icon == nil) if (section != nil)
1710 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1711 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1712 if ([dicon hasPrefix:@"file:///"])
1713 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1715 icon = [UIImage applicationImageNamed:@"unknown.png"];
1719 - (NSString *) homepage {
1723 - (NSString *) depiction {
1727 - (Address *) sponsor {
1731 - (Address *) author {
1735 - (NSArray *) files {
1736 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1737 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1740 fin.open([path UTF8String]);
1745 while (std::getline(fin, line))
1746 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1751 - (NSArray *) relationships {
1752 return relationships_;
1755 - (NSArray *) warnings {
1756 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1757 const char *name(iterator_.Name());
1759 size_t length(strlen(name));
1760 if (length < 2) invalid:
1761 [warnings addObject:@"illegal package identifier"];
1762 else for (size_t i(0); i != length; ++i)
1764 /* XXX: technically this is not allowed */
1765 (name[i] < 'A' || name[i] > 'Z') &&
1766 (name[i] < 'a' || name[i] > 'z') &&
1767 (name[i] < '0' || name[i] > '9') &&
1768 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1771 if (strcmp(name, "cydia") != 0) {
1773 bool _private = false;
1776 bool repository = [[self section] isEqualToString:@"Repositories"];
1778 if (NSArray *files = [self files])
1779 for (NSString *file in files)
1780 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1782 else if (!_private && [file isEqualToString:@"/private"])
1784 else if (!stash && [file isEqualToString:@"/var/stash"])
1787 /* XXX: this is not sensitive enough. only some folders are valid. */
1788 if (cydia && !repository)
1789 [warnings addObject:@"files installed into Cydia.app"];
1791 [warnings addObject:@"files installed with /private/*"];
1793 [warnings addObject:@"files installed to /var/stash"];
1796 return [warnings count] == 0 ? nil : warnings;
1799 - (NSArray *) applications {
1800 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1802 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1804 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1805 if (NSArray *files = [self files])
1806 for (NSString *file in files)
1807 if (application_r(file)) {
1808 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1809 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1810 if ([id isEqualToString:me])
1813 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1815 display = application_r[1];
1817 NSString *bundle([file stringByDeletingLastPathComponent]);
1818 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1819 if (icon == nil || [icon length] == 0)
1821 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1823 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1824 [applications addObject:application];
1826 [application addObject:id];
1827 [application addObject:display];
1828 [application addObject:url];
1831 return [applications count] == 0 ? nil : applications;
1834 - (Source *) source {
1836 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1843 - (NSString *) role {
1847 - (NSString *) rating {
1848 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1849 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1854 - (BOOL) matches:(NSString *)text {
1860 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1861 if (range.location != NSNotFound)
1864 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1865 if (range.location != NSNotFound)
1868 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1869 if (range.location != NSNotFound)
1875 - (bool) hasSupportingRole {
1878 if ([role_ isEqualToString:@"enduser"])
1880 if ([Role_ isEqualToString:@"User"])
1882 if ([role_ isEqualToString:@"hacker"])
1884 if ([Role_ isEqualToString:@"Hacker"])
1886 if ([role_ isEqualToString:@"developer"])
1888 if ([Role_ isEqualToString:@"Developer"])
1893 - (BOOL) hasTag:(NSString *)tag {
1894 return tags_ == nil ? NO : [tags_ containsObject:tag];
1897 - (NSString *) primaryPurpose {
1898 for (NSString *tag in tags_)
1899 if ([tag hasPrefix:@"purpose::"])
1900 return [tag substringFromIndex:9];
1904 - (NSArray *) purposes {
1905 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1906 for (NSString *tag in tags_)
1907 if ([tag hasPrefix:@"purpose::"])
1908 [purposes addObject:[tag substringFromIndex:9]];
1909 return [purposes count] == 0 ? nil : purposes;
1912 - (NSComparisonResult) compareByName:(Package *)package {
1913 NSString *lhs = [self name];
1914 NSString *rhs = [package name];
1916 if ([lhs length] != 0 && [rhs length] != 0) {
1917 unichar lhc = [lhs characterAtIndex:0];
1918 unichar rhc = [rhs characterAtIndex:0];
1920 if (isalpha(lhc) && !isalpha(rhc))
1921 return NSOrderedAscending;
1922 else if (!isalpha(lhc) && isalpha(rhc))
1923 return NSOrderedDescending;
1926 return [lhs compare:rhs options:LaxCompareOptions_];
1929 - (NSComparisonResult) compareBySection:(Package *)package {
1930 NSString *lhs = [self section];
1931 NSString *rhs = [package section];
1933 if (lhs == NULL && rhs != NULL)
1934 return NSOrderedAscending;
1935 else if (lhs != NULL && rhs == NULL)
1936 return NSOrderedDescending;
1937 else if (lhs != NULL && rhs != NULL) {
1938 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1939 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1942 return NSOrderedSame;
1945 - (uint32_t) compareForChanges {
1950 uint32_t timestamp : 30;
1951 uint32_t ignored : 1;
1952 uint32_t upgradable : 1;
1956 bool upgradable([self upgradableAndEssential:YES]);
1957 value.bits.upgradable = upgradable ? 1 : 0;
1960 value.bits.timestamp = 0;
1961 value.bits.ignored = [self ignored] ? 0 : 1;
1962 value.bits.upgradable = 1;
1964 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1965 value.bits.ignored = 0;
1966 value.bits.upgradable = 0;
1969 return _not(uint32_t) - value.key;
1973 pkgProblemResolver *resolver = [database_ resolver];
1974 resolver->Clear(iterator_);
1975 resolver->Protect(iterator_);
1976 pkgCacheFile &cache([database_ cache]);
1977 cache->MarkInstall(iterator_, false);
1978 pkgDepCache::StateCache &state((*cache)[iterator_]);
1979 if (!state.Install())
1980 cache->SetReInstall(iterator_, true);
1984 pkgProblemResolver *resolver = [database_ resolver];
1985 resolver->Clear(iterator_);
1986 resolver->Protect(iterator_);
1987 resolver->Remove(iterator_);
1988 [database_ cache]->MarkDelete(iterator_, true);
1991 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1992 return [NSNumber numberWithBool:(
1993 [self unfiltered] && [self matches:search]
1997 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1998 return [NSNumber numberWithBool:(
1999 (![number boolValue] || [self visible]) && [self installed] != nil
2003 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
2004 NSString *section = [self section];
2006 return [NSNumber numberWithBool:(
2008 [self installed] == nil && (
2010 section == nil && [name length] == 0 ||
2011 [name isEqualToString:section]
2016 - (NSNumber *) isVisibleInSource:(Source *)source {
2017 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2022 /* Section Class {{{ */
2023 @interface Section : NSObject {
2029 - (NSComparisonResult) compareByName:(Section *)section;
2030 - (Section *) initWithName:(NSString *)name;
2031 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2032 - (NSString *) name;
2035 - (void) addToCount;
2039 @implementation Section
2046 - (NSComparisonResult) compareByName:(Section *)section {
2047 NSString *lhs = [self name];
2048 NSString *rhs = [section name];
2050 if ([lhs length] != 0 && [rhs length] != 0) {
2051 unichar lhc = [lhs characterAtIndex:0];
2052 unichar rhc = [rhs characterAtIndex:0];
2054 if (isalpha(lhc) && !isalpha(rhc))
2055 return NSOrderedAscending;
2056 else if (!isalpha(lhc) && isalpha(rhc))
2057 return NSOrderedDescending;
2060 return [lhs compare:rhs options:LaxCompareOptions_];
2063 - (Section *) initWithName:(NSString *)name {
2064 return [self initWithName:name row:0];
2067 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2068 if ((self = [super init]) != nil) {
2069 name_ = [name retain];
2074 - (NSString *) name {
2086 - (void) addToCount {
2094 static NSArray *Finishes_;
2096 /* Database Implementation {{{ */
2097 @implementation Database
2099 + (Database *) sharedInstance {
2100 static Database *instance;
2101 if (instance == nil)
2102 instance = [[Database alloc] init];
2111 - (void) _readCydia:(NSNumber *)fd { _pooled
2112 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2113 std::istream is(&ib);
2116 static Pcre finish_r("^finish:([^:]*)$");
2118 while (std::getline(is, line)) {
2119 const char *data(line.c_str());
2120 size_t size = line.size();
2121 lprintf("C:%s\n", data);
2123 if (finish_r(data, size)) {
2124 NSString *finish = finish_r[1];
2125 int index = [Finishes_ indexOfObject:finish];
2126 if (index != INT_MAX && index > Finish_)
2134 - (void) _readStatus:(NSNumber *)fd { _pooled
2135 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2136 std::istream is(&ib);
2139 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2140 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2142 while (std::getline(is, line)) {
2143 const char *data(line.c_str());
2144 size_t size = line.size();
2145 lprintf("S:%s\n", data);
2147 if (conffile_r(data, size)) {
2148 [delegate_ setConfigurationData:conffile_r[1]];
2149 } else if (strncmp(data, "status: ", 8) == 0) {
2150 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2151 [delegate_ setProgressTitle:string];
2152 } else if (pmstatus_r(data, size)) {
2153 std::string type([pmstatus_r[1] UTF8String]);
2154 NSString *id = pmstatus_r[2];
2156 float percent([pmstatus_r[3] floatValue]);
2157 [delegate_ setProgressPercent:(percent / 100)];
2159 NSString *string = pmstatus_r[4];
2161 if (type == "pmerror")
2162 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2163 withObject:[NSArray arrayWithObjects:string, id, nil]
2166 else if (type == "pmstatus") {
2167 [delegate_ setProgressTitle:string];
2168 } else if (type == "pmconffile")
2169 [delegate_ setConfigurationData:string];
2170 else _assert(false);
2171 } else _assert(false);
2177 - (void) _readOutput:(NSNumber *)fd { _pooled
2178 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2179 std::istream is(&ib);
2182 while (std::getline(is, line)) {
2183 lprintf("O:%s\n", line.c_str());
2184 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2194 - (Package *) packageWithName:(NSString *)name {
2195 if (static_cast<pkgDepCache *>(cache_) == NULL)
2197 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2198 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2201 - (Database *) init {
2202 if ((self = [super init]) != nil) {
2209 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2210 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2214 _assert(pipe(fds) != -1);
2217 _config->Set("APT::Keep-Fds::", cydiafd_);
2218 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2221 detachNewThreadSelector:@selector(_readCydia:)
2223 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2226 _assert(pipe(fds) != -1);
2230 detachNewThreadSelector:@selector(_readStatus:)
2232 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2235 _assert(pipe(fds) != -1);
2236 _assert(dup2(fds[0], 0) != -1);
2237 _assert(close(fds[0]) != -1);
2239 input_ = fdopen(fds[1], "a");
2241 _assert(pipe(fds) != -1);
2242 _assert(dup2(fds[1], 1) != -1);
2243 _assert(close(fds[1]) != -1);
2246 detachNewThreadSelector:@selector(_readOutput:)
2248 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2253 - (pkgCacheFile &) cache {
2257 - (pkgDepCache::Policy *) policy {
2261 - (pkgRecords *) records {
2265 - (pkgProblemResolver *) resolver {
2269 - (pkgAcquire &) fetcher {
2273 - (pkgSourceList &) list {
2277 - (NSArray *) packages {
2281 - (NSArray *) sources {
2282 return [sources_ allValues];
2285 - (NSArray *) issues {
2286 if (cache_->BrokenCount() == 0)
2289 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2291 for (Package *package in packages_) {
2292 if (![package broken])
2294 pkgCache::PkgIterator pkg([package iterator]);
2296 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2297 [entry addObject:[package name]];
2298 [issues addObject:entry];
2300 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2304 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2305 pkgCache::DepIterator start;
2306 pkgCache::DepIterator end;
2307 dep.GlobOr(start, end); // ++dep
2309 if (!cache_->IsImportantDep(end))
2311 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2314 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2315 [entry addObject:failure];
2316 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2318 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2319 [failure addObject:[package name]];
2321 pkgCache::PkgIterator target(start.TargetPkg());
2322 if (target->ProvidesList != 0)
2323 [failure addObject:@"?"];
2325 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2327 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2328 else if (!cache_[target].CandidateVerIter(cache_).end())
2329 [failure addObject:@"-"];
2330 else if (target->ProvidesList == 0)
2331 [failure addObject:@"!"];
2333 [failure addObject:@"%"];
2337 if (start.TargetVer() != 0)
2338 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2349 - (void) reloadData {
2369 if (!cache_.Open(progress_, true)) {
2371 if (!_error->PopMessage(error))
2374 lprintf("cache_.Open():[%s]\n", error.c_str());
2376 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2377 [delegate_ repairWithSelector:@selector(configure)];
2378 else if (error == "The package lists or status file could not be parsed or opened.")
2379 [delegate_ repairWithSelector:@selector(update)];
2380 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2381 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2382 // else if (error == "The list of sources could not be read.")
2383 else _assert(false);
2389 now_ = [[NSDate date] retain];
2391 policy_ = new pkgDepCache::Policy();
2392 records_ = new pkgRecords(cache_);
2393 resolver_ = new pkgProblemResolver(cache_);
2394 fetcher_ = new pkgAcquire(&status_);
2397 list_ = new pkgSourceList();
2398 _assert(list_->ReadMainList());
2400 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2401 _assert(pkgApplyStatus(cache_));
2403 if (cache_->BrokenCount() != 0) {
2404 _assert(pkgFixBroken(cache_));
2405 _assert(cache_->BrokenCount() == 0);
2406 _assert(pkgMinimizeUpgrade(cache_));
2409 [sources_ removeAllObjects];
2410 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2411 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2412 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2414 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2415 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2419 [packages_ removeAllObjects];
2422 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2423 if (Package *package = [Package packageWithIterator:iterator database:self])
2424 [packages_ addObject:package];
2426 [packages_ sortUsingSelector:@selector(compareByName:)];
2430 - (void) configure {
2431 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2432 system([dpkg UTF8String]);
2440 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2441 _assert(!_error->PendingError());
2444 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2447 public pkgArchiveCleaner
2450 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2455 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2457 while (_error->PopMessage(error))
2458 lprintf("ArchiveCleaner: %s\n", error.c_str());
2463 pkgRecords records(cache_);
2465 lock_ = new FileFd();
2466 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2467 _assert(!_error->PendingError());
2470 // XXX: explain this with an error message
2471 _assert(list.ReadMainList());
2473 manager_ = (_system->CreatePM(cache_));
2474 _assert(manager_->GetArchives(fetcher_, &list, &records));
2475 _assert(!_error->PendingError());
2479 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2481 _assert(list.ReadMainList());
2482 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2483 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2486 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2491 bool failed = false;
2492 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2493 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2496 std::string uri = (*item)->DescURI();
2497 std::string error = (*item)->ErrorText;
2499 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2502 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2503 withObject:[NSArray arrayWithObjects:
2504 [NSString stringWithUTF8String:error.c_str()],
2516 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2518 if (_error->PendingError()) {
2523 if (result == pkgPackageManager::Failed) {
2528 if (result != pkgPackageManager::Completed) {
2533 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2535 _assert(list.ReadMainList());
2536 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2537 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2540 if (![before isEqualToArray:after])
2545 _assert(pkgDistUpgrade(cache_));
2549 [self updateWithStatus:status_];
2552 - (void) updateWithStatus:(Status &)status {
2554 _assert(list.ReadMainList());
2557 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2558 _assert(!_error->PendingError());
2560 pkgAcquire fetcher(&status);
2561 _assert(list.GetIndexes(&fetcher));
2563 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2564 bool failed = false;
2565 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2566 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2567 (*item)->Finished();
2571 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2572 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2573 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2576 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2581 - (void) setDelegate:(id)delegate {
2582 delegate_ = delegate;
2583 status_.setDelegate(delegate);
2584 progress_.setDelegate(delegate);
2587 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2588 pkgIndexFile *index(NULL);
2589 list_->FindIndex(file, index);
2590 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2596 /* PopUp Windows {{{ */
2597 @interface PopUpView : UIView {
2598 _transient id delegate_;
2599 UITransitionView *transition_;
2604 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2608 @implementation PopUpView
2611 [transition_ setDelegate:nil];
2612 [transition_ release];
2618 [transition_ transition:UITransitionPushFromTop toView:nil];
2621 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2622 if (from != nil && to == nil)
2623 [self removeFromSuperview];
2626 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2627 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2628 delegate_ = delegate;
2630 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2631 [self addSubview:transition_];
2633 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2635 [view addSubview:self];
2637 [transition_ setDelegate:self];
2639 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2640 [transition_ transition:UITransitionNone toView:blank];
2641 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2648 /* Mail Composition {{{ */
2649 @interface MailToView : PopUpView {
2650 MailComposeController *controller_;
2653 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2657 @implementation MailToView
2660 [controller_ release];
2664 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2668 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2669 NSLog(@"did:%@", delivery);
2670 // [UIApp setStatusBarShowsProgress:NO];
2671 if ([controller error]){
2672 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2673 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2674 [mailAlertSheet setBodyText:[controller error]];
2675 [mailAlertSheet popupAlertAnimated:YES];
2679 - (void) showError {
2680 NSLog(@"%@", [controller_ error]);
2681 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2682 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2683 [mailAlertSheet setBodyText:[controller_ error]];
2684 [mailAlertSheet popupAlertAnimated:YES];
2687 - (void) deliverMessage { _pooled
2691 if (![controller_ deliverMessage])
2692 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2695 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2696 if ([controller_ needsDelivery])
2697 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2702 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2703 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2704 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2705 [controller_ setDelegate:self];
2706 [controller_ initializeUI];
2707 [controller_ setupForURL:url];
2709 UIView *view([controller_ view]);
2710 [overlay_ addSubview:view];
2716 /* Confirmation View {{{ */
2717 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2718 if (!iterator.end())
2719 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2720 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2722 pkgCache::PkgIterator package(dep.TargetPkg());
2725 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2732 @protocol ConfirmationViewDelegate
2737 @interface ConfirmationView : BrowserView {
2738 _transient Database *database_;
2739 UIActionSheet *essential_;
2746 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2750 @implementation ConfirmationView
2757 if (essential_ != nil)
2758 [essential_ release];
2764 [book_ popFromSuperviewAnimated:YES];
2767 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2768 NSString *context([sheet context]);
2770 if ([context isEqualToString:@"remove"]) {
2778 [delegate_ confirm];
2785 } else if ([context isEqualToString:@"unable"]) {
2789 [super alertSheet:sheet buttonClicked:button];
2792 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2793 [window setValue:changes_ forKey:@"changes"];
2794 [window setValue:issues_ forKey:@"issues"];
2795 [window setValue:sizes_ forKey:@"sizes"];
2796 [super webView:sender didClearWindowObject:window forFrame:frame];
2799 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2800 if ((self = [super initWithBook:book]) != nil) {
2801 database_ = database;
2803 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2804 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2805 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2806 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2807 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2811 pkgDepCache::Policy *policy([database_ policy]);
2813 pkgCacheFile &cache([database_ cache]);
2814 NSArray *packages = [database_ packages];
2815 for (size_t i(0), e = [packages count]; i != e; ++i) {
2816 Package *package = [packages objectAtIndex:i];
2817 pkgCache::PkgIterator iterator = [package iterator];
2818 pkgDepCache::StateCache &state(cache[iterator]);
2820 NSString *name([package name]);
2822 if (state.NewInstall())
2823 [installing addObject:name];
2824 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2825 [reinstalling addObject:name];
2826 else if (state.Upgrade())
2827 [upgrading addObject:name];
2828 else if (state.Downgrade())
2829 [downgrading addObject:name];
2830 else if (state.Delete()) {
2831 if ([package essential])
2833 [removing addObject:name];
2836 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2837 substrate_ |= DepSubstrate(iterator.CurrentVer());
2842 else if (Advanced_ || true) {
2843 essential_ = [[UIActionSheet alloc]
2844 initWithTitle:@"Removing Essentials"
2845 buttons:[NSArray arrayWithObjects:
2846 @"Cancel Operation (Safe)",
2847 @"Force Removal (Unsafe)",
2849 defaultButtonIndex:0
2855 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2857 [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."];
2859 essential_ = [[UIActionSheet alloc]
2860 initWithTitle:@"Unable to Comply"
2861 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2862 defaultButtonIndex:0
2867 [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."];
2870 changes_ = [[NSArray alloc] initWithObjects:
2878 issues_ = [database_ issues];
2880 issues_ = [issues_ retain];
2882 sizes_ = [[NSArray alloc] initWithObjects:
2883 SizeString([database_ fetcher].FetchNeeded()),
2884 SizeString([database_ fetcher].PartialPresent()),
2885 SizeString([database_ cache]->UsrSize()),
2888 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2892 - (NSString *) backButtonTitle {
2896 - (NSString *) leftButtonTitle {
2900 - (id) _rightButtonTitle {
2901 #if AlwaysReload || IgnoreInstall
2904 return issues_ == nil ? @"Confirm" : nil;
2908 - (void) _leftButtonClicked {
2913 - (void) _rightButtonClicked {
2915 return [super _rightButtonClicked];
2917 if (essential_ != nil)
2918 [essential_ popupAlertAnimated:YES];
2922 [delegate_ confirm];
2930 /* Progress Data {{{ */
2931 @interface ProgressData : NSObject {
2937 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2944 @implementation ProgressData
2946 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2947 if ((self = [super init]) != nil) {
2948 selector_ = selector;
2968 /* Progress View {{{ */
2969 @interface ProgressView : UIView <
2970 ConfigurationDelegate,
2973 _transient Database *database_;
2975 UIView *background_;
2976 UITransitionView *transition_;
2978 UINavigationBar *navbar_;
2979 UIProgressBar *progress_;
2980 UITextView *output_;
2981 UITextLabel *status_;
2982 UIPushButton *close_;
2985 SHA1SumValue springlist_;
2986 SHA1SumValue notifyconf_;
2987 SHA1SumValue sandplate_;
2989 NSTimeInterval last_;
2992 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2994 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2995 - (void) setContentView:(UIView *)view;
2998 - (void) _retachThread;
2999 - (void) _detachNewThreadData:(ProgressData *)data;
3000 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3006 @protocol ProgressViewDelegate
3007 - (void) progressViewIsComplete:(ProgressView *)sender;
3010 @implementation ProgressView
3013 [transition_ setDelegate:nil];
3014 [navbar_ setDelegate:nil];
3017 if (background_ != nil)
3018 [background_ release];
3019 [transition_ release];
3022 [progress_ release];
3029 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3030 if (bootstrap_ && from == overlay_ && to == view_)
3034 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3035 if ((self = [super initWithFrame:frame]) != nil) {
3036 database_ = database;
3037 delegate_ = delegate;
3039 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3040 [transition_ setDelegate:self];
3042 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3045 [overlay_ setBackgroundColor:[UIColor blackColor]];
3047 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3048 [background_ setBackgroundColor:[UIColor blackColor]];
3049 [self addSubview:background_];
3052 [self addSubview:transition_];
3054 CGSize navsize = [UINavigationBar defaultSize];
3055 CGRect navrect = {{0, 0}, navsize};
3057 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3058 [overlay_ addSubview:navbar_];
3060 [navbar_ setBarStyle:1];
3061 [navbar_ setDelegate:self];
3063 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3064 [navbar_ pushNavigationItem:navitem];
3066 CGRect bounds = [overlay_ bounds];
3067 CGSize prgsize = [UIProgressBar defaultSize];
3070 (bounds.size.width - prgsize.width) / 2,
3071 bounds.size.height - prgsize.height - 20
3074 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3075 [progress_ setStyle:0];
3077 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3079 bounds.size.height - prgsize.height - 50,
3080 bounds.size.width - 20,
3084 [status_ setColor:[UIColor whiteColor]];
3085 [status_ setBackgroundColor:[UIColor clearColor]];
3087 [status_ setCentersHorizontally:YES];
3088 //[status_ setFont:font];
3090 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3092 navrect.size.height + 20,
3093 bounds.size.width - 20,
3094 bounds.size.height - navsize.height - 62 - navrect.size.height
3097 //[output_ setTextFont:@"Courier New"];
3098 [output_ setTextSize:12];
3100 [output_ setTextColor:[UIColor whiteColor]];
3101 [output_ setBackgroundColor:[UIColor clearColor]];
3103 [output_ setMarginTop:0];
3104 [output_ setAllowsRubberBanding:YES];
3105 [output_ setEditable:NO];
3107 [overlay_ addSubview:output_];
3109 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3111 bounds.size.height - prgsize.height - 50,
3112 bounds.size.width - 20,
3116 [close_ setAutosizesToFit:NO];
3117 [close_ setDrawsShadow:YES];
3118 [close_ setStretchBackground:YES];
3119 [close_ setEnabled:YES];
3121 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3122 [close_ setTitleFont:bold];
3124 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3125 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3126 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3130 - (void) setContentView:(UIView *)view {
3131 view_ = [view retain];
3134 - (void) resetView {
3135 [transition_ transition:6 toView:view_];
3138 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3139 NSString *context([sheet context]);
3141 if ([context isEqualToString:@"error"])
3143 else if ([context isEqualToString:@"conffile"]) {
3144 FILE *input = [database_ input];
3148 fprintf(input, "N\n");
3152 fprintf(input, "Y\n");
3163 - (void) closeButtonPushed {
3172 [delegate_ suspendWithAnimation:YES];
3176 system("launchctl stop com.apple.SpringBoard");
3180 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3189 - (void) _retachThread {
3190 UINavigationItem *item = [navbar_ topItem];
3191 [item setTitle:@"Complete"];
3193 [overlay_ addSubview:close_];
3194 [progress_ removeFromSuperview];
3195 [status_ removeFromSuperview];
3197 [delegate_ progressViewIsComplete:self];
3200 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3201 MMap mmap(file, MMap::ReadOnly);
3203 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3204 if (!(sandplate_ == sha1.Result()))
3209 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3210 MMap mmap(file, MMap::ReadOnly);
3212 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3213 if (!(notifyconf_ == sha1.Result()))
3218 FileFd file(SpringBoard_, FileFd::ReadOnly);
3219 MMap mmap(file, MMap::ReadOnly);
3221 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3222 if (!(springlist_ == sha1.Result()))
3227 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3228 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3229 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3230 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3231 case 4: [close_ setTitle:@"Reboot Device"]; break;
3234 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3236 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3237 [cache autorelease];
3239 NSFileManager *manager = [NSFileManager defaultManager];
3240 NSError *error = nil;
3242 id system = [cache objectForKey:@"System"];
3247 if (stat(Cache_, &info) == -1)
3250 [system removeAllObjects];
3252 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3253 for (NSString *app in apps)
3254 if ([app hasSuffix:@".app"]) {
3255 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3256 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3257 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3259 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3260 [info setObject:path forKey:@"Path"];
3261 [info setObject:@"System" forKey:@"ApplicationType"];
3262 [system addInfoDictionary:info];
3268 [cache writeToFile:@Cache_ atomically:YES];
3270 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3272 if (chmod(Cache_, info.st_mode) == -1)
3276 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3279 notify_post("com.apple.mobile.application_installed");
3281 [delegate_ setStatusBarShowsProgress:NO];
3284 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3285 [[data target] performSelector:[data selector] withObject:[data object]];
3288 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3291 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3292 UINavigationItem *item = [navbar_ topItem];
3293 [item setTitle:title];
3295 [status_ setText:nil];
3296 [output_ setText:@""];
3297 [progress_ setProgress:0];
3300 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3302 [close_ removeFromSuperview];
3303 [overlay_ addSubview:progress_];
3304 [overlay_ addSubview:status_];
3306 [delegate_ setStatusBarShowsProgress:YES];
3310 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3311 MMap mmap(file, MMap::ReadOnly);
3313 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3314 sandplate_ = sha1.Result();
3318 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3319 MMap mmap(file, MMap::ReadOnly);
3321 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3322 notifyconf_ = sha1.Result();
3326 FileFd file(SpringBoard_, FileFd::ReadOnly);
3327 MMap mmap(file, MMap::ReadOnly);
3329 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3330 springlist_ = sha1.Result();
3333 [transition_ transition:6 toView:overlay_];
3336 detachNewThreadSelector:@selector(_detachNewThreadData:)
3338 withObject:[[ProgressData alloc]
3339 initWithSelector:selector
3346 - (void) repairWithSelector:(SEL)selector {
3348 detachNewThreadSelector:selector
3355 - (void) setConfigurationData:(NSString *)data {
3357 performSelectorOnMainThread:@selector(_setConfigurationData:)
3363 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3364 Package *package = id == nil ? nil : [database_ packageWithName:id];
3366 UIActionSheet *sheet = [[[UIActionSheet alloc]
3367 initWithTitle:(package == nil ? id : [package name])
3368 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3369 defaultButtonIndex:0
3374 [sheet setBodyText:error];
3375 [sheet popupAlertAnimated:YES];
3378 - (void) setProgressTitle:(NSString *)title {
3380 performSelectorOnMainThread:@selector(_setProgressTitle:)
3386 - (void) setProgressPercent:(float)percent {
3388 performSelectorOnMainThread:@selector(_setProgressPercent:)
3389 withObject:[NSNumber numberWithFloat:percent]
3394 - (void) startProgress {
3395 last_ = [NSDate timeIntervalSinceReferenceDate];
3398 - (void) addProgressOutput:(NSString *)output {
3400 performSelectorOnMainThread:@selector(_addProgressOutput:)
3406 - (bool) isCancelling:(size_t)received {
3408 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3409 if (received_ != received) {
3410 received_ = received;
3412 } else if (now - last_ > 30)
3419 - (void) _setConfigurationData:(NSString *)data {
3420 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3422 _assert(conffile_r(data));
3424 NSString *ofile = conffile_r[1];
3425 //NSString *nfile = conffile_r[2];
3427 UIActionSheet *sheet = [[[UIActionSheet alloc]
3428 initWithTitle:@"Configuration Upgrade"
3429 buttons:[NSArray arrayWithObjects:
3430 @"Keep My Old Copy",
3431 @"Accept The New Copy",
3432 // XXX: @"See What Changed",
3434 defaultButtonIndex:0
3439 [sheet setBodyText:[NSString stringWithFormat:
3440 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3443 [sheet popupAlertAnimated:YES];
3446 - (void) _setProgressTitle:(NSString *)title {
3447 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3448 for (size_t i(0), e([words count]); i != e; ++i) {
3449 NSString *word([words objectAtIndex:i]);
3450 if (Package *package = [database_ packageWithName:word])
3451 [words replaceObjectAtIndex:i withObject:[package name]];
3454 [status_ setText:[words componentsJoinedByString:@" "]];
3457 - (void) _setProgressPercent:(NSNumber *)percent {
3458 [progress_ setProgress:[percent floatValue]];
3461 - (void) _addProgressOutput:(NSString *)output {
3462 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3463 CGSize size = [output_ contentSize];
3464 CGRect rect = {{0, size.height}, {size.width, 0}};
3465 [output_ scrollRectToVisible:rect animated:YES];
3468 - (BOOL) isRunning {
3475 /* Package Cell {{{ */
3476 @interface PackageCell : UISimpleTableCell {
3479 NSString *description_;
3483 UITextLabel *status_;
3487 - (PackageCell *) init;
3488 - (void) setPackage:(Package *)package;
3490 + (int) heightForPackage:(Package *)package;
3494 @implementation PackageCell
3496 - (void) clearPackage {
3507 if (description_ != nil) {
3508 [description_ release];
3512 if (source_ != nil) {
3517 if (badge_ != nil) {
3524 [self clearPackage];
3531 - (PackageCell *) init {
3532 if ((self = [super init]) != nil) {
3534 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3535 [status_ setBackgroundColor:[UIColor clearColor]];
3536 [status_ setFont:small];
3541 - (void) setPackage:(Package *)package {
3542 [self clearPackage];
3544 Source *source = [package source];
3545 NSString *section = [package simpleSection];
3547 icon_ = [[package icon] retain];
3549 name_ = [[package name] retain];
3550 description_ = [[package tagline] retain];
3552 NSString *label = nil;
3553 bool trusted = false;
3555 if (source != nil) {
3556 label = [source label];
3557 trusted = [source trusted];
3558 } else if ([[package id] isEqualToString:@"firmware"])
3561 label = @"Unknown/Local";
3563 NSString *from = [NSString stringWithFormat:@"from %@", label];
3565 if (section != nil && ![section isEqualToString:label])
3566 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3568 source_ = [from retain];
3570 if (NSString *purpose = [package primaryPurpose])
3571 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3572 badge_ = [badge_ retain];
3575 if (NSString *mode = [package mode]) {
3576 [badge_ setImage:[UIImage applicationImageNamed:
3577 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3580 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3581 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3582 } else if ([package half]) {
3583 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3584 [status_ setText:@"Package Damaged"];
3585 [status_ setColor:[UIColor redColor]];
3587 [badge_ setImage:nil];
3588 [status_ setText:nil];
3593 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3596 rect.size = [icon_ size];
3598 rect.size.width /= 2;
3599 rect.size.height /= 2;
3601 rect.origin.x = 25 - rect.size.width / 2;
3602 rect.origin.y = 25 - rect.size.height / 2;
3604 [icon_ drawInRect:rect];
3607 if (badge_ != nil) {
3608 CGSize size = [badge_ size];
3610 [badge_ drawAtPoint:CGPointMake(
3611 36 - size.width / 2,
3612 36 - size.height / 2
3621 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3622 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3626 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3628 [super drawContentInRect:rect selected:selected];
3631 + (int) heightForPackage:(Package *)package {
3632 NSString *tagline([package tagline]);
3633 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3635 if ([package hasMode] || [package half])
3644 /* Section Cell {{{ */
3645 @interface SectionCell : UISimpleTableCell {
3650 _UISwitchSlider *switch_;
3655 - (void) setSection:(Section *)section editing:(BOOL)editing;
3659 @implementation SectionCell
3661 - (void) clearSection {
3662 if (section_ != nil) {
3672 if (count_ != nil) {
3679 [self clearSection];
3686 if ((self = [super init]) != nil) {
3687 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3689 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3690 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3694 - (void) onSwitch:(id)sender {
3695 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3696 if (metadata == nil) {
3697 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3698 [Sections_ setObject:metadata forKey:section_];
3702 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3705 - (void) setSection:(Section *)section editing:(BOOL)editing {
3706 if (editing != editing_) {
3708 [switch_ removeFromSuperview];
3710 [self addSubview:switch_];
3714 [self clearSection];
3716 if (section == nil) {
3717 name_ = [@"All Packages" retain];
3720 section_ = [section name];
3721 if (section_ != nil)
3722 section_ = [section_ retain];
3723 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3724 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3727 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3731 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3732 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3739 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3741 CGSize size = [count_ sizeWithFont:Font14_];
3745 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3747 [super drawContentInRect:rect selected:selected];
3753 /* File Table {{{ */
3754 @interface FileTable : RVPage {
3755 _transient Database *database_;
3758 NSMutableArray *files_;
3762 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3763 - (void) setPackage:(Package *)package;
3767 @implementation FileTable
3770 if (package_ != nil)
3779 - (int) numberOfRowsInTable:(UITable *)table {
3780 return files_ == nil ? 0 : [files_ count];
3783 - (float) table:(UITable *)table heightForRow:(int)row {
3787 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3788 if (reusing == nil) {
3789 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3790 UIFont *font = [UIFont systemFontOfSize:16];
3791 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3793 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3797 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3801 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3802 if ((self = [super initWithBook:book]) != nil) {
3803 database_ = database;
3805 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3807 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3808 [self addSubview:list_];
3810 UITableColumn *column = [[[UITableColumn alloc]
3811 initWithTitle:@"Name"
3813 width:[self frame].size.width
3816 [list_ setDataSource:self];
3817 [list_ setSeparatorStyle:1];
3818 [list_ addTableColumn:column];
3819 [list_ setDelegate:self];
3820 [list_ setReusesTableCells:YES];
3824 - (void) setPackage:(Package *)package {
3825 if (package_ != nil) {
3826 [package_ autorelease];
3835 [files_ removeAllObjects];
3837 if (package != nil) {
3838 package_ = [package retain];
3839 name_ = [[package id] retain];
3841 if (NSArray *files = [package files])
3842 [files_ addObjectsFromArray:files];
3844 if ([files_ count] != 0) {
3845 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3846 [files_ removeObjectAtIndex:0];
3847 [files_ sortUsingSelector:@selector(compareByPath:)];
3849 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3850 [stack addObject:@"/"];
3852 for (int i(0), e([files_ count]); i != e; ++i) {
3853 NSString *file = [files_ objectAtIndex:i];
3854 while (![file hasPrefix:[stack lastObject]])
3855 [stack removeLastObject];
3856 NSString *directory = [stack lastObject];
3857 [stack addObject:[file stringByAppendingString:@"/"]];
3858 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3859 ([stack count] - 2) * 3, "",
3860 [file substringFromIndex:[directory length]]
3869 - (void) resetViewAnimated:(BOOL)animated {
3870 [list_ resetViewAnimated:animated];
3873 - (void) reloadData {
3874 [self setPackage:[database_ packageWithName:name_]];
3875 [self reloadButtons];
3878 - (NSString *) title {
3879 return @"Installed Files";
3882 - (NSString *) backButtonTitle {
3888 /* Package View {{{ */
3889 @interface PackageView : BrowserView {
3890 _transient Database *database_;
3893 NSMutableArray *buttons_;
3896 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3897 - (void) setPackage:(Package *)package;
3901 @implementation PackageView
3904 if (package_ != nil)
3912 - (void) _clickButtonWithName:(NSString *)name {
3913 if ([name isEqualToString:@"Install"])
3914 [delegate_ installPackage:package_];
3915 else if ([name isEqualToString:@"Reinstall"])
3916 [delegate_ installPackage:package_];
3917 else if ([name isEqualToString:@"Remove"])
3918 [delegate_ removePackage:package_];
3919 else if ([name isEqualToString:@"Upgrade"])
3920 [delegate_ installPackage:package_];
3921 else _assert(false);
3924 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3925 NSString *context([sheet context]);
3927 if ([context isEqualToString:@"modify"]) {
3928 int count = [buttons_ count];
3929 _assert(count != 0);
3930 _assert(button <= count + 1);
3932 if (count != button - 1)
3933 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3937 [super alertSheet:sheet buttonClicked:button];
3940 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3941 return [super webView:sender didFinishLoadForFrame:frame];
3944 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3945 [window setValue:package_ forKey:@"package"];
3946 [super webView:sender didClearWindowObject:window forFrame:frame];
3950 - (void) _rightButtonClicked {
3951 /*[super _rightButtonClicked];
3954 int count = [buttons_ count];
3955 _assert(count != 0);
3958 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3960 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3961 [buttons addObjectsFromArray:buttons_];
3962 [buttons addObject:@"Cancel"];
3964 [delegate_ slideUp:[[[UIActionSheet alloc]
3967 defaultButtonIndex:2
3975 - (id) _rightButtonTitle {
3976 int count = [buttons_ count];
3977 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3980 - (NSString *) backButtonTitle {
3984 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3985 if ((self = [super initWithBook:book]) != nil) {
3986 database_ = database;
3987 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3991 - (void) setPackage:(Package *)package {
3992 if (package_ != nil) {
3993 [package_ autorelease];
4002 [buttons_ removeAllObjects];
4004 if (package != nil) {
4005 package_ = [package retain];
4006 name_ = [[package id] retain];
4008 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4010 if ([package_ source] == nil);
4011 else if ([package_ upgradableAndEssential:NO])
4012 [buttons_ addObject:@"Upgrade"];
4013 else if ([package_ installed] == nil)
4014 [buttons_ addObject:@"Install"];
4016 [buttons_ addObject:@"Reinstall"];
4017 if ([package_ installed] != nil)
4018 [buttons_ addObject:@"Remove"];
4026 - (void) reloadData {
4027 [self setPackage:[database_ packageWithName:name_]];
4028 [self reloadButtons];
4033 /* Package Table {{{ */
4034 @interface PackageTable : RVPage {
4035 _transient Database *database_;
4037 NSMutableArray *packages_;
4038 NSMutableArray *sections_;
4039 UISectionList *list_;
4042 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4044 - (void) setDelegate:(id)delegate;
4046 - (void) reloadData;
4047 - (void) resetCursor;
4049 - (UISectionList *) list;
4051 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4055 @implementation PackageTable
4058 [list_ setDataSource:nil];
4061 [packages_ release];
4062 [sections_ release];
4067 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4068 return [sections_ count];
4071 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4072 return [[sections_ objectAtIndex:section] name];
4075 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4076 return [[sections_ objectAtIndex:section] row];
4079 - (int) numberOfRowsInTable:(UITable *)table {
4080 return [packages_ count];
4083 - (float) table:(UITable *)table heightForRow:(int)row {
4084 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4087 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4089 reusing = [[[PackageCell alloc] init] autorelease];
4090 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4094 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4098 - (void) tableRowSelected:(NSNotification *)notification {
4099 int row = [[notification object] selectedRow];
4103 Package *package = [packages_ objectAtIndex:row];
4104 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4105 [view setDelegate:delegate_];
4106 [view setPackage:package];
4107 [book_ pushPage:view];
4110 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4111 if ((self = [super initWithBook:book]) != nil) {
4112 database_ = database;
4113 title_ = [title retain];
4115 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4116 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4118 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4119 [list_ setDataSource:self];
4121 UITableColumn *column = [[[UITableColumn alloc]
4122 initWithTitle:@"Name"
4124 width:[self frame].size.width
4127 UITable *table = [list_ table];
4128 [table setSeparatorStyle:1];
4129 [table addTableColumn:column];
4130 [table setDelegate:self];
4131 [table setReusesTableCells:YES];
4133 [self addSubview:list_];
4135 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4136 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4140 - (void) setDelegate:(id)delegate {
4141 delegate_ = delegate;
4144 - (bool) hasPackage:(Package *)package {
4148 - (void) reloadData {
4149 NSArray *packages = [database_ packages];
4151 [packages_ removeAllObjects];
4152 [sections_ removeAllObjects];
4154 for (size_t i(0); i != [packages count]; ++i) {
4155 Package *package([packages objectAtIndex:i]);
4156 if ([self hasPackage:package])
4157 [packages_ addObject:package];
4160 Section *section = nil;
4162 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4163 Package *package = [packages_ objectAtIndex:offset];
4164 NSString *name = [package index];
4166 if (section == nil || ![[section name] isEqualToString:name]) {
4167 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4168 [sections_ addObject:section];
4171 [section addToCount];
4177 - (NSString *) title {
4181 - (void) resetViewAnimated:(BOOL)animated {
4182 [list_ resetViewAnimated:animated];
4185 - (void) resetCursor {
4186 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4189 - (UISectionList *) list {
4193 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4194 [list_ setShouldHideHeaderInShortLists:hide];
4199 /* Filtered Package Table {{{ */
4200 @interface FilteredPackageTable : PackageTable {
4205 - (void) setObject:(id)object;
4207 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4211 @implementation FilteredPackageTable
4219 - (void) setObject:(id)object {
4225 object_ = [object retain];
4228 - (bool) hasPackage:(Package *)package {
4229 return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
4232 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4233 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4235 object_ = object == nil ? nil : [object retain];
4244 /* Add Source View {{{ */
4245 @interface AddSourceView : RVPage {
4246 _transient Database *database_;
4249 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4253 @implementation AddSourceView
4255 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4256 if ((self = [super initWithBook:book]) != nil) {
4257 database_ = database;
4263 /* Source Cell {{{ */
4264 @interface SourceCell : UITableCell {
4267 NSString *description_;
4273 - (SourceCell *) initWithSource:(Source *)source;
4277 @implementation SourceCell
4282 [description_ release];
4287 - (SourceCell *) initWithSource:(Source *)source {
4288 if ((self = [super init]) != nil) {
4290 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4292 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4293 icon_ = [icon_ retain];
4295 origin_ = [[source name] retain];
4296 label_ = [[source uri] retain];
4297 description_ = [[source description] retain];
4301 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4303 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4310 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4314 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4318 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4320 [super drawContentInRect:rect selected:selected];
4325 /* Source Table {{{ */
4326 @interface SourceTable : RVPage {
4327 _transient Database *database_;
4328 UISectionList *list_;
4329 NSMutableArray *sources_;
4330 UIActionSheet *alert_;
4334 UIProgressHUD *hud_;
4337 //NSURLConnection *installer_;
4338 NSURLConnection *trivial_bz2_;
4339 NSURLConnection *trivial_gz_;
4340 //NSURLConnection *automatic_;
4345 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4349 @implementation SourceTable
4351 - (void) _deallocConnection:(NSURLConnection *)connection {
4352 if (connection != nil) {
4353 [connection cancel];
4354 //[connection setDelegate:nil];
4355 [connection release];
4360 [[list_ table] setDelegate:nil];
4361 [list_ setDataSource:nil];
4370 //[self _deallocConnection:installer_];
4371 [self _deallocConnection:trivial_gz_];
4372 [self _deallocConnection:trivial_bz2_];
4373 //[self _deallocConnection:automatic_];
4380 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4381 return offset_ == 0 ? 1 : 2;
4384 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4385 switch (section + (offset_ == 0 ? 1 : 0)) {
4386 case 0: return @"Entered by User";
4387 case 1: return @"Installed by Packages";
4395 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4396 switch (section + (offset_ == 0 ? 1 : 0)) {
4398 case 1: return offset_;
4406 - (int) numberOfRowsInTable:(UITable *)table {
4407 return [sources_ count];
4410 - (float) table:(UITable *)table heightForRow:(int)row {
4411 Source *source = [sources_ objectAtIndex:row];
4412 return [source description] == nil ? 56 : 73;
4415 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4416 Source *source = [sources_ objectAtIndex:row];
4417 // XXX: weird warning, stupid selectors ;P
4418 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4421 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4425 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4429 - (void) tableRowSelected:(NSNotification*)notification {
4430 UITable *table([list_ table]);
4431 int row([table selectedRow]);
4435 Source *source = [sources_ objectAtIndex:row];
4437 PackageTable *packages = [[[FilteredPackageTable alloc]
4440 title:[source label]
4441 filter:@selector(isVisibleInSource:)
4445 [packages setDelegate:delegate_];
4447 [book_ pushPage:packages];
4450 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4451 Source *source = [sources_ objectAtIndex:row];
4452 return [source record] != nil;
4455 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4456 [[list_ table] setDeleteConfirmationRow:row];
4459 - (void) table:(UITable *)table deleteRow:(int)row {
4460 Source *source = [sources_ objectAtIndex:row];
4461 [Sources_ removeObjectForKey:[source key]];
4462 [delegate_ syncData];
4465 - (void) _endConnection:(NSURLConnection *)connection {
4466 NSURLConnection **field = NULL;
4467 if (connection == trivial_bz2_)
4468 field = &trivial_bz2_;
4469 else if (connection == trivial_gz_)
4470 field = &trivial_gz_;
4471 _assert(field != NULL);
4472 [connection release];
4476 trivial_bz2_ == nil &&
4479 [delegate_ setStatusBarShowsProgress:NO];
4482 [hud_ removeFromSuperview];
4487 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4490 @"./", @"Distribution",
4491 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4493 [delegate_ syncData];
4494 } else if (error_ != nil) {
4495 UIActionSheet *sheet = [[[UIActionSheet alloc]
4496 initWithTitle:@"Verification Error"
4497 buttons:[NSArray arrayWithObjects:@"OK", nil]
4498 defaultButtonIndex:0
4503 [sheet setBodyText:[error_ localizedDescription]];
4504 [sheet popupAlertAnimated:YES];
4506 UIActionSheet *sheet = [[[UIActionSheet alloc]
4507 initWithTitle:@"Did not Find Repository"
4508 buttons:[NSArray arrayWithObjects:@"OK", nil]
4509 defaultButtonIndex:0
4514 [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."];
4515 [sheet popupAlertAnimated:YES];
4521 if (error_ != nil) {
4528 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4529 switch ([response statusCode]) {
4535 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4536 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4538 error_ = [error retain];
4539 [self _endConnection:connection];
4542 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4543 [self _endConnection:connection];
4546 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4547 NSMutableURLRequest *request = [NSMutableURLRequest
4548 requestWithURL:[NSURL URLWithString:href]
4549 cachePolicy:NSURLRequestUseProtocolCachePolicy
4550 timeoutInterval:20.0
4553 [request setHTTPMethod:method];
4555 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4558 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4559 NSString *context([sheet context]);
4561 if ([context isEqualToString:@"source"]) {
4564 NSString *href = [[sheet textField] text];
4566 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4568 if (![href hasSuffix:@"/"])
4569 href_ = [href stringByAppendingString:@"/"];
4572 href_ = [href_ retain];
4574 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4575 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4576 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4580 hud_ = [delegate_ addProgressHUD];
4581 [hud_ setText:@"Verifying URL"];
4592 } else if ([context isEqualToString:@"trivial"])
4594 else if ([context isEqualToString:@"urlerror"])
4598 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4599 if ((self = [super initWithBook:book]) != nil) {
4600 database_ = database;
4601 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4603 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4604 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4605 [list_ setShouldHideHeaderInShortLists:NO];
4607 [self addSubview:list_];
4608 [list_ setDataSource:self];
4610 UITableColumn *column = [[UITableColumn alloc]
4611 initWithTitle:@"Name"
4613 width:[self frame].size.width
4616 UITable *table = [list_ table];
4617 [table setSeparatorStyle:1];
4618 [table addTableColumn:column];
4619 [table setDelegate:self];
4623 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4624 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4628 - (void) reloadData {
4630 _assert(list.ReadMainList());
4632 [sources_ removeAllObjects];
4633 [sources_ addObjectsFromArray:[database_ sources]];
4635 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4638 int count = [sources_ count];
4639 for (offset_ = 0; offset_ != count; ++offset_) {
4640 Source *source = [sources_ objectAtIndex:offset_];
4641 if ([source record] == nil)
4648 - (void) resetViewAnimated:(BOOL)animated {
4649 [list_ resetViewAnimated:animated];
4652 - (void) _leftButtonClicked {
4653 /*[book_ pushPage:[[[AddSourceView alloc]
4658 UIActionSheet *sheet = [[[UIActionSheet alloc]
4659 initWithTitle:@"Enter Cydia/APT URL"
4660 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4661 defaultButtonIndex:0
4666 [sheet setNumberOfRows:1];
4668 [sheet addTextFieldWithValue:@"http://" label:@""];
4670 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4671 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4672 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4673 [traits setKeyboardType:UIKeyboardTypeURL];
4674 // XXX: UIReturnKeyDone
4675 [traits setReturnKeyType:UIReturnKeyNext];
4677 [sheet popupAlertAnimated:YES];
4680 - (void) _rightButtonClicked {
4681 UITable *table = [list_ table];
4682 BOOL editing = [table isRowDeletionEnabled];
4683 [table enableRowDeletion:!editing animated:YES];
4684 [book_ reloadButtonsForPage:self];
4687 - (NSString *) title {
4691 - (NSString *) leftButtonTitle {
4692 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4695 - (id) rightButtonTitle {
4696 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4699 - (UINavigationButtonStyle) rightButtonStyle {
4700 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4706 /* Installed View {{{ */
4707 @interface InstalledView : RVPage {
4708 _transient Database *database_;
4709 FilteredPackageTable *packages_;
4713 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4717 @implementation InstalledView
4720 [packages_ release];
4724 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4725 if ((self = [super initWithBook:book]) != nil) {
4726 database_ = database;
4728 packages_ = [[FilteredPackageTable alloc]
4732 filter:@selector(isInstalledAndVisible:)
4733 with:[NSNumber numberWithBool:YES]
4736 [self addSubview:packages_];
4738 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4739 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4743 - (void) resetViewAnimated:(BOOL)animated {
4744 [packages_ resetViewAnimated:animated];
4747 - (void) reloadData {
4748 [packages_ reloadData];
4751 - (void) _rightButtonClicked {
4752 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4753 [packages_ reloadData];
4755 [book_ reloadButtonsForPage:self];
4758 - (NSString *) title {
4759 return @"Installed";
4762 - (NSString *) backButtonTitle {
4766 - (id) rightButtonTitle {
4767 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4770 - (UINavigationButtonStyle) rightButtonStyle {
4771 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4774 - (void) setDelegate:(id)delegate {
4775 [super setDelegate:delegate];
4776 [packages_ setDelegate:delegate];
4783 @interface HomeView : BrowserView {
4788 @implementation HomeView
4790 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4791 NSString *context([sheet context]);
4793 if ([context isEqualToString:@"about"])
4796 [super alertSheet:sheet buttonClicked:button];
4799 - (void) _leftButtonClicked {
4800 UIActionSheet *sheet = [[[UIActionSheet alloc]
4801 initWithTitle:@"About Cydia Installer"
4802 buttons:[NSArray arrayWithObjects:@"Close", nil]
4803 defaultButtonIndex:0
4809 @"Copyright (C) 2008\n"
4810 "Jay Freeman (saurik)\n"
4811 "saurik@saurik.com\n"
4812 "http://www.saurik.com/\n"
4815 "http://www.theokorigroup.com/\n"
4817 "College of Creative Studies,\n"
4818 "University of California,\n"
4820 "http://www.ccs.ucsb.edu/"
4823 [sheet popupAlertAnimated:YES];
4826 - (NSString *) leftButtonTitle {
4832 /* Manage View {{{ */
4833 @interface ManageView : BrowserView {
4838 @implementation ManageView
4840 - (NSString *) title {
4844 - (void) _leftButtonClicked {
4845 [delegate_ askForSettings];
4848 - (NSString *) leftButtonTitle {
4853 - (id) _rightButtonTitle {
4865 /* Indirect Delegate {{{ */
4866 @interface IndirectDelegate : NSProxy {
4867 _transient volatile id delegate_;
4870 - (void) setDelegate:(id)delegate;
4871 - (id) initWithDelegate:(id)delegate;
4874 @implementation IndirectDelegate
4876 - (void) setDelegate:(id)delegate {
4877 delegate_ = delegate;
4880 - (id) initWithDelegate:(id)delegate {
4881 delegate_ = delegate;
4885 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4886 if (delegate_ != nil)
4887 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4889 // XXX: I fucking hate Apple so very very bad
4890 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4893 - (void) forwardInvocation:(NSInvocation *)inv {
4894 SEL sel = [inv selector];
4895 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4896 [inv invokeWithTarget:delegate_];
4902 #include <BrowserView.m>
4904 /* Cydia Book {{{ */
4905 @interface CYBook : RVBook <
4908 _transient Database *database_;
4909 UINavigationBar *overlay_;
4910 UINavigationBar *underlay_;
4911 UIProgressIndicator *indicator_;
4912 UITextLabel *prompt_;
4913 UIProgressBar *progress_;
4914 UINavigationButton *cancel_;
4917 NSTimeInterval last_;
4920 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4926 @implementation CYBook
4930 [indicator_ release];
4932 [progress_ release];
4937 - (NSString *) getTitleForPage:(RVPage *)page {
4938 return Simplify([super getTitleForPage:page]);
4946 [UIView beginAnimations:nil context:NULL];
4948 CGRect ovrframe = [overlay_ frame];
4949 ovrframe.origin.y = 0;
4950 [overlay_ setFrame:ovrframe];
4952 CGRect barframe = [navbar_ frame];
4953 barframe.origin.y += ovrframe.size.height;
4954 [navbar_ setFrame:barframe];
4956 CGRect trnframe = [transition_ frame];
4957 trnframe.origin.y += ovrframe.size.height;
4958 trnframe.size.height -= ovrframe.size.height;
4959 [transition_ setFrame:trnframe];
4961 [UIView endAnimations];
4963 [indicator_ startAnimation];
4964 [prompt_ setText:@"Updating Database"];
4965 [progress_ setProgress:0];
4968 last_ = [NSDate timeIntervalSinceReferenceDate];
4970 [overlay_ addSubview:cancel_];
4973 detachNewThreadSelector:@selector(_update)
4982 [indicator_ stopAnimation];
4984 [UIView beginAnimations:nil context:NULL];
4986 CGRect ovrframe = [overlay_ frame];
4987 ovrframe.origin.y = -ovrframe.size.height;
4988 [overlay_ setFrame:ovrframe];
4990 CGRect barframe = [navbar_ frame];
4991 barframe.origin.y -= ovrframe.size.height;
4992 [navbar_ setFrame:barframe];
4994 CGRect trnframe = [transition_ frame];
4995 trnframe.origin.y -= ovrframe.size.height;
4996 trnframe.size.height += ovrframe.size.height;
4997 [transition_ setFrame:trnframe];
4999 [UIView commitAnimations];
5001 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5004 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5005 if ((self = [super initWithFrame:frame]) != nil) {
5006 database_ = database;
5008 CGRect ovrrect = [navbar_ bounds];
5009 ovrrect.size.height = [UINavigationBar defaultSize].height;
5010 ovrrect.origin.y = -ovrrect.size.height;
5012 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5013 [self addSubview:overlay_];
5015 ovrrect.origin.y = frame.size.height;
5016 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5017 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5018 [self addSubview:underlay_];
5020 [overlay_ setBarStyle:1];
5021 [underlay_ setBarStyle:1];
5023 int barstyle = [overlay_ _barStyle:NO];
5024 bool ugly = barstyle == 0;
5026 UIProgressIndicatorStyle style = ugly ?
5027 UIProgressIndicatorStyleMediumBrown :
5028 UIProgressIndicatorStyleMediumWhite;
5030 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5031 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5032 CGRect indrect = {{indoffset, indoffset}, indsize};
5034 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5035 [indicator_ setStyle:style];
5036 [overlay_ addSubview:indicator_];
5038 CGSize prmsize = {215, indsize.height + 4};
5041 indoffset * 2 + indsize.width,
5045 unsigned(ovrrect.size.height - prmsize.height) / 2
5048 UIFont *font = [UIFont systemFontOfSize:15];
5050 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5052 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5053 [prompt_ setBackgroundColor:[UIColor clearColor]];
5054 [prompt_ setFont:font];
5056 [overlay_ addSubview:prompt_];
5058 CGSize prgsize = {75, 100};
5061 ovrrect.size.width - prgsize.width - 10,
5062 (ovrrect.size.height - prgsize.height) / 2
5065 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5066 [progress_ setStyle:0];
5067 [overlay_ addSubview:progress_];
5069 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5070 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5072 CGRect frame = [cancel_ frame];
5073 frame.size.width = 65;
5074 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5075 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5076 [cancel_ setFrame:frame];
5078 [cancel_ setBarStyle:barstyle];
5082 - (void) _onCancel {
5084 [cancel_ removeFromSuperview];
5087 - (void) _update { _pooled
5089 status.setDelegate(self);
5091 [database_ updateWithStatus:status];
5094 performSelectorOnMainThread:@selector(_update_)
5100 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5101 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5104 - (void) setProgressTitle:(NSString *)title {
5106 performSelectorOnMainThread:@selector(_setProgressTitle:)
5112 - (void) setProgressPercent:(float)percent {
5114 performSelectorOnMainThread:@selector(_setProgressPercent:)
5115 withObject:[NSNumber numberWithFloat:percent]
5120 - (void) startProgress {
5123 - (void) addProgressOutput:(NSString *)output {
5125 performSelectorOnMainThread:@selector(_addProgressOutput:)
5131 - (bool) isCancelling:(size_t)received {
5132 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5133 if (received_ != received) {
5134 received_ = received;
5136 } else if (now - last_ > 15)
5141 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5145 - (void) _setProgressTitle:(NSString *)title {
5146 [prompt_ setText:title];
5149 - (void) _setProgressPercent:(NSNumber *)percent {
5150 [progress_ setProgress:[percent floatValue]];
5153 - (void) _addProgressOutput:(NSString *)output {
5158 /* Cydia:// Protocol {{{ */
5159 @interface CydiaURLProtocol : NSURLProtocol {
5164 @implementation CydiaURLProtocol
5166 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5167 NSURL *url([request URL]);
5170 NSString *scheme([[url scheme] lowercaseString]);
5171 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5176 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5180 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5181 id<NSURLProtocolClient> client([self client]);
5183 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5185 NSData *data(UIImagePNGRepresentation(icon));
5187 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5188 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5189 [client URLProtocol:self didLoadData:data];
5190 [client URLProtocolDidFinishLoading:self];
5194 - (void) startLoading {
5195 id<NSURLProtocolClient> client([self client]);
5196 NSURLRequest *request([self request]);
5198 NSURL *url([request URL]);
5199 NSString *href([url absoluteString]);
5201 NSString *path([href substringFromIndex:8]);
5202 NSRange slash([path rangeOfString:@"/"]);
5205 if (slash.location == NSNotFound) {
5209 command = [path substringToIndex:slash.location];
5210 path = [path substringFromIndex:(slash.location + 1)];
5213 Database *database([Database sharedInstance]);
5215 if ([command isEqualToString:@"package-icon"]) {
5218 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5219 Package *package([database packageWithName:path]);
5222 UIImage *icon([package icon]);
5223 [self _returnPNGWithImage:icon forRequest:request];
5224 } else if ([command isEqualToString:@"source-icon"]) {
5227 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5228 NSString *source(Simplify(path));
5229 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5231 icon = [UIImage applicationImageNamed:@"unknown.png"];
5232 [self _returnPNGWithImage:icon forRequest:request];
5233 } else if ([command isEqualToString:@"uikit-image"]) {
5236 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5237 UIImage *icon(_UIImageWithName(path));
5238 [self _returnPNGWithImage:icon forRequest:request];
5239 } else if ([command isEqualToString:@"section-icon"]) {
5242 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5243 NSString *section(Simplify(path));
5244 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5246 icon = [UIImage applicationImageNamed:@"unknown.png"];
5247 [self _returnPNGWithImage:icon forRequest:request];
5249 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5253 - (void) stopLoading {
5259 /* Sections View {{{ */
5260 @interface SectionsView : RVPage {
5261 _transient Database *database_;
5262 NSMutableArray *sections_;
5263 NSMutableArray *filtered_;
5264 UITransitionView *transition_;
5270 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5271 - (void) reloadData;
5276 @implementation SectionsView
5279 [list_ setDataSource:nil];
5280 [list_ setDelegate:nil];
5282 [sections_ release];
5283 [filtered_ release];
5284 [transition_ release];
5286 [accessory_ release];
5290 - (int) numberOfRowsInTable:(UITable *)table {
5291 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5294 - (float) table:(UITable *)table heightForRow:(int)row {
5298 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5300 reusing = [[[SectionCell alloc] init] autorelease];
5301 [(SectionCell *)reusing setSection:(editing_ ?
5302 [sections_ objectAtIndex:row] :
5303 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5304 ) editing:editing_];
5308 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5312 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5316 - (void) tableRowSelected:(NSNotification *)notification {
5317 int row = [[notification object] selectedRow];
5328 title = @"All Packages";
5330 section = [filtered_ objectAtIndex:(row - 1)];
5331 name = [section name];
5337 title = @"(No Section)";
5341 PackageTable *table = [[[FilteredPackageTable alloc]
5345 filter:@selector(isVisiblyUninstalledInSection:)
5349 [table setDelegate:delegate_];
5351 [book_ pushPage:table];
5354 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5355 if ((self = [super initWithBook:book]) != nil) {
5356 database_ = database;
5358 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5359 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5361 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5362 [self addSubview:transition_];
5364 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5365 [transition_ transition:0 toView:list_];
5367 UITableColumn *column = [[[UITableColumn alloc]
5368 initWithTitle:@"Name"
5370 width:[self frame].size.width
5373 [list_ setDataSource:self];
5374 [list_ setSeparatorStyle:1];
5375 [list_ addTableColumn:column];
5376 [list_ setDelegate:self];
5377 [list_ setReusesTableCells:YES];
5381 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5382 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5386 - (void) reloadData {
5387 NSArray *packages = [database_ packages];
5389 [sections_ removeAllObjects];
5390 [filtered_ removeAllObjects];
5392 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5393 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5396 for (size_t i(0); i != [packages count]; ++i) {
5397 Package *package([packages objectAtIndex:i]);
5398 NSString *name([package section]);
5401 Section *section([sections objectForKey:name]);
5402 if (section == nil) {
5403 section = [[[Section alloc] initWithName:name] autorelease];
5404 [sections setObject:section forKey:name];
5408 if ([package valid] && [package installed] == nil && [package visible])
5409 [filtered addObject:package];
5413 [sections_ addObjectsFromArray:[sections allValues]];
5414 [sections_ sortUsingSelector:@selector(compareByName:)];
5417 [filtered sortUsingSelector:@selector(compareBySection:)];
5420 Section *section = nil;
5421 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5422 Package *package = [filtered objectAtIndex:offset];
5423 NSString *name = [package section];
5425 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5426 section = name == nil ?
5427 [[[Section alloc] initWithName:nil] autorelease] :
5428 [sections objectForKey:name];
5429 [filtered_ addObject:section];
5432 [section addToCount];
5440 - (void) resetView {
5442 [self _rightButtonClicked];
5445 - (void) resetViewAnimated:(BOOL)animated {
5446 [list_ resetViewAnimated:animated];
5449 - (void) _rightButtonClicked {
5450 if ((editing_ = !editing_))
5453 [delegate_ updateData];
5454 [book_ reloadTitleForPage:self];
5455 [book_ reloadButtonsForPage:self];
5458 - (NSString *) title {
5459 return editing_ ? @"Section Visibility" : @"Install by Section";
5462 - (NSString *) backButtonTitle {
5466 - (id) rightButtonTitle {
5467 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5470 - (UINavigationButtonStyle) rightButtonStyle {
5471 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5474 - (UIView *) accessoryView {
5480 /* Changes View {{{ */
5481 @interface ChangesView : RVPage {
5482 _transient Database *database_;
5483 NSMutableArray *packages_;
5484 NSMutableArray *sections_;
5485 UISectionList *list_;
5489 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5490 - (void) reloadData;
5494 @implementation ChangesView
5497 [[list_ table] setDelegate:nil];
5498 [list_ setDataSource:nil];
5500 [packages_ release];
5501 [sections_ release];
5506 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5507 return [sections_ count];
5510 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5511 return [[sections_ objectAtIndex:section] name];
5514 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5515 return [[sections_ objectAtIndex:section] row];
5518 - (int) numberOfRowsInTable:(UITable *)table {
5519 return [packages_ count];
5522 - (float) table:(UITable *)table heightForRow:(int)row {
5523 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5526 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5528 reusing = [[[PackageCell alloc] init] autorelease];
5529 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5533 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5537 - (void) tableRowSelected:(NSNotification *)notification {
5538 int row = [[notification object] selectedRow];
5541 Package *package = [packages_ objectAtIndex:row];
5542 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5543 [view setDelegate:delegate_];
5544 [view setPackage:package];
5545 [book_ pushPage:view];
5548 - (void) _leftButtonClicked {
5549 [(CYBook *)book_ update];
5550 [self reloadButtons];
5553 - (void) _rightButtonClicked {
5554 [delegate_ distUpgrade];
5557 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5558 if ((self = [super initWithBook:book]) != nil) {
5559 database_ = database;
5561 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5562 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5564 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5565 [self addSubview:list_];
5567 [list_ setShouldHideHeaderInShortLists:NO];
5568 [list_ setDataSource:self];
5569 //[list_ setSectionListStyle:1];
5571 UITableColumn *column = [[[UITableColumn alloc]
5572 initWithTitle:@"Name"
5574 width:[self frame].size.width
5577 UITable *table = [list_ table];
5578 [table setSeparatorStyle:1];
5579 [table addTableColumn:column];
5580 [table setDelegate:self];
5581 [table setReusesTableCells:YES];
5585 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5586 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5590 - (void) reloadData {
5591 NSArray *packages = [database_ packages];
5593 [packages_ removeAllObjects];
5594 [sections_ removeAllObjects];
5597 for (size_t i(0); i != [packages count]; ++i) {
5598 Package *package([packages objectAtIndex:i]);
5601 [package installed] == nil && [package valid] && [package visible] ||
5602 [package upgradableAndEssential:YES]
5604 [packages_ addObject:package];
5608 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5611 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5612 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5613 Section *section = nil;
5617 bool unseens = false;
5619 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5622 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5623 Package *package = [packages_ objectAtIndex:offset];
5625 if (![package upgradableAndEssential:YES]) {
5627 NSDate *seen = [package seen];
5629 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5632 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5633 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5634 [sections_ addObject:section];
5638 [section addToCount];
5639 } else if ([package ignored])
5640 [ignored addToCount];
5643 [upgradable addToCount];
5648 CFRelease(formatter);
5651 Section *last = [sections_ lastObject];
5652 size_t count = [last count];
5653 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5654 [sections_ removeLastObject];
5657 if ([ignored count] != 0)
5658 [sections_ insertObject:ignored atIndex:0];
5660 [sections_ insertObject:upgradable atIndex:0];
5663 [self reloadButtons];
5666 - (void) resetViewAnimated:(BOOL)animated {
5667 [list_ resetViewAnimated:animated];
5670 - (NSString *) leftButtonTitle {
5671 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5674 - (id) rightButtonTitle {
5675 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5678 - (NSString *) title {
5684 /* Search View {{{ */
5685 @protocol SearchViewDelegate
5686 - (void) showKeyboard:(BOOL)show;
5689 @interface SearchView : RVPage {
5691 UISearchField *field_;
5692 UITransitionView *transition_;
5693 FilteredPackageTable *table_;
5694 UIPreferencesTable *advanced_;
5700 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5701 - (void) reloadData;
5705 @implementation SearchView
5708 [field_ setDelegate:nil];
5710 [accessory_ release];
5712 [transition_ release];
5714 [advanced_ release];
5719 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5723 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5725 case 0: return @"Advanced Search (Coming Soon!)";
5727 default: _assert(false);
5731 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5735 default: _assert(false);
5739 - (void) _showKeyboard:(BOOL)show {
5740 CGSize keysize = [UIKeyboard defaultSize];
5741 CGRect keydown = [book_ pageBounds];
5742 CGRect keyup = keydown;
5743 keyup.size.height -= keysize.height - ButtonBarHeight_;
5745 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5747 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5748 [animation setSignificantRectFields:8];
5751 [animation setStartFrame:keydown];
5752 [animation setEndFrame:keyup];
5754 [animation setStartFrame:keyup];
5755 [animation setEndFrame:keydown];
5758 UIAnimator *animator = [UIAnimator sharedAnimator];
5761 addAnimations:[NSArray arrayWithObjects:animation, nil]
5762 withDuration:(KeyboardTime_ - delay)
5767 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5769 [delegate_ showKeyboard:show];
5772 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5773 [self _showKeyboard:YES];
5776 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5777 [self _showKeyboard:NO];
5780 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5782 NSString *text([field_ text]);
5783 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5789 - (void) textFieldClearButtonPressed:(UITextField *)field {
5793 - (void) keyboardInputShouldDelete:(id)input {
5797 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5798 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5802 [field_ resignFirstResponder];
5807 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5808 if ((self = [super initWithBook:book]) != nil) {
5809 CGRect pageBounds = [book_ pageBounds];
5811 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5812 [self addSubview:transition_];
5814 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5816 [advanced_ setReusesTableCells:YES];
5817 [advanced_ setDataSource:self];
5818 [advanced_ reloadData];
5820 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5821 CGColor dimmed(space_, 0, 0, 0, 0.5);
5822 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5824 table_ = [[FilteredPackageTable alloc]
5828 filter:@selector(isUnfilteredAndSearchedForBy:)
5832 [table_ setShouldHideHeaderInShortLists:NO];
5833 [transition_ transition:0 toView:table_];
5842 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5849 [self bounds].size.width - area.origin.x - 18;
5851 area.size.height = [UISearchField defaultHeight];
5853 field_ = [[UISearchField alloc] initWithFrame:area];
5855 UIFont *font = [UIFont systemFontOfSize:16];
5856 [field_ setFont:font];
5858 [field_ setPlaceholder:@"Package Names & Descriptions"];
5859 [field_ setDelegate:self];
5861 [field_ setPaddingTop:5];
5863 UITextInputTraits *traits([field_ textInputTraits]);
5864 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5865 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5866 [traits setReturnKeyType:UIReturnKeySearch];
5868 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5870 accessory_ = [[UIView alloc] initWithFrame:accrect];
5871 [accessory_ addSubview:field_];
5873 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5874 [configure setShowPressFeedback:YES];
5875 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5876 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5877 [accessory_ addSubview:configure];*/
5879 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5880 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5886 LKAnimation *animation = [LKTransition animation];
5887 [animation setType:@"oglFlip"];
5888 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5889 [animation setFillMode:@"extended"];
5890 [animation setTransitionFlags:3];
5891 [animation setDuration:10];
5892 [animation setSpeed:0.35];
5893 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5894 [[transition_ _layer] addAnimation:animation forKey:0];
5895 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5896 flipped_ = !flipped_;
5900 - (void) configurePushed {
5901 [field_ resignFirstResponder];
5905 - (void) resetViewAnimated:(BOOL)animated {
5908 [table_ resetViewAnimated:animated];
5911 - (void) _reloadData {
5914 - (void) reloadData {
5917 [table_ setObject:[field_ text]];
5918 [table_ reloadData];
5919 [table_ resetCursor];
5922 - (UIView *) accessoryView {
5926 - (NSString *) title {
5930 - (NSString *) backButtonTitle {
5934 - (void) setDelegate:(id)delegate {
5935 [table_ setDelegate:delegate];
5936 [super setDelegate:delegate];
5942 @interface SettingsView : RVPage {
5943 _transient Database *database_;
5946 UIPreferencesTable *table_;
5947 _UISwitchSlider *subscribedSwitch_;
5948 _UISwitchSlider *ignoredSwitch_;
5949 UIPreferencesControlTableCell *subscribedCell_;
5950 UIPreferencesControlTableCell *ignoredCell_;
5953 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5957 @implementation SettingsView
5960 [table_ setDataSource:nil];
5963 if (package_ != nil)
5966 [subscribedSwitch_ release];
5967 [ignoredSwitch_ release];
5968 [subscribedCell_ release];
5969 [ignoredCell_ release];
5973 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5974 if (package_ == nil)
5980 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5981 if (package_ == nil)
5988 default: _assert(false);
5994 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5995 if (package_ == nil)
6002 default: _assert(false);
6008 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6009 if (package_ == nil)
6016 default: _assert(false);
6022 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6023 if (package_ == nil)
6026 _UISwitchSlider *slider([cell control]);
6027 BOOL value([slider value] != 0);
6028 NSMutableDictionary *metadata([package_ metadata]);
6031 if (NSNumber *number = [metadata objectForKey:key])
6032 before = [number boolValue];
6036 if (value != before) {
6037 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6039 [delegate_ updateData];
6043 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6044 [self onSomething:cell withKey:@"IsSubscribed"];
6047 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6048 [self onSomething:cell withKey:@"IsIgnored"];
6051 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6052 if (package_ == nil)
6056 case 0: switch (row) {
6058 return subscribedCell_;
6060 return ignoredCell_;
6061 default: _assert(false);
6064 case 1: switch (row) {
6066 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6067 [cell setShowSelection:NO];
6068 [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."];
6072 default: _assert(false);
6075 default: _assert(false);
6081 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6082 if ((self = [super initWithBook:book])) {
6083 database_ = database;
6084 name_ = [package retain];
6086 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6087 [self addSubview:table_];
6089 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6090 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6092 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6093 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6095 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6096 [subscribedCell_ setShowSelection:NO];
6097 [subscribedCell_ setTitle:@"Show All Changes"];
6098 [subscribedCell_ setControl:subscribedSwitch_];
6100 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6101 [ignoredCell_ setShowSelection:NO];
6102 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6103 [ignoredCell_ setControl:ignoredSwitch_];
6105 [table_ setDataSource:self];
6110 - (void) resetViewAnimated:(BOOL)animated {
6111 [table_ resetViewAnimated:animated];
6114 - (void) reloadData {
6115 if (package_ != nil)
6116 [package_ autorelease];
6117 package_ = [database_ packageWithName:name_];
6118 if (package_ != nil) {
6120 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6121 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6124 [table_ reloadData];
6127 - (NSString *) title {
6133 /* Signature View {{{ */
6134 @interface SignatureView : BrowserView {
6135 _transient Database *database_;
6139 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6143 @implementation SignatureView
6150 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6152 [super webView:sender didClearWindowObject:window forFrame:frame];
6155 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6156 if ((self = [super initWithBook:book]) != nil) {
6157 database_ = database;
6158 package_ = [package retain];
6163 - (void) resetViewAnimated:(BOOL)animated {
6166 - (void) reloadData {
6167 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6173 @interface Cydia : UIApplication <
6174 ConfirmationViewDelegate,
6175 ProgressViewDelegate,
6184 UIToolbar *buttonbar_;
6188 NSMutableArray *essential_;
6189 NSMutableArray *broken_;
6191 Database *database_;
6192 ProgressView *progress_;
6196 UIKeyboard *keyboard_;
6197 UIProgressHUD *hud_;
6199 SectionsView *sections_;
6200 ChangesView *changes_;
6201 ManageView *manage_;
6202 SearchView *search_;
6207 @implementation Cydia
6210 if ([broken_ count] != 0) {
6211 int count = [broken_ count];
6213 UIActionSheet *sheet = [[[UIActionSheet alloc]
6214 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6215 buttons:[NSArray arrayWithObjects:
6217 @"Ignore (Temporary)",
6219 defaultButtonIndex:0
6224 [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."];
6225 [sheet popupAlertAnimated:YES];
6226 } else if (!Ignored_ && [essential_ count] != 0) {
6227 int count = [essential_ count];
6229 UIActionSheet *sheet = [[[UIActionSheet alloc]
6230 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6231 buttons:[NSArray arrayWithObjects:
6232 @"Upgrade Essential",
6233 @"Complete Upgrade",
6234 @"Ignore (Temporary)",
6236 defaultButtonIndex:0
6241 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6242 [sheet popupAlertAnimated:YES];
6246 - (void) _reloadData {
6247 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6248 [hud setText:@"Reloading Data"];
6249 [overlay_ addSubview:hud];
6252 [database_ reloadData];
6256 [essential_ removeAllObjects];
6257 [broken_ removeAllObjects];
6259 NSArray *packages = [database_ packages];
6260 for (Package *package in packages) {
6262 [broken_ addObject:package];
6263 if ([package upgradableAndEssential:NO]) {
6264 if ([package essential])
6265 [essential_ addObject:package];
6271 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6272 [buttonbar_ setBadgeValue:badge forButton:3];
6273 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6274 [buttonbar_ setBadgeAnimated:YES forButton:3];
6275 [self setApplicationBadge:badge];
6277 [buttonbar_ setBadgeValue:nil forButton:3];
6278 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6279 [buttonbar_ setBadgeAnimated:NO forButton:3];
6280 [self removeApplicationBadge];
6286 if ([packages count] == 0);
6298 [hud removeFromSuperview];*/
6301 - (void) _saveConfig {
6304 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6310 - (void) updateData {
6313 /* XXX: this is just stupid */
6314 if (tag_ != 2 && sections_ != nil)
6315 [sections_ reloadData];
6316 if (tag_ != 3 && changes_ != nil)
6317 [changes_ reloadData];
6318 if (tag_ != 5 && search_ != nil)
6319 [search_ reloadData];
6329 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6330 _assert(file != NULL);
6332 NSArray *keys = [Sources_ allKeys];
6334 for (int i(0), e([keys count]); i != e; ++i) {
6335 NSString *key = [keys objectAtIndex:i];
6336 NSDictionary *source = [Sources_ objectForKey:key];
6338 fprintf(file, "%s %s %s\n",
6339 [[source objectForKey:@"Type"] UTF8String],
6340 [[source objectForKey:@"URI"] UTF8String],
6341 [[source objectForKey:@"Distribution"] UTF8String]
6350 detachNewThreadSelector:@selector(update_)
6353 title:@"Updating Sources"
6357 - (void) reloadData {
6358 @synchronized (self) {
6359 if (confirm_ == nil)
6365 pkgProblemResolver *resolver = [database_ resolver];
6367 resolver->InstallProtect();
6368 if (!resolver->Resolve(true))
6373 [database_ prepare];
6375 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6376 [confirm_ setDelegate:self];
6378 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6379 [page setDelegate:self];
6381 [confirm_ setPage:page];
6382 [underlay_ popSubview:confirm_];
6385 - (void) installPackage:(Package *)package {
6386 @synchronized (self) {
6393 - (void) removePackage:(Package *)package {
6394 @synchronized (self) {
6401 - (void) distUpgrade {
6402 @synchronized (self) {
6403 [database_ upgrade];
6409 @synchronized (self) {
6411 if (confirm_ != nil) {
6419 [overlay_ removeFromSuperview];
6423 detachNewThreadSelector:@selector(perform)
6430 - (void) bootstrap_ {
6432 [database_ upgrade];
6433 [database_ prepare];
6434 [database_ perform];
6437 - (void) bootstrap {
6439 detachNewThreadSelector:@selector(bootstrap_)
6442 title:@"Bootstrap Install"
6446 - (void) progressViewIsComplete:(ProgressView *)progress {
6447 if (confirm_ != nil) {
6448 [underlay_ addSubview:overlay_];
6449 [confirm_ popFromSuperviewAnimated:NO];
6455 - (void) setPage:(RVPage *)page {
6456 [page resetViewAnimated:NO];
6457 [page setDelegate:self];
6458 [book_ setPage:page];
6461 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6462 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6463 [browser loadURL:url];
6467 - (void) _setHomePage {
6468 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6471 - (void) buttonBarItemTapped:(id)sender {
6472 unsigned tag = [sender tag];
6474 [book_ resetViewAnimated:YES];
6476 } else if (tag_ == 2 && tag != 2)
6477 [sections_ resetView];
6480 case 1: [self _setHomePage]; break;
6482 case 2: [self setPage:sections_]; break;
6483 case 3: [self setPage:changes_]; break;
6484 case 4: [self setPage:manage_]; break;
6485 case 5: [self setPage:search_]; break;
6487 default: _assert(false);
6493 - (void) applicationWillSuspend {
6495 [super applicationWillSuspend];
6498 - (void) askForSettings {
6499 UIActionSheet *role = [[[UIActionSheet alloc]
6500 initWithTitle:@"Who Are You?"
6501 buttons:[NSArray arrayWithObjects:
6502 @"User (Graphical Only)",
6503 @"Hacker (+ Command Line)",
6504 @"Developer (No Filters)",
6506 defaultButtonIndex:-1
6511 [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."];
6512 [role popupAlertAnimated:YES];
6517 [self setStatusBarShowsProgress:NO];
6520 [hud_ removeFromSuperview];
6524 pid_t pid = ExecFork();
6526 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6527 perror("launchctl stop");
6534 [self askForSettings];
6538 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6540 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6541 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6542 0, 0, screenrect.size.width, screenrect.size.height - 48
6543 ) database:database_];
6545 [book_ setDelegate:self];
6547 [overlay_ addSubview:book_];
6549 NSArray *buttonitems = [NSArray arrayWithObjects:
6550 [NSDictionary dictionaryWithObjectsAndKeys:
6551 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6552 @"home-up.png", kUIButtonBarButtonInfo,
6553 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6554 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6555 self, kUIButtonBarButtonTarget,
6556 @"Home", kUIButtonBarButtonTitle,
6557 @"0", kUIButtonBarButtonType,
6560 [NSDictionary dictionaryWithObjectsAndKeys:
6561 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6562 @"install-up.png", kUIButtonBarButtonInfo,
6563 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6564 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6565 self, kUIButtonBarButtonTarget,
6566 @"Sections", kUIButtonBarButtonTitle,
6567 @"0", kUIButtonBarButtonType,
6570 [NSDictionary dictionaryWithObjectsAndKeys:
6571 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6572 @"changes-up.png", kUIButtonBarButtonInfo,
6573 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6574 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6575 self, kUIButtonBarButtonTarget,
6576 @"Changes", kUIButtonBarButtonTitle,
6577 @"0", kUIButtonBarButtonType,
6580 [NSDictionary dictionaryWithObjectsAndKeys:
6581 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6582 @"manage-up.png", kUIButtonBarButtonInfo,
6583 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6584 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6585 self, kUIButtonBarButtonTarget,
6586 @"Manage", kUIButtonBarButtonTitle,
6587 @"0", kUIButtonBarButtonType,
6590 [NSDictionary dictionaryWithObjectsAndKeys:
6591 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6592 @"search-up.png", kUIButtonBarButtonInfo,
6593 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6594 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6595 self, kUIButtonBarButtonTarget,
6596 @"Search", kUIButtonBarButtonTitle,
6597 @"0", kUIButtonBarButtonType,
6601 buttonbar_ = [[UIToolbar alloc]
6603 withFrame:CGRectMake(
6604 0, screenrect.size.height - ButtonBarHeight_,
6605 screenrect.size.width, ButtonBarHeight_
6607 withItemList:buttonitems
6610 [buttonbar_ setDelegate:self];
6611 [buttonbar_ setBarStyle:1];
6612 [buttonbar_ setButtonBarTrackingMode:2];
6614 int buttons[5] = {1, 2, 3, 4, 5};
6615 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6616 [buttonbar_ showButtonGroup:0 withDuration:0];
6618 for (int i = 0; i != 5; ++i)
6619 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6620 i * 64 + 2, 1, 60, ButtonBarHeight_
6623 [buttonbar_ showSelectionForButton:1];
6624 [overlay_ addSubview:buttonbar_];
6626 [UIKeyboard initImplementationNow];
6627 CGSize keysize = [UIKeyboard defaultSize];
6628 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6629 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6630 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6631 [overlay_ addSubview:keyboard_];
6634 [underlay_ addSubview:overlay_];
6638 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6639 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6640 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6642 manage_ = (ManageView *) [[self
6643 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6644 withClass:[ManageView class]
6650 [self _setHomePage];
6653 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6654 NSString *context([sheet context]);
6656 if ([context isEqualToString:@"missing"])
6658 else if ([context isEqualToString:@"fixhalf"]) {
6661 @synchronized (self) {
6662 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6663 Package *broken = [broken_ objectAtIndex:i];
6666 NSString *id = [broken id];
6667 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6668 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6669 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6670 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6679 [broken_ removeAllObjects];
6688 } else if ([context isEqualToString:@"role"]) {
6690 case 1: Role_ = @"User"; break;
6691 case 2: Role_ = @"Hacker"; break;
6692 case 3: Role_ = @"Developer"; break;
6699 bool reset = Settings_ != nil;
6701 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6705 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6715 } else if ([context isEqualToString:@"upgrade"]) {
6718 @synchronized (self) {
6719 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6720 Package *essential = [essential_ objectAtIndex:i];
6721 [essential install];
6745 - (void) reorganize { _pooled
6746 system("/usr/libexec/cydia/free.sh");
6747 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6750 - (void) applicationSuspend:(__GSEvent *)event {
6751 if (hud_ == nil && ![progress_ isRunning])
6752 [super applicationSuspend:event];
6755 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6757 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6760 - (void) _setSuspended:(BOOL)value {
6762 [super _setSuspended:value];
6765 - (UIProgressHUD *) addProgressHUD {
6766 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6768 [underlay_ addSubview:hud];
6772 - (void) openMailToURL:(NSURL *)url {
6773 // XXX: this makes me sad
6775 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6777 [UIApp openURL:url];// asPanel:YES];
6781 - (void) clearFirstResponder {
6782 if (id responder = [window_ firstResponder])
6783 [responder resignFirstResponder];
6786 - (RVPage *) pageForPackage:(NSString *)name {
6787 if (Package *package = [database_ packageWithName:name]) {
6788 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6789 [view setPackage:package];
6792 UIActionSheet *sheet = [[[UIActionSheet alloc]
6793 initWithTitle:@"Cannot Locate Package"
6794 buttons:[NSArray arrayWithObjects:@"Close", nil]
6795 defaultButtonIndex:0
6800 [sheet setBodyText:[NSString stringWithFormat:
6801 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6804 [sheet popupAlertAnimated:YES];
6809 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6813 NSString *scheme([[url scheme] lowercaseString]);
6814 if (![scheme isEqualToString:@"cydia"])
6816 NSString *path([url absoluteString]);
6817 if ([path length] < 8)
6819 path = [path substringFromIndex:8];
6820 if (![path hasPrefix:@"/"])
6821 path = [@"/" stringByAppendingString:path];
6823 if ([path isEqualToString:@"/add-source"])
6824 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6825 else if ([path isEqualToString:@"/storage"])
6826 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6827 else if ([path isEqualToString:@"/sources"])
6828 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6829 else if ([path isEqualToString:@"/packages"])
6830 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6831 else if ([path hasPrefix:@"/url/"])
6832 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6833 else if ([path hasPrefix:@"/launch/"])
6834 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6835 else if ([path hasPrefix:@"/package-settings/"])
6836 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6837 else if ([path hasPrefix:@"/package-signature/"])
6838 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6839 else if ([path hasPrefix:@"/package/"])
6840 return [self pageForPackage:[path substringFromIndex:9]];
6841 else if ([path hasPrefix:@"/files/"]) {
6842 NSString *name = [path substringFromIndex:7];
6844 if (Package *package = [database_ packageWithName:name]) {
6845 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6846 [files setPackage:package];
6854 - (void) applicationOpenURL:(NSURL *)url {
6855 [super applicationOpenURL:url];
6857 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6858 [self setPage:page];
6859 [buttonbar_ showSelectionForButton:tag];
6864 - (void) applicationDidFinishLaunching:(id)unused {
6865 Font12_ = [[UIFont systemFontOfSize:12] retain];
6866 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6867 Font14_ = [[UIFont systemFontOfSize:14] retain];
6868 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6869 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6871 _assert(pkgInitConfig(*_config));
6872 _assert(pkgInitSystem(*_config, _system));
6876 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6877 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6879 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6881 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6882 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6884 [window_ orderFront:self];
6885 [window_ makeKey:self];
6886 [window_ setHidden:NO];
6888 database_ = [Database sharedInstance];
6889 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6890 [database_ setDelegate:progress_];
6891 [window_ setContentView:progress_];
6893 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6894 [progress_ setContentView:underlay_];
6896 [progress_ resetView];
6899 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6900 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6901 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6902 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6903 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6904 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6905 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6907 [self setIdleTimerDisabled:YES];
6909 hud_ = [self addProgressHUD];
6910 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6912 [self setStatusBarShowsProgress:YES];
6915 detachNewThreadSelector:@selector(reorganize)
6923 - (void) showKeyboard:(BOOL)show {
6924 CGSize keysize = [UIKeyboard defaultSize];
6925 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6926 CGRect keyup = keydown;
6927 keyup.origin.y -= keysize.height;
6929 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6930 [animation setSignificantRectFields:2];
6933 [animation setStartFrame:keydown];
6934 [animation setEndFrame:keyup];
6935 [keyboard_ activate];
6937 [animation setStartFrame:keyup];
6938 [animation setEndFrame:keydown];
6939 [keyboard_ deactivate];
6942 [[UIAnimator sharedAnimator]
6943 addAnimations:[NSArray arrayWithObjects:animation, nil]
6944 withDuration:KeyboardTime_
6949 - (void) slideUp:(UIActionSheet *)alert {
6951 [alert presentSheetFromButtonBar:buttonbar_];
6953 [alert presentSheetInView:overlay_];
6958 void AddPreferences(NSString *plist) { _pooled
6959 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6960 _assert(settings != NULL);
6961 NSMutableArray *items = [settings objectForKey:@"items"];
6965 for (size_t i(0); i != [items count]; ++i) {
6966 NSMutableDictionary *item([items objectAtIndex:i]);
6967 NSString *label = [item objectForKey:@"label"];
6968 if (label != nil && [label isEqualToString:@"Cydia"]) {
6975 for (size_t i(0); i != [items count]; ++i) {
6976 NSDictionary *item([items objectAtIndex:i]);
6977 NSString *label = [item objectForKey:@"label"];
6978 if (label != nil && [label isEqualToString:@"General"]) {
6979 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6980 @"CydiaSettings", @"bundle",
6981 @"PSLinkCell", @"cell",
6982 [NSNumber numberWithBool:YES], @"hasIcon",
6983 [NSNumber numberWithBool:YES], @"isController",
6985 nil] atIndex:(i + 1)];
6991 _assert([settings writeToFile:plist atomically:YES] == YES);
6996 id Alloc_(id self, SEL selector) {
6997 id object = alloc_(self, selector);
6998 lprintf("[%s]A-%p\n", self->isa->name, object);
7003 id Dealloc_(id self, SEL selector) {
7004 id object = dealloc_(self, selector);
7005 lprintf("[%s]D-%p\n", self->isa->name, object);
7009 int main(int argc, char *argv[]) { _pooled
7010 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7012 bool substrate(false);
7018 for (int argi(1); argi != argc; ++argi)
7019 if (strcmp(argv[argi], "--") == 0) {
7021 argv[argi] = argv[0];
7027 for (int argi(1); argi != arge; ++argi)
7028 if (strcmp(args[argi], "--bootstrap") == 0)
7030 else if (strcmp(args[argi], "--substrate") == 0)
7033 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7036 App_ = [[NSBundle mainBundle] bundlePath];
7037 Home_ = NSHomeDirectory();
7038 Locale_ = CFLocaleCopyCurrent();
7041 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7042 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7043 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7044 Sounds_Keyboard_ = [keyboard boolValue];
7050 #if 1 /* XXX: this costs 1.4s of startup performance */
7051 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7052 _assert(errno == ENOENT);
7053 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7054 _assert(errno == ENOENT);
7057 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7058 alloc_ = alloc->method_imp;
7059 alloc->method_imp = (IMP) &Alloc_;*/
7061 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7062 dealloc_ = dealloc->method_imp;
7063 dealloc->method_imp = (IMP) &Dealloc_;*/
7068 size = sizeof(maxproc);
7069 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7070 perror("sysctlbyname(\"kern.maxproc\", ?)");
7071 else if (maxproc < 64) {
7073 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7074 perror("sysctlbyname(\"kern.maxproc\", #)");
7077 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7078 char *machine = new char[size];
7079 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7080 perror("sysctlbyname(\"hw.machine\", ?)");
7084 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7086 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7087 Build_ = [system objectForKey:@"ProductBuildVersion"];
7089 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7090 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7092 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7093 Indices_ = [[NSMutableDictionary alloc] init];*/
7095 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7096 //@"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
7097 //@"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7100 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7101 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7103 Settings_ = [Metadata_ objectForKey:@"Settings"];
7105 Packages_ = [Metadata_ objectForKey:@"Packages"];
7106 Sections_ = [Metadata_ objectForKey:@"Sections"];
7107 Sources_ = [Metadata_ objectForKey:@"Sources"];
7110 if (Settings_ != nil)
7111 Role_ = [Settings_ objectForKey:@"Role"];
7113 if (Packages_ == nil) {
7114 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7115 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7118 if (Sections_ == nil) {
7119 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7120 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7123 if (Sources_ == nil) {
7124 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7125 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7129 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7132 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7133 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7134 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7135 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7137 if (access("/User", F_OK) != 0)
7138 system("/usr/libexec/cydia/firmware.sh");
7140 _assert([[NSFileManager defaultManager]
7141 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7142 withIntermediateDirectories:YES
7147 space_ = CGColorSpaceCreateDeviceRGB();
7149 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7150 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7151 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7152 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7153 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7154 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7156 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7158 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7160 UIApplicationUseLegacyEvents(YES);
7161 UIKeyboardDisableAutomaticAppearance();
7163 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7165 CGColorSpaceRelease(space_);