1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 // XXX: wtf/FastMalloc.h... wtf?
39 #define USE_SYSTEM_MALLOC 1
41 /* #include Directives {{{ */
42 #import "UICaboodle.h"
44 #include <objc/objc.h>
45 #include <objc/runtime.h>
47 #include <CoreGraphics/CoreGraphics.h>
48 #include <GraphicsServices/GraphicsServices.h>
49 #include <Foundation/Foundation.h>
51 #import <QuartzCore/CALayer.h>
53 #import <UIKit/UIKit.h>
56 #import <MessageUI/MailComposeController.h>
61 #include <ext/stdio_filebuf.h>
63 #include <apt-pkg/acquire.h>
64 #include <apt-pkg/acquire-item.h>
65 #include <apt-pkg/algorithms.h>
66 #include <apt-pkg/cachefile.h>
67 #include <apt-pkg/clean.h>
68 #include <apt-pkg/configuration.h>
69 #include <apt-pkg/debmetaindex.h>
70 #include <apt-pkg/error.h>
71 #include <apt-pkg/init.h>
72 #include <apt-pkg/mmap.h>
73 #include <apt-pkg/pkgrecords.h>
74 #include <apt-pkg/sha1.h>
75 #include <apt-pkg/sourcelist.h>
76 #include <apt-pkg/sptr.h>
77 #include <apt-pkg/strutl.h>
79 #include <sys/types.h>
81 #include <sys/sysctl.h>
82 #include <sys/param.h>
83 #include <sys/mount.h>
89 #include <mach-o/nlist.h>
99 #import "BrowserView.h"
100 #import "ResetView.h"
102 #import "substrate.h"
105 //#define _finline __attribute__((force_inline))
106 #define _finline inline
111 #define _limit(count) do { \
112 static size_t _count(0); \
113 if (++_count == count) \
117 static uint64_t profile_;
119 #define _timestamp ({ \
121 gettimeofday(&tv, NULL); \
122 tv.tv_sec * 1000000 + tv.tv_usec; \
125 /* Objective-C Handle<> {{{ */
126 template <typename Type_>
128 typedef _H<Type_> This_;
133 _finline void Retain_() {
138 _finline void Clear_() {
144 _finline _H(Type_ *value = NULL, bool mended = false) :
155 _finline This_ &operator =(Type_ *value) {
156 if (value_ != value) {
165 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
167 void NSLogPoint(const char *fix, const CGPoint &point) {
168 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
171 void NSLogRect(const char *fix, const CGRect &rect) {
172 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
175 /* NSForcedOrderingSearch doesn't work on the iPhone */
176 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
177 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
178 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
180 /* iPhoneOS 2.0 Compatibility {{{ */
182 @interface UITextView (iPhoneOS)
183 - (void) setTextSize:(float)size;
186 @implementation UITextView (iPhoneOS)
188 - (void) setTextSize:(float)size {
189 [self setFont:[[self font] fontWithSize:size]];
196 extern NSString * const kCAFilterNearest;
198 /* Information Dictionaries {{{ */
199 @interface NSMutableArray (Cydia)
200 - (void) addInfoDictionary:(NSDictionary *)info;
203 @implementation NSMutableArray (Cydia)
205 - (void) addInfoDictionary:(NSDictionary *)info {
206 [self addObject:info];
211 @interface NSMutableDictionary (Cydia)
212 - (void) addInfoDictionary:(NSDictionary *)info;
215 @implementation NSMutableDictionary (Cydia)
217 - (void) addInfoDictionary:(NSDictionary *)info {
218 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
219 [self setObject:info forKey:bundle];
224 /* Pop Transitions {{{ */
225 @interface PopTransitionView : UITransitionView {
230 @implementation PopTransitionView
232 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
233 if (from != nil && to == nil)
234 [self removeFromSuperview];
239 @interface UIView (PopUpView)
240 - (void) popFromSuperviewAnimated:(BOOL)animated;
241 - (void) popSubview:(UIView *)view;
244 @implementation UIView (PopUpView)
246 - (void) popFromSuperviewAnimated:(BOOL)animated {
247 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
250 - (void) popSubview:(UIView *)view {
251 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
252 [transition setDelegate:transition];
253 [self addSubview:transition];
255 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
256 [transition transition:UITransitionNone toView:blank];
257 [transition transition:UITransitionPushFromBottom toView:view];
263 #define lprintf(args...) fprintf(stderr, args)
266 #define ForSaurik (1 && !ForRelease)
267 #define IgnoreInstall (0 && !ForRelease)
268 #define RecycleWebViews 0
269 #define AlwaysReload (1 && !ForRelease)
272 @interface NSMutableArray (Radix)
273 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
276 @implementation NSMutableArray (Radix)
278 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
279 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
280 [invocation setSelector:selector];
281 [invocation setArgument:&object atIndex:2];
283 size_t count([self count]);
288 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
290 for (size_t i(0); i != count; ++i) {
291 RadixItem &item(lhs[i]);
294 id object([self objectAtIndex:i]);
295 [invocation setTarget:object];
298 [invocation getReturnValue:&item.key];
301 static const size_t width = 32;
302 static const size_t bits = 11;
303 static const size_t slots = 1 << bits;
304 static const size_t passes = (width + (bits - 1)) / bits;
306 size_t *hist(new size_t[slots]);
308 for (size_t pass(0); pass != passes; ++pass) {
309 memset(hist, 0, sizeof(size_t) * slots);
311 for (size_t i(0); i != count; ++i) {
312 uint32_t key(lhs[i].key);
314 key &= _not(uint32_t) >> width - bits;
319 for (size_t i(0); i != slots; ++i) {
320 size_t local(offset);
325 for (size_t i(0); i != count; ++i) {
326 uint32_t key(lhs[i].key);
328 key &= _not(uint32_t) >> width - bits;
329 rhs[hist[key]++] = lhs[i];
339 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
340 for (size_t i(0); i != count; ++i)
341 [values addObject:[self objectAtIndex:lhs[i].index]];
342 [self setArray:values];
350 /* Apple Bug Fixes {{{ */
351 @implementation UIWebDocumentView (Cydia)
353 - (void) _setScrollerOffset:(CGPoint)offset {
354 UIScroller *scroller([self _scroller]);
356 CGSize size([scroller contentSize]);
357 CGSize bounds([scroller bounds].size);
360 max.x = size.width - bounds.width;
361 max.y = size.height - bounds.height;
369 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
370 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
372 [scroller setOffset:offset];
379 kUIControlEventMouseDown = 1 << 0,
380 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
381 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
382 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
383 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
384 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
385 } UIControlEventMasks;
387 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
388 size_t length([self length] - state->state);
391 else if (length > count)
393 for (size_t i(0); i != length; ++i)
394 objects[i] = [self item:state->state++];
395 state->itemsPtr = objects;
396 state->mutationsPtr = (unsigned long *) self;
400 @interface NSString (UIKit)
401 - (NSString *) stringByAddingPercentEscapes;
402 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
405 @interface NSString (Cydia)
406 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
407 - (NSComparisonResult) compareByPath:(NSString *)other;
410 @implementation NSString (Cydia)
412 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
413 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
416 - (NSComparisonResult) compareByPath:(NSString *)other {
417 NSString *prefix = [self commonPrefixWithString:other options:0];
418 size_t length = [prefix length];
420 NSRange lrange = NSMakeRange(length, [self length] - length);
421 NSRange rrange = NSMakeRange(length, [other length] - length);
423 lrange = [self rangeOfString:@"/" options:0 range:lrange];
424 rrange = [other rangeOfString:@"/" options:0 range:rrange];
426 NSComparisonResult value;
428 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
429 value = NSOrderedSame;
430 else if (lrange.location == NSNotFound)
431 value = NSOrderedAscending;
432 else if (rrange.location == NSNotFound)
433 value = NSOrderedDescending;
435 value = NSOrderedSame;
437 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
438 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
439 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
440 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
442 NSComparisonResult result = [lpath compare:rpath];
443 return result == NSOrderedSame ? value : result;
448 /* Perl-Compatible RegEx {{{ */
458 Pcre(const char *regex) :
463 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
466 lprintf("%d:%s\n", offset, error);
470 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
471 matches_ = new int[(capture_ + 1) * 3];
479 NSString *operator [](size_t match) {
480 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
483 bool operator ()(NSString *data) {
484 // XXX: length is for characters, not for bytes
485 return operator ()([data UTF8String], [data length]);
488 bool operator ()(const char *data, size_t size) {
490 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
494 /* Mime Addresses {{{ */
495 @interface Address : NSObject {
501 - (NSString *) address;
503 + (Address *) addressWithString:(NSString *)string;
504 - (Address *) initWithString:(NSString *)string;
507 @implementation Address
516 - (NSString *) name {
520 - (NSString *) address {
524 + (Address *) addressWithString:(NSString *)string {
525 return [[[Address alloc] initWithString:string] autorelease];
528 + (NSArray *) _attributeKeys {
529 return [NSArray arrayWithObjects:@"address", @"name", nil];
532 - (NSArray *) attributeKeys {
533 return [[self class] _attributeKeys];
536 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
537 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
540 - (Address *) initWithString:(NSString *)string {
541 if ((self = [super init]) != nil) {
542 const char *data = [string UTF8String];
543 size_t size = [string length];
545 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
547 if (address_r(data, size)) {
548 name_ = [address_r[1] retain];
549 address_ = [address_r[2] retain];
551 name_ = [string retain];
559 /* CoreGraphics Primitives {{{ */
570 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
573 Set(space, red, green, blue, alpha);
578 CGColorRelease(color_);
585 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
587 float color[] = {red, green, blue, alpha};
588 color_ = CGColorCreate(space, color);
591 operator CGColorRef() {
597 extern "C" void UISetColor(CGColorRef color);
599 /* Random Global Variables {{{ */
600 static const int PulseInterval_ = 50000;
601 static const int ButtonBarHeight_ = 48;
602 static const float KeyboardTime_ = 0.3f;
604 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
605 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
606 #define NotifyConfig_ "/etc/notify.conf"
608 static CGColor Blue_;
609 static CGColor Blueish_;
610 static CGColor Black_;
612 static CGColor White_;
613 static CGColor Gray_;
615 static NSString *App_;
616 static NSString *Home_;
617 static BOOL Sounds_Keyboard_;
619 static BOOL Advanced_;
623 static BOOL Ignored_;
625 static UIFont *Font12_;
626 static UIFont *Font12Bold_;
627 static UIFont *Font14_;
628 static UIFont *Font18Bold_;
629 static UIFont *Font22Bold_;
631 static const char *Machine_ = NULL;
632 static const NSString *UniqueID_ = NULL;
639 CGColorSpaceRef space_;
641 #define FW_LEAST(major, minor, bugfix) \
642 (major < Major_ || major == Major_ && \
643 (minor < Minor_ || minor == Minor_ && \
649 static NSDictionary *SectionMap_;
650 static NSMutableDictionary *Metadata_;
651 static NSMutableDictionary *Indices_;
652 static _transient NSMutableDictionary *Settings_;
653 static _transient NSString *Role_;
654 static _transient NSMutableDictionary *Packages_;
655 static _transient NSMutableDictionary *Sections_;
656 static _transient NSMutableDictionary *Sources_;
657 static bool Changed_;
661 static NSMutableArray *Documents_;
664 NSString *GetLastUpdate() {
665 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
668 return @"Never or Unknown";
670 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
671 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
673 CFRelease(formatter);
675 return [(NSString *) formatted autorelease];
678 /* Display Helpers {{{ */
679 inline float Interpolate(float begin, float end, float fraction) {
680 return (end - begin) * fraction + begin;
683 NSString *SizeString(double size) {
684 bool negative = size < 0;
689 while (size > 1024) {
694 static const char *powers_[] = {"B", "kB", "MB", "GB"};
696 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
699 NSString *StripVersion(NSString *version) {
700 NSRange colon = [version rangeOfString:@":"];
701 if (colon.location != NSNotFound)
702 version = [version substringFromIndex:(colon.location + 1)];
706 NSString *Simplify(NSString *title) {
707 const char *data = [title UTF8String];
708 size_t size = [title length];
710 static Pcre square_r("^\\[(.*)\\]$");
711 if (square_r(data, size))
712 return Simplify(square_r[1]);
714 static Pcre paren_r("^\\((.*)\\)$");
715 if (paren_r(data, size))
716 return Simplify(paren_r[1]);
718 static Pcre title_r("^(.*?) \\(.*\\)$");
719 if (title_r(data, size))
720 return Simplify(title_r[1]);
726 bool isSectionVisible(NSString *section) {
727 NSDictionary *metadata = [Sections_ objectForKey:section];
728 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
729 return hidden == nil || ![hidden boolValue];
732 /* Delegate Prototypes {{{ */
736 @interface NSObject (ProgressDelegate)
739 @implementation NSObject(ProgressDelegate)
741 - (void) _setProgressError:(NSArray *)args {
742 [self performSelector:@selector(setProgressError:forPackage:)
743 withObject:[args objectAtIndex:0]
744 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
750 @protocol ProgressDelegate
751 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
752 - (void) setProgressTitle:(NSString *)title;
753 - (void) setProgressPercent:(float)percent;
754 - (void) startProgress;
755 - (void) addProgressOutput:(NSString *)output;
756 - (bool) isCancelling:(size_t)received;
759 @protocol ConfigurationDelegate
760 - (void) repairWithSelector:(SEL)selector;
761 - (void) setConfigurationData:(NSString *)data;
764 @protocol CydiaDelegate
765 - (void) installPackage:(Package *)package;
766 - (void) removePackage:(Package *)package;
767 - (void) slideUp:(UIActionSheet *)alert;
768 - (void) distUpgrade;
771 - (void) askForSettings;
772 - (UIProgressHUD *) addProgressHUD;
773 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
774 - (RVPage *) pageForPackage:(NSString *)name;
775 - (void) openMailToURL:(NSURL *)url;
776 - (void) clearFirstResponder;
780 /* Status Delegation {{{ */
782 public pkgAcquireStatus
785 _transient NSObject<ProgressDelegate> *delegate_;
793 void setDelegate(id delegate) {
794 delegate_ = delegate;
797 virtual bool MediaChange(std::string media, std::string drive) {
801 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
804 virtual void Fetch(pkgAcquire::ItemDesc &item) {
805 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
806 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
809 virtual void Done(pkgAcquire::ItemDesc &item) {
812 virtual void Fail(pkgAcquire::ItemDesc &item) {
814 item.Owner->Status == pkgAcquire::Item::StatIdle ||
815 item.Owner->Status == pkgAcquire::Item::StatDone
819 std::string &error(item.Owner->ErrorText);
823 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
824 NSArray *fields([description componentsSeparatedByString:@" "]);
825 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
827 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
828 withObject:[NSArray arrayWithObjects:
829 [NSString stringWithUTF8String:error.c_str()],
836 virtual bool Pulse(pkgAcquire *Owner) {
837 bool value = pkgAcquireStatus::Pulse(Owner);
840 double(CurrentBytes + CurrentItems) /
841 double(TotalBytes + TotalItems)
844 [delegate_ setProgressPercent:percent];
845 return [delegate_ isCancelling:CurrentBytes] ? false : value;
848 virtual void Start() {
849 [delegate_ startProgress];
852 virtual void Stop() {
856 /* Progress Delegation {{{ */
861 _transient id<ProgressDelegate> delegate_;
864 virtual void Update() {
865 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
866 [delegate_ setProgressPercent:(Percent / 100)];
875 void setDelegate(id delegate) {
876 delegate_ = delegate;
879 virtual void Done() {
880 [delegate_ setProgressPercent:1];
885 /* Database Interface {{{ */
886 @interface Database : NSObject {
888 pkgDepCache::Policy *policy_;
889 pkgRecords *records_;
890 pkgProblemResolver *resolver_;
891 pkgAcquire *fetcher_;
893 SPtr<pkgPackageManager> manager_;
894 pkgSourceList *list_;
896 NSMutableDictionary *sources_;
897 NSMutableArray *packages_;
899 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
908 + (Database *) sharedInstance;
910 - (void) _readCydia:(NSNumber *)fd;
911 - (void) _readStatus:(NSNumber *)fd;
912 - (void) _readOutput:(NSNumber *)fd;
916 - (Package *) packageWithName:(NSString *)name;
918 - (pkgCacheFile &) cache;
919 - (pkgDepCache::Policy *) policy;
920 - (pkgRecords *) records;
921 - (pkgProblemResolver *) resolver;
922 - (pkgAcquire &) fetcher;
923 - (NSArray *) packages;
924 - (NSArray *) sources;
933 - (void) updateWithStatus:(Status &)status;
935 - (void) setDelegate:(id)delegate;
936 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
940 /* Source Class {{{ */
941 @interface Source : NSObject {
942 NSString *description_;
947 NSString *distribution_;
951 NSString *defaultIcon_;
953 NSDictionary *record_;
957 - (Source *) initWithMetaIndex:(metaIndex *)index;
959 - (NSComparisonResult) compareByNameAndType:(Source *)source;
961 - (NSDictionary *) record;
965 - (NSString *) distribution;
971 - (NSString *) description;
972 - (NSString *) label;
973 - (NSString *) origin;
974 - (NSString *) version;
976 - (NSString *) defaultIcon;
980 @implementation Source
984 [distribution_ release];
987 if (description_ != nil)
988 [description_ release];
995 if (defaultIcon_ != nil)
996 [defaultIcon_ release];
1003 + (NSArray *) _attributeKeys {
1004 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1007 - (NSArray *) attributeKeys {
1008 return [[self class] _attributeKeys];
1011 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1012 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1015 - (Source *) initWithMetaIndex:(metaIndex *)index {
1016 if ((self = [super init]) != nil) {
1017 trusted_ = index->IsTrusted();
1019 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1020 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1021 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1023 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1024 if (dindex != NULL) {
1025 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1027 while (std::getline(release, line)) {
1028 std::string::size_type colon(line.find(':'));
1029 if (colon == std::string::npos)
1032 std::string name(line.substr(0, colon));
1033 std::string value(line.substr(colon + 1));
1034 while (!value.empty() && value[0] == ' ')
1035 value = value.substr(1);
1037 if (name == "Default-Icon")
1038 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1039 else if (name == "Description")
1040 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1041 else if (name == "Label")
1042 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1043 else if (name == "Origin")
1044 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1045 else if (name == "Version")
1046 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1050 record_ = [Sources_ objectForKey:[self key]];
1052 record_ = [record_ retain];
1056 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1057 NSDictionary *lhr = [self record];
1058 NSDictionary *rhr = [source record];
1061 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1063 NSString *lhs = [self name];
1064 NSString *rhs = [source name];
1066 if ([lhs length] != 0 && [rhs length] != 0) {
1067 unichar lhc = [lhs characterAtIndex:0];
1068 unichar rhc = [rhs characterAtIndex:0];
1070 if (isalpha(lhc) && !isalpha(rhc))
1071 return NSOrderedAscending;
1072 else if (!isalpha(lhc) && isalpha(rhc))
1073 return NSOrderedDescending;
1076 return [lhs compare:rhs options:LaxCompareOptions_];
1079 - (NSDictionary *) record {
1087 - (NSString *) uri {
1091 - (NSString *) distribution {
1092 return distribution_;
1095 - (NSString *) type {
1099 - (NSString *) key {
1100 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1103 - (NSString *) host {
1104 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1107 - (NSString *) name {
1108 return origin_ == nil ? [self host] : origin_;
1111 - (NSString *) description {
1112 return description_;
1115 - (NSString *) label {
1116 return label_ == nil ? [self host] : label_;
1119 - (NSString *) origin {
1123 - (NSString *) version {
1127 - (NSString *) defaultIcon {
1128 return defaultIcon_;
1133 /* Relationship Class {{{ */
1134 @interface Relationship : NSObject {
1139 - (NSString *) type;
1141 - (NSString *) name;
1145 @implementation Relationship
1153 - (NSString *) type {
1161 - (NSString *) name {
1168 /* Package Class {{{ */
1169 @interface Package : NSObject {
1170 pkgCache::PkgIterator iterator_;
1171 _transient Database *database_;
1172 pkgCache::VerIterator version_;
1173 pkgCache::VerFileIterator file_;
1181 NSString *installed_;
1187 NSString *depiction_;
1188 NSString *homepage_;
1194 NSArray *relationships_;
1197 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1198 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1200 - (pkgCache::PkgIterator) iterator;
1202 - (NSString *) section;
1203 - (NSString *) simpleSection;
1205 - (Address *) maintainer;
1207 - (NSString *) description;
1208 - (NSString *) index;
1210 - (NSMutableDictionary *) metadata;
1212 - (BOOL) subscribed;
1215 - (NSString *) latest;
1216 - (NSString *) installed;
1219 - (BOOL) upgradableAndEssential:(BOOL)essential;
1222 - (BOOL) unfiltered;
1226 - (BOOL) halfConfigured;
1227 - (BOOL) halfInstalled;
1229 - (NSString *) mode;
1232 - (NSString *) name;
1233 - (NSString *) tagline;
1235 - (NSString *) homepage;
1236 - (NSString *) depiction;
1237 - (Address *) author;
1239 - (NSArray *) files;
1240 - (NSArray *) relationships;
1241 - (NSArray *) warnings;
1242 - (NSArray *) applications;
1244 - (Source *) source;
1245 - (NSString *) role;
1246 - (NSString *) rating;
1248 - (BOOL) matches:(NSString *)text;
1250 - (bool) hasSupportingRole;
1251 - (BOOL) hasTag:(NSString *)tag;
1252 - (NSString *) primaryPurpose;
1253 - (NSArray *) purposes;
1255 - (NSComparisonResult) compareByName:(Package *)package;
1256 - (NSComparisonResult) compareBySection:(Package *)package;
1258 - (uint32_t) compareForChanges;
1263 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1264 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1265 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1266 - (NSNumber *) isVisibleInSource:(Source *)source;
1270 @implementation Package
1276 if (section_ != nil)
1280 if (installed_ != nil)
1281 [installed_ release];
1289 if (depiction_ != nil)
1290 [depiction_ release];
1291 if (homepage_ != nil)
1292 [homepage_ release];
1293 if (sponsor_ != nil)
1302 if (relationships_ != nil)
1303 [relationships_ release];
1308 + (NSArray *) _attributeKeys {
1309 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1312 - (NSArray *) attributeKeys {
1313 return [[self class] _attributeKeys];
1316 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1317 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1320 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1321 if ((self = [super init]) != nil) {
1322 iterator_ = iterator;
1323 database_ = database;
1325 version_ = [database_ policy]->GetCandidateVer(iterator_);
1326 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1327 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1329 pkgCache::VerIterator current = iterator_.CurrentVer();
1330 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1331 installed_ = [StripVersion(installed) retain];
1333 if (!version_.end())
1334 file_ = version_.FileList();
1336 pkgCache &cache([database_ cache]);
1337 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1340 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1343 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1345 const char *begin, *end;
1346 parser->GetRec(begin, end);
1348 NSString *website(nil);
1349 NSString *sponsor(nil);
1350 NSString *author(nil);
1359 {"depiction", &depiction_},
1360 {"homepage", &homepage_},
1361 {"website", &website},
1362 {"sponsor", &sponsor},
1363 {"author", &author},
1367 while (begin != end)
1368 if (*begin == '\n') {
1371 } else if (isblank(*begin)) next: {
1372 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1375 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1376 const char *name(begin);
1377 size_t size(colon - begin);
1379 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1382 const char *stop(begin == NULL ? end : begin);
1383 while (stop[-1] == '\r')
1385 while (++colon != stop && isblank(*colon));
1387 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1388 if (strncasecmp(names[i].name_, name, size) == 0) {
1389 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1390 *names[i].value_ = value;
1401 name_ = [name_ retain];
1402 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1404 icon_ = [icon_ retain];
1405 if (depiction_ != nil)
1406 depiction_ = [depiction_ retain];
1407 if (homepage_ == nil)
1408 homepage_ = website;
1409 if ([homepage_ isEqualToString:depiction_])
1411 if (homepage_ != nil)
1412 homepage_ = [homepage_ retain];
1414 sponsor_ = [[Address addressWithString:sponsor] retain];
1416 author_ = [[Address addressWithString:author] retain];
1418 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1422 for (int i(0), e([tags_ count]); i != e; ++i) {
1423 NSString *tag = [tags_ objectAtIndex:i];
1424 if ([tag hasPrefix:@"role::"]) {
1425 role_ = [[tag substringFromIndex:6] retain];
1430 NSString *solid(latest == nil ? installed : latest);
1431 bool changed(false);
1433 NSString *key([id_ lowercaseString]);
1435 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1436 if (metadata == nil) {
1437 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1442 [metadata setObject:solid forKey:@"LastVersion"];
1445 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1446 NSDate *last([metadata objectForKey:@"LastSeen"]);
1447 NSString *version([metadata objectForKey:@"LastVersion"]);
1450 first = last == nil ? now_ : last;
1451 [metadata setObject:first forKey:@"FirstSeen"];
1456 if (version == nil) {
1457 [metadata setObject:solid forKey:@"LastVersion"];
1459 } else if (![version isEqualToString:solid]) {
1460 [metadata setObject:solid forKey:@"LastVersion"];
1462 [metadata setObject:last forKey:@"LastSeen"];
1468 [Packages_ setObject:metadata forKey:key];
1474 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1475 return [[[Package alloc]
1476 initWithIterator:iterator
1481 - (pkgCache::PkgIterator) iterator {
1485 - (NSString *) section {
1486 if (section_ != nil)
1489 const char *section = iterator_.Section();
1490 if (section == NULL)
1493 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1496 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1497 if (NSString *rename = [value objectForKey:@"Rename"]) {
1502 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1506 - (NSString *) simpleSection {
1507 if (NSString *section = [self section])
1508 return Simplify(section);
1514 - (Address *) maintainer {
1517 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1518 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1522 return version_.end() ? 0 : version_->InstalledSize;
1525 - (NSString *) description {
1528 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1529 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1531 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1532 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1533 if ([lines count] < 2)
1536 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1537 for (size_t i(1); i != [lines count]; ++i) {
1538 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1539 [trimmed addObject:trim];
1542 return [trimmed componentsJoinedByString:@"\n"];
1545 - (NSString *) index {
1546 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1547 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1550 - (NSMutableDictionary *) metadata {
1551 return [Packages_ objectForKey:[id_ lowercaseString]];
1555 NSDictionary *metadata([self metadata]);
1556 if ([self subscribed])
1557 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1559 return [metadata objectForKey:@"FirstSeen"];
1562 - (BOOL) subscribed {
1563 NSDictionary *metadata([self metadata]);
1564 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1565 return [subscribed boolValue];
1571 NSDictionary *metadata([self metadata]);
1572 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1573 return [ignored boolValue];
1578 - (NSString *) latest {
1582 - (NSString *) installed {
1587 return !version_.end();
1590 - (BOOL) upgradableAndEssential:(BOOL)essential {
1591 pkgCache::VerIterator current = iterator_.CurrentVer();
1594 return essential && [self essential];
1596 return !version_.end() && version_ != current;
1599 - (BOOL) essential {
1600 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1604 return [database_ cache][iterator_].InstBroken();
1607 - (BOOL) unfiltered {
1608 NSString *section = [self section];
1609 return section == nil || isSectionVisible(section);
1613 return [self hasSupportingRole] && [self unfiltered];
1617 unsigned char current = iterator_->CurrentState;
1618 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1621 - (BOOL) halfConfigured {
1622 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1625 - (BOOL) halfInstalled {
1626 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1630 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1631 return state.Mode != pkgDepCache::ModeKeep;
1634 - (NSString *) mode {
1635 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1637 switch (state.Mode) {
1638 case pkgDepCache::ModeDelete:
1639 if ((state.iFlags & pkgDepCache::Purge) != 0)
1643 case pkgDepCache::ModeKeep:
1644 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1648 case pkgDepCache::ModeInstall:
1649 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1650 return @"Reinstall";
1651 else switch (state.Status) {
1653 return @"Downgrade";
1659 return @"New Install";
1672 - (NSString *) name {
1673 return name_ == nil ? id_ : name_;
1676 - (NSString *) tagline {
1680 - (UIImage *) icon {
1681 NSString *section = [self simpleSection];
1684 if (NSString *icon = icon_)
1685 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1686 if (icon == nil) if (section != nil)
1687 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1688 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1689 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1691 icon = [UIImage applicationImageNamed:@"unknown.png"];
1695 - (NSString *) homepage {
1699 - (NSString *) depiction {
1703 - (Address *) sponsor {
1707 - (Address *) author {
1711 - (NSArray *) files {
1712 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1713 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1716 fin.open([path UTF8String]);
1721 while (std::getline(fin, line))
1722 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1727 - (NSArray *) relationships {
1728 return relationships_;
1731 - (NSArray *) warnings {
1732 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1733 const char *name(iterator_.Name());
1735 size_t length(strlen(name));
1736 if (length < 2) invalid:
1737 [warnings addObject:@"illegal package identifier"];
1738 else for (size_t i(0); i != length; ++i)
1740 (name[i] < 'a' || name[i] > 'z') &&
1741 (name[i] < '0' || name[i] > '9') &&
1742 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1745 if (strcmp(name, "cydia") != 0) {
1749 if (NSArray *files = [self files])
1750 for (NSString *file in files)
1751 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1753 else if (!stash && [file isEqualToString:@"/var/stash"])
1757 [warnings addObject:@"files installed into Cydia.app"];
1759 [warnings addObject:@"files installed to /var/stash"];
1762 return [warnings count] == 0 ? nil : warnings;
1765 - (NSArray *) applications {
1766 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1768 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1770 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1771 if (NSArray *files = [self files])
1772 for (NSString *file in files)
1773 if (application_r(file)) {
1774 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1775 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1776 if ([id isEqualToString:me])
1779 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1781 display = application_r[1];
1783 NSString *bundle([file stringByDeletingLastPathComponent]);
1784 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1785 if (icon == nil || [icon length] == 0)
1787 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1789 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1790 [applications addObject:application];
1792 [application addObject:id];
1793 [application addObject:display];
1794 [application addObject:url];
1797 return [applications count] == 0 ? nil : applications;
1800 - (Source *) source {
1802 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1809 - (NSString *) role {
1813 - (NSString *) rating {
1814 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1815 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1820 - (BOOL) matches:(NSString *)text {
1826 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1827 if (range.location != NSNotFound)
1830 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1831 if (range.location != NSNotFound)
1834 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1835 if (range.location != NSNotFound)
1841 - (bool) hasSupportingRole {
1844 if ([role_ isEqualToString:@"enduser"])
1846 if ([Role_ isEqualToString:@"User"])
1848 if ([role_ isEqualToString:@"hacker"])
1850 if ([Role_ isEqualToString:@"Hacker"])
1852 if ([role_ isEqualToString:@"developer"])
1854 if ([Role_ isEqualToString:@"Developer"])
1859 - (BOOL) hasTag:(NSString *)tag {
1860 return tags_ == nil ? NO : [tags_ containsObject:tag];
1863 - (NSString *) primaryPurpose {
1864 for (NSString *tag in tags_)
1865 if ([tag hasPrefix:@"purpose::"])
1866 return [tag substringFromIndex:9];
1870 - (NSArray *) purposes {
1871 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1872 for (NSString *tag in tags_)
1873 if ([tag hasPrefix:@"purpose::"])
1874 [purposes addObject:[tag substringFromIndex:9]];
1875 return [purposes count] == 0 ? nil : purposes;
1878 - (NSComparisonResult) compareByName:(Package *)package {
1879 NSString *lhs = [self name];
1880 NSString *rhs = [package name];
1882 if ([lhs length] != 0 && [rhs length] != 0) {
1883 unichar lhc = [lhs characterAtIndex:0];
1884 unichar rhc = [rhs characterAtIndex:0];
1886 if (isalpha(lhc) && !isalpha(rhc))
1887 return NSOrderedAscending;
1888 else if (!isalpha(lhc) && isalpha(rhc))
1889 return NSOrderedDescending;
1892 return [lhs compare:rhs options:LaxCompareOptions_];
1895 - (NSComparisonResult) compareBySection:(Package *)package {
1896 NSString *lhs = [self section];
1897 NSString *rhs = [package section];
1899 if (lhs == NULL && rhs != NULL)
1900 return NSOrderedAscending;
1901 else if (lhs != NULL && rhs == NULL)
1902 return NSOrderedDescending;
1903 else if (lhs != NULL && rhs != NULL) {
1904 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1905 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1908 return NSOrderedSame;
1911 - (uint32_t) compareForChanges {
1916 uint32_t timestamp : 30;
1917 uint32_t ignored : 1;
1918 uint32_t upgradable : 1;
1922 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1924 if ([self upgradableAndEssential:YES]) {
1925 value.bits.timestamp = 0;
1926 value.bits.ignored = [self ignored] ? 0 : 1;
1927 value.bits.upgradable = 1;
1929 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1930 value.bits.ignored = 0;
1931 value.bits.upgradable = 0;
1934 return _not(uint32_t) - value.key;
1938 pkgProblemResolver *resolver = [database_ resolver];
1939 resolver->Clear(iterator_);
1940 resolver->Protect(iterator_);
1941 pkgCacheFile &cache([database_ cache]);
1942 cache->MarkInstall(iterator_, false);
1943 pkgDepCache::StateCache &state((*cache)[iterator_]);
1944 if (!state.Install())
1945 cache->SetReInstall(iterator_, true);
1949 pkgProblemResolver *resolver = [database_ resolver];
1950 resolver->Clear(iterator_);
1951 resolver->Protect(iterator_);
1952 resolver->Remove(iterator_);
1953 [database_ cache]->MarkDelete(iterator_, true);
1956 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1957 return [NSNumber numberWithBool:(
1958 [self unfiltered] && [self matches:search]
1962 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1963 return [NSNumber numberWithBool:(
1964 (![number boolValue] || [self visible]) && [self installed] != nil
1968 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1969 NSString *section = [self section];
1971 return [NSNumber numberWithBool:(
1973 [self installed] == nil && (
1975 section == nil && [name length] == 0 ||
1976 [name isEqualToString:section]
1981 - (NSNumber *) isVisibleInSource:(Source *)source {
1982 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1987 /* Section Class {{{ */
1988 @interface Section : NSObject {
1994 - (NSComparisonResult) compareByName:(Section *)section;
1995 - (Section *) initWithName:(NSString *)name;
1996 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1997 - (NSString *) name;
2000 - (void) addToCount;
2004 @implementation Section
2011 - (NSComparisonResult) compareByName:(Section *)section {
2012 NSString *lhs = [self name];
2013 NSString *rhs = [section name];
2015 if ([lhs length] != 0 && [rhs length] != 0) {
2016 unichar lhc = [lhs characterAtIndex:0];
2017 unichar rhc = [rhs characterAtIndex:0];
2019 if (isalpha(lhc) && !isalpha(rhc))
2020 return NSOrderedAscending;
2021 else if (!isalpha(lhc) && isalpha(rhc))
2022 return NSOrderedDescending;
2025 return [lhs compare:rhs options:LaxCompareOptions_];
2028 - (Section *) initWithName:(NSString *)name {
2029 return [self initWithName:name row:0];
2032 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2033 if ((self = [super init]) != nil) {
2034 name_ = [name retain];
2039 - (NSString *) name {
2051 - (void) addToCount {
2059 static NSArray *Finishes_;
2061 /* Database Implementation {{{ */
2062 @implementation Database
2064 + (Database *) sharedInstance {
2065 static Database *instance;
2066 if (instance == nil)
2067 instance = [[Database alloc] init];
2076 - (void) _readCydia:(NSNumber *)fd { _pooled
2077 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2078 std::istream is(&ib);
2081 static Pcre finish_r("^finish:([^:]*)$");
2083 while (std::getline(is, line)) {
2084 const char *data(line.c_str());
2085 size_t size = line.size();
2086 lprintf("C:%s\n", data);
2088 if (finish_r(data, size)) {
2089 NSString *finish = finish_r[1];
2090 int index = [Finishes_ indexOfObject:finish];
2091 if (index != INT_MAX && index > Finish_)
2099 - (void) _readStatus:(NSNumber *)fd { _pooled
2100 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2101 std::istream is(&ib);
2104 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2105 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2107 while (std::getline(is, line)) {
2108 const char *data(line.c_str());
2109 size_t size = line.size();
2110 lprintf("S:%s\n", data);
2112 if (conffile_r(data, size)) {
2113 [delegate_ setConfigurationData:conffile_r[1]];
2114 } else if (strncmp(data, "status: ", 8) == 0) {
2115 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2116 [delegate_ setProgressTitle:string];
2117 } else if (pmstatus_r(data, size)) {
2118 std::string type([pmstatus_r[1] UTF8String]);
2119 NSString *id = pmstatus_r[2];
2121 float percent([pmstatus_r[3] floatValue]);
2122 [delegate_ setProgressPercent:(percent / 100)];
2124 NSString *string = pmstatus_r[4];
2126 if (type == "pmerror")
2127 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2128 withObject:[NSArray arrayWithObjects:string, id, nil]
2131 else if (type == "pmstatus") {
2132 [delegate_ setProgressTitle:string];
2133 } else if (type == "pmconffile")
2134 [delegate_ setConfigurationData:string];
2135 else _assert(false);
2136 } else _assert(false);
2142 - (void) _readOutput:(NSNumber *)fd { _pooled
2143 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2144 std::istream is(&ib);
2147 while (std::getline(is, line)) {
2148 lprintf("O:%s\n", line.c_str());
2149 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2159 - (Package *) packageWithName:(NSString *)name {
2160 if (static_cast<pkgDepCache *>(cache_) == NULL)
2162 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2163 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2166 - (Database *) init {
2167 if ((self = [super init]) != nil) {
2174 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2175 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2179 _assert(pipe(fds) != -1);
2182 _config->Set("APT::Keep-Fds::", cydiafd_);
2183 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2186 detachNewThreadSelector:@selector(_readCydia:)
2188 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2191 _assert(pipe(fds) != -1);
2195 detachNewThreadSelector:@selector(_readStatus:)
2197 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2200 _assert(pipe(fds) != -1);
2201 _assert(dup2(fds[0], 0) != -1);
2202 _assert(close(fds[0]) != -1);
2204 input_ = fdopen(fds[1], "a");
2206 _assert(pipe(fds) != -1);
2207 _assert(dup2(fds[1], 1) != -1);
2208 _assert(close(fds[1]) != -1);
2211 detachNewThreadSelector:@selector(_readOutput:)
2213 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2218 - (pkgCacheFile &) cache {
2222 - (pkgDepCache::Policy *) policy {
2226 - (pkgRecords *) records {
2230 - (pkgProblemResolver *) resolver {
2234 - (pkgAcquire &) fetcher {
2238 - (NSArray *) packages {
2242 - (NSArray *) sources {
2243 return [sources_ allValues];
2246 - (NSArray *) issues {
2247 if (cache_->BrokenCount() == 0)
2250 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2252 for (Package *package in packages_) {
2253 if (![package broken])
2255 pkgCache::PkgIterator pkg([package iterator]);
2257 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2258 [entry addObject:[package name]];
2259 [issues addObject:entry];
2261 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2265 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2266 pkgCache::DepIterator start;
2267 pkgCache::DepIterator end;
2268 dep.GlobOr(start, end); // ++dep
2270 if (!cache_->IsImportantDep(end))
2272 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2275 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2276 [entry addObject:failure];
2277 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2279 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2280 [failure addObject:[package name]];
2282 pkgCache::PkgIterator target(start.TargetPkg());
2283 if (target->ProvidesList != 0)
2284 [failure addObject:@"?"];
2286 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2288 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2289 else if (!cache_[target].CandidateVerIter(cache_).end())
2290 [failure addObject:@"-"];
2291 else if (target->ProvidesList == 0)
2292 [failure addObject:@"!"];
2294 [failure addObject:@"%"];
2298 if (start.TargetVer() != 0)
2299 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2310 - (void) reloadData {
2330 if (!cache_.Open(progress_, true)) {
2332 if (!_error->PopMessage(error))
2335 lprintf("cache_.Open():[%s]\n", error.c_str());
2337 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2338 [delegate_ repairWithSelector:@selector(configure)];
2339 else if (error == "The package lists or status file could not be parsed or opened.")
2340 [delegate_ repairWithSelector:@selector(update)];
2341 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2342 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2343 // else if (error == "The list of sources could not be read.")
2344 else _assert(false);
2350 now_ = [[NSDate date] retain];
2352 policy_ = new pkgDepCache::Policy();
2353 records_ = new pkgRecords(cache_);
2354 resolver_ = new pkgProblemResolver(cache_);
2355 fetcher_ = new pkgAcquire(&status_);
2358 list_ = new pkgSourceList();
2359 _assert(list_->ReadMainList());
2361 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2362 _assert(pkgApplyStatus(cache_));
2364 if (cache_->BrokenCount() != 0) {
2365 _assert(pkgFixBroken(cache_));
2366 _assert(cache_->BrokenCount() == 0);
2367 _assert(pkgMinimizeUpgrade(cache_));
2370 [sources_ removeAllObjects];
2371 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2372 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2373 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2375 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2376 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2380 [packages_ removeAllObjects];
2383 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2384 if (Package *package = [Package packageWithIterator:iterator database:self])
2385 [packages_ addObject:package];
2387 [packages_ sortUsingSelector:@selector(compareByName:)];
2391 - (void) configure {
2392 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2393 system([dpkg UTF8String]);
2401 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2402 _assert(!_error->PendingError());
2405 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2408 public pkgArchiveCleaner
2411 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2416 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2418 while (_error->PopMessage(error))
2419 lprintf("ArchiveCleaner: %s\n", error.c_str());
2424 pkgRecords records(cache_);
2426 lock_ = new FileFd();
2427 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2428 _assert(!_error->PendingError());
2431 // XXX: explain this with an error message
2432 _assert(list.ReadMainList());
2434 manager_ = (_system->CreatePM(cache_));
2435 _assert(manager_->GetArchives(fetcher_, &list, &records));
2436 _assert(!_error->PendingError());
2440 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2442 _assert(list.ReadMainList());
2443 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2444 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2447 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2452 bool failed = false;
2453 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2454 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2457 std::string uri = (*item)->DescURI();
2458 std::string error = (*item)->ErrorText;
2460 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2463 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2464 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2475 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2477 if (_error->PendingError()) {
2482 if (result == pkgPackageManager::Failed) {
2487 if (result != pkgPackageManager::Completed) {
2492 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2494 _assert(list.ReadMainList());
2495 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2496 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2499 if (![before isEqualToArray:after])
2504 _assert(pkgDistUpgrade(cache_));
2508 [self updateWithStatus:status_];
2511 - (void) updateWithStatus:(Status &)status {
2513 _assert(list.ReadMainList());
2516 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2517 _assert(!_error->PendingError());
2519 pkgAcquire fetcher(&status);
2520 _assert(list.GetIndexes(&fetcher));
2522 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2523 bool failed = false;
2524 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2525 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2526 (*item)->Finished();
2530 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2531 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2532 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2535 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2540 - (void) setDelegate:(id)delegate {
2541 delegate_ = delegate;
2542 status_.setDelegate(delegate);
2543 progress_.setDelegate(delegate);
2546 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2547 pkgIndexFile *index(NULL);
2548 list_->FindIndex(file, index);
2549 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2555 /* PopUp Windows {{{ */
2556 @interface PopUpView : UIView {
2557 _transient id delegate_;
2558 UITransitionView *transition_;
2563 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2567 @implementation PopUpView
2570 [transition_ setDelegate:nil];
2571 [transition_ release];
2577 [transition_ transition:UITransitionPushFromTop toView:nil];
2580 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2581 if (from != nil && to == nil)
2582 [self removeFromSuperview];
2585 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2586 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2587 delegate_ = delegate;
2589 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2590 [self addSubview:transition_];
2592 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2594 [view addSubview:self];
2596 [transition_ setDelegate:self];
2598 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2599 [transition_ transition:UITransitionNone toView:blank];
2600 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2607 /* Mail Composition {{{ */
2608 @interface MailToView : PopUpView {
2609 MailComposeController *controller_;
2612 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2616 @implementation MailToView
2619 [controller_ release];
2623 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2627 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2628 NSLog(@"did:%@", delivery);
2629 // [UIApp setStatusBarShowsProgress:NO];
2630 if ([controller error]){
2631 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2632 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2633 [mailAlertSheet setBodyText:[controller error]];
2634 [mailAlertSheet popupAlertAnimated:YES];
2638 - (void) showError {
2639 NSLog(@"%@", [controller_ error]);
2640 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2641 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2642 [mailAlertSheet setBodyText:[controller_ error]];
2643 [mailAlertSheet popupAlertAnimated:YES];
2646 - (void) deliverMessage { _pooled
2650 if (![controller_ deliverMessage])
2651 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2654 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2655 if ([controller_ needsDelivery])
2656 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2661 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2662 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2663 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2664 [controller_ setDelegate:self];
2665 [controller_ initializeUI];
2666 [controller_ setupForURL:url];
2668 UIView *view([controller_ view]);
2669 [overlay_ addSubview:view];
2675 /* Confirmation View {{{ */
2676 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2677 if (!iterator.end())
2678 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2679 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2681 pkgCache::PkgIterator package(dep.TargetPkg());
2684 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2691 @protocol ConfirmationViewDelegate
2696 @interface ConfirmationView : BrowserView {
2697 _transient Database *database_;
2698 UIActionSheet *essential_;
2705 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2709 @implementation ConfirmationView
2716 if (essential_ != nil)
2717 [essential_ release];
2723 [book_ popFromSuperviewAnimated:YES];
2726 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2727 NSString *context([sheet context]);
2729 if ([context isEqualToString:@"remove"]) {
2737 [delegate_ confirm];
2744 } else if ([context isEqualToString:@"unable"]) {
2748 [super alertSheet:sheet buttonClicked:button];
2751 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2752 [window setValue:changes_ forKey:@"changes"];
2753 [window setValue:issues_ forKey:@"issues"];
2754 [window setValue:sizes_ forKey:@"sizes"];
2755 [super webView:sender didClearWindowObject:window forFrame:frame];
2758 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2759 if ((self = [super initWithBook:book]) != nil) {
2760 database_ = database;
2762 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2763 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2764 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2765 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2766 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2770 pkgDepCache::Policy *policy([database_ policy]);
2772 pkgCacheFile &cache([database_ cache]);
2773 NSArray *packages = [database_ packages];
2774 for (size_t i(0), e = [packages count]; i != e; ++i) {
2775 Package *package = [packages objectAtIndex:i];
2776 pkgCache::PkgIterator iterator = [package iterator];
2777 pkgDepCache::StateCache &state(cache[iterator]);
2779 NSString *name([package name]);
2781 if (state.NewInstall())
2782 [installing addObject:name];
2783 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2784 [reinstalling addObject:name];
2785 else if (state.Upgrade())
2786 [upgrading addObject:name];
2787 else if (state.Downgrade())
2788 [downgrading addObject:name];
2789 else if (state.Delete()) {
2790 if ([package essential])
2792 [removing addObject:name];
2795 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2796 substrate_ |= DepSubstrate(iterator.CurrentVer());
2801 else if (Advanced_ || true) {
2802 essential_ = [[UIActionSheet alloc]
2803 initWithTitle:@"Removing Essentials"
2804 buttons:[NSArray arrayWithObjects:
2805 @"Cancel Operation (Safe)",
2806 @"Force Removal (Unsafe)",
2808 defaultButtonIndex:0
2814 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2816 [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."];
2818 essential_ = [[UIActionSheet alloc]
2819 initWithTitle:@"Unable to Comply"
2820 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2821 defaultButtonIndex:0
2826 [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."];
2829 changes_ = [[NSArray alloc] initWithObjects:
2837 issues_ = [database_ issues];
2839 issues_ = [issues_ retain];
2841 sizes_ = [[NSArray alloc] initWithObjects:
2842 SizeString([database_ fetcher].FetchNeeded()),
2843 SizeString([database_ fetcher].PartialPresent()),
2844 SizeString([database_ cache]->UsrSize()),
2847 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2851 - (NSString *) backButtonTitle {
2855 - (NSString *) leftButtonTitle {
2859 - (id) _rightButtonTitle {
2860 #if AlwaysReload || IgnoreInstall
2863 return issues_ == nil ? @"Confirm" : nil;
2867 - (void) _leftButtonClicked {
2872 - (void) _rightButtonClicked {
2874 return [super _rightButtonClicked];
2876 if (essential_ != nil)
2877 [essential_ popupAlertAnimated:YES];
2881 [delegate_ confirm];
2889 /* Progress Data {{{ */
2890 @interface ProgressData : NSObject {
2896 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2903 @implementation ProgressData
2905 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2906 if ((self = [super init]) != nil) {
2907 selector_ = selector;
2927 /* Progress View {{{ */
2928 @interface ProgressView : UIView <
2929 ConfigurationDelegate,
2932 _transient Database *database_;
2934 UIView *background_;
2935 UITransitionView *transition_;
2937 UINavigationBar *navbar_;
2938 UIProgressBar *progress_;
2939 UITextView *output_;
2940 UITextLabel *status_;
2941 UIPushButton *close_;
2944 SHA1SumValue springlist_;
2945 SHA1SumValue notifyconf_;
2946 SHA1SumValue sandplate_;
2948 NSTimeInterval last_;
2951 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2953 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2954 - (void) setContentView:(UIView *)view;
2957 - (void) _retachThread;
2958 - (void) _detachNewThreadData:(ProgressData *)data;
2959 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2965 @protocol ProgressViewDelegate
2966 - (void) progressViewIsComplete:(ProgressView *)sender;
2969 @implementation ProgressView
2972 [transition_ setDelegate:nil];
2973 [navbar_ setDelegate:nil];
2976 if (background_ != nil)
2977 [background_ release];
2978 [transition_ release];
2981 [progress_ release];
2988 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2989 if (bootstrap_ && from == overlay_ && to == view_)
2993 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2994 if ((self = [super initWithFrame:frame]) != nil) {
2995 database_ = database;
2996 delegate_ = delegate;
2998 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2999 [transition_ setDelegate:self];
3001 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3004 [overlay_ setBackgroundColor:[UIColor blackColor]];
3006 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3007 [background_ setBackgroundColor:[UIColor blackColor]];
3008 [self addSubview:background_];
3011 [self addSubview:transition_];
3013 CGSize navsize = [UINavigationBar defaultSize];
3014 CGRect navrect = {{0, 0}, navsize};
3016 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3017 [overlay_ addSubview:navbar_];
3019 [navbar_ setBarStyle:1];
3020 [navbar_ setDelegate:self];
3022 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3023 [navbar_ pushNavigationItem:navitem];
3025 CGRect bounds = [overlay_ bounds];
3026 CGSize prgsize = [UIProgressBar defaultSize];
3029 (bounds.size.width - prgsize.width) / 2,
3030 bounds.size.height - prgsize.height - 20
3033 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3034 [progress_ setStyle:0];
3036 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3038 bounds.size.height - prgsize.height - 50,
3039 bounds.size.width - 20,
3043 [status_ setColor:[UIColor whiteColor]];
3044 [status_ setBackgroundColor:[UIColor clearColor]];
3046 [status_ setCentersHorizontally:YES];
3047 //[status_ setFont:font];
3049 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3051 navrect.size.height + 20,
3052 bounds.size.width - 20,
3053 bounds.size.height - navsize.height - 62 - navrect.size.height
3056 //[output_ setTextFont:@"Courier New"];
3057 [output_ setTextSize:12];
3059 [output_ setTextColor:[UIColor whiteColor]];
3060 [output_ setBackgroundColor:[UIColor clearColor]];
3062 [output_ setMarginTop:0];
3063 [output_ setAllowsRubberBanding:YES];
3064 [output_ setEditable:NO];
3066 [overlay_ addSubview:output_];
3068 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3070 bounds.size.height - prgsize.height - 50,
3071 bounds.size.width - 20,
3075 [close_ setAutosizesToFit:NO];
3076 [close_ setDrawsShadow:YES];
3077 [close_ setStretchBackground:YES];
3078 [close_ setEnabled:YES];
3080 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3081 [close_ setTitleFont:bold];
3083 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3084 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3085 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3089 - (void) setContentView:(UIView *)view {
3090 view_ = [view retain];
3093 - (void) resetView {
3094 [transition_ transition:6 toView:view_];
3097 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3098 NSString *context([sheet context]);
3100 if ([context isEqualToString:@"conffile"]) {
3101 FILE *input = [database_ input];
3105 fprintf(input, "N\n");
3109 fprintf(input, "Y\n");
3120 - (void) closeButtonPushed {
3129 [delegate_ suspendWithAnimation:YES];
3133 system("launchctl stop com.apple.SpringBoard");
3137 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3146 - (void) _retachThread {
3147 UINavigationItem *item = [navbar_ topItem];
3148 [item setTitle:@"Complete"];
3150 [overlay_ addSubview:close_];
3151 [progress_ removeFromSuperview];
3152 [status_ removeFromSuperview];
3154 [delegate_ progressViewIsComplete:self];
3157 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3158 MMap mmap(file, MMap::ReadOnly);
3160 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3161 if (!(sandplate_ == sha1.Result()))
3166 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3167 MMap mmap(file, MMap::ReadOnly);
3169 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3170 if (!(notifyconf_ == sha1.Result()))
3175 FileFd file(SpringBoard_, FileFd::ReadOnly);
3176 MMap mmap(file, MMap::ReadOnly);
3178 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3179 if (!(springlist_ == sha1.Result()))
3184 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3185 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3186 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3187 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3188 case 4: [close_ setTitle:@"Reboot Device"]; break;
3191 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3193 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3194 [cache autorelease];
3196 NSFileManager *manager = [NSFileManager defaultManager];
3197 NSError *error = nil;
3199 id system = [cache objectForKey:@"System"];
3204 if (stat(Cache_, &info) == -1)
3207 [system removeAllObjects];
3209 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3210 for (NSString *app in apps)
3211 if ([app hasSuffix:@".app"]) {
3212 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3213 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3214 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3216 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3217 [info setObject:path forKey:@"Path"];
3218 [info setObject:@"System" forKey:@"ApplicationType"];
3219 [system addInfoDictionary:info];
3225 [cache writeToFile:@Cache_ atomically:YES];
3227 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3229 if (chmod(Cache_, info.st_mode) == -1)
3233 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3236 notify_post("com.apple.mobile.application_installed");
3238 [delegate_ setStatusBarShowsProgress:NO];
3241 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3242 [[data target] performSelector:[data selector] withObject:[data object]];
3245 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3248 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3249 UINavigationItem *item = [navbar_ topItem];
3250 [item setTitle:title];
3252 [status_ setText:nil];
3253 [output_ setText:@""];
3254 [progress_ setProgress:0];
3257 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3259 [close_ removeFromSuperview];
3260 [overlay_ addSubview:progress_];
3261 [overlay_ addSubview:status_];
3263 [delegate_ setStatusBarShowsProgress:YES];
3267 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3268 MMap mmap(file, MMap::ReadOnly);
3270 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3271 sandplate_ = sha1.Result();
3275 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3276 MMap mmap(file, MMap::ReadOnly);
3278 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3279 notifyconf_ = sha1.Result();
3283 FileFd file(SpringBoard_, FileFd::ReadOnly);
3284 MMap mmap(file, MMap::ReadOnly);
3286 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3287 springlist_ = sha1.Result();
3290 [transition_ transition:6 toView:overlay_];
3293 detachNewThreadSelector:@selector(_detachNewThreadData:)
3295 withObject:[[ProgressData alloc]
3296 initWithSelector:selector
3303 - (void) repairWithSelector:(SEL)selector {
3305 detachNewThreadSelector:selector
3312 - (void) setConfigurationData:(NSString *)data {
3314 performSelectorOnMainThread:@selector(_setConfigurationData:)
3320 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3321 Package *package = id == nil ? nil : [database_ packageWithName:id];
3323 UIActionSheet *sheet = [[[UIActionSheet alloc]
3324 initWithTitle:(package == nil ? id : [package name])
3325 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3326 defaultButtonIndex:0
3331 [sheet setBodyText:error];
3332 [sheet popupAlertAnimated:YES];
3335 - (void) setProgressTitle:(NSString *)title {
3337 performSelectorOnMainThread:@selector(_setProgressTitle:)
3343 - (void) setProgressPercent:(float)percent {
3345 performSelectorOnMainThread:@selector(_setProgressPercent:)
3346 withObject:[NSNumber numberWithFloat:percent]
3351 - (void) startProgress {
3352 last_ = [NSDate timeIntervalSinceReferenceDate];
3355 - (void) addProgressOutput:(NSString *)output {
3357 performSelectorOnMainThread:@selector(_addProgressOutput:)
3363 - (bool) isCancelling:(size_t)received {
3365 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3366 if (received_ != received) {
3367 received_ = received;
3369 } else if (now - last_ > 30)
3376 - (void) _setConfigurationData:(NSString *)data {
3377 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3379 _assert(conffile_r(data));
3381 NSString *ofile = conffile_r[1];
3382 //NSString *nfile = conffile_r[2];
3384 UIActionSheet *sheet = [[[UIActionSheet alloc]
3385 initWithTitle:@"Configuration Upgrade"
3386 buttons:[NSArray arrayWithObjects:
3387 @"Keep My Old Copy",
3388 @"Accept The New Copy",
3389 // XXX: @"See What Changed",
3391 defaultButtonIndex:0
3396 [sheet setBodyText:[NSString stringWithFormat:
3397 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3400 [sheet popupAlertAnimated:YES];
3403 - (void) _setProgressTitle:(NSString *)title {
3404 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3405 for (size_t i(0), e([words count]); i != e; ++i) {
3406 NSString *word([words objectAtIndex:i]);
3407 if (Package *package = [database_ packageWithName:word])
3408 [words replaceObjectAtIndex:i withObject:[package name]];
3411 [status_ setText:[words componentsJoinedByString:@" "]];
3414 - (void) _setProgressPercent:(NSNumber *)percent {
3415 [progress_ setProgress:[percent floatValue]];
3418 - (void) _addProgressOutput:(NSString *)output {
3419 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3420 CGSize size = [output_ contentSize];
3421 CGRect rect = {{0, size.height}, {size.width, 0}};
3422 [output_ scrollRectToVisible:rect animated:YES];
3425 - (BOOL) isRunning {
3432 /* Package Cell {{{ */
3433 @interface PackageCell : UISimpleTableCell {
3436 NSString *description_;
3440 UITextLabel *status_;
3444 - (PackageCell *) init;
3445 - (void) setPackage:(Package *)package;
3447 + (int) heightForPackage:(Package *)package;
3451 @implementation PackageCell
3453 - (void) clearPackage {
3464 if (description_ != nil) {
3465 [description_ release];
3469 if (source_ != nil) {
3474 if (badge_ != nil) {
3481 [self clearPackage];
3488 - (PackageCell *) init {
3489 if ((self = [super init]) != nil) {
3491 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3492 [status_ setBackgroundColor:[UIColor clearColor]];
3493 [status_ setFont:small];
3498 - (void) setPackage:(Package *)package {
3499 [self clearPackage];
3501 Source *source = [package source];
3502 NSString *section = [package simpleSection];
3504 icon_ = [[package icon] retain];
3506 name_ = [[package name] retain];
3507 description_ = [[package tagline] retain];
3509 NSString *label = nil;
3510 bool trusted = false;
3512 if (source != nil) {
3513 label = [source label];
3514 trusted = [source trusted];
3515 } else if ([[package id] isEqualToString:@"firmware"])
3518 label = @"Unknown/Local";
3520 NSString *from = [NSString stringWithFormat:@"from %@", label];
3522 if (section != nil && ![section isEqualToString:label])
3523 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3525 source_ = [from retain];
3527 if (NSString *purpose = [package primaryPurpose])
3528 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3529 badge_ = [badge_ retain];
3532 if (NSString *mode = [package mode]) {
3533 [badge_ setImage:[UIImage applicationImageNamed:
3534 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3537 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3538 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3539 } else if ([package half]) {
3540 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3541 [status_ setText:@"Package Damaged"];
3542 [status_ setColor:[UIColor redColor]];
3544 [badge_ setImage:nil];
3545 [status_ setText:nil];
3550 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3553 rect.size = [icon_ size];
3555 rect.size.width /= 2;
3556 rect.size.height /= 2;
3558 rect.origin.x = 25 - rect.size.width / 2;
3559 rect.origin.y = 25 - rect.size.height / 2;
3561 [icon_ drawInRect:rect];
3564 if (badge_ != nil) {
3565 CGSize size = [badge_ size];
3567 [badge_ drawAtPoint:CGPointMake(
3568 36 - size.width / 2,
3569 36 - size.height / 2
3578 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3579 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3583 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3585 [super drawContentInRect:rect selected:selected];
3588 + (int) heightForPackage:(Package *)package {
3589 NSString *tagline([package tagline]);
3590 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3592 if ([package hasMode] || [package half])
3601 /* Section Cell {{{ */
3602 @interface SectionCell : UISimpleTableCell {
3607 _UISwitchSlider *switch_;
3612 - (void) setSection:(Section *)section editing:(BOOL)editing;
3616 @implementation SectionCell
3618 - (void) clearSection {
3619 if (section_ != nil) {
3629 if (count_ != nil) {
3636 [self clearSection];
3643 if ((self = [super init]) != nil) {
3644 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3646 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3647 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3651 - (void) onSwitch:(id)sender {
3652 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3653 if (metadata == nil) {
3654 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3655 [Sections_ setObject:metadata forKey:section_];
3659 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3662 - (void) setSection:(Section *)section editing:(BOOL)editing {
3663 if (editing != editing_) {
3665 [switch_ removeFromSuperview];
3667 [self addSubview:switch_];
3671 [self clearSection];
3673 if (section == nil) {
3674 name_ = [@"All Packages" retain];
3677 section_ = [section name];
3678 if (section_ != nil)
3679 section_ = [section_ retain];
3680 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3681 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3684 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3688 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3689 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3696 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3698 CGSize size = [count_ sizeWithFont:Font14_];
3702 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3704 [super drawContentInRect:rect selected:selected];
3710 /* File Table {{{ */
3711 @interface FileTable : RVPage {
3712 _transient Database *database_;
3715 NSMutableArray *files_;
3719 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3720 - (void) setPackage:(Package *)package;
3724 @implementation FileTable
3727 if (package_ != nil)
3736 - (int) numberOfRowsInTable:(UITable *)table {
3737 return files_ == nil ? 0 : [files_ count];
3740 - (float) table:(UITable *)table heightForRow:(int)row {
3744 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3745 if (reusing == nil) {
3746 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3747 UIFont *font = [UIFont systemFontOfSize:16];
3748 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3750 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3754 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3758 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3759 if ((self = [super initWithBook:book]) != nil) {
3760 database_ = database;
3762 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3764 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3765 [self addSubview:list_];
3767 UITableColumn *column = [[[UITableColumn alloc]
3768 initWithTitle:@"Name"
3770 width:[self frame].size.width
3773 [list_ setDataSource:self];
3774 [list_ setSeparatorStyle:1];
3775 [list_ addTableColumn:column];
3776 [list_ setDelegate:self];
3777 [list_ setReusesTableCells:YES];
3781 - (void) setPackage:(Package *)package {
3782 if (package_ != nil) {
3783 [package_ autorelease];
3792 [files_ removeAllObjects];
3794 if (package != nil) {
3795 package_ = [package retain];
3796 name_ = [[package id] retain];
3798 if (NSArray *files = [package files])
3799 [files_ addObjectsFromArray:files];
3801 if ([files_ count] != 0) {
3802 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3803 [files_ removeObjectAtIndex:0];
3804 [files_ sortUsingSelector:@selector(compareByPath:)];
3806 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3807 [stack addObject:@"/"];
3809 for (int i(0), e([files_ count]); i != e; ++i) {
3810 NSString *file = [files_ objectAtIndex:i];
3811 while (![file hasPrefix:[stack lastObject]])
3812 [stack removeLastObject];
3813 NSString *directory = [stack lastObject];
3814 [stack addObject:[file stringByAppendingString:@"/"]];
3815 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3816 ([stack count] - 2) * 3, "",
3817 [file substringFromIndex:[directory length]]
3826 - (void) resetViewAnimated:(BOOL)animated {
3827 [list_ resetViewAnimated:animated];
3830 - (void) reloadData {
3831 [self setPackage:[database_ packageWithName:name_]];
3832 [self reloadButtons];
3835 - (NSString *) title {
3836 return @"Installed Files";
3839 - (NSString *) backButtonTitle {
3845 /* Package View {{{ */
3846 @interface PackageView : BrowserView {
3847 _transient Database *database_;
3850 NSMutableArray *buttons_;
3853 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3854 - (void) setPackage:(Package *)package;
3858 @implementation PackageView
3861 if (package_ != nil)
3869 - (void) _clickButtonWithName:(NSString *)name {
3870 if ([name isEqualToString:@"Install"])
3871 [delegate_ installPackage:package_];
3872 else if ([name isEqualToString:@"Reinstall"])
3873 [delegate_ installPackage:package_];
3874 else if ([name isEqualToString:@"Remove"])
3875 [delegate_ removePackage:package_];
3876 else if ([name isEqualToString:@"Upgrade"])
3877 [delegate_ installPackage:package_];
3878 else _assert(false);
3881 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3882 NSString *context([sheet context]);
3884 if ([context isEqualToString:@"modify"]) {
3885 int count = [buttons_ count];
3886 _assert(count != 0);
3887 _assert(button <= count + 1);
3889 if (count != button - 1)
3890 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3894 [super alertSheet:sheet buttonClicked:button];
3897 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3898 return [super webView:sender didFinishLoadForFrame:frame];
3901 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3902 [window setValue:package_ forKey:@"package"];
3903 [super webView:sender didClearWindowObject:window forFrame:frame];
3907 - (void) _rightButtonClicked {
3908 /*[super _rightButtonClicked];
3911 int count = [buttons_ count];
3912 _assert(count != 0);
3915 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3917 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3918 [buttons addObjectsFromArray:buttons_];
3919 [buttons addObject:@"Cancel"];
3921 [delegate_ slideUp:[[[UIActionSheet alloc]
3924 defaultButtonIndex:2
3932 - (id) _rightButtonTitle {
3933 int count = [buttons_ count];
3934 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3937 - (NSString *) backButtonTitle {
3941 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3942 if ((self = [super initWithBook:book]) != nil) {
3943 database_ = database;
3944 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3948 - (void) setPackage:(Package *)package {
3949 if (package_ != nil) {
3950 [package_ autorelease];
3959 [buttons_ removeAllObjects];
3961 if (package != nil) {
3962 package_ = [package retain];
3963 name_ = [[package id] retain];
3965 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3967 if ([package_ source] == nil);
3968 else if ([package_ upgradableAndEssential:NO])
3969 [buttons_ addObject:@"Upgrade"];
3970 else if ([package_ installed] == nil)
3971 [buttons_ addObject:@"Install"];
3973 [buttons_ addObject:@"Reinstall"];
3974 if ([package_ installed] != nil)
3975 [buttons_ addObject:@"Remove"];
3983 - (void) reloadData {
3984 [self setPackage:[database_ packageWithName:name_]];
3985 [self reloadButtons];
3990 /* Package Table {{{ */
3991 @interface PackageTable : RVPage {
3992 _transient Database *database_;
3996 NSMutableArray *packages_;
3997 NSMutableArray *sections_;
3998 UISectionList *list_;
4001 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4003 - (void) setDelegate:(id)delegate;
4004 - (void) setObject:(id)object;
4006 - (void) reloadData;
4007 - (void) resetCursor;
4009 - (UISectionList *) list;
4011 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4015 @implementation PackageTable
4018 [list_ setDataSource:nil];
4023 [packages_ release];
4024 [sections_ release];
4029 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4030 return [sections_ count];
4033 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4034 return [[sections_ objectAtIndex:section] name];
4037 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4038 return [[sections_ objectAtIndex:section] row];
4041 - (int) numberOfRowsInTable:(UITable *)table {
4042 return [packages_ count];
4045 - (float) table:(UITable *)table heightForRow:(int)row {
4046 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4049 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4051 reusing = [[[PackageCell alloc] init] autorelease];
4052 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4056 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4060 - (void) tableRowSelected:(NSNotification *)notification {
4061 int row = [[notification object] selectedRow];
4065 Package *package = [packages_ objectAtIndex:row];
4066 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4067 [view setDelegate:delegate_];
4068 [view setPackage:package];
4069 [book_ pushPage:view];
4072 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4073 if ((self = [super initWithBook:book]) != nil) {
4074 database_ = database;
4075 title_ = [title retain];
4077 object_ = object == nil ? nil : [object retain];
4079 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4080 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4082 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4083 [list_ setDataSource:self];
4085 UITableColumn *column = [[[UITableColumn alloc]
4086 initWithTitle:@"Name"
4088 width:[self frame].size.width
4091 UITable *table = [list_ table];
4092 [table setSeparatorStyle:1];
4093 [table addTableColumn:column];
4094 [table setDelegate:self];
4095 [table setReusesTableCells:YES];
4097 [self addSubview:list_];
4100 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4101 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4105 - (void) setDelegate:(id)delegate {
4106 delegate_ = delegate;
4109 - (void) setObject:(id)object {
4115 object_ = [object retain];
4118 - (void) reloadData {
4119 NSArray *packages = [database_ packages];
4121 [packages_ removeAllObjects];
4122 [sections_ removeAllObjects];
4124 for (size_t i(0); i != [packages count]; ++i) {
4125 Package *package([packages objectAtIndex:i]);
4126 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4127 [packages_ addObject:package];
4130 Section *section = nil;
4132 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4133 Package *package = [packages_ objectAtIndex:offset];
4134 NSString *name = [package index];
4136 if (section == nil || ![[section name] isEqualToString:name]) {
4137 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4138 [sections_ addObject:section];
4141 [section addToCount];
4147 - (NSString *) title {
4151 - (void) resetViewAnimated:(BOOL)animated {
4152 [list_ resetViewAnimated:animated];
4155 - (void) resetCursor {
4156 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4159 - (UISectionList *) list {
4163 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4164 [list_ setShouldHideHeaderInShortLists:hide];
4170 /* Add Source View {{{ */
4171 @interface AddSourceView : RVPage {
4172 _transient Database *database_;
4175 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4179 @implementation AddSourceView
4181 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4182 if ((self = [super initWithBook:book]) != nil) {
4183 database_ = database;
4189 /* Source Cell {{{ */
4190 @interface SourceCell : UITableCell {
4193 NSString *description_;
4199 - (SourceCell *) initWithSource:(Source *)source;
4203 @implementation SourceCell
4208 [description_ release];
4213 - (SourceCell *) initWithSource:(Source *)source {
4214 if ((self = [super init]) != nil) {
4216 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4218 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4219 icon_ = [icon_ retain];
4221 origin_ = [[source name] retain];
4222 label_ = [[source uri] retain];
4223 description_ = [[source description] retain];
4227 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4229 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4236 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4240 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4244 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4246 [super drawContentInRect:rect selected:selected];
4251 /* Source Table {{{ */
4252 @interface SourceTable : RVPage {
4253 _transient Database *database_;
4254 UISectionList *list_;
4255 NSMutableArray *sources_;
4256 UIActionSheet *alert_;
4260 UIProgressHUD *hud_;
4263 //NSURLConnection *installer_;
4264 NSURLConnection *trivial_bz2_;
4265 NSURLConnection *trivial_gz_;
4266 //NSURLConnection *automatic_;
4271 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4275 @implementation SourceTable
4277 - (void) _deallocConnection:(NSURLConnection *)connection {
4278 if (connection != nil) {
4279 [connection cancel];
4280 //[connection setDelegate:nil];
4281 [connection release];
4286 [[list_ table] setDelegate:nil];
4287 [list_ setDataSource:nil];
4296 //[self _deallocConnection:installer_];
4297 [self _deallocConnection:trivial_gz_];
4298 [self _deallocConnection:trivial_bz2_];
4299 //[self _deallocConnection:automatic_];
4306 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4307 return offset_ == 0 ? 1 : 2;
4310 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4311 switch (section + (offset_ == 0 ? 1 : 0)) {
4312 case 0: return @"Entered by User";
4313 case 1: return @"Installed by Packages";
4321 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4322 switch (section + (offset_ == 0 ? 1 : 0)) {
4324 case 1: return offset_;
4332 - (int) numberOfRowsInTable:(UITable *)table {
4333 return [sources_ count];
4336 - (float) table:(UITable *)table heightForRow:(int)row {
4337 Source *source = [sources_ objectAtIndex:row];
4338 return [source description] == nil ? 56 : 73;
4341 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4342 Source *source = [sources_ objectAtIndex:row];
4343 // XXX: weird warning, stupid selectors ;P
4344 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4347 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4351 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4355 - (void) tableRowSelected:(NSNotification*)notification {
4356 UITable *table([list_ table]);
4357 int row([table selectedRow]);
4361 Source *source = [sources_ objectAtIndex:row];
4363 PackageTable *packages = [[[PackageTable alloc]
4366 title:[source label]
4367 filter:@selector(isVisibleInSource:)
4371 [packages setDelegate:delegate_];
4373 [book_ pushPage:packages];
4376 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4377 Source *source = [sources_ objectAtIndex:row];
4378 return [source record] != nil;
4381 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4382 [[list_ table] setDeleteConfirmationRow:row];
4385 - (void) table:(UITable *)table deleteRow:(int)row {
4386 Source *source = [sources_ objectAtIndex:row];
4387 [Sources_ removeObjectForKey:[source key]];
4388 [delegate_ syncData];
4391 - (void) _endConnection:(NSURLConnection *)connection {
4392 NSURLConnection **field = NULL;
4393 if (connection == trivial_bz2_)
4394 field = &trivial_bz2_;
4395 else if (connection == trivial_gz_)
4396 field = &trivial_gz_;
4397 _assert(field != NULL);
4398 [connection release];
4402 trivial_bz2_ == nil &&
4405 [delegate_ setStatusBarShowsProgress:NO];
4408 [hud_ removeFromSuperview];
4413 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4416 @"./", @"Distribution",
4417 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4419 [delegate_ syncData];
4420 } else if (error_ != nil) {
4421 UIActionSheet *sheet = [[[UIActionSheet alloc]
4422 initWithTitle:@"Verification Error"
4423 buttons:[NSArray arrayWithObjects:@"OK", nil]
4424 defaultButtonIndex:0
4429 [sheet setBodyText:[error_ localizedDescription]];
4430 [sheet popupAlertAnimated:YES];
4432 UIActionSheet *sheet = [[[UIActionSheet alloc]
4433 initWithTitle:@"Did not Find Repository"
4434 buttons:[NSArray arrayWithObjects:@"OK", nil]
4435 defaultButtonIndex:0
4440 [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."];
4441 [sheet popupAlertAnimated:YES];
4447 if (error_ != nil) {
4454 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4455 switch ([response statusCode]) {
4461 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4462 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4464 error_ = [error retain];
4465 [self _endConnection:connection];
4468 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4469 [self _endConnection:connection];
4472 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4473 NSMutableURLRequest *request = [NSMutableURLRequest
4474 requestWithURL:[NSURL URLWithString:href]
4475 cachePolicy:NSURLRequestUseProtocolCachePolicy
4476 timeoutInterval:20.0
4479 [request setHTTPMethod:method];
4481 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4484 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4485 NSString *context([sheet context]);
4487 if ([context isEqualToString:@"source"]) {
4490 NSString *href = [[sheet textField] text];
4492 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4494 if (![href hasSuffix:@"/"])
4495 href_ = [href stringByAppendingString:@"/"];
4498 href_ = [href_ retain];
4500 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4501 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4502 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4506 hud_ = [delegate_ addProgressHUD];
4507 [hud_ setText:@"Verifying URL"];
4518 } else if ([context isEqualToString:@"trivial"])
4520 else if ([context isEqualToString:@"urlerror"])
4524 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4525 if ((self = [super initWithBook:book]) != nil) {
4526 database_ = database;
4527 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4529 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4530 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4531 [list_ setShouldHideHeaderInShortLists:NO];
4533 [self addSubview:list_];
4534 [list_ setDataSource:self];
4536 UITableColumn *column = [[UITableColumn alloc]
4537 initWithTitle:@"Name"
4539 width:[self frame].size.width
4542 UITable *table = [list_ table];
4543 [table setSeparatorStyle:1];
4544 [table addTableColumn:column];
4545 [table setDelegate:self];
4549 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4550 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4554 - (void) reloadData {
4556 _assert(list.ReadMainList());
4558 [sources_ removeAllObjects];
4559 [sources_ addObjectsFromArray:[database_ sources]];
4561 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4564 int count = [sources_ count];
4565 for (offset_ = 0; offset_ != count; ++offset_) {
4566 Source *source = [sources_ objectAtIndex:offset_];
4567 if ([source record] == nil)
4574 - (void) resetViewAnimated:(BOOL)animated {
4575 [list_ resetViewAnimated:animated];
4578 - (void) _leftButtonClicked {
4579 /*[book_ pushPage:[[[AddSourceView alloc]
4584 UIActionSheet *sheet = [[[UIActionSheet alloc]
4585 initWithTitle:@"Enter Cydia/APT URL"
4586 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4587 defaultButtonIndex:0
4592 [sheet setNumberOfRows:1];
4594 [sheet addTextFieldWithValue:@"http://" label:@""];
4596 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4597 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4598 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4599 [traits setKeyboardType:UIKeyboardTypeURL];
4600 // XXX: UIReturnKeyDone
4601 [traits setReturnKeyType:UIReturnKeyNext];
4603 [sheet popupAlertAnimated:YES];
4606 - (void) _rightButtonClicked {
4607 UITable *table = [list_ table];
4608 BOOL editing = [table isRowDeletionEnabled];
4609 [table enableRowDeletion:!editing animated:YES];
4610 [book_ reloadButtonsForPage:self];
4613 - (NSString *) title {
4617 - (NSString *) leftButtonTitle {
4618 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4621 - (id) rightButtonTitle {
4622 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4625 - (UINavigationButtonStyle) rightButtonStyle {
4626 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4632 /* Installed View {{{ */
4633 @interface InstalledView : RVPage {
4634 _transient Database *database_;
4635 PackageTable *packages_;
4639 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4643 @implementation InstalledView
4646 [packages_ release];
4650 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4651 if ((self = [super initWithBook:book]) != nil) {
4652 database_ = database;
4654 packages_ = [[PackageTable alloc]
4658 filter:@selector(isInstalledAndVisible:)
4659 with:[NSNumber numberWithBool:YES]
4662 [self addSubview:packages_];
4664 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4665 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4669 - (void) resetViewAnimated:(BOOL)animated {
4670 [packages_ resetViewAnimated:animated];
4673 - (void) reloadData {
4674 [packages_ reloadData];
4677 - (void) _rightButtonClicked {
4678 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4679 [packages_ reloadData];
4681 [book_ reloadButtonsForPage:self];
4684 - (NSString *) title {
4685 return @"Installed";
4688 - (NSString *) backButtonTitle {
4692 - (id) rightButtonTitle {
4693 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4696 - (UINavigationButtonStyle) rightButtonStyle {
4697 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4700 - (void) setDelegate:(id)delegate {
4701 [super setDelegate:delegate];
4702 [packages_ setDelegate:delegate];
4709 @interface HomeView : BrowserView {
4714 @implementation HomeView
4716 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4717 NSString *context([sheet context]);
4719 if ([context isEqualToString:@"about"])
4722 [super alertSheet:sheet buttonClicked:button];
4725 - (void) _leftButtonClicked {
4726 UIActionSheet *sheet = [[[UIActionSheet alloc]
4727 initWithTitle:@"About Cydia Installer"
4728 buttons:[NSArray arrayWithObjects:@"Close", nil]
4729 defaultButtonIndex:0
4735 @"Copyright (C) 2008\n"
4736 "Jay Freeman (saurik)\n"
4737 "saurik@saurik.com\n"
4738 "http://www.saurik.com/\n"
4741 "http://www.theokorigroup.com/\n"
4743 "College of Creative Studies,\n"
4744 "University of California,\n"
4746 "http://www.ccs.ucsb.edu/"
4749 [sheet popupAlertAnimated:YES];
4752 - (NSString *) leftButtonTitle {
4758 /* Storage View {{{ */
4759 @interface StorageView : BrowserView {
4764 @implementation StorageView
4766 - (NSString *) title {
4771 - (id) _rightButtonTitle {
4782 /* Manage View {{{ */
4783 @interface ManageView : BrowserView {
4788 @implementation ManageView
4790 - (NSString *) title {
4794 - (void) _leftButtonClicked {
4795 [delegate_ askForSettings];
4798 - (NSString *) leftButtonTitle {
4803 - (id) _rightButtonTitle {
4815 /* Indirect Delegate {{{ */
4816 @interface IndirectDelegate : NSProxy {
4817 _transient volatile id delegate_;
4820 - (void) setDelegate:(id)delegate;
4821 - (id) initWithDelegate:(id)delegate;
4824 @implementation IndirectDelegate
4826 - (void) setDelegate:(id)delegate {
4827 delegate_ = delegate;
4830 - (id) initWithDelegate:(id)delegate {
4831 delegate_ = delegate;
4835 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4836 if (delegate_ != nil)
4837 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4839 // XXX: I fucking hate Apple so very very bad
4840 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4843 - (void) forwardInvocation:(NSInvocation *)inv {
4844 SEL sel = [inv selector];
4845 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4846 [inv invokeWithTarget:delegate_];
4852 #include <BrowserView.m>
4854 /* Cydia Book {{{ */
4855 @interface CYBook : RVBook <
4858 _transient Database *database_;
4859 UINavigationBar *overlay_;
4860 UINavigationBar *underlay_;
4861 UIProgressIndicator *indicator_;
4862 UITextLabel *prompt_;
4863 UIProgressBar *progress_;
4864 UINavigationButton *cancel_;
4867 NSTimeInterval last_;
4870 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4876 @implementation CYBook
4880 [indicator_ release];
4882 [progress_ release];
4887 - (NSString *) getTitleForPage:(RVPage *)page {
4888 return Simplify([super getTitleForPage:page]);
4896 [UIView beginAnimations:nil context:NULL];
4898 CGRect ovrframe = [overlay_ frame];
4899 ovrframe.origin.y = 0;
4900 [overlay_ setFrame:ovrframe];
4902 CGRect barframe = [navbar_ frame];
4903 barframe.origin.y += ovrframe.size.height;
4904 [navbar_ setFrame:barframe];
4906 CGRect trnframe = [transition_ frame];
4907 trnframe.origin.y += ovrframe.size.height;
4908 trnframe.size.height -= ovrframe.size.height;
4909 [transition_ setFrame:trnframe];
4911 [UIView endAnimations];
4913 [indicator_ startAnimation];
4914 [prompt_ setText:@"Updating Database"];
4915 [progress_ setProgress:0];
4918 last_ = [NSDate timeIntervalSinceReferenceDate];
4920 [overlay_ addSubview:cancel_];
4923 detachNewThreadSelector:@selector(_update)
4932 [indicator_ stopAnimation];
4934 [UIView beginAnimations:nil context:NULL];
4936 CGRect ovrframe = [overlay_ frame];
4937 ovrframe.origin.y = -ovrframe.size.height;
4938 [overlay_ setFrame:ovrframe];
4940 CGRect barframe = [navbar_ frame];
4941 barframe.origin.y -= ovrframe.size.height;
4942 [navbar_ setFrame:barframe];
4944 CGRect trnframe = [transition_ frame];
4945 trnframe.origin.y -= ovrframe.size.height;
4946 trnframe.size.height += ovrframe.size.height;
4947 [transition_ setFrame:trnframe];
4949 [UIView commitAnimations];
4951 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4954 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4955 if ((self = [super initWithFrame:frame]) != nil) {
4956 database_ = database;
4958 CGRect ovrrect = [navbar_ bounds];
4959 ovrrect.size.height = [UINavigationBar defaultSize].height;
4960 ovrrect.origin.y = -ovrrect.size.height;
4962 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4963 [self addSubview:overlay_];
4965 ovrrect.origin.y = frame.size.height;
4966 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4967 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4968 [self addSubview:underlay_];
4970 [overlay_ setBarStyle:1];
4971 [underlay_ setBarStyle:1];
4973 int barstyle = [overlay_ _barStyle:NO];
4974 bool ugly = barstyle == 0;
4976 UIProgressIndicatorStyle style = ugly ?
4977 UIProgressIndicatorStyleMediumBrown :
4978 UIProgressIndicatorStyleMediumWhite;
4980 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4981 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4982 CGRect indrect = {{indoffset, indoffset}, indsize};
4984 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4985 [indicator_ setStyle:style];
4986 [overlay_ addSubview:indicator_];
4988 CGSize prmsize = {215, indsize.height + 4};
4991 indoffset * 2 + indsize.width,
4995 unsigned(ovrrect.size.height - prmsize.height) / 2
4998 UIFont *font = [UIFont systemFontOfSize:15];
5000 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5002 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5003 [prompt_ setBackgroundColor:[UIColor clearColor]];
5004 [prompt_ setFont:font];
5006 [overlay_ addSubview:prompt_];
5008 CGSize prgsize = {75, 100};
5011 ovrrect.size.width - prgsize.width - 10,
5012 (ovrrect.size.height - prgsize.height) / 2
5015 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5016 [progress_ setStyle:0];
5017 [overlay_ addSubview:progress_];
5019 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5020 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5022 CGRect frame = [cancel_ frame];
5023 frame.size.width = 65;
5024 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5025 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5026 [cancel_ setFrame:frame];
5028 [cancel_ setBarStyle:barstyle];
5032 - (void) _onCancel {
5034 [cancel_ removeFromSuperview];
5037 - (void) _update { _pooled
5039 status.setDelegate(self);
5041 [database_ updateWithStatus:status];
5044 performSelectorOnMainThread:@selector(_update_)
5050 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5051 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5054 - (void) setProgressTitle:(NSString *)title {
5056 performSelectorOnMainThread:@selector(_setProgressTitle:)
5062 - (void) setProgressPercent:(float)percent {
5064 performSelectorOnMainThread:@selector(_setProgressPercent:)
5065 withObject:[NSNumber numberWithFloat:percent]
5070 - (void) startProgress {
5073 - (void) addProgressOutput:(NSString *)output {
5075 performSelectorOnMainThread:@selector(_addProgressOutput:)
5081 - (bool) isCancelling:(size_t)received {
5082 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5083 if (received_ != received) {
5084 received_ = received;
5086 } else if (now - last_ > 15)
5091 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5095 - (void) _setProgressTitle:(NSString *)title {
5096 [prompt_ setText:title];
5099 - (void) _setProgressPercent:(NSNumber *)percent {
5100 [progress_ setProgress:[percent floatValue]];
5103 - (void) _addProgressOutput:(NSString *)output {
5108 /* Cydia:// Protocol {{{ */
5109 @interface CydiaURLProtocol : NSURLProtocol {
5114 @implementation CydiaURLProtocol
5116 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5117 NSURL *url([request URL]);
5120 NSString *scheme([[url scheme] lowercaseString]);
5121 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5126 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5130 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5131 id<NSURLProtocolClient> client([self client]);
5132 NSData *data(UIImagePNGRepresentation(icon));
5134 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5135 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5136 [client URLProtocol:self didLoadData:data];
5137 [client URLProtocolDidFinishLoading:self];
5140 - (void) startLoading {
5141 id<NSURLProtocolClient> client([self client]);
5142 NSURLRequest *request([self request]);
5144 NSURL *url([request URL]);
5145 NSString *href([url absoluteString]);
5147 NSString *path([href substringFromIndex:8]);
5148 NSRange slash([path rangeOfString:@"/"]);
5151 if (slash.location == NSNotFound) {
5155 command = [path substringToIndex:slash.location];
5156 path = [path substringFromIndex:(slash.location + 1)];
5159 Database *database([Database sharedInstance]);
5161 if ([command isEqualToString:@"package-icon"]) {
5164 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5165 Package *package([database packageWithName:path]);
5168 UIImage *icon([package icon]);
5169 [self _returnPNGWithImage:icon forRequest:request];
5170 } else if ([command isEqualToString:@"source-icon"]) {
5173 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5174 NSString *source(Simplify(path));
5175 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5177 icon = [UIImage applicationImageNamed:@"unknown.png"];
5178 [self _returnPNGWithImage:icon forRequest:request];
5179 } else if ([command isEqualToString:@"uikit-image"]) {
5182 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5183 UIImage *icon(_UIImageWithName(path));
5184 [self _returnPNGWithImage:icon forRequest:request];
5185 } else if ([command isEqualToString:@"section-icon"]) {
5188 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5189 NSString *section(Simplify(path));
5190 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5192 icon = [UIImage applicationImageNamed:@"unknown.png"];
5193 [self _returnPNGWithImage:icon forRequest:request];
5195 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5199 - (void) stopLoading {
5205 /* Install View {{{ */
5206 @interface InstallView : RVPage {
5207 _transient Database *database_;
5208 NSMutableArray *sections_;
5209 NSMutableArray *filtered_;
5210 UITransitionView *transition_;
5216 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5217 - (void) reloadData;
5222 @implementation InstallView
5225 [list_ setDataSource:nil];
5226 [list_ setDelegate:nil];
5228 [sections_ release];
5229 [filtered_ release];
5230 [transition_ release];
5232 [accessory_ release];
5236 - (int) numberOfRowsInTable:(UITable *)table {
5237 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5240 - (float) table:(UITable *)table heightForRow:(int)row {
5244 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5246 reusing = [[[SectionCell alloc] init] autorelease];
5247 [(SectionCell *)reusing setSection:(editing_ ?
5248 [sections_ objectAtIndex:row] :
5249 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5250 ) editing:editing_];
5254 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5258 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5262 - (void) tableRowSelected:(NSNotification *)notification {
5263 int row = [[notification object] selectedRow];
5274 title = @"All Packages";
5276 section = [filtered_ objectAtIndex:(row - 1)];
5277 name = [section name];
5283 title = @"(No Section)";
5287 PackageTable *table = [[[PackageTable alloc]
5291 filter:@selector(isVisiblyUninstalledInSection:)
5295 [table setDelegate:delegate_];
5297 [book_ pushPage:table];
5300 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5301 if ((self = [super initWithBook:book]) != nil) {
5302 database_ = database;
5304 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5305 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5307 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5308 [self addSubview:transition_];
5310 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5311 [transition_ transition:0 toView:list_];
5313 UITableColumn *column = [[[UITableColumn alloc]
5314 initWithTitle:@"Name"
5316 width:[self frame].size.width
5319 [list_ setDataSource:self];
5320 [list_ setSeparatorStyle:1];
5321 [list_ addTableColumn:column];
5322 [list_ setDelegate:self];
5323 [list_ setReusesTableCells:YES];
5327 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5328 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5332 - (void) reloadData {
5333 NSArray *packages = [database_ packages];
5335 [sections_ removeAllObjects];
5336 [filtered_ removeAllObjects];
5338 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5339 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5342 for (size_t i(0); i != [packages count]; ++i) {
5343 Package *package([packages objectAtIndex:i]);
5344 NSString *name([package section]);
5347 Section *section([sections objectForKey:name]);
5348 if (section == nil) {
5349 section = [[[Section alloc] initWithName:name] autorelease];
5350 [sections setObject:section forKey:name];
5354 if ([package valid] && [package installed] == nil && [package visible])
5355 [filtered addObject:package];
5359 [sections_ addObjectsFromArray:[sections allValues]];
5360 [sections_ sortUsingSelector:@selector(compareByName:)];
5363 [filtered sortUsingSelector:@selector(compareBySection:)];
5366 Section *section = nil;
5367 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5368 Package *package = [filtered objectAtIndex:offset];
5369 NSString *name = [package section];
5371 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5372 section = name == nil ?
5373 [[[Section alloc] initWithName:nil] autorelease] :
5374 [sections objectForKey:name];
5375 [filtered_ addObject:section];
5378 [section addToCount];
5386 - (void) resetView {
5388 [self _rightButtonClicked];
5391 - (void) resetViewAnimated:(BOOL)animated {
5392 [list_ resetViewAnimated:animated];
5395 - (void) _rightButtonClicked {
5396 if ((editing_ = !editing_))
5399 [delegate_ updateData];
5400 [book_ reloadTitleForPage:self];
5401 [book_ reloadButtonsForPage:self];
5404 - (NSString *) title {
5405 return editing_ ? @"Section Visibility" : @"Install by Section";
5408 - (NSString *) backButtonTitle {
5412 - (id) rightButtonTitle {
5413 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5416 - (UINavigationButtonStyle) rightButtonStyle {
5417 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5420 - (UIView *) accessoryView {
5426 /* Changes View {{{ */
5427 @interface ChangesView : RVPage {
5428 _transient Database *database_;
5429 NSMutableArray *packages_;
5430 NSMutableArray *sections_;
5431 UISectionList *list_;
5435 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5436 - (void) reloadData;
5440 @implementation ChangesView
5443 [[list_ table] setDelegate:nil];
5444 [list_ setDataSource:nil];
5446 [packages_ release];
5447 [sections_ release];
5452 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5453 return [sections_ count];
5456 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5457 return [[sections_ objectAtIndex:section] name];
5460 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5461 return [[sections_ objectAtIndex:section] row];
5464 - (int) numberOfRowsInTable:(UITable *)table {
5465 return [packages_ count];
5468 - (float) table:(UITable *)table heightForRow:(int)row {
5469 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5472 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5474 reusing = [[[PackageCell alloc] init] autorelease];
5475 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5479 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5483 - (void) tableRowSelected:(NSNotification *)notification {
5484 int row = [[notification object] selectedRow];
5487 Package *package = [packages_ objectAtIndex:row];
5488 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5489 [view setDelegate:delegate_];
5490 [view setPackage:package];
5491 [book_ pushPage:view];
5494 - (void) _leftButtonClicked {
5495 [(CYBook *)book_ update];
5496 [self reloadButtons];
5499 - (void) _rightButtonClicked {
5500 [delegate_ distUpgrade];
5503 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5504 if ((self = [super initWithBook:book]) != nil) {
5505 database_ = database;
5507 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5508 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5510 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5511 [self addSubview:list_];
5513 [list_ setShouldHideHeaderInShortLists:NO];
5514 [list_ setDataSource:self];
5515 //[list_ setSectionListStyle:1];
5517 UITableColumn *column = [[[UITableColumn alloc]
5518 initWithTitle:@"Name"
5520 width:[self frame].size.width
5523 UITable *table = [list_ table];
5524 [table setSeparatorStyle:1];
5525 [table addTableColumn:column];
5526 [table setDelegate:self];
5527 [table setReusesTableCells:YES];
5531 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5532 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5536 - (void) reloadData {
5537 NSArray *packages = [database_ packages];
5539 [packages_ removeAllObjects];
5540 [sections_ removeAllObjects];
5543 for (size_t i(0); i != [packages count]; ++i) {
5544 Package *package([packages objectAtIndex:i]);
5547 [package installed] == nil && [package valid] && [package visible] ||
5548 [package upgradableAndEssential:NO]
5550 [packages_ addObject:package];
5554 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5557 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5558 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5559 Section *section = nil;
5563 bool unseens = false;
5565 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5568 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5569 Package *package = [packages_ objectAtIndex:offset];
5571 if (![package upgradableAndEssential:YES]) {
5573 NSDate *seen = [package seen];
5575 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5578 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5579 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5580 [sections_ addObject:section];
5584 [section addToCount];
5585 } else if ([package ignored])
5586 [ignored addToCount];
5589 [upgradable addToCount];
5594 CFRelease(formatter);
5597 Section *last = [sections_ lastObject];
5598 size_t count = [last count];
5599 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5600 [sections_ removeLastObject];
5603 if ([ignored count] != 0)
5604 [sections_ insertObject:ignored atIndex:0];
5606 [sections_ insertObject:upgradable atIndex:0];
5609 [self reloadButtons];
5612 - (void) resetViewAnimated:(BOOL)animated {
5613 [list_ resetViewAnimated:animated];
5616 - (NSString *) leftButtonTitle {
5617 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5620 - (id) rightButtonTitle {
5621 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5624 - (NSString *) title {
5630 /* Search View {{{ */
5631 @protocol SearchViewDelegate
5632 - (void) showKeyboard:(BOOL)show;
5635 @interface SearchView : RVPage {
5637 UISearchField *field_;
5638 UITransitionView *transition_;
5639 PackageTable *table_;
5640 UIPreferencesTable *advanced_;
5646 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5647 - (void) reloadData;
5651 @implementation SearchView
5654 [field_ setDelegate:nil];
5656 [accessory_ release];
5658 [transition_ release];
5660 [advanced_ release];
5665 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5669 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5671 case 0: return @"Advanced Search (Coming Soon!)";
5673 default: _assert(false);
5677 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5681 default: _assert(false);
5685 - (void) _showKeyboard:(BOOL)show {
5686 CGSize keysize = [UIKeyboard defaultSize];
5687 CGRect keydown = [book_ pageBounds];
5688 CGRect keyup = keydown;
5689 keyup.size.height -= keysize.height - ButtonBarHeight_;
5691 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5693 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5694 [animation setSignificantRectFields:8];
5697 [animation setStartFrame:keydown];
5698 [animation setEndFrame:keyup];
5700 [animation setStartFrame:keyup];
5701 [animation setEndFrame:keydown];
5704 UIAnimator *animator = [UIAnimator sharedAnimator];
5707 addAnimations:[NSArray arrayWithObjects:animation, nil]
5708 withDuration:(KeyboardTime_ - delay)
5713 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5715 [delegate_ showKeyboard:show];
5718 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5719 [self _showKeyboard:YES];
5722 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5723 [self _showKeyboard:NO];
5726 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5728 NSString *text([field_ text]);
5729 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5735 - (void) textFieldClearButtonPressed:(UITextField *)field {
5739 - (void) keyboardInputShouldDelete:(id)input {
5743 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5744 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5748 [field_ resignFirstResponder];
5753 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5754 if ((self = [super initWithBook:book]) != nil) {
5755 CGRect pageBounds = [book_ pageBounds];
5757 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5758 [self addSubview:transition_];
5760 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5762 [advanced_ setReusesTableCells:YES];
5763 [advanced_ setDataSource:self];
5764 [advanced_ reloadData];
5766 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5767 CGColor dimmed(space_, 0, 0, 0, 0.5);
5768 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5770 table_ = [[PackageTable alloc]
5774 filter:@selector(isUnfilteredAndSearchedForBy:)
5778 [table_ setShouldHideHeaderInShortLists:NO];
5779 [transition_ transition:0 toView:table_];
5788 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5795 [self bounds].size.width - area.origin.x - 18;
5797 area.size.height = [UISearchField defaultHeight];
5799 field_ = [[UISearchField alloc] initWithFrame:area];
5801 UIFont *font = [UIFont systemFontOfSize:16];
5802 [field_ setFont:font];
5804 [field_ setPlaceholder:@"Package Names & Descriptions"];
5805 [field_ setDelegate:self];
5807 [field_ setPaddingTop:5];
5809 UITextInputTraits *traits([field_ textInputTraits]);
5810 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5811 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5812 [traits setReturnKeyType:UIReturnKeySearch];
5814 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5816 accessory_ = [[UIView alloc] initWithFrame:accrect];
5817 [accessory_ addSubview:field_];
5819 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5820 [configure setShowPressFeedback:YES];
5821 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5822 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5823 [accessory_ addSubview:configure];*/
5825 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5826 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5832 LKAnimation *animation = [LKTransition animation];
5833 [animation setType:@"oglFlip"];
5834 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5835 [animation setFillMode:@"extended"];
5836 [animation setTransitionFlags:3];
5837 [animation setDuration:10];
5838 [animation setSpeed:0.35];
5839 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5840 [[transition_ _layer] addAnimation:animation forKey:0];
5841 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5842 flipped_ = !flipped_;
5846 - (void) configurePushed {
5847 [field_ resignFirstResponder];
5851 - (void) resetViewAnimated:(BOOL)animated {
5854 [table_ resetViewAnimated:animated];
5857 - (void) _reloadData {
5860 - (void) reloadData {
5863 [table_ setObject:[field_ text]];
5864 [table_ reloadData];
5865 [table_ resetCursor];
5868 - (UIView *) accessoryView {
5872 - (NSString *) title {
5876 - (NSString *) backButtonTitle {
5880 - (void) setDelegate:(id)delegate {
5881 [table_ setDelegate:delegate];
5882 [super setDelegate:delegate];
5888 @interface SettingsView : RVPage {
5889 _transient Database *database_;
5892 UIPreferencesTable *table_;
5893 _UISwitchSlider *subscribedSwitch_;
5894 _UISwitchSlider *ignoredSwitch_;
5895 UIPreferencesControlTableCell *subscribedCell_;
5896 UIPreferencesControlTableCell *ignoredCell_;
5899 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5903 @implementation SettingsView
5906 [table_ setDataSource:nil];
5909 if (package_ != nil)
5912 [subscribedSwitch_ release];
5913 [ignoredSwitch_ release];
5914 [subscribedCell_ release];
5915 [ignoredCell_ release];
5919 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5920 if (package_ == nil)
5926 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5927 if (package_ == nil)
5934 default: _assert(false);
5940 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5941 if (package_ == nil)
5948 default: _assert(false);
5954 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5955 if (package_ == nil)
5962 default: _assert(false);
5968 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5969 if (package_ == nil)
5972 _UISwitchSlider *slider([cell control]);
5973 BOOL value([slider value] != 0);
5974 NSMutableDictionary *metadata([package_ metadata]);
5977 if (NSNumber *number = [metadata objectForKey:key])
5978 before = [number boolValue];
5982 if (value != before) {
5983 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5985 [delegate_ updateData];
5989 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
5990 [self onSomething:cell withKey:@"IsSubscribed"];
5993 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
5994 [self onSomething:cell withKey:@"IsIgnored"];
5997 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
5998 if (package_ == nil)
6002 case 0: switch (row) {
6004 return subscribedCell_;
6006 return ignoredCell_;
6007 default: _assert(false);
6010 case 1: switch (row) {
6012 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6013 [cell setShowSelection:NO];
6014 [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."];
6018 default: _assert(false);
6021 default: _assert(false);
6027 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6028 if ((self = [super initWithBook:book])) {
6029 database_ = database;
6030 name_ = [package retain];
6032 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6033 [self addSubview:table_];
6035 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6036 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6038 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6039 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6041 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6042 [subscribedCell_ setShowSelection:NO];
6043 [subscribedCell_ setTitle:@"Show All Changes"];
6044 [subscribedCell_ setControl:subscribedSwitch_];
6046 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6047 [ignoredCell_ setShowSelection:NO];
6048 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6049 [ignoredCell_ setControl:ignoredSwitch_];
6051 [table_ setDataSource:self];
6056 - (void) resetViewAnimated:(BOOL)animated {
6057 [table_ resetViewAnimated:animated];
6060 - (void) reloadData {
6061 if (package_ != nil)
6062 [package_ autorelease];
6063 package_ = [database_ packageWithName:name_];
6064 if (package_ != nil) {
6066 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6067 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6070 [table_ reloadData];
6073 - (NSString *) title {
6079 /* Signature View {{{ */
6080 @interface SignatureView : BrowserView {
6081 _transient Database *database_;
6085 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6089 @implementation SignatureView
6096 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6098 [super webView:sender didClearWindowObject:window forFrame:frame];
6101 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6102 if ((self = [super initWithBook:book]) != nil) {
6103 database_ = database;
6104 package_ = [package retain];
6109 - (void) resetViewAnimated:(BOOL)animated {
6112 - (void) reloadData {
6113 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6119 @interface Cydia : UIApplication <
6120 ConfirmationViewDelegate,
6121 ProgressViewDelegate,
6130 UIToolbar *buttonbar_;
6134 NSMutableArray *essential_;
6135 NSMutableArray *broken_;
6137 Database *database_;
6138 ProgressView *progress_;
6142 UIKeyboard *keyboard_;
6143 UIProgressHUD *hud_;
6145 InstallView *install_;
6146 ChangesView *changes_;
6147 ManageView *manage_;
6148 SearchView *search_;
6153 @implementation Cydia
6156 if ([broken_ count] != 0) {
6157 int count = [broken_ count];
6159 UIActionSheet *sheet = [[[UIActionSheet alloc]
6160 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6161 buttons:[NSArray arrayWithObjects:
6163 @"Ignore (Temporary)",
6165 defaultButtonIndex:0
6170 [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."];
6171 [sheet popupAlertAnimated:YES];
6172 } else if (!Ignored_ && [essential_ count] != 0) {
6173 int count = [essential_ count];
6175 UIActionSheet *sheet = [[[UIActionSheet alloc]
6176 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6177 buttons:[NSArray arrayWithObjects:
6178 @"Upgrade Essential",
6179 @"Complete Upgrade",
6180 @"Ignore (Temporary)",
6182 defaultButtonIndex:0
6187 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6188 [sheet popupAlertAnimated:YES];
6192 - (void) _reloadData {
6193 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6194 [hud setText:@"Reloading Data"];
6195 [overlay_ addSubview:hud];
6198 [database_ reloadData];
6202 [essential_ removeAllObjects];
6203 [broken_ removeAllObjects];
6205 NSArray *packages = [database_ packages];
6206 for (Package *package in packages) {
6208 [broken_ addObject:package];
6209 if ([package upgradableAndEssential:NO]) {
6210 if ([package essential])
6211 [essential_ addObject:package];
6217 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6218 [buttonbar_ setBadgeValue:badge forButton:3];
6219 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6220 [buttonbar_ setBadgeAnimated:YES forButton:3];
6221 [self setApplicationBadge:badge];
6223 [buttonbar_ setBadgeValue:nil forButton:3];
6224 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6225 [buttonbar_ setBadgeAnimated:NO forButton:3];
6226 [self removeApplicationBadge];
6232 if ([packages count] == 0);
6244 [hud removeFromSuperview];*/
6247 - (void) _saveConfig {
6250 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6256 - (void) updateData {
6259 /* XXX: this is just stupid */
6260 if (tag_ != 2 && install_ != nil)
6261 [install_ reloadData];
6262 if (tag_ != 3 && changes_ != nil)
6263 [changes_ reloadData];
6264 if (tag_ != 5 && search_ != nil)
6265 [search_ reloadData];
6275 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6276 _assert(file != NULL);
6278 NSArray *keys = [Sources_ allKeys];
6280 for (int i(0), e([keys count]); i != e; ++i) {
6281 NSString *key = [keys objectAtIndex:i];
6282 NSDictionary *source = [Sources_ objectForKey:key];
6284 fprintf(file, "%s %s %s\n",
6285 [[source objectForKey:@"Type"] UTF8String],
6286 [[source objectForKey:@"URI"] UTF8String],
6287 [[source objectForKey:@"Distribution"] UTF8String]
6296 detachNewThreadSelector:@selector(update_)
6299 title:@"Updating Sources"
6303 - (void) reloadData {
6304 @synchronized (self) {
6305 if (confirm_ == nil)
6311 pkgProblemResolver *resolver = [database_ resolver];
6313 resolver->InstallProtect();
6314 if (!resolver->Resolve(true))
6319 [database_ prepare];
6321 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6322 [confirm_ setDelegate:self];
6324 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6325 [page setDelegate:self];
6327 [confirm_ setPage:page];
6328 [underlay_ popSubview:confirm_];
6331 - (void) installPackage:(Package *)package {
6332 @synchronized (self) {
6339 - (void) removePackage:(Package *)package {
6340 @synchronized (self) {
6347 - (void) distUpgrade {
6348 @synchronized (self) {
6349 [database_ upgrade];
6355 @synchronized (self) {
6357 if (confirm_ != nil) {
6365 [overlay_ removeFromSuperview];
6369 detachNewThreadSelector:@selector(perform)
6376 - (void) bootstrap_ {
6378 [database_ upgrade];
6379 [database_ prepare];
6380 [database_ perform];
6383 - (void) bootstrap {
6385 detachNewThreadSelector:@selector(bootstrap_)
6388 title:@"Bootstrap Install"
6392 - (void) progressViewIsComplete:(ProgressView *)progress {
6393 if (confirm_ != nil) {
6394 [underlay_ addSubview:overlay_];
6395 [confirm_ popFromSuperviewAnimated:NO];
6401 - (void) setPage:(RVPage *)page {
6402 [page resetViewAnimated:NO];
6403 [page setDelegate:self];
6404 [book_ setPage:page];
6407 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6408 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6409 [browser loadURL:url];
6413 - (void) _setHomePage {
6414 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6417 - (void) buttonBarItemTapped:(id)sender {
6418 unsigned tag = [sender tag];
6420 [book_ resetViewAnimated:YES];
6422 } else if (tag_ == 2 && tag != 2)
6423 [install_ resetView];
6426 case 1: [self _setHomePage]; break;
6428 case 2: [self setPage:install_]; break;
6429 case 3: [self setPage:changes_]; break;
6430 case 4: [self setPage:manage_]; break;
6431 case 5: [self setPage:search_]; break;
6433 default: _assert(false);
6439 - (void) applicationWillSuspend {
6441 [super applicationWillSuspend];
6444 - (void) askForSettings {
6445 UIActionSheet *role = [[[UIActionSheet alloc]
6446 initWithTitle:@"Who Are You?"
6447 buttons:[NSArray arrayWithObjects:
6448 @"User (Graphical Only)",
6449 @"Hacker (+ Command Line)",
6450 @"Developer (No Filters)",
6452 defaultButtonIndex:-1
6457 [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."];
6458 [role popupAlertAnimated:YES];
6463 [self setStatusBarShowsProgress:NO];
6466 [hud_ removeFromSuperview];
6470 pid_t pid = ExecFork();
6472 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6473 perror("launchctl stop");
6480 [self askForSettings];
6484 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6486 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6487 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6488 0, 0, screenrect.size.width, screenrect.size.height - 48
6489 ) database:database_];
6491 [book_ setDelegate:self];
6493 [overlay_ addSubview:book_];
6495 NSArray *buttonitems = [NSArray arrayWithObjects:
6496 [NSDictionary dictionaryWithObjectsAndKeys:
6497 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6498 @"home-up.png", kUIButtonBarButtonInfo,
6499 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6500 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6501 self, kUIButtonBarButtonTarget,
6502 @"Home", kUIButtonBarButtonTitle,
6503 @"0", kUIButtonBarButtonType,
6506 [NSDictionary dictionaryWithObjectsAndKeys:
6507 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6508 @"install-up.png", kUIButtonBarButtonInfo,
6509 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6510 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6511 self, kUIButtonBarButtonTarget,
6512 @"Sections", kUIButtonBarButtonTitle,
6513 @"0", kUIButtonBarButtonType,
6516 [NSDictionary dictionaryWithObjectsAndKeys:
6517 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6518 @"changes-up.png", kUIButtonBarButtonInfo,
6519 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6520 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6521 self, kUIButtonBarButtonTarget,
6522 @"Changes", kUIButtonBarButtonTitle,
6523 @"0", kUIButtonBarButtonType,
6526 [NSDictionary dictionaryWithObjectsAndKeys:
6527 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6528 @"manage-up.png", kUIButtonBarButtonInfo,
6529 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6530 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6531 self, kUIButtonBarButtonTarget,
6532 @"Manage", kUIButtonBarButtonTitle,
6533 @"0", kUIButtonBarButtonType,
6536 [NSDictionary dictionaryWithObjectsAndKeys:
6537 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6538 @"search-up.png", kUIButtonBarButtonInfo,
6539 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6540 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6541 self, kUIButtonBarButtonTarget,
6542 @"Search", kUIButtonBarButtonTitle,
6543 @"0", kUIButtonBarButtonType,
6547 buttonbar_ = [[UIToolbar alloc]
6549 withFrame:CGRectMake(
6550 0, screenrect.size.height - ButtonBarHeight_,
6551 screenrect.size.width, ButtonBarHeight_
6553 withItemList:buttonitems
6556 [buttonbar_ setDelegate:self];
6557 [buttonbar_ setBarStyle:1];
6558 [buttonbar_ setButtonBarTrackingMode:2];
6560 int buttons[5] = {1, 2, 3, 4, 5};
6561 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6562 [buttonbar_ showButtonGroup:0 withDuration:0];
6564 for (int i = 0; i != 5; ++i)
6565 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6566 i * 64 + 2, 1, 60, ButtonBarHeight_
6569 [buttonbar_ showSelectionForButton:1];
6570 [overlay_ addSubview:buttonbar_];
6572 [UIKeyboard initImplementationNow];
6573 CGSize keysize = [UIKeyboard defaultSize];
6574 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6575 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6576 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6577 [overlay_ addSubview:keyboard_];
6580 [underlay_ addSubview:overlay_];
6584 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6585 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6586 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6588 manage_ = (ManageView *) [[self
6589 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6590 withClass:[ManageView class]
6596 [self _setHomePage];
6599 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6600 NSString *context([sheet context]);
6602 if ([context isEqualToString:@"fixhalf"]) {
6605 @synchronized (self) {
6606 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6607 Package *broken = [broken_ objectAtIndex:i];
6610 NSString *id = [broken id];
6611 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6612 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6613 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6614 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6623 [broken_ removeAllObjects];
6632 } else if ([context isEqualToString:@"role"]) {
6634 case 1: Role_ = @"User"; break;
6635 case 2: Role_ = @"Hacker"; break;
6636 case 3: Role_ = @"Developer"; break;
6643 bool reset = Settings_ != nil;
6645 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6649 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6659 } else if ([context isEqualToString:@"upgrade"]) {
6662 @synchronized (self) {
6663 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6664 Package *essential = [essential_ objectAtIndex:i];
6665 [essential install];
6689 - (void) reorganize { _pooled
6690 system("/usr/libexec/cydia/free.sh");
6691 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6694 - (void) applicationSuspend:(__GSEvent *)event {
6695 if (hud_ == nil && ![progress_ isRunning])
6696 [super applicationSuspend:event];
6699 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6701 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6704 - (void) _setSuspended:(BOOL)value {
6706 [super _setSuspended:value];
6709 - (UIProgressHUD *) addProgressHUD {
6710 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6712 [underlay_ addSubview:hud];
6716 - (void) openMailToURL:(NSURL *)url {
6717 // XXX: this makes me sad
6719 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6721 [UIApp openURL:url];// asPanel:YES];
6725 - (void) clearFirstResponder {
6726 if (id responder = [window_ firstResponder])
6727 [responder resignFirstResponder];
6730 - (RVPage *) pageForPackage:(NSString *)name {
6731 if (Package *package = [database_ packageWithName:name]) {
6732 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6733 [view setPackage:package];
6736 UIActionSheet *sheet = [[[UIActionSheet alloc]
6737 initWithTitle:@"Cannot Locate Package"
6738 buttons:[NSArray arrayWithObjects:@"Close", nil]
6739 defaultButtonIndex:0
6744 [sheet setBodyText:[NSString stringWithFormat:
6745 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6748 [sheet popupAlertAnimated:YES];
6753 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6754 NSString *href = [url absoluteString];
6759 if ([href isEqualToString:@"cydia://add-source"])
6760 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6761 else if ([href isEqualToString:@"cydia://storage"])
6762 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[StorageView class]];
6763 else if ([href isEqualToString:@"cydia://sources"])
6764 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6765 else if ([href isEqualToString:@"cydia://packages"])
6766 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6767 else if ([href hasPrefix:@"cydia://url/"])
6768 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
6769 else if ([href hasPrefix:@"cydia://launch/"])
6770 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
6771 else if ([href hasPrefix:@"cydia://package-settings/"])
6772 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
6773 else if ([href hasPrefix:@"cydia://package-signature/"])
6774 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
6775 else if ([href hasPrefix:@"cydia://package/"])
6776 return [self pageForPackage:[href substringFromIndex:16]];
6777 else if ([href hasPrefix:@"cydia://files/"]) {
6778 NSString *name = [href substringFromIndex:14];
6780 if (Package *package = [database_ packageWithName:name]) {
6781 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6782 [files setPackage:package];
6790 - (void) applicationOpenURL:(NSURL *)url {
6791 [super applicationOpenURL:url];
6793 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6794 [self setPage:page];
6795 [buttonbar_ showSelectionForButton:tag];
6800 - (void) applicationDidFinishLaunching:(id)unused {
6801 Font12_ = [[UIFont systemFontOfSize:12] retain];
6802 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6803 Font14_ = [[UIFont systemFontOfSize:14] retain];
6804 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6805 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6807 _assert(pkgInitConfig(*_config));
6808 _assert(pkgInitSystem(*_config, _system));
6812 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6813 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6815 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6817 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6818 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6820 [window_ orderFront:self];
6821 [window_ makeKey:self];
6822 [window_ setHidden:NO];
6824 database_ = [Database sharedInstance];
6825 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6826 [database_ setDelegate:progress_];
6827 [window_ setContentView:progress_];
6829 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6830 [progress_ setContentView:underlay_];
6832 [progress_ resetView];
6835 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6836 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6837 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6838 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6839 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6840 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6841 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6843 [self setIdleTimerDisabled:YES];
6845 hud_ = [self addProgressHUD];
6846 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6848 [self setStatusBarShowsProgress:YES];
6851 detachNewThreadSelector:@selector(reorganize)
6859 - (void) showKeyboard:(BOOL)show {
6860 CGSize keysize = [UIKeyboard defaultSize];
6861 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6862 CGRect keyup = keydown;
6863 keyup.origin.y -= keysize.height;
6865 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6866 [animation setSignificantRectFields:2];
6869 [animation setStartFrame:keydown];
6870 [animation setEndFrame:keyup];
6871 [keyboard_ activate];
6873 [animation setStartFrame:keyup];
6874 [animation setEndFrame:keydown];
6875 [keyboard_ deactivate];
6878 [[UIAnimator sharedAnimator]
6879 addAnimations:[NSArray arrayWithObjects:animation, nil]
6880 withDuration:KeyboardTime_
6885 - (void) slideUp:(UIActionSheet *)alert {
6887 [alert presentSheetFromButtonBar:buttonbar_];
6889 [alert presentSheetInView:overlay_];
6894 void AddPreferences(NSString *plist) { _pooled
6895 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6896 _assert(settings != NULL);
6897 NSMutableArray *items = [settings objectForKey:@"items"];
6901 for (size_t i(0); i != [items count]; ++i) {
6902 NSMutableDictionary *item([items objectAtIndex:i]);
6903 NSString *label = [item objectForKey:@"label"];
6904 if (label != nil && [label isEqualToString:@"Cydia"]) {
6911 for (size_t i(0); i != [items count]; ++i) {
6912 NSDictionary *item([items objectAtIndex:i]);
6913 NSString *label = [item objectForKey:@"label"];
6914 if (label != nil && [label isEqualToString:@"General"]) {
6915 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6916 @"CydiaSettings", @"bundle",
6917 @"PSLinkCell", @"cell",
6918 [NSNumber numberWithBool:YES], @"hasIcon",
6919 [NSNumber numberWithBool:YES], @"isController",
6921 nil] atIndex:(i + 1)];
6927 _assert([settings writeToFile:plist atomically:YES] == YES);
6932 id Alloc_(id self, SEL selector) {
6933 id object = alloc_(self, selector);
6934 lprintf("[%s]A-%p\n", self->isa->name, object);
6939 id Dealloc_(id self, SEL selector) {
6940 id object = dealloc_(self, selector);
6941 lprintf("[%s]D-%p\n", self->isa->name, object);
6945 int main(int argc, char *argv[]) { _pooled
6946 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6948 bool substrate(false);
6954 for (int argi(1); argi != argc; ++argi)
6955 if (strcmp(argv[argi], "--") == 0) {
6957 argv[argi] = argv[0];
6963 for (int argi(1); argi != arge; ++argi)
6964 if (strcmp(args[argi], "--bootstrap") == 0)
6966 else if (strcmp(args[argi], "--substrate") == 0)
6969 fprintf(stderr, "unknown argument: %s\n", args[argi]);
6972 App_ = [[NSBundle mainBundle] bundlePath];
6973 Home_ = NSHomeDirectory();
6974 Locale_ = CFLocaleCopyCurrent();
6977 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6978 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6979 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6980 Sounds_Keyboard_ = [keyboard boolValue];
6986 #if 1 /* XXX: this costs 1.4s of startup performance */
6987 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6988 _assert(errno == ENOENT);
6989 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6990 _assert(errno == ENOENT);
6993 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6994 alloc_ = alloc->method_imp;
6995 alloc->method_imp = (IMP) &Alloc_;*/
6997 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6998 dealloc_ = dealloc->method_imp;
6999 dealloc->method_imp = (IMP) &Dealloc_;*/
7004 size = sizeof(maxproc);
7005 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7006 perror("sysctlbyname(\"kern.maxproc\", ?)");
7007 else if (maxproc < 64) {
7009 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7010 perror("sysctlbyname(\"kern.maxproc\", #)");
7013 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7014 char *machine = new char[size];
7015 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7016 perror("sysctlbyname(\"hw.machine\", ?)");
7020 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7022 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7023 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7025 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7026 Indices_ = [[NSMutableDictionary alloc] init];*/
7028 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7029 //@"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
7030 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7033 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7034 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7036 Settings_ = [Metadata_ objectForKey:@"Settings"];
7038 Packages_ = [Metadata_ objectForKey:@"Packages"];
7039 Sections_ = [Metadata_ objectForKey:@"Sections"];
7040 Sources_ = [Metadata_ objectForKey:@"Sources"];
7043 if (Settings_ != nil)
7044 Role_ = [Settings_ objectForKey:@"Role"];
7046 if (Packages_ == nil) {
7047 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7048 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7051 if (Sections_ == nil) {
7052 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7053 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7056 if (Sources_ == nil) {
7057 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7058 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7062 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7065 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7066 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7068 if (access("/User", F_OK) != 0)
7069 system("/usr/libexec/cydia/firmware.sh");
7071 _assert([[NSFileManager defaultManager]
7072 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7073 withIntermediateDirectories:YES
7078 space_ = CGColorSpaceCreateDeviceRGB();
7080 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7081 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7082 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7083 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7084 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7085 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7087 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7089 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7091 UIApplicationUseLegacyEvents(YES);
7092 UIKeyboardDisableAutomaticAppearance();
7094 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7096 CGColorSpaceRelease(space_);