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 @interface NSObject (Cydia)
176 - (void) yieldToSelector:(SEL)selector withObject:(id)object;
179 @implementation NSObject (Cydia)
184 - (void) _yieldToContext:(NSArray *)context { _pooled
185 SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
186 id object([[context objectAtIndex:1] nonretainedObjectValue]);
187 volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
189 [self performSelector:selector withObject:object];
194 performSelectorOnMainThread:@selector(doNothing)
200 - (void) yieldToSelector:(SEL)selector withObject:(id)object {
201 volatile bool stopped(false);
203 NSArray *context([NSArray arrayWithObjects:
204 [NSValue valueWithPointer:selector],
205 [NSValue valueWithNonretainedObject:object],
206 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
209 NSThread *thread([[[NSThread alloc]
211 selector:@selector(_yieldToContext:)
217 NSRunLoop *loop([NSRunLoop currentRunLoop]);
218 NSDate *future([NSDate distantFuture]);
220 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
225 /* NSForcedOrderingSearch doesn't work on the iPhone */
226 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
227 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
228 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
230 /* iPhoneOS 2.0 Compatibility {{{ */
232 @interface UITextView (iPhoneOS)
233 - (void) setTextSize:(float)size;
236 @implementation UITextView (iPhoneOS)
238 - (void) setTextSize:(float)size {
239 [self setFont:[[self font] fontWithSize:size]];
246 extern NSString * const kCAFilterNearest;
248 /* Information Dictionaries {{{ */
249 @interface NSMutableArray (Cydia)
250 - (void) addInfoDictionary:(NSDictionary *)info;
253 @implementation NSMutableArray (Cydia)
255 - (void) addInfoDictionary:(NSDictionary *)info {
256 [self addObject:info];
261 @interface NSMutableDictionary (Cydia)
262 - (void) addInfoDictionary:(NSDictionary *)info;
265 @implementation NSMutableDictionary (Cydia)
267 - (void) addInfoDictionary:(NSDictionary *)info {
268 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
269 [self setObject:info forKey:bundle];
274 /* Pop Transitions {{{ */
275 @interface PopTransitionView : UITransitionView {
280 @implementation PopTransitionView
282 - (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
283 if (from != nil && to == nil)
284 [self removeFromSuperview];
289 @implementation UIView (PopUpView)
291 - (void) popFromSuperviewAnimated:(BOOL)animated {
292 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
295 - (void) popSubview:(UIView *)view {
296 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
297 [transition setDelegate:transition];
298 [self addSubview:transition];
300 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
301 [transition transition:UITransitionNone toView:blank];
302 [transition transition:UITransitionPushFromBottom toView:view];
308 #define lprintf(args...) fprintf(stderr, args)
311 #define ForSaurik (1 && !ForRelease)
312 #define IgnoreInstall (0 && !ForRelease)
313 #define RecycleWebViews 0
314 #define AlwaysReload (1 && !ForRelease)
318 #define _trace(args...)
322 @interface NSMutableArray (Radix)
323 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
326 @implementation NSMutableArray (Radix)
328 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
329 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
330 [invocation setSelector:selector];
331 [invocation setArgument:&object atIndex:2];
333 size_t count([self count]);
338 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
340 for (size_t i(0); i != count; ++i) {
341 RadixItem &item(lhs[i]);
344 id object([self objectAtIndex:i]);
345 [invocation setTarget:object];
348 [invocation getReturnValue:&item.key];
351 static const size_t width = 32;
352 static const size_t bits = 11;
353 static const size_t slots = 1 << bits;
354 static const size_t passes = (width + (bits - 1)) / bits;
356 size_t *hist(new size_t[slots]);
358 for (size_t pass(0); pass != passes; ++pass) {
359 memset(hist, 0, sizeof(size_t) * slots);
361 for (size_t i(0); i != count; ++i) {
362 uint32_t key(lhs[i].key);
364 key &= _not(uint32_t) >> width - bits;
369 for (size_t i(0); i != slots; ++i) {
370 size_t local(offset);
375 for (size_t i(0); i != count; ++i) {
376 uint32_t key(lhs[i].key);
378 key &= _not(uint32_t) >> width - bits;
379 rhs[hist[key]++] = lhs[i];
389 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
390 for (size_t i(0); i != count; ++i)
391 [values addObject:[self objectAtIndex:lhs[i].index]];
392 [self setArray:values];
400 /* Apple Bug Fixes {{{ */
401 @implementation UIWebDocumentView (Cydia)
403 - (void) _setScrollerOffset:(CGPoint)offset {
404 UIScroller *scroller([self _scroller]);
406 CGSize size([scroller contentSize]);
407 CGSize bounds([scroller bounds].size);
410 max.x = size.width - bounds.width;
411 max.y = size.height - bounds.height;
419 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
420 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
422 [scroller setOffset:offset];
429 kUIControlEventMouseDown = 1 << 0,
430 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
431 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
432 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
433 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
434 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
435 } UIControlEventMasks;
437 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
438 size_t length([self length] - state->state);
441 else if (length > count)
443 for (size_t i(0); i != length; ++i)
444 objects[i] = [self item:state->state++];
445 state->itemsPtr = objects;
446 state->mutationsPtr = (unsigned long *) self;
450 @interface NSString (UIKit)
451 - (NSString *) stringByAddingPercentEscapes;
452 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
455 @interface NSString (Cydia)
456 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
457 - (NSComparisonResult) compareByPath:(NSString *)other;
460 @implementation NSString (Cydia)
462 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
463 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
466 - (NSComparisonResult) compareByPath:(NSString *)other {
467 NSString *prefix = [self commonPrefixWithString:other options:0];
468 size_t length = [prefix length];
470 NSRange lrange = NSMakeRange(length, [self length] - length);
471 NSRange rrange = NSMakeRange(length, [other length] - length);
473 lrange = [self rangeOfString:@"/" options:0 range:lrange];
474 rrange = [other rangeOfString:@"/" options:0 range:rrange];
476 NSComparisonResult value;
478 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
479 value = NSOrderedSame;
480 else if (lrange.location == NSNotFound)
481 value = NSOrderedAscending;
482 else if (rrange.location == NSNotFound)
483 value = NSOrderedDescending;
485 value = NSOrderedSame;
487 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
488 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
489 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
490 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
492 NSComparisonResult result = [lpath compare:rpath];
493 return result == NSOrderedSame ? value : result;
498 /* Perl-Compatible RegEx {{{ */
508 Pcre(const char *regex) :
513 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
516 lprintf("%d:%s\n", offset, error);
520 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
521 matches_ = new int[(capture_ + 1) * 3];
529 NSString *operator [](size_t match) {
530 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
533 bool operator ()(NSString *data) {
534 // XXX: length is for characters, not for bytes
535 return operator ()([data UTF8String], [data length]);
538 bool operator ()(const char *data, size_t size) {
540 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
544 /* Mime Addresses {{{ */
545 @interface Address : NSObject {
551 - (NSString *) address;
553 + (Address *) addressWithString:(NSString *)string;
554 - (Address *) initWithString:(NSString *)string;
557 @implementation Address
566 - (NSString *) name {
570 - (NSString *) address {
574 + (Address *) addressWithString:(NSString *)string {
575 return [[[Address alloc] initWithString:string] autorelease];
578 + (NSArray *) _attributeKeys {
579 return [NSArray arrayWithObjects:@"address", @"name", nil];
582 - (NSArray *) attributeKeys {
583 return [[self class] _attributeKeys];
586 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
587 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
590 - (Address *) initWithString:(NSString *)string {
591 if ((self = [super init]) != nil) {
592 const char *data = [string UTF8String];
593 size_t size = [string length];
595 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
597 if (address_r(data, size)) {
598 name_ = [address_r[1] retain];
599 address_ = [address_r[2] retain];
601 name_ = [string retain];
609 /* CoreGraphics Primitives {{{ */
620 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
623 Set(space, red, green, blue, alpha);
628 CGColorRelease(color_);
635 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
637 float color[] = {red, green, blue, alpha};
638 color_ = CGColorCreate(space, color);
641 operator CGColorRef() {
647 extern "C" void UISetColor(CGColorRef color);
649 /* Random Global Variables {{{ */
650 static const int PulseInterval_ = 50000;
651 static const int ButtonBarHeight_ = 48;
652 static const float KeyboardTime_ = 0.3f;
654 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
655 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
656 #define NotifyConfig_ "/etc/notify.conf"
658 static CGColor Blue_;
659 static CGColor Blueish_;
660 static CGColor Black_;
662 static CGColor White_;
663 static CGColor Gray_;
665 static NSString *App_;
666 static NSString *Home_;
667 static BOOL Sounds_Keyboard_;
669 static BOOL Advanced_;
671 static BOOL Ignored_;
673 static UIFont *Font12_;
674 static UIFont *Font12Bold_;
675 static UIFont *Font14_;
676 static UIFont *Font18Bold_;
677 static UIFont *Font22Bold_;
679 static const char *Machine_ = NULL;
680 static const NSString *UniqueID_ = nil;
681 static const NSString *Build_ = nil;
684 CGColorSpaceRef space_;
689 static NSDictionary *SectionMap_;
690 static NSMutableDictionary *Metadata_;
691 static _transient NSMutableDictionary *Settings_;
692 static _transient NSString *Role_;
693 static _transient NSMutableDictionary *Packages_;
694 static _transient NSMutableDictionary *Sections_;
695 static _transient NSMutableDictionary *Sources_;
696 static bool Changed_;
700 static NSMutableArray *Documents_;
703 NSString *GetLastUpdate() {
704 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
707 return @"Never or Unknown";
709 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
710 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
712 CFRelease(formatter);
714 return [(NSString *) formatted autorelease];
717 /* Display Helpers {{{ */
718 inline float Interpolate(float begin, float end, float fraction) {
719 return (end - begin) * fraction + begin;
722 NSString *SizeString(double size) {
723 bool negative = size < 0;
728 while (size > 1024) {
733 static const char *powers_[] = {"B", "kB", "MB", "GB"};
735 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
738 NSString *StripVersion(NSString *version) {
739 NSRange colon = [version rangeOfString:@":"];
740 if (colon.location != NSNotFound)
741 version = [version substringFromIndex:(colon.location + 1)];
745 NSString *Simplify(NSString *title) {
746 const char *data = [title UTF8String];
747 size_t size = [title length];
749 static Pcre square_r("^\\[(.*)\\]$");
750 if (square_r(data, size))
751 return Simplify(square_r[1]);
753 static Pcre paren_r("^\\((.*)\\)$");
754 if (paren_r(data, size))
755 return Simplify(paren_r[1]);
757 static Pcre title_r("^(.*?) \\(.*\\)$");
758 if (title_r(data, size))
759 return Simplify(title_r[1]);
765 bool isSectionVisible(NSString *section) {
766 NSDictionary *metadata = [Sections_ objectForKey:section];
767 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
768 return hidden == nil || ![hidden boolValue];
771 /* Delegate Prototypes {{{ */
775 @interface NSObject (ProgressDelegate)
778 @implementation NSObject(ProgressDelegate)
780 - (void) _setProgressError:(NSArray *)args {
781 [self performSelector:@selector(setProgressError:forPackage:)
782 withObject:[args objectAtIndex:0]
783 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
789 @protocol ProgressDelegate
790 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
791 - (void) setProgressTitle:(NSString *)title;
792 - (void) setProgressPercent:(float)percent;
793 - (void) startProgress;
794 - (void) addProgressOutput:(NSString *)output;
795 - (bool) isCancelling:(size_t)received;
798 @protocol ConfigurationDelegate
799 - (void) repairWithSelector:(SEL)selector;
800 - (void) setConfigurationData:(NSString *)data;
803 @protocol CydiaDelegate
804 - (void) installPackage:(Package *)package;
805 - (void) removePackage:(Package *)package;
806 - (void) slideUp:(UIActionSheet *)alert;
807 - (void) distUpgrade;
810 - (void) askForSettings;
811 - (UIProgressHUD *) addProgressHUD;
812 - (void) removeProgressHUD:(UIProgressHUD *)hud;
813 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
814 - (RVPage *) pageForPackage:(NSString *)name;
815 - (void) openMailToURL:(NSURL *)url;
816 - (void) clearFirstResponder;
820 /* Status Delegation {{{ */
822 public pkgAcquireStatus
825 _transient NSObject<ProgressDelegate> *delegate_;
833 void setDelegate(id delegate) {
834 delegate_ = delegate;
837 virtual bool MediaChange(std::string media, std::string drive) {
841 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
844 virtual void Fetch(pkgAcquire::ItemDesc &item) {
845 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
846 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
849 virtual void Done(pkgAcquire::ItemDesc &item) {
852 virtual void Fail(pkgAcquire::ItemDesc &item) {
854 item.Owner->Status == pkgAcquire::Item::StatIdle ||
855 item.Owner->Status == pkgAcquire::Item::StatDone
859 std::string &error(item.Owner->ErrorText);
863 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
864 NSArray *fields([description componentsSeparatedByString:@" "]);
865 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
867 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
868 withObject:[NSArray arrayWithObjects:
869 [NSString stringWithUTF8String:error.c_str()],
876 virtual bool Pulse(pkgAcquire *Owner) {
877 bool value = pkgAcquireStatus::Pulse(Owner);
880 double(CurrentBytes + CurrentItems) /
881 double(TotalBytes + TotalItems)
884 [delegate_ setProgressPercent:percent];
885 return [delegate_ isCancelling:CurrentBytes] ? false : value;
888 virtual void Start() {
889 [delegate_ startProgress];
892 virtual void Stop() {
896 /* Progress Delegation {{{ */
901 _transient id<ProgressDelegate> delegate_;
904 virtual void Update() {
905 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
906 [delegate_ setProgressPercent:(Percent / 100)];*/
915 void setDelegate(id delegate) {
916 delegate_ = delegate;
919 virtual void Done() {
920 //[delegate_ setProgressPercent:1];
925 /* Database Interface {{{ */
926 @interface Database : NSObject {
928 pkgDepCache::Policy *policy_;
929 pkgRecords *records_;
930 pkgProblemResolver *resolver_;
931 pkgAcquire *fetcher_;
933 SPtr<pkgPackageManager> manager_;
934 pkgSourceList *list_;
936 NSMutableDictionary *sources_;
937 NSMutableArray *packages_;
939 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
948 + (Database *) sharedInstance;
950 - (void) _readCydia:(NSNumber *)fd;
951 - (void) _readStatus:(NSNumber *)fd;
952 - (void) _readOutput:(NSNumber *)fd;
956 - (Package *) packageWithName:(NSString *)name;
958 - (pkgCacheFile &) cache;
959 - (pkgDepCache::Policy *) policy;
960 - (pkgRecords *) records;
961 - (pkgProblemResolver *) resolver;
962 - (pkgAcquire &) fetcher;
963 - (pkgSourceList &) list;
964 - (NSArray *) packages;
965 - (NSArray *) sources;
974 - (void) updateWithStatus:(Status &)status;
976 - (void) setDelegate:(id)delegate;
977 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
981 /* Source Class {{{ */
982 @interface Source : NSObject {
983 NSString *description_;
988 NSString *distribution_;
992 NSString *defaultIcon_;
994 NSDictionary *record_;
998 - (Source *) initWithMetaIndex:(metaIndex *)index;
1000 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1002 - (NSDictionary *) record;
1006 - (NSString *) distribution;
1007 - (NSString *) type;
1009 - (NSString *) host;
1011 - (NSString *) name;
1012 - (NSString *) description;
1013 - (NSString *) label;
1014 - (NSString *) origin;
1015 - (NSString *) version;
1017 - (NSString *) defaultIcon;
1021 @implementation Source
1023 #define _clear(field) \
1030 _clear(distribution_)
1033 _clear(description_)
1037 _clear(defaultIcon_)
1046 + (NSArray *) _attributeKeys {
1047 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1050 - (NSArray *) attributeKeys {
1051 return [[self class] _attributeKeys];
1054 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1055 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1058 - (void) setMetaIndex:(metaIndex *)index {
1061 trusted_ = index->IsTrusted();
1063 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1064 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1065 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1067 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1068 if (dindex != NULL) {
1069 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1071 while (std::getline(release, line)) {
1072 std::string::size_type colon(line.find(':'));
1073 if (colon == std::string::npos)
1076 std::string name(line.substr(0, colon));
1077 std::string value(line.substr(colon + 1));
1078 while (!value.empty() && value[0] == ' ')
1079 value = value.substr(1);
1081 if (name == "Default-Icon")
1082 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1083 else if (name == "Description")
1084 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1085 else if (name == "Label")
1086 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1087 else if (name == "Origin")
1088 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1089 else if (name == "Version")
1090 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1094 record_ = [Sources_ objectForKey:[self key]];
1096 record_ = [record_ retain];
1099 - (Source *) initWithMetaIndex:(metaIndex *)index {
1100 if ((self = [super init]) != nil) {
1101 [self setMetaIndex:index];
1105 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1106 NSDictionary *lhr = [self record];
1107 NSDictionary *rhr = [source record];
1110 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1112 NSString *lhs = [self name];
1113 NSString *rhs = [source name];
1115 if ([lhs length] != 0 && [rhs length] != 0) {
1116 unichar lhc = [lhs characterAtIndex:0];
1117 unichar rhc = [rhs characterAtIndex:0];
1119 if (isalpha(lhc) && !isalpha(rhc))
1120 return NSOrderedAscending;
1121 else if (!isalpha(lhc) && isalpha(rhc))
1122 return NSOrderedDescending;
1125 return [lhs compare:rhs options:LaxCompareOptions_];
1128 - (NSDictionary *) record {
1136 - (NSString *) uri {
1140 - (NSString *) distribution {
1141 return distribution_;
1144 - (NSString *) type {
1148 - (NSString *) key {
1149 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1152 - (NSString *) host {
1153 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1156 - (NSString *) name {
1157 return origin_ == nil ? [self host] : origin_;
1160 - (NSString *) description {
1161 return description_;
1164 - (NSString *) label {
1165 return label_ == nil ? [self host] : label_;
1168 - (NSString *) origin {
1172 - (NSString *) version {
1176 - (NSString *) defaultIcon {
1177 return defaultIcon_;
1182 /* Relationship Class {{{ */
1183 @interface Relationship : NSObject {
1188 - (NSString *) type;
1190 - (NSString *) name;
1194 @implementation Relationship
1202 - (NSString *) type {
1210 - (NSString *) name {
1217 /* Package Class {{{ */
1218 @interface Package : NSObject {
1219 pkgCache::PkgIterator iterator_;
1220 _transient Database *database_;
1221 pkgCache::VerIterator version_;
1222 pkgCache::VerFileIterator file_;
1230 NSString *installed_;
1236 NSString *depiction_;
1237 NSString *homepage_;
1243 NSArray *relationships_;
1246 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1247 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1249 - (pkgCache::PkgIterator) iterator;
1251 - (NSString *) section;
1252 - (NSString *) simpleSection;
1256 - (Address *) maintainer;
1258 - (NSString *) description;
1259 - (NSString *) index;
1261 - (NSMutableDictionary *) metadata;
1263 - (BOOL) subscribed;
1266 - (NSString *) latest;
1267 - (NSString *) installed;
1270 - (BOOL) upgradableAndEssential:(BOOL)essential;
1273 - (BOOL) unfiltered;
1277 - (BOOL) halfConfigured;
1278 - (BOOL) halfInstalled;
1280 - (NSString *) mode;
1283 - (NSString *) name;
1284 - (NSString *) tagline;
1286 - (NSString *) homepage;
1287 - (NSString *) depiction;
1288 - (Address *) author;
1290 - (NSArray *) files;
1291 - (NSArray *) relationships;
1292 - (NSArray *) warnings;
1293 - (NSArray *) applications;
1295 - (Source *) source;
1296 - (NSString *) role;
1298 - (BOOL) matches:(NSString *)text;
1300 - (bool) hasSupportingRole;
1301 - (BOOL) hasTag:(NSString *)tag;
1302 - (NSString *) primaryPurpose;
1303 - (NSArray *) purposes;
1305 - (NSComparisonResult) compareByName:(Package *)package;
1306 - (NSComparisonResult) compareBySection:(Package *)package;
1308 - (uint32_t) compareForChanges;
1313 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1314 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1315 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1316 - (NSNumber *) isVisibleInSource:(Source *)source;
1320 @implementation Package
1326 if (section_ != nil)
1330 if (installed_ != nil)
1331 [installed_ release];
1339 if (depiction_ != nil)
1340 [depiction_ release];
1341 if (homepage_ != nil)
1342 [homepage_ release];
1343 if (sponsor_ != nil)
1352 if (relationships_ != nil)
1353 [relationships_ release];
1358 + (NSArray *) _attributeKeys {
1359 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1362 - (NSArray *) attributeKeys {
1363 return [[self class] _attributeKeys];
1366 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1367 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1370 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1371 if ((self = [super init]) != nil) {
1372 iterator_ = iterator;
1373 database_ = database;
1375 version_ = [database_ policy]->GetCandidateVer(iterator_);
1376 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1377 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1379 pkgCache::VerIterator current = iterator_.CurrentVer();
1380 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1381 installed_ = [StripVersion(installed) retain];
1383 if (!version_.end())
1384 file_ = version_.FileList();
1386 pkgCache &cache([database_ cache]);
1387 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1390 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1393 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1395 const char *begin, *end;
1396 parser->GetRec(begin, end);
1398 NSString *website(nil);
1399 NSString *sponsor(nil);
1400 NSString *author(nil);
1409 {"depiction", &depiction_},
1410 {"homepage", &homepage_},
1411 {"website", &website},
1412 {"sponsor", &sponsor},
1413 {"author", &author},
1417 while (begin != end)
1418 if (*begin == '\n') {
1421 } else if (isblank(*begin)) next: {
1422 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1425 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1426 const char *name(begin);
1427 size_t size(colon - begin);
1429 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1432 const char *stop(begin == NULL ? end : begin);
1433 while (stop[-1] == '\r')
1435 while (++colon != stop && isblank(*colon));
1437 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1438 if (strncasecmp(names[i].name_, name, size) == 0) {
1439 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1440 *names[i].value_ = value;
1451 name_ = [name_ retain];
1452 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1454 icon_ = [icon_ retain];
1455 if (depiction_ != nil)
1456 depiction_ = [depiction_ retain];
1457 if (homepage_ == nil)
1458 homepage_ = website;
1459 if ([homepage_ isEqualToString:depiction_])
1461 if (homepage_ != nil)
1462 homepage_ = [homepage_ retain];
1464 sponsor_ = [[Address addressWithString:sponsor] retain];
1466 author_ = [[Address addressWithString:author] retain];
1468 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1472 for (int i(0), e([tags_ count]); i != e; ++i) {
1473 NSString *tag = [tags_ objectAtIndex:i];
1474 if ([tag hasPrefix:@"role::"]) {
1475 role_ = [[tag substringFromIndex:6] retain];
1480 NSString *solid(latest == nil ? installed : latest);
1481 bool changed(false);
1483 NSString *key([id_ lowercaseString]);
1485 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1486 if (metadata == nil) {
1487 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1492 [metadata setObject:solid forKey:@"LastVersion"];
1495 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1496 NSDate *last([metadata objectForKey:@"LastSeen"]);
1497 NSString *version([metadata objectForKey:@"LastVersion"]);
1500 first = last == nil ? now_ : last;
1501 [metadata setObject:first forKey:@"FirstSeen"];
1506 if (version == nil) {
1507 [metadata setObject:solid forKey:@"LastVersion"];
1509 } else if (![version isEqualToString:solid]) {
1510 [metadata setObject:solid forKey:@"LastVersion"];
1512 [metadata setObject:last forKey:@"LastSeen"];
1518 [Packages_ setObject:metadata forKey:key];
1524 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1525 return [[[Package alloc]
1526 initWithIterator:iterator
1531 - (pkgCache::PkgIterator) iterator {
1535 - (NSString *) section {
1536 if (section_ != nil)
1539 const char *section = iterator_.Section();
1540 if (section == NULL)
1543 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1546 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1547 if (NSString *rename = [value objectForKey:@"Rename"]) {
1552 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1556 - (NSString *) simpleSection {
1557 if (NSString *section = [self section])
1558 return Simplify(section);
1563 - (NSString *) uri {
1566 pkgIndexFile *index;
1567 pkgCache::PkgFileIterator file(file_.File());
1568 if (![database_ list].FindIndex(file, index))
1570 return [NSString stringWithUTF8String:iterator_->Path];
1571 //return [NSString stringWithUTF8String:file.Site()];
1572 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1576 - (Address *) maintainer {
1579 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1580 const std::string &maintainer(parser->Maintainer());
1581 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1585 return version_.end() ? 0 : version_->InstalledSize;
1588 - (NSString *) description {
1591 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1592 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1594 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1595 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1596 if ([lines count] < 2)
1599 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1600 for (size_t i(1); i != [lines count]; ++i) {
1601 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1602 [trimmed addObject:trim];
1605 return [trimmed componentsJoinedByString:@"\n"];
1608 - (NSString *) index {
1609 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1610 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1613 - (NSMutableDictionary *) metadata {
1614 return [Packages_ objectForKey:[id_ lowercaseString]];
1618 NSDictionary *metadata([self metadata]);
1619 if ([self subscribed])
1620 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1622 return [metadata objectForKey:@"FirstSeen"];
1625 - (BOOL) subscribed {
1626 NSDictionary *metadata([self metadata]);
1627 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1628 return [subscribed boolValue];
1634 NSDictionary *metadata([self metadata]);
1635 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1636 return [ignored boolValue];
1641 - (NSString *) latest {
1645 - (NSString *) installed {
1650 return !version_.end();
1653 - (BOOL) upgradableAndEssential:(BOOL)essential {
1654 pkgCache::VerIterator current = iterator_.CurrentVer();
1658 value = essential && [self essential] && [self visible];
1660 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1664 - (BOOL) essential {
1665 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1669 return [database_ cache][iterator_].InstBroken();
1672 - (BOOL) unfiltered {
1673 NSString *section = [self section];
1674 return section == nil || isSectionVisible(section);
1678 return [self hasSupportingRole] && [self unfiltered];
1682 unsigned char current = iterator_->CurrentState;
1683 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1686 - (BOOL) halfConfigured {
1687 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1690 - (BOOL) halfInstalled {
1691 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1695 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1696 return state.Mode != pkgDepCache::ModeKeep;
1699 - (NSString *) mode {
1700 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1702 switch (state.Mode) {
1703 case pkgDepCache::ModeDelete:
1704 if ((state.iFlags & pkgDepCache::Purge) != 0)
1708 case pkgDepCache::ModeKeep:
1709 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1713 case pkgDepCache::ModeInstall:
1714 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1715 return @"Reinstall";
1716 else switch (state.Status) {
1718 return @"Downgrade";
1724 return @"New Install";
1737 - (NSString *) name {
1738 return name_ == nil ? id_ : name_;
1741 - (NSString *) tagline {
1745 - (UIImage *) icon {
1746 NSString *section = [self simpleSection];
1750 if ([icon_ hasPrefix:@"file:///"])
1751 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1752 if (icon == nil) if (section != nil)
1753 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1754 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1755 if ([dicon hasPrefix:@"file:///"])
1756 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1758 icon = [UIImage applicationImageNamed:@"unknown.png"];
1762 - (NSString *) homepage {
1766 - (NSString *) depiction {
1770 - (Address *) sponsor {
1774 - (Address *) author {
1778 - (NSArray *) files {
1779 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1780 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1783 fin.open([path UTF8String]);
1788 while (std::getline(fin, line))
1789 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1794 - (NSArray *) relationships {
1795 return relationships_;
1798 - (NSArray *) warnings {
1799 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1800 const char *name(iterator_.Name());
1802 size_t length(strlen(name));
1803 if (length < 2) invalid:
1804 [warnings addObject:@"illegal package identifier"];
1805 else for (size_t i(0); i != length; ++i)
1807 /* XXX: technically this is not allowed */
1808 (name[i] < 'A' || name[i] > 'Z') &&
1809 (name[i] < 'a' || name[i] > 'z') &&
1810 (name[i] < '0' || name[i] > '9') &&
1811 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1814 if (strcmp(name, "cydia") != 0) {
1816 bool _private = false;
1819 bool repository = [[self section] isEqualToString:@"Repositories"];
1821 if (NSArray *files = [self files])
1822 for (NSString *file in files)
1823 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1825 else if (!_private && [file isEqualToString:@"/private"])
1827 else if (!stash && [file isEqualToString:@"/var/stash"])
1830 /* XXX: this is not sensitive enough. only some folders are valid. */
1831 if (cydia && !repository)
1832 [warnings addObject:@"files installed into Cydia.app"];
1834 [warnings addObject:@"files installed with /private/*"];
1836 [warnings addObject:@"files installed to /var/stash"];
1839 return [warnings count] == 0 ? nil : warnings;
1842 - (NSArray *) applications {
1843 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1845 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1847 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1848 if (NSArray *files = [self files])
1849 for (NSString *file in files)
1850 if (application_r(file)) {
1851 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1852 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1853 if ([id isEqualToString:me])
1856 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1858 display = application_r[1];
1860 NSString *bundle([file stringByDeletingLastPathComponent]);
1861 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1862 if (icon == nil || [icon length] == 0)
1864 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1866 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1867 [applications addObject:application];
1869 [application addObject:id];
1870 [application addObject:display];
1871 [application addObject:url];
1874 return [applications count] == 0 ? nil : applications;
1877 - (Source *) source {
1879 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1886 - (NSString *) role {
1890 - (BOOL) matches:(NSString *)text {
1896 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1897 if (range.location != NSNotFound)
1900 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1901 if (range.location != NSNotFound)
1904 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1905 if (range.location != NSNotFound)
1911 - (bool) hasSupportingRole {
1914 if ([role_ isEqualToString:@"enduser"])
1916 if ([Role_ isEqualToString:@"User"])
1918 if ([role_ isEqualToString:@"hacker"])
1920 if ([Role_ isEqualToString:@"Hacker"])
1922 if ([role_ isEqualToString:@"developer"])
1924 if ([Role_ isEqualToString:@"Developer"])
1929 - (BOOL) hasTag:(NSString *)tag {
1930 return tags_ == nil ? NO : [tags_ containsObject:tag];
1933 - (NSString *) primaryPurpose {
1934 for (NSString *tag in tags_)
1935 if ([tag hasPrefix:@"purpose::"])
1936 return [tag substringFromIndex:9];
1940 - (NSArray *) purposes {
1941 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1942 for (NSString *tag in tags_)
1943 if ([tag hasPrefix:@"purpose::"])
1944 [purposes addObject:[tag substringFromIndex:9]];
1945 return [purposes count] == 0 ? nil : purposes;
1948 - (NSComparisonResult) compareByName:(Package *)package {
1949 NSString *lhs = [self name];
1950 NSString *rhs = [package name];
1952 if ([lhs length] != 0 && [rhs length] != 0) {
1953 unichar lhc = [lhs characterAtIndex:0];
1954 unichar rhc = [rhs characterAtIndex:0];
1956 if (isalpha(lhc) && !isalpha(rhc))
1957 return NSOrderedAscending;
1958 else if (!isalpha(lhc) && isalpha(rhc))
1959 return NSOrderedDescending;
1962 return [lhs compare:rhs options:LaxCompareOptions_];
1965 - (NSComparisonResult) compareBySection:(Package *)package {
1966 NSString *lhs = [self section];
1967 NSString *rhs = [package section];
1969 if (lhs == NULL && rhs != NULL)
1970 return NSOrderedAscending;
1971 else if (lhs != NULL && rhs == NULL)
1972 return NSOrderedDescending;
1973 else if (lhs != NULL && rhs != NULL) {
1974 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1975 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1978 return NSOrderedSame;
1981 - (uint32_t) compareForChanges {
1986 uint32_t timestamp : 30;
1987 uint32_t ignored : 1;
1988 uint32_t upgradable : 1;
1992 bool upgradable([self upgradableAndEssential:YES]);
1993 value.bits.upgradable = upgradable ? 1 : 0;
1996 value.bits.timestamp = 0;
1997 value.bits.ignored = [self ignored] ? 0 : 1;
1998 value.bits.upgradable = 1;
2000 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2001 value.bits.ignored = 0;
2002 value.bits.upgradable = 0;
2005 return _not(uint32_t) - value.key;
2009 pkgProblemResolver *resolver = [database_ resolver];
2010 resolver->Clear(iterator_);
2011 resolver->Protect(iterator_);
2012 pkgCacheFile &cache([database_ cache]);
2013 cache->MarkInstall(iterator_, false);
2014 pkgDepCache::StateCache &state((*cache)[iterator_]);
2015 if (!state.Install())
2016 cache->SetReInstall(iterator_, true);
2020 pkgProblemResolver *resolver = [database_ resolver];
2021 resolver->Clear(iterator_);
2022 resolver->Protect(iterator_);
2023 resolver->Remove(iterator_);
2024 [database_ cache]->MarkDelete(iterator_, true);
2027 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
2028 return [NSNumber numberWithBool:(
2029 [self unfiltered] && [self matches:search]
2033 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
2034 return [NSNumber numberWithBool:(
2035 (![number boolValue] || [self visible]) && [self installed] != nil
2039 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
2040 NSString *section = [self section];
2042 return [NSNumber numberWithBool:(
2044 [self installed] == nil && (
2046 section == nil && [name length] == 0 ||
2047 [name isEqualToString:section]
2052 - (NSNumber *) isVisibleInSource:(Source *)source {
2053 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2058 /* Section Class {{{ */
2059 @interface Section : NSObject {
2065 - (NSComparisonResult) compareByName:(Section *)section;
2066 - (Section *) initWithName:(NSString *)name;
2067 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2068 - (NSString *) name;
2071 - (void) addToCount;
2075 @implementation Section
2082 - (NSComparisonResult) compareByName:(Section *)section {
2083 NSString *lhs = [self name];
2084 NSString *rhs = [section name];
2086 if ([lhs length] != 0 && [rhs length] != 0) {
2087 unichar lhc = [lhs characterAtIndex:0];
2088 unichar rhc = [rhs characterAtIndex:0];
2090 if (isalpha(lhc) && !isalpha(rhc))
2091 return NSOrderedAscending;
2092 else if (!isalpha(lhc) && isalpha(rhc))
2093 return NSOrderedDescending;
2096 return [lhs compare:rhs options:LaxCompareOptions_];
2099 - (Section *) initWithName:(NSString *)name {
2100 return [self initWithName:name row:0];
2103 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2104 if ((self = [super init]) != nil) {
2105 name_ = [name retain];
2110 - (NSString *) name {
2122 - (void) addToCount {
2130 static NSArray *Finishes_;
2132 /* Database Implementation {{{ */
2133 @implementation Database
2135 + (Database *) sharedInstance {
2136 static Database *instance;
2137 if (instance == nil)
2138 instance = [[Database alloc] init];
2147 - (void) _readCydia:(NSNumber *)fd { _pooled
2148 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2149 std::istream is(&ib);
2152 static Pcre finish_r("^finish:([^:]*)$");
2154 while (std::getline(is, line)) {
2155 const char *data(line.c_str());
2156 size_t size = line.size();
2157 lprintf("C:%s\n", data);
2159 if (finish_r(data, size)) {
2160 NSString *finish = finish_r[1];
2161 int index = [Finishes_ indexOfObject:finish];
2162 if (index != INT_MAX && index > Finish_)
2170 - (void) _readStatus:(NSNumber *)fd { _pooled
2171 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2172 std::istream is(&ib);
2175 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2176 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2178 while (std::getline(is, line)) {
2179 const char *data(line.c_str());
2180 size_t size = line.size();
2181 lprintf("S:%s\n", data);
2183 if (conffile_r(data, size)) {
2184 [delegate_ setConfigurationData:conffile_r[1]];
2185 } else if (strncmp(data, "status: ", 8) == 0) {
2186 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2187 [delegate_ setProgressTitle:string];
2188 } else if (pmstatus_r(data, size)) {
2189 std::string type([pmstatus_r[1] UTF8String]);
2190 NSString *id = pmstatus_r[2];
2192 float percent([pmstatus_r[3] floatValue]);
2193 [delegate_ setProgressPercent:(percent / 100)];
2195 NSString *string = pmstatus_r[4];
2197 if (type == "pmerror")
2198 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2199 withObject:[NSArray arrayWithObjects:string, id, nil]
2202 else if (type == "pmstatus") {
2203 [delegate_ setProgressTitle:string];
2204 } else if (type == "pmconffile")
2205 [delegate_ setConfigurationData:string];
2206 else _assert(false);
2207 } else _assert(false);
2213 - (void) _readOutput:(NSNumber *)fd { _pooled
2214 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2215 std::istream is(&ib);
2218 while (std::getline(is, line)) {
2219 lprintf("O:%s\n", line.c_str());
2220 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2230 - (Package *) packageWithName:(NSString *)name {
2231 if (static_cast<pkgDepCache *>(cache_) == NULL)
2233 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2234 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2237 - (Database *) init {
2238 if ((self = [super init]) != nil) {
2245 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2246 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2250 _assert(pipe(fds) != -1);
2253 _config->Set("APT::Keep-Fds::", cydiafd_);
2254 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2257 detachNewThreadSelector:@selector(_readCydia:)
2259 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2262 _assert(pipe(fds) != -1);
2266 detachNewThreadSelector:@selector(_readStatus:)
2268 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2271 _assert(pipe(fds) != -1);
2272 _assert(dup2(fds[0], 0) != -1);
2273 _assert(close(fds[0]) != -1);
2275 input_ = fdopen(fds[1], "a");
2277 _assert(pipe(fds) != -1);
2278 _assert(dup2(fds[1], 1) != -1);
2279 _assert(close(fds[1]) != -1);
2282 detachNewThreadSelector:@selector(_readOutput:)
2284 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2289 - (pkgCacheFile &) cache {
2293 - (pkgDepCache::Policy *) policy {
2297 - (pkgRecords *) records {
2301 - (pkgProblemResolver *) resolver {
2305 - (pkgAcquire &) fetcher {
2309 - (pkgSourceList &) list {
2313 - (NSArray *) packages {
2317 - (NSArray *) sources {
2318 return [sources_ allValues];
2321 - (NSArray *) issues {
2322 if (cache_->BrokenCount() == 0)
2325 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2327 for (Package *package in packages_) {
2328 if (![package broken])
2330 pkgCache::PkgIterator pkg([package iterator]);
2332 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2333 [entry addObject:[package name]];
2334 [issues addObject:entry];
2336 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2340 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2341 pkgCache::DepIterator start;
2342 pkgCache::DepIterator end;
2343 dep.GlobOr(start, end); // ++dep
2345 if (!cache_->IsImportantDep(end))
2347 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2350 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2351 [entry addObject:failure];
2352 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2354 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2355 [failure addObject:[package name]];
2357 pkgCache::PkgIterator target(start.TargetPkg());
2358 if (target->ProvidesList != 0)
2359 [failure addObject:@"?"];
2361 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2363 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2364 else if (!cache_[target].CandidateVerIter(cache_).end())
2365 [failure addObject:@"-"];
2366 else if (target->ProvidesList == 0)
2367 [failure addObject:@"!"];
2369 [failure addObject:@"%"];
2373 if (start.TargetVer() != 0)
2374 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2385 - (void) reloadData { _pooled
2405 if (!cache_.Open(progress_, true)) {
2407 if (!_error->PopMessage(error))
2410 lprintf("cache_.Open():[%s]\n", error.c_str());
2412 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2413 [delegate_ repairWithSelector:@selector(configure)];
2414 else if (error == "The package lists or status file could not be parsed or opened.")
2415 [delegate_ repairWithSelector:@selector(update)];
2416 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2417 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2418 // else if (error == "The list of sources could not be read.")
2419 else _assert(false);
2425 now_ = [[NSDate date] retain];
2427 policy_ = new pkgDepCache::Policy();
2428 records_ = new pkgRecords(cache_);
2429 resolver_ = new pkgProblemResolver(cache_);
2430 fetcher_ = new pkgAcquire(&status_);
2433 list_ = new pkgSourceList();
2434 _assert(list_->ReadMainList());
2436 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2437 _assert(pkgApplyStatus(cache_));
2439 if (cache_->BrokenCount() != 0) {
2440 _assert(pkgFixBroken(cache_));
2441 _assert(cache_->BrokenCount() == 0);
2442 _assert(pkgMinimizeUpgrade(cache_));
2445 [sources_ removeAllObjects];
2446 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2447 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2448 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2450 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2451 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2455 [packages_ removeAllObjects];
2458 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2459 if (Package *package = [Package packageWithIterator:iterator database:self])
2460 [packages_ addObject:package];
2462 [packages_ sortUsingSelector:@selector(compareByName:)];
2465 _config->Set("Acquire::http::Timeout", 15);
2466 _config->Set("Acquire::http::MaxParallel", 4);
2469 - (void) configure {
2470 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2471 system([dpkg UTF8String]);
2479 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2480 _assert(!_error->PendingError());
2483 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2486 public pkgArchiveCleaner
2489 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2494 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2496 while (_error->PopMessage(error))
2497 lprintf("ArchiveCleaner: %s\n", error.c_str());
2502 pkgRecords records(cache_);
2504 lock_ = new FileFd();
2505 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2506 _assert(!_error->PendingError());
2509 // XXX: explain this with an error message
2510 _assert(list.ReadMainList());
2512 manager_ = (_system->CreatePM(cache_));
2513 _assert(manager_->GetArchives(fetcher_, &list, &records));
2514 _assert(!_error->PendingError());
2518 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2520 _assert(list.ReadMainList());
2521 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2522 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2525 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2530 bool failed = false;
2531 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2532 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2535 std::string uri = (*item)->DescURI();
2536 std::string error = (*item)->ErrorText;
2538 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2541 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2542 withObject:[NSArray arrayWithObjects:
2543 [NSString stringWithUTF8String:error.c_str()],
2555 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2557 if (_error->PendingError()) {
2562 if (result == pkgPackageManager::Failed) {
2567 if (result != pkgPackageManager::Completed) {
2572 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2574 _assert(list.ReadMainList());
2575 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2576 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2579 if (![before isEqualToArray:after])
2584 _assert(pkgDistUpgrade(cache_));
2588 [self updateWithStatus:status_];
2591 - (void) updateWithStatus:(Status &)status {
2593 _assert(list.ReadMainList());
2596 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2597 _assert(!_error->PendingError());
2599 pkgAcquire fetcher(&status);
2600 _assert(list.GetIndexes(&fetcher));
2602 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2603 bool failed = false;
2604 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2605 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2606 (*item)->Finished();
2610 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2611 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2612 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2615 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2620 - (void) setDelegate:(id)delegate {
2621 delegate_ = delegate;
2622 status_.setDelegate(delegate);
2623 progress_.setDelegate(delegate);
2626 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2627 pkgIndexFile *index(NULL);
2628 list_->FindIndex(file, index);
2629 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2635 /* PopUp Windows {{{ */
2636 @interface PopUpView : UIView {
2637 _transient id delegate_;
2638 UITransitionView *transition_;
2643 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2647 @implementation PopUpView
2650 [transition_ setDelegate:nil];
2651 [transition_ release];
2657 [transition_ transition:UITransitionPushFromTop toView:nil];
2660 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2661 if (from != nil && to == nil)
2662 [self removeFromSuperview];
2665 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2666 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2667 delegate_ = delegate;
2669 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2670 [self addSubview:transition_];
2672 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2674 [view addSubview:self];
2676 [transition_ setDelegate:self];
2678 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2679 [transition_ transition:UITransitionNone toView:blank];
2680 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2687 /* Mail Composition {{{ */
2688 @interface MailToView : PopUpView {
2689 MailComposeController *controller_;
2692 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2696 @implementation MailToView
2699 [controller_ release];
2703 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2707 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2708 NSLog(@"did:%@", delivery);
2709 // [UIApp setStatusBarShowsProgress:NO];
2710 if ([controller error]){
2711 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2712 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2713 [mailAlertSheet setBodyText:[controller error]];
2714 [mailAlertSheet popupAlertAnimated:YES];
2718 - (void) showError {
2719 NSLog(@"%@", [controller_ error]);
2720 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2721 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2722 [mailAlertSheet setBodyText:[controller_ error]];
2723 [mailAlertSheet popupAlertAnimated:YES];
2726 - (void) deliverMessage { _pooled
2730 if (![controller_ deliverMessage])
2731 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2734 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2735 if ([controller_ needsDelivery])
2736 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2741 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2742 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2743 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2744 [controller_ setDelegate:self];
2745 [controller_ initializeUI];
2746 [controller_ setupForURL:url];
2748 UIView *view([controller_ view]);
2749 [overlay_ addSubview:view];
2755 /* Confirmation View {{{ */
2756 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2757 if (!iterator.end())
2758 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2759 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2761 pkgCache::PkgIterator package(dep.TargetPkg());
2764 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2771 @protocol ConfirmationViewDelegate
2776 @interface ConfirmationView : BrowserView {
2777 _transient Database *database_;
2778 UIActionSheet *essential_;
2785 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2789 @implementation ConfirmationView
2796 if (essential_ != nil)
2797 [essential_ release];
2803 [book_ popFromSuperviewAnimated:YES];
2806 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2807 NSString *context([sheet context]);
2809 if ([context isEqualToString:@"remove"]) {
2817 [delegate_ confirm];
2824 } else if ([context isEqualToString:@"unable"]) {
2828 [super alertSheet:sheet buttonClicked:button];
2831 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2832 [window setValue:changes_ forKey:@"changes"];
2833 [window setValue:issues_ forKey:@"issues"];
2834 [window setValue:sizes_ forKey:@"sizes"];
2835 [super webView:sender didClearWindowObject:window forFrame:frame];
2838 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2839 if ((self = [super initWithBook:book]) != nil) {
2840 database_ = database;
2842 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2843 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2844 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2845 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2846 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2850 pkgDepCache::Policy *policy([database_ policy]);
2852 pkgCacheFile &cache([database_ cache]);
2853 NSArray *packages = [database_ packages];
2854 for (size_t i(0), e = [packages count]; i != e; ++i) {
2855 Package *package = [packages objectAtIndex:i];
2856 pkgCache::PkgIterator iterator = [package iterator];
2857 pkgDepCache::StateCache &state(cache[iterator]);
2859 NSString *name([package name]);
2861 if (state.NewInstall())
2862 [installing addObject:name];
2863 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2864 [reinstalling addObject:name];
2865 else if (state.Upgrade())
2866 [upgrading addObject:name];
2867 else if (state.Downgrade())
2868 [downgrading addObject:name];
2869 else if (state.Delete()) {
2870 if ([package essential])
2872 [removing addObject:name];
2875 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2876 substrate_ |= DepSubstrate(iterator.CurrentVer());
2881 else if (Advanced_ || true) {
2882 essential_ = [[UIActionSheet alloc]
2883 initWithTitle:@"Removing Essentials"
2884 buttons:[NSArray arrayWithObjects:
2885 @"Cancel Operation (Safe)",
2886 @"Force Removal (Unsafe)",
2888 defaultButtonIndex:0
2894 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2896 [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."];
2898 essential_ = [[UIActionSheet alloc]
2899 initWithTitle:@"Unable to Comply"
2900 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2901 defaultButtonIndex:0
2906 [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."];
2909 changes_ = [[NSArray alloc] initWithObjects:
2917 issues_ = [database_ issues];
2919 issues_ = [issues_ retain];
2921 sizes_ = [[NSArray alloc] initWithObjects:
2922 SizeString([database_ fetcher].FetchNeeded()),
2923 SizeString([database_ fetcher].PartialPresent()),
2924 SizeString([database_ cache]->UsrSize()),
2927 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2931 - (NSString *) backButtonTitle {
2935 - (NSString *) leftButtonTitle {
2939 - (id) _rightButtonTitle {
2940 #if AlwaysReload || IgnoreInstall
2943 return issues_ == nil ? @"Confirm" : nil;
2947 - (void) _leftButtonClicked {
2952 - (void) _rightButtonClicked {
2954 return [super _rightButtonClicked];
2956 if (essential_ != nil)
2957 [essential_ popupAlertAnimated:YES];
2961 [delegate_ confirm];
2969 /* Progress Data {{{ */
2970 @interface ProgressData : NSObject {
2976 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2983 @implementation ProgressData
2985 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2986 if ((self = [super init]) != nil) {
2987 selector_ = selector;
3007 /* Progress View {{{ */
3008 @interface ProgressView : UIView <
3009 ConfigurationDelegate,
3012 _transient Database *database_;
3014 UIView *background_;
3015 UITransitionView *transition_;
3017 UINavigationBar *navbar_;
3018 UIProgressBar *progress_;
3019 UITextView *output_;
3020 UITextLabel *status_;
3021 UIPushButton *close_;
3024 SHA1SumValue springlist_;
3025 SHA1SumValue notifyconf_;
3026 SHA1SumValue sandplate_;
3029 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3031 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3032 - (void) setContentView:(UIView *)view;
3035 - (void) _retachThread;
3036 - (void) _detachNewThreadData:(ProgressData *)data;
3037 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3043 @protocol ProgressViewDelegate
3044 - (void) progressViewIsComplete:(ProgressView *)sender;
3047 @implementation ProgressView
3050 [transition_ setDelegate:nil];
3051 [navbar_ setDelegate:nil];
3054 if (background_ != nil)
3055 [background_ release];
3056 [transition_ release];
3059 [progress_ release];
3066 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3067 if (bootstrap_ && from == overlay_ && to == view_)
3071 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3072 if ((self = [super initWithFrame:frame]) != nil) {
3073 database_ = database;
3074 delegate_ = delegate;
3076 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3077 [transition_ setDelegate:self];
3079 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3082 [overlay_ setBackgroundColor:[UIColor blackColor]];
3084 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3085 [background_ setBackgroundColor:[UIColor blackColor]];
3086 [self addSubview:background_];
3089 [self addSubview:transition_];
3091 CGSize navsize = [UINavigationBar defaultSize];
3092 CGRect navrect = {{0, 0}, navsize};
3094 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3095 [overlay_ addSubview:navbar_];
3097 [navbar_ setBarStyle:1];
3098 [navbar_ setDelegate:self];
3100 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3101 [navbar_ pushNavigationItem:navitem];
3103 CGRect bounds = [overlay_ bounds];
3104 CGSize prgsize = [UIProgressBar defaultSize];
3107 (bounds.size.width - prgsize.width) / 2,
3108 bounds.size.height - prgsize.height - 20
3111 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3112 [progress_ setStyle:0];
3114 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3116 bounds.size.height - prgsize.height - 50,
3117 bounds.size.width - 20,
3121 [status_ setColor:[UIColor whiteColor]];
3122 [status_ setBackgroundColor:[UIColor clearColor]];
3124 [status_ setCentersHorizontally:YES];
3125 //[status_ setFont:font];
3128 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3130 navrect.size.height + 20,
3131 bounds.size.width - 20,
3132 bounds.size.height - navsize.height - 62 - navrect.size.height
3136 //[output_ setTextFont:@"Courier New"];
3137 [output_ setTextSize:12];
3139 [output_ setTextColor:[UIColor whiteColor]];
3140 [output_ setBackgroundColor:[UIColor clearColor]];
3142 [output_ setMarginTop:0];
3143 [output_ setAllowsRubberBanding:YES];
3144 [output_ setEditable:NO];
3146 [overlay_ addSubview:output_];
3148 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3150 bounds.size.height - prgsize.height - 50,
3151 bounds.size.width - 20,
3155 [close_ setAutosizesToFit:NO];
3156 [close_ setDrawsShadow:YES];
3157 [close_ setStretchBackground:YES];
3158 [close_ setEnabled:YES];
3160 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3161 [close_ setTitleFont:bold];
3163 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3164 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3165 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3169 - (void) setContentView:(UIView *)view {
3170 view_ = [view retain];
3173 - (void) resetView {
3174 [transition_ transition:6 toView:view_];
3177 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3178 NSString *context([sheet context]);
3180 if ([context isEqualToString:@"error"])
3182 else if ([context isEqualToString:@"conffile"]) {
3183 FILE *input = [database_ input];
3187 fprintf(input, "N\n");
3191 fprintf(input, "Y\n");
3202 - (void) closeButtonPushed {
3211 [delegate_ suspendWithAnimation:YES];
3215 system("launchctl stop com.apple.SpringBoard");
3219 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3228 - (void) _retachThread {
3229 UINavigationItem *item = [navbar_ topItem];
3230 [item setTitle:@"Complete"];
3232 [overlay_ addSubview:close_];
3233 [progress_ removeFromSuperview];
3234 [status_ removeFromSuperview];
3236 [delegate_ progressViewIsComplete:self];
3239 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3240 MMap mmap(file, MMap::ReadOnly);
3242 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3243 if (!(sandplate_ == sha1.Result()))
3248 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3249 MMap mmap(file, MMap::ReadOnly);
3251 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3252 if (!(notifyconf_ == sha1.Result()))
3257 FileFd file(SpringBoard_, FileFd::ReadOnly);
3258 MMap mmap(file, MMap::ReadOnly);
3260 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3261 if (!(springlist_ == sha1.Result()))
3266 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3267 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3268 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3269 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3270 case 4: [close_ setTitle:@"Reboot Device"]; break;
3273 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3275 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3276 [cache autorelease];
3278 NSFileManager *manager = [NSFileManager defaultManager];
3279 NSError *error = nil;
3281 id system = [cache objectForKey:@"System"];
3286 if (stat(Cache_, &info) == -1)
3289 [system removeAllObjects];
3291 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3292 for (NSString *app in apps)
3293 if ([app hasSuffix:@".app"]) {
3294 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3295 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3296 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3298 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3299 [info setObject:path forKey:@"Path"];
3300 [info setObject:@"System" forKey:@"ApplicationType"];
3301 [system addInfoDictionary:info];
3307 [cache writeToFile:@Cache_ atomically:YES];
3309 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3311 if (chmod(Cache_, info.st_mode) == -1)
3315 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3318 notify_post("com.apple.mobile.application_installed");
3320 [delegate_ setStatusBarShowsProgress:NO];
3323 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3324 [[data target] performSelector:[data selector] withObject:[data object]];
3327 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3330 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3331 UINavigationItem *item = [navbar_ topItem];
3332 [item setTitle:title];
3334 [status_ setText:nil];
3335 [output_ setText:@""];
3336 [progress_ setProgress:0];
3338 [close_ removeFromSuperview];
3339 [overlay_ addSubview:progress_];
3340 [overlay_ addSubview:status_];
3342 [delegate_ setStatusBarShowsProgress:YES];
3346 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3347 MMap mmap(file, MMap::ReadOnly);
3349 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3350 sandplate_ = sha1.Result();
3354 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3355 MMap mmap(file, MMap::ReadOnly);
3357 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3358 notifyconf_ = sha1.Result();
3362 FileFd file(SpringBoard_, FileFd::ReadOnly);
3363 MMap mmap(file, MMap::ReadOnly);
3365 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3366 springlist_ = sha1.Result();
3369 [transition_ transition:6 toView:overlay_];
3372 detachNewThreadSelector:@selector(_detachNewThreadData:)
3374 withObject:[[ProgressData alloc]
3375 initWithSelector:selector
3382 - (void) repairWithSelector:(SEL)selector {
3384 detachNewThreadSelector:selector
3391 - (void) setConfigurationData:(NSString *)data {
3393 performSelectorOnMainThread:@selector(_setConfigurationData:)
3399 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3400 Package *package = id == nil ? nil : [database_ packageWithName:id];
3402 UIActionSheet *sheet = [[[UIActionSheet alloc]
3403 initWithTitle:(package == nil ? id : [package name])
3404 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3405 defaultButtonIndex:0
3410 [sheet setBodyText:error];
3411 [sheet popupAlertAnimated:YES];
3414 - (void) setProgressTitle:(NSString *)title {
3416 performSelectorOnMainThread:@selector(_setProgressTitle:)
3422 - (void) setProgressPercent:(float)percent {
3424 performSelectorOnMainThread:@selector(_setProgressPercent:)
3425 withObject:[NSNumber numberWithFloat:percent]
3430 - (void) startProgress {
3433 - (void) addProgressOutput:(NSString *)output {
3435 performSelectorOnMainThread:@selector(_addProgressOutput:)
3441 - (bool) isCancelling:(size_t)received {
3445 - (void) _setConfigurationData:(NSString *)data {
3446 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3448 _assert(conffile_r(data));
3450 NSString *ofile = conffile_r[1];
3451 //NSString *nfile = conffile_r[2];
3453 UIActionSheet *sheet = [[[UIActionSheet alloc]
3454 initWithTitle:@"Configuration Upgrade"
3455 buttons:[NSArray arrayWithObjects:
3456 @"Keep My Old Copy",
3457 @"Accept The New Copy",
3458 // XXX: @"See What Changed",
3460 defaultButtonIndex:0
3465 [sheet setBodyText:[NSString stringWithFormat:
3466 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3469 [sheet popupAlertAnimated:YES];
3472 - (void) _setProgressTitle:(NSString *)title {
3473 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3474 for (size_t i(0), e([words count]); i != e; ++i) {
3475 NSString *word([words objectAtIndex:i]);
3476 if (Package *package = [database_ packageWithName:word])
3477 [words replaceObjectAtIndex:i withObject:[package name]];
3480 [status_ setText:[words componentsJoinedByString:@" "]];
3483 - (void) _setProgressPercent:(NSNumber *)percent {
3484 [progress_ setProgress:[percent floatValue]];
3487 - (void) _addProgressOutput:(NSString *)output {
3488 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3489 CGSize size = [output_ contentSize];
3490 CGRect rect = {{0, size.height}, {size.width, 0}};
3491 [output_ scrollRectToVisible:rect animated:YES];
3494 - (BOOL) isRunning {
3501 /* Package Cell {{{ */
3502 @interface PackageCell : UISimpleTableCell {
3505 NSString *description_;
3509 UITextLabel *status_;
3513 - (PackageCell *) init;
3514 - (void) setPackage:(Package *)package;
3516 + (int) heightForPackage:(Package *)package;
3520 @implementation PackageCell
3522 - (void) clearPackage {
3533 if (description_ != nil) {
3534 [description_ release];
3538 if (source_ != nil) {
3543 if (badge_ != nil) {
3550 [self clearPackage];
3557 - (PackageCell *) init {
3558 if ((self = [super init]) != nil) {
3560 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3561 [status_ setBackgroundColor:[UIColor clearColor]];
3562 [status_ setFont:small];
3567 - (void) setPackage:(Package *)package {
3568 [self clearPackage];
3570 Source *source = [package source];
3571 NSString *section = [package simpleSection];
3573 icon_ = [[package icon] retain];
3575 name_ = [[package name] retain];
3576 description_ = [[package tagline] retain];
3578 NSString *label = nil;
3579 bool trusted = false;
3581 if (source != nil) {
3582 label = [source label];
3583 trusted = [source trusted];
3584 } else if ([[package id] isEqualToString:@"firmware"])
3587 label = @"Unknown/Local";
3589 NSString *from = [NSString stringWithFormat:@"from %@", label];
3591 if (section != nil && ![section isEqualToString:label])
3592 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3594 source_ = [from retain];
3596 if (NSString *purpose = [package primaryPurpose])
3597 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3598 badge_ = [badge_ retain];
3601 if (NSString *mode = [package mode]) {
3602 [badge_ setImage:[UIImage applicationImageNamed:
3603 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3606 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3607 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3608 } else if ([package half]) {
3609 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3610 [status_ setText:@"Package Damaged"];
3611 [status_ setColor:[UIColor redColor]];
3613 [badge_ setImage:nil];
3614 [status_ setText:nil];
3619 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3622 rect.size = [icon_ size];
3624 rect.size.width /= 2;
3625 rect.size.height /= 2;
3627 rect.origin.x = 25 - rect.size.width / 2;
3628 rect.origin.y = 25 - rect.size.height / 2;
3630 [icon_ drawInRect:rect];
3633 if (badge_ != nil) {
3634 CGSize size = [badge_ size];
3636 [badge_ drawAtPoint:CGPointMake(
3637 36 - size.width / 2,
3638 36 - size.height / 2
3647 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3648 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3652 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3654 [super drawContentInRect:rect selected:selected];
3657 + (int) heightForPackage:(Package *)package {
3658 NSString *tagline([package tagline]);
3659 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3661 if ([package hasMode] || [package half])
3670 /* Section Cell {{{ */
3671 @interface SectionCell : UISimpleTableCell {
3676 _UISwitchSlider *switch_;
3681 - (void) setSection:(Section *)section editing:(BOOL)editing;
3685 @implementation SectionCell
3687 - (void) clearSection {
3688 if (section_ != nil) {
3698 if (count_ != nil) {
3705 [self clearSection];
3712 if ((self = [super init]) != nil) {
3713 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3715 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3716 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3720 - (void) onSwitch:(id)sender {
3721 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3722 if (metadata == nil) {
3723 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3724 [Sections_ setObject:metadata forKey:section_];
3728 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3731 - (void) setSection:(Section *)section editing:(BOOL)editing {
3732 if (editing != editing_) {
3734 [switch_ removeFromSuperview];
3736 [self addSubview:switch_];
3740 [self clearSection];
3742 if (section == nil) {
3743 name_ = [@"All Packages" retain];
3746 section_ = [section name];
3747 if (section_ != nil)
3748 section_ = [section_ retain];
3749 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3750 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3753 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3757 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3758 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3765 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3767 CGSize size = [count_ sizeWithFont:Font14_];
3771 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3773 [super drawContentInRect:rect selected:selected];
3779 /* File Table {{{ */
3780 @interface FileTable : RVPage {
3781 _transient Database *database_;
3784 NSMutableArray *files_;
3788 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3789 - (void) setPackage:(Package *)package;
3793 @implementation FileTable
3796 if (package_ != nil)
3805 - (int) numberOfRowsInTable:(UITable *)table {
3806 return files_ == nil ? 0 : [files_ count];
3809 - (float) table:(UITable *)table heightForRow:(int)row {
3813 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3814 if (reusing == nil) {
3815 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3816 UIFont *font = [UIFont systemFontOfSize:16];
3817 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3819 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3823 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3827 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3828 if ((self = [super initWithBook:book]) != nil) {
3829 database_ = database;
3831 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3833 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3834 [self addSubview:list_];
3836 UITableColumn *column = [[[UITableColumn alloc]
3837 initWithTitle:@"Name"
3839 width:[self frame].size.width
3842 [list_ setDataSource:self];
3843 [list_ setSeparatorStyle:1];
3844 [list_ addTableColumn:column];
3845 [list_ setDelegate:self];
3846 [list_ setReusesTableCells:YES];
3850 - (void) setPackage:(Package *)package {
3851 if (package_ != nil) {
3852 [package_ autorelease];
3861 [files_ removeAllObjects];
3863 if (package != nil) {
3864 package_ = [package retain];
3865 name_ = [[package id] retain];
3867 if (NSArray *files = [package files])
3868 [files_ addObjectsFromArray:files];
3870 if ([files_ count] != 0) {
3871 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3872 [files_ removeObjectAtIndex:0];
3873 [files_ sortUsingSelector:@selector(compareByPath:)];
3875 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3876 [stack addObject:@"/"];
3878 for (int i(0), e([files_ count]); i != e; ++i) {
3879 NSString *file = [files_ objectAtIndex:i];
3880 while (![file hasPrefix:[stack lastObject]])
3881 [stack removeLastObject];
3882 NSString *directory = [stack lastObject];
3883 [stack addObject:[file stringByAppendingString:@"/"]];
3884 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3885 ([stack count] - 2) * 3, "",
3886 [file substringFromIndex:[directory length]]
3895 - (void) resetViewAnimated:(BOOL)animated {
3896 [list_ resetViewAnimated:animated];
3899 - (void) reloadData {
3900 [self setPackage:[database_ packageWithName:name_]];
3901 [self reloadButtons];
3904 - (NSString *) title {
3905 return @"Installed Files";
3908 - (NSString *) backButtonTitle {
3914 /* Package View {{{ */
3915 @interface PackageView : BrowserView {
3916 _transient Database *database_;
3919 NSMutableArray *buttons_;
3922 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3923 - (void) setPackage:(Package *)package;
3927 @implementation PackageView
3930 if (package_ != nil)
3938 - (void) _clickButtonWithName:(NSString *)name {
3939 if ([name isEqualToString:@"Install"])
3940 [delegate_ installPackage:package_];
3941 else if ([name isEqualToString:@"Reinstall"])
3942 [delegate_ installPackage:package_];
3943 else if ([name isEqualToString:@"Remove"])
3944 [delegate_ removePackage:package_];
3945 else if ([name isEqualToString:@"Upgrade"])
3946 [delegate_ installPackage:package_];
3947 else _assert(false);
3950 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3951 NSString *context([sheet context]);
3953 if ([context isEqualToString:@"modify"]) {
3954 int count = [buttons_ count];
3955 _assert(count != 0);
3956 _assert(button <= count + 1);
3958 if (count != button - 1)
3959 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3963 [super alertSheet:sheet buttonClicked:button];
3966 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3967 return [super webView:sender didFinishLoadForFrame:frame];
3970 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3971 [window setValue:package_ forKey:@"package"];
3972 [super webView:sender didClearWindowObject:window forFrame:frame];
3976 - (void) _rightButtonClicked {
3977 /*[super _rightButtonClicked];
3980 int count = [buttons_ count];
3981 _assert(count != 0);
3984 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3986 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3987 [buttons addObjectsFromArray:buttons_];
3988 [buttons addObject:@"Cancel"];
3990 [delegate_ slideUp:[[[UIActionSheet alloc]
3993 defaultButtonIndex:2
4001 - (id) _rightButtonTitle {
4002 int count = [buttons_ count];
4003 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4006 - (NSString *) backButtonTitle {
4010 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4011 if ((self = [super initWithBook:book]) != nil) {
4012 database_ = database;
4013 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4017 - (void) setPackage:(Package *)package {
4018 if (package_ != nil) {
4019 [package_ autorelease];
4028 [buttons_ removeAllObjects];
4030 if (package != nil) {
4031 package_ = [package retain];
4032 name_ = [[package id] retain];
4034 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4036 if ([package_ source] == nil);
4037 else if ([package_ upgradableAndEssential:NO])
4038 [buttons_ addObject:@"Upgrade"];
4039 else if ([package_ installed] == nil)
4040 [buttons_ addObject:@"Install"];
4042 [buttons_ addObject:@"Reinstall"];
4043 if ([package_ installed] != nil)
4044 [buttons_ addObject:@"Remove"];
4052 - (void) reloadData {
4053 [self setPackage:[database_ packageWithName:name_]];
4054 [self reloadButtons];
4059 /* Package Table {{{ */
4060 @interface PackageTable : RVPage {
4061 _transient Database *database_;
4063 NSMutableArray *packages_;
4064 NSMutableArray *sections_;
4065 UISectionList *list_;
4068 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4070 - (void) setDelegate:(id)delegate;
4072 - (void) reloadData;
4073 - (void) resetCursor;
4075 - (UISectionList *) list;
4077 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4081 @implementation PackageTable
4084 [list_ setDataSource:nil];
4087 [packages_ release];
4088 [sections_ release];
4093 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4094 return [sections_ count];
4097 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4098 return [[sections_ objectAtIndex:section] name];
4101 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4102 return [[sections_ objectAtIndex:section] row];
4105 - (int) numberOfRowsInTable:(UITable *)table {
4106 return [packages_ count];
4109 - (float) table:(UITable *)table heightForRow:(int)row {
4110 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4113 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4115 reusing = [[[PackageCell alloc] init] autorelease];
4116 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4120 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4124 - (void) tableRowSelected:(NSNotification *)notification {
4125 int row = [[notification object] selectedRow];
4129 Package *package = [packages_ objectAtIndex:row];
4130 package = [database_ packageWithName:[package id]];
4131 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4132 [view setPackage:package];
4133 [view setDelegate:delegate_];
4134 [book_ pushPage:view];
4137 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4138 if ((self = [super initWithBook:book]) != nil) {
4139 database_ = database;
4140 title_ = [title retain];
4142 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4143 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4145 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4146 [list_ setDataSource:self];
4148 UITableColumn *column = [[[UITableColumn alloc]
4149 initWithTitle:@"Name"
4151 width:[self frame].size.width
4154 UITable *table = [list_ table];
4155 [table setSeparatorStyle:1];
4156 [table addTableColumn:column];
4157 [table setDelegate:self];
4158 [table setReusesTableCells:YES];
4160 [self addSubview:list_];
4162 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4163 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4167 - (void) setDelegate:(id)delegate {
4168 delegate_ = delegate;
4171 - (bool) hasPackage:(Package *)package {
4175 - (void) reloadData {
4176 NSArray *packages = [database_ packages];
4178 [packages_ removeAllObjects];
4179 [sections_ removeAllObjects];
4181 for (size_t i(0); i != [packages count]; ++i) {
4182 Package *package([packages objectAtIndex:i]);
4183 if ([self hasPackage:package])
4184 [packages_ addObject:package];
4187 Section *section = nil;
4189 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4190 Package *package = [packages_ objectAtIndex:offset];
4191 NSString *name = [package index];
4193 if (section == nil || ![[section name] isEqualToString:name]) {
4194 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4195 [sections_ addObject:section];
4198 [section addToCount];
4204 - (NSString *) title {
4208 - (void) resetViewAnimated:(BOOL)animated {
4209 [list_ resetViewAnimated:animated];
4212 - (void) resetCursor {
4213 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4216 - (UISectionList *) list {
4220 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4221 [list_ setShouldHideHeaderInShortLists:hide];
4226 /* Filtered Package Table {{{ */
4227 @interface FilteredPackageTable : PackageTable {
4232 - (void) setObject:(id)object;
4234 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4238 @implementation FilteredPackageTable
4246 - (void) setObject:(id)object {
4252 object_ = [object retain];
4255 - (bool) hasPackage:(Package *)package {
4256 return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
4259 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4260 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4262 object_ = object == nil ? nil : [object retain];
4271 /* Add Source View {{{ */
4272 @interface AddSourceView : RVPage {
4273 _transient Database *database_;
4276 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4280 @implementation AddSourceView
4282 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4283 if ((self = [super initWithBook:book]) != nil) {
4284 database_ = database;
4290 /* Source Cell {{{ */
4291 @interface SourceCell : UITableCell {
4294 NSString *description_;
4300 - (SourceCell *) initWithSource:(Source *)source;
4304 @implementation SourceCell
4309 [description_ release];
4314 - (SourceCell *) initWithSource:(Source *)source {
4315 if ((self = [super init]) != nil) {
4317 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4319 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4320 icon_ = [icon_ retain];
4322 origin_ = [[source name] retain];
4323 label_ = [[source uri] retain];
4324 description_ = [[source description] retain];
4328 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4330 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4337 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4341 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4345 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4347 [super drawContentInRect:rect selected:selected];
4352 /* Source Table {{{ */
4353 @interface SourceTable : RVPage {
4354 _transient Database *database_;
4355 UISectionList *list_;
4356 NSMutableArray *sources_;
4357 UIActionSheet *alert_;
4361 UIProgressHUD *hud_;
4364 //NSURLConnection *installer_;
4365 NSURLConnection *trivial_bz2_;
4366 NSURLConnection *trivial_gz_;
4367 //NSURLConnection *automatic_;
4372 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4376 @implementation SourceTable
4378 - (void) _deallocConnection:(NSURLConnection *)connection {
4379 if (connection != nil) {
4380 [connection cancel];
4381 //[connection setDelegate:nil];
4382 [connection release];
4387 [[list_ table] setDelegate:nil];
4388 [list_ setDataSource:nil];
4397 //[self _deallocConnection:installer_];
4398 [self _deallocConnection:trivial_gz_];
4399 [self _deallocConnection:trivial_bz2_];
4400 //[self _deallocConnection:automatic_];
4407 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4408 return offset_ == 0 ? 1 : 2;
4411 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4412 switch (section + (offset_ == 0 ? 1 : 0)) {
4413 case 0: return @"Entered by User";
4414 case 1: return @"Installed by Packages";
4422 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4423 switch (section + (offset_ == 0 ? 1 : 0)) {
4425 case 1: return offset_;
4433 - (int) numberOfRowsInTable:(UITable *)table {
4434 return [sources_ count];
4437 - (float) table:(UITable *)table heightForRow:(int)row {
4438 Source *source = [sources_ objectAtIndex:row];
4439 return [source description] == nil ? 56 : 73;
4442 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4443 Source *source = [sources_ objectAtIndex:row];
4444 // XXX: weird warning, stupid selectors ;P
4445 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4448 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4452 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4456 - (void) tableRowSelected:(NSNotification*)notification {
4457 UITable *table([list_ table]);
4458 int row([table selectedRow]);
4462 Source *source = [sources_ objectAtIndex:row];
4464 PackageTable *packages = [[[FilteredPackageTable alloc]
4467 title:[source label]
4468 filter:@selector(isVisibleInSource:)
4472 [packages setDelegate:delegate_];
4474 [book_ pushPage:packages];
4477 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4478 Source *source = [sources_ objectAtIndex:row];
4479 return [source record] != nil;
4482 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4483 [[list_ table] setDeleteConfirmationRow:row];
4486 - (void) table:(UITable *)table deleteRow:(int)row {
4487 Source *source = [sources_ objectAtIndex:row];
4488 [Sources_ removeObjectForKey:[source key]];
4489 [delegate_ syncData];
4492 - (void) _endConnection:(NSURLConnection *)connection {
4493 NSURLConnection **field = NULL;
4494 if (connection == trivial_bz2_)
4495 field = &trivial_bz2_;
4496 else if (connection == trivial_gz_)
4497 field = &trivial_gz_;
4498 _assert(field != NULL);
4499 [connection release];
4503 trivial_bz2_ == nil &&
4506 [delegate_ setStatusBarShowsProgress:NO];
4507 [delegate_ removeProgressHUD:hud_];
4513 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4516 @"./", @"Distribution",
4517 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4519 [delegate_ syncData];
4520 } else if (error_ != nil) {
4521 UIActionSheet *sheet = [[[UIActionSheet alloc]
4522 initWithTitle:@"Verification Error"
4523 buttons:[NSArray arrayWithObjects:@"OK", nil]
4524 defaultButtonIndex:0
4529 [sheet setBodyText:[error_ localizedDescription]];
4530 [sheet popupAlertAnimated:YES];
4532 UIActionSheet *sheet = [[[UIActionSheet alloc]
4533 initWithTitle:@"Did not Find Repository"
4534 buttons:[NSArray arrayWithObjects:@"OK", nil]
4535 defaultButtonIndex:0
4540 [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."];
4541 [sheet popupAlertAnimated:YES];
4547 if (error_ != nil) {
4554 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4555 switch ([response statusCode]) {
4561 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4562 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4564 error_ = [error retain];
4565 [self _endConnection:connection];
4568 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4569 [self _endConnection:connection];
4572 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4573 NSMutableURLRequest *request = [NSMutableURLRequest
4574 requestWithURL:[NSURL URLWithString:href]
4575 cachePolicy:NSURLRequestUseProtocolCachePolicy
4576 timeoutInterval:20.0
4579 [request setHTTPMethod:method];
4581 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4584 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4585 NSString *context([sheet context]);
4587 if ([context isEqualToString:@"source"]) {
4590 NSString *href = [[sheet textField] text];
4592 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4594 if (![href hasSuffix:@"/"])
4595 href_ = [href stringByAppendingString:@"/"];
4598 href_ = [href_ retain];
4600 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4601 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4602 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4606 hud_ = [[delegate_ addProgressHUD] retain];
4607 [hud_ setText:@"Verifying URL"];
4618 } else if ([context isEqualToString:@"trivial"])
4620 else if ([context isEqualToString:@"urlerror"])
4624 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4625 if ((self = [super initWithBook:book]) != nil) {
4626 database_ = database;
4627 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4629 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4630 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4631 [list_ setShouldHideHeaderInShortLists:NO];
4633 [self addSubview:list_];
4634 [list_ setDataSource:self];
4636 UITableColumn *column = [[UITableColumn alloc]
4637 initWithTitle:@"Name"
4639 width:[self frame].size.width
4642 UITable *table = [list_ table];
4643 [table setSeparatorStyle:1];
4644 [table addTableColumn:column];
4645 [table setDelegate:self];
4649 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4650 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4654 - (void) reloadData {
4656 _assert(list.ReadMainList());
4658 [sources_ removeAllObjects];
4659 [sources_ addObjectsFromArray:[database_ sources]];
4661 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4664 int count = [sources_ count];
4665 for (offset_ = 0; offset_ != count; ++offset_) {
4666 Source *source = [sources_ objectAtIndex:offset_];
4667 if ([source record] == nil)
4674 - (void) resetViewAnimated:(BOOL)animated {
4675 [list_ resetViewAnimated:animated];
4678 - (void) _leftButtonClicked {
4679 /*[book_ pushPage:[[[AddSourceView alloc]
4684 UIActionSheet *sheet = [[[UIActionSheet alloc]
4685 initWithTitle:@"Enter Cydia/APT URL"
4686 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4687 defaultButtonIndex:0
4692 [sheet setNumberOfRows:1];
4694 [sheet addTextFieldWithValue:@"http://" label:@""];
4696 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4697 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4698 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4699 [traits setKeyboardType:UIKeyboardTypeURL];
4700 // XXX: UIReturnKeyDone
4701 [traits setReturnKeyType:UIReturnKeyNext];
4703 [sheet popupAlertAnimated:YES];
4706 - (void) _rightButtonClicked {
4707 UITable *table = [list_ table];
4708 BOOL editing = [table isRowDeletionEnabled];
4709 [table enableRowDeletion:!editing animated:YES];
4710 [book_ reloadButtonsForPage:self];
4713 - (NSString *) title {
4717 - (NSString *) leftButtonTitle {
4718 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4721 - (id) rightButtonTitle {
4722 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4725 - (UINavigationButtonStyle) rightButtonStyle {
4726 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4732 /* Installed View {{{ */
4733 @interface InstalledView : RVPage {
4734 _transient Database *database_;
4735 FilteredPackageTable *packages_;
4739 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4743 @implementation InstalledView
4746 [packages_ release];
4750 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4751 if ((self = [super initWithBook:book]) != nil) {
4752 database_ = database;
4754 packages_ = [[FilteredPackageTable alloc]
4758 filter:@selector(isInstalledAndVisible:)
4759 with:[NSNumber numberWithBool:YES]
4762 [self addSubview:packages_];
4764 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4765 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4769 - (void) resetViewAnimated:(BOOL)animated {
4770 [packages_ resetViewAnimated:animated];
4773 - (void) reloadData {
4774 [packages_ reloadData];
4777 - (void) _rightButtonClicked {
4778 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4779 [packages_ reloadData];
4781 [book_ reloadButtonsForPage:self];
4784 - (NSString *) title {
4785 return @"Installed";
4788 - (NSString *) backButtonTitle {
4792 - (id) rightButtonTitle {
4793 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4796 - (UINavigationButtonStyle) rightButtonStyle {
4797 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4800 - (void) setDelegate:(id)delegate {
4801 [super setDelegate:delegate];
4802 [packages_ setDelegate:delegate];
4809 @interface HomeView : BrowserView {
4814 @implementation HomeView
4816 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4817 NSString *context([sheet context]);
4819 if ([context isEqualToString:@"about"])
4822 [super alertSheet:sheet buttonClicked:button];
4825 - (void) _leftButtonClicked {
4826 UIActionSheet *sheet = [[[UIActionSheet alloc]
4827 initWithTitle:@"About Cydia Installer"
4828 buttons:[NSArray arrayWithObjects:@"Close", nil]
4829 defaultButtonIndex:0
4835 @"Copyright (C) 2008\n"
4836 "Jay Freeman (saurik)\n"
4837 "saurik@saurik.com\n"
4838 "http://www.saurik.com/\n"
4841 "http://www.theokorigroup.com/\n"
4843 "College of Creative Studies,\n"
4844 "University of California,\n"
4846 "http://www.ccs.ucsb.edu/"
4849 [sheet popupAlertAnimated:YES];
4852 - (NSString *) leftButtonTitle {
4858 /* Manage View {{{ */
4859 @interface ManageView : BrowserView {
4864 @implementation ManageView
4866 - (NSString *) title {
4870 - (void) _leftButtonClicked {
4871 [delegate_ askForSettings];
4874 - (NSString *) leftButtonTitle {
4879 - (id) _rightButtonTitle {
4891 #include <BrowserView.m>
4893 /* Cydia Book {{{ */
4894 @interface CYBook : RVBook <
4897 _transient Database *database_;
4898 UINavigationBar *overlay_;
4899 UINavigationBar *underlay_;
4900 UIProgressIndicator *indicator_;
4901 UITextLabel *prompt_;
4902 UIProgressBar *progress_;
4903 UINavigationButton *cancel_;
4907 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4913 @implementation CYBook
4917 [indicator_ release];
4919 [progress_ release];
4924 - (NSString *) getTitleForPage:(RVPage *)page {
4925 return Simplify([super getTitleForPage:page]);
4933 [UIView beginAnimations:nil context:NULL];
4935 CGRect ovrframe = [overlay_ frame];
4936 ovrframe.origin.y = 0;
4937 [overlay_ setFrame:ovrframe];
4939 CGRect barframe = [navbar_ frame];
4940 barframe.origin.y += ovrframe.size.height;
4941 [navbar_ setFrame:barframe];
4943 CGRect trnframe = [transition_ frame];
4944 trnframe.origin.y += ovrframe.size.height;
4945 trnframe.size.height -= ovrframe.size.height;
4946 [transition_ setFrame:trnframe];
4948 [UIView endAnimations];
4950 [indicator_ startAnimation];
4951 [prompt_ setText:@"Updating Database"];
4952 [progress_ setProgress:0];
4955 [overlay_ addSubview:cancel_];
4958 detachNewThreadSelector:@selector(_update)
4967 [indicator_ stopAnimation];
4969 [UIView beginAnimations:nil context:NULL];
4971 CGRect ovrframe = [overlay_ frame];
4972 ovrframe.origin.y = -ovrframe.size.height;
4973 [overlay_ setFrame:ovrframe];
4975 CGRect barframe = [navbar_ frame];
4976 barframe.origin.y -= ovrframe.size.height;
4977 [navbar_ setFrame:barframe];
4979 CGRect trnframe = [transition_ frame];
4980 trnframe.origin.y -= ovrframe.size.height;
4981 trnframe.size.height += ovrframe.size.height;
4982 [transition_ setFrame:trnframe];
4984 [UIView commitAnimations];
4986 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4989 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4990 if ((self = [super initWithFrame:frame]) != nil) {
4991 database_ = database;
4993 CGRect ovrrect = [navbar_ bounds];
4994 ovrrect.size.height = [UINavigationBar defaultSize].height;
4995 ovrrect.origin.y = -ovrrect.size.height;
4997 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4998 [self addSubview:overlay_];
5000 ovrrect.origin.y = frame.size.height;
5001 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5002 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5003 [self addSubview:underlay_];
5005 [overlay_ setBarStyle:1];
5006 [underlay_ setBarStyle:1];
5008 int barstyle = [overlay_ _barStyle:NO];
5009 bool ugly = barstyle == 0;
5011 UIProgressIndicatorStyle style = ugly ?
5012 UIProgressIndicatorStyleMediumBrown :
5013 UIProgressIndicatorStyleMediumWhite;
5015 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5016 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5017 CGRect indrect = {{indoffset, indoffset}, indsize};
5019 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5020 [indicator_ setStyle:style];
5021 [overlay_ addSubview:indicator_];
5023 CGSize prmsize = {215, indsize.height + 4};
5026 indoffset * 2 + indsize.width,
5030 unsigned(ovrrect.size.height - prmsize.height) / 2
5033 UIFont *font = [UIFont systemFontOfSize:15];
5035 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5037 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5038 [prompt_ setBackgroundColor:[UIColor clearColor]];
5039 [prompt_ setFont:font];
5041 [overlay_ addSubview:prompt_];
5043 CGSize prgsize = {75, 100};
5046 ovrrect.size.width - prgsize.width - 10,
5047 (ovrrect.size.height - prgsize.height) / 2
5050 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5051 [progress_ setStyle:0];
5052 [overlay_ addSubview:progress_];
5054 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5055 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5057 CGRect frame = [cancel_ frame];
5058 frame.size.width = 65;
5059 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5060 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5061 [cancel_ setFrame:frame];
5063 [cancel_ setBarStyle:barstyle];
5067 - (void) _onCancel {
5069 [cancel_ removeFromSuperview];
5072 - (void) _update { _pooled
5074 status.setDelegate(self);
5076 [database_ updateWithStatus:status];
5079 performSelectorOnMainThread:@selector(_update_)
5085 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5086 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5089 - (void) setProgressTitle:(NSString *)title {
5091 performSelectorOnMainThread:@selector(_setProgressTitle:)
5097 - (void) setProgressPercent:(float)percent {
5099 performSelectorOnMainThread:@selector(_setProgressPercent:)
5100 withObject:[NSNumber numberWithFloat:percent]
5105 - (void) startProgress {
5108 - (void) addProgressOutput:(NSString *)output {
5110 performSelectorOnMainThread:@selector(_addProgressOutput:)
5116 - (bool) isCancelling:(size_t)received {
5120 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5124 - (void) _setProgressTitle:(NSString *)title {
5125 [prompt_ setText:title];
5128 - (void) _setProgressPercent:(NSNumber *)percent {
5129 [progress_ setProgress:[percent floatValue]];
5132 - (void) _addProgressOutput:(NSString *)output {
5137 /* Cydia:// Protocol {{{ */
5138 @interface CydiaURLProtocol : NSURLProtocol {
5143 @implementation CydiaURLProtocol
5145 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5146 NSURL *url([request URL]);
5149 NSString *scheme([[url scheme] lowercaseString]);
5150 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5155 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5159 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5160 id<NSURLProtocolClient> client([self client]);
5162 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5164 NSData *data(UIImagePNGRepresentation(icon));
5166 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5167 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5168 [client URLProtocol:self didLoadData:data];
5169 [client URLProtocolDidFinishLoading:self];
5173 - (void) startLoading {
5174 id<NSURLProtocolClient> client([self client]);
5175 NSURLRequest *request([self request]);
5177 NSURL *url([request URL]);
5178 NSString *href([url absoluteString]);
5180 NSString *path([href substringFromIndex:8]);
5181 NSRange slash([path rangeOfString:@"/"]);
5184 if (slash.location == NSNotFound) {
5188 command = [path substringToIndex:slash.location];
5189 path = [path substringFromIndex:(slash.location + 1)];
5192 Database *database([Database sharedInstance]);
5194 if ([command isEqualToString:@"package-icon"]) {
5197 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5198 Package *package([database packageWithName:path]);
5201 UIImage *icon([package icon]);
5202 [self _returnPNGWithImage:icon forRequest:request];
5203 } else if ([command isEqualToString:@"source-icon"]) {
5206 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5207 NSString *source(Simplify(path));
5208 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5210 icon = [UIImage applicationImageNamed:@"unknown.png"];
5211 [self _returnPNGWithImage:icon forRequest:request];
5212 } else if ([command isEqualToString:@"uikit-image"]) {
5215 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5216 UIImage *icon(_UIImageWithName(path));
5217 [self _returnPNGWithImage:icon forRequest:request];
5218 } else if ([command isEqualToString:@"section-icon"]) {
5221 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5222 NSString *section(Simplify(path));
5223 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5225 icon = [UIImage applicationImageNamed:@"unknown.png"];
5226 [self _returnPNGWithImage:icon forRequest:request];
5228 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5232 - (void) stopLoading {
5238 /* Sections View {{{ */
5239 @interface SectionsView : RVPage {
5240 _transient Database *database_;
5241 NSMutableArray *sections_;
5242 NSMutableArray *filtered_;
5243 UITransitionView *transition_;
5249 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5250 - (void) reloadData;
5255 @implementation SectionsView
5258 [list_ setDataSource:nil];
5259 [list_ setDelegate:nil];
5261 [sections_ release];
5262 [filtered_ release];
5263 [transition_ release];
5265 [accessory_ release];
5269 - (int) numberOfRowsInTable:(UITable *)table {
5270 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5273 - (float) table:(UITable *)table heightForRow:(int)row {
5277 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5279 reusing = [[[SectionCell alloc] init] autorelease];
5280 [(SectionCell *)reusing setSection:(editing_ ?
5281 [sections_ objectAtIndex:row] :
5282 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5283 ) editing:editing_];
5287 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5291 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5295 - (void) tableRowSelected:(NSNotification *)notification {
5296 int row = [[notification object] selectedRow];
5307 title = @"All Packages";
5309 section = [filtered_ objectAtIndex:(row - 1)];
5310 name = [section name];
5316 title = @"(No Section)";
5320 PackageTable *table = [[[FilteredPackageTable alloc]
5324 filter:@selector(isVisiblyUninstalledInSection:)
5328 [table setDelegate:delegate_];
5330 [book_ pushPage:table];
5333 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5334 if ((self = [super initWithBook:book]) != nil) {
5335 database_ = database;
5337 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5338 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5340 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5341 [self addSubview:transition_];
5343 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5344 [transition_ transition:0 toView:list_];
5346 UITableColumn *column = [[[UITableColumn alloc]
5347 initWithTitle:@"Name"
5349 width:[self frame].size.width
5352 [list_ setDataSource:self];
5353 [list_ setSeparatorStyle:1];
5354 [list_ addTableColumn:column];
5355 [list_ setDelegate:self];
5356 [list_ setReusesTableCells:YES];
5360 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5361 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5365 - (void) reloadData {
5366 NSArray *packages = [database_ packages];
5368 [sections_ removeAllObjects];
5369 [filtered_ removeAllObjects];
5371 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5372 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5375 for (size_t i(0); i != [packages count]; ++i) {
5376 Package *package([packages objectAtIndex:i]);
5377 NSString *name([package section]);
5380 Section *section([sections objectForKey:name]);
5381 if (section == nil) {
5382 section = [[[Section alloc] initWithName:name] autorelease];
5383 [sections setObject:section forKey:name];
5387 if ([package valid] && [package installed] == nil && [package visible])
5388 [filtered addObject:package];
5392 [sections_ addObjectsFromArray:[sections allValues]];
5393 [sections_ sortUsingSelector:@selector(compareByName:)];
5396 [filtered sortUsingSelector:@selector(compareBySection:)];
5399 Section *section = nil;
5400 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5401 Package *package = [filtered objectAtIndex:offset];
5402 NSString *name = [package section];
5404 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5405 section = name == nil ?
5406 [[[Section alloc] initWithName:nil] autorelease] :
5407 [sections objectForKey:name];
5408 [filtered_ addObject:section];
5411 [section addToCount];
5419 - (void) resetView {
5421 [self _rightButtonClicked];
5424 - (void) resetViewAnimated:(BOOL)animated {
5425 [list_ resetViewAnimated:animated];
5428 - (void) _rightButtonClicked {
5429 if ((editing_ = !editing_))
5432 [delegate_ updateData];
5433 [book_ reloadTitleForPage:self];
5434 [book_ reloadButtonsForPage:self];
5437 - (NSString *) title {
5438 return editing_ ? @"Section Visibility" : @"Install by Section";
5441 - (NSString *) backButtonTitle {
5445 - (id) rightButtonTitle {
5446 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5449 - (UINavigationButtonStyle) rightButtonStyle {
5450 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5453 - (UIView *) accessoryView {
5459 /* Changes View {{{ */
5460 @interface ChangesView : RVPage {
5461 _transient Database *database_;
5462 NSMutableArray *packages_;
5463 NSMutableArray *sections_;
5464 UISectionList *list_;
5468 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5469 - (void) reloadData;
5473 @implementation ChangesView
5476 [[list_ table] setDelegate:nil];
5477 [list_ setDataSource:nil];
5479 [packages_ release];
5480 [sections_ release];
5485 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5486 return [sections_ count];
5489 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5490 return [[sections_ objectAtIndex:section] name];
5493 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5494 return [[sections_ objectAtIndex:section] row];
5497 - (int) numberOfRowsInTable:(UITable *)table {
5498 return [packages_ count];
5501 - (float) table:(UITable *)table heightForRow:(int)row {
5502 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5505 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5507 reusing = [[[PackageCell alloc] init] autorelease];
5508 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5512 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5516 - (void) tableRowSelected:(NSNotification *)notification {
5517 int row = [[notification object] selectedRow];
5520 Package *package = [packages_ objectAtIndex:row];
5521 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5522 [view setDelegate:delegate_];
5523 [view setPackage:package];
5524 [book_ pushPage:view];
5527 - (void) _leftButtonClicked {
5528 [(CYBook *)book_ update];
5529 [self reloadButtons];
5532 - (void) _rightButtonClicked {
5533 [delegate_ distUpgrade];
5536 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5537 if ((self = [super initWithBook:book]) != nil) {
5538 database_ = database;
5540 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5541 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5543 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5544 [self addSubview:list_];
5546 [list_ setShouldHideHeaderInShortLists:NO];
5547 [list_ setDataSource:self];
5548 //[list_ setSectionListStyle:1];
5550 UITableColumn *column = [[[UITableColumn alloc]
5551 initWithTitle:@"Name"
5553 width:[self frame].size.width
5556 UITable *table = [list_ table];
5557 [table setSeparatorStyle:1];
5558 [table addTableColumn:column];
5559 [table setDelegate:self];
5560 [table setReusesTableCells:YES];
5564 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5565 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5569 - (void) reloadData {
5570 NSArray *packages = [database_ packages];
5572 [packages_ removeAllObjects];
5573 [sections_ removeAllObjects];
5576 for (size_t i(0); i != [packages count]; ++i) {
5577 Package *package([packages objectAtIndex:i]);
5580 [package installed] == nil && [package valid] && [package visible] ||
5581 [package upgradableAndEssential:YES]
5583 [packages_ addObject:package];
5587 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5590 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5591 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5592 Section *section = nil;
5596 bool unseens = false;
5598 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5601 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5602 Package *package = [packages_ objectAtIndex:offset];
5604 if (![package upgradableAndEssential:YES]) {
5606 NSDate *seen = [package seen];
5608 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5611 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5612 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5613 [sections_ addObject:section];
5617 [section addToCount];
5618 } else if ([package ignored])
5619 [ignored addToCount];
5622 [upgradable addToCount];
5627 CFRelease(formatter);
5630 Section *last = [sections_ lastObject];
5631 size_t count = [last count];
5632 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5633 [sections_ removeLastObject];
5636 if ([ignored count] != 0)
5637 [sections_ insertObject:ignored atIndex:0];
5639 [sections_ insertObject:upgradable atIndex:0];
5642 [self reloadButtons];
5645 - (void) resetViewAnimated:(BOOL)animated {
5646 [list_ resetViewAnimated:animated];
5649 - (NSString *) leftButtonTitle {
5650 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5653 - (id) rightButtonTitle {
5654 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5657 - (NSString *) title {
5663 /* Search View {{{ */
5664 @protocol SearchViewDelegate
5665 - (void) showKeyboard:(BOOL)show;
5668 @interface SearchView : RVPage {
5670 UISearchField *field_;
5671 UITransitionView *transition_;
5672 FilteredPackageTable *table_;
5673 UIPreferencesTable *advanced_;
5679 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5680 - (void) reloadData;
5684 @implementation SearchView
5687 [field_ setDelegate:nil];
5689 [accessory_ release];
5691 [transition_ release];
5693 [advanced_ release];
5698 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5702 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5704 case 0: return @"Advanced Search (Coming Soon!)";
5706 default: _assert(false);
5710 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5714 default: _assert(false);
5718 - (void) _showKeyboard:(BOOL)show {
5719 CGSize keysize = [UIKeyboard defaultSize];
5720 CGRect keydown = [book_ pageBounds];
5721 CGRect keyup = keydown;
5722 keyup.size.height -= keysize.height - ButtonBarHeight_;
5724 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5726 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5727 [animation setSignificantRectFields:8];
5730 [animation setStartFrame:keydown];
5731 [animation setEndFrame:keyup];
5733 [animation setStartFrame:keyup];
5734 [animation setEndFrame:keydown];
5737 UIAnimator *animator = [UIAnimator sharedAnimator];
5740 addAnimations:[NSArray arrayWithObjects:animation, nil]
5741 withDuration:(KeyboardTime_ - delay)
5746 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5748 [delegate_ showKeyboard:show];
5751 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5752 [self _showKeyboard:YES];
5755 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5756 [self _showKeyboard:NO];
5759 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5761 NSString *text([field_ text]);
5762 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5768 - (void) textFieldClearButtonPressed:(UITextField *)field {
5772 - (void) keyboardInputShouldDelete:(id)input {
5776 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5777 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5781 [field_ resignFirstResponder];
5786 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5787 if ((self = [super initWithBook:book]) != nil) {
5788 CGRect pageBounds = [book_ pageBounds];
5790 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5791 [self addSubview:transition_];
5793 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5795 [advanced_ setReusesTableCells:YES];
5796 [advanced_ setDataSource:self];
5797 [advanced_ reloadData];
5799 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5800 CGColor dimmed(space_, 0, 0, 0, 0.5);
5801 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5803 table_ = [[FilteredPackageTable alloc]
5807 filter:@selector(isUnfilteredAndSearchedForBy:)
5811 [table_ setShouldHideHeaderInShortLists:NO];
5812 [transition_ transition:0 toView:table_];
5821 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5828 [self bounds].size.width - area.origin.x - 18;
5830 area.size.height = [UISearchField defaultHeight];
5832 field_ = [[UISearchField alloc] initWithFrame:area];
5834 UIFont *font = [UIFont systemFontOfSize:16];
5835 [field_ setFont:font];
5837 [field_ setPlaceholder:@"Package Names & Descriptions"];
5838 [field_ setDelegate:self];
5840 [field_ setPaddingTop:5];
5842 UITextInputTraits *traits([field_ textInputTraits]);
5843 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5844 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5845 [traits setReturnKeyType:UIReturnKeySearch];
5847 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5849 accessory_ = [[UIView alloc] initWithFrame:accrect];
5850 [accessory_ addSubview:field_];
5852 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5853 [configure setShowPressFeedback:YES];
5854 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5855 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5856 [accessory_ addSubview:configure];*/
5858 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5859 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5865 LKAnimation *animation = [LKTransition animation];
5866 [animation setType:@"oglFlip"];
5867 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5868 [animation setFillMode:@"extended"];
5869 [animation setTransitionFlags:3];
5870 [animation setDuration:10];
5871 [animation setSpeed:0.35];
5872 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5873 [[transition_ _layer] addAnimation:animation forKey:0];
5874 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5875 flipped_ = !flipped_;
5879 - (void) configurePushed {
5880 [field_ resignFirstResponder];
5884 - (void) resetViewAnimated:(BOOL)animated {
5887 [table_ resetViewAnimated:animated];
5890 - (void) _reloadData {
5893 - (void) reloadData {
5896 [table_ setObject:[field_ text]];
5897 [table_ reloadData];
5898 [table_ resetCursor];
5901 - (UIView *) accessoryView {
5905 - (NSString *) title {
5909 - (NSString *) backButtonTitle {
5913 - (void) setDelegate:(id)delegate {
5914 [table_ setDelegate:delegate];
5915 [super setDelegate:delegate];
5921 @interface SettingsView : RVPage {
5922 _transient Database *database_;
5925 UIPreferencesTable *table_;
5926 _UISwitchSlider *subscribedSwitch_;
5927 _UISwitchSlider *ignoredSwitch_;
5928 UIPreferencesControlTableCell *subscribedCell_;
5929 UIPreferencesControlTableCell *ignoredCell_;
5932 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5936 @implementation SettingsView
5939 [table_ setDataSource:nil];
5942 if (package_ != nil)
5945 [subscribedSwitch_ release];
5946 [ignoredSwitch_ release];
5947 [subscribedCell_ release];
5948 [ignoredCell_ release];
5952 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5953 if (package_ == nil)
5959 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5960 if (package_ == nil)
5967 default: _assert(false);
5973 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5974 if (package_ == nil)
5981 default: _assert(false);
5987 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5988 if (package_ == nil)
5995 default: _assert(false);
6001 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6002 if (package_ == nil)
6005 _UISwitchSlider *slider([cell control]);
6006 BOOL value([slider value] != 0);
6007 NSMutableDictionary *metadata([package_ metadata]);
6010 if (NSNumber *number = [metadata objectForKey:key])
6011 before = [number boolValue];
6015 if (value != before) {
6016 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6018 [delegate_ updateData];
6022 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6023 [self onSomething:cell withKey:@"IsSubscribed"];
6026 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6027 [self onSomething:cell withKey:@"IsIgnored"];
6030 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6031 if (package_ == nil)
6035 case 0: switch (row) {
6037 return subscribedCell_;
6039 return ignoredCell_;
6040 default: _assert(false);
6043 case 1: switch (row) {
6045 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6046 [cell setShowSelection:NO];
6047 [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."];
6051 default: _assert(false);
6054 default: _assert(false);
6060 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6061 if ((self = [super initWithBook:book])) {
6062 database_ = database;
6063 name_ = [package retain];
6065 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6066 [self addSubview:table_];
6068 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6069 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6071 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6072 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6074 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6075 [subscribedCell_ setShowSelection:NO];
6076 [subscribedCell_ setTitle:@"Show All Changes"];
6077 [subscribedCell_ setControl:subscribedSwitch_];
6079 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6080 [ignoredCell_ setShowSelection:NO];
6081 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6082 [ignoredCell_ setControl:ignoredSwitch_];
6084 [table_ setDataSource:self];
6089 - (void) resetViewAnimated:(BOOL)animated {
6090 [table_ resetViewAnimated:animated];
6093 - (void) reloadData {
6094 if (package_ != nil)
6095 [package_ autorelease];
6096 package_ = [database_ packageWithName:name_];
6097 if (package_ != nil) {
6099 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6100 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6103 [table_ reloadData];
6106 - (NSString *) title {
6112 /* Signature View {{{ */
6113 @interface SignatureView : BrowserView {
6114 _transient Database *database_;
6118 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6122 @implementation SignatureView
6129 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6131 [super webView:sender didClearWindowObject:window forFrame:frame];
6134 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6135 if ((self = [super initWithBook:book]) != nil) {
6136 database_ = database;
6137 package_ = [package retain];
6142 - (void) resetViewAnimated:(BOOL)animated {
6145 - (void) reloadData {
6146 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6152 @interface Cydia : UIApplication <
6153 ConfirmationViewDelegate,
6154 ProgressViewDelegate,
6163 UIToolbar *buttonbar_;
6167 NSMutableArray *essential_;
6168 NSMutableArray *broken_;
6170 Database *database_;
6171 ProgressView *progress_;
6175 UIKeyboard *keyboard_;
6176 UIProgressHUD *hud_;
6178 SectionsView *sections_;
6179 ChangesView *changes_;
6180 ManageView *manage_;
6181 SearchView *search_;
6186 @implementation Cydia
6189 if ([broken_ count] != 0) {
6190 int count = [broken_ count];
6192 UIActionSheet *sheet = [[[UIActionSheet alloc]
6193 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6194 buttons:[NSArray arrayWithObjects:
6196 @"Ignore (Temporary)",
6198 defaultButtonIndex:0
6203 [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."];
6204 [sheet popupAlertAnimated:YES];
6205 } else if (!Ignored_ && [essential_ count] != 0) {
6206 int count = [essential_ count];
6208 UIActionSheet *sheet = [[[UIActionSheet alloc]
6209 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6210 buttons:[NSArray arrayWithObjects:
6211 @"Upgrade Essential",
6212 @"Complete Upgrade",
6213 @"Ignore (Temporary)",
6215 defaultButtonIndex:0
6220 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6221 [sheet popupAlertAnimated:YES];
6225 - (void) _reloadData {
6228 UIProgressHUD *hud([self addProgressHUD]);
6229 [hud setText:@"Reloading Data"];
6231 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6234 [self removeProgressHUD:hud];
6238 [essential_ removeAllObjects];
6239 [broken_ removeAllObjects];
6241 NSArray *packages = [database_ packages];
6242 for (Package *package in packages) {
6244 [broken_ addObject:package];
6245 if ([package upgradableAndEssential:NO]) {
6246 if ([package essential])
6247 [essential_ addObject:package];
6253 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6254 [buttonbar_ setBadgeValue:badge forButton:3];
6255 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6256 [buttonbar_ setBadgeAnimated:YES forButton:3];
6257 [self setApplicationBadge:badge];
6259 [buttonbar_ setBadgeValue:nil forButton:3];
6260 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6261 [buttonbar_ setBadgeAnimated:NO forButton:3];
6262 [self removeApplicationBadge];
6267 // XXX: what is this line of code for?
6268 if ([packages count] == 0);
6269 else if (Loaded_) loaded:
6274 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6275 NSTimeInterval interval([update timeIntervalSinceNow]);
6276 if (interval <= 0 && interval > -600)
6284 - (void) _saveConfig {
6287 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6293 - (void) updateData {
6296 /* XXX: this is just stupid */
6297 if (tag_ != 2 && sections_ != nil)
6298 [sections_ reloadData];
6299 if (tag_ != 3 && changes_ != nil)
6300 [changes_ reloadData];
6301 if (tag_ != 5 && search_ != nil)
6302 [search_ reloadData];
6312 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6313 _assert(file != NULL);
6315 NSArray *keys = [Sources_ allKeys];
6317 for (int i(0), e([keys count]); i != e; ++i) {
6318 NSString *key = [keys objectAtIndex:i];
6319 NSDictionary *source = [Sources_ objectForKey:key];
6321 fprintf(file, "%s %s %s\n",
6322 [[source objectForKey:@"Type"] UTF8String],
6323 [[source objectForKey:@"URI"] UTF8String],
6324 [[source objectForKey:@"Distribution"] UTF8String]
6333 detachNewThreadSelector:@selector(update_)
6336 title:@"Updating Sources"
6340 - (void) reloadData {
6341 @synchronized (self) {
6342 if (confirm_ == nil)
6348 pkgProblemResolver *resolver = [database_ resolver];
6350 resolver->InstallProtect();
6351 if (!resolver->Resolve(true))
6355 - (void) popUpBook:(RVBook *)book {
6356 [underlay_ popSubview:book];
6359 - (CGRect) popUpBounds {
6360 return [underlay_ bounds];
6364 [database_ prepare];
6366 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6367 [confirm_ setDelegate:self];
6369 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6370 [page setDelegate:self];
6372 [confirm_ setPage:page];
6373 [self popUpBook:confirm_];
6376 - (void) installPackage:(Package *)package {
6377 @synchronized (self) {
6384 - (void) removePackage:(Package *)package {
6385 @synchronized (self) {
6392 - (void) distUpgrade {
6393 @synchronized (self) {
6394 [database_ upgrade];
6400 @synchronized (self) {
6402 if (confirm_ != nil) {
6410 [overlay_ removeFromSuperview];
6414 detachNewThreadSelector:@selector(perform)
6421 - (void) bootstrap_ {
6423 [database_ upgrade];
6424 [database_ prepare];
6425 [database_ perform];
6428 - (void) bootstrap {
6430 detachNewThreadSelector:@selector(bootstrap_)
6433 title:@"Bootstrap Install"
6437 - (void) progressViewIsComplete:(ProgressView *)progress {
6438 if (confirm_ != nil) {
6439 [underlay_ addSubview:overlay_];
6440 [confirm_ popFromSuperviewAnimated:NO];
6446 - (void) setPage:(RVPage *)page {
6447 [page resetViewAnimated:NO];
6448 [page setDelegate:self];
6449 [book_ setPage:page];
6452 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6453 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6454 [browser loadURL:url];
6458 - (void) _setHomePage {
6459 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6462 - (void) buttonBarItemTapped:(id)sender {
6463 unsigned tag = [sender tag];
6465 [book_ resetViewAnimated:YES];
6467 } else if (tag_ == 2 && tag != 2)
6468 [sections_ resetView];
6471 case 1: [self _setHomePage]; break;
6473 case 2: [self setPage:sections_]; break;
6474 case 3: [self setPage:changes_]; break;
6475 case 4: [self setPage:manage_]; break;
6476 case 5: [self setPage:search_]; break;
6478 default: _assert(false);
6484 - (void) applicationWillSuspend {
6486 [super applicationWillSuspend];
6489 - (void) askForSettings {
6490 UIActionSheet *role = [[[UIActionSheet alloc]
6491 initWithTitle:@"Who Are You?"
6492 buttons:[NSArray arrayWithObjects:
6493 @"User (Graphical Only)",
6494 @"Hacker (+ Command Line)",
6495 @"Developer (No Filters)",
6497 defaultButtonIndex:-1
6502 [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."];
6503 [role popupAlertAnimated:YES];
6508 [self setStatusBarShowsProgress:NO];
6509 [self removeProgressHUD:hud_];
6514 pid_t pid = ExecFork();
6516 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6517 perror("launchctl stop");
6524 [self askForSettings];
6529 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6531 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6532 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6533 0, 0, screenrect.size.width, screenrect.size.height - 48
6534 ) database:database_];
6536 [book_ setDelegate:self];
6538 [overlay_ addSubview:book_];
6540 NSArray *buttonitems = [NSArray arrayWithObjects:
6541 [NSDictionary dictionaryWithObjectsAndKeys:
6542 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6543 @"home-up.png", kUIButtonBarButtonInfo,
6544 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6545 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6546 self, kUIButtonBarButtonTarget,
6547 @"Home", kUIButtonBarButtonTitle,
6548 @"0", kUIButtonBarButtonType,
6551 [NSDictionary dictionaryWithObjectsAndKeys:
6552 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6553 @"install-up.png", kUIButtonBarButtonInfo,
6554 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6555 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6556 self, kUIButtonBarButtonTarget,
6557 @"Sections", kUIButtonBarButtonTitle,
6558 @"0", kUIButtonBarButtonType,
6561 [NSDictionary dictionaryWithObjectsAndKeys:
6562 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6563 @"changes-up.png", kUIButtonBarButtonInfo,
6564 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6565 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6566 self, kUIButtonBarButtonTarget,
6567 @"Changes", kUIButtonBarButtonTitle,
6568 @"0", kUIButtonBarButtonType,
6571 [NSDictionary dictionaryWithObjectsAndKeys:
6572 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6573 @"manage-up.png", kUIButtonBarButtonInfo,
6574 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6575 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6576 self, kUIButtonBarButtonTarget,
6577 @"Manage", kUIButtonBarButtonTitle,
6578 @"0", kUIButtonBarButtonType,
6581 [NSDictionary dictionaryWithObjectsAndKeys:
6582 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6583 @"search-up.png", kUIButtonBarButtonInfo,
6584 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6585 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6586 self, kUIButtonBarButtonTarget,
6587 @"Search", kUIButtonBarButtonTitle,
6588 @"0", kUIButtonBarButtonType,
6592 buttonbar_ = [[UIToolbar alloc]
6594 withFrame:CGRectMake(
6595 0, screenrect.size.height - ButtonBarHeight_,
6596 screenrect.size.width, ButtonBarHeight_
6598 withItemList:buttonitems
6601 [buttonbar_ setDelegate:self];
6602 [buttonbar_ setBarStyle:1];
6603 [buttonbar_ setButtonBarTrackingMode:2];
6605 int buttons[5] = {1, 2, 3, 4, 5};
6606 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6607 [buttonbar_ showButtonGroup:0 withDuration:0];
6609 for (int i = 0; i != 5; ++i)
6610 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6611 i * 64 + 2, 1, 60, ButtonBarHeight_
6614 [buttonbar_ showSelectionForButton:1];
6615 [overlay_ addSubview:buttonbar_];
6617 [UIKeyboard initImplementationNow];
6618 CGSize keysize = [UIKeyboard defaultSize];
6619 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6620 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6621 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6622 [overlay_ addSubview:keyboard_];
6625 [underlay_ addSubview:overlay_];
6629 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6630 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6631 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6633 manage_ = (ManageView *) [[self
6634 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6635 withClass:[ManageView class]
6641 [self _setHomePage];
6644 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6645 NSString *context([sheet context]);
6647 if ([context isEqualToString:@"missing"])
6649 else if ([context isEqualToString:@"fixhalf"]) {
6652 @synchronized (self) {
6653 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6654 Package *broken = [broken_ objectAtIndex:i];
6657 NSString *id = [broken id];
6658 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6659 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6660 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6661 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6670 [broken_ removeAllObjects];
6679 } else if ([context isEqualToString:@"role"]) {
6681 case 1: Role_ = @"User"; break;
6682 case 2: Role_ = @"Hacker"; break;
6683 case 3: Role_ = @"Developer"; break;
6690 bool reset = Settings_ != nil;
6692 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6696 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6706 } else if ([context isEqualToString:@"upgrade"]) {
6709 @synchronized (self) {
6710 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6711 Package *essential = [essential_ objectAtIndex:i];
6712 [essential install];
6736 - (void) reorganize { _pooled
6737 system("/usr/libexec/cydia/free.sh");
6738 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6741 - (void) applicationSuspend:(__GSEvent *)event {
6742 if (hud_ == nil && ![progress_ isRunning])
6743 [super applicationSuspend:event];
6746 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6748 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6751 - (void) _setSuspended:(BOOL)value {
6753 [super _setSuspended:value];
6756 - (UIProgressHUD *) addProgressHUD {
6757 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
6758 [window_ setUserInteractionEnabled:NO];
6760 [progress_ addSubview:hud];
6764 - (void) removeProgressHUD:(UIProgressHUD *)hud {
6766 [hud removeFromSuperview];
6767 [window_ setUserInteractionEnabled:YES];
6770 - (void) openMailToURL:(NSURL *)url {
6771 // XXX: this makes me sad
6773 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6775 [UIApp openURL:url];// asPanel:YES];
6779 - (void) clearFirstResponder {
6780 if (id responder = [window_ firstResponder])
6781 [responder resignFirstResponder];
6784 - (RVPage *) pageForPackage:(NSString *)name {
6785 if (Package *package = [database_ packageWithName:name]) {
6786 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6787 [view setPackage:package];
6790 UIActionSheet *sheet = [[[UIActionSheet alloc]
6791 initWithTitle:@"Cannot Locate Package"
6792 buttons:[NSArray arrayWithObjects:@"Close", nil]
6793 defaultButtonIndex:0
6798 [sheet setBodyText:[NSString stringWithFormat:
6799 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6802 [sheet popupAlertAnimated:YES];
6807 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6811 NSString *scheme([[url scheme] lowercaseString]);
6812 if (![scheme isEqualToString:@"cydia"])
6814 NSString *path([url absoluteString]);
6815 if ([path length] < 8)
6817 path = [path substringFromIndex:8];
6818 if (![path hasPrefix:@"/"])
6819 path = [@"/" stringByAppendingString:path];
6821 if ([path isEqualToString:@"/add-source"])
6822 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6823 else if ([path isEqualToString:@"/storage"])
6824 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6825 else if ([path isEqualToString:@"/sources"])
6826 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6827 else if ([path isEqualToString:@"/packages"])
6828 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6829 else if ([path hasPrefix:@"/url/"])
6830 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6831 else if ([path hasPrefix:@"/launch/"])
6832 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6833 else if ([path hasPrefix:@"/package-settings/"])
6834 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6835 else if ([path hasPrefix:@"/package-signature/"])
6836 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6837 else if ([path hasPrefix:@"/package/"])
6838 return [self pageForPackage:[path substringFromIndex:9]];
6839 else if ([path hasPrefix:@"/files/"]) {
6840 NSString *name = [path substringFromIndex:7];
6842 if (Package *package = [database_ packageWithName:name]) {
6843 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6844 [files setPackage:package];
6852 - (void) applicationOpenURL:(NSURL *)url {
6853 [super applicationOpenURL:url];
6855 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6856 [self setPage:page];
6857 [buttonbar_ showSelectionForButton:tag];
6862 - (void) applicationDidFinishLaunching:(id)unused {
6864 Font12_ = [[UIFont systemFontOfSize:12] retain];
6865 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6866 Font14_ = [[UIFont systemFontOfSize:14] retain];
6867 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6868 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6870 _assert(pkgInitConfig(*_config));
6871 _assert(pkgInitSystem(*_config, _system));
6875 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6876 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6878 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6880 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6881 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6883 [window_ orderFront:self];
6884 [window_ makeKey:self];
6885 [window_ setHidden:NO];
6887 database_ = [Database sharedInstance];
6888 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6889 [database_ setDelegate:progress_];
6890 [window_ setContentView:progress_];
6892 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6893 [progress_ setContentView:underlay_];
6895 [progress_ resetView];
6898 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6899 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6900 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6901 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6902 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6903 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6904 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6906 [self setIdleTimerDisabled:YES];
6908 hud_ = [[self addProgressHUD] retain];
6909 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6911 [self setStatusBarShowsProgress:YES];
6914 detachNewThreadSelector:@selector(reorganize)
6922 - (void) showKeyboard:(BOOL)show {
6923 CGSize keysize = [UIKeyboard defaultSize];
6924 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6925 CGRect keyup = keydown;
6926 keyup.origin.y -= keysize.height;
6928 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6929 [animation setSignificantRectFields:2];
6932 [animation setStartFrame:keydown];
6933 [animation setEndFrame:keyup];
6934 [keyboard_ activate];
6936 [animation setStartFrame:keyup];
6937 [animation setEndFrame:keydown];
6938 [keyboard_ deactivate];
6941 [[UIAnimator sharedAnimator]
6942 addAnimations:[NSArray arrayWithObjects:animation, nil]
6943 withDuration:KeyboardTime_
6948 - (void) slideUp:(UIActionSheet *)alert {
6950 [alert presentSheetFromButtonBar:buttonbar_];
6952 [alert presentSheetInView:overlay_];
6957 void AddPreferences(NSString *plist) { _pooled
6958 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6959 _assert(settings != NULL);
6960 NSMutableArray *items = [settings objectForKey:@"items"];
6964 for (size_t i(0); i != [items count]; ++i) {
6965 NSMutableDictionary *item([items objectAtIndex:i]);
6966 NSString *label = [item objectForKey:@"label"];
6967 if (label != nil && [label isEqualToString:@"Cydia"]) {
6974 for (size_t i(0); i != [items count]; ++i) {
6975 NSDictionary *item([items objectAtIndex:i]);
6976 NSString *label = [item objectForKey:@"label"];
6977 if (label != nil && [label isEqualToString:@"General"]) {
6978 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6979 @"CydiaSettings", @"bundle",
6980 @"PSLinkCell", @"cell",
6981 [NSNumber numberWithBool:YES], @"hasIcon",
6982 [NSNumber numberWithBool:YES], @"isController",
6984 nil] atIndex:(i + 1)];
6990 _assert([settings writeToFile:plist atomically:YES] == YES);
6995 id Alloc_(id self, SEL selector) {
6996 id object = alloc_(self, selector);
6997 lprintf("[%s]A-%p\n", self->isa->name, object);
7002 id Dealloc_(id self, SEL selector) {
7003 id object = dealloc_(self, selector);
7004 lprintf("[%s]D-%p\n", self->isa->name, object);
7008 int main(int argc, char *argv[]) { _pooled
7010 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7012 bool substrate(false);
7018 for (int argi(1); argi != argc; ++argi)
7019 if (strcmp(argv[argi], "--") == 0) {
7021 argv[argi] = argv[0];
7027 for (int argi(1); argi != arge; ++argi)
7028 if (strcmp(args[argi], "--bootstrap") == 0)
7030 else if (strcmp(args[argi], "--substrate") == 0)
7033 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7036 App_ = [[NSBundle mainBundle] bundlePath];
7037 Home_ = NSHomeDirectory();
7038 Locale_ = CFLocaleCopyCurrent();
7041 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7042 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7043 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7044 Sounds_Keyboard_ = [keyboard boolValue];
7050 #if 1 /* XXX: this costs 1.4s of startup performance */
7051 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7052 _assert(errno == ENOENT);
7053 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7054 _assert(errno == ENOENT);
7057 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7058 alloc_ = alloc->method_imp;
7059 alloc->method_imp = (IMP) &Alloc_;*/
7061 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7062 dealloc_ = dealloc->method_imp;
7063 dealloc->method_imp = (IMP) &Dealloc_;*/
7068 size = sizeof(maxproc);
7069 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7070 perror("sysctlbyname(\"kern.maxproc\", ?)");
7071 else if (maxproc < 64) {
7073 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7074 perror("sysctlbyname(\"kern.maxproc\", #)");
7077 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7078 char *machine = new char[size];
7079 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7080 perror("sysctlbyname(\"hw.machine\", ?)");
7084 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7086 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7087 Build_ = [system objectForKey:@"ProductBuildVersion"];
7089 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7090 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7093 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7096 if (Metadata_ == NULL)
7097 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7099 Settings_ = [Metadata_ objectForKey:@"Settings"];
7101 Packages_ = [Metadata_ objectForKey:@"Packages"];
7102 Sections_ = [Metadata_ objectForKey:@"Sections"];
7103 Sources_ = [Metadata_ objectForKey:@"Sources"];
7106 if (Settings_ != nil)
7107 Role_ = [Settings_ objectForKey:@"Role"];
7109 if (Packages_ == nil) {
7110 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7111 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7114 if (Sections_ == nil) {
7115 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7116 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7119 if (Sources_ == nil) {
7120 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7121 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7125 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7128 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7129 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7130 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7131 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7133 if (access("/User", F_OK) != 0) {
7135 system("/usr/libexec/cydia/firmware.sh");
7139 _assert([[NSFileManager defaultManager]
7140 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7141 withIntermediateDirectories:YES
7146 space_ = CGColorSpaceCreateDeviceRGB();
7148 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7149 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7150 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7151 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7152 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7153 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7155 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7157 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7159 UIApplicationUseLegacyEvents(YES);
7160 UIKeyboardDisableAutomaticAppearance();
7163 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7165 CGColorSpaceRelease(space_);