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>
87 #include <mach-o/nlist.h>
97 #import "BrowserView.h"
100 #import "substrate.h"
103 //#define _finline __attribute__((force_inline))
104 #define _finline inline
109 #define _limit(count) do { \
110 static size_t _count(0); \
111 if (++_count == count) \
115 static uint64_t profile_;
117 #define _timestamp ({ \
119 gettimeofday(&tv, NULL); \
120 tv.tv_sec * 1000000 + tv.tv_usec; \
123 /* Objective-C Handle<> {{{ */
124 template <typename Type_>
126 typedef _H<Type_> This_;
131 _finline void Retain_() {
136 _finline void Clear_() {
142 _finline _H(Type_ *value = NULL, bool mended = false) :
153 _finline This_ &operator =(Type_ *value) {
154 if (value_ != value) {
163 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
165 void NSLogPoint(const char *fix, const CGPoint &point) {
166 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
169 void NSLogRect(const char *fix, const CGRect &rect) {
170 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
173 /* NSForcedOrderingSearch doesn't work on the iPhone */
174 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
175 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
176 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
178 /* iPhoneOS 2.0 Compatibility {{{ */
180 @interface UITextView (iPhoneOS)
181 - (void) setTextSize:(float)size;
184 @implementation UITextView (iPhoneOS)
186 - (void) setTextSize:(float)size {
187 [self setFont:[[self font] fontWithSize:size]];
194 extern NSString * const kCAFilterNearest;
196 /* Information Dictionaries {{{ */
197 @interface NSMutableArray (Cydia)
198 - (void) addInfoDictionary:(NSDictionary *)info;
201 @implementation NSMutableArray (Cydia)
203 - (void) addInfoDictionary:(NSDictionary *)info {
204 [self addObject:info];
209 @interface NSMutableDictionary (Cydia)
210 - (void) addInfoDictionary:(NSDictionary *)info;
213 @implementation NSMutableDictionary (Cydia)
215 - (void) addInfoDictionary:(NSDictionary *)info {
216 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
217 [self setObject:info forKey:bundle];
222 /* Pop Transitions {{{ */
223 @interface PopTransitionView : UITransitionView {
228 @implementation PopTransitionView
230 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
231 if (from != nil && to == nil)
232 [self removeFromSuperview];
237 @interface UIView (PopUpView)
238 - (void) popFromSuperviewAnimated:(BOOL)animated;
239 - (void) popSubview:(UIView *)view;
242 @implementation UIView (PopUpView)
244 - (void) popFromSuperviewAnimated:(BOOL)animated {
245 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
248 - (void) popSubview:(UIView *)view {
249 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
250 [transition setDelegate:transition];
251 [self addSubview:transition];
253 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
254 [transition transition:UITransitionNone toView:blank];
255 [transition transition:UITransitionPushFromBottom toView:view];
261 #define lprintf(args...) fprintf(stderr, args)
264 #define ForSaurik (1 && !ForRelease)
265 #define IgnoreInstall (0 && !ForRelease)
266 #define RecycleWebViews 0
267 #define AlwaysReload (0 && !ForRelease)
270 @interface NSMutableArray (Radix)
271 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
274 @implementation NSMutableArray (Radix)
276 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
277 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
278 [invocation setSelector:selector];
279 [invocation setArgument:&object atIndex:2];
281 size_t count([self count]);
286 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
288 for (size_t i(0); i != count; ++i) {
289 RadixItem &item(lhs[i]);
292 id object([self objectAtIndex:i]);
293 [invocation setTarget:object];
296 [invocation getReturnValue:&item.key];
299 static const size_t width = 32;
300 static const size_t bits = 11;
301 static const size_t slots = 1 << bits;
302 static const size_t passes = (width + (bits - 1)) / bits;
304 size_t *hist(new size_t[slots]);
306 for (size_t pass(0); pass != passes; ++pass) {
307 memset(hist, 0, sizeof(size_t) * slots);
309 for (size_t i(0); i != count; ++i) {
310 uint32_t key(lhs[i].key);
312 key &= _not(uint32_t) >> width - bits;
317 for (size_t i(0); i != slots; ++i) {
318 size_t local(offset);
323 for (size_t i(0); i != count; ++i) {
324 uint32_t key(lhs[i].key);
326 key &= _not(uint32_t) >> width - bits;
327 rhs[hist[key]++] = lhs[i];
337 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
338 for (size_t i(0); i != count; ++i)
339 [values addObject:[self objectAtIndex:lhs[i].index]];
340 [self setArray:values];
348 /* Apple Bug Fixes {{{ */
349 @implementation UIWebDocumentView (Cydia)
351 - (void) _setScrollerOffset:(CGPoint)offset {
352 UIScroller *scroller([self _scroller]);
354 CGSize size([scroller contentSize]);
355 CGSize bounds([scroller bounds].size);
358 max.x = size.width - bounds.width;
359 max.y = size.height - bounds.height;
367 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
368 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
370 [scroller setOffset:offset];
377 kUIControlEventMouseDown = 1 << 0,
378 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
379 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
380 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
381 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
382 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
383 } UIControlEventMasks;
385 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
386 size_t length([self length] - state->state);
389 else if (length > count)
391 for (size_t i(0); i != length; ++i)
392 objects[i] = [self item:state->state++];
393 state->itemsPtr = objects;
394 state->mutationsPtr = (unsigned long *) self;
398 @interface NSString (UIKit)
399 - (NSString *) stringByAddingPercentEscapes;
400 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
403 @interface NSString (Cydia)
404 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
405 - (NSComparisonResult) compareByPath:(NSString *)other;
408 @implementation NSString (Cydia)
410 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
411 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
414 - (NSComparisonResult) compareByPath:(NSString *)other {
415 NSString *prefix = [self commonPrefixWithString:other options:0];
416 size_t length = [prefix length];
418 NSRange lrange = NSMakeRange(length, [self length] - length);
419 NSRange rrange = NSMakeRange(length, [other length] - length);
421 lrange = [self rangeOfString:@"/" options:0 range:lrange];
422 rrange = [other rangeOfString:@"/" options:0 range:rrange];
424 NSComparisonResult value;
426 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
427 value = NSOrderedSame;
428 else if (lrange.location == NSNotFound)
429 value = NSOrderedAscending;
430 else if (rrange.location == NSNotFound)
431 value = NSOrderedDescending;
433 value = NSOrderedSame;
435 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
436 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
437 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
438 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
440 NSComparisonResult result = [lpath compare:rpath];
441 return result == NSOrderedSame ? value : result;
446 /* Perl-Compatible RegEx {{{ */
456 Pcre(const char *regex) :
461 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
464 lprintf("%d:%s\n", offset, error);
468 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
469 matches_ = new int[(capture_ + 1) * 3];
477 NSString *operator [](size_t match) {
478 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
481 bool operator ()(NSString *data) {
482 // XXX: length is for characters, not for bytes
483 return operator ()([data UTF8String], [data length]);
486 bool operator ()(const char *data, size_t size) {
488 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
492 /* Mime Addresses {{{ */
493 @interface Address : NSObject {
499 - (NSString *) address;
501 + (Address *) addressWithString:(NSString *)string;
502 - (Address *) initWithString:(NSString *)string;
505 @implementation Address
514 - (NSString *) name {
518 - (NSString *) address {
522 + (Address *) addressWithString:(NSString *)string {
523 return [[[Address alloc] initWithString:string] autorelease];
526 + (NSArray *) _attributeKeys {
527 return [NSArray arrayWithObjects:@"address", @"name", nil];
530 - (NSArray *) attributeKeys {
531 return [[self class] _attributeKeys];
534 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
535 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
538 - (Address *) initWithString:(NSString *)string {
539 if ((self = [super init]) != nil) {
540 const char *data = [string UTF8String];
541 size_t size = [string length];
543 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
545 if (address_r(data, size)) {
546 name_ = [address_r[1] retain];
547 address_ = [address_r[2] retain];
549 name_ = [string retain];
557 /* CoreGraphics Primitives {{{ */
568 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
571 Set(space, red, green, blue, alpha);
576 CGColorRelease(color_);
583 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
585 float color[] = {red, green, blue, alpha};
586 color_ = CGColorCreate(space, color);
589 operator CGColorRef() {
595 extern "C" void UISetColor(CGColorRef color);
597 /* Random Global Variables {{{ */
598 static const int PulseInterval_ = 50000;
599 static const int ButtonBarHeight_ = 48;
600 static const float KeyboardTime_ = 0.3f;
602 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
603 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
604 #define NotifyConfig_ "/etc/notify.conf"
606 static CGColor Blue_;
607 static CGColor Blueish_;
608 static CGColor Black_;
610 static CGColor White_;
611 static CGColor Gray_;
613 static NSString *App_;
614 static NSString *Home_;
615 static BOOL Sounds_Keyboard_;
617 static BOOL Advanced_;
621 static BOOL Ignored_;
623 static UIFont *Font12_;
624 static UIFont *Font12Bold_;
625 static UIFont *Font14_;
626 static UIFont *Font18Bold_;
627 static UIFont *Font22Bold_;
629 static const char *Machine_ = NULL;
630 static const NSString *UniqueID_ = NULL;
637 CGColorSpaceRef space_;
639 #define FW_LEAST(major, minor, bugfix) \
640 (major < Major_ || major == Major_ && \
641 (minor < Minor_ || minor == Minor_ && \
647 static NSDictionary *SectionMap_;
648 static NSMutableDictionary *Metadata_;
649 static NSMutableDictionary *Indices_;
650 static _transient NSMutableDictionary *Settings_;
651 static _transient NSString *Role_;
652 static _transient NSMutableDictionary *Packages_;
653 static _transient NSMutableDictionary *Sections_;
654 static _transient NSMutableDictionary *Sources_;
655 static bool Changed_;
659 static NSMutableArray *Documents_;
662 NSString *GetLastUpdate() {
663 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
666 return @"Never or Unknown";
668 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
669 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
671 CFRelease(formatter);
673 return [(NSString *) formatted autorelease];
676 /* Display Helpers {{{ */
677 inline float Interpolate(float begin, float end, float fraction) {
678 return (end - begin) * fraction + begin;
681 NSString *SizeString(double size) {
682 bool negative = size < 0;
687 while (size > 1024) {
692 static const char *powers_[] = {"B", "kB", "MB", "GB"};
694 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
697 NSString *StripVersion(NSString *version) {
698 NSRange colon = [version rangeOfString:@":"];
699 if (colon.location != NSNotFound)
700 version = [version substringFromIndex:(colon.location + 1)];
704 NSString *Simplify(NSString *title) {
705 const char *data = [title UTF8String];
706 size_t size = [title length];
708 static Pcre square_r("^\\[(.*)\\]$");
709 if (square_r(data, size))
710 return Simplify(square_r[1]);
712 static Pcre paren_r("^\\((.*)\\)$");
713 if (paren_r(data, size))
714 return Simplify(paren_r[1]);
716 static Pcre title_r("^(.*?) \\(.*\\)$");
717 if (title_r(data, size))
718 return Simplify(title_r[1]);
724 bool isSectionVisible(NSString *section) {
725 NSDictionary *metadata = [Sections_ objectForKey:section];
726 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
727 return hidden == nil || ![hidden boolValue];
730 /* Delegate Prototypes {{{ */
734 @interface NSObject (ProgressDelegate)
737 @implementation NSObject(ProgressDelegate)
739 - (void) _setProgressError:(NSArray *)args {
740 [self performSelector:@selector(setProgressError:forPackage:)
741 withObject:[args objectAtIndex:0]
742 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
748 @protocol ProgressDelegate
749 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
750 - (void) setProgressTitle:(NSString *)title;
751 - (void) setProgressPercent:(float)percent;
752 - (void) startProgress;
753 - (void) addProgressOutput:(NSString *)output;
754 - (bool) isCancelling:(size_t)received;
757 @protocol ConfigurationDelegate
758 - (void) repairWithSelector:(SEL)selector;
759 - (void) setConfigurationData:(NSString *)data;
762 @protocol CydiaDelegate
763 - (void) installPackage:(Package *)package;
764 - (void) removePackage:(Package *)package;
765 - (void) slideUp:(UIActionSheet *)alert;
766 - (void) distUpgrade;
769 - (void) askForSettings;
770 - (UIProgressHUD *) addProgressHUD;
771 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
772 - (RVPage *) pageForPackage:(NSString *)name;
773 - (void) openMailToURL:(NSURL *)url;
774 - (void) clearFirstResponder;
778 /* Status Delegation {{{ */
780 public pkgAcquireStatus
783 _transient NSObject<ProgressDelegate> *delegate_;
791 void setDelegate(id delegate) {
792 delegate_ = delegate;
795 virtual bool MediaChange(std::string media, std::string drive) {
799 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
802 virtual void Fetch(pkgAcquire::ItemDesc &item) {
803 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
804 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
807 virtual void Done(pkgAcquire::ItemDesc &item) {
810 virtual void Fail(pkgAcquire::ItemDesc &item) {
812 item.Owner->Status == pkgAcquire::Item::StatIdle ||
813 item.Owner->Status == pkgAcquire::Item::StatDone
817 std::string &error(item.Owner->ErrorText);
821 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
822 NSArray *fields([description componentsSeparatedByString:@" "]);
823 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
825 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
826 withObject:[NSArray arrayWithObjects:
827 [NSString stringWithUTF8String:error.c_str()],
834 virtual bool Pulse(pkgAcquire *Owner) {
835 bool value = pkgAcquireStatus::Pulse(Owner);
838 double(CurrentBytes + CurrentItems) /
839 double(TotalBytes + TotalItems)
842 [delegate_ setProgressPercent:percent];
843 return [delegate_ isCancelling:CurrentBytes] ? false : value;
846 virtual void Start() {
847 [delegate_ startProgress];
850 virtual void Stop() {
854 /* Progress Delegation {{{ */
859 _transient id<ProgressDelegate> delegate_;
862 virtual void Update() {
863 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
864 [delegate_ setProgressPercent:(Percent / 100)];
873 void setDelegate(id delegate) {
874 delegate_ = delegate;
877 virtual void Done() {
878 [delegate_ setProgressPercent:1];
883 /* Database Interface {{{ */
884 @interface Database : NSObject {
886 pkgDepCache::Policy *policy_;
887 pkgRecords *records_;
888 pkgProblemResolver *resolver_;
889 pkgAcquire *fetcher_;
891 SPtr<pkgPackageManager> manager_;
892 pkgSourceList *list_;
894 NSMutableDictionary *sources_;
895 NSMutableArray *packages_;
897 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
906 + (Database *) sharedInstance;
908 - (void) _readCydia:(NSNumber *)fd;
909 - (void) _readStatus:(NSNumber *)fd;
910 - (void) _readOutput:(NSNumber *)fd;
914 - (Package *) packageWithName:(NSString *)name;
916 - (pkgCacheFile &) cache;
917 - (pkgDepCache::Policy *) policy;
918 - (pkgRecords *) records;
919 - (pkgProblemResolver *) resolver;
920 - (pkgAcquire &) fetcher;
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
982 [distribution_ release];
985 if (description_ != nil)
986 [description_ release];
993 if (defaultIcon_ != nil)
994 [defaultIcon_ release];
1001 + (NSArray *) _attributeKeys {
1002 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1005 - (NSArray *) attributeKeys {
1006 return [[self class] _attributeKeys];
1009 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1010 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1013 - (Source *) initWithMetaIndex:(metaIndex *)index {
1014 if ((self = [super init]) != nil) {
1015 trusted_ = index->IsTrusted();
1017 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1018 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1019 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1021 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1022 if (dindex != NULL) {
1023 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1025 while (std::getline(release, line)) {
1026 std::string::size_type colon(line.find(':'));
1027 if (colon == std::string::npos)
1030 std::string name(line.substr(0, colon));
1031 std::string value(line.substr(colon + 1));
1032 while (!value.empty() && value[0] == ' ')
1033 value = value.substr(1);
1035 if (name == "Default-Icon")
1036 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1037 else if (name == "Description")
1038 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1039 else if (name == "Label")
1040 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1041 else if (name == "Origin")
1042 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1043 else if (name == "Version")
1044 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1048 record_ = [Sources_ objectForKey:[self key]];
1050 record_ = [record_ retain];
1054 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1055 NSDictionary *lhr = [self record];
1056 NSDictionary *rhr = [source record];
1059 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1061 NSString *lhs = [self name];
1062 NSString *rhs = [source name];
1064 if ([lhs length] != 0 && [rhs length] != 0) {
1065 unichar lhc = [lhs characterAtIndex:0];
1066 unichar rhc = [rhs characterAtIndex:0];
1068 if (isalpha(lhc) && !isalpha(rhc))
1069 return NSOrderedAscending;
1070 else if (!isalpha(lhc) && isalpha(rhc))
1071 return NSOrderedDescending;
1074 return [lhs compare:rhs options:LaxCompareOptions_];
1077 - (NSDictionary *) record {
1085 - (NSString *) uri {
1089 - (NSString *) distribution {
1090 return distribution_;
1093 - (NSString *) type {
1097 - (NSString *) key {
1098 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1101 - (NSString *) host {
1102 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1105 - (NSString *) name {
1106 return origin_ == nil ? [self host] : origin_;
1109 - (NSString *) description {
1110 return description_;
1113 - (NSString *) label {
1114 return label_ == nil ? [self host] : label_;
1117 - (NSString *) origin {
1121 - (NSString *) version {
1125 - (NSString *) defaultIcon {
1126 return defaultIcon_;
1131 /* Relationship Class {{{ */
1132 @interface Relationship : NSObject {
1137 - (NSString *) type;
1139 - (NSString *) name;
1143 @implementation Relationship
1151 - (NSString *) type {
1159 - (NSString *) name {
1166 /* Package Class {{{ */
1167 @interface Package : NSObject {
1168 pkgCache::PkgIterator iterator_;
1169 _transient Database *database_;
1170 pkgCache::VerIterator version_;
1171 pkgCache::VerFileIterator file_;
1179 NSString *installed_;
1185 NSString *depiction_;
1186 NSString *homepage_;
1192 NSArray *relationships_;
1195 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1196 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1198 - (pkgCache::PkgIterator) iterator;
1200 - (NSString *) section;
1201 - (NSString *) simpleSection;
1203 - (Address *) maintainer;
1205 - (NSString *) description;
1206 - (NSString *) index;
1208 - (NSMutableDictionary *) metadata;
1210 - (BOOL) subscribed;
1213 - (NSString *) latest;
1214 - (NSString *) installed;
1217 - (BOOL) upgradableAndEssential:(BOOL)essential;
1220 - (BOOL) unfiltered;
1224 - (BOOL) halfConfigured;
1225 - (BOOL) halfInstalled;
1227 - (NSString *) mode;
1230 - (NSString *) name;
1231 - (NSString *) tagline;
1233 - (NSString *) homepage;
1234 - (NSString *) depiction;
1235 - (Address *) author;
1237 - (NSArray *) files;
1238 - (NSArray *) relationships;
1239 - (NSArray *) warnings;
1240 - (NSArray *) applications;
1242 - (Source *) source;
1243 - (NSString *) role;
1244 - (NSString *) rating;
1246 - (BOOL) matches:(NSString *)text;
1248 - (bool) hasSupportingRole;
1249 - (BOOL) hasTag:(NSString *)tag;
1250 - (NSString *) primaryPurpose;
1251 - (NSArray *) purposes;
1253 - (NSComparisonResult) compareByName:(Package *)package;
1254 - (NSComparisonResult) compareBySection:(Package *)package;
1256 - (uint32_t) compareForChanges;
1261 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1262 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1263 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1264 - (NSNumber *) isVisibleInSource:(Source *)source;
1268 @implementation Package
1274 if (section_ != nil)
1278 if (installed_ != nil)
1279 [installed_ release];
1287 if (depiction_ != nil)
1288 [depiction_ release];
1289 if (homepage_ != nil)
1290 [homepage_ release];
1291 if (sponsor_ != nil)
1300 if (relationships_ != nil)
1301 [relationships_ release];
1306 + (NSArray *) _attributeKeys {
1307 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1310 - (NSArray *) attributeKeys {
1311 return [[self class] _attributeKeys];
1314 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1315 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1318 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1319 if ((self = [super init]) != nil) {
1320 iterator_ = iterator;
1321 database_ = database;
1323 version_ = [database_ policy]->GetCandidateVer(iterator_);
1324 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1325 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1327 pkgCache::VerIterator current = iterator_.CurrentVer();
1328 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1329 installed_ = [StripVersion(installed) retain];
1331 if (!version_.end())
1332 file_ = version_.FileList();
1334 pkgCache &cache([database_ cache]);
1335 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1338 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1341 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1343 const char *begin, *end;
1344 parser->GetRec(begin, end);
1346 NSString *website(nil);
1347 NSString *sponsor(nil);
1348 NSString *author(nil);
1357 {"depiction", &depiction_},
1358 {"homepage", &homepage_},
1359 {"website", &website},
1360 {"sponsor", &sponsor},
1361 {"author", &author},
1365 while (begin != end)
1366 if (*begin == '\n') {
1369 } else if (isblank(*begin)) next: {
1370 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1373 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1374 const char *name(begin);
1375 size_t size(colon - begin);
1377 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1380 const char *stop(begin == NULL ? end : begin);
1381 while (stop[-1] == '\r')
1383 while (++colon != stop && isblank(*colon));
1385 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1386 if (strncasecmp(names[i].name_, name, size) == 0) {
1387 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1388 *names[i].value_ = value;
1399 name_ = [name_ retain];
1400 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1402 icon_ = [icon_ retain];
1403 if (depiction_ != nil)
1404 depiction_ = [depiction_ retain];
1405 if (homepage_ == nil)
1406 homepage_ = website;
1407 if ([homepage_ isEqualToString:depiction_])
1409 if (homepage_ != nil)
1410 homepage_ = [homepage_ retain];
1412 sponsor_ = [[Address addressWithString:sponsor] retain];
1414 author_ = [[Address addressWithString:author] retain];
1416 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1420 for (int i(0), e([tags_ count]); i != e; ++i) {
1421 NSString *tag = [tags_ objectAtIndex:i];
1422 if ([tag hasPrefix:@"role::"]) {
1423 role_ = [[tag substringFromIndex:6] retain];
1428 NSString *solid(latest == nil ? installed : latest);
1429 bool changed(false);
1431 NSString *key([id_ lowercaseString]);
1433 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1434 if (metadata == nil) {
1435 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1440 [metadata setObject:solid forKey:@"LastVersion"];
1443 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1444 NSDate *last([metadata objectForKey:@"LastSeen"]);
1445 NSString *version([metadata objectForKey:@"LastVersion"]);
1448 first = last == nil ? now_ : last;
1449 [metadata setObject:first forKey:@"FirstSeen"];
1454 if (version == nil) {
1455 [metadata setObject:solid forKey:@"LastVersion"];
1457 } else if (![version isEqualToString:solid]) {
1458 [metadata setObject:solid forKey:@"LastVersion"];
1460 [metadata setObject:last forKey:@"LastSeen"];
1466 [Packages_ setObject:metadata forKey:key];
1472 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1473 return [[[Package alloc]
1474 initWithIterator:iterator
1479 - (pkgCache::PkgIterator) iterator {
1483 - (NSString *) section {
1484 if (section_ != nil)
1487 const char *section = iterator_.Section();
1488 if (section == NULL)
1491 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1494 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1495 if (NSString *rename = [value objectForKey:@"Rename"]) {
1500 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1504 - (NSString *) simpleSection {
1505 if (NSString *section = [self section])
1506 return Simplify(section);
1512 - (Address *) maintainer {
1515 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1516 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1520 return version_.end() ? 0 : version_->InstalledSize;
1523 - (NSString *) description {
1526 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1527 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1529 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1530 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1531 if ([lines count] < 2)
1534 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1535 for (size_t i(1); i != [lines count]; ++i) {
1536 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1537 [trimmed addObject:trim];
1540 return [trimmed componentsJoinedByString:@"\n"];
1543 - (NSString *) index {
1544 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1545 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1548 - (NSMutableDictionary *) metadata {
1549 return [Packages_ objectForKey:[id_ lowercaseString]];
1553 NSDictionary *metadata([self metadata]);
1554 if ([self subscribed])
1555 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1557 return [metadata objectForKey:@"FirstSeen"];
1560 - (BOOL) subscribed {
1561 NSDictionary *metadata([self metadata]);
1562 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1563 return [subscribed boolValue];
1569 NSDictionary *metadata([self metadata]);
1570 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1571 return [ignored boolValue];
1576 - (NSString *) latest {
1580 - (NSString *) installed {
1585 return !version_.end();
1588 - (BOOL) upgradableAndEssential:(BOOL)essential {
1589 pkgCache::VerIterator current = iterator_.CurrentVer();
1592 return essential && [self essential];
1594 return !version_.end() && version_ != current;
1597 - (BOOL) essential {
1598 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1602 return [database_ cache][iterator_].InstBroken();
1605 - (BOOL) unfiltered {
1606 NSString *section = [self section];
1607 return section == nil || isSectionVisible(section);
1611 return [self hasSupportingRole] && [self unfiltered];
1615 unsigned char current = iterator_->CurrentState;
1616 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1619 - (BOOL) halfConfigured {
1620 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1623 - (BOOL) halfInstalled {
1624 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1628 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1629 return state.Mode != pkgDepCache::ModeKeep;
1632 - (NSString *) mode {
1633 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1635 switch (state.Mode) {
1636 case pkgDepCache::ModeDelete:
1637 if ((state.iFlags & pkgDepCache::Purge) != 0)
1641 case pkgDepCache::ModeKeep:
1642 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1646 case pkgDepCache::ModeInstall:
1647 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1648 return @"Reinstall";
1649 else switch (state.Status) {
1651 return @"Downgrade";
1657 return @"New Install";
1670 - (NSString *) name {
1671 return name_ == nil ? id_ : name_;
1674 - (NSString *) tagline {
1678 - (UIImage *) icon {
1679 NSString *section = [self simpleSection];
1682 if (NSString *icon = icon_)
1683 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1684 if (icon == nil) if (section != nil)
1685 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1686 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1687 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1689 icon = [UIImage applicationImageNamed:@"unknown.png"];
1693 - (NSString *) homepage {
1697 - (NSString *) depiction {
1701 - (Address *) sponsor {
1705 - (Address *) author {
1709 - (NSArray *) files {
1710 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1711 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1714 fin.open([path UTF8String]);
1719 while (std::getline(fin, line))
1720 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1725 - (NSArray *) relationships {
1726 return relationships_;
1729 - (NSArray *) warnings {
1730 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1731 const char *name(iterator_.Name());
1733 size_t length(strlen(name));
1734 if (length < 2) invalid:
1735 [warnings addObject:@"illegal package identifier"];
1736 else for (size_t i(0); i != length; ++i)
1738 (name[i] < 'a' || name[i] > 'z') &&
1739 (name[i] < '0' || name[i] > '9') &&
1740 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1743 if (strcmp(name, "cydia") != 0) {
1747 if (NSArray *files = [self files])
1748 for (NSString *file in files)
1749 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1751 else if (!stash && [file isEqualToString:@"/var/stash"])
1755 [warnings addObject:@"files installed into Cydia.app"];
1757 [warnings addObject:@"files installed to /var/stash"];
1760 return [warnings count] == 0 ? nil : warnings;
1763 - (NSArray *) applications {
1764 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1766 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1768 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1769 if (NSArray *files = [self files])
1770 for (NSString *file in files)
1771 if (application_r(file)) {
1772 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1773 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1774 if ([id isEqualToString:me])
1777 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1779 display = application_r[1];
1781 NSString *bundle([file stringByDeletingLastPathComponent]);
1782 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1783 if (icon == nil || [icon length] == 0)
1785 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1787 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1788 [applications addObject:application];
1790 [application addObject:id];
1791 [application addObject:display];
1792 [application addObject:url];
1795 return [applications count] == 0 ? nil : applications;
1798 - (Source *) source {
1800 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1807 - (NSString *) role {
1811 - (NSString *) rating {
1812 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1813 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1818 - (BOOL) matches:(NSString *)text {
1824 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1825 if (range.location != NSNotFound)
1828 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1829 if (range.location != NSNotFound)
1832 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1833 if (range.location != NSNotFound)
1839 - (bool) hasSupportingRole {
1842 if ([role_ isEqualToString:@"enduser"])
1844 if ([Role_ isEqualToString:@"User"])
1846 if ([role_ isEqualToString:@"hacker"])
1848 if ([Role_ isEqualToString:@"Hacker"])
1850 if ([role_ isEqualToString:@"developer"])
1852 if ([Role_ isEqualToString:@"Developer"])
1857 - (BOOL) hasTag:(NSString *)tag {
1858 return tags_ == nil ? NO : [tags_ containsObject:tag];
1861 - (NSString *) primaryPurpose {
1862 for (NSString *tag in tags_)
1863 if ([tag hasPrefix:@"purpose::"])
1864 return [tag substringFromIndex:9];
1868 - (NSArray *) purposes {
1869 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1870 for (NSString *tag in tags_)
1871 if ([tag hasPrefix:@"purpose::"])
1872 [purposes addObject:[tag substringFromIndex:9]];
1873 return [purposes count] == 0 ? nil : purposes;
1876 - (NSComparisonResult) compareByName:(Package *)package {
1877 NSString *lhs = [self name];
1878 NSString *rhs = [package name];
1880 if ([lhs length] != 0 && [rhs length] != 0) {
1881 unichar lhc = [lhs characterAtIndex:0];
1882 unichar rhc = [rhs characterAtIndex:0];
1884 if (isalpha(lhc) && !isalpha(rhc))
1885 return NSOrderedAscending;
1886 else if (!isalpha(lhc) && isalpha(rhc))
1887 return NSOrderedDescending;
1890 return [lhs compare:rhs options:LaxCompareOptions_];
1893 - (NSComparisonResult) compareBySection:(Package *)package {
1894 NSString *lhs = [self section];
1895 NSString *rhs = [package section];
1897 if (lhs == NULL && rhs != NULL)
1898 return NSOrderedAscending;
1899 else if (lhs != NULL && rhs == NULL)
1900 return NSOrderedDescending;
1901 else if (lhs != NULL && rhs != NULL) {
1902 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1903 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1906 return NSOrderedSame;
1909 - (uint32_t) compareForChanges {
1914 uint32_t timestamp : 30;
1915 uint32_t ignored : 1;
1916 uint32_t upgradable : 1;
1920 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1922 if ([self upgradableAndEssential:YES]) {
1923 value.bits.timestamp = 0;
1924 value.bits.ignored = [self ignored] ? 0 : 1;
1925 value.bits.upgradable = 1;
1927 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1928 value.bits.ignored = 0;
1929 value.bits.upgradable = 0;
1932 return _not(uint32_t) - value.key;
1936 pkgProblemResolver *resolver = [database_ resolver];
1937 resolver->Clear(iterator_);
1938 resolver->Protect(iterator_);
1939 pkgCacheFile &cache([database_ cache]);
1940 cache->MarkInstall(iterator_, false);
1941 pkgDepCache::StateCache &state((*cache)[iterator_]);
1942 if (!state.Install())
1943 cache->SetReInstall(iterator_, true);
1947 pkgProblemResolver *resolver = [database_ resolver];
1948 resolver->Clear(iterator_);
1949 resolver->Protect(iterator_);
1950 resolver->Remove(iterator_);
1951 [database_ cache]->MarkDelete(iterator_, true);
1954 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1955 return [NSNumber numberWithBool:(
1956 [self unfiltered] && [self matches:search]
1960 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1961 return [NSNumber numberWithBool:(
1962 (![number boolValue] || [self visible]) && [self installed] != nil
1966 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1967 NSString *section = [self section];
1969 return [NSNumber numberWithBool:(
1971 [self installed] == nil && (
1973 section == nil && [name length] == 0 ||
1974 [name isEqualToString:section]
1979 - (NSNumber *) isVisibleInSource:(Source *)source {
1980 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1985 /* Section Class {{{ */
1986 @interface Section : NSObject {
1992 - (NSComparisonResult) compareByName:(Section *)section;
1993 - (Section *) initWithName:(NSString *)name;
1994 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1995 - (NSString *) name;
1998 - (void) addToCount;
2002 @implementation Section
2009 - (NSComparisonResult) compareByName:(Section *)section {
2010 NSString *lhs = [self name];
2011 NSString *rhs = [section name];
2013 if ([lhs length] != 0 && [rhs length] != 0) {
2014 unichar lhc = [lhs characterAtIndex:0];
2015 unichar rhc = [rhs characterAtIndex:0];
2017 if (isalpha(lhc) && !isalpha(rhc))
2018 return NSOrderedAscending;
2019 else if (!isalpha(lhc) && isalpha(rhc))
2020 return NSOrderedDescending;
2023 return [lhs compare:rhs options:LaxCompareOptions_];
2026 - (Section *) initWithName:(NSString *)name {
2027 return [self initWithName:name row:0];
2030 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2031 if ((self = [super init]) != nil) {
2032 name_ = [name retain];
2037 - (NSString *) name {
2049 - (void) addToCount {
2057 static NSArray *Finishes_;
2059 /* Database Implementation {{{ */
2060 @implementation Database
2062 + (Database *) sharedInstance {
2063 static Database *instance;
2064 if (instance == nil)
2065 instance = [[Database alloc] init];
2074 - (void) _readCydia:(NSNumber *)fd { _pooled
2075 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2076 std::istream is(&ib);
2079 static Pcre finish_r("^finish:([^:]*)$");
2081 while (std::getline(is, line)) {
2082 const char *data(line.c_str());
2083 size_t size = line.size();
2084 lprintf("C:%s\n", data);
2086 if (finish_r(data, size)) {
2087 NSString *finish = finish_r[1];
2088 int index = [Finishes_ indexOfObject:finish];
2089 if (index != INT_MAX && index > Finish_)
2097 - (void) _readStatus:(NSNumber *)fd { _pooled
2098 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2099 std::istream is(&ib);
2102 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2103 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2105 while (std::getline(is, line)) {
2106 const char *data(line.c_str());
2107 size_t size = line.size();
2108 lprintf("S:%s\n", data);
2110 if (conffile_r(data, size)) {
2111 [delegate_ setConfigurationData:conffile_r[1]];
2112 } else if (strncmp(data, "status: ", 8) == 0) {
2113 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2114 [delegate_ setProgressTitle:string];
2115 } else if (pmstatus_r(data, size)) {
2116 std::string type([pmstatus_r[1] UTF8String]);
2117 NSString *id = pmstatus_r[2];
2119 float percent([pmstatus_r[3] floatValue]);
2120 [delegate_ setProgressPercent:(percent / 100)];
2122 NSString *string = pmstatus_r[4];
2124 if (type == "pmerror")
2125 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2126 withObject:[NSArray arrayWithObjects:string, id, nil]
2129 else if (type == "pmstatus") {
2130 [delegate_ setProgressTitle:string];
2131 } else if (type == "pmconffile")
2132 [delegate_ setConfigurationData:string];
2133 else _assert(false);
2134 } else _assert(false);
2140 - (void) _readOutput:(NSNumber *)fd { _pooled
2141 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2142 std::istream is(&ib);
2145 while (std::getline(is, line)) {
2146 lprintf("O:%s\n", line.c_str());
2147 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2157 - (Package *) packageWithName:(NSString *)name {
2158 if (static_cast<pkgDepCache *>(cache_) == NULL)
2160 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2161 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2164 - (Database *) init {
2165 if ((self = [super init]) != nil) {
2172 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2173 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2177 _assert(pipe(fds) != -1);
2180 _config->Set("APT::Keep-Fds::", cydiafd_);
2181 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2184 detachNewThreadSelector:@selector(_readCydia:)
2186 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2189 _assert(pipe(fds) != -1);
2193 detachNewThreadSelector:@selector(_readStatus:)
2195 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2198 _assert(pipe(fds) != -1);
2199 _assert(dup2(fds[0], 0) != -1);
2200 _assert(close(fds[0]) != -1);
2202 input_ = fdopen(fds[1], "a");
2204 _assert(pipe(fds) != -1);
2205 _assert(dup2(fds[1], 1) != -1);
2206 _assert(close(fds[1]) != -1);
2209 detachNewThreadSelector:@selector(_readOutput:)
2211 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2216 - (pkgCacheFile &) cache {
2220 - (pkgDepCache::Policy *) policy {
2224 - (pkgRecords *) records {
2228 - (pkgProblemResolver *) resolver {
2232 - (pkgAcquire &) fetcher {
2236 - (NSArray *) packages {
2240 - (NSArray *) sources {
2241 return [sources_ allValues];
2244 - (NSArray *) issues {
2245 if (cache_->BrokenCount() == 0)
2248 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2250 for (Package *package in packages_) {
2251 if (![package broken])
2253 pkgCache::PkgIterator pkg([package iterator]);
2255 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2256 [entry addObject:[package name]];
2257 [issues addObject:entry];
2259 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2263 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2264 pkgCache::DepIterator start;
2265 pkgCache::DepIterator end;
2266 dep.GlobOr(start, end); // ++dep
2268 if (!cache_->IsImportantDep(end))
2270 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2273 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2274 [entry addObject:failure];
2275 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2277 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2278 [failure addObject:[package name]];
2280 pkgCache::PkgIterator target(start.TargetPkg());
2281 if (target->ProvidesList != 0)
2282 [failure addObject:@"?"];
2284 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2286 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2287 else if (!cache_[target].CandidateVerIter(cache_).end())
2288 [failure addObject:@"-"];
2289 else if (target->ProvidesList == 0)
2290 [failure addObject:@"!"];
2292 [failure addObject:@"%"];
2296 if (start.TargetVer() != 0)
2297 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2308 - (void) reloadData {
2328 if (!cache_.Open(progress_, true)) {
2330 if (!_error->PopMessage(error))
2333 lprintf("cache_.Open():[%s]\n", error.c_str());
2335 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2336 [delegate_ repairWithSelector:@selector(configure)];
2337 else if (error == "The package lists or status file could not be parsed or opened.")
2338 [delegate_ repairWithSelector:@selector(update)];
2339 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2340 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2341 // else if (error == "The list of sources could not be read.")
2342 else _assert(false);
2348 now_ = [[NSDate date] retain];
2350 policy_ = new pkgDepCache::Policy();
2351 records_ = new pkgRecords(cache_);
2352 resolver_ = new pkgProblemResolver(cache_);
2353 fetcher_ = new pkgAcquire(&status_);
2356 list_ = new pkgSourceList();
2357 _assert(list_->ReadMainList());
2359 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2360 _assert(pkgApplyStatus(cache_));
2362 if (cache_->BrokenCount() != 0) {
2363 _assert(pkgFixBroken(cache_));
2364 _assert(cache_->BrokenCount() == 0);
2365 _assert(pkgMinimizeUpgrade(cache_));
2368 [sources_ removeAllObjects];
2369 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2370 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2371 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2373 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2374 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2378 [packages_ removeAllObjects];
2381 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2382 if (Package *package = [Package packageWithIterator:iterator database:self])
2383 [packages_ addObject:package];
2385 [packages_ sortUsingSelector:@selector(compareByName:)];
2389 - (void) configure {
2390 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2391 system([dpkg UTF8String]);
2399 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2400 _assert(!_error->PendingError());
2403 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2406 public pkgArchiveCleaner
2409 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2414 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2416 while (_error->PopMessage(error))
2417 lprintf("ArchiveCleaner: %s\n", error.c_str());
2422 pkgRecords records(cache_);
2424 lock_ = new FileFd();
2425 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2426 _assert(!_error->PendingError());
2429 // XXX: explain this with an error message
2430 _assert(list.ReadMainList());
2432 manager_ = (_system->CreatePM(cache_));
2433 _assert(manager_->GetArchives(fetcher_, &list, &records));
2434 _assert(!_error->PendingError());
2438 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2440 _assert(list.ReadMainList());
2441 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2442 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2445 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2450 bool failed = false;
2451 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2452 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2455 std::string uri = (*item)->DescURI();
2456 std::string error = (*item)->ErrorText;
2458 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2461 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2462 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2473 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2475 if (_error->PendingError()) {
2480 if (result == pkgPackageManager::Failed) {
2485 if (result != pkgPackageManager::Completed) {
2490 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2492 _assert(list.ReadMainList());
2493 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2494 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2497 if (![before isEqualToArray:after])
2502 _assert(pkgDistUpgrade(cache_));
2506 [self updateWithStatus:status_];
2509 - (void) updateWithStatus:(Status &)status {
2511 _assert(list.ReadMainList());
2514 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2515 _assert(!_error->PendingError());
2517 pkgAcquire fetcher(&status);
2518 _assert(list.GetIndexes(&fetcher));
2520 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2521 bool failed = false;
2522 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2523 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2524 (*item)->Finished();
2528 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2529 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2530 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2533 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2538 - (void) setDelegate:(id)delegate {
2539 delegate_ = delegate;
2540 status_.setDelegate(delegate);
2541 progress_.setDelegate(delegate);
2544 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2545 pkgIndexFile *index(NULL);
2546 list_->FindIndex(file, index);
2547 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2553 /* PopUp Windows {{{ */
2554 @interface PopUpView : UIView {
2555 _transient id delegate_;
2556 UITransitionView *transition_;
2561 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2565 @implementation PopUpView
2568 [transition_ setDelegate:nil];
2569 [transition_ release];
2575 [transition_ transition:UITransitionPushFromTop toView:nil];
2578 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2579 if (from != nil && to == nil)
2580 [self removeFromSuperview];
2583 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2584 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2585 delegate_ = delegate;
2587 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2588 [self addSubview:transition_];
2590 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2592 [view addSubview:self];
2594 [transition_ setDelegate:self];
2596 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2597 [transition_ transition:UITransitionNone toView:blank];
2598 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2605 /* Mail Composition {{{ */
2606 @interface MailToView : PopUpView {
2607 MailComposeController *controller_;
2610 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2614 @implementation MailToView
2617 [controller_ release];
2621 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2625 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2626 NSLog(@"did:%@", delivery);
2627 // [UIApp setStatusBarShowsProgress:NO];
2628 if ([controller error]){
2629 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2630 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2631 [mailAlertSheet setBodyText:[controller error]];
2632 [mailAlertSheet popupAlertAnimated:YES];
2636 - (void) showError {
2637 NSLog(@"%@", [controller_ error]);
2638 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2639 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2640 [mailAlertSheet setBodyText:[controller_ error]];
2641 [mailAlertSheet popupAlertAnimated:YES];
2644 - (void) deliverMessage { _pooled
2648 if (![controller_ deliverMessage])
2649 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2652 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2653 if ([controller_ needsDelivery])
2654 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2659 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2660 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2661 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2662 [controller_ setDelegate:self];
2663 [controller_ initializeUI];
2664 [controller_ setupForURL:url];
2666 UIView *view([controller_ view]);
2667 [overlay_ addSubview:view];
2673 /* Confirmation View {{{ */
2674 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2675 if (!iterator.end())
2676 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2677 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2679 pkgCache::PkgIterator package(dep.TargetPkg());
2682 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2689 @protocol ConfirmationViewDelegate
2694 @interface ConfirmationView : BrowserView {
2695 _transient Database *database_;
2696 UIActionSheet *essential_;
2703 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2707 @implementation ConfirmationView
2714 if (essential_ != nil)
2715 [essential_ release];
2721 [book_ popFromSuperviewAnimated:YES];
2724 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2725 NSString *context([sheet context]);
2727 if ([context isEqualToString:@"remove"]) {
2735 [delegate_ confirm];
2742 } else if ([context isEqualToString:@"unable"]) {
2746 [super alertSheet:sheet buttonClicked:button];
2749 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2750 [window setValue:changes_ forKey:@"changes"];
2751 [window setValue:issues_ forKey:@"issues"];
2752 [window setValue:sizes_ forKey:@"sizes"];
2753 [super webView:sender didClearWindowObject:window forFrame:frame];
2756 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2757 if ((self = [super initWithBook:book]) != nil) {
2758 database_ = database;
2760 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2761 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2762 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2763 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2764 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2768 pkgDepCache::Policy *policy([database_ policy]);
2770 pkgCacheFile &cache([database_ cache]);
2771 NSArray *packages = [database_ packages];
2772 for (size_t i(0), e = [packages count]; i != e; ++i) {
2773 Package *package = [packages objectAtIndex:i];
2774 pkgCache::PkgIterator iterator = [package iterator];
2775 pkgDepCache::StateCache &state(cache[iterator]);
2777 NSString *name([package name]);
2779 if (state.NewInstall())
2780 [installing addObject:name];
2781 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2782 [reinstalling addObject:name];
2783 else if (state.Upgrade())
2784 [upgrading addObject:name];
2785 else if (state.Downgrade())
2786 [downgrading addObject:name];
2787 else if (state.Delete()) {
2788 if ([package essential])
2790 [removing addObject:name];
2793 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2794 substrate_ |= DepSubstrate(iterator.CurrentVer());
2799 else if (Advanced_ || true) {
2800 essential_ = [[UIActionSheet alloc]
2801 initWithTitle:@"Removing Essentials"
2802 buttons:[NSArray arrayWithObjects:
2803 @"Cancel Operation (Safe)",
2804 @"Force Removal (Unsafe)",
2806 defaultButtonIndex:0
2812 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2814 [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."];
2816 essential_ = [[UIActionSheet alloc]
2817 initWithTitle:@"Unable to Comply"
2818 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2819 defaultButtonIndex:0
2824 [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."];
2827 changes_ = [[NSArray alloc] initWithObjects:
2835 issues_ = [database_ issues];
2837 issues_ = [issues_ retain];
2839 sizes_ = [[NSArray alloc] initWithObjects:
2840 SizeString([database_ fetcher].FetchNeeded()),
2841 SizeString([database_ fetcher].PartialPresent()),
2842 SizeString([database_ cache]->UsrSize()),
2845 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2849 - (NSString *) backButtonTitle {
2853 - (NSString *) leftButtonTitle {
2857 - (id) _rightButtonTitle {
2858 #if AlwaysReload || IgnoreInstall
2861 return issues_ == nil ? @"Confirm" : nil;
2865 - (void) _leftButtonClicked {
2870 - (void) _rightButtonClicked {
2872 return [super _rightButtonClicked];
2874 if (essential_ != nil)
2875 [essential_ popupAlertAnimated:YES];
2879 [delegate_ confirm];
2887 /* Progress Data {{{ */
2888 @interface ProgressData : NSObject {
2894 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2901 @implementation ProgressData
2903 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2904 if ((self = [super init]) != nil) {
2905 selector_ = selector;
2925 /* Progress View {{{ */
2926 @interface ProgressView : UIView <
2927 ConfigurationDelegate,
2930 _transient Database *database_;
2932 UIView *background_;
2933 UITransitionView *transition_;
2935 UINavigationBar *navbar_;
2936 UIProgressBar *progress_;
2937 UITextView *output_;
2938 UITextLabel *status_;
2939 UIPushButton *close_;
2942 SHA1SumValue springlist_;
2943 SHA1SumValue notifyconf_;
2944 SHA1SumValue sandplate_;
2946 NSTimeInterval last_;
2949 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2951 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2952 - (void) setContentView:(UIView *)view;
2955 - (void) _retachThread;
2956 - (void) _detachNewThreadData:(ProgressData *)data;
2957 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2963 @protocol ProgressViewDelegate
2964 - (void) progressViewIsComplete:(ProgressView *)sender;
2967 @implementation ProgressView
2970 [transition_ setDelegate:nil];
2971 [navbar_ setDelegate:nil];
2974 if (background_ != nil)
2975 [background_ release];
2976 [transition_ release];
2979 [progress_ release];
2986 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2987 if (bootstrap_ && from == overlay_ && to == view_)
2991 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2992 if ((self = [super initWithFrame:frame]) != nil) {
2993 database_ = database;
2994 delegate_ = delegate;
2996 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2997 [transition_ setDelegate:self];
2999 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3002 [overlay_ setBackgroundColor:[UIColor blackColor]];
3004 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3005 [background_ setBackgroundColor:[UIColor blackColor]];
3006 [self addSubview:background_];
3009 [self addSubview:transition_];
3011 CGSize navsize = [UINavigationBar defaultSize];
3012 CGRect navrect = {{0, 0}, navsize};
3014 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3015 [overlay_ addSubview:navbar_];
3017 [navbar_ setBarStyle:1];
3018 [navbar_ setDelegate:self];
3020 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3021 [navbar_ pushNavigationItem:navitem];
3023 CGRect bounds = [overlay_ bounds];
3024 CGSize prgsize = [UIProgressBar defaultSize];
3027 (bounds.size.width - prgsize.width) / 2,
3028 bounds.size.height - prgsize.height - 20
3031 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3032 [progress_ setStyle:0];
3034 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3036 bounds.size.height - prgsize.height - 50,
3037 bounds.size.width - 20,
3041 [status_ setColor:[UIColor whiteColor]];
3042 [status_ setBackgroundColor:[UIColor clearColor]];
3044 [status_ setCentersHorizontally:YES];
3045 //[status_ setFont:font];
3047 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3049 navrect.size.height + 20,
3050 bounds.size.width - 20,
3051 bounds.size.height - navsize.height - 62 - navrect.size.height
3054 //[output_ setTextFont:@"Courier New"];
3055 [output_ setTextSize:12];
3057 [output_ setTextColor:[UIColor whiteColor]];
3058 [output_ setBackgroundColor:[UIColor clearColor]];
3060 [output_ setMarginTop:0];
3061 [output_ setAllowsRubberBanding:YES];
3062 [output_ setEditable:NO];
3064 [overlay_ addSubview:output_];
3066 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3068 bounds.size.height - prgsize.height - 50,
3069 bounds.size.width - 20,
3073 [close_ setAutosizesToFit:NO];
3074 [close_ setDrawsShadow:YES];
3075 [close_ setStretchBackground:YES];
3076 [close_ setEnabled:YES];
3078 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3079 [close_ setTitleFont:bold];
3081 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3082 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3083 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3087 - (void) setContentView:(UIView *)view {
3088 view_ = [view retain];
3091 - (void) resetView {
3092 [transition_ transition:6 toView:view_];
3095 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3096 NSString *context([sheet context]);
3098 if ([context isEqualToString:@"conffile"]) {
3099 FILE *input = [database_ input];
3103 fprintf(input, "N\n");
3107 fprintf(input, "Y\n");
3118 - (void) closeButtonPushed {
3127 [delegate_ suspendWithAnimation:YES];
3131 system("launchctl stop com.apple.SpringBoard");
3135 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3144 - (void) _retachThread {
3145 UINavigationItem *item = [navbar_ topItem];
3146 [item setTitle:@"Complete"];
3148 [overlay_ addSubview:close_];
3149 [progress_ removeFromSuperview];
3150 [status_ removeFromSuperview];
3152 [delegate_ progressViewIsComplete:self];
3155 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3156 MMap mmap(file, MMap::ReadOnly);
3158 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3159 if (!(sandplate_ == sha1.Result()))
3164 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3165 MMap mmap(file, MMap::ReadOnly);
3167 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3168 if (!(notifyconf_ == sha1.Result()))
3173 FileFd file(SpringBoard_, FileFd::ReadOnly);
3174 MMap mmap(file, MMap::ReadOnly);
3176 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3177 if (!(springlist_ == sha1.Result()))
3182 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3183 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3184 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3185 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3186 case 4: [close_ setTitle:@"Reboot Device"]; break;
3189 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3191 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3192 [cache autorelease];
3194 NSFileManager *manager = [NSFileManager defaultManager];
3195 NSError *error = nil;
3197 id system = [cache objectForKey:@"System"];
3202 if (stat(Cache_, &info) == -1)
3205 [system removeAllObjects];
3207 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3208 for (NSString *app in apps)
3209 if ([app hasSuffix:@".app"]) {
3210 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3211 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3212 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3214 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3215 [info setObject:path forKey:@"Path"];
3216 [info setObject:@"System" forKey:@"ApplicationType"];
3217 [system addInfoDictionary:info];
3223 [cache writeToFile:@Cache_ atomically:YES];
3225 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3227 if (chmod(Cache_, info.st_mode) == -1)
3231 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3234 notify_post("com.apple.mobile.application_installed");
3236 [delegate_ setStatusBarShowsProgress:NO];
3239 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3240 [[data target] performSelector:[data selector] withObject:[data object]];
3243 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3246 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3247 UINavigationItem *item = [navbar_ topItem];
3248 [item setTitle:title];
3250 [status_ setText:nil];
3251 [output_ setText:@""];
3252 [progress_ setProgress:0];
3255 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3257 [close_ removeFromSuperview];
3258 [overlay_ addSubview:progress_];
3259 [overlay_ addSubview:status_];
3261 [delegate_ setStatusBarShowsProgress:YES];
3265 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3266 MMap mmap(file, MMap::ReadOnly);
3268 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3269 sandplate_ = sha1.Result();
3273 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3274 MMap mmap(file, MMap::ReadOnly);
3276 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3277 notifyconf_ = sha1.Result();
3281 FileFd file(SpringBoard_, FileFd::ReadOnly);
3282 MMap mmap(file, MMap::ReadOnly);
3284 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3285 springlist_ = sha1.Result();
3288 [transition_ transition:6 toView:overlay_];
3291 detachNewThreadSelector:@selector(_detachNewThreadData:)
3293 withObject:[[ProgressData alloc]
3294 initWithSelector:selector
3301 - (void) repairWithSelector:(SEL)selector {
3303 detachNewThreadSelector:selector
3310 - (void) setConfigurationData:(NSString *)data {
3312 performSelectorOnMainThread:@selector(_setConfigurationData:)
3318 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3319 Package *package = id == nil ? nil : [database_ packageWithName:id];
3321 UIActionSheet *sheet = [[[UIActionSheet alloc]
3322 initWithTitle:(package == nil ? id : [package name])
3323 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3324 defaultButtonIndex:0
3329 [sheet setBodyText:error];
3330 [sheet popupAlertAnimated:YES];
3333 - (void) setProgressTitle:(NSString *)title {
3335 performSelectorOnMainThread:@selector(_setProgressTitle:)
3341 - (void) setProgressPercent:(float)percent {
3343 performSelectorOnMainThread:@selector(_setProgressPercent:)
3344 withObject:[NSNumber numberWithFloat:percent]
3349 - (void) startProgress {
3350 last_ = [NSDate timeIntervalSinceReferenceDate];
3353 - (void) addProgressOutput:(NSString *)output {
3355 performSelectorOnMainThread:@selector(_addProgressOutput:)
3361 - (bool) isCancelling:(size_t)received {
3363 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3364 if (received_ != received) {
3365 received_ = received;
3367 } else if (now - last_ > 30)
3374 - (void) _setConfigurationData:(NSString *)data {
3375 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3377 _assert(conffile_r(data));
3379 NSString *ofile = conffile_r[1];
3380 //NSString *nfile = conffile_r[2];
3382 UIActionSheet *sheet = [[[UIActionSheet alloc]
3383 initWithTitle:@"Configuration Upgrade"
3384 buttons:[NSArray arrayWithObjects:
3385 @"Keep My Old Copy",
3386 @"Accept The New Copy",
3387 // XXX: @"See What Changed",
3389 defaultButtonIndex:0
3394 [sheet setBodyText:[NSString stringWithFormat:
3395 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3398 [sheet popupAlertAnimated:YES];
3401 - (void) _setProgressTitle:(NSString *)title {
3402 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3403 for (size_t i(0), e([words count]); i != e; ++i) {
3404 NSString *word([words objectAtIndex:i]);
3405 if (Package *package = [database_ packageWithName:word])
3406 [words replaceObjectAtIndex:i withObject:[package name]];
3409 [status_ setText:[words componentsJoinedByString:@" "]];
3412 - (void) _setProgressPercent:(NSNumber *)percent {
3413 [progress_ setProgress:[percent floatValue]];
3416 - (void) _addProgressOutput:(NSString *)output {
3417 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3418 CGSize size = [output_ contentSize];
3419 CGRect rect = {{0, size.height}, {size.width, 0}};
3420 [output_ scrollRectToVisible:rect animated:YES];
3423 - (BOOL) isRunning {
3430 /* Package Cell {{{ */
3431 @interface PackageCell : UISimpleTableCell {
3434 NSString *description_;
3438 UITextLabel *status_;
3442 - (PackageCell *) init;
3443 - (void) setPackage:(Package *)package;
3445 + (int) heightForPackage:(Package *)package;
3449 @implementation PackageCell
3451 - (void) clearPackage {
3462 if (description_ != nil) {
3463 [description_ release];
3467 if (source_ != nil) {
3472 if (badge_ != nil) {
3479 [self clearPackage];
3486 - (PackageCell *) init {
3487 if ((self = [super init]) != nil) {
3489 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3490 [status_ setBackgroundColor:[UIColor clearColor]];
3491 [status_ setFont:small];
3496 - (void) setPackage:(Package *)package {
3497 [self clearPackage];
3499 Source *source = [package source];
3500 NSString *section = [package simpleSection];
3502 icon_ = [[package icon] retain];
3504 name_ = [[package name] retain];
3505 description_ = [[package tagline] retain];
3507 NSString *label = nil;
3508 bool trusted = false;
3510 if (source != nil) {
3511 label = [source label];
3512 trusted = [source trusted];
3513 } else if ([[package id] isEqualToString:@"firmware"])
3516 label = @"Unknown/Local";
3518 NSString *from = [NSString stringWithFormat:@"from %@", label];
3520 if (section != nil && ![section isEqualToString:label])
3521 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3523 source_ = [from retain];
3525 if (NSString *purpose = [package primaryPurpose])
3526 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3527 badge_ = [badge_ retain];
3530 if (NSString *mode = [package mode]) {
3531 [badge_ setImage:[UIImage applicationImageNamed:
3532 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3535 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3536 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3537 } else if ([package half]) {
3538 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3539 [status_ setText:@"Package Damaged"];
3540 [status_ setColor:[UIColor redColor]];
3542 [badge_ setImage:nil];
3543 [status_ setText:nil];
3548 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3551 rect.size = [icon_ size];
3553 rect.size.width /= 2;
3554 rect.size.height /= 2;
3556 rect.origin.x = 25 - rect.size.width / 2;
3557 rect.origin.y = 25 - rect.size.height / 2;
3559 [icon_ drawInRect:rect];
3562 if (badge_ != nil) {
3563 CGSize size = [badge_ size];
3565 [badge_ drawAtPoint:CGPointMake(
3566 36 - size.width / 2,
3567 36 - size.height / 2
3576 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3577 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3581 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3583 [super drawContentInRect:rect selected:selected];
3586 + (int) heightForPackage:(Package *)package {
3587 NSString *tagline([package tagline]);
3588 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3590 if ([package hasMode] || [package half])
3599 /* Section Cell {{{ */
3600 @interface SectionCell : UISimpleTableCell {
3605 _UISwitchSlider *switch_;
3610 - (void) setSection:(Section *)section editing:(BOOL)editing;
3614 @implementation SectionCell
3616 - (void) clearSection {
3617 if (section_ != nil) {
3627 if (count_ != nil) {
3634 [self clearSection];
3641 if ((self = [super init]) != nil) {
3642 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3644 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3645 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3649 - (void) onSwitch:(id)sender {
3650 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3651 if (metadata == nil) {
3652 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3653 [Sections_ setObject:metadata forKey:section_];
3657 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3660 - (void) setSection:(Section *)section editing:(BOOL)editing {
3661 if (editing != editing_) {
3663 [switch_ removeFromSuperview];
3665 [self addSubview:switch_];
3669 [self clearSection];
3671 if (section == nil) {
3672 name_ = [@"All Packages" retain];
3675 section_ = [section name];
3676 if (section_ != nil)
3677 section_ = [section_ retain];
3678 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3679 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3682 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3686 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3687 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3694 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3696 CGSize size = [count_ sizeWithFont:Font14_];
3700 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3702 [super drawContentInRect:rect selected:selected];
3708 /* File Table {{{ */
3709 @interface FileTable : RVPage {
3710 _transient Database *database_;
3713 NSMutableArray *files_;
3717 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3718 - (void) setPackage:(Package *)package;
3722 @implementation FileTable
3725 if (package_ != nil)
3734 - (int) numberOfRowsInTable:(UITable *)table {
3735 return files_ == nil ? 0 : [files_ count];
3738 - (float) table:(UITable *)table heightForRow:(int)row {
3742 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3743 if (reusing == nil) {
3744 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3745 UIFont *font = [UIFont systemFontOfSize:16];
3746 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3748 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3752 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3756 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3757 if ((self = [super initWithBook:book]) != nil) {
3758 database_ = database;
3760 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3762 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3763 [self addSubview:list_];
3765 UITableColumn *column = [[[UITableColumn alloc]
3766 initWithTitle:@"Name"
3768 width:[self frame].size.width
3771 [list_ setDataSource:self];
3772 [list_ setSeparatorStyle:1];
3773 [list_ addTableColumn:column];
3774 [list_ setDelegate:self];
3775 [list_ setReusesTableCells:YES];
3779 - (void) setPackage:(Package *)package {
3780 if (package_ != nil) {
3781 [package_ autorelease];
3790 [files_ removeAllObjects];
3792 if (package != nil) {
3793 package_ = [package retain];
3794 name_ = [[package id] retain];
3796 if (NSArray *files = [package files])
3797 [files_ addObjectsFromArray:files];
3799 if ([files_ count] != 0) {
3800 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3801 [files_ removeObjectAtIndex:0];
3802 [files_ sortUsingSelector:@selector(compareByPath:)];
3804 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3805 [stack addObject:@"/"];
3807 for (int i(0), e([files_ count]); i != e; ++i) {
3808 NSString *file = [files_ objectAtIndex:i];
3809 while (![file hasPrefix:[stack lastObject]])
3810 [stack removeLastObject];
3811 NSString *directory = [stack lastObject];
3812 [stack addObject:[file stringByAppendingString:@"/"]];
3813 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3814 ([stack count] - 2) * 3, "",
3815 [file substringFromIndex:[directory length]]
3824 - (void) resetViewAnimated:(BOOL)animated {
3825 [list_ resetViewAnimated:animated];
3828 - (void) reloadData {
3829 [self setPackage:[database_ packageWithName:name_]];
3830 [self reloadButtons];
3833 - (NSString *) title {
3834 return @"Installed Files";
3837 - (NSString *) backButtonTitle {
3843 /* Package View {{{ */
3844 @interface PackageView : BrowserView {
3845 _transient Database *database_;
3848 NSMutableArray *buttons_;
3851 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3852 - (void) setPackage:(Package *)package;
3856 @implementation PackageView
3859 if (package_ != nil)
3867 - (void) _clickButtonWithName:(NSString *)name {
3868 if ([name isEqualToString:@"Install"])
3869 [delegate_ installPackage:package_];
3870 else if ([name isEqualToString:@"Reinstall"])
3871 [delegate_ installPackage:package_];
3872 else if ([name isEqualToString:@"Remove"])
3873 [delegate_ removePackage:package_];
3874 else if ([name isEqualToString:@"Upgrade"])
3875 [delegate_ installPackage:package_];
3876 else _assert(false);
3879 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3880 NSString *context([sheet context]);
3882 if ([context isEqualToString:@"modify"]) {
3883 int count = [buttons_ count];
3884 _assert(count != 0);
3885 _assert(button <= count + 1);
3887 if (count != button - 1)
3888 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3892 [super alertSheet:sheet buttonClicked:button];
3895 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3896 return [super webView:sender didFinishLoadForFrame:frame];
3899 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3900 [window setValue:package_ forKey:@"package"];
3901 [super webView:sender didClearWindowObject:window forFrame:frame];
3905 - (void) _rightButtonClicked {
3906 /*[super _rightButtonClicked];
3909 int count = [buttons_ count];
3910 _assert(count != 0);
3913 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3915 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3916 [buttons addObjectsFromArray:buttons_];
3917 [buttons addObject:@"Cancel"];
3919 [delegate_ slideUp:[[[UIActionSheet alloc]
3922 defaultButtonIndex:2
3930 - (id) _rightButtonTitle {
3931 int count = [buttons_ count];
3932 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3935 - (NSString *) backButtonTitle {
3939 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3940 if ((self = [super initWithBook:book]) != nil) {
3941 database_ = database;
3942 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3946 - (void) setPackage:(Package *)package {
3947 if (package_ != nil) {
3948 [package_ autorelease];
3957 [buttons_ removeAllObjects];
3959 if (package != nil) {
3960 package_ = [package retain];
3961 name_ = [[package id] retain];
3963 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3965 if ([package_ source] == nil);
3966 else if ([package_ upgradableAndEssential:NO])
3967 [buttons_ addObject:@"Upgrade"];
3968 else if ([package_ installed] == nil)
3969 [buttons_ addObject:@"Install"];
3971 [buttons_ addObject:@"Reinstall"];
3972 if ([package_ installed] != nil)
3973 [buttons_ addObject:@"Remove"];
3981 - (void) reloadData {
3982 [self setPackage:[database_ packageWithName:name_]];
3983 [self reloadButtons];
3988 /* Package Table {{{ */
3989 @interface PackageTable : RVPage {
3990 _transient Database *database_;
3994 NSMutableArray *packages_;
3995 NSMutableArray *sections_;
3996 UISectionList *list_;
3999 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4001 - (void) setDelegate:(id)delegate;
4002 - (void) setObject:(id)object;
4004 - (void) reloadData;
4005 - (void) resetCursor;
4007 - (UISectionList *) list;
4009 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4013 @implementation PackageTable
4016 [list_ setDataSource:nil];
4021 [packages_ release];
4022 [sections_ release];
4027 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4028 return [sections_ count];
4031 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4032 return [[sections_ objectAtIndex:section] name];
4035 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4036 return [[sections_ objectAtIndex:section] row];
4039 - (int) numberOfRowsInTable:(UITable *)table {
4040 return [packages_ count];
4043 - (float) table:(UITable *)table heightForRow:(int)row {
4044 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4047 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4049 reusing = [[[PackageCell alloc] init] autorelease];
4050 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4054 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4058 - (void) tableRowSelected:(NSNotification *)notification {
4059 int row = [[notification object] selectedRow];
4063 Package *package = [packages_ objectAtIndex:row];
4064 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4065 [view setDelegate:delegate_];
4066 [view setPackage:package];
4067 [book_ pushPage:view];
4070 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4071 if ((self = [super initWithBook:book]) != nil) {
4072 database_ = database;
4073 title_ = [title retain];
4075 object_ = object == nil ? nil : [object retain];
4077 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4078 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4080 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4081 [list_ setDataSource:self];
4083 UITableColumn *column = [[[UITableColumn alloc]
4084 initWithTitle:@"Name"
4086 width:[self frame].size.width
4089 UITable *table = [list_ table];
4090 [table setSeparatorStyle:1];
4091 [table addTableColumn:column];
4092 [table setDelegate:self];
4093 [table setReusesTableCells:YES];
4095 [self addSubview:list_];
4098 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4099 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4103 - (void) setDelegate:(id)delegate {
4104 delegate_ = delegate;
4107 - (void) setObject:(id)object {
4113 object_ = [object retain];
4116 - (void) reloadData {
4117 NSArray *packages = [database_ packages];
4119 [packages_ removeAllObjects];
4120 [sections_ removeAllObjects];
4122 for (size_t i(0); i != [packages count]; ++i) {
4123 Package *package([packages objectAtIndex:i]);
4124 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4125 [packages_ addObject:package];
4128 Section *section = nil;
4130 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4131 Package *package = [packages_ objectAtIndex:offset];
4132 NSString *name = [package index];
4134 if (section == nil || ![[section name] isEqualToString:name]) {
4135 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4136 [sections_ addObject:section];
4139 [section addToCount];
4145 - (NSString *) title {
4149 - (void) resetViewAnimated:(BOOL)animated {
4150 [list_ resetViewAnimated:animated];
4153 - (void) resetCursor {
4154 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4157 - (UISectionList *) list {
4161 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4162 [list_ setShouldHideHeaderInShortLists:hide];
4168 /* Add Source View {{{ */
4169 @interface AddSourceView : RVPage {
4170 _transient Database *database_;
4173 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4177 @implementation AddSourceView
4179 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4180 if ((self = [super initWithBook:book]) != nil) {
4181 database_ = database;
4187 /* Source Cell {{{ */
4188 @interface SourceCell : UITableCell {
4191 NSString *description_;
4197 - (SourceCell *) initWithSource:(Source *)source;
4201 @implementation SourceCell
4206 [description_ release];
4211 - (SourceCell *) initWithSource:(Source *)source {
4212 if ((self = [super init]) != nil) {
4214 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4216 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4217 icon_ = [icon_ retain];
4219 origin_ = [[source name] retain];
4220 label_ = [[source uri] retain];
4221 description_ = [[source description] retain];
4225 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4227 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4234 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4238 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4242 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4244 [super drawContentInRect:rect selected:selected];
4249 /* Source Table {{{ */
4250 @interface SourceTable : RVPage {
4251 _transient Database *database_;
4252 UISectionList *list_;
4253 NSMutableArray *sources_;
4254 UIActionSheet *alert_;
4258 UIProgressHUD *hud_;
4261 //NSURLConnection *installer_;
4262 NSURLConnection *trivial_bz2_;
4263 NSURLConnection *trivial_gz_;
4264 //NSURLConnection *automatic_;
4269 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4273 @implementation SourceTable
4275 - (void) _deallocConnection:(NSURLConnection *)connection {
4276 if (connection != nil) {
4277 [connection cancel];
4278 //[connection setDelegate:nil];
4279 [connection release];
4284 [[list_ table] setDelegate:nil];
4285 [list_ setDataSource:nil];
4294 //[self _deallocConnection:installer_];
4295 [self _deallocConnection:trivial_gz_];
4296 [self _deallocConnection:trivial_bz2_];
4297 //[self _deallocConnection:automatic_];
4304 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4305 return offset_ == 0 ? 1 : 2;
4308 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4309 switch (section + (offset_ == 0 ? 1 : 0)) {
4310 case 0: return @"Entered by User";
4311 case 1: return @"Installed by Packages";
4319 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4320 switch (section + (offset_ == 0 ? 1 : 0)) {
4322 case 1: return offset_;
4330 - (int) numberOfRowsInTable:(UITable *)table {
4331 return [sources_ count];
4334 - (float) table:(UITable *)table heightForRow:(int)row {
4335 Source *source = [sources_ objectAtIndex:row];
4336 return [source description] == nil ? 56 : 73;
4339 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4340 Source *source = [sources_ objectAtIndex:row];
4341 // XXX: weird warning, stupid selectors ;P
4342 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4345 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4349 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4353 - (void) tableRowSelected:(NSNotification*)notification {
4354 UITable *table([list_ table]);
4355 int row([table selectedRow]);
4359 Source *source = [sources_ objectAtIndex:row];
4361 PackageTable *packages = [[[PackageTable alloc]
4364 title:[source label]
4365 filter:@selector(isVisibleInSource:)
4369 [packages setDelegate:delegate_];
4371 [book_ pushPage:packages];
4374 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4375 Source *source = [sources_ objectAtIndex:row];
4376 return [source record] != nil;
4379 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4380 [[list_ table] setDeleteConfirmationRow:row];
4383 - (void) table:(UITable *)table deleteRow:(int)row {
4384 Source *source = [sources_ objectAtIndex:row];
4385 [Sources_ removeObjectForKey:[source key]];
4386 [delegate_ syncData];
4389 - (void) _endConnection:(NSURLConnection *)connection {
4390 NSURLConnection **field = NULL;
4391 if (connection == trivial_bz2_)
4392 field = &trivial_bz2_;
4393 else if (connection == trivial_gz_)
4394 field = &trivial_gz_;
4395 _assert(field != NULL);
4396 [connection release];
4400 trivial_bz2_ == nil &&
4403 [delegate_ setStatusBarShowsProgress:NO];
4406 [hud_ removeFromSuperview];
4411 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4414 @"./", @"Distribution",
4415 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4417 [delegate_ syncData];
4418 } else if (error_ != nil) {
4419 UIActionSheet *sheet = [[[UIActionSheet alloc]
4420 initWithTitle:@"Verification Error"
4421 buttons:[NSArray arrayWithObjects:@"OK", nil]
4422 defaultButtonIndex:0
4427 [sheet setBodyText:[error_ localizedDescription]];
4428 [sheet popupAlertAnimated:YES];
4430 UIActionSheet *sheet = [[[UIActionSheet alloc]
4431 initWithTitle:@"Did not Find Repository"
4432 buttons:[NSArray arrayWithObjects:@"OK", nil]
4433 defaultButtonIndex:0
4438 [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."];
4439 [sheet popupAlertAnimated:YES];
4445 if (error_ != nil) {
4452 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4453 switch ([response statusCode]) {
4459 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4460 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4462 error_ = [error retain];
4463 [self _endConnection:connection];
4466 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4467 [self _endConnection:connection];
4470 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4471 NSMutableURLRequest *request = [NSMutableURLRequest
4472 requestWithURL:[NSURL URLWithString:href]
4473 cachePolicy:NSURLRequestUseProtocolCachePolicy
4474 timeoutInterval:20.0
4477 [request setHTTPMethod:method];
4479 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4482 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4483 NSString *context([sheet context]);
4485 if ([context isEqualToString:@"source"]) {
4488 NSString *href = [[sheet textField] text];
4490 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4492 if (![href hasSuffix:@"/"])
4493 href_ = [href stringByAppendingString:@"/"];
4496 href_ = [href_ retain];
4498 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4499 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4500 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4504 hud_ = [delegate_ addProgressHUD];
4505 [hud_ setText:@"Verifying URL"];
4516 } else if ([context isEqualToString:@"trivial"])
4518 else if ([context isEqualToString:@"urlerror"])
4522 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4523 if ((self = [super initWithBook:book]) != nil) {
4524 database_ = database;
4525 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4527 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4528 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4529 [list_ setShouldHideHeaderInShortLists:NO];
4531 [self addSubview:list_];
4532 [list_ setDataSource:self];
4534 UITableColumn *column = [[UITableColumn alloc]
4535 initWithTitle:@"Name"
4537 width:[self frame].size.width
4540 UITable *table = [list_ table];
4541 [table setSeparatorStyle:1];
4542 [table addTableColumn:column];
4543 [table setDelegate:self];
4547 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4548 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4552 - (void) reloadData {
4554 _assert(list.ReadMainList());
4556 [sources_ removeAllObjects];
4557 [sources_ addObjectsFromArray:[database_ sources]];
4559 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4562 int count = [sources_ count];
4563 for (offset_ = 0; offset_ != count; ++offset_) {
4564 Source *source = [sources_ objectAtIndex:offset_];
4565 if ([source record] == nil)
4572 - (void) resetViewAnimated:(BOOL)animated {
4573 [list_ resetViewAnimated:animated];
4576 - (void) _leftButtonClicked {
4577 /*[book_ pushPage:[[[AddSourceView alloc]
4582 UIActionSheet *sheet = [[[UIActionSheet alloc]
4583 initWithTitle:@"Enter Cydia/APT URL"
4584 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4585 defaultButtonIndex:0
4590 [sheet setNumberOfRows:1];
4592 [sheet addTextFieldWithValue:@"http://" label:@""];
4594 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4595 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4596 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4597 [traits setKeyboardType:UIKeyboardTypeURL];
4598 // XXX: UIReturnKeyDone
4599 [traits setReturnKeyType:UIReturnKeyNext];
4601 [sheet popupAlertAnimated:YES];
4604 - (void) _rightButtonClicked {
4605 UITable *table = [list_ table];
4606 BOOL editing = [table isRowDeletionEnabled];
4607 [table enableRowDeletion:!editing animated:YES];
4608 [book_ reloadButtonsForPage:self];
4611 - (NSString *) title {
4615 - (NSString *) leftButtonTitle {
4616 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4619 - (id) rightButtonTitle {
4620 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4623 - (UINavigationButtonStyle) rightButtonStyle {
4624 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4630 /* Installed View {{{ */
4631 @interface InstalledView : RVPage {
4632 _transient Database *database_;
4633 PackageTable *packages_;
4637 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4641 @implementation InstalledView
4644 [packages_ release];
4648 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4649 if ((self = [super initWithBook:book]) != nil) {
4650 database_ = database;
4652 packages_ = [[PackageTable alloc]
4656 filter:@selector(isInstalledAndVisible:)
4657 with:[NSNumber numberWithBool:YES]
4660 [self addSubview:packages_];
4662 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4663 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4667 - (void) resetViewAnimated:(BOOL)animated {
4668 [packages_ resetViewAnimated:animated];
4671 - (void) reloadData {
4672 [packages_ reloadData];
4675 - (void) _rightButtonClicked {
4676 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4677 [packages_ reloadData];
4679 [book_ reloadButtonsForPage:self];
4682 - (NSString *) title {
4683 return @"Installed";
4686 - (NSString *) backButtonTitle {
4690 - (id) rightButtonTitle {
4691 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4694 - (UINavigationButtonStyle) rightButtonStyle {
4695 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4698 - (void) setDelegate:(id)delegate {
4699 [super setDelegate:delegate];
4700 [packages_ setDelegate:delegate];
4707 @interface HomeView : BrowserView {
4712 @implementation HomeView
4714 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4715 NSString *context([sheet context]);
4717 if ([context isEqualToString:@"about"])
4720 [super alertSheet:sheet buttonClicked:button];
4723 - (void) _leftButtonClicked {
4724 UIActionSheet *sheet = [[[UIActionSheet alloc]
4725 initWithTitle:@"About Cydia Installer"
4726 buttons:[NSArray arrayWithObjects:@"Close", nil]
4727 defaultButtonIndex:0
4733 @"Copyright (C) 2008\n"
4734 "Jay Freeman (saurik)\n"
4735 "saurik@saurik.com\n"
4736 "http://www.saurik.com/\n"
4739 "http://www.theokorigroup.com/\n"
4741 "College of Creative Studies,\n"
4742 "University of California,\n"
4744 "http://www.ccs.ucsb.edu/"
4747 [sheet popupAlertAnimated:YES];
4750 - (NSString *) leftButtonTitle {
4756 /* Manage View {{{ */
4757 @interface ManageView : BrowserView {
4762 @implementation ManageView
4764 - (NSString *) title {
4768 - (void) _leftButtonClicked {
4769 [delegate_ askForSettings];
4772 - (NSString *) leftButtonTitle {
4777 - (id) _rightButtonTitle {
4789 /* Indirect Delegate {{{ */
4790 @interface IndirectDelegate : NSProxy {
4791 _transient volatile id delegate_;
4794 - (void) setDelegate:(id)delegate;
4795 - (id) initWithDelegate:(id)delegate;
4798 @implementation IndirectDelegate
4800 - (void) setDelegate:(id)delegate {
4801 delegate_ = delegate;
4804 - (id) initWithDelegate:(id)delegate {
4805 delegate_ = delegate;
4809 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4810 if (delegate_ != nil)
4811 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4813 // XXX: I fucking hate Apple so very very bad
4814 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4817 - (void) forwardInvocation:(NSInvocation *)inv {
4818 SEL sel = [inv selector];
4819 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4820 [inv invokeWithTarget:delegate_];
4826 #include <BrowserView.m>
4828 /* Cydia Book {{{ */
4829 @interface CYBook : RVBook <
4832 _transient Database *database_;
4833 UINavigationBar *overlay_;
4834 UINavigationBar *underlay_;
4835 UIProgressIndicator *indicator_;
4836 UITextLabel *prompt_;
4837 UIProgressBar *progress_;
4838 UINavigationButton *cancel_;
4841 NSTimeInterval last_;
4844 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4850 @implementation CYBook
4854 [indicator_ release];
4856 [progress_ release];
4861 - (NSString *) getTitleForPage:(RVPage *)page {
4862 return Simplify([super getTitleForPage:page]);
4870 [UIView beginAnimations:nil context:NULL];
4872 CGRect ovrframe = [overlay_ frame];
4873 ovrframe.origin.y = 0;
4874 [overlay_ setFrame:ovrframe];
4876 CGRect barframe = [navbar_ frame];
4877 barframe.origin.y += ovrframe.size.height;
4878 [navbar_ setFrame:barframe];
4880 CGRect trnframe = [transition_ frame];
4881 trnframe.origin.y += ovrframe.size.height;
4882 trnframe.size.height -= ovrframe.size.height;
4883 [transition_ setFrame:trnframe];
4885 [UIView endAnimations];
4887 [indicator_ startAnimation];
4888 [prompt_ setText:@"Updating Database"];
4889 [progress_ setProgress:0];
4892 last_ = [NSDate timeIntervalSinceReferenceDate];
4894 [overlay_ addSubview:cancel_];
4897 detachNewThreadSelector:@selector(_update)
4906 [indicator_ stopAnimation];
4908 [UIView beginAnimations:nil context:NULL];
4910 CGRect ovrframe = [overlay_ frame];
4911 ovrframe.origin.y = -ovrframe.size.height;
4912 [overlay_ setFrame:ovrframe];
4914 CGRect barframe = [navbar_ frame];
4915 barframe.origin.y -= ovrframe.size.height;
4916 [navbar_ setFrame:barframe];
4918 CGRect trnframe = [transition_ frame];
4919 trnframe.origin.y -= ovrframe.size.height;
4920 trnframe.size.height += ovrframe.size.height;
4921 [transition_ setFrame:trnframe];
4923 [UIView commitAnimations];
4925 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4928 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4929 if ((self = [super initWithFrame:frame]) != nil) {
4930 database_ = database;
4932 CGRect ovrrect = [navbar_ bounds];
4933 ovrrect.size.height = [UINavigationBar defaultSize].height;
4934 ovrrect.origin.y = -ovrrect.size.height;
4936 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4937 [self addSubview:overlay_];
4939 ovrrect.origin.y = frame.size.height;
4940 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4941 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4942 [self addSubview:underlay_];
4944 [overlay_ setBarStyle:1];
4945 [underlay_ setBarStyle:1];
4947 int barstyle = [overlay_ _barStyle:NO];
4948 bool ugly = barstyle == 0;
4950 UIProgressIndicatorStyle style = ugly ?
4951 UIProgressIndicatorStyleMediumBrown :
4952 UIProgressIndicatorStyleMediumWhite;
4954 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4955 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4956 CGRect indrect = {{indoffset, indoffset}, indsize};
4958 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4959 [indicator_ setStyle:style];
4960 [overlay_ addSubview:indicator_];
4962 CGSize prmsize = {215, indsize.height + 4};
4965 indoffset * 2 + indsize.width,
4969 unsigned(ovrrect.size.height - prmsize.height) / 2
4972 UIFont *font = [UIFont systemFontOfSize:15];
4974 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4976 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
4977 [prompt_ setBackgroundColor:[UIColor clearColor]];
4978 [prompt_ setFont:font];
4980 [overlay_ addSubview:prompt_];
4982 CGSize prgsize = {75, 100};
4985 ovrrect.size.width - prgsize.width - 10,
4986 (ovrrect.size.height - prgsize.height) / 2
4989 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4990 [progress_ setStyle:0];
4991 [overlay_ addSubview:progress_];
4993 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
4994 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
4996 CGRect frame = [cancel_ frame];
4997 frame.size.width = 65;
4998 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
4999 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5000 [cancel_ setFrame:frame];
5002 [cancel_ setBarStyle:barstyle];
5006 - (void) _onCancel {
5008 [cancel_ removeFromSuperview];
5011 - (void) _update { _pooled
5013 status.setDelegate(self);
5015 [database_ updateWithStatus:status];
5018 performSelectorOnMainThread:@selector(_update_)
5024 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5025 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5028 - (void) setProgressTitle:(NSString *)title {
5030 performSelectorOnMainThread:@selector(_setProgressTitle:)
5036 - (void) setProgressPercent:(float)percent {
5038 performSelectorOnMainThread:@selector(_setProgressPercent:)
5039 withObject:[NSNumber numberWithFloat:percent]
5044 - (void) startProgress {
5047 - (void) addProgressOutput:(NSString *)output {
5049 performSelectorOnMainThread:@selector(_addProgressOutput:)
5055 - (bool) isCancelling:(size_t)received {
5056 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5057 if (received_ != received) {
5058 received_ = received;
5060 } else if (now - last_ > 15)
5065 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5069 - (void) _setProgressTitle:(NSString *)title {
5070 [prompt_ setText:title];
5073 - (void) _setProgressPercent:(NSNumber *)percent {
5074 [progress_ setProgress:[percent floatValue]];
5077 - (void) _addProgressOutput:(NSString *)output {
5082 /* Cydia:// Protocol {{{ */
5083 @interface CydiaURLProtocol : NSURLProtocol {
5088 @implementation CydiaURLProtocol
5090 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5091 NSURL *url([request URL]);
5094 NSString *scheme([[url scheme] lowercaseString]);
5095 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5100 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5104 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5105 id<NSURLProtocolClient> client([self client]);
5106 NSData *data(UIImagePNGRepresentation(icon));
5108 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5109 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5110 [client URLProtocol:self didLoadData:data];
5111 [client URLProtocolDidFinishLoading:self];
5114 - (void) startLoading {
5115 id<NSURLProtocolClient> client([self client]);
5116 NSURLRequest *request([self request]);
5118 NSURL *url([request URL]);
5119 NSString *href([url absoluteString]);
5121 NSString *path([href substringFromIndex:8]);
5122 NSRange slash([path rangeOfString:@"/"]);
5125 if (slash.location == NSNotFound) {
5129 command = [path substringToIndex:slash.location];
5130 path = [path substringFromIndex:(slash.location + 1)];
5133 Database *database([Database sharedInstance]);
5135 if ([command isEqualToString:@"package-icon"]) {
5138 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5139 Package *package([database packageWithName:path]);
5142 UIImage *icon([package icon]);
5143 [self _returnPNGWithImage:icon forRequest:request];
5144 } else if ([command isEqualToString:@"source-icon"]) {
5147 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5148 NSString *source(Simplify(path));
5149 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5151 icon = [UIImage applicationImageNamed:@"unknown.png"];
5152 [self _returnPNGWithImage:icon forRequest:request];
5153 } else if ([command isEqualToString:@"uikit-image"]) {
5156 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5157 UIImage *icon(_UIImageWithName(path));
5158 [self _returnPNGWithImage:icon forRequest:request];
5159 } else if ([command isEqualToString:@"section-icon"]) {
5162 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5163 NSString *section(Simplify(path));
5164 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5166 icon = [UIImage applicationImageNamed:@"unknown.png"];
5167 [self _returnPNGWithImage:icon forRequest:request];
5169 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5173 - (void) stopLoading {
5179 /* Install View {{{ */
5180 @interface InstallView : RVPage {
5181 _transient Database *database_;
5182 NSMutableArray *sections_;
5183 NSMutableArray *filtered_;
5184 UITransitionView *transition_;
5190 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5191 - (void) reloadData;
5196 @implementation InstallView
5199 [list_ setDataSource:nil];
5200 [list_ setDelegate:nil];
5202 [sections_ release];
5203 [filtered_ release];
5204 [transition_ release];
5206 [accessory_ release];
5210 - (int) numberOfRowsInTable:(UITable *)table {
5211 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5214 - (float) table:(UITable *)table heightForRow:(int)row {
5218 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5220 reusing = [[[SectionCell alloc] init] autorelease];
5221 [(SectionCell *)reusing setSection:(editing_ ?
5222 [sections_ objectAtIndex:row] :
5223 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5224 ) editing:editing_];
5228 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5232 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5236 - (void) tableRowSelected:(NSNotification *)notification {
5237 int row = [[notification object] selectedRow];
5248 title = @"All Packages";
5250 section = [filtered_ objectAtIndex:(row - 1)];
5251 name = [section name];
5257 title = @"(No Section)";
5261 PackageTable *table = [[[PackageTable alloc]
5265 filter:@selector(isVisiblyUninstalledInSection:)
5269 [table setDelegate:delegate_];
5271 [book_ pushPage:table];
5274 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5275 if ((self = [super initWithBook:book]) != nil) {
5276 database_ = database;
5278 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5279 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5281 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5282 [self addSubview:transition_];
5284 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5285 [transition_ transition:0 toView:list_];
5287 UITableColumn *column = [[[UITableColumn alloc]
5288 initWithTitle:@"Name"
5290 width:[self frame].size.width
5293 [list_ setDataSource:self];
5294 [list_ setSeparatorStyle:1];
5295 [list_ addTableColumn:column];
5296 [list_ setDelegate:self];
5297 [list_ setReusesTableCells:YES];
5301 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5302 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5306 - (void) reloadData {
5307 NSArray *packages = [database_ packages];
5309 [sections_ removeAllObjects];
5310 [filtered_ removeAllObjects];
5312 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5313 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5316 for (size_t i(0); i != [packages count]; ++i) {
5317 Package *package([packages objectAtIndex:i]);
5318 NSString *name([package section]);
5321 Section *section([sections objectForKey:name]);
5322 if (section == nil) {
5323 section = [[[Section alloc] initWithName:name] autorelease];
5324 [sections setObject:section forKey:name];
5328 if ([package valid] && [package installed] == nil && [package visible])
5329 [filtered addObject:package];
5333 [sections_ addObjectsFromArray:[sections allValues]];
5334 [sections_ sortUsingSelector:@selector(compareByName:)];
5337 [filtered sortUsingSelector:@selector(compareBySection:)];
5340 Section *section = nil;
5341 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5342 Package *package = [filtered objectAtIndex:offset];
5343 NSString *name = [package section];
5345 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5346 section = name == nil ?
5347 [[[Section alloc] initWithName:nil] autorelease] :
5348 [sections objectForKey:name];
5349 [filtered_ addObject:section];
5352 [section addToCount];
5360 - (void) resetView {
5362 [self _rightButtonClicked];
5365 - (void) resetViewAnimated:(BOOL)animated {
5366 [list_ resetViewAnimated:animated];
5369 - (void) _rightButtonClicked {
5370 if ((editing_ = !editing_))
5373 [delegate_ updateData];
5374 [book_ reloadTitleForPage:self];
5375 [book_ reloadButtonsForPage:self];
5378 - (NSString *) title {
5379 return editing_ ? @"Section Visibility" : @"Install by Section";
5382 - (NSString *) backButtonTitle {
5386 - (id) rightButtonTitle {
5387 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5390 - (UINavigationButtonStyle) rightButtonStyle {
5391 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5394 - (UIView *) accessoryView {
5400 /* Changes View {{{ */
5401 @interface ChangesView : RVPage {
5402 _transient Database *database_;
5403 NSMutableArray *packages_;
5404 NSMutableArray *sections_;
5405 UISectionList *list_;
5409 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5410 - (void) reloadData;
5414 @implementation ChangesView
5417 [[list_ table] setDelegate:nil];
5418 [list_ setDataSource:nil];
5420 [packages_ release];
5421 [sections_ release];
5426 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5427 return [sections_ count];
5430 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5431 return [[sections_ objectAtIndex:section] name];
5434 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5435 return [[sections_ objectAtIndex:section] row];
5438 - (int) numberOfRowsInTable:(UITable *)table {
5439 return [packages_ count];
5442 - (float) table:(UITable *)table heightForRow:(int)row {
5443 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5446 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5448 reusing = [[[PackageCell alloc] init] autorelease];
5449 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5453 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5457 - (void) tableRowSelected:(NSNotification *)notification {
5458 int row = [[notification object] selectedRow];
5461 Package *package = [packages_ objectAtIndex:row];
5462 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5463 [view setDelegate:delegate_];
5464 [view setPackage:package];
5465 [book_ pushPage:view];
5468 - (void) _leftButtonClicked {
5469 [(CYBook *)book_ update];
5470 [self reloadButtons];
5473 - (void) _rightButtonClicked {
5474 [delegate_ distUpgrade];
5477 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5478 if ((self = [super initWithBook:book]) != nil) {
5479 database_ = database;
5481 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5482 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5484 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5485 [self addSubview:list_];
5487 [list_ setShouldHideHeaderInShortLists:NO];
5488 [list_ setDataSource:self];
5489 //[list_ setSectionListStyle:1];
5491 UITableColumn *column = [[[UITableColumn alloc]
5492 initWithTitle:@"Name"
5494 width:[self frame].size.width
5497 UITable *table = [list_ table];
5498 [table setSeparatorStyle:1];
5499 [table addTableColumn:column];
5500 [table setDelegate:self];
5501 [table setReusesTableCells:YES];
5505 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5506 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5510 - (void) reloadData {
5511 NSArray *packages = [database_ packages];
5513 [packages_ removeAllObjects];
5514 [sections_ removeAllObjects];
5517 for (size_t i(0); i != [packages count]; ++i) {
5518 Package *package([packages objectAtIndex:i]);
5521 [package installed] == nil && [package valid] && [package visible] ||
5522 [package upgradableAndEssential:NO]
5524 [packages_ addObject:package];
5528 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5531 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5532 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5533 Section *section = nil;
5537 bool unseens = false;
5539 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5542 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5543 Package *package = [packages_ objectAtIndex:offset];
5545 if (![package upgradableAndEssential:YES]) {
5547 NSDate *seen = [package seen];
5549 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5552 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5553 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5554 [sections_ addObject:section];
5558 [section addToCount];
5559 } else if ([package ignored])
5560 [ignored addToCount];
5563 [upgradable addToCount];
5568 CFRelease(formatter);
5571 Section *last = [sections_ lastObject];
5572 size_t count = [last count];
5573 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5574 [sections_ removeLastObject];
5577 if ([ignored count] != 0)
5578 [sections_ insertObject:ignored atIndex:0];
5580 [sections_ insertObject:upgradable atIndex:0];
5583 [self reloadButtons];
5586 - (void) resetViewAnimated:(BOOL)animated {
5587 [list_ resetViewAnimated:animated];
5590 - (NSString *) leftButtonTitle {
5591 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5594 - (id) rightButtonTitle {
5595 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5598 - (NSString *) title {
5604 /* Search View {{{ */
5605 @protocol SearchViewDelegate
5606 - (void) showKeyboard:(BOOL)show;
5609 @interface SearchView : RVPage {
5611 UISearchField *field_;
5612 UITransitionView *transition_;
5613 PackageTable *table_;
5614 UIPreferencesTable *advanced_;
5620 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5621 - (void) reloadData;
5625 @implementation SearchView
5628 [field_ setDelegate:nil];
5630 [accessory_ release];
5632 [transition_ release];
5634 [advanced_ release];
5639 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5643 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5645 case 0: return @"Advanced Search (Coming Soon!)";
5647 default: _assert(false);
5651 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5655 default: _assert(false);
5659 - (void) _showKeyboard:(BOOL)show {
5660 CGSize keysize = [UIKeyboard defaultSize];
5661 CGRect keydown = [book_ pageBounds];
5662 CGRect keyup = keydown;
5663 keyup.size.height -= keysize.height - ButtonBarHeight_;
5665 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5667 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5668 [animation setSignificantRectFields:8];
5671 [animation setStartFrame:keydown];
5672 [animation setEndFrame:keyup];
5674 [animation setStartFrame:keyup];
5675 [animation setEndFrame:keydown];
5678 UIAnimator *animator = [UIAnimator sharedAnimator];
5681 addAnimations:[NSArray arrayWithObjects:animation, nil]
5682 withDuration:(KeyboardTime_ - delay)
5687 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5689 [delegate_ showKeyboard:show];
5692 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5693 [self _showKeyboard:YES];
5696 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5697 [self _showKeyboard:NO];
5700 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5702 NSString *text([field_ text]);
5703 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5709 - (void) textFieldClearButtonPressed:(UITextField *)field {
5713 - (void) keyboardInputShouldDelete:(id)input {
5717 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5718 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5722 [field_ resignFirstResponder];
5727 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5728 if ((self = [super initWithBook:book]) != nil) {
5729 CGRect pageBounds = [book_ pageBounds];
5731 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5732 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5733 [self addSubview:pinstripe];*/
5735 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5736 [self addSubview:transition_];
5738 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5740 [advanced_ setReusesTableCells:YES];
5741 [advanced_ setDataSource:self];
5742 [advanced_ reloadData];
5744 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5745 CGColor dimmed(space_, 0, 0, 0, 0.5);
5746 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5748 table_ = [[PackageTable alloc]
5752 filter:@selector(isUnfilteredAndSearchedForBy:)
5756 [table_ setShouldHideHeaderInShortLists:NO];
5757 [transition_ transition:0 toView:table_];
5766 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5773 [self bounds].size.width - area.origin.x - 18;
5775 area.size.height = [UISearchField defaultHeight];
5777 field_ = [[UISearchField alloc] initWithFrame:area];
5779 UIFont *font = [UIFont systemFontOfSize:16];
5780 [field_ setFont:font];
5782 [field_ setPlaceholder:@"Package Names & Descriptions"];
5783 [field_ setDelegate:self];
5785 [field_ setPaddingTop:5];
5787 UITextInputTraits *traits([field_ textInputTraits]);
5788 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5789 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5790 [traits setReturnKeyType:UIReturnKeySearch];
5792 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5794 accessory_ = [[UIView alloc] initWithFrame:accrect];
5795 [accessory_ addSubview:field_];
5797 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5798 [configure setShowPressFeedback:YES];
5799 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5800 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5801 [accessory_ addSubview:configure];*/
5803 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5804 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5810 LKAnimation *animation = [LKTransition animation];
5811 [animation setType:@"oglFlip"];
5812 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5813 [animation setFillMode:@"extended"];
5814 [animation setTransitionFlags:3];
5815 [animation setDuration:10];
5816 [animation setSpeed:0.35];
5817 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5818 [[transition_ _layer] addAnimation:animation forKey:0];
5819 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5820 flipped_ = !flipped_;
5824 - (void) configurePushed {
5825 [field_ resignFirstResponder];
5829 - (void) resetViewAnimated:(BOOL)animated {
5832 [table_ resetViewAnimated:animated];
5835 - (void) _reloadData {
5838 - (void) reloadData {
5841 [table_ setObject:[field_ text]];
5842 [table_ reloadData];
5843 [table_ resetCursor];
5846 - (UIView *) accessoryView {
5850 - (NSString *) title {
5854 - (NSString *) backButtonTitle {
5858 - (void) setDelegate:(id)delegate {
5859 [table_ setDelegate:delegate];
5860 [super setDelegate:delegate];
5866 @interface SettingsView : RVPage {
5867 _transient Database *database_;
5870 UIPreferencesTable *table_;
5871 _UISwitchSlider *subscribedSwitch_;
5872 _UISwitchSlider *ignoredSwitch_;
5873 UIPreferencesControlTableCell *subscribedCell_;
5874 UIPreferencesControlTableCell *ignoredCell_;
5877 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5881 @implementation SettingsView
5884 [table_ setDataSource:nil];
5887 if (package_ != nil)
5890 [subscribedSwitch_ release];
5891 [ignoredSwitch_ release];
5892 [subscribedCell_ release];
5893 [ignoredCell_ release];
5897 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5898 if (package_ == nil)
5904 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5905 if (package_ == nil)
5912 default: _assert(false);
5918 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5919 if (package_ == nil)
5926 default: _assert(false);
5932 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5933 if (package_ == nil)
5940 default: _assert(false);
5946 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5947 if (package_ == nil)
5950 _UISwitchSlider *slider([cell control]);
5951 BOOL value([slider value] != 0);
5952 NSMutableDictionary *metadata([package_ metadata]);
5955 if (NSNumber *number = [metadata objectForKey:key])
5956 before = [number boolValue];
5960 if (value != before) {
5961 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5963 [delegate_ updateData];
5967 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
5968 [self onSomething:cell withKey:@"IsSubscribed"];
5971 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
5972 [self onSomething:cell withKey:@"IsIgnored"];
5975 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
5976 if (package_ == nil)
5980 case 0: switch (row) {
5982 return subscribedCell_;
5984 return ignoredCell_;
5985 default: _assert(false);
5988 case 1: switch (row) {
5990 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
5991 [cell setShowSelection:NO];
5992 [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."];
5996 default: _assert(false);
5999 default: _assert(false);
6005 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6006 if ((self = [super initWithBook:book])) {
6007 database_ = database;
6008 name_ = [package retain];
6010 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6011 [self addSubview:table_];
6013 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6014 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6016 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6017 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6019 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6020 [subscribedCell_ setShowSelection:NO];
6021 [subscribedCell_ setTitle:@"Show All Changes"];
6022 [subscribedCell_ setControl:subscribedSwitch_];
6024 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6025 [ignoredCell_ setShowSelection:NO];
6026 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6027 [ignoredCell_ setControl:ignoredSwitch_];
6029 [table_ setDataSource:self];
6034 - (void) resetViewAnimated:(BOOL)animated {
6035 [table_ resetViewAnimated:animated];
6038 - (void) reloadData {
6039 if (package_ != nil)
6040 [package_ autorelease];
6041 package_ = [database_ packageWithName:name_];
6042 if (package_ != nil) {
6044 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6045 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6048 [table_ reloadData];
6051 - (NSString *) title {
6057 /* Signature View {{{ */
6058 @interface SignatureView : BrowserView {
6059 _transient Database *database_;
6063 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6067 @implementation SignatureView
6074 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6076 [super webView:sender didClearWindowObject:window forFrame:frame];
6079 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6080 if ((self = [super initWithBook:book]) != nil) {
6081 database_ = database;
6082 package_ = [package retain];
6087 - (void) resetViewAnimated:(BOOL)animated {
6090 - (void) reloadData {
6091 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6097 @interface Cydia : UIApplication <
6098 ConfirmationViewDelegate,
6099 ProgressViewDelegate,
6108 UIToolbar *buttonbar_;
6112 NSMutableArray *essential_;
6113 NSMutableArray *broken_;
6115 Database *database_;
6116 ProgressView *progress_;
6120 UIKeyboard *keyboard_;
6121 UIProgressHUD *hud_;
6123 InstallView *install_;
6124 ChangesView *changes_;
6125 ManageView *manage_;
6126 SearchView *search_;
6131 @implementation Cydia
6134 if ([broken_ count] != 0) {
6135 int count = [broken_ count];
6137 UIActionSheet *sheet = [[[UIActionSheet alloc]
6138 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6139 buttons:[NSArray arrayWithObjects:
6141 @"Ignore (Temporary)",
6143 defaultButtonIndex:0
6148 [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."];
6149 [sheet popupAlertAnimated:YES];
6150 } else if (!Ignored_ && [essential_ count] != 0) {
6151 int count = [essential_ count];
6153 UIActionSheet *sheet = [[[UIActionSheet alloc]
6154 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6155 buttons:[NSArray arrayWithObjects:
6156 @"Upgrade Essential",
6157 @"Complete Upgrade",
6158 @"Ignore (Temporary)",
6160 defaultButtonIndex:0
6165 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6166 [sheet popupAlertAnimated:YES];
6170 - (void) _reloadData {
6171 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6172 [hud setText:@"Reloading Data"];
6173 [overlay_ addSubview:hud];
6176 [database_ reloadData];
6180 [essential_ removeAllObjects];
6181 [broken_ removeAllObjects];
6183 NSArray *packages = [database_ packages];
6184 for (Package *package in packages) {
6186 [broken_ addObject:package];
6187 if ([package upgradableAndEssential:NO]) {
6188 if ([package essential])
6189 [essential_ addObject:package];
6195 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6196 [buttonbar_ setBadgeValue:badge forButton:3];
6197 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6198 [buttonbar_ setBadgeAnimated:YES forButton:3];
6199 [self setApplicationBadge:badge];
6201 [buttonbar_ setBadgeValue:nil forButton:3];
6202 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6203 [buttonbar_ setBadgeAnimated:NO forButton:3];
6204 [self removeApplicationBadge];
6210 if ([packages count] == 0);
6222 [hud removeFromSuperview];*/
6225 - (void) _saveConfig {
6228 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6234 - (void) updateData {
6237 /* XXX: this is just stupid */
6238 if (tag_ != 2 && install_ != nil)
6239 [install_ reloadData];
6240 if (tag_ != 3 && changes_ != nil)
6241 [changes_ reloadData];
6242 if (tag_ != 5 && search_ != nil)
6243 [search_ reloadData];
6253 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6254 _assert(file != NULL);
6256 NSArray *keys = [Sources_ allKeys];
6258 for (int i(0), e([keys count]); i != e; ++i) {
6259 NSString *key = [keys objectAtIndex:i];
6260 NSDictionary *source = [Sources_ objectForKey:key];
6262 fprintf(file, "%s %s %s\n",
6263 [[source objectForKey:@"Type"] UTF8String],
6264 [[source objectForKey:@"URI"] UTF8String],
6265 [[source objectForKey:@"Distribution"] UTF8String]
6274 detachNewThreadSelector:@selector(update_)
6277 title:@"Updating Sources"
6281 - (void) reloadData {
6282 @synchronized (self) {
6283 if (confirm_ == nil)
6289 pkgProblemResolver *resolver = [database_ resolver];
6291 resolver->InstallProtect();
6292 if (!resolver->Resolve(true))
6297 [database_ prepare];
6299 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6300 [confirm_ setDelegate:self];
6302 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6303 [page setDelegate:self];
6305 [confirm_ setPage:page];
6306 [underlay_ popSubview:confirm_];
6309 - (void) installPackage:(Package *)package {
6310 @synchronized (self) {
6317 - (void) removePackage:(Package *)package {
6318 @synchronized (self) {
6325 - (void) distUpgrade {
6326 @synchronized (self) {
6327 [database_ upgrade];
6333 @synchronized (self) {
6335 if (confirm_ != nil) {
6343 [overlay_ removeFromSuperview];
6347 detachNewThreadSelector:@selector(perform)
6354 - (void) bootstrap_ {
6356 [database_ upgrade];
6357 [database_ prepare];
6358 [database_ perform];
6361 - (void) bootstrap {
6363 detachNewThreadSelector:@selector(bootstrap_)
6366 title:@"Bootstrap Install"
6370 - (void) progressViewIsComplete:(ProgressView *)progress {
6371 if (confirm_ != nil) {
6372 [underlay_ addSubview:overlay_];
6373 [confirm_ popFromSuperviewAnimated:NO];
6379 - (void) setPage:(RVPage *)page {
6380 [page resetViewAnimated:NO];
6381 [page setDelegate:self];
6382 [book_ setPage:page];
6385 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6386 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6387 [browser loadURL:url];
6391 - (void) _setHomePage {
6392 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6395 - (void) buttonBarItemTapped:(id)sender {
6396 unsigned tag = [sender tag];
6398 [book_ resetViewAnimated:YES];
6400 } else if (tag_ == 2 && tag != 2)
6401 [install_ resetView];
6404 case 1: [self _setHomePage]; break;
6406 case 2: [self setPage:install_]; break;
6407 case 3: [self setPage:changes_]; break;
6408 case 4: [self setPage:manage_]; break;
6409 case 5: [self setPage:search_]; break;
6411 default: _assert(false);
6417 - (void) applicationWillSuspend {
6419 [super applicationWillSuspend];
6422 - (void) askForSettings {
6423 UIActionSheet *role = [[[UIActionSheet alloc]
6424 initWithTitle:@"Who Are You?"
6425 buttons:[NSArray arrayWithObjects:
6426 @"User (Graphical Only)",
6427 @"Hacker (+ Command Line)",
6428 @"Developer (No Filters)",
6430 defaultButtonIndex:-1
6435 [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."];
6436 [role popupAlertAnimated:YES];
6441 [self setStatusBarShowsProgress:NO];
6444 [hud_ removeFromSuperview];
6448 pid_t pid = ExecFork();
6450 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6451 perror("launchctl stop");
6458 [self askForSettings];
6462 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6464 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6465 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6466 0, 0, screenrect.size.width, screenrect.size.height - 48
6467 ) database:database_];
6469 [book_ setDelegate:self];
6471 [overlay_ addSubview:book_];
6473 NSArray *buttonitems = [NSArray arrayWithObjects:
6474 [NSDictionary dictionaryWithObjectsAndKeys:
6475 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6476 @"home-up.png", kUIButtonBarButtonInfo,
6477 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6478 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6479 self, kUIButtonBarButtonTarget,
6480 @"Home", kUIButtonBarButtonTitle,
6481 @"0", kUIButtonBarButtonType,
6484 [NSDictionary dictionaryWithObjectsAndKeys:
6485 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6486 @"install-up.png", kUIButtonBarButtonInfo,
6487 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6488 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6489 self, kUIButtonBarButtonTarget,
6490 @"Sections", kUIButtonBarButtonTitle,
6491 @"0", kUIButtonBarButtonType,
6494 [NSDictionary dictionaryWithObjectsAndKeys:
6495 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6496 @"changes-up.png", kUIButtonBarButtonInfo,
6497 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6498 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6499 self, kUIButtonBarButtonTarget,
6500 @"Changes", kUIButtonBarButtonTitle,
6501 @"0", kUIButtonBarButtonType,
6504 [NSDictionary dictionaryWithObjectsAndKeys:
6505 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6506 @"manage-up.png", kUIButtonBarButtonInfo,
6507 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6508 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6509 self, kUIButtonBarButtonTarget,
6510 @"Manage", kUIButtonBarButtonTitle,
6511 @"0", kUIButtonBarButtonType,
6514 [NSDictionary dictionaryWithObjectsAndKeys:
6515 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6516 @"search-up.png", kUIButtonBarButtonInfo,
6517 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6518 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6519 self, kUIButtonBarButtonTarget,
6520 @"Search", kUIButtonBarButtonTitle,
6521 @"0", kUIButtonBarButtonType,
6525 buttonbar_ = [[UIToolbar alloc]
6527 withFrame:CGRectMake(
6528 0, screenrect.size.height - ButtonBarHeight_,
6529 screenrect.size.width, ButtonBarHeight_
6531 withItemList:buttonitems
6534 [buttonbar_ setDelegate:self];
6535 [buttonbar_ setBarStyle:1];
6536 [buttonbar_ setButtonBarTrackingMode:2];
6538 int buttons[5] = {1, 2, 3, 4, 5};
6539 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6540 [buttonbar_ showButtonGroup:0 withDuration:0];
6542 for (int i = 0; i != 5; ++i)
6543 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6544 i * 64 + 2, 1, 60, ButtonBarHeight_
6547 [buttonbar_ showSelectionForButton:1];
6548 [overlay_ addSubview:buttonbar_];
6550 [UIKeyboard initImplementationNow];
6551 CGSize keysize = [UIKeyboard defaultSize];
6552 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6553 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6554 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6555 [overlay_ addSubview:keyboard_];
6558 [underlay_ addSubview:overlay_];
6562 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6563 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6564 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6566 manage_ = (ManageView *) [[self
6567 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6568 withClass:[ManageView class]
6574 [self _setHomePage];
6577 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6578 NSString *context([sheet context]);
6580 if ([context isEqualToString:@"fixhalf"]) {
6583 @synchronized (self) {
6584 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6585 Package *broken = [broken_ objectAtIndex:i];
6588 NSString *id = [broken id];
6589 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6590 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6591 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6592 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6601 [broken_ removeAllObjects];
6610 } else if ([context isEqualToString:@"role"]) {
6612 case 1: Role_ = @"User"; break;
6613 case 2: Role_ = @"Hacker"; break;
6614 case 3: Role_ = @"Developer"; break;
6621 bool reset = Settings_ != nil;
6623 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6627 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6637 } else if ([context isEqualToString:@"upgrade"]) {
6640 @synchronized (self) {
6641 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6642 Package *essential = [essential_ objectAtIndex:i];
6643 [essential install];
6667 - (void) reorganize { _pooled
6668 system("/usr/libexec/cydia/free.sh");
6669 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6672 - (void) applicationSuspend:(__GSEvent *)event {
6673 if (hud_ == nil && ![progress_ isRunning])
6674 [super applicationSuspend:event];
6677 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6679 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6682 - (void) _setSuspended:(BOOL)value {
6684 [super _setSuspended:value];
6687 - (UIProgressHUD *) addProgressHUD {
6688 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6690 [underlay_ addSubview:hud];
6694 - (void) openMailToURL:(NSURL *)url {
6695 // XXX: this makes me sad
6697 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6699 [UIApp openURL:url];// asPanel:YES];
6703 - (void) clearFirstResponder {
6704 if (id responder = [window_ firstResponder])
6705 [responder resignFirstResponder];
6708 - (RVPage *) pageForPackage:(NSString *)name {
6709 if (Package *package = [database_ packageWithName:name]) {
6710 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6711 [view setPackage:package];
6714 UIActionSheet *sheet = [[[UIActionSheet alloc]
6715 initWithTitle:@"Cannot Locate Package"
6716 buttons:[NSArray arrayWithObjects:@"Close", nil]
6717 defaultButtonIndex:0
6722 [sheet setBodyText:[NSString stringWithFormat:
6723 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6726 [sheet popupAlertAnimated:YES];
6731 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6732 NSString *href = [url absoluteString];
6737 if ([href isEqualToString:@"cydia://add-source"])
6738 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6739 else if ([href isEqualToString:@"cydia://sources"])
6740 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6741 else if ([href isEqualToString:@"cydia://packages"])
6742 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6743 else if ([href hasPrefix:@"cydia://url/"])
6744 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
6745 else if ([href hasPrefix:@"cydia://launch/"])
6746 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
6747 else if ([href hasPrefix:@"cydia://package-settings/"])
6748 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
6749 else if ([href hasPrefix:@"cydia://package-signature/"])
6750 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
6751 else if ([href hasPrefix:@"cydia://package/"])
6752 return [self pageForPackage:[href substringFromIndex:16]];
6753 else if ([href hasPrefix:@"cydia://files/"]) {
6754 NSString *name = [href substringFromIndex:14];
6756 if (Package *package = [database_ packageWithName:name]) {
6757 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6758 [files setPackage:package];
6766 - (void) applicationOpenURL:(NSURL *)url {
6767 [super applicationOpenURL:url];
6769 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6770 [self setPage:page];
6771 [buttonbar_ showSelectionForButton:tag];
6776 - (void) applicationDidFinishLaunching:(id)unused {
6777 Font12_ = [[UIFont systemFontOfSize:12] retain];
6778 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6779 Font14_ = [[UIFont systemFontOfSize:14] retain];
6780 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6781 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6783 _assert(pkgInitConfig(*_config));
6784 _assert(pkgInitSystem(*_config, _system));
6788 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6789 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6791 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6793 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6794 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6796 [window_ orderFront:self];
6797 [window_ makeKey:self];
6798 [window_ setHidden:NO];
6800 database_ = [Database sharedInstance];
6801 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6802 [database_ setDelegate:progress_];
6803 [window_ setContentView:progress_];
6805 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6806 [progress_ setContentView:underlay_];
6808 [progress_ resetView];
6811 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6812 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6813 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6814 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6815 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6816 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6817 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6819 [self setIdleTimerDisabled:YES];
6821 hud_ = [self addProgressHUD];
6822 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6824 [self setStatusBarShowsProgress:YES];
6827 detachNewThreadSelector:@selector(reorganize)
6835 - (void) showKeyboard:(BOOL)show {
6836 CGSize keysize = [UIKeyboard defaultSize];
6837 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6838 CGRect keyup = keydown;
6839 keyup.origin.y -= keysize.height;
6841 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6842 [animation setSignificantRectFields:2];
6845 [animation setStartFrame:keydown];
6846 [animation setEndFrame:keyup];
6847 [keyboard_ activate];
6849 [animation setStartFrame:keyup];
6850 [animation setEndFrame:keydown];
6851 [keyboard_ deactivate];
6854 [[UIAnimator sharedAnimator]
6855 addAnimations:[NSArray arrayWithObjects:animation, nil]
6856 withDuration:KeyboardTime_
6861 - (void) slideUp:(UIActionSheet *)alert {
6863 [alert presentSheetFromButtonBar:buttonbar_];
6865 [alert presentSheetInView:overlay_];
6870 void AddPreferences(NSString *plist) { _pooled
6871 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6872 _assert(settings != NULL);
6873 NSMutableArray *items = [settings objectForKey:@"items"];
6877 for (size_t i(0); i != [items count]; ++i) {
6878 NSMutableDictionary *item([items objectAtIndex:i]);
6879 NSString *label = [item objectForKey:@"label"];
6880 if (label != nil && [label isEqualToString:@"Cydia"]) {
6887 for (size_t i(0); i != [items count]; ++i) {
6888 NSDictionary *item([items objectAtIndex:i]);
6889 NSString *label = [item objectForKey:@"label"];
6890 if (label != nil && [label isEqualToString:@"General"]) {
6891 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6892 @"CydiaSettings", @"bundle",
6893 @"PSLinkCell", @"cell",
6894 [NSNumber numberWithBool:YES], @"hasIcon",
6895 [NSNumber numberWithBool:YES], @"isController",
6897 nil] atIndex:(i + 1)];
6903 _assert([settings writeToFile:plist atomically:YES] == YES);
6908 id Alloc_(id self, SEL selector) {
6909 id object = alloc_(self, selector);
6910 lprintf("[%s]A-%p\n", self->isa->name, object);
6915 id Dealloc_(id self, SEL selector) {
6916 id object = dealloc_(self, selector);
6917 lprintf("[%s]D-%p\n", self->isa->name, object);
6921 int main(int argc, char *argv[]) { _pooled
6922 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6924 bool substrate(false);
6930 for (int argi(1); argi != argc; ++argi)
6931 if (strcmp(argv[argi], "--") == 0) {
6933 argv[argi] = argv[0];
6939 for (int argi(1); argi != arge; ++argi)
6940 if (strcmp(args[argi], "--bootstrap") == 0)
6942 else if (strcmp(args[argi], "--substrate") == 0)
6945 fprintf(stderr, "unknown argument: %s\n", args[argi]);
6948 App_ = [[NSBundle mainBundle] bundlePath];
6949 Home_ = NSHomeDirectory();
6950 Locale_ = CFLocaleCopyCurrent();
6953 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6954 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6955 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6956 Sounds_Keyboard_ = [keyboard boolValue];
6962 #if 1 /* XXX: this costs 1.4s of startup performance */
6963 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6964 _assert(errno == ENOENT);
6965 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6966 _assert(errno == ENOENT);
6969 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6970 alloc_ = alloc->method_imp;
6971 alloc->method_imp = (IMP) &Alloc_;*/
6973 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6974 dealloc_ = dealloc->method_imp;
6975 dealloc->method_imp = (IMP) &Dealloc_;*/
6980 size = sizeof(maxproc);
6981 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6982 perror("sysctlbyname(\"kern.maxproc\", ?)");
6983 else if (maxproc < 64) {
6985 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
6986 perror("sysctlbyname(\"kern.maxproc\", #)");
6989 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6990 char *machine = new char[size];
6991 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
6992 perror("sysctlbyname(\"hw.machine\", ?)");
6996 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
6998 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6999 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7001 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7002 Indices_ = [[NSMutableDictionary alloc] init];*/
7004 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7005 //@"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
7006 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7009 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7010 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7012 Settings_ = [Metadata_ objectForKey:@"Settings"];
7014 Packages_ = [Metadata_ objectForKey:@"Packages"];
7015 Sections_ = [Metadata_ objectForKey:@"Sections"];
7016 Sources_ = [Metadata_ objectForKey:@"Sources"];
7019 if (Settings_ != nil)
7020 Role_ = [Settings_ objectForKey:@"Role"];
7022 if (Packages_ == nil) {
7023 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7024 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7027 if (Sections_ == nil) {
7028 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7029 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7032 if (Sources_ == nil) {
7033 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7034 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7038 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7041 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7042 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7044 if (access("/User", F_OK) != 0)
7045 system("/usr/libexec/cydia/firmware.sh");
7047 _assert([[NSFileManager defaultManager]
7048 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7049 withIntermediateDirectories:YES
7054 space_ = CGColorSpaceCreateDeviceRGB();
7056 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7057 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7058 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7059 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7060 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7061 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7063 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7065 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7067 UIApplicationUseLegacyEvents(YES);
7068 UIKeyboardDisableAutomaticAppearance();
7070 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7072 CGColorSpaceRelease(space_);