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/message.h>
45 #include <objc/objc.h>
46 #include <objc/runtime.h>
48 #include <CoreGraphics/CoreGraphics.h>
49 #include <GraphicsServices/GraphicsServices.h>
50 #include <Foundation/Foundation.h>
52 #import <QuartzCore/CALayer.h>
54 #import <UIKit/UIKit.h>
57 #import <MessageUI/MailComposeController.h>
63 #include <ext/stdio_filebuf.h>
65 #include <apt-pkg/acquire.h>
66 #include <apt-pkg/acquire-item.h>
67 #include <apt-pkg/algorithms.h>
68 #include <apt-pkg/cachefile.h>
69 #include <apt-pkg/clean.h>
70 #include <apt-pkg/configuration.h>
71 #include <apt-pkg/debmetaindex.h>
72 #include <apt-pkg/error.h>
73 #include <apt-pkg/init.h>
74 #include <apt-pkg/mmap.h>
75 #include <apt-pkg/pkgrecords.h>
76 #include <apt-pkg/sha1.h>
77 #include <apt-pkg/sourcelist.h>
78 #include <apt-pkg/sptr.h>
79 #include <apt-pkg/strutl.h>
81 #include <sys/types.h>
83 #include <sys/sysctl.h>
84 #include <sys/param.h>
85 #include <sys/mount.h>
91 #include <mach-o/nlist.h>
101 #import "BrowserView.h"
102 #import "ResetView.h"
104 #import "substrate.h"
107 //#define _finline __attribute__((force_inline))
108 #define _finline inline
113 #define _limit(count) do { \
114 static size_t _count(0); \
115 if (++_count == count) \
119 #define _timestamp ({ \
121 gettimeofday(&tv, NULL); \
122 tv.tv_sec * 1000000 + tv.tv_usec; \
125 typedef std::vector<class ProfileTime *> TimeList;
135 ProfileTime(const char *name) :
139 times_.push_back(this);
142 void AddTime(uint64_t time) {
149 std::cerr << std::setw(5) << count_ << ", " << std::setw(7) << total_ << " : " << name_ << std::endl;
161 ProfileTimer(ProfileTime &time) :
168 time_.AddTime(_timestamp - start_);
173 for (TimeList::const_iterator i(times_.begin()); i != times_.end(); ++i)
175 std::cerr << "========" << std::endl;
178 #define _profile(name) { \
179 static ProfileTime name(#name); \
180 ProfileTimer _ ## name(name);
184 /* Objective-C Handle<> {{{ */
185 template <typename Type_>
187 typedef _H<Type_> This_;
192 _finline void Retain_() {
197 _finline void Clear_() {
203 _finline _H(Type_ *value = NULL, bool mended = false) :
214 _finline This_ &operator =(Type_ *value) {
215 if (value_ != value) {
224 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
226 void NSLogPoint(const char *fix, const CGPoint &point) {
227 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
230 void NSLogRect(const char *fix, const CGRect &rect) {
231 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
234 @interface NSObject (Cydia)
235 - (id) yieldToSelector:(SEL)selector withObject:(id)object;
236 - (id) yieldToSelector:(SEL)selector;
239 @implementation NSObject (Cydia)
244 - (void) _yieldToContext:(NSMutableArray *)context { _pooled
245 SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
246 id object([[context objectAtIndex:1] nonretainedObjectValue]);
247 volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
249 /* XXX: deal with exceptions */
250 id value([self performSelector:selector withObject:object]);
252 [context removeAllObjects];
254 [context addObject:value];
259 performSelectorOnMainThread:@selector(doNothing)
265 - (id) yieldToSelector:(SEL)selector withObject:(id)object {
266 /*return [self performSelector:selector withObject:object];*/
268 volatile bool stopped(false);
270 NSMutableArray *context([NSMutableArray arrayWithObjects:
271 [NSValue valueWithPointer:selector],
272 [NSValue valueWithNonretainedObject:object],
273 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
276 NSThread *thread([[[NSThread alloc]
278 selector:@selector(_yieldToContext:)
284 NSRunLoop *loop([NSRunLoop currentRunLoop]);
285 NSDate *future([NSDate distantFuture]);
287 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
289 return [context count] == 0 ? nil : [context objectAtIndex:0];
292 - (id) yieldToSelector:(SEL)selector {
293 return [self yieldToSelector:selector withObject:nil];
298 /* NSForcedOrderingSearch doesn't work on the iPhone */
299 static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
300 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
301 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
302 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
304 /* iPhoneOS 2.0 Compatibility {{{ */
306 @interface UITextView (iPhoneOS)
307 - (void) setTextSize:(float)size;
310 @implementation UITextView (iPhoneOS)
312 - (void) setTextSize:(float)size {
313 [self setFont:[[self font] fontWithSize:size]];
320 extern NSString * const kCAFilterNearest;
322 /* Information Dictionaries {{{ */
323 @interface NSMutableArray (Cydia)
324 - (void) addInfoDictionary:(NSDictionary *)info;
327 @implementation NSMutableArray (Cydia)
329 - (void) addInfoDictionary:(NSDictionary *)info {
330 [self addObject:info];
335 @interface NSMutableDictionary (Cydia)
336 - (void) addInfoDictionary:(NSDictionary *)info;
339 @implementation NSMutableDictionary (Cydia)
341 - (void) addInfoDictionary:(NSDictionary *)info {
342 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
343 [self setObject:info forKey:bundle];
348 /* Pop Transitions {{{ */
349 @interface PopTransitionView : UITransitionView {
354 @implementation PopTransitionView
356 - (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
357 if (from != nil && to == nil)
358 [self removeFromSuperview];
363 @implementation UIView (PopUpView)
365 - (void) popFromSuperviewAnimated:(BOOL)animated {
366 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
369 - (void) popSubview:(UIView *)view {
370 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
371 [transition setDelegate:transition];
372 [self addSubview:transition];
374 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
375 [transition transition:UITransitionNone toView:blank];
376 [transition transition:UITransitionPushFromBottom toView:view];
382 #define lprintf(args...) fprintf(stderr, args)
385 #define ForSaurik (1 && !ForRelease)
386 #define LogBrowser (0 && !ForRelease)
387 #define ManualRefresh (1 && !ForRelease)
388 #define ShowInternals (1 && !ForRelease)
389 #define IgnoreInstall (0 && !ForRelease)
390 #define RecycleWebViews 0
391 #define AlwaysReload (0 && !ForRelease)
395 #define _trace(args...)
397 #define _profile(name) {
400 #define PrintTimes() do {} while (false)
404 @interface NSMutableArray (Radix)
405 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
408 @implementation NSMutableArray (Radix)
410 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
411 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
412 [invocation setSelector:selector];
413 [invocation setArgument:&object atIndex:2];
415 size_t count([self count]);
420 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
422 for (size_t i(0); i != count; ++i) {
423 RadixItem &item(lhs[i]);
426 id object([self objectAtIndex:i]);
427 [invocation setTarget:object];
430 [invocation getReturnValue:&item.key];
433 static const size_t width = 32;
434 static const size_t bits = 11;
435 static const size_t slots = 1 << bits;
436 static const size_t passes = (width + (bits - 1)) / bits;
438 size_t *hist(new size_t[slots]);
440 for (size_t pass(0); pass != passes; ++pass) {
441 memset(hist, 0, sizeof(size_t) * slots);
443 for (size_t i(0); i != count; ++i) {
444 uint32_t key(lhs[i].key);
446 key &= _not(uint32_t) >> width - bits;
451 for (size_t i(0); i != slots; ++i) {
452 size_t local(offset);
457 for (size_t i(0); i != count; ++i) {
458 uint32_t key(lhs[i].key);
460 key &= _not(uint32_t) >> width - bits;
461 rhs[hist[key]++] = lhs[i];
471 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
472 for (size_t i(0); i != count; ++i)
473 [values addObject:[self objectAtIndex:lhs[i].index]];
474 [self setArray:values];
482 /* Apple Bug Fixes {{{ */
483 @implementation UIWebDocumentView (Cydia)
485 - (void) _setScrollerOffset:(CGPoint)offset {
486 UIScroller *scroller([self _scroller]);
488 CGSize size([scroller contentSize]);
489 CGSize bounds([scroller bounds].size);
492 max.x = size.width - bounds.width;
493 max.y = size.height - bounds.height;
501 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
502 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
504 [scroller setOffset:offset];
511 kUIControlEventMouseDown = 1 << 0,
512 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
513 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
514 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
515 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
516 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
517 } UIControlEventMasks;
519 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
520 size_t length([self length] - state->state);
523 else if (length > count)
525 for (size_t i(0); i != length; ++i)
526 objects[i] = [self item:state->state++];
527 state->itemsPtr = objects;
528 state->mutationsPtr = (unsigned long *) self;
532 @interface NSString (UIKit)
533 - (NSString *) stringByAddingPercentEscapes;
534 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
537 @interface NSString (Cydia)
538 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
539 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
540 - (NSComparisonResult) compareByPath:(NSString *)other;
541 - (NSString *) stringByCachingURLWithCurrentCDN;
542 - (NSString *) stringByAddingPercentEscapesIncludingReserved;
545 @implementation NSString (Cydia)
547 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length {
548 return [[[NSString alloc] initWithBytesNoCopy:const_cast<char *>(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
551 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
552 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
555 - (NSComparisonResult) compareByPath:(NSString *)other {
556 NSString *prefix = [self commonPrefixWithString:other options:0];
557 size_t length = [prefix length];
559 NSRange lrange = NSMakeRange(length, [self length] - length);
560 NSRange rrange = NSMakeRange(length, [other length] - length);
562 lrange = [self rangeOfString:@"/" options:0 range:lrange];
563 rrange = [other rangeOfString:@"/" options:0 range:rrange];
565 NSComparisonResult value;
567 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
568 value = NSOrderedSame;
569 else if (lrange.location == NSNotFound)
570 value = NSOrderedAscending;
571 else if (rrange.location == NSNotFound)
572 value = NSOrderedDescending;
574 value = NSOrderedSame;
576 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
577 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
578 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
579 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
581 NSComparisonResult result = [lpath compare:rpath];
582 return result == NSOrderedSame ? value : result;
585 - (NSString *) stringByCachingURLWithCurrentCDN {
587 stringByReplacingOccurrencesOfString:@"://"
588 withString:@"://ne.edgecastcdn.net/8003A4/"
590 /* XXX: this is somewhat inaccurate */
591 range:NSMakeRange(0, 10)
595 - (NSString *) stringByAddingPercentEscapesIncludingReserved {
596 return [(id)CFURLCreateStringByAddingPercentEscapes(
601 kCFStringEncodingUTF8
607 /* Perl-Compatible RegEx {{{ */
617 Pcre(const char *regex) :
622 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
625 lprintf("%d:%s\n", offset, error);
629 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
630 matches_ = new int[(capture_ + 1) * 3];
638 NSString *operator [](size_t match) {
639 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
642 bool operator ()(NSString *data) {
643 // XXX: length is for characters, not for bytes
644 return operator ()([data UTF8String], [data length]);
647 bool operator ()(const char *data, size_t size) {
649 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
653 /* Mime Addresses {{{ */
654 @interface Address : NSObject {
660 - (NSString *) address;
662 + (Address *) addressWithString:(NSString *)string;
663 - (Address *) initWithString:(NSString *)string;
666 @implementation Address
675 - (NSString *) name {
679 - (NSString *) address {
683 + (Address *) addressWithString:(NSString *)string {
684 return [[[Address alloc] initWithString:string] autorelease];
687 + (NSArray *) _attributeKeys {
688 return [NSArray arrayWithObjects:@"address", @"name", nil];
691 - (NSArray *) attributeKeys {
692 return [[self class] _attributeKeys];
695 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
696 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
699 - (Address *) initWithString:(NSString *)string {
700 if ((self = [super init]) != nil) {
701 const char *data = [string UTF8String];
702 size_t size = [string length];
704 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
706 if (address_r(data, size)) {
707 name_ = [address_r[1] retain];
708 address_ = [address_r[2] retain];
710 name_ = [string retain];
718 /* CoreGraphics Primitives {{{ */
729 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
732 Set(space, red, green, blue, alpha);
737 CGColorRelease(color_);
744 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
746 float color[] = {red, green, blue, alpha};
747 color_ = CGColorCreate(space, color);
750 operator CGColorRef() {
756 extern "C" void UISetColor(CGColorRef color);
758 /* Random Global Variables {{{ */
759 static const int PulseInterval_ = 50000;
760 static const int ButtonBarHeight_ = 48;
761 static const float KeyboardTime_ = 0.3f;
763 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
764 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
765 #define NotifyConfig_ "/etc/notify.conf"
767 static CGColor Blue_;
768 static CGColor Blueish_;
769 static CGColor Black_;
771 static CGColor White_;
772 static CGColor Gray_;
773 static CGColor Green_;
774 static CGColor Purple_;
775 static CGColor Purplish_;
777 static UIColor *CommercialColor_;
779 static NSString *App_;
780 static NSString *Home_;
781 static BOOL Sounds_Keyboard_;
783 static BOOL Advanced_;
785 static BOOL Ignored_;
787 static UIFont *Font12_;
788 static UIFont *Font12Bold_;
789 static UIFont *Font14_;
790 static UIFont *Font18Bold_;
791 static UIFont *Font22Bold_;
793 static const char *Machine_ = NULL;
794 static const NSString *UniqueID_ = nil;
795 static const NSString *Build_ = nil;
796 static const NSString *Product_ = nil;
797 static const NSString *Safari_ = nil;
800 CGColorSpaceRef space_;
805 static NSDictionary *SectionMap_;
806 static NSMutableDictionary *Metadata_;
807 static _transient NSMutableDictionary *Settings_;
808 static _transient NSString *Role_;
809 static _transient NSMutableDictionary *Packages_;
810 static _transient NSMutableDictionary *Sections_;
811 static _transient NSMutableDictionary *Sources_;
812 static bool Changed_;
816 static NSMutableArray *Documents_;
819 NSString *GetLastUpdate() {
820 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
823 return @"Never or Unknown";
825 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
826 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
828 CFRelease(formatter);
830 return [(NSString *) formatted autorelease];
833 /* Display Helpers {{{ */
834 inline float Interpolate(float begin, float end, float fraction) {
835 return (end - begin) * fraction + begin;
838 NSString *SizeString(double size) {
839 bool negative = size < 0;
844 while (size > 1024) {
849 static const char *powers_[] = {"B", "kB", "MB", "GB"};
851 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
854 NSString *StripVersion(NSString *version) {
855 NSRange colon = [version rangeOfString:@":"];
856 if (colon.location != NSNotFound)
857 version = [version substringFromIndex:(colon.location + 1)];
861 NSString *Simplify(NSString *title) {
862 const char *data = [title UTF8String];
863 size_t size = [title length];
865 static Pcre square_r("^\\[(.*)\\]$");
866 if (square_r(data, size))
867 return Simplify(square_r[1]);
869 static Pcre paren_r("^\\((.*)\\)$");
870 if (paren_r(data, size))
871 return Simplify(paren_r[1]);
873 static Pcre title_r("^(.*?) \\(.*\\)$");
874 if (title_r(data, size))
875 return Simplify(title_r[1]);
881 bool isSectionVisible(NSString *section) {
882 NSDictionary *metadata = [Sections_ objectForKey:section];
883 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
884 return hidden == nil || ![hidden boolValue];
887 /* Delegate Prototypes {{{ */
891 @interface NSObject (ProgressDelegate)
894 @implementation NSObject(ProgressDelegate)
896 - (void) _setProgressError:(NSArray *)args {
897 [self performSelector:@selector(setProgressError:forPackage:)
898 withObject:[args objectAtIndex:0]
899 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
905 @protocol ProgressDelegate
906 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
907 - (void) setProgressTitle:(NSString *)title;
908 - (void) setProgressPercent:(float)percent;
909 - (void) startProgress;
910 - (void) addProgressOutput:(NSString *)output;
911 - (bool) isCancelling:(size_t)received;
914 @protocol ConfigurationDelegate
915 - (void) repairWithSelector:(SEL)selector;
916 - (void) setConfigurationData:(NSString *)data;
919 @protocol CydiaDelegate
920 - (void) installPackage:(Package *)package;
921 - (void) removePackage:(Package *)package;
922 - (void) slideUp:(UIActionSheet *)alert;
923 - (void) distUpgrade;
926 - (void) askForSettings;
927 - (UIProgressHUD *) addProgressHUD;
928 - (void) removeProgressHUD:(UIProgressHUD *)hud;
929 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
930 - (RVPage *) pageForPackage:(NSString *)name;
931 - (void) openMailToURL:(NSURL *)url;
932 - (void) clearFirstResponder;
936 /* Status Delegation {{{ */
938 public pkgAcquireStatus
941 _transient NSObject<ProgressDelegate> *delegate_;
949 void setDelegate(id delegate) {
950 delegate_ = delegate;
953 virtual bool MediaChange(std::string media, std::string drive) {
957 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
960 virtual void Fetch(pkgAcquire::ItemDesc &item) {
961 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
962 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
965 virtual void Done(pkgAcquire::ItemDesc &item) {
968 virtual void Fail(pkgAcquire::ItemDesc &item) {
970 item.Owner->Status == pkgAcquire::Item::StatIdle ||
971 item.Owner->Status == pkgAcquire::Item::StatDone
975 std::string &error(item.Owner->ErrorText);
979 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
980 NSArray *fields([description componentsSeparatedByString:@" "]);
981 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
983 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
984 withObject:[NSArray arrayWithObjects:
985 [NSString stringWithUTF8String:error.c_str()],
992 virtual bool Pulse(pkgAcquire *Owner) {
993 bool value = pkgAcquireStatus::Pulse(Owner);
996 double(CurrentBytes + CurrentItems) /
997 double(TotalBytes + TotalItems)
1000 [delegate_ setProgressPercent:percent];
1001 return [delegate_ isCancelling:CurrentBytes] ? false : value;
1004 virtual void Start() {
1005 [delegate_ startProgress];
1008 virtual void Stop() {
1012 /* Progress Delegation {{{ */
1017 _transient id<ProgressDelegate> delegate_;
1020 virtual void Update() {
1021 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
1022 [delegate_ setProgressPercent:(Percent / 100)];*/
1031 void setDelegate(id delegate) {
1032 delegate_ = delegate;
1035 virtual void Done() {
1036 //[delegate_ setProgressPercent:1];
1041 /* Database Interface {{{ */
1042 @interface Database : NSObject {
1045 pkgCacheFile cache_;
1046 pkgDepCache::Policy *policy_;
1047 pkgRecords *records_;
1048 pkgProblemResolver *resolver_;
1049 pkgAcquire *fetcher_;
1051 SPtr<pkgPackageManager> manager_;
1052 pkgSourceList *list_;
1054 NSMutableDictionary *sources_;
1055 NSMutableArray *packages_;
1057 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
1066 + (Database *) sharedInstance;
1069 - (void) _readCydia:(NSNumber *)fd;
1070 - (void) _readStatus:(NSNumber *)fd;
1071 - (void) _readOutput:(NSNumber *)fd;
1075 - (Package *) packageWithName:(NSString *)name;
1077 - (pkgCacheFile &) cache;
1078 - (pkgDepCache::Policy *) policy;
1079 - (pkgRecords *) records;
1080 - (pkgProblemResolver *) resolver;
1081 - (pkgAcquire &) fetcher;
1082 - (pkgSourceList &) list;
1083 - (NSArray *) packages;
1084 - (NSArray *) sources;
1085 - (void) reloadData;
1093 - (void) updateWithStatus:(Status &)status;
1095 - (void) setDelegate:(id)delegate;
1096 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
1100 /* Source Class {{{ */
1101 @interface Source : NSObject {
1102 NSString *description_;
1107 NSString *distribution_;
1111 NSString *defaultIcon_;
1113 NSDictionary *record_;
1117 - (Source *) initWithMetaIndex:(metaIndex *)index;
1119 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1121 - (NSDictionary *) record;
1125 - (NSString *) distribution;
1126 - (NSString *) type;
1128 - (NSString *) host;
1130 - (NSString *) name;
1131 - (NSString *) description;
1132 - (NSString *) label;
1133 - (NSString *) origin;
1134 - (NSString *) version;
1136 - (NSString *) defaultIcon;
1140 @implementation Source
1142 #define _clear(field) \
1149 _clear(distribution_)
1152 _clear(description_)
1156 _clear(defaultIcon_)
1165 + (NSArray *) _attributeKeys {
1166 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1169 - (NSArray *) attributeKeys {
1170 return [[self class] _attributeKeys];
1173 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1174 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1177 - (void) setMetaIndex:(metaIndex *)index {
1180 trusted_ = index->IsTrusted();
1182 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1183 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1184 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1186 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1187 if (dindex != NULL) {
1188 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1190 while (std::getline(release, line)) {
1191 std::string::size_type colon(line.find(':'));
1192 if (colon == std::string::npos)
1195 std::string name(line.substr(0, colon));
1196 std::string value(line.substr(colon + 1));
1197 while (!value.empty() && value[0] == ' ')
1198 value = value.substr(1);
1200 if (name == "Default-Icon")
1201 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1202 else if (name == "Description")
1203 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1204 else if (name == "Label")
1205 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1206 else if (name == "Origin")
1207 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1208 else if (name == "Version")
1209 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1213 record_ = [Sources_ objectForKey:[self key]];
1215 record_ = [record_ retain];
1218 - (Source *) initWithMetaIndex:(metaIndex *)index {
1219 if ((self = [super init]) != nil) {
1220 [self setMetaIndex:index];
1224 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1225 NSDictionary *lhr = [self record];
1226 NSDictionary *rhr = [source record];
1229 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1231 NSString *lhs = [self name];
1232 NSString *rhs = [source name];
1234 if ([lhs length] != 0 && [rhs length] != 0) {
1235 unichar lhc = [lhs characterAtIndex:0];
1236 unichar rhc = [rhs characterAtIndex:0];
1238 if (isalpha(lhc) && !isalpha(rhc))
1239 return NSOrderedAscending;
1240 else if (!isalpha(lhc) && isalpha(rhc))
1241 return NSOrderedDescending;
1244 return [lhs compare:rhs options:LaxCompareOptions_];
1247 - (NSDictionary *) record {
1255 - (NSString *) uri {
1259 - (NSString *) distribution {
1260 return distribution_;
1263 - (NSString *) type {
1267 - (NSString *) key {
1268 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1271 - (NSString *) host {
1272 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1275 - (NSString *) name {
1276 return origin_ == nil ? [self host] : origin_;
1279 - (NSString *) description {
1280 return description_;
1283 - (NSString *) label {
1284 return label_ == nil ? [self host] : label_;
1287 - (NSString *) origin {
1291 - (NSString *) version {
1295 - (NSString *) defaultIcon {
1296 return defaultIcon_;
1301 /* Relationship Class {{{ */
1302 @interface Relationship : NSObject {
1307 - (NSString *) type;
1309 - (NSString *) name;
1313 @implementation Relationship
1321 - (NSString *) type {
1329 - (NSString *) name {
1336 /* Package Class {{{ */
1337 @interface Package : NSObject {
1340 pkgCache::PkgIterator iterator_;
1341 _transient Database *database_;
1342 pkgCache::VerIterator version_;
1343 pkgCache::VerFileIterator file_;
1352 NSString *installed_;
1358 NSString *depiction_;
1359 NSString *homepage_;
1365 NSArray *relationships_;
1368 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1369 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1371 - (pkgCache::PkgIterator) iterator;
1373 - (NSString *) section;
1374 - (NSString *) simpleSection;
1378 - (Address *) maintainer;
1380 - (NSString *) description;
1383 - (NSMutableDictionary *) metadata;
1385 - (BOOL) subscribed;
1388 - (NSString *) latest;
1389 - (NSString *) installed;
1392 - (BOOL) upgradableAndEssential:(BOOL)essential;
1395 - (BOOL) unfiltered;
1399 - (BOOL) halfConfigured;
1400 - (BOOL) halfInstalled;
1402 - (NSString *) mode;
1405 - (NSString *) name;
1406 - (NSString *) tagline;
1408 - (NSString *) homepage;
1409 - (NSString *) depiction;
1410 - (Address *) author;
1412 - (NSArray *) files;
1413 - (NSArray *) relationships;
1414 - (NSArray *) warnings;
1415 - (NSArray *) applications;
1417 - (Source *) source;
1418 - (NSString *) role;
1420 - (BOOL) matches:(NSString *)text;
1422 - (bool) hasSupportingRole;
1423 - (BOOL) hasTag:(NSString *)tag;
1424 - (NSString *) primaryPurpose;
1425 - (NSArray *) purposes;
1426 - (bool) isCommercial;
1428 - (NSComparisonResult) compareByName:(Package *)package;
1429 - (NSComparisonResult) compareBySection:(Package *)package;
1431 - (uint32_t) compareForChanges;
1436 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
1437 - (bool) isInstalledAndVisible:(NSNumber *)number;
1438 - (bool) isVisiblyUninstalledInSection:(NSString *)section;
1439 - (bool) isVisibleInSource:(Source *)source;
1443 @implementation Package
1448 if (section_ != nil)
1452 if (installed_ != nil)
1453 [installed_ release];
1461 if (depiction_ != nil)
1462 [depiction_ release];
1463 if (homepage_ != nil)
1464 [homepage_ release];
1465 if (sponsor_ != nil)
1474 if (relationships_ != nil)
1475 [relationships_ release];
1480 + (NSString *) webScriptNameForSelector:(SEL)selector {
1481 if (selector == @selector(hasTag:))
1487 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
1488 return [self webScriptNameForSelector:selector] == nil;
1491 + (NSArray *) _attributeKeys {
1492 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1495 - (NSArray *) attributeKeys {
1496 return [[self class] _attributeKeys];
1499 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1500 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1503 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1504 if ((self = [super init]) != nil) {
1505 _profile(Package$initWithIterator)
1506 @synchronized (database) {
1507 era_ = [database era];
1509 iterator_ = iterator;
1510 database_ = database;
1512 _profile(Package$initWithIterator$Control)
1515 _profile(Package$initWithIterator$Version)
1516 version_ = [database_ policy]->GetCandidateVer(iterator_);
1519 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1521 _profile(Package$initWithIterator$Latest)
1522 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1525 pkgCache::VerIterator current;
1526 NSString *installed;
1528 _profile(Package$initWithIterator$Current)
1529 current = iterator_.CurrentVer();
1530 installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1533 _profile(Package$initWithIterator$Installed)
1534 installed_ = [StripVersion(installed) retain];
1537 _profile(Package$initWithIterator$File)
1538 if (!version_.end())
1539 file_ = version_.FileList();
1541 pkgCache &cache([database_ cache]);
1542 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1546 _profile(Package$initWithIterator$Name)
1547 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1551 _profile(Package$initWithIterator$Parse)
1552 pkgRecords::Parser *parser;
1554 _profile(Package$initWithIterator$Parse$Lookup)
1555 parser = &[database_ records]->Lookup(file_);
1558 const char *begin, *end;
1559 parser->GetRec(begin, end);
1561 NSString *website(nil);
1562 NSString *sponsor(nil);
1563 NSString *author(nil);
1572 {"depiction", &depiction_},
1573 {"homepage", &homepage_},
1574 {"website", &website},
1575 {"sponsor", &sponsor},
1576 {"author", &author},
1580 while (begin != end)
1581 if (*begin == '\n') {
1584 } else if (isblank(*begin)) next: {
1585 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1588 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1589 const char *name(begin);
1590 size_t size(colon - begin);
1592 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1595 const char *stop(begin == NULL ? end : begin);
1596 while (stop[-1] == '\r')
1598 while (++colon != stop && isblank(*colon));
1600 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1601 if (strncasecmp(names[i].name_, name, size) == 0) {
1604 _profile(Package$initWithIterator$Parse$Value)
1605 value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
1608 *names[i].value_ = value;
1618 _profile(Package$initWithIterator$Parse$Retain)
1620 name_ = [name_ retain];
1621 _profile(Package$initWithIterator$Parse$Tagline)
1622 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1625 icon_ = [icon_ retain];
1626 if (depiction_ != nil)
1627 depiction_ = [depiction_ retain];
1628 if (homepage_ == nil)
1629 homepage_ = website;
1630 if ([homepage_ isEqualToString:depiction_])
1632 if (homepage_ != nil)
1633 homepage_ = [homepage_ retain];
1635 sponsor_ = [[Address addressWithString:sponsor] retain];
1637 author_ = [[Address addressWithString:author] retain];
1639 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1643 _profile(Package$initWithIterator$Tags)
1645 for (NSString *tag in tags_)
1646 if ([tag hasPrefix:@"role::"]) {
1647 role_ = [[tag substringFromIndex:6] retain];
1652 NSString *solid(latest == nil ? installed : latest);
1653 bool changed(false);
1655 NSString *key([id_ lowercaseString]);
1657 _profile(Package$initWithIterator$Metadata)
1658 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1659 if (metadata == nil) {
1660 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1665 [metadata setObject:solid forKey:@"LastVersion"];
1668 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1669 NSDate *last([metadata objectForKey:@"LastSeen"]);
1670 NSString *version([metadata objectForKey:@"LastVersion"]);
1673 first = last == nil ? now_ : last;
1674 [metadata setObject:first forKey:@"FirstSeen"];
1679 if (version == nil) {
1680 [metadata setObject:solid forKey:@"LastVersion"];
1682 } else if (![version isEqualToString:solid]) {
1683 [metadata setObject:solid forKey:@"LastVersion"];
1685 [metadata setObject:last forKey:@"LastSeen"];
1691 [Packages_ setObject:metadata forKey:key];
1696 const char *section(iterator_.Section());
1697 if (section == NULL)
1700 NSString *name([[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_']);
1703 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1704 if (NSString *rename = [value objectForKey:@"Rename"]) {
1709 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1712 essential_ = (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1713 } _end } return self;
1716 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1717 return [[[Package alloc]
1718 initWithIterator:iterator
1723 - (pkgCache::PkgIterator) iterator {
1727 - (NSString *) section {
1731 - (NSString *) simpleSection {
1732 if (NSString *section = [self section])
1733 return Simplify(section);
1738 - (NSString *) uri {
1741 pkgIndexFile *index;
1742 pkgCache::PkgFileIterator file(file_.File());
1743 if (![database_ list].FindIndex(file, index))
1745 return [NSString stringWithUTF8String:iterator_->Path];
1746 //return [NSString stringWithUTF8String:file.Site()];
1747 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1751 - (Address *) maintainer {
1754 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1755 const std::string &maintainer(parser->Maintainer());
1756 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1760 return version_.end() ? 0 : version_->InstalledSize;
1763 - (NSString *) description {
1766 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1767 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1769 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1770 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1771 if ([lines count] < 2)
1774 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1775 for (size_t i(1), e([lines count]); i != e; ++i) {
1776 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1777 [trimmed addObject:trim];
1780 return [trimmed componentsJoinedByString:@"\n"];
1784 _profile(Package$index)
1785 NSString *name([self name]);
1786 if ([name length] == 0)
1788 unichar character([name characterAtIndex:0]);
1789 if (!isalpha(character))
1791 return toupper(character);
1795 - (NSMutableDictionary *) metadata {
1796 return [Packages_ objectForKey:[id_ lowercaseString]];
1800 NSDictionary *metadata([self metadata]);
1801 if ([self subscribed])
1802 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1804 return [metadata objectForKey:@"FirstSeen"];
1807 - (BOOL) subscribed {
1808 NSDictionary *metadata([self metadata]);
1809 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1810 return [subscribed boolValue];
1816 NSDictionary *metadata([self metadata]);
1817 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1818 return [ignored boolValue];
1823 - (NSString *) latest {
1827 - (NSString *) installed {
1832 return !version_.end();
1835 - (BOOL) upgradableAndEssential:(BOOL)essential {
1836 pkgCache::VerIterator current = iterator_.CurrentVer();
1840 value = essential && [self essential] && [self visible];
1842 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1846 - (BOOL) essential {
1851 return [database_ cache][iterator_].InstBroken();
1854 - (BOOL) unfiltered {
1855 NSString *section = [self section];
1856 return section == nil || isSectionVisible(section);
1860 return [self hasSupportingRole] && [self unfiltered];
1864 unsigned char current = iterator_->CurrentState;
1865 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1868 - (BOOL) halfConfigured {
1869 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1872 - (BOOL) halfInstalled {
1873 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1877 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1878 return state.Mode != pkgDepCache::ModeKeep;
1881 - (NSString *) mode {
1882 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1884 switch (state.Mode) {
1885 case pkgDepCache::ModeDelete:
1886 if ((state.iFlags & pkgDepCache::Purge) != 0)
1890 case pkgDepCache::ModeKeep:
1891 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1895 case pkgDepCache::ModeInstall:
1896 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1897 return @"Reinstall";
1898 else switch (state.Status) {
1900 return @"Downgrade";
1906 return @"New Install";
1919 - (NSString *) name {
1920 return name_ == nil ? id_ : name_;
1923 - (NSString *) tagline {
1927 - (UIImage *) icon {
1928 NSString *section = [self simpleSection];
1932 if ([icon_ hasPrefix:@"file:///"])
1933 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1934 if (icon == nil) if (section != nil)
1935 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1936 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1937 if ([dicon hasPrefix:@"file:///"])
1938 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1940 icon = [UIImage applicationImageNamed:@"unknown.png"];
1944 - (NSString *) homepage {
1948 - (NSString *) depiction {
1952 - (Address *) sponsor {
1956 - (Address *) author {
1960 - (NSArray *) files {
1961 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1962 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1965 fin.open([path UTF8String]);
1970 while (std::getline(fin, line))
1971 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1976 - (NSArray *) relationships {
1977 return relationships_;
1980 - (NSArray *) warnings {
1981 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1982 const char *name(iterator_.Name());
1984 size_t length(strlen(name));
1985 if (length < 2) invalid:
1986 [warnings addObject:@"illegal package identifier"];
1987 else for (size_t i(0); i != length; ++i)
1989 /* XXX: technically this is not allowed */
1990 (name[i] < 'A' || name[i] > 'Z') &&
1991 (name[i] < 'a' || name[i] > 'z') &&
1992 (name[i] < '0' || name[i] > '9') &&
1993 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1996 if (strcmp(name, "cydia") != 0) {
1998 bool _private = false;
2001 bool repository = [[self section] isEqualToString:@"Repositories"];
2003 if (NSArray *files = [self files])
2004 for (NSString *file in files)
2005 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
2007 else if (!_private && [file isEqualToString:@"/private"])
2009 else if (!stash && [file isEqualToString:@"/var/stash"])
2012 /* XXX: this is not sensitive enough. only some folders are valid. */
2013 if (cydia && !repository)
2014 [warnings addObject:@"files installed into Cydia.app"];
2016 [warnings addObject:@"files installed with /private/*"];
2018 [warnings addObject:@"files installed to /var/stash"];
2021 return [warnings count] == 0 ? nil : warnings;
2024 - (NSArray *) applications {
2025 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
2027 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
2029 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
2030 if (NSArray *files = [self files])
2031 for (NSString *file in files)
2032 if (application_r(file)) {
2033 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
2034 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
2035 if ([id isEqualToString:me])
2038 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
2040 display = application_r[1];
2042 NSString *bundle([file stringByDeletingLastPathComponent]);
2043 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
2044 if (icon == nil || [icon length] == 0)
2046 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
2048 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
2049 [applications addObject:application];
2051 [application addObject:id];
2052 [application addObject:display];
2053 [application addObject:url];
2056 return [applications count] == 0 ? nil : applications;
2059 - (Source *) source {
2061 @synchronized (database_) {
2062 if ([database_ era] != era_ || file_.end())
2065 source_ = [database_ getSource:file_.File()];
2077 - (NSString *) role {
2081 - (BOOL) matches:(NSString *)text {
2087 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2088 if (range.location != NSNotFound)
2091 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2092 if (range.location != NSNotFound)
2095 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2096 if (range.location != NSNotFound)
2102 - (bool) hasSupportingRole {
2105 if ([role_ isEqualToString:@"enduser"])
2107 if ([Role_ isEqualToString:@"User"])
2109 if ([role_ isEqualToString:@"hacker"])
2111 if ([Role_ isEqualToString:@"Hacker"])
2113 if ([role_ isEqualToString:@"developer"])
2115 if ([Role_ isEqualToString:@"Developer"])
2120 - (BOOL) hasTag:(NSString *)tag {
2121 return tags_ == nil ? NO : [tags_ containsObject:tag];
2124 - (NSString *) primaryPurpose {
2125 for (NSString *tag in tags_)
2126 if ([tag hasPrefix:@"purpose::"])
2127 return [tag substringFromIndex:9];
2131 - (NSArray *) purposes {
2132 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2133 for (NSString *tag in tags_)
2134 if ([tag hasPrefix:@"purpose::"])
2135 [purposes addObject:[tag substringFromIndex:9]];
2136 return [purposes count] == 0 ? nil : purposes;
2139 - (bool) isCommercial {
2140 return [self hasTag:@"cydia::commercial"];
2143 - (NSComparisonResult) compareByName:(Package *)package {
2144 NSString *lhs = [self name];
2145 NSString *rhs = [package name];
2147 if ([lhs length] != 0 && [rhs length] != 0) {
2148 unichar lhc = [lhs characterAtIndex:0];
2149 unichar rhc = [rhs characterAtIndex:0];
2151 if (isalpha(lhc) && !isalpha(rhc))
2152 return NSOrderedAscending;
2153 else if (!isalpha(lhc) && isalpha(rhc))
2154 return NSOrderedDescending;
2157 return [lhs compare:rhs options:LaxCompareOptions_];
2160 - (NSComparisonResult) compareBySection:(Package *)package {
2161 NSString *lhs = [self section];
2162 NSString *rhs = [package section];
2164 if (lhs == NULL && rhs != NULL)
2165 return NSOrderedAscending;
2166 else if (lhs != NULL && rhs == NULL)
2167 return NSOrderedDescending;
2168 else if (lhs != NULL && rhs != NULL) {
2169 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2170 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2173 return NSOrderedSame;
2176 - (uint32_t) compareForChanges {
2181 uint32_t timestamp : 30;
2182 uint32_t ignored : 1;
2183 uint32_t upgradable : 1;
2187 bool upgradable([self upgradableAndEssential:YES]);
2188 value.bits.upgradable = upgradable ? 1 : 0;
2191 value.bits.timestamp = 0;
2192 value.bits.ignored = [self ignored] ? 0 : 1;
2193 value.bits.upgradable = 1;
2195 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2196 value.bits.ignored = 0;
2197 value.bits.upgradable = 0;
2200 return _not(uint32_t) - value.key;
2204 pkgProblemResolver *resolver = [database_ resolver];
2205 resolver->Clear(iterator_);
2206 resolver->Protect(iterator_);
2207 pkgCacheFile &cache([database_ cache]);
2208 cache->MarkInstall(iterator_, false);
2209 pkgDepCache::StateCache &state((*cache)[iterator_]);
2210 if (!state.Install())
2211 cache->SetReInstall(iterator_, true);
2215 pkgProblemResolver *resolver = [database_ resolver];
2216 resolver->Clear(iterator_);
2217 resolver->Protect(iterator_);
2218 resolver->Remove(iterator_);
2219 [database_ cache]->MarkDelete(iterator_, true);
2222 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
2223 _profile(Package$isUnfilteredAndSearchedForBy)
2226 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2227 value &= [self unfiltered];
2230 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2231 value &= [self matches:search];
2238 - (bool) isInstalledAndVisible:(NSNumber *)number {
2239 return (![number boolValue] || [self visible]) && [self installed] != nil;
2242 - (bool) isVisiblyUninstalledInSection:(NSString *)name {
2243 NSString *section = [self section];
2247 [self installed] == nil && (
2249 section == nil && [name length] == 0 ||
2250 [name isEqualToString:section]
2254 - (bool) isVisibleInSource:(Source *)source {
2255 return [self source] == source && [self visible];
2260 /* Section Class {{{ */
2261 @interface Section : NSObject {
2268 - (NSComparisonResult) compareByName:(Section *)section;
2269 - (Section *) initWithName:(NSString *)name;
2270 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2271 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2272 - (NSString *) name;
2276 - (void) addToCount;
2280 @implementation Section
2287 - (NSComparisonResult) compareByName:(Section *)section {
2288 NSString *lhs = [self name];
2289 NSString *rhs = [section name];
2291 if ([lhs length] != 0 && [rhs length] != 0) {
2292 unichar lhc = [lhs characterAtIndex:0];
2293 unichar rhc = [rhs characterAtIndex:0];
2295 if (isalpha(lhc) && !isalpha(rhc))
2296 return NSOrderedAscending;
2297 else if (!isalpha(lhc) && isalpha(rhc))
2298 return NSOrderedDescending;
2301 return [lhs compare:rhs options:LaxCompareOptions_];
2304 - (Section *) initWithName:(NSString *)name {
2305 return [self initWithName:name row:0];
2308 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2309 if ((self = [super init]) != nil) {
2310 name_ = [name retain];
2316 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2317 if ((self = [super init]) != nil) {
2318 name_ = [(index == '#' ? @"123" : [NSString stringWithCharacters:&index length:1]) retain];
2324 - (NSString *) name {
2340 - (void) addToCount {
2348 static NSArray *Finishes_;
2350 /* Database Implementation {{{ */
2351 @implementation Database
2353 + (Database *) sharedInstance {
2354 static Database *instance;
2355 if (instance == nil)
2356 instance = [[Database alloc] init];
2369 - (void) _readCydia:(NSNumber *)fd { _pooled
2370 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2371 std::istream is(&ib);
2374 static Pcre finish_r("^finish:([^:]*)$");
2376 while (std::getline(is, line)) {
2377 const char *data(line.c_str());
2378 size_t size = line.size();
2379 lprintf("C:%s\n", data);
2381 if (finish_r(data, size)) {
2382 NSString *finish = finish_r[1];
2383 int index = [Finishes_ indexOfObject:finish];
2384 if (index != INT_MAX && index > Finish_)
2392 - (void) _readStatus:(NSNumber *)fd { _pooled
2393 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2394 std::istream is(&ib);
2397 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2398 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2400 while (std::getline(is, line)) {
2401 const char *data(line.c_str());
2402 size_t size = line.size();
2403 lprintf("S:%s\n", data);
2405 if (conffile_r(data, size)) {
2406 [delegate_ setConfigurationData:conffile_r[1]];
2407 } else if (strncmp(data, "status: ", 8) == 0) {
2408 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2409 [delegate_ setProgressTitle:string];
2410 } else if (pmstatus_r(data, size)) {
2411 std::string type([pmstatus_r[1] UTF8String]);
2412 NSString *id = pmstatus_r[2];
2414 float percent([pmstatus_r[3] floatValue]);
2415 [delegate_ setProgressPercent:(percent / 100)];
2417 NSString *string = pmstatus_r[4];
2419 if (type == "pmerror")
2420 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2421 withObject:[NSArray arrayWithObjects:string, id, nil]
2424 else if (type == "pmstatus") {
2425 [delegate_ setProgressTitle:string];
2426 } else if (type == "pmconffile")
2427 [delegate_ setConfigurationData:string];
2428 else _assert(false);
2429 } else _assert(false);
2435 - (void) _readOutput:(NSNumber *)fd { _pooled
2436 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2437 std::istream is(&ib);
2440 while (std::getline(is, line)) {
2441 lprintf("O:%s\n", line.c_str());
2442 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2452 - (Package *) packageWithName:(NSString *)name {
2453 if (static_cast<pkgDepCache *>(cache_) == NULL)
2455 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2456 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2459 - (Database *) init {
2460 if ((self = [super init]) != nil) {
2467 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2468 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2472 _assert(pipe(fds) != -1);
2475 _config->Set("APT::Keep-Fds::", cydiafd_);
2476 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2479 detachNewThreadSelector:@selector(_readCydia:)
2481 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2484 _assert(pipe(fds) != -1);
2488 detachNewThreadSelector:@selector(_readStatus:)
2490 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2493 _assert(pipe(fds) != -1);
2494 _assert(dup2(fds[0], 0) != -1);
2495 _assert(close(fds[0]) != -1);
2497 input_ = fdopen(fds[1], "a");
2499 _assert(pipe(fds) != -1);
2500 _assert(dup2(fds[1], 1) != -1);
2501 _assert(close(fds[1]) != -1);
2504 detachNewThreadSelector:@selector(_readOutput:)
2506 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2511 - (pkgCacheFile &) cache {
2515 - (pkgDepCache::Policy *) policy {
2519 - (pkgRecords *) records {
2523 - (pkgProblemResolver *) resolver {
2527 - (pkgAcquire &) fetcher {
2531 - (pkgSourceList &) list {
2535 - (NSArray *) packages {
2539 - (NSArray *) sources {
2540 return [sources_ allValues];
2543 - (NSArray *) issues {
2544 if (cache_->BrokenCount() == 0)
2547 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2549 for (Package *package in packages_) {
2550 if (![package broken])
2552 pkgCache::PkgIterator pkg([package iterator]);
2554 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2555 [entry addObject:[package name]];
2556 [issues addObject:entry];
2558 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2562 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2563 pkgCache::DepIterator start;
2564 pkgCache::DepIterator end;
2565 dep.GlobOr(start, end); // ++dep
2567 if (!cache_->IsImportantDep(end))
2569 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2572 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2573 [entry addObject:failure];
2574 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2576 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2577 [failure addObject:[package name]];
2579 pkgCache::PkgIterator target(start.TargetPkg());
2580 if (target->ProvidesList != 0)
2581 [failure addObject:@"?"];
2583 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2585 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2586 else if (!cache_[target].CandidateVerIter(cache_).end())
2587 [failure addObject:@"-"];
2588 else if (target->ProvidesList == 0)
2589 [failure addObject:@"!"];
2591 [failure addObject:@"%"];
2595 if (start.TargetVer() != 0)
2596 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2607 - (void) reloadData { _pooled
2608 @synchronized (self) {
2631 if (!cache_.Open(progress_, true)) {
2633 if (!_error->PopMessage(error))
2636 lprintf("cache_.Open():[%s]\n", error.c_str());
2638 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2639 [delegate_ repairWithSelector:@selector(configure)];
2640 else if (error == "The package lists or status file could not be parsed or opened.")
2641 [delegate_ repairWithSelector:@selector(update)];
2642 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2643 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2644 // else if (error == "The list of sources could not be read.")
2645 else _assert(false);
2651 now_ = [[NSDate date] retain];
2653 policy_ = new pkgDepCache::Policy();
2654 records_ = new pkgRecords(cache_);
2655 resolver_ = new pkgProblemResolver(cache_);
2656 fetcher_ = new pkgAcquire(&status_);
2659 list_ = new pkgSourceList();
2660 _assert(list_->ReadMainList());
2662 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2663 _assert(pkgApplyStatus(cache_));
2665 if (cache_->BrokenCount() != 0) {
2666 _assert(pkgFixBroken(cache_));
2667 _assert(cache_->BrokenCount() == 0);
2668 _assert(pkgMinimizeUpgrade(cache_));
2671 [sources_ removeAllObjects];
2672 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2673 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2674 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2676 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2677 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2681 [packages_ removeAllObjects];
2683 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2684 if (Package *package = [Package packageWithIterator:iterator database:self])
2685 [packages_ addObject:package];
2687 [packages_ sortUsingSelector:@selector(compareByName:)];
2690 _config->Set("Acquire::http::Timeout", 15);
2691 _config->Set("Acquire::http::MaxParallel", 4);
2694 - (void) configure {
2695 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2696 system([dpkg UTF8String]);
2704 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2705 _assert(!_error->PendingError());
2708 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2711 public pkgArchiveCleaner
2714 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2719 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2721 while (_error->PopMessage(error))
2722 lprintf("ArchiveCleaner: %s\n", error.c_str());
2727 pkgRecords records(cache_);
2729 lock_ = new FileFd();
2730 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2731 _assert(!_error->PendingError());
2734 // XXX: explain this with an error message
2735 _assert(list.ReadMainList());
2737 manager_ = (_system->CreatePM(cache_));
2738 _assert(manager_->GetArchives(fetcher_, &list, &records));
2739 _assert(!_error->PendingError());
2743 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2745 _assert(list.ReadMainList());
2746 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2747 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2750 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2755 bool failed = false;
2756 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2757 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2760 std::string uri = (*item)->DescURI();
2761 std::string error = (*item)->ErrorText;
2763 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2766 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2767 withObject:[NSArray arrayWithObjects:
2768 [NSString stringWithUTF8String:error.c_str()],
2780 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2782 if (_error->PendingError()) {
2787 if (result == pkgPackageManager::Failed) {
2792 if (result != pkgPackageManager::Completed) {
2797 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2799 _assert(list.ReadMainList());
2800 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2801 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2804 if (![before isEqualToArray:after])
2809 _assert(pkgDistUpgrade(cache_));
2813 [self updateWithStatus:status_];
2816 - (void) updateWithStatus:(Status &)status {
2818 _assert(list.ReadMainList());
2821 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2822 _assert(!_error->PendingError());
2824 pkgAcquire fetcher(&status);
2825 _assert(list.GetIndexes(&fetcher));
2827 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2828 bool failed = false;
2829 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2830 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2831 (*item)->Finished();
2835 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2836 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2837 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2840 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2845 - (void) setDelegate:(id)delegate {
2846 delegate_ = delegate;
2847 status_.setDelegate(delegate);
2848 progress_.setDelegate(delegate);
2851 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2852 pkgIndexFile *index(NULL);
2853 list_->FindIndex(file, index);
2854 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2860 /* PopUp Windows {{{ */
2861 @interface PopUpView : UIView {
2862 _transient id delegate_;
2863 UITransitionView *transition_;
2868 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2872 @implementation PopUpView
2875 [transition_ setDelegate:nil];
2876 [transition_ release];
2882 [transition_ transition:UITransitionPushFromTop toView:nil];
2885 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2886 if (from != nil && to == nil)
2887 [self removeFromSuperview];
2890 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2891 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2892 delegate_ = delegate;
2894 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2895 [self addSubview:transition_];
2897 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2899 [view addSubview:self];
2901 [transition_ setDelegate:self];
2903 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2904 [transition_ transition:UITransitionNone toView:blank];
2905 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2912 /* Mail Composition {{{ */
2913 @interface MailToView : PopUpView {
2914 MailComposeController *controller_;
2917 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2921 @implementation MailToView
2924 [controller_ release];
2928 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2932 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2933 NSLog(@"did:%@", delivery);
2934 // [UIApp setStatusBarShowsProgress:NO];
2935 if ([controller error]){
2936 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2937 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2938 [mailAlertSheet setBodyText:[controller error]];
2939 [mailAlertSheet popupAlertAnimated:YES];
2943 - (void) showError {
2944 NSLog(@"%@", [controller_ error]);
2945 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2946 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2947 [mailAlertSheet setBodyText:[controller_ error]];
2948 [mailAlertSheet popupAlertAnimated:YES];
2951 - (void) deliverMessage { _pooled
2955 if (![controller_ deliverMessage])
2956 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2959 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2960 if ([controller_ needsDelivery])
2961 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2966 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2967 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2968 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2969 [controller_ setDelegate:self];
2970 [controller_ initializeUI];
2971 [controller_ setupForURL:url];
2973 UIView *view([controller_ view]);
2974 [overlay_ addSubview:view];
2980 /* Confirmation View {{{ */
2981 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2982 if (!iterator.end())
2983 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2984 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2986 pkgCache::PkgIterator package(dep.TargetPkg());
2989 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2996 @protocol ConfirmationViewDelegate
3001 @interface ConfirmationView : BrowserView {
3002 _transient Database *database_;
3003 UIActionSheet *essential_;
3010 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3014 @implementation ConfirmationView
3021 if (essential_ != nil)
3022 [essential_ release];
3028 [book_ popFromSuperviewAnimated:YES];
3031 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3032 NSString *context([sheet context]);
3034 if ([context isEqualToString:@"remove"]) {
3042 [delegate_ confirm];
3049 } else if ([context isEqualToString:@"unable"]) {
3053 [super alertSheet:sheet buttonClicked:button];
3056 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3057 [window setValue:changes_ forKey:@"changes"];
3058 [window setValue:issues_ forKey:@"issues"];
3059 [window setValue:sizes_ forKey:@"sizes"];
3060 [super webView:sender didClearWindowObject:window forFrame:frame];
3063 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3064 if ((self = [super initWithBook:book]) != nil) {
3065 database_ = database;
3067 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
3068 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
3069 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
3070 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
3071 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
3075 pkgDepCache::Policy *policy([database_ policy]);
3077 pkgCacheFile &cache([database_ cache]);
3078 NSArray *packages = [database_ packages];
3079 for (Package *package in packages) {
3080 pkgCache::PkgIterator iterator = [package iterator];
3081 pkgDepCache::StateCache &state(cache[iterator]);
3083 NSString *name([package name]);
3085 if (state.NewInstall())
3086 [installing addObject:name];
3087 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
3088 [reinstalling addObject:name];
3089 else if (state.Upgrade())
3090 [upgrading addObject:name];
3091 else if (state.Downgrade())
3092 [downgrading addObject:name];
3093 else if (state.Delete()) {
3094 if ([package essential])
3096 [removing addObject:name];
3099 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3100 substrate_ |= DepSubstrate(iterator.CurrentVer());
3105 else if (Advanced_ || true) {
3106 essential_ = [[UIActionSheet alloc]
3107 initWithTitle:@"Removing Essentials"
3108 buttons:[NSArray arrayWithObjects:
3109 @"Cancel Operation (Safe)",
3110 @"Force Removal (Unsafe)",
3112 defaultButtonIndex:0
3118 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3120 [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."];
3122 essential_ = [[UIActionSheet alloc]
3123 initWithTitle:@"Unable to Comply"
3124 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3125 defaultButtonIndex:0
3130 [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."];
3133 changes_ = [[NSArray alloc] initWithObjects:
3141 issues_ = [database_ issues];
3143 issues_ = [issues_ retain];
3145 sizes_ = [[NSArray alloc] initWithObjects:
3146 SizeString([database_ fetcher].FetchNeeded()),
3147 SizeString([database_ fetcher].PartialPresent()),
3148 SizeString([database_ cache]->UsrSize()),
3151 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3155 - (NSString *) backButtonTitle {
3159 - (NSString *) leftButtonTitle {
3163 - (id) rightButtonTitle {
3164 return issues_ != nil ? nil : [super rightButtonTitle];
3167 - (id) _rightButtonTitle {
3168 #if AlwaysReload || IgnoreInstall
3169 return [super _rightButtonTitle];
3175 - (void) _leftButtonClicked {
3180 - (void) _rightButtonClicked {
3182 return [super _rightButtonClicked];
3184 if (essential_ != nil)
3185 [essential_ popupAlertAnimated:YES];
3189 [delegate_ confirm];
3197 /* Progress Data {{{ */
3198 @interface ProgressData : NSObject {
3204 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3211 @implementation ProgressData
3213 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3214 if ((self = [super init]) != nil) {
3215 selector_ = selector;
3235 /* Progress View {{{ */
3236 @interface ProgressView : UIView <
3237 ConfigurationDelegate,
3240 _transient Database *database_;
3242 UIView *background_;
3243 UITransitionView *transition_;
3245 UINavigationBar *navbar_;
3246 UIProgressBar *progress_;
3247 UITextView *output_;
3248 UITextLabel *status_;
3249 UIPushButton *close_;
3252 SHA1SumValue springlist_;
3253 SHA1SumValue notifyconf_;
3254 SHA1SumValue sandplate_;
3257 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3259 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3260 - (void) setContentView:(UIView *)view;
3263 - (void) _retachThread;
3264 - (void) _detachNewThreadData:(ProgressData *)data;
3265 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3271 @protocol ProgressViewDelegate
3272 - (void) progressViewIsComplete:(ProgressView *)sender;
3275 @implementation ProgressView
3278 [transition_ setDelegate:nil];
3279 [navbar_ setDelegate:nil];
3282 if (background_ != nil)
3283 [background_ release];
3284 [transition_ release];
3287 [progress_ release];
3294 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3295 if (bootstrap_ && from == overlay_ && to == view_)
3299 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3300 if ((self = [super initWithFrame:frame]) != nil) {
3301 database_ = database;
3302 delegate_ = delegate;
3304 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3305 [transition_ setDelegate:self];
3307 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3310 [overlay_ setBackgroundColor:[UIColor blackColor]];
3312 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3313 [background_ setBackgroundColor:[UIColor blackColor]];
3314 [self addSubview:background_];
3317 [self addSubview:transition_];
3319 CGSize navsize = [UINavigationBar defaultSize];
3320 CGRect navrect = {{0, 0}, navsize};
3322 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3323 [overlay_ addSubview:navbar_];
3325 [navbar_ setBarStyle:1];
3326 [navbar_ setDelegate:self];
3328 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3329 [navbar_ pushNavigationItem:navitem];
3331 CGRect bounds = [overlay_ bounds];
3332 CGSize prgsize = [UIProgressBar defaultSize];
3335 (bounds.size.width - prgsize.width) / 2,
3336 bounds.size.height - prgsize.height - 20
3339 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3340 [progress_ setStyle:0];
3342 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3344 bounds.size.height - prgsize.height - 50,
3345 bounds.size.width - 20,
3349 [status_ setColor:[UIColor whiteColor]];
3350 [status_ setBackgroundColor:[UIColor clearColor]];
3352 [status_ setCentersHorizontally:YES];
3353 //[status_ setFont:font];
3356 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3358 navrect.size.height + 20,
3359 bounds.size.width - 20,
3360 bounds.size.height - navsize.height - 62 - navrect.size.height
3364 //[output_ setTextFont:@"Courier New"];
3365 [output_ setTextSize:12];
3367 [output_ setTextColor:[UIColor whiteColor]];
3368 [output_ setBackgroundColor:[UIColor clearColor]];
3370 [output_ setMarginTop:0];
3371 [output_ setAllowsRubberBanding:YES];
3372 [output_ setEditable:NO];
3374 [overlay_ addSubview:output_];
3376 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3378 bounds.size.height - prgsize.height - 50,
3379 bounds.size.width - 20,
3383 [close_ setAutosizesToFit:NO];
3384 [close_ setDrawsShadow:YES];
3385 [close_ setStretchBackground:YES];
3386 [close_ setEnabled:YES];
3388 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3389 [close_ setTitleFont:bold];
3391 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3392 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3393 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3397 - (void) setContentView:(UIView *)view {
3398 view_ = [view retain];
3401 - (void) resetView {
3402 [transition_ transition:6 toView:view_];
3405 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3406 NSString *context([sheet context]);
3408 if ([context isEqualToString:@"error"])
3410 else if ([context isEqualToString:@"conffile"]) {
3411 FILE *input = [database_ input];
3415 fprintf(input, "N\n");
3419 fprintf(input, "Y\n");
3430 - (void) closeButtonPushed {
3439 [delegate_ suspendWithAnimation:YES];
3443 system("launchctl stop com.apple.SpringBoard");
3447 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3456 - (void) _retachThread {
3457 UINavigationItem *item = [navbar_ topItem];
3458 [item setTitle:@"Complete"];
3460 [overlay_ addSubview:close_];
3461 [progress_ removeFromSuperview];
3462 [status_ removeFromSuperview];
3464 [delegate_ progressViewIsComplete:self];
3467 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3468 MMap mmap(file, MMap::ReadOnly);
3470 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3471 if (!(sandplate_ == sha1.Result()))
3476 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3477 MMap mmap(file, MMap::ReadOnly);
3479 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3480 if (!(notifyconf_ == sha1.Result()))
3485 FileFd file(SpringBoard_, FileFd::ReadOnly);
3486 MMap mmap(file, MMap::ReadOnly);
3488 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3489 if (!(springlist_ == sha1.Result()))
3494 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3495 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3496 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3497 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3498 case 4: [close_ setTitle:@"Reboot Device"]; break;
3501 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3503 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3504 [cache autorelease];
3506 NSFileManager *manager = [NSFileManager defaultManager];
3507 NSError *error = nil;
3509 id system = [cache objectForKey:@"System"];
3514 if (stat(Cache_, &info) == -1)
3517 [system removeAllObjects];
3519 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3520 for (NSString *app in apps)
3521 if ([app hasSuffix:@".app"]) {
3522 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3523 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3524 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3526 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3527 [info setObject:path forKey:@"Path"];
3528 [info setObject:@"System" forKey:@"ApplicationType"];
3529 [system addInfoDictionary:info];
3535 [cache writeToFile:@Cache_ atomically:YES];
3537 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3539 if (chmod(Cache_, info.st_mode) == -1)
3543 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3546 notify_post("com.apple.mobile.application_installed");
3548 [delegate_ setStatusBarShowsProgress:NO];
3551 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3552 [[data target] performSelector:[data selector] withObject:[data object]];
3555 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3558 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3559 UINavigationItem *item = [navbar_ topItem];
3560 [item setTitle:title];
3562 [status_ setText:nil];
3563 [output_ setText:@""];
3564 [progress_ setProgress:0];
3566 [close_ removeFromSuperview];
3567 [overlay_ addSubview:progress_];
3568 [overlay_ addSubview:status_];
3570 [delegate_ setStatusBarShowsProgress:YES];
3574 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3575 MMap mmap(file, MMap::ReadOnly);
3577 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3578 sandplate_ = sha1.Result();
3582 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3583 MMap mmap(file, MMap::ReadOnly);
3585 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3586 notifyconf_ = sha1.Result();
3590 FileFd file(SpringBoard_, FileFd::ReadOnly);
3591 MMap mmap(file, MMap::ReadOnly);
3593 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3594 springlist_ = sha1.Result();
3597 [transition_ transition:6 toView:overlay_];
3600 detachNewThreadSelector:@selector(_detachNewThreadData:)
3602 withObject:[[ProgressData alloc]
3603 initWithSelector:selector
3610 - (void) repairWithSelector:(SEL)selector {
3612 detachNewThreadSelector:selector
3619 - (void) setConfigurationData:(NSString *)data {
3621 performSelectorOnMainThread:@selector(_setConfigurationData:)
3627 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3628 Package *package = id == nil ? nil : [database_ packageWithName:id];
3630 UIActionSheet *sheet = [[[UIActionSheet alloc]
3631 initWithTitle:(package == nil ? id : [package name])
3632 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3633 defaultButtonIndex:0
3638 [sheet setBodyText:error];
3639 [sheet popupAlertAnimated:YES];
3642 - (void) setProgressTitle:(NSString *)title {
3644 performSelectorOnMainThread:@selector(_setProgressTitle:)
3650 - (void) setProgressPercent:(float)percent {
3652 performSelectorOnMainThread:@selector(_setProgressPercent:)
3653 withObject:[NSNumber numberWithFloat:percent]
3658 - (void) startProgress {
3661 - (void) addProgressOutput:(NSString *)output {
3663 performSelectorOnMainThread:@selector(_addProgressOutput:)
3669 - (bool) isCancelling:(size_t)received {
3673 - (void) _setConfigurationData:(NSString *)data {
3674 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3676 _assert(conffile_r(data));
3678 NSString *ofile = conffile_r[1];
3679 //NSString *nfile = conffile_r[2];
3681 UIActionSheet *sheet = [[[UIActionSheet alloc]
3682 initWithTitle:@"Configuration Upgrade"
3683 buttons:[NSArray arrayWithObjects:
3684 @"Keep My Old Copy",
3685 @"Accept The New Copy",
3686 // XXX: @"See What Changed",
3688 defaultButtonIndex:0
3693 [sheet setBodyText:[NSString stringWithFormat:
3694 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3697 [sheet popupAlertAnimated:YES];
3700 - (void) _setProgressTitle:(NSString *)title {
3701 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3702 for (size_t i(0), e([words count]); i != e; ++i) {
3703 NSString *word([words objectAtIndex:i]);
3704 if (Package *package = [database_ packageWithName:word])
3705 [words replaceObjectAtIndex:i withObject:[package name]];
3708 [status_ setText:[words componentsJoinedByString:@" "]];
3711 - (void) _setProgressPercent:(NSNumber *)percent {
3712 [progress_ setProgress:[percent floatValue]];
3715 - (void) _addProgressOutput:(NSString *)output {
3716 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3717 CGSize size = [output_ contentSize];
3718 CGRect rect = {{0, size.height}, {size.width, 0}};
3719 [output_ scrollRectToVisible:rect animated:YES];
3722 - (BOOL) isRunning {
3729 /* Package Cell {{{ */
3730 @interface PackageCell : UITableCell {
3733 NSString *description_;
3739 UITextLabel *status_;
3743 - (PackageCell *) init;
3744 - (void) setPackage:(Package *)package;
3746 + (int) heightForPackage:(Package *)package;
3750 @implementation PackageCell
3752 - (void) clearPackage {
3763 if (description_ != nil) {
3764 [description_ release];
3768 if (source_ != nil) {
3773 if (badge_ != nil) {
3780 [self clearPackage];
3787 - (PackageCell *) init {
3788 if ((self = [super init]) != nil) {
3790 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3791 [status_ setBackgroundColor:[UIColor clearColor]];
3792 [status_ setFont:small];
3797 - (void) setPackage:(Package *)package {
3798 [self clearPackage];
3800 Source *source = [package source];
3801 NSString *section = [package simpleSection];
3803 icon_ = [[package icon] retain];
3805 name_ = [[package name] retain];
3806 description_ = [[package tagline] retain];
3807 commercial_ = [package isCommercial];
3809 NSString *label = nil;
3810 bool trusted = false;
3812 if (source != nil) {
3813 label = [source label];
3814 trusted = [source trusted];
3815 } else if ([[package id] isEqualToString:@"firmware"])
3818 label = @"Unknown/Local";
3820 NSString *from = [NSString stringWithFormat:@"from %@", label];
3822 if (section != nil && ![section isEqualToString:label])
3823 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3825 source_ = [from retain];
3827 if (NSString *purpose = [package primaryPurpose])
3828 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3829 badge_ = [badge_ retain];
3832 if (NSString *mode = [package mode]) {
3833 [badge_ setImage:[UIImage applicationImageNamed:
3834 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3837 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3838 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3839 } else if ([package half]) {
3840 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3841 [status_ setText:@"Package Damaged"];
3842 [status_ setColor:[UIColor redColor]];
3844 [badge_ setImage:nil];
3845 [status_ setText:nil];
3852 - (void) drawRect:(CGRect)rect {
3854 //[self setBackgroundColor:(commercial_ ? CommercialColor_ : [UIColor whiteColor])];
3858 [super drawRect:rect];
3861 - (void) drawBackgroundInRect:(CGRect)rect withFade:(float)fade {
3862 if (fade == 0 && commercial_) {
3863 CGContextRef context(UIGraphicsGetCurrentContext());
3864 [[self backgroundColor] set];
3866 back.size.height -= 1;
3867 CGContextFillRect(context, back);
3870 [super drawBackgroundInRect:rect withFade:fade];
3873 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3876 rect.size = [icon_ size];
3878 rect.size.width /= 2;
3879 rect.size.height /= 2;
3881 rect.origin.x = 25 - rect.size.width / 2;
3882 rect.origin.y = 25 - rect.size.height / 2;
3884 [icon_ drawInRect:rect];
3887 if (badge_ != nil) {
3888 CGSize size = [badge_ size];
3890 [badge_ drawAtPoint:CGPointMake(
3891 36 - size.width / 2,
3892 36 - size.height / 2
3900 UISetColor(commercial_ ? Purple_ : Black_);
3901 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3902 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3905 UISetColor(commercial_ ? Purplish_ : Gray_);
3906 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3908 [super drawContentInRect:rect selected:selected];
3911 + (int) heightForPackage:(Package *)package {
3912 NSString *tagline([package tagline]);
3913 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3915 if ([package hasMode] || [package half])
3924 /* Section Cell {{{ */
3925 @interface SectionCell : UISimpleTableCell {
3930 _UISwitchSlider *switch_;
3935 - (void) setSection:(Section *)section editing:(BOOL)editing;
3939 @implementation SectionCell
3941 - (void) clearSection {
3942 if (section_ != nil) {
3952 if (count_ != nil) {
3959 [self clearSection];
3966 if ((self = [super init]) != nil) {
3967 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3969 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3970 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3974 - (void) onSwitch:(id)sender {
3975 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3976 if (metadata == nil) {
3977 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3978 [Sections_ setObject:metadata forKey:section_];
3982 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3985 - (void) setSection:(Section *)section editing:(BOOL)editing {
3986 if (editing != editing_) {
3988 [switch_ removeFromSuperview];
3990 [self addSubview:switch_];
3994 [self clearSection];
3996 if (section == nil) {
3997 name_ = [@"All Packages" retain];
4000 section_ = [section name];
4001 if (section_ != nil)
4002 section_ = [section_ retain];
4003 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
4004 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
4007 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
4011 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4012 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
4019 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
4021 CGSize size = [count_ sizeWithFont:Font14_];
4025 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
4027 [super drawContentInRect:rect selected:selected];
4033 /* File Table {{{ */
4034 @interface FileTable : RVPage {
4035 _transient Database *database_;
4038 NSMutableArray *files_;
4042 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4043 - (void) setPackage:(Package *)package;
4047 @implementation FileTable
4050 if (package_ != nil)
4059 - (int) numberOfRowsInTable:(UITable *)table {
4060 return files_ == nil ? 0 : [files_ count];
4063 - (float) table:(UITable *)table heightForRow:(int)row {
4067 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4068 if (reusing == nil) {
4069 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
4070 UIFont *font = [UIFont systemFontOfSize:16];
4071 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
4073 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
4077 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4081 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4082 if ((self = [super initWithBook:book]) != nil) {
4083 database_ = database;
4085 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
4087 list_ = [[UITable alloc] initWithFrame:[self bounds]];
4088 [self addSubview:list_];
4090 UITableColumn *column = [[[UITableColumn alloc]
4091 initWithTitle:@"Name"
4093 width:[self frame].size.width
4096 [list_ setDataSource:self];
4097 [list_ setSeparatorStyle:1];
4098 [list_ addTableColumn:column];
4099 [list_ setDelegate:self];
4100 [list_ setReusesTableCells:YES];
4104 - (void) setPackage:(Package *)package {
4105 if (package_ != nil) {
4106 [package_ autorelease];
4115 [files_ removeAllObjects];
4117 if (package != nil) {
4118 package_ = [package retain];
4119 name_ = [[package id] retain];
4121 if (NSArray *files = [package files])
4122 [files_ addObjectsFromArray:files];
4124 if ([files_ count] != 0) {
4125 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4126 [files_ removeObjectAtIndex:0];
4127 [files_ sortUsingSelector:@selector(compareByPath:)];
4129 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4130 [stack addObject:@"/"];
4132 for (int i(0), e([files_ count]); i != e; ++i) {
4133 NSString *file = [files_ objectAtIndex:i];
4134 while (![file hasPrefix:[stack lastObject]])
4135 [stack removeLastObject];
4136 NSString *directory = [stack lastObject];
4137 [stack addObject:[file stringByAppendingString:@"/"]];
4138 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4139 ([stack count] - 2) * 3, "",
4140 [file substringFromIndex:[directory length]]
4149 - (void) resetViewAnimated:(BOOL)animated {
4150 [list_ resetViewAnimated:animated];
4153 - (void) reloadData {
4154 [self setPackage:[database_ packageWithName:name_]];
4155 [self reloadButtons];
4158 - (NSString *) title {
4159 return @"Installed Files";
4162 - (NSString *) backButtonTitle {
4168 /* Package View {{{ */
4169 @interface PackageView : BrowserView {
4170 _transient Database *database_;
4174 NSMutableArray *buttons_;
4177 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4178 - (void) setPackage:(Package *)package;
4182 @implementation PackageView
4185 if (package_ != nil)
4193 - (void) _clickButtonWithName:(NSString *)name {
4194 if ([name isEqualToString:@"Install"])
4195 [delegate_ installPackage:package_];
4196 else if ([name isEqualToString:@"Reinstall"])
4197 [delegate_ installPackage:package_];
4198 else if ([name isEqualToString:@"Remove"])
4199 [delegate_ removePackage:package_];
4200 else if ([name isEqualToString:@"Upgrade"])
4201 [delegate_ installPackage:package_];
4202 else _assert(false);
4205 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4206 NSString *context([sheet context]);
4208 if ([context isEqualToString:@"modify"]) {
4209 int count = [buttons_ count];
4210 _assert(count != 0);
4211 _assert(button <= count + 1);
4213 if (count != button - 1)
4214 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4218 [super alertSheet:sheet buttonClicked:button];
4221 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4222 return [super webView:sender didFinishLoadForFrame:frame];
4225 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4226 [window setValue:package_ forKey:@"package"];
4227 [super webView:sender didClearWindowObject:window forFrame:frame];
4230 - (bool) _allowJavaScriptPanel {
4235 - (void) __rightButtonClicked {
4236 int count = [buttons_ count];
4237 _assert(count != 0);
4240 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4242 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4243 [buttons addObjectsFromArray:buttons_];
4244 [buttons addObject:@"Cancel"];
4246 [delegate_ slideUp:[[[UIActionSheet alloc]
4249 defaultButtonIndex:2
4256 - (void) _rightButtonClicked {
4258 [super _rightButtonClicked];
4260 [self __rightButtonClicked];
4264 - (id) _rightButtonTitle {
4265 int count = [buttons_ count];
4266 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4269 - (NSString *) backButtonTitle {
4273 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4274 if ((self = [super initWithBook:book]) != nil) {
4275 database_ = database;
4276 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4280 - (void) setPackage:(Package *)package {
4281 if (package_ != nil) {
4282 [package_ autorelease];
4291 [buttons_ removeAllObjects];
4293 if (package != nil) {
4294 package_ = [package retain];
4295 name_ = [[package id] retain];
4296 commercial_ = [package isCommercial];
4298 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4300 if ([package_ source] == nil);
4301 else if ([package_ upgradableAndEssential:NO])
4302 [buttons_ addObject:@"Upgrade"];
4303 else if ([package_ installed] == nil)
4304 [buttons_ addObject:@"Install"];
4306 [buttons_ addObject:@"Reinstall"];
4307 if ([package_ installed] != nil)
4308 [buttons_ addObject:@"Remove"];
4312 - (bool) isLoading {
4313 return commercial_ ? [super isLoading] : false;
4316 - (void) reloadData {
4317 [self setPackage:[database_ packageWithName:name_]];
4318 [self reloadButtons];
4323 /* Package Table {{{ */
4324 @interface PackageTable : RVPage {
4325 _transient Database *database_;
4327 NSMutableArray *packages_;
4328 NSMutableArray *sections_;
4329 UISectionList *list_;
4332 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4334 - (void) setDelegate:(id)delegate;
4336 - (void) reloadData;
4337 - (void) resetCursor;
4339 - (UISectionList *) list;
4341 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4345 @implementation PackageTable
4348 [list_ setDataSource:nil];
4351 [packages_ release];
4352 [sections_ release];
4357 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4358 return [sections_ count];
4361 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4362 return [[sections_ objectAtIndex:section] name];
4365 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4366 return [[sections_ objectAtIndex:section] row];
4369 - (int) numberOfRowsInTable:(UITable *)table {
4370 return [packages_ count];
4373 - (float) table:(UITable *)table heightForRow:(int)row {
4374 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4377 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4379 reusing = [[[PackageCell alloc] init] autorelease];
4380 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4384 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4388 - (void) tableRowSelected:(NSNotification *)notification {
4389 int row = [[notification object] selectedRow];
4393 Package *package = [packages_ objectAtIndex:row];
4394 package = [database_ packageWithName:[package id]];
4395 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4396 [view setPackage:package];
4397 [view setDelegate:delegate_];
4398 [book_ pushPage:view];
4401 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4402 if ((self = [super initWithBook:book]) != nil) {
4403 database_ = database;
4404 title_ = [title retain];
4406 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4407 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4409 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4410 [list_ setDataSource:self];
4412 UITableColumn *column = [[[UITableColumn alloc]
4413 initWithTitle:@"Name"
4415 width:[self frame].size.width
4418 UITable *table = [list_ table];
4419 [table setSeparatorStyle:1];
4420 [table addTableColumn:column];
4421 [table setDelegate:self];
4422 [table setReusesTableCells:YES];
4424 [self addSubview:list_];
4426 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4427 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4431 - (void) setDelegate:(id)delegate {
4432 delegate_ = delegate;
4435 - (bool) hasPackage:(Package *)package {
4439 - (void) reloadData {
4440 NSArray *packages = [database_ packages];
4442 [packages_ removeAllObjects];
4443 [sections_ removeAllObjects];
4445 _profile(PackageTable$reloadData$Filter)
4446 for (Package *package in packages)
4447 if ([self hasPackage:package])
4448 [packages_ addObject:package];
4451 Section *section = nil;
4453 _profile(PackageTable$reloadData$Section)
4454 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4458 _profile(PackageTable$reloadData$Section$Package)
4459 package = [packages_ objectAtIndex:offset];
4460 index = [package index];
4463 if (section == nil || [section index] != index) {
4464 _profile(PackageTable$reloadData$Section$Allocate)
4465 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4468 _profile(PackageTable$reloadData$Section$Add)
4469 [sections_ addObject:section];
4473 [section addToCount];
4477 _profile(PackageTable$reloadData$List)
4482 - (NSString *) title {
4486 - (void) resetViewAnimated:(BOOL)animated {
4487 [list_ resetViewAnimated:animated];
4490 - (void) resetCursor {
4491 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4494 - (UISectionList *) list {
4498 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4499 [list_ setShouldHideHeaderInShortLists:hide];
4504 /* Filtered Package Table {{{ */
4505 @interface FilteredPackageTable : PackageTable {
4511 - (void) setObject:(id)object;
4513 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4517 @implementation FilteredPackageTable
4525 - (void) setObject:(id)object {
4531 object_ = [object retain];
4534 - (bool) hasPackage:(Package *)package {
4535 _profile(FilteredPackageTable$hasPackage)
4536 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
4540 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4541 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4543 object_ = object == nil ? nil : [object retain];
4545 /* XXX: this is an unsafe optimization of doomy hell */
4546 Method method = class_getInstanceMethod([Package class], filter);
4547 imp_ = method_getImplementation(method);
4548 _assert(imp_ != NULL);
4557 /* Add Source View {{{ */
4558 @interface AddSourceView : RVPage {
4559 _transient Database *database_;
4562 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4566 @implementation AddSourceView
4568 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4569 if ((self = [super initWithBook:book]) != nil) {
4570 database_ = database;
4576 /* Source Cell {{{ */
4577 @interface SourceCell : UITableCell {
4580 NSString *description_;
4586 - (SourceCell *) initWithSource:(Source *)source;
4590 @implementation SourceCell
4595 [description_ release];
4600 - (SourceCell *) initWithSource:(Source *)source {
4601 if ((self = [super init]) != nil) {
4603 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4605 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4606 icon_ = [icon_ retain];
4608 origin_ = [[source name] retain];
4609 label_ = [[source uri] retain];
4610 description_ = [[source description] retain];
4614 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4616 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4623 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4627 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4631 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4633 [super drawContentInRect:rect selected:selected];
4638 /* Source Table {{{ */
4639 @interface SourceTable : RVPage {
4640 _transient Database *database_;
4641 UISectionList *list_;
4642 NSMutableArray *sources_;
4643 UIActionSheet *alert_;
4647 UIProgressHUD *hud_;
4650 //NSURLConnection *installer_;
4651 NSURLConnection *trivial_bz2_;
4652 NSURLConnection *trivial_gz_;
4653 //NSURLConnection *automatic_;
4658 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4662 @implementation SourceTable
4664 - (void) _deallocConnection:(NSURLConnection *)connection {
4665 if (connection != nil) {
4666 [connection cancel];
4667 //[connection setDelegate:nil];
4668 [connection release];
4673 [[list_ table] setDelegate:nil];
4674 [list_ setDataSource:nil];
4683 //[self _deallocConnection:installer_];
4684 [self _deallocConnection:trivial_gz_];
4685 [self _deallocConnection:trivial_bz2_];
4686 //[self _deallocConnection:automatic_];
4693 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4694 return offset_ == 0 ? 1 : 2;
4697 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4698 switch (section + (offset_ == 0 ? 1 : 0)) {
4699 case 0: return @"Entered by User";
4700 case 1: return @"Installed by Packages";
4708 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4709 switch (section + (offset_ == 0 ? 1 : 0)) {
4711 case 1: return offset_;
4719 - (int) numberOfRowsInTable:(UITable *)table {
4720 return [sources_ count];
4723 - (float) table:(UITable *)table heightForRow:(int)row {
4724 Source *source = [sources_ objectAtIndex:row];
4725 return [source description] == nil ? 56 : 73;
4728 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4729 Source *source = [sources_ objectAtIndex:row];
4730 // XXX: weird warning, stupid selectors ;P
4731 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4734 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4738 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4742 - (void) tableRowSelected:(NSNotification*)notification {
4743 UITable *table([list_ table]);
4744 int row([table selectedRow]);
4748 Source *source = [sources_ objectAtIndex:row];
4750 PackageTable *packages = [[[FilteredPackageTable alloc]
4753 title:[source label]
4754 filter:@selector(isVisibleInSource:)
4758 [packages setDelegate:delegate_];
4760 [book_ pushPage:packages];
4763 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4764 Source *source = [sources_ objectAtIndex:row];
4765 return [source record] != nil;
4768 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4769 [[list_ table] setDeleteConfirmationRow:row];
4772 - (void) table:(UITable *)table deleteRow:(int)row {
4773 Source *source = [sources_ objectAtIndex:row];
4774 [Sources_ removeObjectForKey:[source key]];
4775 [delegate_ syncData];
4779 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4782 @"./", @"Distribution",
4783 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4785 [delegate_ syncData];
4788 - (NSString *) getWarning {
4789 NSString *href(href_);
4790 NSRange colon([href rangeOfString:@"://"]);
4791 if (colon.location != NSNotFound)
4792 href = [href substringFromIndex:(colon.location + 3)];
4793 href = [href stringByAddingPercentEscapes];
4794 href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href];
4795 href = [href stringByCachingURLWithCurrentCDN];
4797 NSURL *url([NSURL URLWithString:href]);
4799 NSStringEncoding encoding;
4800 NSError *error(nil);
4802 if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error])
4803 return [warning length] == 0 ? nil : warning;
4807 - (void) _endConnection:(NSURLConnection *)connection {
4808 NSURLConnection **field = NULL;
4809 if (connection == trivial_bz2_)
4810 field = &trivial_bz2_;
4811 else if (connection == trivial_gz_)
4812 field = &trivial_gz_;
4813 _assert(field != NULL);
4814 [connection release];
4818 trivial_bz2_ == nil &&
4824 if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
4827 UIActionSheet *sheet = [[[UIActionSheet alloc]
4828 initWithTitle:@"Source Warning"
4829 buttons:[NSArray arrayWithObjects:@"Add Anyway", @"Cancel", nil]
4830 defaultButtonIndex:0
4835 [sheet setNumberOfRows:1];
4837 [sheet setBodyText:warning];
4838 [sheet popupAlertAnimated:YES];
4841 } else if (error_ != nil) {
4842 UIActionSheet *sheet = [[[UIActionSheet alloc]
4843 initWithTitle:@"Verification Error"
4844 buttons:[NSArray arrayWithObjects:@"OK", nil]
4845 defaultButtonIndex:0
4850 [sheet setBodyText:[error_ localizedDescription]];
4851 [sheet popupAlertAnimated:YES];
4853 UIActionSheet *sheet = [[[UIActionSheet alloc]
4854 initWithTitle:@"Did not Find Repository"
4855 buttons:[NSArray arrayWithObjects:@"OK", nil]
4856 defaultButtonIndex:0
4861 [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."];
4862 [sheet popupAlertAnimated:YES];
4865 [delegate_ setStatusBarShowsProgress:NO];
4866 [delegate_ removeProgressHUD:hud_];
4876 if (error_ != nil) {
4883 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4884 switch ([response statusCode]) {
4890 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4891 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4893 error_ = [error retain];
4894 [self _endConnection:connection];
4897 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4898 [self _endConnection:connection];
4901 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4902 NSMutableURLRequest *request = [NSMutableURLRequest
4903 requestWithURL:[NSURL URLWithString:href]
4904 cachePolicy:NSURLRequestUseProtocolCachePolicy
4905 timeoutInterval:20.0
4908 [request setHTTPMethod:method];
4910 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4913 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4914 NSString *context([sheet context]);
4916 if ([context isEqualToString:@"source"]) {
4919 NSString *href = [[sheet textField] text];
4921 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4923 if (![href hasSuffix:@"/"])
4924 href_ = [href stringByAppendingString:@"/"];
4927 href_ = [href_ retain];
4929 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4930 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4931 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4935 hud_ = [[delegate_ addProgressHUD] retain];
4936 [hud_ setText:@"Verifying URL"];
4947 } else if ([context isEqualToString:@"trivial"])
4949 else if ([context isEqualToString:@"urlerror"])
4951 else if ([context isEqualToString:@"warning"]) {
4971 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4972 if ((self = [super initWithBook:book]) != nil) {
4973 database_ = database;
4974 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4976 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4977 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4978 [list_ setShouldHideHeaderInShortLists:NO];
4980 [self addSubview:list_];
4981 [list_ setDataSource:self];
4983 UITableColumn *column = [[UITableColumn alloc]
4984 initWithTitle:@"Name"
4986 width:[self frame].size.width
4989 UITable *table = [list_ table];
4990 [table setSeparatorStyle:1];
4991 [table addTableColumn:column];
4992 [table setDelegate:self];
4996 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4997 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5001 - (void) reloadData {
5003 _assert(list.ReadMainList());
5005 [sources_ removeAllObjects];
5006 [sources_ addObjectsFromArray:[database_ sources]];
5008 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
5011 int count = [sources_ count];
5012 for (offset_ = 0; offset_ != count; ++offset_) {
5013 Source *source = [sources_ objectAtIndex:offset_];
5014 if ([source record] == nil)
5021 - (void) resetViewAnimated:(BOOL)animated {
5022 [list_ resetViewAnimated:animated];
5025 - (void) _leftButtonClicked {
5026 /*[book_ pushPage:[[[AddSourceView alloc]
5031 UIActionSheet *sheet = [[[UIActionSheet alloc]
5032 initWithTitle:@"Enter Cydia/APT URL"
5033 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
5034 defaultButtonIndex:0
5039 [sheet setNumberOfRows:1];
5041 [sheet addTextFieldWithValue:@"http://" label:@""];
5043 UITextInputTraits *traits = [[sheet textField] textInputTraits];
5044 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5045 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5046 [traits setKeyboardType:UIKeyboardTypeURL];
5047 // XXX: UIReturnKeyDone
5048 [traits setReturnKeyType:UIReturnKeyNext];
5050 [sheet popupAlertAnimated:YES];
5053 - (void) _rightButtonClicked {
5054 UITable *table = [list_ table];
5055 BOOL editing = [table isRowDeletionEnabled];
5056 [table enableRowDeletion:!editing animated:YES];
5057 [book_ reloadButtonsForPage:self];
5060 - (NSString *) title {
5064 - (NSString *) leftButtonTitle {
5065 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
5068 - (id) rightButtonTitle {
5069 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
5072 - (UINavigationButtonStyle) rightButtonStyle {
5073 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5079 /* Installed View {{{ */
5080 @interface InstalledView : RVPage {
5081 _transient Database *database_;
5082 FilteredPackageTable *packages_;
5086 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5090 @implementation InstalledView
5093 [packages_ release];
5097 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5098 if ((self = [super initWithBook:book]) != nil) {
5099 database_ = database;
5101 packages_ = [[FilteredPackageTable alloc]
5105 filter:@selector(isInstalledAndVisible:)
5106 with:[NSNumber numberWithBool:YES]
5109 [self addSubview:packages_];
5111 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5112 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5116 - (void) resetViewAnimated:(BOOL)animated {
5117 [packages_ resetViewAnimated:animated];
5120 - (void) reloadData {
5121 [packages_ reloadData];
5124 - (void) _rightButtonClicked {
5125 [packages_ setObject:[NSNumber numberWithBool:expert_]];
5126 [packages_ reloadData];
5128 [book_ reloadButtonsForPage:self];
5131 - (NSString *) title {
5132 return @"Installed";
5135 - (NSString *) backButtonTitle {
5139 - (id) rightButtonTitle {
5140 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
5143 - (UINavigationButtonStyle) rightButtonStyle {
5144 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5147 - (void) setDelegate:(id)delegate {
5148 [super setDelegate:delegate];
5149 [packages_ setDelegate:delegate];
5156 @interface HomeView : BrowserView {
5161 @implementation HomeView
5163 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5164 NSString *context([sheet context]);
5166 if ([context isEqualToString:@"about"])
5169 [super alertSheet:sheet buttonClicked:button];
5172 - (void) _leftButtonClicked {
5173 UIActionSheet *sheet = [[[UIActionSheet alloc]
5174 initWithTitle:@"About Cydia Installer"
5175 buttons:[NSArray arrayWithObjects:@"Close", nil]
5176 defaultButtonIndex:0
5182 @"Copyright (C) 2008-2009\n"
5183 "Jay Freeman (saurik)\n"
5184 "saurik@saurik.com\n"
5185 "http://www.saurik.com/\n"
5188 "http://www.theokorigroup.com/\n"
5190 "College of Creative Studies,\n"
5191 "University of California,\n"
5193 "http://www.ccs.ucsb.edu/"
5196 [sheet popupAlertAnimated:YES];
5199 - (NSString *) leftButtonTitle {
5205 /* Manage View {{{ */
5206 @interface ManageView : BrowserView {
5211 @implementation ManageView
5213 - (NSString *) title {
5217 - (void) _leftButtonClicked {
5218 [delegate_ askForSettings];
5221 - (NSString *) leftButtonTitle {
5226 - (id) _rightButtonTitle {
5231 - (bool) isLoading {
5238 #include <BrowserView.m>
5240 /* Cydia Book {{{ */
5241 @interface CYBook : RVBook <
5244 _transient Database *database_;
5245 UINavigationBar *overlay_;
5246 UINavigationBar *underlay_;
5247 UIProgressIndicator *indicator_;
5248 UITextLabel *prompt_;
5249 UIProgressBar *progress_;
5250 UINavigationButton *cancel_;
5254 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5260 @implementation CYBook
5264 [indicator_ release];
5266 [progress_ release];
5271 - (NSString *) getTitleForPage:(RVPage *)page {
5272 return Simplify([super getTitleForPage:page]);
5280 [UIView beginAnimations:nil context:NULL];
5282 CGRect ovrframe = [overlay_ frame];
5283 ovrframe.origin.y = 0;
5284 [overlay_ setFrame:ovrframe];
5286 CGRect barframe = [navbar_ frame];
5287 barframe.origin.y += ovrframe.size.height;
5288 [navbar_ setFrame:barframe];
5290 CGRect trnframe = [transition_ frame];
5291 trnframe.origin.y += ovrframe.size.height;
5292 trnframe.size.height -= ovrframe.size.height;
5293 [transition_ setFrame:trnframe];
5295 [UIView endAnimations];
5297 [indicator_ startAnimation];
5298 [prompt_ setText:@"Updating Database"];
5299 [progress_ setProgress:0];
5302 [overlay_ addSubview:cancel_];
5305 detachNewThreadSelector:@selector(_update)
5314 [indicator_ stopAnimation];
5316 [UIView beginAnimations:nil context:NULL];
5318 CGRect ovrframe = [overlay_ frame];
5319 ovrframe.origin.y = -ovrframe.size.height;
5320 [overlay_ setFrame:ovrframe];
5322 CGRect barframe = [navbar_ frame];
5323 barframe.origin.y -= ovrframe.size.height;
5324 [navbar_ setFrame:barframe];
5326 CGRect trnframe = [transition_ frame];
5327 trnframe.origin.y -= ovrframe.size.height;
5328 trnframe.size.height += ovrframe.size.height;
5329 [transition_ setFrame:trnframe];
5331 [UIView commitAnimations];
5333 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5336 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5337 if ((self = [super initWithFrame:frame]) != nil) {
5338 database_ = database;
5340 CGRect ovrrect = [navbar_ bounds];
5341 ovrrect.size.height = [UINavigationBar defaultSize].height;
5342 ovrrect.origin.y = -ovrrect.size.height;
5344 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5345 [self addSubview:overlay_];
5347 ovrrect.origin.y = frame.size.height;
5348 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5349 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5350 [self addSubview:underlay_];
5352 [overlay_ setBarStyle:1];
5353 [underlay_ setBarStyle:1];
5355 int barstyle = [overlay_ _barStyle:NO];
5356 bool ugly = barstyle == 0;
5358 UIProgressIndicatorStyle style = ugly ?
5359 UIProgressIndicatorStyleMediumBrown :
5360 UIProgressIndicatorStyleMediumWhite;
5362 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5363 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5364 CGRect indrect = {{indoffset, indoffset}, indsize};
5366 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5367 [indicator_ setStyle:style];
5368 [overlay_ addSubview:indicator_];
5370 CGSize prmsize = {215, indsize.height + 4};
5373 indoffset * 2 + indsize.width,
5377 unsigned(ovrrect.size.height - prmsize.height) / 2
5380 UIFont *font = [UIFont systemFontOfSize:15];
5382 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5384 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5385 [prompt_ setBackgroundColor:[UIColor clearColor]];
5386 [prompt_ setFont:font];
5388 [overlay_ addSubview:prompt_];
5390 CGSize prgsize = {75, 100};
5393 ovrrect.size.width - prgsize.width - 10,
5394 (ovrrect.size.height - prgsize.height) / 2
5397 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5398 [progress_ setStyle:0];
5399 [overlay_ addSubview:progress_];
5401 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5402 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5404 CGRect frame = [cancel_ frame];
5405 frame.size.width = 65;
5406 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5407 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5408 [cancel_ setFrame:frame];
5410 [cancel_ setBarStyle:barstyle];
5414 - (void) _onCancel {
5416 [cancel_ removeFromSuperview];
5419 - (void) _update { _pooled
5421 status.setDelegate(self);
5423 [database_ updateWithStatus:status];
5426 performSelectorOnMainThread:@selector(_update_)
5432 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5433 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5436 - (void) setProgressTitle:(NSString *)title {
5438 performSelectorOnMainThread:@selector(_setProgressTitle:)
5444 - (void) setProgressPercent:(float)percent {
5446 performSelectorOnMainThread:@selector(_setProgressPercent:)
5447 withObject:[NSNumber numberWithFloat:percent]
5452 - (void) startProgress {
5455 - (void) addProgressOutput:(NSString *)output {
5457 performSelectorOnMainThread:@selector(_addProgressOutput:)
5463 - (bool) isCancelling:(size_t)received {
5467 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5471 - (void) _setProgressTitle:(NSString *)title {
5472 [prompt_ setText:title];
5475 - (void) _setProgressPercent:(NSNumber *)percent {
5476 [progress_ setProgress:[percent floatValue]];
5479 - (void) _addProgressOutput:(NSString *)output {
5484 /* Cydia:// Protocol {{{ */
5485 @interface CydiaURLProtocol : NSURLProtocol {
5490 @implementation CydiaURLProtocol
5492 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5493 NSURL *url([request URL]);
5496 NSString *scheme([[url scheme] lowercaseString]);
5497 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5502 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5506 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5507 id<NSURLProtocolClient> client([self client]);
5509 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5511 NSData *data(UIImagePNGRepresentation(icon));
5513 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5514 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5515 [client URLProtocol:self didLoadData:data];
5516 [client URLProtocolDidFinishLoading:self];
5520 - (void) startLoading {
5521 id<NSURLProtocolClient> client([self client]);
5522 NSURLRequest *request([self request]);
5524 NSURL *url([request URL]);
5525 NSString *href([url absoluteString]);
5527 NSString *path([href substringFromIndex:8]);
5528 NSRange slash([path rangeOfString:@"/"]);
5531 if (slash.location == NSNotFound) {
5535 command = [path substringToIndex:slash.location];
5536 path = [path substringFromIndex:(slash.location + 1)];
5539 Database *database([Database sharedInstance]);
5541 if ([command isEqualToString:@"package-icon"]) {
5544 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5545 Package *package([database packageWithName:path]);
5548 UIImage *icon([package icon]);
5549 [self _returnPNGWithImage:icon forRequest:request];
5550 } else if ([command isEqualToString:@"source-icon"]) {
5553 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5554 NSString *source(Simplify(path));
5555 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5557 icon = [UIImage applicationImageNamed:@"unknown.png"];
5558 [self _returnPNGWithImage:icon forRequest:request];
5559 } else if ([command isEqualToString:@"uikit-image"]) {
5562 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5563 UIImage *icon(_UIImageWithName(path));
5564 [self _returnPNGWithImage:icon forRequest:request];
5565 } else if ([command isEqualToString:@"section-icon"]) {
5568 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5569 NSString *section(Simplify(path));
5570 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5572 icon = [UIImage applicationImageNamed:@"unknown.png"];
5573 [self _returnPNGWithImage:icon forRequest:request];
5575 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5579 - (void) stopLoading {
5585 /* Sections View {{{ */
5586 @interface SectionsView : RVPage {
5587 _transient Database *database_;
5588 NSMutableArray *sections_;
5589 NSMutableArray *filtered_;
5590 UITransitionView *transition_;
5596 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5597 - (void) reloadData;
5602 @implementation SectionsView
5605 [list_ setDataSource:nil];
5606 [list_ setDelegate:nil];
5608 [sections_ release];
5609 [filtered_ release];
5610 [transition_ release];
5612 [accessory_ release];
5616 - (int) numberOfRowsInTable:(UITable *)table {
5617 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5620 - (float) table:(UITable *)table heightForRow:(int)row {
5624 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5626 reusing = [[[SectionCell alloc] init] autorelease];
5627 [(SectionCell *)reusing setSection:(editing_ ?
5628 [sections_ objectAtIndex:row] :
5629 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5630 ) editing:editing_];
5634 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5638 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5642 - (void) tableRowSelected:(NSNotification *)notification {
5643 int row = [[notification object] selectedRow];
5654 title = @"All Packages";
5656 section = [filtered_ objectAtIndex:(row - 1)];
5657 name = [section name];
5663 title = @"(No Section)";
5667 PackageTable *table = [[[FilteredPackageTable alloc]
5671 filter:@selector(isVisiblyUninstalledInSection:)
5675 [table setDelegate:delegate_];
5677 [book_ pushPage:table];
5680 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5681 if ((self = [super initWithBook:book]) != nil) {
5682 database_ = database;
5684 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5685 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5687 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5688 [self addSubview:transition_];
5690 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5691 [transition_ transition:0 toView:list_];
5693 UITableColumn *column = [[[UITableColumn alloc]
5694 initWithTitle:@"Name"
5696 width:[self frame].size.width
5699 [list_ setDataSource:self];
5700 [list_ setSeparatorStyle:1];
5701 [list_ addTableColumn:column];
5702 [list_ setDelegate:self];
5703 [list_ setReusesTableCells:YES];
5707 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5708 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5712 - (void) reloadData {
5713 NSArray *packages = [database_ packages];
5715 [sections_ removeAllObjects];
5716 [filtered_ removeAllObjects];
5718 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5719 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5722 for (Package *package in packages) {
5723 NSString *name([package section]);
5726 Section *section([sections objectForKey:name]);
5727 if (section == nil) {
5728 section = [[[Section alloc] initWithName:name] autorelease];
5729 [sections setObject:section forKey:name];
5733 if ([package valid] && [package installed] == nil && [package visible])
5734 [filtered addObject:package];
5738 [sections_ addObjectsFromArray:[sections allValues]];
5739 [sections_ sortUsingSelector:@selector(compareByName:)];
5742 [filtered sortUsingSelector:@selector(compareBySection:)];
5745 Section *section = nil;
5746 for (Package *package in filtered) {
5747 NSString *name = [package section];
5749 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5750 section = name == nil ?
5751 [[[Section alloc] initWithName:nil] autorelease] :
5752 [sections objectForKey:name];
5753 [filtered_ addObject:section];
5756 [section addToCount];
5764 - (void) resetView {
5766 [self _rightButtonClicked];
5769 - (void) resetViewAnimated:(BOOL)animated {
5770 [list_ resetViewAnimated:animated];
5773 - (void) _rightButtonClicked {
5774 if ((editing_ = !editing_))
5777 [delegate_ updateData];
5778 [book_ reloadTitleForPage:self];
5779 [book_ reloadButtonsForPage:self];
5782 - (NSString *) title {
5783 return editing_ ? @"Section Visibility" : @"Install by Section";
5786 - (NSString *) backButtonTitle {
5790 - (id) rightButtonTitle {
5791 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5794 - (UINavigationButtonStyle) rightButtonStyle {
5795 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5798 - (UIView *) accessoryView {
5804 /* Changes View {{{ */
5805 @interface ChangesView : RVPage {
5806 _transient Database *database_;
5807 NSMutableArray *packages_;
5808 NSMutableArray *sections_;
5809 UISectionList *list_;
5813 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5814 - (void) reloadData;
5818 @implementation ChangesView
5821 [[list_ table] setDelegate:nil];
5822 [list_ setDataSource:nil];
5824 [packages_ release];
5825 [sections_ release];
5830 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5831 return [sections_ count];
5834 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5835 return [[sections_ objectAtIndex:section] name];
5838 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5839 return [[sections_ objectAtIndex:section] row];
5842 - (int) numberOfRowsInTable:(UITable *)table {
5843 return [packages_ count];
5846 - (float) table:(UITable *)table heightForRow:(int)row {
5847 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5850 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5852 reusing = [[[PackageCell alloc] init] autorelease];
5853 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5857 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5861 - (void) tableRowSelected:(NSNotification *)notification {
5862 int row = [[notification object] selectedRow];
5865 Package *package = [packages_ objectAtIndex:row];
5866 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5867 [view setDelegate:delegate_];
5868 [view setPackage:package];
5869 [book_ pushPage:view];
5872 - (void) _leftButtonClicked {
5873 [(CYBook *)book_ update];
5874 [self reloadButtons];
5877 - (void) _rightButtonClicked {
5878 [delegate_ distUpgrade];
5881 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5882 if ((self = [super initWithBook:book]) != nil) {
5883 database_ = database;
5885 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5886 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5888 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5889 [self addSubview:list_];
5891 [list_ setShouldHideHeaderInShortLists:NO];
5892 [list_ setDataSource:self];
5893 //[list_ setSectionListStyle:1];
5895 UITableColumn *column = [[[UITableColumn alloc]
5896 initWithTitle:@"Name"
5898 width:[self frame].size.width
5901 UITable *table = [list_ table];
5902 [table setSeparatorStyle:1];
5903 [table addTableColumn:column];
5904 [table setDelegate:self];
5905 [table setReusesTableCells:YES];
5909 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5910 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5914 - (void) reloadData {
5915 NSArray *packages = [database_ packages];
5917 [packages_ removeAllObjects];
5918 [sections_ removeAllObjects];
5921 for (Package *package in packages)
5923 [package installed] == nil && [package valid] && [package visible] ||
5924 [package upgradableAndEssential:YES]
5926 [packages_ addObject:package];
5929 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5932 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5933 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5934 Section *section = nil;
5938 bool unseens = false;
5940 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5943 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5944 Package *package = [packages_ objectAtIndex:offset];
5946 if (![package upgradableAndEssential:YES]) {
5948 NSDate *seen = [package seen];
5950 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5953 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5954 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5955 [sections_ addObject:section];
5959 [section addToCount];
5960 } else if ([package ignored])
5961 [ignored addToCount];
5964 [upgradable addToCount];
5969 CFRelease(formatter);
5972 Section *last = [sections_ lastObject];
5973 size_t count = [last count];
5974 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5975 [sections_ removeLastObject];
5978 if ([ignored count] != 0)
5979 [sections_ insertObject:ignored atIndex:0];
5981 [sections_ insertObject:upgradable atIndex:0];
5984 [self reloadButtons];
5987 - (void) resetViewAnimated:(BOOL)animated {
5988 [list_ resetViewAnimated:animated];
5991 - (NSString *) leftButtonTitle {
5992 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5995 - (id) rightButtonTitle {
5996 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5999 - (NSString *) title {
6005 /* Search View {{{ */
6006 @protocol SearchViewDelegate
6007 - (void) showKeyboard:(BOOL)show;
6010 @interface SearchView : RVPage {
6012 UISearchField *field_;
6013 UITransitionView *transition_;
6014 FilteredPackageTable *table_;
6015 UIPreferencesTable *advanced_;
6021 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6022 - (void) reloadData;
6026 @implementation SearchView
6029 [field_ setDelegate:nil];
6031 [accessory_ release];
6033 [transition_ release];
6035 [advanced_ release];
6040 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6044 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6046 case 0: return @"Advanced Search (Coming Soon!)";
6048 default: _assert(false);
6052 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6056 default: _assert(false);
6060 - (void) _showKeyboard:(BOOL)show {
6061 CGSize keysize = [UIKeyboard defaultSize];
6062 CGRect keydown = [book_ pageBounds];
6063 CGRect keyup = keydown;
6064 keyup.size.height -= keysize.height - ButtonBarHeight_;
6066 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6068 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6069 [animation setSignificantRectFields:8];
6072 [animation setStartFrame:keydown];
6073 [animation setEndFrame:keyup];
6075 [animation setStartFrame:keyup];
6076 [animation setEndFrame:keydown];
6079 UIAnimator *animator = [UIAnimator sharedAnimator];
6082 addAnimations:[NSArray arrayWithObjects:animation, nil]
6083 withDuration:(KeyboardTime_ - delay)
6088 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6090 [delegate_ showKeyboard:show];
6093 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6094 [self _showKeyboard:YES];
6097 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6098 [self _showKeyboard:NO];
6101 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6103 NSString *text([field_ text]);
6104 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6110 - (void) textFieldClearButtonPressed:(UITextField *)field {
6114 - (void) keyboardInputShouldDelete:(id)input {
6118 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6119 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6123 [field_ resignFirstResponder];
6128 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6129 if ((self = [super initWithBook:book]) != nil) {
6130 CGRect pageBounds = [book_ pageBounds];
6132 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6133 [self addSubview:transition_];
6135 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6137 [advanced_ setReusesTableCells:YES];
6138 [advanced_ setDataSource:self];
6139 [advanced_ reloadData];
6141 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6142 CGColor dimmed(space_, 0, 0, 0, 0.5);
6143 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6145 table_ = [[FilteredPackageTable alloc]
6149 filter:@selector(isUnfilteredAndSearchedForBy:)
6153 [table_ setShouldHideHeaderInShortLists:NO];
6154 [transition_ transition:0 toView:table_];
6163 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6170 [self bounds].size.width - area.origin.x - 18;
6172 area.size.height = [UISearchField defaultHeight];
6174 field_ = [[UISearchField alloc] initWithFrame:area];
6176 UIFont *font = [UIFont systemFontOfSize:16];
6177 [field_ setFont:font];
6179 [field_ setPlaceholder:@"Package Names & Descriptions"];
6180 [field_ setDelegate:self];
6182 [field_ setPaddingTop:5];
6184 UITextInputTraits *traits([field_ textInputTraits]);
6185 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6186 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6187 [traits setReturnKeyType:UIReturnKeySearch];
6189 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6191 accessory_ = [[UIView alloc] initWithFrame:accrect];
6192 [accessory_ addSubview:field_];
6194 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6195 [configure setShowPressFeedback:YES];
6196 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6197 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6198 [accessory_ addSubview:configure];*/
6200 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6201 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6207 LKAnimation *animation = [LKTransition animation];
6208 [animation setType:@"oglFlip"];
6209 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6210 [animation setFillMode:@"extended"];
6211 [animation setTransitionFlags:3];
6212 [animation setDuration:10];
6213 [animation setSpeed:0.35];
6214 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6215 [[transition_ _layer] addAnimation:animation forKey:0];
6216 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6217 flipped_ = !flipped_;
6221 - (void) configurePushed {
6222 [field_ resignFirstResponder];
6226 - (void) resetViewAnimated:(BOOL)animated {
6229 [table_ resetViewAnimated:animated];
6232 - (void) _reloadData {
6235 - (void) reloadData {
6238 [table_ setObject:[field_ text]];
6239 _profile(SearchView$reloadData)
6240 [table_ reloadData];
6243 [table_ resetCursor];
6246 - (UIView *) accessoryView {
6250 - (NSString *) title {
6254 - (NSString *) backButtonTitle {
6258 - (void) setDelegate:(id)delegate {
6259 [table_ setDelegate:delegate];
6260 [super setDelegate:delegate];
6266 @interface SettingsView : RVPage {
6267 _transient Database *database_;
6270 UIPreferencesTable *table_;
6271 _UISwitchSlider *subscribedSwitch_;
6272 _UISwitchSlider *ignoredSwitch_;
6273 UIPreferencesControlTableCell *subscribedCell_;
6274 UIPreferencesControlTableCell *ignoredCell_;
6277 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6281 @implementation SettingsView
6284 [table_ setDataSource:nil];
6287 if (package_ != nil)
6290 [subscribedSwitch_ release];
6291 [ignoredSwitch_ release];
6292 [subscribedCell_ release];
6293 [ignoredCell_ release];
6297 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6298 if (package_ == nil)
6304 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6305 if (package_ == nil)
6312 default: _assert(false);
6318 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6319 if (package_ == nil)
6326 default: _assert(false);
6332 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6333 if (package_ == nil)
6340 default: _assert(false);
6346 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6347 if (package_ == nil)
6350 _UISwitchSlider *slider([cell control]);
6351 BOOL value([slider value] != 0);
6352 NSMutableDictionary *metadata([package_ metadata]);
6355 if (NSNumber *number = [metadata objectForKey:key])
6356 before = [number boolValue];
6360 if (value != before) {
6361 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6363 [delegate_ updateData];
6367 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6368 [self onSomething:cell withKey:@"IsSubscribed"];
6371 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6372 [self onSomething:cell withKey:@"IsIgnored"];
6375 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6376 if (package_ == nil)
6380 case 0: switch (row) {
6382 return subscribedCell_;
6384 return ignoredCell_;
6385 default: _assert(false);
6388 case 1: switch (row) {
6390 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6391 [cell setShowSelection:NO];
6392 [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."];
6396 default: _assert(false);
6399 default: _assert(false);
6405 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6406 if ((self = [super initWithBook:book])) {
6407 database_ = database;
6408 name_ = [package retain];
6410 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6411 [self addSubview:table_];
6413 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6414 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6416 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6417 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6419 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6420 [subscribedCell_ setShowSelection:NO];
6421 [subscribedCell_ setTitle:@"Show All Changes"];
6422 [subscribedCell_ setControl:subscribedSwitch_];
6424 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6425 [ignoredCell_ setShowSelection:NO];
6426 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6427 [ignoredCell_ setControl:ignoredSwitch_];
6429 [table_ setDataSource:self];
6434 - (void) resetViewAnimated:(BOOL)animated {
6435 [table_ resetViewAnimated:animated];
6438 - (void) reloadData {
6439 if (package_ != nil)
6440 [package_ autorelease];
6441 package_ = [database_ packageWithName:name_];
6442 if (package_ != nil) {
6444 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6445 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6448 [table_ reloadData];
6451 - (NSString *) title {
6457 /* Signature View {{{ */
6458 @interface SignatureView : BrowserView {
6459 _transient Database *database_;
6463 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6467 @implementation SignatureView
6474 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6476 [super webView:sender didClearWindowObject:window forFrame:frame];
6479 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6480 if ((self = [super initWithBook:book]) != nil) {
6481 database_ = database;
6482 package_ = [package retain];
6487 - (void) resetViewAnimated:(BOOL)animated {
6490 - (void) reloadData {
6491 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6497 @interface Cydia : UIApplication <
6498 ConfirmationViewDelegate,
6499 ProgressViewDelegate,
6508 UIToolbar *buttonbar_;
6512 NSMutableArray *essential_;
6513 NSMutableArray *broken_;
6515 Database *database_;
6516 ProgressView *progress_;
6520 UIKeyboard *keyboard_;
6521 UIProgressHUD *hud_;
6523 SectionsView *sections_;
6524 ChangesView *changes_;
6525 ManageView *manage_;
6526 SearchView *search_;
6531 @implementation Cydia
6534 if ([broken_ count] != 0) {
6535 int count = [broken_ count];
6537 UIActionSheet *sheet = [[[UIActionSheet alloc]
6538 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6539 buttons:[NSArray arrayWithObjects:
6541 @"Ignore (Temporary)",
6543 defaultButtonIndex:0
6548 [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."];
6549 [sheet popupAlertAnimated:YES];
6550 } else if (!Ignored_ && [essential_ count] != 0) {
6551 int count = [essential_ count];
6553 UIActionSheet *sheet = [[[UIActionSheet alloc]
6554 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6555 buttons:[NSArray arrayWithObjects:
6556 @"Upgrade Essential",
6557 @"Complete Upgrade",
6558 @"Ignore (Temporary)",
6560 defaultButtonIndex:0
6565 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6566 [sheet popupAlertAnimated:YES];
6570 - (void) _reloadData {
6573 static bool loaded(false);
6574 UIProgressHUD *hud([self addProgressHUD]);
6575 [hud setText:(loaded ? @"Reloading Data" : @"Loading Data")];
6578 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6581 [self removeProgressHUD:hud];
6585 [essential_ removeAllObjects];
6586 [broken_ removeAllObjects];
6588 NSArray *packages = [database_ packages];
6589 for (Package *package in packages) {
6591 [broken_ addObject:package];
6592 if ([package upgradableAndEssential:NO]) {
6593 if ([package essential])
6594 [essential_ addObject:package];
6600 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6601 [buttonbar_ setBadgeValue:badge forButton:3];
6602 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6603 [buttonbar_ setBadgeAnimated:YES forButton:3];
6604 [self setApplicationBadge:badge];
6606 [buttonbar_ setBadgeValue:nil forButton:3];
6607 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6608 [buttonbar_ setBadgeAnimated:NO forButton:3];
6609 [self removeApplicationBadge];
6614 // XXX: what is this line of code for?
6615 if ([packages count] == 0);
6616 else if (Loaded_ || ManualRefresh) loaded:
6621 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6622 NSTimeInterval interval([update timeIntervalSinceNow]);
6623 if (interval <= 0 && interval > -600)
6631 - (void) _saveConfig {
6634 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6640 - (void) updateData {
6643 /* XXX: this is just stupid */
6644 if (tag_ != 2 && sections_ != nil)
6645 [sections_ reloadData];
6646 if (tag_ != 3 && changes_ != nil)
6647 [changes_ reloadData];
6648 if (tag_ != 5 && search_ != nil)
6649 [search_ reloadData];
6659 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6660 _assert(file != NULL);
6662 NSArray *keys = [Sources_ allKeys];
6664 for (NSString *key in keys) {
6665 NSDictionary *source = [Sources_ objectForKey:key];
6667 fprintf(file, "%s %s %s\n",
6668 [[source objectForKey:@"Type"] UTF8String],
6669 [[source objectForKey:@"URI"] UTF8String],
6670 [[source objectForKey:@"Distribution"] UTF8String]
6679 detachNewThreadSelector:@selector(update_)
6682 title:@"Updating Sources"
6686 - (void) reloadData {
6687 @synchronized (self) {
6688 if (confirm_ == nil)
6694 pkgProblemResolver *resolver = [database_ resolver];
6696 resolver->InstallProtect();
6697 if (!resolver->Resolve(true))
6701 - (void) popUpBook:(RVBook *)book {
6702 [underlay_ popSubview:book];
6705 - (CGRect) popUpBounds {
6706 return [underlay_ bounds];
6710 [database_ prepare];
6712 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6713 [confirm_ setDelegate:self];
6715 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6716 [page setDelegate:self];
6718 [confirm_ setPage:page];
6719 [self popUpBook:confirm_];
6722 - (void) installPackage:(Package *)package {
6723 @synchronized (self) {
6730 - (void) removePackage:(Package *)package {
6731 @synchronized (self) {
6738 - (void) distUpgrade {
6739 @synchronized (self) {
6740 [database_ upgrade];
6746 @synchronized (self) {
6748 if (confirm_ != nil) {
6756 [overlay_ removeFromSuperview];
6760 detachNewThreadSelector:@selector(perform)
6767 - (void) bootstrap_ {
6769 [database_ upgrade];
6770 [database_ prepare];
6771 [database_ perform];
6774 - (void) bootstrap {
6776 detachNewThreadSelector:@selector(bootstrap_)
6779 title:@"Bootstrap Install"
6783 - (void) progressViewIsComplete:(ProgressView *)progress {
6784 if (confirm_ != nil) {
6785 [underlay_ addSubview:overlay_];
6786 [confirm_ popFromSuperviewAnimated:NO];
6792 - (void) setPage:(RVPage *)page {
6793 [page resetViewAnimated:NO];
6794 [page setDelegate:self];
6795 [book_ setPage:page];
6798 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6799 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6800 [browser loadURL:url];
6804 - (void) _setHomePage {
6805 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6808 - (void) buttonBarItemTapped:(id)sender {
6809 unsigned tag = [sender tag];
6811 [book_ resetViewAnimated:YES];
6813 } else if (tag_ == 2 && tag != 2)
6814 [sections_ resetView];
6817 case 1: [self _setHomePage]; break;
6819 case 2: [self setPage:sections_]; break;
6820 case 3: [self setPage:changes_]; break;
6821 case 4: [self setPage:manage_]; break;
6822 case 5: [self setPage:search_]; break;
6824 default: _assert(false);
6830 - (void) applicationWillSuspend {
6832 [super applicationWillSuspend];
6835 - (void) askForSettings {
6836 UIActionSheet *role = [[[UIActionSheet alloc]
6837 initWithTitle:@"Who Are You?"
6838 buttons:[NSArray arrayWithObjects:
6839 @"User (Graphical Only)",
6840 @"Hacker (+ Command Line)",
6841 @"Developer (No Filters)",
6843 defaultButtonIndex:-1
6848 [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."];
6849 [role popupAlertAnimated:YES];
6854 [self setStatusBarShowsProgress:NO];
6855 [self removeProgressHUD:hud_];
6860 pid_t pid = ExecFork();
6862 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6863 perror("launchctl stop");
6870 [self askForSettings];
6875 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6877 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6878 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6879 0, 0, screenrect.size.width, screenrect.size.height - 48
6880 ) database:database_];
6882 [book_ setDelegate:self];
6884 [overlay_ addSubview:book_];
6886 NSArray *buttonitems = [NSArray arrayWithObjects:
6887 [NSDictionary dictionaryWithObjectsAndKeys:
6888 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6889 @"home-up.png", kUIButtonBarButtonInfo,
6890 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6891 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6892 self, kUIButtonBarButtonTarget,
6893 @"Home", kUIButtonBarButtonTitle,
6894 @"0", kUIButtonBarButtonType,
6897 [NSDictionary dictionaryWithObjectsAndKeys:
6898 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6899 @"install-up.png", kUIButtonBarButtonInfo,
6900 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6901 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6902 self, kUIButtonBarButtonTarget,
6903 @"Sections", kUIButtonBarButtonTitle,
6904 @"0", kUIButtonBarButtonType,
6907 [NSDictionary dictionaryWithObjectsAndKeys:
6908 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6909 @"changes-up.png", kUIButtonBarButtonInfo,
6910 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6911 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6912 self, kUIButtonBarButtonTarget,
6913 @"Changes", kUIButtonBarButtonTitle,
6914 @"0", kUIButtonBarButtonType,
6917 [NSDictionary dictionaryWithObjectsAndKeys:
6918 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6919 @"manage-up.png", kUIButtonBarButtonInfo,
6920 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6921 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6922 self, kUIButtonBarButtonTarget,
6923 @"Manage", kUIButtonBarButtonTitle,
6924 @"0", kUIButtonBarButtonType,
6927 [NSDictionary dictionaryWithObjectsAndKeys:
6928 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6929 @"search-up.png", kUIButtonBarButtonInfo,
6930 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6931 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6932 self, kUIButtonBarButtonTarget,
6933 @"Search", kUIButtonBarButtonTitle,
6934 @"0", kUIButtonBarButtonType,
6938 buttonbar_ = [[UIToolbar alloc]
6940 withFrame:CGRectMake(
6941 0, screenrect.size.height - ButtonBarHeight_,
6942 screenrect.size.width, ButtonBarHeight_
6944 withItemList:buttonitems
6947 [buttonbar_ setDelegate:self];
6948 [buttonbar_ setBarStyle:1];
6949 [buttonbar_ setButtonBarTrackingMode:2];
6951 int buttons[5] = {1, 2, 3, 4, 5};
6952 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6953 [buttonbar_ showButtonGroup:0 withDuration:0];
6955 for (int i = 0; i != 5; ++i)
6956 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6957 i * 64 + 2, 1, 60, ButtonBarHeight_
6960 [buttonbar_ showSelectionForButton:1];
6961 [overlay_ addSubview:buttonbar_];
6963 [UIKeyboard initImplementationNow];
6964 CGSize keysize = [UIKeyboard defaultSize];
6965 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6966 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6967 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6968 [overlay_ addSubview:keyboard_];
6971 [underlay_ addSubview:overlay_];
6975 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6976 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6977 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6979 manage_ = (ManageView *) [[self
6980 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6981 withClass:[ManageView class]
6989 [self _setHomePage];
6992 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6993 NSString *context([sheet context]);
6995 if ([context isEqualToString:@"missing"])
6997 else if ([context isEqualToString:@"fixhalf"]) {
7000 @synchronized (self) {
7001 for (Package *broken in broken_) {
7004 NSString *id = [broken id];
7005 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7006 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7007 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7008 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7017 [broken_ removeAllObjects];
7026 } else if ([context isEqualToString:@"role"]) {
7028 case 1: Role_ = @"User"; break;
7029 case 2: Role_ = @"Hacker"; break;
7030 case 3: Role_ = @"Developer"; break;
7037 bool reset = Settings_ != nil;
7039 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7043 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7053 } else if ([context isEqualToString:@"upgrade"]) {
7056 @synchronized (self) {
7057 for (Package *essential in essential_)
7058 [essential install];
7081 - (void) reorganize { _pooled
7082 system("/usr/libexec/cydia/free.sh");
7083 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7086 - (void) applicationSuspend:(__GSEvent *)event {
7087 if (hud_ == nil && ![progress_ isRunning])
7088 [super applicationSuspend:event];
7091 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7093 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7096 - (void) _setSuspended:(BOOL)value {
7098 [super _setSuspended:value];
7101 - (UIProgressHUD *) addProgressHUD {
7102 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
7103 [window_ setUserInteractionEnabled:NO];
7105 [progress_ addSubview:hud];
7109 - (void) removeProgressHUD:(UIProgressHUD *)hud {
7111 [hud removeFromSuperview];
7112 [window_ setUserInteractionEnabled:YES];
7115 - (void) openMailToURL:(NSURL *)url {
7116 // XXX: this makes me sad
7118 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7120 [UIApp openURL:url];// asPanel:YES];
7124 - (void) clearFirstResponder {
7125 if (id responder = [window_ firstResponder])
7126 [responder resignFirstResponder];
7129 - (RVPage *) pageForPackage:(NSString *)name {
7130 if (Package *package = [database_ packageWithName:name]) {
7131 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7132 [view setPackage:package];
7135 UIActionSheet *sheet = [[[UIActionSheet alloc]
7136 initWithTitle:@"Cannot Locate Package"
7137 buttons:[NSArray arrayWithObjects:@"Close", nil]
7138 defaultButtonIndex:0
7143 [sheet setBodyText:[NSString stringWithFormat:
7144 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7147 [sheet popupAlertAnimated:YES];
7152 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7156 NSString *scheme([[url scheme] lowercaseString]);
7157 if (![scheme isEqualToString:@"cydia"])
7159 NSString *path([url absoluteString]);
7160 if ([path length] < 8)
7162 path = [path substringFromIndex:8];
7163 if (![path hasPrefix:@"/"])
7164 path = [@"/" stringByAppendingString:path];
7166 if ([path isEqualToString:@"/add-source"])
7167 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7168 else if ([path isEqualToString:@"/storage"])
7169 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
7170 else if ([path isEqualToString:@"/sources"])
7171 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7172 else if ([path isEqualToString:@"/packages"])
7173 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7174 else if ([path hasPrefix:@"/url/"])
7175 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
7176 else if ([path hasPrefix:@"/launch/"])
7177 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
7178 else if ([path hasPrefix:@"/package-settings/"])
7179 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
7180 else if ([path hasPrefix:@"/package-signature/"])
7181 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
7182 else if ([path hasPrefix:@"/package/"])
7183 return [self pageForPackage:[path substringFromIndex:9]];
7184 else if ([path hasPrefix:@"/files/"]) {
7185 NSString *name = [path substringFromIndex:7];
7187 if (Package *package = [database_ packageWithName:name]) {
7188 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7189 [files setPackage:package];
7197 - (void) applicationOpenURL:(NSURL *)url {
7198 [super applicationOpenURL:url];
7200 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7201 [self setPage:page];
7202 [buttonbar_ showSelectionForButton:tag];
7207 - (void) applicationDidFinishLaunching:(id)unused {
7209 Font12_ = [[UIFont systemFontOfSize:12] retain];
7210 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7211 Font14_ = [[UIFont systemFontOfSize:14] retain];
7212 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7213 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7215 _assert(pkgInitConfig(*_config));
7216 _assert(pkgInitSystem(*_config, _system));
7220 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7221 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7223 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7225 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7226 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7228 [window_ orderFront:self];
7229 [window_ makeKey:self];
7230 [window_ setHidden:NO];
7232 database_ = [Database sharedInstance];
7233 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7234 [database_ setDelegate:progress_];
7235 [window_ setContentView:progress_];
7237 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7238 [progress_ setContentView:underlay_];
7240 [progress_ resetView];
7243 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7244 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7245 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7246 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7247 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7248 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7249 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7251 [self setIdleTimerDisabled:YES];
7253 hud_ = [[self addProgressHUD] retain];
7254 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7256 [self setStatusBarShowsProgress:YES];
7259 detachNewThreadSelector:@selector(reorganize)
7267 - (void) showKeyboard:(BOOL)show {
7268 CGSize keysize = [UIKeyboard defaultSize];
7269 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7270 CGRect keyup = keydown;
7271 keyup.origin.y -= keysize.height;
7273 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7274 [animation setSignificantRectFields:2];
7277 [animation setStartFrame:keydown];
7278 [animation setEndFrame:keyup];
7279 [keyboard_ activate];
7281 [animation setStartFrame:keyup];
7282 [animation setEndFrame:keydown];
7283 [keyboard_ deactivate];
7286 [[UIAnimator sharedAnimator]
7287 addAnimations:[NSArray arrayWithObjects:animation, nil]
7288 withDuration:KeyboardTime_
7293 - (void) slideUp:(UIActionSheet *)alert {
7295 [alert presentSheetFromButtonBar:buttonbar_];
7297 [alert presentSheetInView:overlay_];
7302 void AddPreferences(NSString *plist) { _pooled
7303 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7304 _assert(settings != NULL);
7305 NSMutableArray *items = [settings objectForKey:@"items"];
7309 for (NSMutableDictionary *item in items) {
7310 NSString *label = [item objectForKey:@"label"];
7311 if (label != nil && [label isEqualToString:@"Cydia"]) {
7318 for (size_t i(0); i != [items count]; ++i) {
7319 NSDictionary *item([items objectAtIndex:i]);
7320 NSString *label = [item objectForKey:@"label"];
7321 if (label != nil && [label isEqualToString:@"General"]) {
7322 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7323 @"CydiaSettings", @"bundle",
7324 @"PSLinkCell", @"cell",
7325 [NSNumber numberWithBool:YES], @"hasIcon",
7326 [NSNumber numberWithBool:YES], @"isController",
7328 nil] atIndex:(i + 1)];
7334 _assert([settings writeToFile:plist atomically:YES] == YES);
7339 id Alloc_(id self, SEL selector) {
7340 id object = alloc_(self, selector);
7341 lprintf("[%s]A-%p\n", self->isa->name, object);
7346 id Dealloc_(id self, SEL selector) {
7347 id object = dealloc_(self, selector);
7348 lprintf("[%s]D-%p\n", self->isa->name, object);
7352 int main(int argc, char *argv[]) { _pooled
7354 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7356 bool substrate(false);
7362 for (int argi(1); argi != argc; ++argi)
7363 if (strcmp(argv[argi], "--") == 0) {
7365 argv[argi] = argv[0];
7371 for (int argi(1); argi != arge; ++argi)
7372 if (strcmp(args[argi], "--bootstrap") == 0)
7374 else if (strcmp(args[argi], "--substrate") == 0)
7377 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7380 App_ = [[NSBundle mainBundle] bundlePath];
7381 Home_ = NSHomeDirectory();
7382 Locale_ = CFLocaleCopyCurrent();
7385 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7386 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7387 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7388 Sounds_Keyboard_ = [keyboard boolValue];
7394 #if 1 /* XXX: this costs 1.4s of startup performance */
7395 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7396 _assert(errno == ENOENT);
7397 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7398 _assert(errno == ENOENT);
7401 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7402 alloc_ = alloc->method_imp;
7403 alloc->method_imp = (IMP) &Alloc_;*/
7405 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7406 dealloc_ = dealloc->method_imp;
7407 dealloc->method_imp = (IMP) &Dealloc_;*/
7412 size = sizeof(maxproc);
7413 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7414 perror("sysctlbyname(\"kern.maxproc\", ?)");
7415 else if (maxproc < 64) {
7417 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7418 perror("sysctlbyname(\"kern.maxproc\", #)");
7421 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7422 char *machine = new char[size];
7423 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7424 perror("sysctlbyname(\"hw.machine\", ?)");
7428 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7430 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7431 Build_ = [system objectForKey:@"ProductBuildVersion"];
7432 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
7433 Product_ = [info objectForKey:@"SafariProductVersion"];
7434 Safari_ = [info objectForKey:@"CFBundleVersion"];
7437 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7438 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7441 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7444 if (Metadata_ == NULL)
7445 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7447 Settings_ = [Metadata_ objectForKey:@"Settings"];
7449 Packages_ = [Metadata_ objectForKey:@"Packages"];
7450 Sections_ = [Metadata_ objectForKey:@"Sections"];
7451 Sources_ = [Metadata_ objectForKey:@"Sources"];
7454 if (Settings_ != nil)
7455 Role_ = [Settings_ objectForKey:@"Role"];
7457 if (Packages_ == nil) {
7458 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7459 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7462 if (Sections_ == nil) {
7463 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7464 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7467 if (Sources_ == nil) {
7468 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7469 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7473 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7476 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7477 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7478 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7479 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7481 if (access("/User", F_OK) != 0) {
7483 system("/usr/libexec/cydia/firmware.sh");
7487 _assert([[NSFileManager defaultManager]
7488 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7489 withIntermediateDirectories:YES
7494 space_ = CGColorSpaceCreateDeviceRGB();
7496 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7497 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7498 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7499 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7500 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7501 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7502 Green_.Set(space_, 0.0, 0.5, 0.0, 1.0);
7503 Purple_.Set(space_, 0.0, 0.0, 0.7, 1.0);
7504 Purplish_.Set(space_, 0.4, 0.4, 0.8, 1.0);
7505 /*Purple_.Set(space_, 1.0, 0.3, 0.0, 1.0);
7506 Purplish_.Set(space_, 1.0, 0.6, 0.4, 1.0); ORANGE */
7507 /*Purple_.Set(space_, 1.0, 0.5, 0.0, 1.0);
7508 Purplish_.Set(space_, 1.0, 0.7, 0.2, 1.0); ORANGISH */
7509 /*Purple_.Set(space_, 0.5, 0.0, 0.7, 1.0);
7510 Purplish_.Set(space_, 0.7, 0.4, 0.8, 1.0); PURPLE */
7512 CommercialColor_ = [UIColor colorWithRed:0.93f green:1.00f blue:0.88f alpha:1.00f];
7514 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7516 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7518 UIApplicationUseLegacyEvents(YES);
7519 UIKeyboardDisableAutomaticAppearance();
7522 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7524 CGColorSpaceRelease(space_);