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 (0 && !ForRelease)
386 #define LogBrowser (1 && !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 - (void) setAddress:(NSString *)address;
664 + (Address *) addressWithString:(NSString *)string;
665 - (Address *) initWithString:(NSString *)string;
668 @implementation Address
677 - (NSString *) name {
681 - (NSString *) address {
685 - (void) setAddress:(NSString *)address {
687 [address_ autorelease];
691 address_ = [address retain];
694 + (Address *) addressWithString:(NSString *)string {
695 return [[[Address alloc] initWithString:string] autorelease];
698 + (NSArray *) _attributeKeys {
699 return [NSArray arrayWithObjects:@"address", @"name", nil];
702 - (NSArray *) attributeKeys {
703 return [[self class] _attributeKeys];
706 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
707 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
710 - (Address *) initWithString:(NSString *)string {
711 if ((self = [super init]) != nil) {
712 const char *data = [string UTF8String];
713 size_t size = [string length];
715 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
717 if (address_r(data, size)) {
718 name_ = [address_r[1] retain];
719 address_ = [address_r[2] retain];
721 name_ = [string retain];
729 /* CoreGraphics Primitives {{{ */
740 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
743 Set(space, red, green, blue, alpha);
748 CGColorRelease(color_);
755 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
757 float color[] = {red, green, blue, alpha};
758 color_ = CGColorCreate(space, color);
761 operator CGColorRef() {
767 extern "C" void UISetColor(CGColorRef color);
769 /* Random Global Variables {{{ */
770 static const int PulseInterval_ = 50000;
771 static const int ButtonBarHeight_ = 48;
772 static const float KeyboardTime_ = 0.3f;
774 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
775 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
776 #define NotifyConfig_ "/etc/notify.conf"
778 static bool Queuing_;
780 static CGColor Blue_;
781 static CGColor Blueish_;
782 static CGColor Black_;
784 static CGColor White_;
785 static CGColor Gray_;
786 static CGColor Green_;
787 static CGColor Purple_;
788 static CGColor Purplish_;
790 static UIColor *InstallingColor_;
791 static UIColor *RemovingColor_;
793 static NSString *App_;
794 static NSString *Home_;
795 static BOOL Sounds_Keyboard_;
797 static BOOL Advanced_;
799 static BOOL Ignored_;
801 static UIFont *Font12_;
802 static UIFont *Font12Bold_;
803 static UIFont *Font14_;
804 static UIFont *Font18Bold_;
805 static UIFont *Font22Bold_;
807 static const char *Machine_ = NULL;
808 static const NSString *UniqueID_ = nil;
809 static const NSString *Build_ = nil;
810 static const NSString *Product_ = nil;
811 static const NSString *Safari_ = nil;
814 CGColorSpaceRef space_;
819 static NSDictionary *SectionMap_;
820 static NSMutableDictionary *Metadata_;
821 static _transient NSMutableDictionary *Settings_;
822 static _transient NSString *Role_;
823 static _transient NSMutableDictionary *Packages_;
824 static _transient NSMutableDictionary *Sections_;
825 static _transient NSMutableDictionary *Sources_;
826 static bool Changed_;
830 static NSMutableArray *Documents_;
833 NSString *GetLastUpdate() {
834 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
837 return @"Never or Unknown";
839 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
840 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
842 CFRelease(formatter);
844 return [(NSString *) formatted autorelease];
847 /* Display Helpers {{{ */
848 inline float Interpolate(float begin, float end, float fraction) {
849 return (end - begin) * fraction + begin;
852 NSString *SizeString(double size) {
853 bool negative = size < 0;
858 while (size > 1024) {
863 static const char *powers_[] = {"B", "kB", "MB", "GB"};
865 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
868 NSString *StripVersion(NSString *version) {
869 NSRange colon = [version rangeOfString:@":"];
870 if (colon.location != NSNotFound)
871 version = [version substringFromIndex:(colon.location + 1)];
875 NSString *Simplify(NSString *title) {
876 const char *data = [title UTF8String];
877 size_t size = [title length];
879 static Pcre square_r("^\\[(.*)\\]$");
880 if (square_r(data, size))
881 return Simplify(square_r[1]);
883 static Pcre paren_r("^\\((.*)\\)$");
884 if (paren_r(data, size))
885 return Simplify(paren_r[1]);
887 static Pcre title_r("^(.*?) \\(.*\\)$");
888 if (title_r(data, size))
889 return Simplify(title_r[1]);
895 bool isSectionVisible(NSString *section) {
896 NSDictionary *metadata = [Sections_ objectForKey:section];
897 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
898 return hidden == nil || ![hidden boolValue];
901 /* Delegate Prototypes {{{ */
905 @interface NSObject (ProgressDelegate)
908 @implementation NSObject(ProgressDelegate)
910 - (void) _setProgressError:(NSArray *)args {
911 [self performSelector:@selector(setProgressError:forPackage:)
912 withObject:[args objectAtIndex:0]
913 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
919 @protocol ProgressDelegate
920 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
921 - (void) setProgressTitle:(NSString *)title;
922 - (void) setProgressPercent:(float)percent;
923 - (void) startProgress;
924 - (void) addProgressOutput:(NSString *)output;
925 - (bool) isCancelling:(size_t)received;
928 @protocol ConfigurationDelegate
929 - (void) repairWithSelector:(SEL)selector;
930 - (void) setConfigurationData:(NSString *)data;
933 @protocol CydiaDelegate
934 - (void) clearPackage:(Package *)package;
935 - (void) installPackage:(Package *)package;
936 - (void) removePackage:(Package *)package;
937 - (void) slideUp:(UIActionSheet *)alert;
938 - (void) distUpgrade;
941 - (void) askForSettings;
942 - (UIProgressHUD *) addProgressHUD;
943 - (void) removeProgressHUD:(UIProgressHUD *)hud;
944 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
945 - (RVPage *) pageForPackage:(NSString *)name;
946 - (void) openMailToURL:(NSURL *)url;
947 - (void) clearFirstResponder;
951 /* Status Delegation {{{ */
953 public pkgAcquireStatus
956 _transient NSObject<ProgressDelegate> *delegate_;
964 void setDelegate(id delegate) {
965 delegate_ = delegate;
968 virtual bool MediaChange(std::string media, std::string drive) {
972 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
975 virtual void Fetch(pkgAcquire::ItemDesc &item) {
976 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
977 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
980 virtual void Done(pkgAcquire::ItemDesc &item) {
983 virtual void Fail(pkgAcquire::ItemDesc &item) {
985 item.Owner->Status == pkgAcquire::Item::StatIdle ||
986 item.Owner->Status == pkgAcquire::Item::StatDone
990 std::string &error(item.Owner->ErrorText);
994 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
995 NSArray *fields([description componentsSeparatedByString:@" "]);
996 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
998 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
999 withObject:[NSArray arrayWithObjects:
1000 [NSString stringWithUTF8String:error.c_str()],
1007 virtual bool Pulse(pkgAcquire *Owner) {
1008 bool value = pkgAcquireStatus::Pulse(Owner);
1011 double(CurrentBytes + CurrentItems) /
1012 double(TotalBytes + TotalItems)
1015 [delegate_ setProgressPercent:percent];
1016 return [delegate_ isCancelling:CurrentBytes] ? false : value;
1019 virtual void Start() {
1020 [delegate_ startProgress];
1023 virtual void Stop() {
1027 /* Progress Delegation {{{ */
1032 _transient id<ProgressDelegate> delegate_;
1035 virtual void Update() {
1036 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
1037 [delegate_ setProgressPercent:(Percent / 100)];*/
1046 void setDelegate(id delegate) {
1047 delegate_ = delegate;
1050 virtual void Done() {
1051 //[delegate_ setProgressPercent:1];
1056 /* Database Interface {{{ */
1057 @interface Database : NSObject {
1060 pkgCacheFile cache_;
1061 pkgDepCache::Policy *policy_;
1062 pkgRecords *records_;
1063 pkgProblemResolver *resolver_;
1064 pkgAcquire *fetcher_;
1066 SPtr<pkgPackageManager> manager_;
1067 pkgSourceList *list_;
1069 NSMutableDictionary *sources_;
1070 NSMutableArray *packages_;
1072 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
1081 + (Database *) sharedInstance;
1084 - (void) _readCydia:(NSNumber *)fd;
1085 - (void) _readStatus:(NSNumber *)fd;
1086 - (void) _readOutput:(NSNumber *)fd;
1090 - (Package *) packageWithName:(NSString *)name;
1092 - (pkgCacheFile &) cache;
1093 - (pkgDepCache::Policy *) policy;
1094 - (pkgRecords *) records;
1095 - (pkgProblemResolver *) resolver;
1096 - (pkgAcquire &) fetcher;
1097 - (pkgSourceList &) list;
1098 - (NSArray *) packages;
1099 - (NSArray *) sources;
1100 - (void) reloadData;
1108 - (void) updateWithStatus:(Status &)status;
1110 - (void) setDelegate:(id)delegate;
1111 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
1115 /* Source Class {{{ */
1116 @interface Source : NSObject {
1117 NSString *description_;
1123 NSString *distribution_;
1127 NSString *defaultIcon_;
1129 NSDictionary *record_;
1133 - (Source *) initWithMetaIndex:(metaIndex *)index;
1135 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1137 - (NSString *) supportForPackage:(NSString *)package;
1139 - (NSDictionary *) record;
1143 - (NSString *) distribution;
1144 - (NSString *) type;
1146 - (NSString *) host;
1148 - (NSString *) name;
1149 - (NSString *) description;
1150 - (NSString *) label;
1151 - (NSString *) origin;
1152 - (NSString *) version;
1154 - (NSString *) defaultIcon;
1158 @implementation Source
1160 #define _clear(field) \
1167 _clear(distribution_)
1170 _clear(description_)
1175 _clear(defaultIcon_)
1184 + (NSArray *) _attributeKeys {
1185 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1188 - (NSArray *) attributeKeys {
1189 return [[self class] _attributeKeys];
1192 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1193 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1196 - (void) setMetaIndex:(metaIndex *)index {
1199 trusted_ = index->IsTrusted();
1201 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1202 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1203 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1205 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1206 if (dindex != NULL) {
1207 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1209 while (std::getline(release, line)) {
1210 std::string::size_type colon(line.find(':'));
1211 if (colon == std::string::npos)
1214 std::string name(line.substr(0, colon));
1215 std::string value(line.substr(colon + 1));
1216 while (!value.empty() && value[0] == ' ')
1217 value = value.substr(1);
1219 if (name == "Default-Icon")
1220 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1221 else if (name == "Description")
1222 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1223 else if (name == "Label")
1224 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1225 else if (name == "Origin")
1226 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1227 else if (name == "Support")
1228 support_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1229 else if (name == "Version")
1230 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1234 record_ = [Sources_ objectForKey:[self key]];
1236 record_ = [record_ retain];
1239 - (Source *) initWithMetaIndex:(metaIndex *)index {
1240 if ((self = [super init]) != nil) {
1241 [self setMetaIndex:index];
1245 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1246 NSDictionary *lhr = [self record];
1247 NSDictionary *rhr = [source record];
1250 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1252 NSString *lhs = [self name];
1253 NSString *rhs = [source name];
1255 if ([lhs length] != 0 && [rhs length] != 0) {
1256 unichar lhc = [lhs characterAtIndex:0];
1257 unichar rhc = [rhs characterAtIndex:0];
1259 if (isalpha(lhc) && !isalpha(rhc))
1260 return NSOrderedAscending;
1261 else if (!isalpha(lhc) && isalpha(rhc))
1262 return NSOrderedDescending;
1265 return [lhs compare:rhs options:LaxCompareOptions_];
1268 - (NSString *) supportForPackage:(NSString *)package {
1269 return support_ == nil ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
1272 - (NSDictionary *) record {
1280 - (NSString *) uri {
1284 - (NSString *) distribution {
1285 return distribution_;
1288 - (NSString *) type {
1292 - (NSString *) key {
1293 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1296 - (NSString *) host {
1297 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1300 - (NSString *) name {
1301 return origin_ == nil ? [self host] : origin_;
1304 - (NSString *) description {
1305 return description_;
1308 - (NSString *) label {
1309 return label_ == nil ? [self host] : label_;
1312 - (NSString *) origin {
1316 - (NSString *) version {
1320 - (NSString *) defaultIcon {
1321 return defaultIcon_;
1326 /* Relationship Class {{{ */
1327 @interface Relationship : NSObject {
1332 - (NSString *) type;
1334 - (NSString *) name;
1338 @implementation Relationship
1346 - (NSString *) type {
1354 - (NSString *) name {
1361 /* Package Class {{{ */
1362 @interface Package : NSObject {
1365 pkgCache::PkgIterator iterator_;
1366 _transient Database *database_;
1367 pkgCache::VerIterator version_;
1368 pkgCache::VerFileIterator file_;
1377 NSString *installed_;
1383 NSString *depiction_;
1384 NSString *homepage_;
1391 NSArray *relationships_;
1394 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1395 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1397 - (pkgCache::PkgIterator) iterator;
1399 - (NSString *) section;
1400 - (NSString *) simpleSection;
1404 - (Address *) maintainer;
1406 - (NSString *) description;
1409 - (NSMutableDictionary *) metadata;
1411 - (BOOL) subscribed;
1414 - (NSString *) latest;
1415 - (NSString *) installed;
1418 - (BOOL) upgradableAndEssential:(BOOL)essential;
1421 - (BOOL) unfiltered;
1425 - (BOOL) halfConfigured;
1426 - (BOOL) halfInstalled;
1428 - (NSString *) mode;
1431 - (NSString *) name;
1432 - (NSString *) tagline;
1434 - (NSString *) homepage;
1435 - (NSString *) depiction;
1436 - (Address *) author;
1438 - (NSString *) support;
1440 - (NSArray *) files;
1441 - (NSArray *) relationships;
1442 - (NSArray *) warnings;
1443 - (NSArray *) applications;
1445 - (Source *) source;
1446 - (NSString *) role;
1448 - (BOOL) matches:(NSString *)text;
1450 - (bool) hasSupportingRole;
1451 - (BOOL) hasTag:(NSString *)tag;
1452 - (NSString *) primaryPurpose;
1453 - (NSArray *) purposes;
1454 - (bool) isCommercial;
1456 - (NSComparisonResult) compareByName:(Package *)package;
1457 - (NSComparisonResult) compareBySection:(Package *)package;
1459 - (uint32_t) compareForChanges;
1464 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
1465 - (bool) isInstalledAndVisible:(NSNumber *)number;
1466 - (bool) isVisiblyUninstalledInSection:(NSString *)section;
1467 - (bool) isVisibleInSource:(Source *)source;
1471 @implementation Package
1476 if (section_ != nil)
1480 if (installed_ != nil)
1481 [installed_ release];
1489 if (depiction_ != nil)
1490 [depiction_ release];
1491 if (homepage_ != nil)
1492 [homepage_ release];
1493 if (sponsor_ != nil)
1497 if (support_ != nil)
1504 if (relationships_ != nil)
1505 [relationships_ release];
1510 + (NSString *) webScriptNameForSelector:(SEL)selector {
1511 if (selector == @selector(hasTag:))
1517 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
1518 return [self webScriptNameForSelector:selector] == nil;
1521 + (NSArray *) _attributeKeys {
1522 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"mode", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"support", @"tagline", @"warnings", nil];
1525 - (NSArray *) attributeKeys {
1526 return [[self class] _attributeKeys];
1529 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1530 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1533 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1534 if ((self = [super init]) != nil) {
1535 _profile(Package$initWithIterator)
1536 @synchronized (database) {
1537 era_ = [database era];
1539 iterator_ = iterator;
1540 database_ = database;
1542 _profile(Package$initWithIterator$Control)
1545 _profile(Package$initWithIterator$Version)
1546 version_ = [database_ policy]->GetCandidateVer(iterator_);
1549 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1551 _profile(Package$initWithIterator$Latest)
1552 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1555 pkgCache::VerIterator current;
1556 NSString *installed;
1558 _profile(Package$initWithIterator$Current)
1559 current = iterator_.CurrentVer();
1560 installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1563 _profile(Package$initWithIterator$Installed)
1564 installed_ = [StripVersion(installed) retain];
1567 _profile(Package$initWithIterator$File)
1568 if (!version_.end())
1569 file_ = version_.FileList();
1571 pkgCache &cache([database_ cache]);
1572 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1576 _profile(Package$initWithIterator$Name)
1577 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1581 _profile(Package$initWithIterator$Parse)
1582 pkgRecords::Parser *parser;
1584 _profile(Package$initWithIterator$Parse$Lookup)
1585 parser = &[database_ records]->Lookup(file_);
1588 const char *begin, *end;
1589 parser->GetRec(begin, end);
1591 NSString *website(nil);
1592 NSString *sponsor(nil);
1593 NSString *author(nil);
1602 {"depiction", &depiction_},
1603 {"homepage", &homepage_},
1604 {"website", &website},
1605 {"support", &support_},
1606 {"sponsor", &sponsor},
1607 {"author", &author},
1611 while (begin != end)
1612 if (*begin == '\n') {
1615 } else if (isblank(*begin)) next: {
1616 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1619 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1620 const char *name(begin);
1621 size_t size(colon - begin);
1623 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1626 const char *stop(begin == NULL ? end : begin);
1627 while (stop[-1] == '\r')
1629 while (++colon != stop && isblank(*colon));
1631 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1632 if (strncasecmp(names[i].name_, name, size) == 0) {
1635 _profile(Package$initWithIterator$Parse$Value)
1636 value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
1639 *names[i].value_ = value;
1649 _profile(Package$initWithIterator$Parse$Retain)
1651 name_ = [name_ retain];
1652 _profile(Package$initWithIterator$Parse$Tagline)
1653 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1656 icon_ = [icon_ retain];
1657 if (depiction_ != nil)
1658 depiction_ = [depiction_ retain];
1659 if (homepage_ == nil)
1660 homepage_ = website;
1661 if ([homepage_ isEqualToString:depiction_])
1663 if (homepage_ != nil)
1664 homepage_ = [homepage_ retain];
1666 sponsor_ = [[Address addressWithString:sponsor] retain];
1668 author_ = [[Address addressWithString:author] retain];
1670 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1674 _profile(Package$initWithIterator$Tags)
1676 for (NSString *tag in tags_)
1677 if ([tag hasPrefix:@"role::"]) {
1678 role_ = [[tag substringFromIndex:6] retain];
1683 NSString *solid(latest == nil ? installed : latest);
1684 bool changed(false);
1686 NSString *key([id_ lowercaseString]);
1688 _profile(Package$initWithIterator$Metadata)
1689 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1690 if (metadata == nil) {
1691 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1696 [metadata setObject:solid forKey:@"LastVersion"];
1699 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1700 NSDate *last([metadata objectForKey:@"LastSeen"]);
1701 NSString *version([metadata objectForKey:@"LastVersion"]);
1704 first = last == nil ? now_ : last;
1705 [metadata setObject:first forKey:@"FirstSeen"];
1710 if (version == nil) {
1711 [metadata setObject:solid forKey:@"LastVersion"];
1713 } else if (![version isEqualToString:solid]) {
1714 [metadata setObject:solid forKey:@"LastVersion"];
1716 [metadata setObject:last forKey:@"LastSeen"];
1722 [Packages_ setObject:metadata forKey:key];
1727 const char *section(iterator_.Section());
1728 if (section == NULL)
1731 NSString *name([[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_']);
1734 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1735 if (NSString *rename = [value objectForKey:@"Rename"]) {
1740 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1743 essential_ = (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1744 } _end } return self;
1747 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1748 return [[[Package alloc]
1749 initWithIterator:iterator
1754 - (pkgCache::PkgIterator) iterator {
1758 - (NSString *) section {
1762 - (NSString *) simpleSection {
1763 if (NSString *section = [self section])
1764 return Simplify(section);
1769 - (NSString *) uri {
1772 pkgIndexFile *index;
1773 pkgCache::PkgFileIterator file(file_.File());
1774 if (![database_ list].FindIndex(file, index))
1776 return [NSString stringWithUTF8String:iterator_->Path];
1777 //return [NSString stringWithUTF8String:file.Site()];
1778 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1782 - (Address *) maintainer {
1785 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1786 const std::string &maintainer(parser->Maintainer());
1787 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1791 return version_.end() ? 0 : version_->InstalledSize;
1794 - (NSString *) description {
1797 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1798 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1800 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1801 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1802 if ([lines count] < 2)
1805 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1806 for (size_t i(1), e([lines count]); i != e; ++i) {
1807 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1808 [trimmed addObject:trim];
1811 return [trimmed componentsJoinedByString:@"\n"];
1815 _profile(Package$index)
1816 NSString *name([self name]);
1817 if ([name length] == 0)
1819 unichar character([name characterAtIndex:0]);
1820 if (!isalpha(character))
1822 return toupper(character);
1826 - (NSMutableDictionary *) metadata {
1827 return [Packages_ objectForKey:[id_ lowercaseString]];
1831 NSDictionary *metadata([self metadata]);
1832 if ([self subscribed])
1833 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1835 return [metadata objectForKey:@"FirstSeen"];
1838 - (BOOL) subscribed {
1839 NSDictionary *metadata([self metadata]);
1840 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1841 return [subscribed boolValue];
1847 NSDictionary *metadata([self metadata]);
1848 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1849 return [ignored boolValue];
1854 - (NSString *) latest {
1858 - (NSString *) installed {
1863 return !version_.end();
1866 - (BOOL) upgradableAndEssential:(BOOL)essential {
1867 pkgCache::VerIterator current = iterator_.CurrentVer();
1871 value = essential && [self essential] && [self visible];
1873 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1877 - (BOOL) essential {
1882 return [database_ cache][iterator_].InstBroken();
1885 - (BOOL) unfiltered {
1886 NSString *section = [self section];
1887 return section == nil || isSectionVisible(section);
1891 return [self hasSupportingRole] && [self unfiltered];
1895 unsigned char current = iterator_->CurrentState;
1896 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1899 - (BOOL) halfConfigured {
1900 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1903 - (BOOL) halfInstalled {
1904 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1908 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1909 return state.Mode != pkgDepCache::ModeKeep;
1912 - (NSString *) mode {
1913 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1915 switch (state.Mode) {
1916 case pkgDepCache::ModeDelete:
1917 if ((state.iFlags & pkgDepCache::Purge) != 0)
1921 case pkgDepCache::ModeKeep:
1922 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1923 return @"Reinstall";
1924 /*else if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1928 case pkgDepCache::ModeInstall:
1929 /*if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1930 return @"Reinstall";
1931 else*/ switch (state.Status) {
1933 return @"Downgrade";
1939 return @"New Install";
1952 - (NSString *) name {
1953 return name_ == nil ? id_ : name_;
1956 - (NSString *) tagline {
1960 - (UIImage *) icon {
1961 NSString *section = [self simpleSection];
1965 if ([icon_ hasPrefix:@"file:///"])
1966 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1967 if (icon == nil) if (section != nil)
1968 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1969 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1970 if ([dicon hasPrefix:@"file:///"])
1971 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1973 icon = [UIImage applicationImageNamed:@"unknown.png"];
1977 - (NSString *) homepage {
1981 - (NSString *) depiction {
1985 - (Address *) sponsor {
1989 - (Address *) author {
1993 - (NSString *) support {
1994 return support_ != nil ? support_ : [[self source] supportForPackage:id_];
1997 - (NSArray *) files {
1998 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1999 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
2002 fin.open([path UTF8String]);
2007 while (std::getline(fin, line))
2008 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
2013 - (NSArray *) relationships {
2014 return relationships_;
2017 - (NSArray *) warnings {
2018 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
2019 const char *name(iterator_.Name());
2021 size_t length(strlen(name));
2022 if (length < 2) invalid:
2023 [warnings addObject:@"illegal package identifier"];
2024 else for (size_t i(0); i != length; ++i)
2026 /* XXX: technically this is not allowed */
2027 (name[i] < 'A' || name[i] > 'Z') &&
2028 (name[i] < 'a' || name[i] > 'z') &&
2029 (name[i] < '0' || name[i] > '9') &&
2030 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
2033 if (strcmp(name, "cydia") != 0) {
2035 bool _private = false;
2038 bool repository = [[self section] isEqualToString:@"Repositories"];
2040 if (NSArray *files = [self files])
2041 for (NSString *file in files)
2042 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
2044 else if (!_private && [file isEqualToString:@"/private"])
2046 else if (!stash && [file isEqualToString:@"/var/stash"])
2049 /* XXX: this is not sensitive enough. only some folders are valid. */
2050 if (cydia && !repository)
2051 [warnings addObject:@"files installed into Cydia.app"];
2053 [warnings addObject:@"files installed with /private/*"];
2055 [warnings addObject:@"files installed to /var/stash"];
2058 return [warnings count] == 0 ? nil : warnings;
2061 - (NSArray *) applications {
2062 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
2064 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
2066 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
2067 if (NSArray *files = [self files])
2068 for (NSString *file in files)
2069 if (application_r(file)) {
2070 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
2071 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
2072 if ([id isEqualToString:me])
2075 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
2077 display = application_r[1];
2079 NSString *bundle([file stringByDeletingLastPathComponent]);
2080 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
2081 if (icon == nil || [icon length] == 0)
2083 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
2085 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
2086 [applications addObject:application];
2088 [application addObject:id];
2089 [application addObject:display];
2090 [application addObject:url];
2093 return [applications count] == 0 ? nil : applications;
2096 - (Source *) source {
2098 @synchronized (database_) {
2099 if ([database_ era] != era_ || file_.end())
2102 source_ = [database_ getSource:file_.File()];
2114 - (NSString *) role {
2118 - (BOOL) matches:(NSString *)text {
2124 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2125 if (range.location != NSNotFound)
2128 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2129 if (range.location != NSNotFound)
2132 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2133 if (range.location != NSNotFound)
2139 - (bool) hasSupportingRole {
2142 if ([role_ isEqualToString:@"enduser"])
2144 if ([Role_ isEqualToString:@"User"])
2146 if ([role_ isEqualToString:@"hacker"])
2148 if ([Role_ isEqualToString:@"Hacker"])
2150 if ([role_ isEqualToString:@"developer"])
2152 if ([Role_ isEqualToString:@"Developer"])
2157 - (BOOL) hasTag:(NSString *)tag {
2158 return tags_ == nil ? NO : [tags_ containsObject:tag];
2161 - (NSString *) primaryPurpose {
2162 for (NSString *tag in tags_)
2163 if ([tag hasPrefix:@"purpose::"])
2164 return [tag substringFromIndex:9];
2168 - (NSArray *) purposes {
2169 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2170 for (NSString *tag in tags_)
2171 if ([tag hasPrefix:@"purpose::"])
2172 [purposes addObject:[tag substringFromIndex:9]];
2173 return [purposes count] == 0 ? nil : purposes;
2176 - (bool) isCommercial {
2177 return [self hasTag:@"cydia::commercial"];
2180 - (NSComparisonResult) compareByName:(Package *)package {
2181 NSString *lhs = [self name];
2182 NSString *rhs = [package name];
2184 if ([lhs length] != 0 && [rhs length] != 0) {
2185 unichar lhc = [lhs characterAtIndex:0];
2186 unichar rhc = [rhs characterAtIndex:0];
2188 if (isalpha(lhc) && !isalpha(rhc))
2189 return NSOrderedAscending;
2190 else if (!isalpha(lhc) && isalpha(rhc))
2191 return NSOrderedDescending;
2194 return [lhs compare:rhs options:LaxCompareOptions_];
2197 - (NSComparisonResult) compareBySection:(Package *)package {
2198 NSString *lhs = [self section];
2199 NSString *rhs = [package section];
2201 if (lhs == NULL && rhs != NULL)
2202 return NSOrderedAscending;
2203 else if (lhs != NULL && rhs == NULL)
2204 return NSOrderedDescending;
2205 else if (lhs != NULL && rhs != NULL) {
2206 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2207 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2210 return NSOrderedSame;
2213 - (uint32_t) compareForChanges {
2218 uint32_t timestamp : 30;
2219 uint32_t ignored : 1;
2220 uint32_t upgradable : 1;
2224 bool upgradable([self upgradableAndEssential:YES]);
2225 value.bits.upgradable = upgradable ? 1 : 0;
2228 value.bits.timestamp = 0;
2229 value.bits.ignored = [self ignored] ? 0 : 1;
2230 value.bits.upgradable = 1;
2232 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2233 value.bits.ignored = 0;
2234 value.bits.upgradable = 0;
2237 return _not(uint32_t) - value.key;
2241 pkgProblemResolver *resolver = [database_ resolver];
2242 resolver->Clear(iterator_);
2243 resolver->Protect(iterator_);
2247 pkgProblemResolver *resolver = [database_ resolver];
2248 resolver->Clear(iterator_);
2249 resolver->Protect(iterator_);
2250 pkgCacheFile &cache([database_ cache]);
2251 cache->MarkInstall(iterator_, false);
2252 pkgDepCache::StateCache &state((*cache)[iterator_]);
2253 if (!state.Install())
2254 cache->SetReInstall(iterator_, true);
2258 pkgProblemResolver *resolver = [database_ resolver];
2259 resolver->Clear(iterator_);
2260 resolver->Protect(iterator_);
2261 resolver->Remove(iterator_);
2262 [database_ cache]->MarkDelete(iterator_, true);
2265 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
2266 _profile(Package$isUnfilteredAndSearchedForBy)
2269 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2270 value &= [self unfiltered];
2273 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2274 value &= [self matches:search];
2281 - (bool) isInstalledAndVisible:(NSNumber *)number {
2282 return (![number boolValue] || [self visible]) && [self installed] != nil;
2285 - (bool) isVisiblyUninstalledInSection:(NSString *)name {
2286 NSString *section = [self section];
2290 [self installed] == nil && (
2292 section == nil && [name length] == 0 ||
2293 [name isEqualToString:section]
2297 - (bool) isVisibleInSource:(Source *)source {
2298 return [self source] == source && [self visible];
2303 /* Section Class {{{ */
2304 @interface Section : NSObject {
2311 - (NSComparisonResult) compareByName:(Section *)section;
2312 - (Section *) initWithName:(NSString *)name;
2313 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2314 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2315 - (NSString *) name;
2319 - (void) addToCount;
2323 @implementation Section
2330 - (NSComparisonResult) compareByName:(Section *)section {
2331 NSString *lhs = [self name];
2332 NSString *rhs = [section name];
2334 if ([lhs length] != 0 && [rhs length] != 0) {
2335 unichar lhc = [lhs characterAtIndex:0];
2336 unichar rhc = [rhs characterAtIndex:0];
2338 if (isalpha(lhc) && !isalpha(rhc))
2339 return NSOrderedAscending;
2340 else if (!isalpha(lhc) && isalpha(rhc))
2341 return NSOrderedDescending;
2344 return [lhs compare:rhs options:LaxCompareOptions_];
2347 - (Section *) initWithName:(NSString *)name {
2348 return [self initWithName:name row:0];
2351 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2352 if ((self = [super init]) != nil) {
2353 name_ = [name retain];
2359 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2360 if ((self = [super init]) != nil) {
2361 name_ = [(index == '#' ? @"123" : [NSString stringWithCharacters:&index length:1]) retain];
2367 - (NSString *) name {
2383 - (void) addToCount {
2391 static NSArray *Finishes_;
2393 /* Database Implementation {{{ */
2394 @implementation Database
2396 + (Database *) sharedInstance {
2397 static Database *instance;
2398 if (instance == nil)
2399 instance = [[Database alloc] init];
2412 - (void) _readCydia:(NSNumber *)fd { _pooled
2413 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2414 std::istream is(&ib);
2417 static Pcre finish_r("^finish:([^:]*)$");
2419 while (std::getline(is, line)) {
2420 const char *data(line.c_str());
2421 size_t size = line.size();
2422 lprintf("C:%s\n", data);
2424 if (finish_r(data, size)) {
2425 NSString *finish = finish_r[1];
2426 int index = [Finishes_ indexOfObject:finish];
2427 if (index != INT_MAX && index > Finish_)
2435 - (void) _readStatus:(NSNumber *)fd { _pooled
2436 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2437 std::istream is(&ib);
2440 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2441 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2443 while (std::getline(is, line)) {
2444 const char *data(line.c_str());
2445 size_t size = line.size();
2446 lprintf("S:%s\n", data);
2448 if (conffile_r(data, size)) {
2449 [delegate_ setConfigurationData:conffile_r[1]];
2450 } else if (strncmp(data, "status: ", 8) == 0) {
2451 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2452 [delegate_ setProgressTitle:string];
2453 } else if (pmstatus_r(data, size)) {
2454 std::string type([pmstatus_r[1] UTF8String]);
2455 NSString *id = pmstatus_r[2];
2457 float percent([pmstatus_r[3] floatValue]);
2458 [delegate_ setProgressPercent:(percent / 100)];
2460 NSString *string = pmstatus_r[4];
2462 if (type == "pmerror")
2463 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2464 withObject:[NSArray arrayWithObjects:string, id, nil]
2467 else if (type == "pmstatus") {
2468 [delegate_ setProgressTitle:string];
2469 } else if (type == "pmconffile")
2470 [delegate_ setConfigurationData:string];
2471 else _assert(false);
2472 } else _assert(false);
2478 - (void) _readOutput:(NSNumber *)fd { _pooled
2479 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2480 std::istream is(&ib);
2483 while (std::getline(is, line)) {
2484 lprintf("O:%s\n", line.c_str());
2485 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2495 - (Package *) packageWithName:(NSString *)name {
2496 if (static_cast<pkgDepCache *>(cache_) == NULL)
2498 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2499 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2502 - (Database *) init {
2503 if ((self = [super init]) != nil) {
2510 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2511 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2515 _assert(pipe(fds) != -1);
2518 _config->Set("APT::Keep-Fds::", cydiafd_);
2519 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2522 detachNewThreadSelector:@selector(_readCydia:)
2524 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2527 _assert(pipe(fds) != -1);
2531 detachNewThreadSelector:@selector(_readStatus:)
2533 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2536 _assert(pipe(fds) != -1);
2537 _assert(dup2(fds[0], 0) != -1);
2538 _assert(close(fds[0]) != -1);
2540 input_ = fdopen(fds[1], "a");
2542 _assert(pipe(fds) != -1);
2543 _assert(dup2(fds[1], 1) != -1);
2544 _assert(close(fds[1]) != -1);
2547 detachNewThreadSelector:@selector(_readOutput:)
2549 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2554 - (pkgCacheFile &) cache {
2558 - (pkgDepCache::Policy *) policy {
2562 - (pkgRecords *) records {
2566 - (pkgProblemResolver *) resolver {
2570 - (pkgAcquire &) fetcher {
2574 - (pkgSourceList &) list {
2578 - (NSArray *) packages {
2582 - (NSArray *) sources {
2583 return [sources_ allValues];
2586 - (NSArray *) issues {
2587 if (cache_->BrokenCount() == 0)
2590 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2592 for (Package *package in packages_) {
2593 if (![package broken])
2595 pkgCache::PkgIterator pkg([package iterator]);
2597 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2598 [entry addObject:[package name]];
2599 [issues addObject:entry];
2601 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2605 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2606 pkgCache::DepIterator start;
2607 pkgCache::DepIterator end;
2608 dep.GlobOr(start, end); // ++dep
2610 if (!cache_->IsImportantDep(end))
2612 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2615 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2616 [entry addObject:failure];
2617 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2619 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2620 [failure addObject:[package name]];
2622 pkgCache::PkgIterator target(start.TargetPkg());
2623 if (target->ProvidesList != 0)
2624 [failure addObject:@"?"];
2626 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2628 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2629 else if (!cache_[target].CandidateVerIter(cache_).end())
2630 [failure addObject:@"-"];
2631 else if (target->ProvidesList == 0)
2632 [failure addObject:@"!"];
2634 [failure addObject:@"%"];
2638 if (start.TargetVer() != 0)
2639 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2650 - (void) reloadData { _pooled
2651 @synchronized (self) {
2674 if (!cache_.Open(progress_, true)) {
2676 if (!_error->PopMessage(error))
2679 lprintf("cache_.Open():[%s]\n", error.c_str());
2681 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2682 [delegate_ repairWithSelector:@selector(configure)];
2683 else if (error == "The package lists or status file could not be parsed or opened.")
2684 [delegate_ repairWithSelector:@selector(update)];
2685 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2686 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2687 // else if (error == "The list of sources could not be read.")
2688 else _assert(false);
2694 now_ = [[NSDate date] retain];
2696 policy_ = new pkgDepCache::Policy();
2697 records_ = new pkgRecords(cache_);
2698 resolver_ = new pkgProblemResolver(cache_);
2699 fetcher_ = new pkgAcquire(&status_);
2702 list_ = new pkgSourceList();
2703 _assert(list_->ReadMainList());
2705 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2706 _assert(pkgApplyStatus(cache_));
2708 if (cache_->BrokenCount() != 0) {
2709 _assert(pkgFixBroken(cache_));
2710 _assert(cache_->BrokenCount() == 0);
2711 _assert(pkgMinimizeUpgrade(cache_));
2714 [sources_ removeAllObjects];
2715 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2716 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2717 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2719 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2720 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2724 [packages_ removeAllObjects];
2726 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2727 if (Package *package = [Package packageWithIterator:iterator database:self])
2728 [packages_ addObject:package];
2730 [packages_ sortUsingSelector:@selector(compareByName:)];
2733 _config->Set("Acquire::http::Timeout", 15);
2734 _config->Set("Acquire::http::MaxParallel", 4);
2737 - (void) configure {
2738 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2739 system([dpkg UTF8String]);
2747 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2748 _assert(!_error->PendingError());
2751 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2754 public pkgArchiveCleaner
2757 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2762 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2764 while (_error->PopMessage(error))
2765 lprintf("ArchiveCleaner: %s\n", error.c_str());
2770 pkgRecords records(cache_);
2772 lock_ = new FileFd();
2773 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2774 _assert(!_error->PendingError());
2777 // XXX: explain this with an error message
2778 _assert(list.ReadMainList());
2780 manager_ = (_system->CreatePM(cache_));
2781 _assert(manager_->GetArchives(fetcher_, &list, &records));
2782 _assert(!_error->PendingError());
2786 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2788 _assert(list.ReadMainList());
2789 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2790 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2793 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2798 bool failed = false;
2799 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2800 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2803 std::string uri = (*item)->DescURI();
2804 std::string error = (*item)->ErrorText;
2806 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2809 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2810 withObject:[NSArray arrayWithObjects:
2811 [NSString stringWithUTF8String:error.c_str()],
2823 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2825 if (_error->PendingError()) {
2830 if (result == pkgPackageManager::Failed) {
2835 if (result != pkgPackageManager::Completed) {
2840 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2842 _assert(list.ReadMainList());
2843 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2844 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2847 if (![before isEqualToArray:after])
2852 _assert(pkgDistUpgrade(cache_));
2856 [self updateWithStatus:status_];
2859 - (void) updateWithStatus:(Status &)status {
2861 _assert(list.ReadMainList());
2864 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2865 _assert(!_error->PendingError());
2867 pkgAcquire fetcher(&status);
2868 _assert(list.GetIndexes(&fetcher));
2870 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2871 bool failed = false;
2872 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2873 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2874 (*item)->Finished();
2878 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2879 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2880 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2883 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2888 - (void) setDelegate:(id)delegate {
2889 delegate_ = delegate;
2890 status_.setDelegate(delegate);
2891 progress_.setDelegate(delegate);
2894 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2895 pkgIndexFile *index(NULL);
2896 list_->FindIndex(file, index);
2897 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2903 /* PopUp Windows {{{ */
2904 @interface PopUpView : UIView {
2905 _transient id delegate_;
2906 UITransitionView *transition_;
2911 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2915 @implementation PopUpView
2918 [transition_ setDelegate:nil];
2919 [transition_ release];
2925 [transition_ transition:UITransitionPushFromTop toView:nil];
2928 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2929 if (from != nil && to == nil)
2930 [self removeFromSuperview];
2933 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2934 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2935 delegate_ = delegate;
2937 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2938 [self addSubview:transition_];
2940 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2942 [view addSubview:self];
2944 [transition_ setDelegate:self];
2946 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2947 [transition_ transition:UITransitionNone toView:blank];
2948 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2955 /* Mail Composition {{{ */
2956 @interface MailToView : PopUpView {
2957 MailComposeController *controller_;
2960 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2964 @implementation MailToView
2967 [controller_ release];
2971 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2975 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2976 NSLog(@"did:%@", delivery);
2977 // [UIApp setStatusBarShowsProgress:NO];
2978 if ([controller error]){
2979 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2980 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2981 [mailAlertSheet setBodyText:[controller error]];
2982 [mailAlertSheet popupAlertAnimated:YES];
2986 - (void) showError {
2987 NSLog(@"%@", [controller_ error]);
2988 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2989 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2990 [mailAlertSheet setBodyText:[controller_ error]];
2991 [mailAlertSheet popupAlertAnimated:YES];
2994 - (void) deliverMessage { _pooled
2998 if (![controller_ deliverMessage])
2999 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
3002 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
3003 if ([controller_ needsDelivery])
3004 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
3009 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
3010 if ((self = [super initWithView:view delegate:delegate]) != nil) {
3011 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
3012 [controller_ setDelegate:self];
3013 [controller_ initializeUI];
3014 [controller_ setupForURL:url];
3016 UIView *view([controller_ view]);
3017 [overlay_ addSubview:view];
3023 /* Confirmation View {{{ */
3024 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
3025 if (!iterator.end())
3026 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
3027 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
3029 pkgCache::PkgIterator package(dep.TargetPkg());
3032 if (strcmp(package.Name(), "mobilesubstrate") == 0)
3039 @protocol ConfirmationViewDelegate
3045 @interface ConfirmationView : BrowserView {
3046 _transient Database *database_;
3047 UIActionSheet *essential_;
3054 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3058 @implementation ConfirmationView
3065 if (essential_ != nil)
3066 [essential_ release];
3072 [book_ popFromSuperviewAnimated:YES];
3075 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3076 NSString *context([sheet context]);
3078 if ([context isEqualToString:@"remove"]) {
3086 [delegate_ confirm];
3093 } else if ([context isEqualToString:@"unable"]) {
3097 [super alertSheet:sheet buttonClicked:button];
3100 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3101 [window setValue:changes_ forKey:@"changes"];
3102 [window setValue:issues_ forKey:@"issues"];
3103 [window setValue:sizes_ forKey:@"sizes"];
3104 [super webView:sender didClearWindowObject:window forFrame:frame];
3107 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3108 if ((self = [super initWithBook:book]) != nil) {
3109 database_ = database;
3111 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
3112 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
3113 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
3114 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
3115 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
3119 pkgDepCache::Policy *policy([database_ policy]);
3121 pkgCacheFile &cache([database_ cache]);
3122 NSArray *packages = [database_ packages];
3123 for (Package *package in packages) {
3124 pkgCache::PkgIterator iterator = [package iterator];
3125 pkgDepCache::StateCache &state(cache[iterator]);
3127 NSString *name([package name]);
3129 if (state.NewInstall())
3130 [installing addObject:name];
3131 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
3132 [reinstalling addObject:name];
3133 else if (state.Upgrade())
3134 [upgrading addObject:name];
3135 else if (state.Downgrade())
3136 [downgrading addObject:name];
3137 else if (state.Delete()) {
3138 if ([package essential])
3140 [removing addObject:name];
3143 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3144 substrate_ |= DepSubstrate(iterator.CurrentVer());
3149 else if (Advanced_ || true) {
3150 essential_ = [[UIActionSheet alloc]
3151 initWithTitle:@"Removing Essentials"
3152 buttons:[NSArray arrayWithObjects:
3153 @"Cancel Operation (Safe)",
3154 @"Force Removal (Unsafe)",
3156 defaultButtonIndex:0
3162 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3164 [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."];
3166 essential_ = [[UIActionSheet alloc]
3167 initWithTitle:@"Unable to Comply"
3168 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3169 defaultButtonIndex:0
3174 [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."];
3177 changes_ = [[NSArray alloc] initWithObjects:
3185 issues_ = [database_ issues];
3187 issues_ = [issues_ retain];
3189 sizes_ = [[NSArray alloc] initWithObjects:
3190 SizeString([database_ fetcher].FetchNeeded()),
3191 SizeString([database_ fetcher].PartialPresent()),
3192 SizeString([database_ cache]->UsrSize()),
3195 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3199 - (NSString *) backButtonTitle {
3203 - (NSString *) leftButtonTitle {
3207 - (id) rightButtonTitle {
3208 return issues_ != nil ? nil : [super rightButtonTitle];
3211 - (id) _rightButtonTitle {
3212 #if AlwaysReload || IgnoreInstall
3213 return [super _rightButtonTitle];
3219 - (void) _leftButtonClicked {
3224 - (void) _rightButtonClicked {
3226 return [super _rightButtonClicked];
3228 if (essential_ != nil)
3229 [essential_ popupAlertAnimated:YES];
3233 [delegate_ confirm];
3241 /* Progress Data {{{ */
3242 @interface ProgressData : NSObject {
3248 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3255 @implementation ProgressData
3257 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3258 if ((self = [super init]) != nil) {
3259 selector_ = selector;
3279 /* Progress View {{{ */
3280 @interface ProgressView : UIView <
3281 ConfigurationDelegate,
3284 _transient Database *database_;
3286 UIView *background_;
3287 UITransitionView *transition_;
3289 UINavigationBar *navbar_;
3290 UIProgressBar *progress_;
3291 UITextView *output_;
3292 UITextLabel *status_;
3293 UIPushButton *close_;
3296 SHA1SumValue springlist_;
3297 SHA1SumValue notifyconf_;
3298 SHA1SumValue sandplate_;
3301 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3303 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3304 - (void) setContentView:(UIView *)view;
3307 - (void) _retachThread;
3308 - (void) _detachNewThreadData:(ProgressData *)data;
3309 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3315 @protocol ProgressViewDelegate
3316 - (void) progressViewIsComplete:(ProgressView *)sender;
3319 @implementation ProgressView
3322 [transition_ setDelegate:nil];
3323 [navbar_ setDelegate:nil];
3326 if (background_ != nil)
3327 [background_ release];
3328 [transition_ release];
3331 [progress_ release];
3338 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3339 if (bootstrap_ && from == overlay_ && to == view_)
3343 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3344 if ((self = [super initWithFrame:frame]) != nil) {
3345 database_ = database;
3346 delegate_ = delegate;
3348 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3349 [transition_ setDelegate:self];
3351 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3354 [overlay_ setBackgroundColor:[UIColor blackColor]];
3356 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3357 [background_ setBackgroundColor:[UIColor blackColor]];
3358 [self addSubview:background_];
3361 [self addSubview:transition_];
3363 CGSize navsize = [UINavigationBar defaultSize];
3364 CGRect navrect = {{0, 0}, navsize};
3366 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3367 [overlay_ addSubview:navbar_];
3369 [navbar_ setBarStyle:1];
3370 [navbar_ setDelegate:self];
3372 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3373 [navbar_ pushNavigationItem:navitem];
3375 CGRect bounds = [overlay_ bounds];
3376 CGSize prgsize = [UIProgressBar defaultSize];
3379 (bounds.size.width - prgsize.width) / 2,
3380 bounds.size.height - prgsize.height - 20
3383 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3384 [progress_ setStyle:0];
3386 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3388 bounds.size.height - prgsize.height - 50,
3389 bounds.size.width - 20,
3393 [status_ setColor:[UIColor whiteColor]];
3394 [status_ setBackgroundColor:[UIColor clearColor]];
3396 [status_ setCentersHorizontally:YES];
3397 //[status_ setFont:font];
3400 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3402 navrect.size.height + 20,
3403 bounds.size.width - 20,
3404 bounds.size.height - navsize.height - 62 - navrect.size.height
3408 //[output_ setTextFont:@"Courier New"];
3409 [output_ setTextSize:12];
3411 [output_ setTextColor:[UIColor whiteColor]];
3412 [output_ setBackgroundColor:[UIColor clearColor]];
3414 [output_ setMarginTop:0];
3415 [output_ setAllowsRubberBanding:YES];
3416 [output_ setEditable:NO];
3418 [overlay_ addSubview:output_];
3420 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3422 bounds.size.height - prgsize.height - 50,
3423 bounds.size.width - 20,
3427 [close_ setAutosizesToFit:NO];
3428 [close_ setDrawsShadow:YES];
3429 [close_ setStretchBackground:YES];
3430 [close_ setEnabled:YES];
3432 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3433 [close_ setTitleFont:bold];
3435 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3436 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3437 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3441 - (void) setContentView:(UIView *)view {
3442 view_ = [view retain];
3445 - (void) resetView {
3446 [transition_ transition:6 toView:view_];
3449 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3450 NSString *context([sheet context]);
3452 if ([context isEqualToString:@"error"])
3454 else if ([context isEqualToString:@"conffile"]) {
3455 FILE *input = [database_ input];
3459 fprintf(input, "N\n");
3463 fprintf(input, "Y\n");
3474 - (void) closeButtonPushed {
3483 [delegate_ suspendWithAnimation:YES];
3487 system("launchctl stop com.apple.SpringBoard");
3491 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3500 - (void) _retachThread {
3501 UINavigationItem *item = [navbar_ topItem];
3502 [item setTitle:@"Complete"];
3504 [overlay_ addSubview:close_];
3505 [progress_ removeFromSuperview];
3506 [status_ removeFromSuperview];
3508 [delegate_ progressViewIsComplete:self];
3511 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3512 MMap mmap(file, MMap::ReadOnly);
3514 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3515 if (!(sandplate_ == sha1.Result()))
3520 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3521 MMap mmap(file, MMap::ReadOnly);
3523 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3524 if (!(notifyconf_ == sha1.Result()))
3529 FileFd file(SpringBoard_, FileFd::ReadOnly);
3530 MMap mmap(file, MMap::ReadOnly);
3532 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3533 if (!(springlist_ == sha1.Result()))
3538 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3539 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3540 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3541 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3542 case 4: [close_ setTitle:@"Reboot Device"]; break;
3545 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3547 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3548 [cache autorelease];
3550 NSFileManager *manager = [NSFileManager defaultManager];
3551 NSError *error = nil;
3553 id system = [cache objectForKey:@"System"];
3558 if (stat(Cache_, &info) == -1)
3561 [system removeAllObjects];
3563 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3564 for (NSString *app in apps)
3565 if ([app hasSuffix:@".app"]) {
3566 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3567 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3568 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3570 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3571 [info setObject:path forKey:@"Path"];
3572 [info setObject:@"System" forKey:@"ApplicationType"];
3573 [system addInfoDictionary:info];
3579 [cache writeToFile:@Cache_ atomically:YES];
3581 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3583 if (chmod(Cache_, info.st_mode) == -1)
3587 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3590 notify_post("com.apple.mobile.application_installed");
3592 [delegate_ setStatusBarShowsProgress:NO];
3595 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3596 [[data target] performSelector:[data selector] withObject:[data object]];
3599 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3602 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3603 UINavigationItem *item = [navbar_ topItem];
3604 [item setTitle:title];
3606 [status_ setText:nil];
3607 [output_ setText:@""];
3608 [progress_ setProgress:0];
3610 [close_ removeFromSuperview];
3611 [overlay_ addSubview:progress_];
3612 [overlay_ addSubview:status_];
3614 [delegate_ setStatusBarShowsProgress:YES];
3618 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3619 MMap mmap(file, MMap::ReadOnly);
3621 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3622 sandplate_ = sha1.Result();
3626 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3627 MMap mmap(file, MMap::ReadOnly);
3629 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3630 notifyconf_ = sha1.Result();
3634 FileFd file(SpringBoard_, FileFd::ReadOnly);
3635 MMap mmap(file, MMap::ReadOnly);
3637 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3638 springlist_ = sha1.Result();
3641 [transition_ transition:6 toView:overlay_];
3644 detachNewThreadSelector:@selector(_detachNewThreadData:)
3646 withObject:[[ProgressData alloc]
3647 initWithSelector:selector
3654 - (void) repairWithSelector:(SEL)selector {
3656 detachNewThreadSelector:selector
3663 - (void) setConfigurationData:(NSString *)data {
3665 performSelectorOnMainThread:@selector(_setConfigurationData:)
3671 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3672 Package *package = id == nil ? nil : [database_ packageWithName:id];
3674 UIActionSheet *sheet = [[[UIActionSheet alloc]
3675 initWithTitle:(package == nil ? id : [package name])
3676 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3677 defaultButtonIndex:0
3682 [sheet setBodyText:error];
3683 [sheet popupAlertAnimated:YES];
3686 - (void) setProgressTitle:(NSString *)title {
3688 performSelectorOnMainThread:@selector(_setProgressTitle:)
3694 - (void) setProgressPercent:(float)percent {
3696 performSelectorOnMainThread:@selector(_setProgressPercent:)
3697 withObject:[NSNumber numberWithFloat:percent]
3702 - (void) startProgress {
3705 - (void) addProgressOutput:(NSString *)output {
3707 performSelectorOnMainThread:@selector(_addProgressOutput:)
3713 - (bool) isCancelling:(size_t)received {
3717 - (void) _setConfigurationData:(NSString *)data {
3718 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3720 _assert(conffile_r(data));
3722 NSString *ofile = conffile_r[1];
3723 //NSString *nfile = conffile_r[2];
3725 UIActionSheet *sheet = [[[UIActionSheet alloc]
3726 initWithTitle:@"Configuration Upgrade"
3727 buttons:[NSArray arrayWithObjects:
3728 @"Keep My Old Copy",
3729 @"Accept The New Copy",
3730 // XXX: @"See What Changed",
3732 defaultButtonIndex:0
3737 [sheet setBodyText:[NSString stringWithFormat:
3738 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3741 [sheet popupAlertAnimated:YES];
3744 - (void) _setProgressTitle:(NSString *)title {
3745 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3746 for (size_t i(0), e([words count]); i != e; ++i) {
3747 NSString *word([words objectAtIndex:i]);
3748 if (Package *package = [database_ packageWithName:word])
3749 [words replaceObjectAtIndex:i withObject:[package name]];
3752 [status_ setText:[words componentsJoinedByString:@" "]];
3755 - (void) _setProgressPercent:(NSNumber *)percent {
3756 [progress_ setProgress:[percent floatValue]];
3759 - (void) _addProgressOutput:(NSString *)output {
3760 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3761 CGSize size = [output_ contentSize];
3762 CGRect rect = {{0, size.height}, {size.width, 0}};
3763 [output_ scrollRectToVisible:rect animated:YES];
3766 - (BOOL) isRunning {
3773 /* Package Cell {{{ */
3774 @interface PackageCell : UITableCell {
3777 NSString *description_;
3784 UITextLabel *status_;
3788 - (PackageCell *) init;
3789 - (void) setPackage:(Package *)package;
3791 + (int) heightForPackage:(Package *)package;
3795 @implementation PackageCell
3797 - (void) clearPackage {
3808 if (description_ != nil) {
3809 [description_ release];
3813 if (source_ != nil) {
3818 if (badge_ != nil) {
3828 [self clearPackage];
3835 - (PackageCell *) init {
3836 if ((self = [super init]) != nil) {
3838 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3839 [status_ setBackgroundColor:[UIColor clearColor]];
3840 [status_ setFont:small];
3845 - (void) setPackage:(Package *)package {
3846 [self clearPackage];
3848 Source *source = [package source];
3849 NSString *section = [package simpleSection];
3851 icon_ = [[package icon] retain];
3853 name_ = [[package name] retain];
3854 description_ = [[package tagline] retain];
3855 commercial_ = [package isCommercial];
3857 package_ = [package retain];
3859 NSString *label = nil;
3860 bool trusted = false;
3862 if (source != nil) {
3863 label = [source label];
3864 trusted = [source trusted];
3865 } else if ([[package id] isEqualToString:@"firmware"])
3868 label = @"Unknown/Local";
3870 NSString *from = [NSString stringWithFormat:@"from %@", label];
3872 if (section != nil && ![section isEqualToString:label])
3873 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3875 source_ = [from retain];
3877 if (NSString *purpose = [package primaryPurpose])
3878 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3879 badge_ = [badge_ retain];
3882 if (NSString *mode = [package mode]) {
3883 [badge_ setImage:[UIImage applicationImageNamed:
3884 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3887 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3888 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3889 } else if ([package half]) {
3890 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3891 [status_ setText:@"Package Damaged"];
3892 [status_ setColor:[UIColor redColor]];
3894 [badge_ setImage:nil];
3895 [status_ setText:nil];
3902 - (void) drawRect:(CGRect)rect {
3906 if (NSString *mode = [package_ mode]) {
3907 bool remove([mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"]);
3908 color = remove ? RemovingColor_ : InstallingColor_;
3910 color = [UIColor whiteColor];
3912 [self setBackgroundColor:color];
3916 [super drawRect:rect];
3919 - (void) drawBackgroundInRect:(CGRect)rect withFade:(float)fade {
3921 CGContextRef context(UIGraphicsGetCurrentContext());
3922 [[self backgroundColor] set];
3924 back.size.height -= 1;
3925 CGContextFillRect(context, back);
3928 [super drawBackgroundInRect:rect withFade:fade];
3931 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3934 rect.size = [icon_ size];
3936 rect.size.width /= 2;
3937 rect.size.height /= 2;
3939 rect.origin.x = 25 - rect.size.width / 2;
3940 rect.origin.y = 25 - rect.size.height / 2;
3942 [icon_ drawInRect:rect];
3945 if (badge_ != nil) {
3946 CGSize size = [badge_ size];
3948 [badge_ drawAtPoint:CGPointMake(
3949 36 - size.width / 2,
3950 36 - size.height / 2
3958 UISetColor(commercial_ ? Purple_ : Black_);
3959 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3960 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3963 UISetColor(commercial_ ? Purplish_ : Gray_);
3964 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3966 [super drawContentInRect:rect selected:selected];
3969 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
3971 [super setSelected:selected withFade:fade];
3974 + (int) heightForPackage:(Package *)package {
3975 NSString *tagline([package tagline]);
3976 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3978 if ([package hasMode] || [package half])
3987 /* Section Cell {{{ */
3988 @interface SectionCell : UISimpleTableCell {
3993 _UISwitchSlider *switch_;
3998 - (void) setSection:(Section *)section editing:(BOOL)editing;
4002 @implementation SectionCell
4004 - (void) clearSection {
4005 if (section_ != nil) {
4015 if (count_ != nil) {
4022 [self clearSection];
4029 if ((self = [super init]) != nil) {
4030 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
4032 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
4033 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
4037 - (void) onSwitch:(id)sender {
4038 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
4039 if (metadata == nil) {
4040 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
4041 [Sections_ setObject:metadata forKey:section_];
4045 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
4048 - (void) setSection:(Section *)section editing:(BOOL)editing {
4049 if (editing != editing_) {
4051 [switch_ removeFromSuperview];
4053 [self addSubview:switch_];
4057 [self clearSection];
4059 if (section == nil) {
4060 name_ = [@"All Packages" retain];
4063 section_ = [section name];
4064 if (section_ != nil)
4065 section_ = [section_ retain];
4066 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
4067 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
4070 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
4074 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4075 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
4082 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
4084 CGSize size = [count_ sizeWithFont:Font14_];
4088 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
4090 [super drawContentInRect:rect selected:selected];
4096 /* File Table {{{ */
4097 @interface FileTable : RVPage {
4098 _transient Database *database_;
4101 NSMutableArray *files_;
4105 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4106 - (void) setPackage:(Package *)package;
4110 @implementation FileTable
4113 if (package_ != nil)
4122 - (int) numberOfRowsInTable:(UITable *)table {
4123 return files_ == nil ? 0 : [files_ count];
4126 - (float) table:(UITable *)table heightForRow:(int)row {
4130 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4131 if (reusing == nil) {
4132 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
4133 UIFont *font = [UIFont systemFontOfSize:16];
4134 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
4136 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
4140 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4144 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4145 if ((self = [super initWithBook:book]) != nil) {
4146 database_ = database;
4148 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
4150 list_ = [[UITable alloc] initWithFrame:[self bounds]];
4151 [self addSubview:list_];
4153 UITableColumn *column = [[[UITableColumn alloc]
4154 initWithTitle:@"Name"
4156 width:[self frame].size.width
4159 [list_ setDataSource:self];
4160 [list_ setSeparatorStyle:1];
4161 [list_ addTableColumn:column];
4162 [list_ setDelegate:self];
4163 [list_ setReusesTableCells:YES];
4167 - (void) setPackage:(Package *)package {
4168 if (package_ != nil) {
4169 [package_ autorelease];
4178 [files_ removeAllObjects];
4180 if (package != nil) {
4181 package_ = [package retain];
4182 name_ = [[package id] retain];
4184 if (NSArray *files = [package files])
4185 [files_ addObjectsFromArray:files];
4187 if ([files_ count] != 0) {
4188 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4189 [files_ removeObjectAtIndex:0];
4190 [files_ sortUsingSelector:@selector(compareByPath:)];
4192 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4193 [stack addObject:@"/"];
4195 for (int i(0), e([files_ count]); i != e; ++i) {
4196 NSString *file = [files_ objectAtIndex:i];
4197 while (![file hasPrefix:[stack lastObject]])
4198 [stack removeLastObject];
4199 NSString *directory = [stack lastObject];
4200 [stack addObject:[file stringByAppendingString:@"/"]];
4201 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4202 ([stack count] - 2) * 3, "",
4203 [file substringFromIndex:[directory length]]
4212 - (void) resetViewAnimated:(BOOL)animated {
4213 [list_ resetViewAnimated:animated];
4216 - (void) reloadData {
4217 [self setPackage:[database_ packageWithName:name_]];
4218 [self reloadButtons];
4221 - (NSString *) title {
4222 return @"Installed Files";
4225 - (NSString *) backButtonTitle {
4231 /* Package View {{{ */
4232 @interface PackageView : BrowserView {
4233 _transient Database *database_;
4237 NSMutableArray *buttons_;
4240 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4241 - (void) setPackage:(Package *)package;
4245 @implementation PackageView
4248 if (package_ != nil)
4256 - (void) _clickButtonWithName:(NSString *)name {
4257 if ([name isEqualToString:@"Clear"])
4258 [delegate_ clearPackage:package_];
4259 else if ([name isEqualToString:@"Install"])
4260 [delegate_ installPackage:package_];
4261 else if ([name isEqualToString:@"Reinstall"])
4262 [delegate_ installPackage:package_];
4263 else if ([name isEqualToString:@"Remove"])
4264 [delegate_ removePackage:package_];
4265 else if ([name isEqualToString:@"Upgrade"])
4266 [delegate_ installPackage:package_];
4267 else _assert(false);
4270 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4271 NSString *context([sheet context]);
4273 if ([context isEqualToString:@"modify"]) {
4274 int count = [buttons_ count];
4275 _assert(count != 0);
4276 _assert(button <= count + 1);
4278 if (count != button - 1)
4279 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4283 [super alertSheet:sheet buttonClicked:button];
4286 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4287 return [super webView:sender didFinishLoadForFrame:frame];
4290 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4291 [window setValue:package_ forKey:@"package"];
4292 [super webView:sender didClearWindowObject:window forFrame:frame];
4295 - (bool) _allowJavaScriptPanel {
4300 - (void) __rightButtonClicked {
4301 int count = [buttons_ count];
4302 _assert(count != 0);
4305 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4307 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4308 [buttons addObjectsFromArray:buttons_];
4309 [buttons addObject:@"Cancel"];
4311 [delegate_ slideUp:[[[UIActionSheet alloc]
4314 defaultButtonIndex:([buttons count] - 1)
4321 - (void) _rightButtonClicked {
4323 [super _rightButtonClicked];
4325 [self __rightButtonClicked];
4329 - (id) _rightButtonTitle {
4330 int count = [buttons_ count];
4331 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4334 - (NSString *) backButtonTitle {
4338 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4339 if ((self = [super initWithBook:book]) != nil) {
4340 database_ = database;
4341 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4345 - (void) setPackage:(Package *)package {
4346 if (package_ != nil) {
4347 [package_ autorelease];
4356 [buttons_ removeAllObjects];
4358 if (package != nil) {
4359 package_ = [package retain];
4360 name_ = [[package id] retain];
4361 commercial_ = [package isCommercial];
4363 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4365 if ([package_ mode] != nil)
4366 [buttons_ addObject:@"Clear"];
4367 if ([package_ source] == nil);
4368 else if ([package_ upgradableAndEssential:NO])
4369 [buttons_ addObject:@"Upgrade"];
4370 else if ([package_ installed] == nil)
4371 [buttons_ addObject:@"Install"];
4373 [buttons_ addObject:@"Reinstall"];
4374 if ([package_ installed] != nil)
4375 [buttons_ addObject:@"Remove"];
4379 - (bool) isLoading {
4380 return commercial_ ? [super isLoading] : false;
4383 - (void) reloadData {
4384 [self setPackage:[database_ packageWithName:name_]];
4385 [self reloadButtons];
4390 /* Package Table {{{ */
4391 @interface PackageTable : RVPage {
4392 _transient Database *database_;
4394 NSMutableArray *packages_;
4395 NSMutableArray *sections_;
4396 UISectionList *list_;
4399 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4401 - (void) setDelegate:(id)delegate;
4403 - (void) reloadData;
4404 - (void) resetCursor;
4406 - (UISectionList *) list;
4408 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4412 @implementation PackageTable
4415 [list_ setDataSource:nil];
4418 [packages_ release];
4419 [sections_ release];
4424 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4425 return [sections_ count];
4428 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4429 return [[sections_ objectAtIndex:section] name];
4432 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4433 return [[sections_ objectAtIndex:section] row];
4436 - (int) numberOfRowsInTable:(UITable *)table {
4437 return [packages_ count];
4440 - (float) table:(UITable *)table heightForRow:(int)row {
4441 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4444 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4446 reusing = [[[PackageCell alloc] init] autorelease];
4447 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4451 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4455 - (void) tableRowSelected:(NSNotification *)notification {
4456 int row = [[notification object] selectedRow];
4460 Package *package = [packages_ objectAtIndex:row];
4461 package = [database_ packageWithName:[package id]];
4462 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4463 [view setPackage:package];
4464 [view setDelegate:delegate_];
4465 [book_ pushPage:view];
4468 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4469 if ((self = [super initWithBook:book]) != nil) {
4470 database_ = database;
4471 title_ = [title retain];
4473 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4474 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4476 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4477 [list_ setDataSource:self];
4479 UITableColumn *column = [[[UITableColumn alloc]
4480 initWithTitle:@"Name"
4482 width:[self frame].size.width
4485 UITable *table = [list_ table];
4486 [table setSeparatorStyle:1];
4487 [table addTableColumn:column];
4488 [table setDelegate:self];
4489 [table setReusesTableCells:YES];
4491 [self addSubview:list_];
4493 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4494 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4498 - (void) setDelegate:(id)delegate {
4499 delegate_ = delegate;
4502 - (bool) hasPackage:(Package *)package {
4506 - (void) reloadData {
4507 NSArray *packages = [database_ packages];
4509 [packages_ removeAllObjects];
4510 [sections_ removeAllObjects];
4512 _profile(PackageTable$reloadData$Filter)
4513 for (Package *package in packages)
4514 if ([self hasPackage:package])
4515 [packages_ addObject:package];
4518 Section *section = nil;
4520 _profile(PackageTable$reloadData$Section)
4521 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4525 _profile(PackageTable$reloadData$Section$Package)
4526 package = [packages_ objectAtIndex:offset];
4527 index = [package index];
4530 if (section == nil || [section index] != index) {
4531 _profile(PackageTable$reloadData$Section$Allocate)
4532 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4535 _profile(PackageTable$reloadData$Section$Add)
4536 [sections_ addObject:section];
4540 [section addToCount];
4544 _profile(PackageTable$reloadData$List)
4549 - (NSString *) title {
4553 - (void) resetViewAnimated:(BOOL)animated {
4554 [list_ resetViewAnimated:animated];
4557 - (void) resetCursor {
4558 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4561 - (UISectionList *) list {
4565 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4566 [list_ setShouldHideHeaderInShortLists:hide];
4571 /* Filtered Package Table {{{ */
4572 @interface FilteredPackageTable : PackageTable {
4578 - (void) setObject:(id)object;
4580 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4584 @implementation FilteredPackageTable
4592 - (void) setObject:(id)object {
4598 object_ = [object retain];
4601 - (bool) hasPackage:(Package *)package {
4602 _profile(FilteredPackageTable$hasPackage)
4603 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
4607 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4608 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4610 object_ = object == nil ? nil : [object retain];
4612 /* XXX: this is an unsafe optimization of doomy hell */
4613 Method method = class_getInstanceMethod([Package class], filter);
4614 imp_ = method_getImplementation(method);
4615 _assert(imp_ != NULL);
4624 /* Add Source View {{{ */
4625 @interface AddSourceView : RVPage {
4626 _transient Database *database_;
4629 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4633 @implementation AddSourceView
4635 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4636 if ((self = [super initWithBook:book]) != nil) {
4637 database_ = database;
4643 /* Source Cell {{{ */
4644 @interface SourceCell : UITableCell {
4647 NSString *description_;
4653 - (SourceCell *) initWithSource:(Source *)source;
4657 @implementation SourceCell
4662 [description_ release];
4667 - (SourceCell *) initWithSource:(Source *)source {
4668 if ((self = [super init]) != nil) {
4670 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4672 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4673 icon_ = [icon_ retain];
4675 origin_ = [[source name] retain];
4676 label_ = [[source uri] retain];
4677 description_ = [[source description] retain];
4681 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4683 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4690 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4694 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4698 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4700 [super drawContentInRect:rect selected:selected];
4705 /* Source Table {{{ */
4706 @interface SourceTable : RVPage {
4707 _transient Database *database_;
4708 UISectionList *list_;
4709 NSMutableArray *sources_;
4710 UIActionSheet *alert_;
4714 UIProgressHUD *hud_;
4717 //NSURLConnection *installer_;
4718 NSURLConnection *trivial_bz2_;
4719 NSURLConnection *trivial_gz_;
4720 //NSURLConnection *automatic_;
4725 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4729 @implementation SourceTable
4731 - (void) _deallocConnection:(NSURLConnection *)connection {
4732 if (connection != nil) {
4733 [connection cancel];
4734 //[connection setDelegate:nil];
4735 [connection release];
4740 [[list_ table] setDelegate:nil];
4741 [list_ setDataSource:nil];
4750 //[self _deallocConnection:installer_];
4751 [self _deallocConnection:trivial_gz_];
4752 [self _deallocConnection:trivial_bz2_];
4753 //[self _deallocConnection:automatic_];
4760 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4761 return offset_ == 0 ? 1 : 2;
4764 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4765 switch (section + (offset_ == 0 ? 1 : 0)) {
4766 case 0: return @"Entered by User";
4767 case 1: return @"Installed by Packages";
4775 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4776 switch (section + (offset_ == 0 ? 1 : 0)) {
4778 case 1: return offset_;
4786 - (int) numberOfRowsInTable:(UITable *)table {
4787 return [sources_ count];
4790 - (float) table:(UITable *)table heightForRow:(int)row {
4791 Source *source = [sources_ objectAtIndex:row];
4792 return [source description] == nil ? 56 : 73;
4795 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4796 Source *source = [sources_ objectAtIndex:row];
4797 // XXX: weird warning, stupid selectors ;P
4798 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4801 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4805 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4809 - (void) tableRowSelected:(NSNotification*)notification {
4810 UITable *table([list_ table]);
4811 int row([table selectedRow]);
4815 Source *source = [sources_ objectAtIndex:row];
4817 PackageTable *packages = [[[FilteredPackageTable alloc]
4820 title:[source label]
4821 filter:@selector(isVisibleInSource:)
4825 [packages setDelegate:delegate_];
4827 [book_ pushPage:packages];
4830 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4831 Source *source = [sources_ objectAtIndex:row];
4832 return [source record] != nil;
4835 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4836 [[list_ table] setDeleteConfirmationRow:row];
4839 - (void) table:(UITable *)table deleteRow:(int)row {
4840 Source *source = [sources_ objectAtIndex:row];
4841 [Sources_ removeObjectForKey:[source key]];
4842 [delegate_ syncData];
4846 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4849 @"./", @"Distribution",
4850 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4852 [delegate_ syncData];
4855 - (NSString *) getWarning {
4856 NSString *href(href_);
4857 NSRange colon([href rangeOfString:@"://"]);
4858 if (colon.location != NSNotFound)
4859 href = [href substringFromIndex:(colon.location + 3)];
4860 href = [href stringByAddingPercentEscapes];
4861 href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href];
4862 href = [href stringByCachingURLWithCurrentCDN];
4864 NSURL *url([NSURL URLWithString:href]);
4866 NSStringEncoding encoding;
4867 NSError *error(nil);
4869 if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error])
4870 return [warning length] == 0 ? nil : warning;
4874 - (void) _endConnection:(NSURLConnection *)connection {
4875 NSURLConnection **field = NULL;
4876 if (connection == trivial_bz2_)
4877 field = &trivial_bz2_;
4878 else if (connection == trivial_gz_)
4879 field = &trivial_gz_;
4880 _assert(field != NULL);
4881 [connection release];
4885 trivial_bz2_ == nil &&
4891 if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
4894 UIActionSheet *sheet = [[[UIActionSheet alloc]
4895 initWithTitle:@"Source Warning"
4896 buttons:[NSArray arrayWithObjects:@"Add Anyway", @"Cancel", nil]
4897 defaultButtonIndex:0
4902 [sheet setNumberOfRows:1];
4904 [sheet setBodyText:warning];
4905 [sheet popupAlertAnimated:YES];
4908 } else if (error_ != nil) {
4909 UIActionSheet *sheet = [[[UIActionSheet alloc]
4910 initWithTitle:@"Verification Error"
4911 buttons:[NSArray arrayWithObjects:@"OK", nil]
4912 defaultButtonIndex:0
4917 [sheet setBodyText:[error_ localizedDescription]];
4918 [sheet popupAlertAnimated:YES];
4920 UIActionSheet *sheet = [[[UIActionSheet alloc]
4921 initWithTitle:@"Did not Find Repository"
4922 buttons:[NSArray arrayWithObjects:@"OK", nil]
4923 defaultButtonIndex:0
4928 [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."];
4929 [sheet popupAlertAnimated:YES];
4932 [delegate_ setStatusBarShowsProgress:NO];
4933 [delegate_ removeProgressHUD:hud_];
4943 if (error_ != nil) {
4950 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4951 switch ([response statusCode]) {
4957 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4958 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4960 error_ = [error retain];
4961 [self _endConnection:connection];
4964 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4965 [self _endConnection:connection];
4968 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4969 NSMutableURLRequest *request = [NSMutableURLRequest
4970 requestWithURL:[NSURL URLWithString:href]
4971 cachePolicy:NSURLRequestUseProtocolCachePolicy
4972 timeoutInterval:20.0
4975 [request setHTTPMethod:method];
4977 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4980 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4981 NSString *context([sheet context]);
4983 if ([context isEqualToString:@"source"]) {
4986 NSString *href = [[sheet textField] text];
4988 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4990 if (![href hasSuffix:@"/"])
4991 href_ = [href stringByAppendingString:@"/"];
4994 href_ = [href_ retain];
4996 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4997 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4998 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
5002 hud_ = [[delegate_ addProgressHUD] retain];
5003 [hud_ setText:@"Verifying URL"];
5014 } else if ([context isEqualToString:@"trivial"])
5016 else if ([context isEqualToString:@"urlerror"])
5018 else if ([context isEqualToString:@"warning"]) {
5038 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5039 if ((self = [super initWithBook:book]) != nil) {
5040 database_ = database;
5041 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
5043 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
5044 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5045 [list_ setShouldHideHeaderInShortLists:NO];
5047 [self addSubview:list_];
5048 [list_ setDataSource:self];
5050 UITableColumn *column = [[UITableColumn alloc]
5051 initWithTitle:@"Name"
5053 width:[self frame].size.width
5056 UITable *table = [list_ table];
5057 [table setSeparatorStyle:1];
5058 [table addTableColumn:column];
5059 [table setDelegate:self];
5063 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5064 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5068 - (void) reloadData {
5070 _assert(list.ReadMainList());
5072 [sources_ removeAllObjects];
5073 [sources_ addObjectsFromArray:[database_ sources]];
5075 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
5078 int count = [sources_ count];
5079 for (offset_ = 0; offset_ != count; ++offset_) {
5080 Source *source = [sources_ objectAtIndex:offset_];
5081 if ([source record] == nil)
5088 - (void) resetViewAnimated:(BOOL)animated {
5089 [list_ resetViewAnimated:animated];
5092 - (void) _leftButtonClicked {
5093 /*[book_ pushPage:[[[AddSourceView alloc]
5098 UIActionSheet *sheet = [[[UIActionSheet alloc]
5099 initWithTitle:@"Enter Cydia/APT URL"
5100 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
5101 defaultButtonIndex:0
5106 [sheet setNumberOfRows:1];
5108 [sheet addTextFieldWithValue:@"http://" label:@""];
5110 UITextInputTraits *traits = [[sheet textField] textInputTraits];
5111 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5112 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5113 [traits setKeyboardType:UIKeyboardTypeURL];
5114 // XXX: UIReturnKeyDone
5115 [traits setReturnKeyType:UIReturnKeyNext];
5117 [sheet popupAlertAnimated:YES];
5120 - (void) _rightButtonClicked {
5121 UITable *table = [list_ table];
5122 BOOL editing = [table isRowDeletionEnabled];
5123 [table enableRowDeletion:!editing animated:YES];
5124 [book_ reloadButtonsForPage:self];
5127 - (NSString *) title {
5131 - (NSString *) leftButtonTitle {
5132 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
5135 - (id) rightButtonTitle {
5136 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
5139 - (UINavigationButtonStyle) rightButtonStyle {
5140 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5146 /* Installed View {{{ */
5147 @interface InstalledView : RVPage {
5148 _transient Database *database_;
5149 FilteredPackageTable *packages_;
5153 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5157 @implementation InstalledView
5160 [packages_ release];
5164 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5165 if ((self = [super initWithBook:book]) != nil) {
5166 database_ = database;
5168 packages_ = [[FilteredPackageTable alloc]
5172 filter:@selector(isInstalledAndVisible:)
5173 with:[NSNumber numberWithBool:YES]
5176 [self addSubview:packages_];
5178 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5179 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5183 - (void) resetViewAnimated:(BOOL)animated {
5184 [packages_ resetViewAnimated:animated];
5187 - (void) reloadData {
5188 [packages_ reloadData];
5191 - (void) _rightButtonClicked {
5192 [packages_ setObject:[NSNumber numberWithBool:expert_]];
5193 [packages_ reloadData];
5195 [book_ reloadButtonsForPage:self];
5198 - (NSString *) title {
5199 return @"Installed";
5202 - (NSString *) backButtonTitle {
5206 - (id) rightButtonTitle {
5207 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
5210 - (UINavigationButtonStyle) rightButtonStyle {
5211 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5214 - (void) setDelegate:(id)delegate {
5215 [super setDelegate:delegate];
5216 [packages_ setDelegate:delegate];
5223 @interface HomeView : BrowserView {
5228 @implementation HomeView
5230 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5231 NSString *context([sheet context]);
5233 if ([context isEqualToString:@"about"])
5236 [super alertSheet:sheet buttonClicked:button];
5239 - (void) _leftButtonClicked {
5240 UIActionSheet *sheet = [[[UIActionSheet alloc]
5241 initWithTitle:@"About Cydia Installer"
5242 buttons:[NSArray arrayWithObjects:@"Close", nil]
5243 defaultButtonIndex:0
5249 @"Copyright (C) 2008-2009\n"
5250 "Jay Freeman (saurik)\n"
5251 "saurik@saurik.com\n"
5252 "http://www.saurik.com/\n"
5255 "http://www.theokorigroup.com/\n"
5257 "College of Creative Studies,\n"
5258 "University of California,\n"
5260 "http://www.ccs.ucsb.edu/"
5263 [sheet popupAlertAnimated:YES];
5266 - (NSString *) leftButtonTitle {
5272 /* Manage View {{{ */
5273 @interface ManageView : BrowserView {
5278 @implementation ManageView
5280 - (NSString *) title {
5284 - (void) _leftButtonClicked {
5285 [delegate_ askForSettings];
5288 - (NSString *) leftButtonTitle {
5293 - (id) _rightButtonTitle {
5294 return Queuing_ ? @"Queue" : nil;
5297 - (UINavigationButtonStyle) rightButtonStyle {
5298 return Queuing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5301 - (void) _rightButtonClicked {
5306 - (bool) isLoading {
5313 #include <BrowserView.m>
5315 /* Cydia Book {{{ */
5316 @interface CYBook : RVBook <
5319 _transient Database *database_;
5320 UINavigationBar *overlay_;
5321 UINavigationBar *underlay_;
5322 UIProgressIndicator *indicator_;
5323 UITextLabel *prompt_;
5324 UIProgressBar *progress_;
5325 UINavigationButton *cancel_;
5329 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5335 @implementation CYBook
5339 [indicator_ release];
5341 [progress_ release];
5346 - (NSString *) getTitleForPage:(RVPage *)page {
5347 return Simplify([super getTitleForPage:page]);
5355 [UIView beginAnimations:nil context:NULL];
5357 CGRect ovrframe = [overlay_ frame];
5358 ovrframe.origin.y = 0;
5359 [overlay_ setFrame:ovrframe];
5361 CGRect barframe = [navbar_ frame];
5362 barframe.origin.y += ovrframe.size.height;
5363 [navbar_ setFrame:barframe];
5365 CGRect trnframe = [transition_ frame];
5366 trnframe.origin.y += ovrframe.size.height;
5367 trnframe.size.height -= ovrframe.size.height;
5368 [transition_ setFrame:trnframe];
5370 [UIView endAnimations];
5372 [indicator_ startAnimation];
5373 [prompt_ setText:@"Updating Database"];
5374 [progress_ setProgress:0];
5377 [overlay_ addSubview:cancel_];
5380 detachNewThreadSelector:@selector(_update)
5389 [indicator_ stopAnimation];
5391 [UIView beginAnimations:nil context:NULL];
5393 CGRect ovrframe = [overlay_ frame];
5394 ovrframe.origin.y = -ovrframe.size.height;
5395 [overlay_ setFrame:ovrframe];
5397 CGRect barframe = [navbar_ frame];
5398 barframe.origin.y -= ovrframe.size.height;
5399 [navbar_ setFrame:barframe];
5401 CGRect trnframe = [transition_ frame];
5402 trnframe.origin.y -= ovrframe.size.height;
5403 trnframe.size.height += ovrframe.size.height;
5404 [transition_ setFrame:trnframe];
5406 [UIView commitAnimations];
5408 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5411 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5412 if ((self = [super initWithFrame:frame]) != nil) {
5413 database_ = database;
5415 CGRect ovrrect = [navbar_ bounds];
5416 ovrrect.size.height = [UINavigationBar defaultSize].height;
5417 ovrrect.origin.y = -ovrrect.size.height;
5419 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5420 [self addSubview:overlay_];
5422 ovrrect.origin.y = frame.size.height;
5423 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5424 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5425 [self addSubview:underlay_];
5427 [overlay_ setBarStyle:1];
5428 [underlay_ setBarStyle:1];
5430 int barstyle = [overlay_ _barStyle:NO];
5431 bool ugly = barstyle == 0;
5433 UIProgressIndicatorStyle style = ugly ?
5434 UIProgressIndicatorStyleMediumBrown :
5435 UIProgressIndicatorStyleMediumWhite;
5437 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5438 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5439 CGRect indrect = {{indoffset, indoffset}, indsize};
5441 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5442 [indicator_ setStyle:style];
5443 [overlay_ addSubview:indicator_];
5445 CGSize prmsize = {215, indsize.height + 4};
5448 indoffset * 2 + indsize.width,
5452 unsigned(ovrrect.size.height - prmsize.height) / 2
5455 UIFont *font = [UIFont systemFontOfSize:15];
5457 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5459 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5460 [prompt_ setBackgroundColor:[UIColor clearColor]];
5461 [prompt_ setFont:font];
5463 [overlay_ addSubview:prompt_];
5465 CGSize prgsize = {75, 100};
5468 ovrrect.size.width - prgsize.width - 10,
5469 (ovrrect.size.height - prgsize.height) / 2
5472 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5473 [progress_ setStyle:0];
5474 [overlay_ addSubview:progress_];
5476 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5477 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5479 CGRect frame = [cancel_ frame];
5480 frame.size.width = 65;
5481 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5482 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5483 [cancel_ setFrame:frame];
5485 [cancel_ setBarStyle:barstyle];
5489 - (void) _onCancel {
5491 [cancel_ removeFromSuperview];
5494 - (void) _update { _pooled
5496 status.setDelegate(self);
5498 [database_ updateWithStatus:status];
5501 performSelectorOnMainThread:@selector(_update_)
5507 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5508 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5511 - (void) setProgressTitle:(NSString *)title {
5513 performSelectorOnMainThread:@selector(_setProgressTitle:)
5519 - (void) setProgressPercent:(float)percent {
5521 performSelectorOnMainThread:@selector(_setProgressPercent:)
5522 withObject:[NSNumber numberWithFloat:percent]
5527 - (void) startProgress {
5530 - (void) addProgressOutput:(NSString *)output {
5532 performSelectorOnMainThread:@selector(_addProgressOutput:)
5538 - (bool) isCancelling:(size_t)received {
5542 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5546 - (void) _setProgressTitle:(NSString *)title {
5547 [prompt_ setText:title];
5550 - (void) _setProgressPercent:(NSNumber *)percent {
5551 [progress_ setProgress:[percent floatValue]];
5554 - (void) _addProgressOutput:(NSString *)output {
5559 /* Cydia:// Protocol {{{ */
5560 @interface CydiaURLProtocol : NSURLProtocol {
5565 @implementation CydiaURLProtocol
5567 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5568 NSURL *url([request URL]);
5571 NSString *scheme([[url scheme] lowercaseString]);
5572 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5577 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5581 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5582 id<NSURLProtocolClient> client([self client]);
5584 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5586 NSData *data(UIImagePNGRepresentation(icon));
5588 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5589 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5590 [client URLProtocol:self didLoadData:data];
5591 [client URLProtocolDidFinishLoading:self];
5595 - (void) startLoading {
5596 id<NSURLProtocolClient> client([self client]);
5597 NSURLRequest *request([self request]);
5599 NSURL *url([request URL]);
5600 NSString *href([url absoluteString]);
5602 NSString *path([href substringFromIndex:8]);
5603 NSRange slash([path rangeOfString:@"/"]);
5606 if (slash.location == NSNotFound) {
5610 command = [path substringToIndex:slash.location];
5611 path = [path substringFromIndex:(slash.location + 1)];
5614 Database *database([Database sharedInstance]);
5616 if ([command isEqualToString:@"package-icon"]) {
5619 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5620 Package *package([database packageWithName:path]);
5623 UIImage *icon([package icon]);
5624 [self _returnPNGWithImage:icon forRequest:request];
5625 } else if ([command isEqualToString:@"source-icon"]) {
5628 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5629 NSString *source(Simplify(path));
5630 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5632 icon = [UIImage applicationImageNamed:@"unknown.png"];
5633 [self _returnPNGWithImage:icon forRequest:request];
5634 } else if ([command isEqualToString:@"uikit-image"]) {
5637 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5638 UIImage *icon(_UIImageWithName(path));
5639 [self _returnPNGWithImage:icon forRequest:request];
5640 } else if ([command isEqualToString:@"section-icon"]) {
5643 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5644 NSString *section(Simplify(path));
5645 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5647 icon = [UIImage applicationImageNamed:@"unknown.png"];
5648 [self _returnPNGWithImage:icon forRequest:request];
5650 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5654 - (void) stopLoading {
5660 /* Sections View {{{ */
5661 @interface SectionsView : RVPage {
5662 _transient Database *database_;
5663 NSMutableArray *sections_;
5664 NSMutableArray *filtered_;
5665 UITransitionView *transition_;
5671 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5672 - (void) reloadData;
5677 @implementation SectionsView
5680 [list_ setDataSource:nil];
5681 [list_ setDelegate:nil];
5683 [sections_ release];
5684 [filtered_ release];
5685 [transition_ release];
5687 [accessory_ release];
5691 - (int) numberOfRowsInTable:(UITable *)table {
5692 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5695 - (float) table:(UITable *)table heightForRow:(int)row {
5699 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5701 reusing = [[[SectionCell alloc] init] autorelease];
5702 [(SectionCell *)reusing setSection:(editing_ ?
5703 [sections_ objectAtIndex:row] :
5704 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5705 ) editing:editing_];
5709 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5713 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5717 - (void) tableRowSelected:(NSNotification *)notification {
5718 int row = [[notification object] selectedRow];
5729 title = @"All Packages";
5731 section = [filtered_ objectAtIndex:(row - 1)];
5732 name = [section name];
5738 title = @"(No Section)";
5742 PackageTable *table = [[[FilteredPackageTable alloc]
5746 filter:@selector(isVisiblyUninstalledInSection:)
5750 [table setDelegate:delegate_];
5752 [book_ pushPage:table];
5755 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5756 if ((self = [super initWithBook:book]) != nil) {
5757 database_ = database;
5759 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5760 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5762 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5763 [self addSubview:transition_];
5765 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5766 [transition_ transition:0 toView:list_];
5768 UITableColumn *column = [[[UITableColumn alloc]
5769 initWithTitle:@"Name"
5771 width:[self frame].size.width
5774 [list_ setDataSource:self];
5775 [list_ setSeparatorStyle:1];
5776 [list_ addTableColumn:column];
5777 [list_ setDelegate:self];
5778 [list_ setReusesTableCells:YES];
5782 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5783 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5787 - (void) reloadData {
5788 NSArray *packages = [database_ packages];
5790 [sections_ removeAllObjects];
5791 [filtered_ removeAllObjects];
5793 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5794 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5797 for (Package *package in packages) {
5798 NSString *name([package section]);
5801 Section *section([sections objectForKey:name]);
5802 if (section == nil) {
5803 section = [[[Section alloc] initWithName:name] autorelease];
5804 [sections setObject:section forKey:name];
5808 if ([package valid] && [package installed] == nil && [package visible])
5809 [filtered addObject:package];
5813 [sections_ addObjectsFromArray:[sections allValues]];
5814 [sections_ sortUsingSelector:@selector(compareByName:)];
5817 [filtered sortUsingSelector:@selector(compareBySection:)];
5820 Section *section = nil;
5821 for (Package *package in filtered) {
5822 NSString *name = [package section];
5824 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5825 section = name == nil ?
5826 [[[Section alloc] initWithName:nil] autorelease] :
5827 [sections objectForKey:name];
5828 [filtered_ addObject:section];
5831 [section addToCount];
5839 - (void) resetView {
5841 [self _rightButtonClicked];
5844 - (void) resetViewAnimated:(BOOL)animated {
5845 [list_ resetViewAnimated:animated];
5848 - (void) _rightButtonClicked {
5849 if ((editing_ = !editing_))
5852 [delegate_ updateData];
5853 [book_ reloadTitleForPage:self];
5854 [book_ reloadButtonsForPage:self];
5857 - (NSString *) title {
5858 return editing_ ? @"Section Visibility" : @"Install by Section";
5861 - (NSString *) backButtonTitle {
5865 - (id) rightButtonTitle {
5866 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5869 - (UINavigationButtonStyle) rightButtonStyle {
5870 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5873 - (UIView *) accessoryView {
5879 /* Changes View {{{ */
5880 @interface ChangesView : RVPage {
5881 _transient Database *database_;
5882 NSMutableArray *packages_;
5883 NSMutableArray *sections_;
5884 UISectionList *list_;
5888 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5889 - (void) reloadData;
5893 @implementation ChangesView
5896 [[list_ table] setDelegate:nil];
5897 [list_ setDataSource:nil];
5899 [packages_ release];
5900 [sections_ release];
5905 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5906 return [sections_ count];
5909 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5910 return [[sections_ objectAtIndex:section] name];
5913 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5914 return [[sections_ objectAtIndex:section] row];
5917 - (int) numberOfRowsInTable:(UITable *)table {
5918 return [packages_ count];
5921 - (float) table:(UITable *)table heightForRow:(int)row {
5922 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5925 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5927 reusing = [[[PackageCell alloc] init] autorelease];
5928 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5932 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5936 - (void) tableRowSelected:(NSNotification *)notification {
5937 int row = [[notification object] selectedRow];
5940 Package *package = [packages_ objectAtIndex:row];
5941 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5942 [view setDelegate:delegate_];
5943 [view setPackage:package];
5944 [book_ pushPage:view];
5947 - (void) _leftButtonClicked {
5948 [(CYBook *)book_ update];
5949 [self reloadButtons];
5952 - (void) _rightButtonClicked {
5953 [delegate_ distUpgrade];
5956 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5957 if ((self = [super initWithBook:book]) != nil) {
5958 database_ = database;
5960 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5961 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5963 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5964 [self addSubview:list_];
5966 [list_ setShouldHideHeaderInShortLists:NO];
5967 [list_ setDataSource:self];
5968 //[list_ setSectionListStyle:1];
5970 UITableColumn *column = [[[UITableColumn alloc]
5971 initWithTitle:@"Name"
5973 width:[self frame].size.width
5976 UITable *table = [list_ table];
5977 [table setSeparatorStyle:1];
5978 [table addTableColumn:column];
5979 [table setDelegate:self];
5980 [table setReusesTableCells:YES];
5984 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5985 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5989 - (void) reloadData {
5990 NSArray *packages = [database_ packages];
5992 [packages_ removeAllObjects];
5993 [sections_ removeAllObjects];
5996 for (Package *package in packages)
5998 [package installed] == nil && [package valid] && [package visible] ||
5999 [package upgradableAndEssential:YES]
6001 [packages_ addObject:package];
6004 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
6007 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
6008 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
6009 Section *section = nil;
6013 bool unseens = false;
6015 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
6018 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6019 Package *package = [packages_ objectAtIndex:offset];
6021 if (![package upgradableAndEssential:YES]) {
6023 NSDate *seen = [package seen];
6025 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6032 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
6036 name = [@"New at " stringByAppendingString:name];
6037 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6038 [sections_ addObject:section];
6041 [section addToCount];
6042 } else if ([package ignored])
6043 [ignored addToCount];
6046 [upgradable addToCount];
6051 CFRelease(formatter);
6054 Section *last = [sections_ lastObject];
6055 size_t count = [last count];
6056 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6057 [sections_ removeLastObject];
6060 if ([ignored count] != 0)
6061 [sections_ insertObject:ignored atIndex:0];
6063 [sections_ insertObject:upgradable atIndex:0];
6066 [self reloadButtons];
6069 - (void) resetViewAnimated:(BOOL)animated {
6070 [list_ resetViewAnimated:animated];
6073 - (NSString *) leftButtonTitle {
6074 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6077 - (id) rightButtonTitle {
6078 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
6081 - (NSString *) title {
6087 /* Search View {{{ */
6088 @protocol SearchViewDelegate
6089 - (void) showKeyboard:(BOOL)show;
6092 @interface SearchView : RVPage {
6094 UISearchField *field_;
6095 UITransitionView *transition_;
6096 FilteredPackageTable *table_;
6097 UIPreferencesTable *advanced_;
6103 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6104 - (void) reloadData;
6108 @implementation SearchView
6111 [field_ setDelegate:nil];
6113 [accessory_ release];
6115 [transition_ release];
6117 [advanced_ release];
6122 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6126 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6128 case 0: return @"Advanced Search (Coming Soon!)";
6130 default: _assert(false);
6134 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6138 default: _assert(false);
6142 - (void) _showKeyboard:(BOOL)show {
6143 CGSize keysize = [UIKeyboard defaultSize];
6144 CGRect keydown = [book_ pageBounds];
6145 CGRect keyup = keydown;
6146 keyup.size.height -= keysize.height - ButtonBarHeight_;
6148 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6150 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6151 [animation setSignificantRectFields:8];
6154 [animation setStartFrame:keydown];
6155 [animation setEndFrame:keyup];
6157 [animation setStartFrame:keyup];
6158 [animation setEndFrame:keydown];
6161 UIAnimator *animator = [UIAnimator sharedAnimator];
6164 addAnimations:[NSArray arrayWithObjects:animation, nil]
6165 withDuration:(KeyboardTime_ - delay)
6170 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6172 [delegate_ showKeyboard:show];
6175 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6176 [self _showKeyboard:YES];
6179 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6180 [self _showKeyboard:NO];
6183 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6185 NSString *text([field_ text]);
6186 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6192 - (void) textFieldClearButtonPressed:(UITextField *)field {
6196 - (void) keyboardInputShouldDelete:(id)input {
6200 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6201 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6205 [field_ resignFirstResponder];
6210 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6211 if ((self = [super initWithBook:book]) != nil) {
6212 CGRect pageBounds = [book_ pageBounds];
6214 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6215 [self addSubview:transition_];
6217 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6219 [advanced_ setReusesTableCells:YES];
6220 [advanced_ setDataSource:self];
6221 [advanced_ reloadData];
6223 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6224 CGColor dimmed(space_, 0, 0, 0, 0.5);
6225 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6227 table_ = [[FilteredPackageTable alloc]
6231 filter:@selector(isUnfilteredAndSearchedForBy:)
6235 [table_ setShouldHideHeaderInShortLists:NO];
6236 [transition_ transition:0 toView:table_];
6245 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6252 [self bounds].size.width - area.origin.x - 18;
6254 area.size.height = [UISearchField defaultHeight];
6256 field_ = [[UISearchField alloc] initWithFrame:area];
6258 UIFont *font = [UIFont systemFontOfSize:16];
6259 [field_ setFont:font];
6261 [field_ setPlaceholder:@"Package Names & Descriptions"];
6262 [field_ setDelegate:self];
6264 [field_ setPaddingTop:5];
6266 UITextInputTraits *traits([field_ textInputTraits]);
6267 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6268 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6269 [traits setReturnKeyType:UIReturnKeySearch];
6271 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6273 accessory_ = [[UIView alloc] initWithFrame:accrect];
6274 [accessory_ addSubview:field_];
6276 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6277 [configure setShowPressFeedback:YES];
6278 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6279 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6280 [accessory_ addSubview:configure];*/
6282 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6283 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6289 LKAnimation *animation = [LKTransition animation];
6290 [animation setType:@"oglFlip"];
6291 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6292 [animation setFillMode:@"extended"];
6293 [animation setTransitionFlags:3];
6294 [animation setDuration:10];
6295 [animation setSpeed:0.35];
6296 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6297 [[transition_ _layer] addAnimation:animation forKey:0];
6298 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6299 flipped_ = !flipped_;
6303 - (void) configurePushed {
6304 [field_ resignFirstResponder];
6308 - (void) resetViewAnimated:(BOOL)animated {
6311 [table_ resetViewAnimated:animated];
6314 - (void) _reloadData {
6317 - (void) reloadData {
6320 [table_ setObject:[field_ text]];
6321 _profile(SearchView$reloadData)
6322 [table_ reloadData];
6325 [table_ resetCursor];
6328 - (UIView *) accessoryView {
6332 - (NSString *) title {
6336 - (NSString *) backButtonTitle {
6340 - (void) setDelegate:(id)delegate {
6341 [table_ setDelegate:delegate];
6342 [super setDelegate:delegate];
6348 @interface SettingsView : RVPage {
6349 _transient Database *database_;
6352 UIPreferencesTable *table_;
6353 _UISwitchSlider *subscribedSwitch_;
6354 _UISwitchSlider *ignoredSwitch_;
6355 UIPreferencesControlTableCell *subscribedCell_;
6356 UIPreferencesControlTableCell *ignoredCell_;
6359 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6363 @implementation SettingsView
6366 [table_ setDataSource:nil];
6369 if (package_ != nil)
6372 [subscribedSwitch_ release];
6373 [ignoredSwitch_ release];
6374 [subscribedCell_ release];
6375 [ignoredCell_ release];
6379 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6380 if (package_ == nil)
6386 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6387 if (package_ == nil)
6394 default: _assert(false);
6400 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6401 if (package_ == nil)
6408 default: _assert(false);
6414 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6415 if (package_ == nil)
6422 default: _assert(false);
6428 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6429 if (package_ == nil)
6432 _UISwitchSlider *slider([cell control]);
6433 BOOL value([slider value] != 0);
6434 NSMutableDictionary *metadata([package_ metadata]);
6437 if (NSNumber *number = [metadata objectForKey:key])
6438 before = [number boolValue];
6442 if (value != before) {
6443 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6445 [delegate_ updateData];
6449 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6450 [self onSomething:cell withKey:@"IsSubscribed"];
6453 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6454 [self onSomething:cell withKey:@"IsIgnored"];
6457 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6458 if (package_ == nil)
6462 case 0: switch (row) {
6464 return subscribedCell_;
6466 return ignoredCell_;
6467 default: _assert(false);
6470 case 1: switch (row) {
6472 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6473 [cell setShowSelection:NO];
6474 [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."];
6478 default: _assert(false);
6481 default: _assert(false);
6487 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6488 if ((self = [super initWithBook:book])) {
6489 database_ = database;
6490 name_ = [package retain];
6492 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6493 [self addSubview:table_];
6495 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6496 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6498 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6499 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6501 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6502 [subscribedCell_ setShowSelection:NO];
6503 [subscribedCell_ setTitle:@"Show All Changes"];
6504 [subscribedCell_ setControl:subscribedSwitch_];
6506 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6507 [ignoredCell_ setShowSelection:NO];
6508 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6509 [ignoredCell_ setControl:ignoredSwitch_];
6511 [table_ setDataSource:self];
6516 - (void) resetViewAnimated:(BOOL)animated {
6517 [table_ resetViewAnimated:animated];
6520 - (void) reloadData {
6521 if (package_ != nil)
6522 [package_ autorelease];
6523 package_ = [database_ packageWithName:name_];
6524 if (package_ != nil) {
6526 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6527 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6530 [table_ reloadData];
6533 - (NSString *) title {
6539 /* Signature View {{{ */
6540 @interface SignatureView : BrowserView {
6541 _transient Database *database_;
6545 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6549 @implementation SignatureView
6556 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6558 [super webView:sender didClearWindowObject:window forFrame:frame];
6561 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6562 if ((self = [super initWithBook:book]) != nil) {
6563 database_ = database;
6564 package_ = [package retain];
6569 - (void) resetViewAnimated:(BOOL)animated {
6572 - (void) reloadData {
6573 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6579 @interface Cydia : UIApplication <
6580 ConfirmationViewDelegate,
6581 ProgressViewDelegate,
6590 UIToolbar *buttonbar_;
6594 NSMutableArray *essential_;
6595 NSMutableArray *broken_;
6597 Database *database_;
6598 ProgressView *progress_;
6602 UIKeyboard *keyboard_;
6603 UIProgressHUD *hud_;
6605 SectionsView *sections_;
6606 ChangesView *changes_;
6607 ManageView *manage_;
6608 SearchView *search_;
6613 @implementation Cydia
6616 if ([broken_ count] != 0) {
6617 int count = [broken_ count];
6619 UIActionSheet *sheet = [[[UIActionSheet alloc]
6620 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6621 buttons:[NSArray arrayWithObjects:
6623 @"Ignore (Temporary)",
6625 defaultButtonIndex:0
6630 [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."];
6631 [sheet popupAlertAnimated:YES];
6632 } else if (!Ignored_ && [essential_ count] != 0) {
6633 int count = [essential_ count];
6635 UIActionSheet *sheet = [[[UIActionSheet alloc]
6636 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6637 buttons:[NSArray arrayWithObjects:
6638 @"Upgrade Essential",
6639 @"Complete Upgrade",
6640 @"Ignore (Temporary)",
6642 defaultButtonIndex:0
6647 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6648 [sheet popupAlertAnimated:YES];
6652 - (void) _reloadData {
6655 static bool loaded(false);
6656 UIProgressHUD *hud([self addProgressHUD]);
6657 [hud setText:(loaded ? @"Reloading Data" : @"Loading Data")];
6660 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6663 [self removeProgressHUD:hud];
6667 [essential_ removeAllObjects];
6668 [broken_ removeAllObjects];
6670 NSArray *packages = [database_ packages];
6671 for (Package *package in packages) {
6673 [broken_ addObject:package];
6674 if ([package upgradableAndEssential:NO]) {
6675 if ([package essential])
6676 [essential_ addObject:package];
6682 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6683 [buttonbar_ setBadgeValue:badge forButton:3];
6684 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6685 [buttonbar_ setBadgeAnimated:([essential_ count] != 0) forButton:3];
6686 [self setApplicationBadge:badge];
6688 [buttonbar_ setBadgeValue:nil forButton:3];
6689 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6690 [buttonbar_ setBadgeAnimated:NO forButton:3];
6691 [self removeApplicationBadge];
6695 [buttonbar_ setBadgeValue:nil forButton:4];
6699 // XXX: what is this line of code for?
6700 if ([packages count] == 0);
6701 else if (Loaded_ || ManualRefresh) loaded:
6706 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6707 NSTimeInterval interval([update timeIntervalSinceNow]);
6708 if (interval <= 0 && interval > -600)
6716 - (void) _saveConfig {
6719 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6725 - (void) updateData {
6728 /* XXX: this is just stupid */
6729 if (tag_ != 2 && sections_ != nil)
6730 [sections_ reloadData];
6731 if (tag_ != 3 && changes_ != nil)
6732 [changes_ reloadData];
6733 if (tag_ != 5 && search_ != nil)
6734 [search_ reloadData];
6744 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6745 _assert(file != NULL);
6747 NSArray *keys = [Sources_ allKeys];
6749 for (NSString *key in keys) {
6750 NSDictionary *source = [Sources_ objectForKey:key];
6752 fprintf(file, "%s %s %s\n",
6753 [[source objectForKey:@"Type"] UTF8String],
6754 [[source objectForKey:@"URI"] UTF8String],
6755 [[source objectForKey:@"Distribution"] UTF8String]
6764 detachNewThreadSelector:@selector(update_)
6767 title:@"Updating Sources"
6771 - (void) reloadData {
6772 @synchronized (self) {
6773 if (confirm_ == nil)
6779 pkgProblemResolver *resolver = [database_ resolver];
6781 resolver->InstallProtect();
6782 if (!resolver->Resolve(true))
6786 - (void) popUpBook:(RVBook *)book {
6787 [underlay_ popSubview:book];
6790 - (CGRect) popUpBounds {
6791 return [underlay_ bounds];
6795 [database_ prepare];
6797 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6798 [confirm_ setDelegate:self];
6800 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6801 [page setDelegate:self];
6803 [confirm_ setPage:page];
6804 [self popUpBook:confirm_];
6808 @synchronized (self) {
6813 - (void) clearPackage:(Package *)package {
6814 @synchronized (self) {
6821 - (void) installPackage:(Package *)package {
6822 @synchronized (self) {
6829 - (void) removePackage:(Package *)package {
6830 @synchronized (self) {
6837 - (void) distUpgrade {
6838 @synchronized (self) {
6839 [database_ upgrade];
6845 [self slideUp:[[[UIActionSheet alloc]
6847 buttons:[NSArray arrayWithObjects:@"Continue Queuing", @"Cancel and Clear", nil]
6848 defaultButtonIndex:1
6855 @synchronized (self) {
6858 if (confirm_ != nil) {
6866 [overlay_ removeFromSuperview];
6870 detachNewThreadSelector:@selector(perform)
6877 - (void) bootstrap_ {
6879 [database_ upgrade];
6880 [database_ prepare];
6881 [database_ perform];
6884 - (void) bootstrap {
6886 detachNewThreadSelector:@selector(bootstrap_)
6889 title:@"Bootstrap Install"
6893 - (void) progressViewIsComplete:(ProgressView *)progress {
6894 if (confirm_ != nil) {
6895 [underlay_ addSubview:overlay_];
6896 [confirm_ popFromSuperviewAnimated:NO];
6902 - (void) setPage:(RVPage *)page {
6903 [page resetViewAnimated:NO];
6904 [page setDelegate:self];
6905 [book_ setPage:page];
6908 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6909 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6910 [browser loadURL:url];
6914 - (void) _setHomePage {
6915 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6918 - (void) buttonBarItemTapped:(id)sender {
6919 unsigned tag = [sender tag];
6921 [book_ resetViewAnimated:YES];
6923 } else if (tag_ == 2 && tag != 2)
6924 [sections_ resetView];
6927 case 1: [self _setHomePage]; break;
6929 case 2: [self setPage:sections_]; break;
6930 case 3: [self setPage:changes_]; break;
6931 case 4: [self setPage:manage_]; break;
6932 case 5: [self setPage:search_]; break;
6934 default: _assert(false);
6940 - (void) applicationWillSuspend {
6942 [super applicationWillSuspend];
6945 - (void) askForSettings {
6946 UIActionSheet *role = [[[UIActionSheet alloc]
6947 initWithTitle:@"Who Are You?"
6948 buttons:[NSArray arrayWithObjects:
6949 @"User (Graphical Only)",
6950 @"Hacker (+ Command Line)",
6951 @"Developer (No Filters)",
6953 defaultButtonIndex:-1
6958 [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."];
6959 [role popupAlertAnimated:YES];
6964 [self setStatusBarShowsProgress:NO];
6965 [self removeProgressHUD:hud_];
6970 pid_t pid = ExecFork();
6972 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6973 perror("launchctl stop");
6980 [self askForSettings];
6985 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6987 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6988 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6989 0, 0, screenrect.size.width, screenrect.size.height - 48
6990 ) database:database_];
6992 [book_ setDelegate:self];
6994 [overlay_ addSubview:book_];
6996 NSArray *buttonitems = [NSArray arrayWithObjects:
6997 [NSDictionary dictionaryWithObjectsAndKeys:
6998 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6999 @"home-up.png", kUIButtonBarButtonInfo,
7000 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
7001 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
7002 self, kUIButtonBarButtonTarget,
7003 @"Home", kUIButtonBarButtonTitle,
7004 @"0", kUIButtonBarButtonType,
7007 [NSDictionary dictionaryWithObjectsAndKeys:
7008 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7009 @"install-up.png", kUIButtonBarButtonInfo,
7010 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
7011 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
7012 self, kUIButtonBarButtonTarget,
7013 @"Sections", kUIButtonBarButtonTitle,
7014 @"0", kUIButtonBarButtonType,
7017 [NSDictionary dictionaryWithObjectsAndKeys:
7018 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7019 @"changes-up.png", kUIButtonBarButtonInfo,
7020 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
7021 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
7022 self, kUIButtonBarButtonTarget,
7023 @"Changes", kUIButtonBarButtonTitle,
7024 @"0", kUIButtonBarButtonType,
7027 [NSDictionary dictionaryWithObjectsAndKeys:
7028 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7029 @"manage-up.png", kUIButtonBarButtonInfo,
7030 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
7031 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
7032 self, kUIButtonBarButtonTarget,
7033 @"Manage", kUIButtonBarButtonTitle,
7034 @"0", kUIButtonBarButtonType,
7037 [NSDictionary dictionaryWithObjectsAndKeys:
7038 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7039 @"search-up.png", kUIButtonBarButtonInfo,
7040 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
7041 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
7042 self, kUIButtonBarButtonTarget,
7043 @"Search", kUIButtonBarButtonTitle,
7044 @"0", kUIButtonBarButtonType,
7048 buttonbar_ = [[UIToolbar alloc]
7050 withFrame:CGRectMake(
7051 0, screenrect.size.height - ButtonBarHeight_,
7052 screenrect.size.width, ButtonBarHeight_
7054 withItemList:buttonitems
7057 [buttonbar_ setDelegate:self];
7058 [buttonbar_ setBarStyle:1];
7059 [buttonbar_ setButtonBarTrackingMode:2];
7061 int buttons[5] = {1, 2, 3, 4, 5};
7062 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7063 [buttonbar_ showButtonGroup:0 withDuration:0];
7065 for (int i = 0; i != 5; ++i)
7066 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
7067 i * 64 + 2, 1, 60, ButtonBarHeight_
7070 [buttonbar_ showSelectionForButton:1];
7071 [overlay_ addSubview:buttonbar_];
7073 [UIKeyboard initImplementationNow];
7074 CGSize keysize = [UIKeyboard defaultSize];
7075 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
7076 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
7077 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
7078 [overlay_ addSubview:keyboard_];
7081 [underlay_ addSubview:overlay_];
7085 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
7086 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
7087 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7089 manage_ = (ManageView *) [[self
7090 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7091 withClass:[ManageView class]
7099 [self _setHomePage];
7102 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7103 NSString *context([sheet context]);
7105 if ([context isEqualToString:@"missing"])
7107 else if ([context isEqualToString:@"cancel"]) {
7125 @synchronized (self) {
7130 [buttonbar_ setBadgeValue:@"Q'd" forButton:4];
7134 if (confirm_ != nil) {
7139 } else if ([context isEqualToString:@"fixhalf"]) {
7142 @synchronized (self) {
7143 for (Package *broken in broken_) {
7146 NSString *id = [broken id];
7147 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7148 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7149 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7150 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7159 [broken_ removeAllObjects];
7168 } else if ([context isEqualToString:@"role"]) {
7170 case 1: Role_ = @"User"; break;
7171 case 2: Role_ = @"Hacker"; break;
7172 case 3: Role_ = @"Developer"; break;
7179 bool reset = Settings_ != nil;
7181 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7185 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7195 } else if ([context isEqualToString:@"upgrade"]) {
7198 @synchronized (self) {
7199 for (Package *essential in essential_)
7200 [essential install];
7223 - (void) reorganize { _pooled
7224 system("/usr/libexec/cydia/free.sh");
7225 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7228 - (void) applicationSuspend:(__GSEvent *)event {
7229 if (hud_ == nil && ![progress_ isRunning])
7230 [super applicationSuspend:event];
7233 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7235 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7238 - (void) _setSuspended:(BOOL)value {
7240 [super _setSuspended:value];
7243 - (UIProgressHUD *) addProgressHUD {
7244 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
7245 [window_ setUserInteractionEnabled:NO];
7247 [progress_ addSubview:hud];
7251 - (void) removeProgressHUD:(UIProgressHUD *)hud {
7253 [hud removeFromSuperview];
7254 [window_ setUserInteractionEnabled:YES];
7257 - (void) openMailToURL:(NSURL *)url {
7258 // XXX: this makes me sad
7260 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7262 [UIApp openURL:url];// asPanel:YES];
7266 - (void) clearFirstResponder {
7267 if (id responder = [window_ firstResponder])
7268 [responder resignFirstResponder];
7271 - (RVPage *) pageForPackage:(NSString *)name {
7272 if (Package *package = [database_ packageWithName:name]) {
7273 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7274 [view setPackage:package];
7277 UIActionSheet *sheet = [[[UIActionSheet alloc]
7278 initWithTitle:@"Cannot Locate Package"
7279 buttons:[NSArray arrayWithObjects:@"Close", nil]
7280 defaultButtonIndex:0
7285 [sheet setBodyText:[NSString stringWithFormat:
7286 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7289 [sheet popupAlertAnimated:YES];
7294 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7298 NSString *scheme([[url scheme] lowercaseString]);
7299 if (![scheme isEqualToString:@"cydia"])
7301 NSString *path([url absoluteString]);
7302 if ([path length] < 8)
7304 path = [path substringFromIndex:8];
7305 if (![path hasPrefix:@"/"])
7306 path = [@"/" stringByAppendingString:path];
7308 if ([path isEqualToString:@"/add-source"])
7309 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7310 else if ([path isEqualToString:@"/storage"])
7311 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
7312 else if ([path isEqualToString:@"/sources"])
7313 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7314 else if ([path isEqualToString:@"/packages"])
7315 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7316 else if ([path hasPrefix:@"/url/"])
7317 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
7318 else if ([path hasPrefix:@"/launch/"])
7319 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
7320 else if ([path hasPrefix:@"/package-settings/"])
7321 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
7322 else if ([path hasPrefix:@"/package-signature/"])
7323 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
7324 else if ([path hasPrefix:@"/package/"])
7325 return [self pageForPackage:[path substringFromIndex:9]];
7326 else if ([path hasPrefix:@"/files/"]) {
7327 NSString *name = [path substringFromIndex:7];
7329 if (Package *package = [database_ packageWithName:name]) {
7330 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7331 [files setPackage:package];
7339 - (void) applicationOpenURL:(NSURL *)url {
7340 [super applicationOpenURL:url];
7342 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7343 [self setPage:page];
7344 [buttonbar_ showSelectionForButton:tag];
7349 - (void) applicationDidFinishLaunching:(id)unused {
7351 Font12_ = [[UIFont systemFontOfSize:12] retain];
7352 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7353 Font14_ = [[UIFont systemFontOfSize:14] retain];
7354 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7355 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7357 _assert(pkgInitConfig(*_config));
7358 _assert(pkgInitSystem(*_config, _system));
7362 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7363 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7365 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7367 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7368 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7370 [window_ orderFront:self];
7371 [window_ makeKey:self];
7372 [window_ setHidden:NO];
7374 database_ = [Database sharedInstance];
7375 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7376 [database_ setDelegate:progress_];
7377 [window_ setContentView:progress_];
7379 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7380 [progress_ setContentView:underlay_];
7382 [progress_ resetView];
7385 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7386 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7387 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7388 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7389 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7390 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7391 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7393 [self setIdleTimerDisabled:YES];
7395 hud_ = [[self addProgressHUD] retain];
7396 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7398 [self setStatusBarShowsProgress:YES];
7401 detachNewThreadSelector:@selector(reorganize)
7409 - (void) showKeyboard:(BOOL)show {
7410 CGSize keysize = [UIKeyboard defaultSize];
7411 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7412 CGRect keyup = keydown;
7413 keyup.origin.y -= keysize.height;
7415 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7416 [animation setSignificantRectFields:2];
7419 [animation setStartFrame:keydown];
7420 [animation setEndFrame:keyup];
7421 [keyboard_ activate];
7423 [animation setStartFrame:keyup];
7424 [animation setEndFrame:keydown];
7425 [keyboard_ deactivate];
7428 [[UIAnimator sharedAnimator]
7429 addAnimations:[NSArray arrayWithObjects:animation, nil]
7430 withDuration:KeyboardTime_
7435 - (void) slideUp:(UIActionSheet *)alert {
7437 [alert presentSheetFromButtonBar:buttonbar_];
7439 [alert presentSheetInView:overlay_];
7444 void AddPreferences(NSString *plist) { _pooled
7445 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7446 _assert(settings != NULL);
7447 NSMutableArray *items = [settings objectForKey:@"items"];
7451 for (NSMutableDictionary *item in items) {
7452 NSString *label = [item objectForKey:@"label"];
7453 if (label != nil && [label isEqualToString:@"Cydia"]) {
7460 for (size_t i(0); i != [items count]; ++i) {
7461 NSDictionary *item([items objectAtIndex:i]);
7462 NSString *label = [item objectForKey:@"label"];
7463 if (label != nil && [label isEqualToString:@"General"]) {
7464 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7465 @"CydiaSettings", @"bundle",
7466 @"PSLinkCell", @"cell",
7467 [NSNumber numberWithBool:YES], @"hasIcon",
7468 [NSNumber numberWithBool:YES], @"isController",
7470 nil] atIndex:(i + 1)];
7476 _assert([settings writeToFile:plist atomically:YES] == YES);
7481 id Alloc_(id self, SEL selector) {
7482 id object = alloc_(self, selector);
7483 lprintf("[%s]A-%p\n", self->isa->name, object);
7488 id Dealloc_(id self, SEL selector) {
7489 id object = dealloc_(self, selector);
7490 lprintf("[%s]D-%p\n", self->isa->name, object);
7494 int main(int argc, char *argv[]) { _pooled
7496 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7498 bool substrate(false);
7504 for (int argi(1); argi != argc; ++argi)
7505 if (strcmp(argv[argi], "--") == 0) {
7507 argv[argi] = argv[0];
7513 for (int argi(1); argi != arge; ++argi)
7514 if (strcmp(args[argi], "--bootstrap") == 0)
7516 else if (strcmp(args[argi], "--substrate") == 0)
7519 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7522 App_ = [[NSBundle mainBundle] bundlePath];
7523 Home_ = NSHomeDirectory();
7524 Locale_ = CFLocaleCopyCurrent();
7527 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7528 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7529 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7530 Sounds_Keyboard_ = [keyboard boolValue];
7536 #if 1 /* XXX: this costs 1.4s of startup performance */
7537 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7538 _assert(errno == ENOENT);
7539 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7540 _assert(errno == ENOENT);
7543 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7544 alloc_ = alloc->method_imp;
7545 alloc->method_imp = (IMP) &Alloc_;*/
7547 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7548 dealloc_ = dealloc->method_imp;
7549 dealloc->method_imp = (IMP) &Dealloc_;*/
7554 size = sizeof(maxproc);
7555 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7556 perror("sysctlbyname(\"kern.maxproc\", ?)");
7557 else if (maxproc < 64) {
7559 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7560 perror("sysctlbyname(\"kern.maxproc\", #)");
7563 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7564 char *machine = new char[size];
7565 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7566 perror("sysctlbyname(\"hw.machine\", ?)");
7570 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7572 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7573 Build_ = [system objectForKey:@"ProductBuildVersion"];
7574 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
7575 Product_ = [info objectForKey:@"SafariProductVersion"];
7576 Safari_ = [info objectForKey:@"CFBundleVersion"];
7579 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7580 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7583 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7586 if (Metadata_ == NULL)
7587 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7589 Settings_ = [Metadata_ objectForKey:@"Settings"];
7591 Packages_ = [Metadata_ objectForKey:@"Packages"];
7592 Sections_ = [Metadata_ objectForKey:@"Sections"];
7593 Sources_ = [Metadata_ objectForKey:@"Sources"];
7596 if (Settings_ != nil)
7597 Role_ = [Settings_ objectForKey:@"Role"];
7599 if (Packages_ == nil) {
7600 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7601 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7604 if (Sections_ == nil) {
7605 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7606 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7609 if (Sources_ == nil) {
7610 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7611 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7615 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7618 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7619 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7620 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7621 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7623 if (access("/User", F_OK) != 0) {
7625 system("/usr/libexec/cydia/firmware.sh");
7629 _assert([[NSFileManager defaultManager]
7630 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7631 withIntermediateDirectories:YES
7636 space_ = CGColorSpaceCreateDeviceRGB();
7638 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7639 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7640 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7641 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7642 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7643 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7644 Green_.Set(space_, 0.0, 0.5, 0.0, 1.0);
7645 Purple_.Set(space_, 0.0, 0.0, 0.7, 1.0);
7646 Purplish_.Set(space_, 0.4, 0.4, 0.8, 1.0);
7647 /*Purple_.Set(space_, 1.0, 0.3, 0.0, 1.0);
7648 Purplish_.Set(space_, 1.0, 0.6, 0.4, 1.0); ORANGE */
7649 /*Purple_.Set(space_, 1.0, 0.5, 0.0, 1.0);
7650 Purplish_.Set(space_, 1.0, 0.7, 0.2, 1.0); ORANGISH */
7651 /*Purple_.Set(space_, 0.5, 0.0, 0.7, 1.0);
7652 Purplish_.Set(space_, 0.7, 0.4, 0.8, 1.0); PURPLE */
7655 InstallingColor_ = [UIColor colorWithRed:0.88f green:1.00f blue:0.88f alpha:1.00f];
7656 RemovingColor_ = [UIColor colorWithRed:1.00f green:0.88f blue:0.88f alpha:1.00f];
7658 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7660 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7662 UIApplicationUseLegacyEvents(YES);
7663 UIKeyboardDisableAutomaticAppearance();
7666 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7668 CGColorSpaceRelease(space_);