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 - (void) yieldToSelector:(SEL)selector withObject:(id)object;
238 @implementation NSObject (Cydia)
243 - (void) _yieldToContext:(NSArray *)context { _pooled
244 SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
245 id object([[context objectAtIndex:1] nonretainedObjectValue]);
246 volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
248 [self performSelector:selector withObject:object];
253 performSelectorOnMainThread:@selector(doNothing)
259 - (void) yieldToSelector:(SEL)selector withObject:(id)object {
260 [self performSelector:selector withObject:object];
263 volatile bool stopped(false);
265 NSArray *context([NSArray arrayWithObjects:
266 [NSValue valueWithPointer:selector],
267 [NSValue valueWithNonretainedObject:object],
268 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
271 NSThread *thread([[[NSThread alloc]
273 selector:@selector(_yieldToContext:)
279 NSRunLoop *loop([NSRunLoop currentRunLoop]);
280 NSDate *future([NSDate distantFuture]);
282 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
287 /* NSForcedOrderingSearch doesn't work on the iPhone */
288 static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
289 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
290 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
291 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
293 /* iPhoneOS 2.0 Compatibility {{{ */
295 @interface UITextView (iPhoneOS)
296 - (void) setTextSize:(float)size;
299 @implementation UITextView (iPhoneOS)
301 - (void) setTextSize:(float)size {
302 [self setFont:[[self font] fontWithSize:size]];
309 extern NSString * const kCAFilterNearest;
311 /* Information Dictionaries {{{ */
312 @interface NSMutableArray (Cydia)
313 - (void) addInfoDictionary:(NSDictionary *)info;
316 @implementation NSMutableArray (Cydia)
318 - (void) addInfoDictionary:(NSDictionary *)info {
319 [self addObject:info];
324 @interface NSMutableDictionary (Cydia)
325 - (void) addInfoDictionary:(NSDictionary *)info;
328 @implementation NSMutableDictionary (Cydia)
330 - (void) addInfoDictionary:(NSDictionary *)info {
331 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
332 [self setObject:info forKey:bundle];
337 /* Pop Transitions {{{ */
338 @interface PopTransitionView : UITransitionView {
343 @implementation PopTransitionView
345 - (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
346 if (from != nil && to == nil)
347 [self removeFromSuperview];
352 @implementation UIView (PopUpView)
354 - (void) popFromSuperviewAnimated:(BOOL)animated {
355 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
358 - (void) popSubview:(UIView *)view {
359 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
360 [transition setDelegate:transition];
361 [self addSubview:transition];
363 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
364 [transition transition:UITransitionNone toView:blank];
365 [transition transition:UITransitionPushFromBottom toView:view];
371 #define lprintf(args...) fprintf(stderr, args)
374 #define ForSaurik (1 && !ForRelease)
375 #define ShowInternals (0 && !ForRelease)
376 #define IgnoreInstall (0 && !ForRelease)
377 #define RecycleWebViews 0
378 #define AlwaysReload (1 && !ForRelease)
382 #define _trace(args...)
384 #define _profile(name)
390 @interface NSMutableArray (Radix)
391 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
394 @implementation NSMutableArray (Radix)
396 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
397 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
398 [invocation setSelector:selector];
399 [invocation setArgument:&object atIndex:2];
401 size_t count([self count]);
406 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
408 for (size_t i(0); i != count; ++i) {
409 RadixItem &item(lhs[i]);
412 id object([self objectAtIndex:i]);
413 [invocation setTarget:object];
416 [invocation getReturnValue:&item.key];
419 static const size_t width = 32;
420 static const size_t bits = 11;
421 static const size_t slots = 1 << bits;
422 static const size_t passes = (width + (bits - 1)) / bits;
424 size_t *hist(new size_t[slots]);
426 for (size_t pass(0); pass != passes; ++pass) {
427 memset(hist, 0, sizeof(size_t) * slots);
429 for (size_t i(0); i != count; ++i) {
430 uint32_t key(lhs[i].key);
432 key &= _not(uint32_t) >> width - bits;
437 for (size_t i(0); i != slots; ++i) {
438 size_t local(offset);
443 for (size_t i(0); i != count; ++i) {
444 uint32_t key(lhs[i].key);
446 key &= _not(uint32_t) >> width - bits;
447 rhs[hist[key]++] = lhs[i];
457 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
458 for (size_t i(0); i != count; ++i)
459 [values addObject:[self objectAtIndex:lhs[i].index]];
460 [self setArray:values];
468 /* Apple Bug Fixes {{{ */
469 @implementation UIWebDocumentView (Cydia)
471 - (void) _setScrollerOffset:(CGPoint)offset {
472 UIScroller *scroller([self _scroller]);
474 CGSize size([scroller contentSize]);
475 CGSize bounds([scroller bounds].size);
478 max.x = size.width - bounds.width;
479 max.y = size.height - bounds.height;
487 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
488 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
490 [scroller setOffset:offset];
497 kUIControlEventMouseDown = 1 << 0,
498 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
499 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
500 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
501 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
502 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
503 } UIControlEventMasks;
505 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
506 size_t length([self length] - state->state);
509 else if (length > count)
511 for (size_t i(0); i != length; ++i)
512 objects[i] = [self item:state->state++];
513 state->itemsPtr = objects;
514 state->mutationsPtr = (unsigned long *) self;
518 @interface NSString (UIKit)
519 - (NSString *) stringByAddingPercentEscapes;
520 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
523 @interface NSString (Cydia)
524 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
525 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
526 - (NSComparisonResult) compareByPath:(NSString *)other;
529 @implementation NSString (Cydia)
531 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length {
532 return [[[NSString alloc] initWithBytesNoCopy:const_cast<char *>(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
535 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
536 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
539 - (NSComparisonResult) compareByPath:(NSString *)other {
540 NSString *prefix = [self commonPrefixWithString:other options:0];
541 size_t length = [prefix length];
543 NSRange lrange = NSMakeRange(length, [self length] - length);
544 NSRange rrange = NSMakeRange(length, [other length] - length);
546 lrange = [self rangeOfString:@"/" options:0 range:lrange];
547 rrange = [other rangeOfString:@"/" options:0 range:rrange];
549 NSComparisonResult value;
551 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
552 value = NSOrderedSame;
553 else if (lrange.location == NSNotFound)
554 value = NSOrderedAscending;
555 else if (rrange.location == NSNotFound)
556 value = NSOrderedDescending;
558 value = NSOrderedSame;
560 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
561 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
562 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
563 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
565 NSComparisonResult result = [lpath compare:rpath];
566 return result == NSOrderedSame ? value : result;
571 /* Perl-Compatible RegEx {{{ */
581 Pcre(const char *regex) :
586 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
589 lprintf("%d:%s\n", offset, error);
593 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
594 matches_ = new int[(capture_ + 1) * 3];
602 NSString *operator [](size_t match) {
603 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
606 bool operator ()(NSString *data) {
607 // XXX: length is for characters, not for bytes
608 return operator ()([data UTF8String], [data length]);
611 bool operator ()(const char *data, size_t size) {
613 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
617 /* Mime Addresses {{{ */
618 @interface Address : NSObject {
624 - (NSString *) address;
626 + (Address *) addressWithString:(NSString *)string;
627 - (Address *) initWithString:(NSString *)string;
630 @implementation Address
639 - (NSString *) name {
643 - (NSString *) address {
647 + (Address *) addressWithString:(NSString *)string {
648 return [[[Address alloc] initWithString:string] autorelease];
651 + (NSArray *) _attributeKeys {
652 return [NSArray arrayWithObjects:@"address", @"name", nil];
655 - (NSArray *) attributeKeys {
656 return [[self class] _attributeKeys];
659 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
660 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
663 - (Address *) initWithString:(NSString *)string {
664 if ((self = [super init]) != nil) {
665 const char *data = [string UTF8String];
666 size_t size = [string length];
668 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
670 if (address_r(data, size)) {
671 name_ = [address_r[1] retain];
672 address_ = [address_r[2] retain];
674 name_ = [string retain];
682 /* CoreGraphics Primitives {{{ */
693 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
696 Set(space, red, green, blue, alpha);
701 CGColorRelease(color_);
708 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
710 float color[] = {red, green, blue, alpha};
711 color_ = CGColorCreate(space, color);
714 operator CGColorRef() {
720 extern "C" void UISetColor(CGColorRef color);
722 /* Random Global Variables {{{ */
723 static const int PulseInterval_ = 50000;
724 static const int ButtonBarHeight_ = 48;
725 static const float KeyboardTime_ = 0.3f;
727 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
728 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
729 #define NotifyConfig_ "/etc/notify.conf"
731 static CGColor Blue_;
732 static CGColor Blueish_;
733 static CGColor Black_;
735 static CGColor White_;
736 static CGColor Gray_;
738 static NSString *App_;
739 static NSString *Home_;
740 static BOOL Sounds_Keyboard_;
742 static BOOL Advanced_;
744 static BOOL Ignored_;
746 static UIFont *Font12_;
747 static UIFont *Font12Bold_;
748 static UIFont *Font14_;
749 static UIFont *Font18Bold_;
750 static UIFont *Font22Bold_;
752 static const char *Machine_ = NULL;
753 static const NSString *UniqueID_ = nil;
754 static const NSString *Build_ = nil;
757 CGColorSpaceRef space_;
762 static NSDictionary *SectionMap_;
763 static NSMutableDictionary *Metadata_;
764 static _transient NSMutableDictionary *Settings_;
765 static _transient NSString *Role_;
766 static _transient NSMutableDictionary *Packages_;
767 static _transient NSMutableDictionary *Sections_;
768 static _transient NSMutableDictionary *Sources_;
769 static bool Changed_;
773 static NSMutableArray *Documents_;
776 NSString *GetLastUpdate() {
777 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
780 return @"Never or Unknown";
782 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
783 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
785 CFRelease(formatter);
787 return [(NSString *) formatted autorelease];
790 /* Display Helpers {{{ */
791 inline float Interpolate(float begin, float end, float fraction) {
792 return (end - begin) * fraction + begin;
795 NSString *SizeString(double size) {
796 bool negative = size < 0;
801 while (size > 1024) {
806 static const char *powers_[] = {"B", "kB", "MB", "GB"};
808 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
811 NSString *StripVersion(NSString *version) {
812 NSRange colon = [version rangeOfString:@":"];
813 if (colon.location != NSNotFound)
814 version = [version substringFromIndex:(colon.location + 1)];
818 NSString *Simplify(NSString *title) {
819 const char *data = [title UTF8String];
820 size_t size = [title length];
822 static Pcre square_r("^\\[(.*)\\]$");
823 if (square_r(data, size))
824 return Simplify(square_r[1]);
826 static Pcre paren_r("^\\((.*)\\)$");
827 if (paren_r(data, size))
828 return Simplify(paren_r[1]);
830 static Pcre title_r("^(.*?) \\(.*\\)$");
831 if (title_r(data, size))
832 return Simplify(title_r[1]);
838 bool isSectionVisible(NSString *section) {
839 NSDictionary *metadata = [Sections_ objectForKey:section];
840 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
841 return hidden == nil || ![hidden boolValue];
844 /* Delegate Prototypes {{{ */
848 @interface NSObject (ProgressDelegate)
851 @implementation NSObject(ProgressDelegate)
853 - (void) _setProgressError:(NSArray *)args {
854 [self performSelector:@selector(setProgressError:forPackage:)
855 withObject:[args objectAtIndex:0]
856 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
862 @protocol ProgressDelegate
863 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
864 - (void) setProgressTitle:(NSString *)title;
865 - (void) setProgressPercent:(float)percent;
866 - (void) startProgress;
867 - (void) addProgressOutput:(NSString *)output;
868 - (bool) isCancelling:(size_t)received;
871 @protocol ConfigurationDelegate
872 - (void) repairWithSelector:(SEL)selector;
873 - (void) setConfigurationData:(NSString *)data;
876 @protocol CydiaDelegate
877 - (void) installPackage:(Package *)package;
878 - (void) removePackage:(Package *)package;
879 - (void) slideUp:(UIActionSheet *)alert;
880 - (void) distUpgrade;
883 - (void) askForSettings;
884 - (UIProgressHUD *) addProgressHUD;
885 - (void) removeProgressHUD:(UIProgressHUD *)hud;
886 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
887 - (RVPage *) pageForPackage:(NSString *)name;
888 - (void) openMailToURL:(NSURL *)url;
889 - (void) clearFirstResponder;
893 /* Status Delegation {{{ */
895 public pkgAcquireStatus
898 _transient NSObject<ProgressDelegate> *delegate_;
906 void setDelegate(id delegate) {
907 delegate_ = delegate;
910 virtual bool MediaChange(std::string media, std::string drive) {
914 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
917 virtual void Fetch(pkgAcquire::ItemDesc &item) {
918 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
919 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
922 virtual void Done(pkgAcquire::ItemDesc &item) {
925 virtual void Fail(pkgAcquire::ItemDesc &item) {
927 item.Owner->Status == pkgAcquire::Item::StatIdle ||
928 item.Owner->Status == pkgAcquire::Item::StatDone
932 std::string &error(item.Owner->ErrorText);
936 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
937 NSArray *fields([description componentsSeparatedByString:@" "]);
938 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
940 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
941 withObject:[NSArray arrayWithObjects:
942 [NSString stringWithUTF8String:error.c_str()],
949 virtual bool Pulse(pkgAcquire *Owner) {
950 bool value = pkgAcquireStatus::Pulse(Owner);
953 double(CurrentBytes + CurrentItems) /
954 double(TotalBytes + TotalItems)
957 [delegate_ setProgressPercent:percent];
958 return [delegate_ isCancelling:CurrentBytes] ? false : value;
961 virtual void Start() {
962 [delegate_ startProgress];
965 virtual void Stop() {
969 /* Progress Delegation {{{ */
974 _transient id<ProgressDelegate> delegate_;
977 virtual void Update() {
978 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
979 [delegate_ setProgressPercent:(Percent / 100)];*/
988 void setDelegate(id delegate) {
989 delegate_ = delegate;
992 virtual void Done() {
993 //[delegate_ setProgressPercent:1];
998 /* Database Interface {{{ */
999 @interface Database : NSObject {
1000 pkgCacheFile cache_;
1001 pkgDepCache::Policy *policy_;
1002 pkgRecords *records_;
1003 pkgProblemResolver *resolver_;
1004 pkgAcquire *fetcher_;
1006 SPtr<pkgPackageManager> manager_;
1007 pkgSourceList *list_;
1009 NSMutableDictionary *sources_;
1010 NSMutableArray *packages_;
1012 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
1021 + (Database *) sharedInstance;
1023 - (void) _readCydia:(NSNumber *)fd;
1024 - (void) _readStatus:(NSNumber *)fd;
1025 - (void) _readOutput:(NSNumber *)fd;
1029 - (Package *) packageWithName:(NSString *)name;
1031 - (pkgCacheFile &) cache;
1032 - (pkgDepCache::Policy *) policy;
1033 - (pkgRecords *) records;
1034 - (pkgProblemResolver *) resolver;
1035 - (pkgAcquire &) fetcher;
1036 - (pkgSourceList &) list;
1037 - (NSArray *) packages;
1038 - (NSArray *) sources;
1039 - (void) reloadData;
1047 - (void) updateWithStatus:(Status &)status;
1049 - (void) setDelegate:(id)delegate;
1050 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
1054 /* Source Class {{{ */
1055 @interface Source : NSObject {
1056 NSString *description_;
1061 NSString *distribution_;
1065 NSString *defaultIcon_;
1067 NSDictionary *record_;
1071 - (Source *) initWithMetaIndex:(metaIndex *)index;
1073 - (NSComparisonResult) compareByNameAndType:(Source *)source;
1075 - (NSDictionary *) record;
1079 - (NSString *) distribution;
1080 - (NSString *) type;
1082 - (NSString *) host;
1084 - (NSString *) name;
1085 - (NSString *) description;
1086 - (NSString *) label;
1087 - (NSString *) origin;
1088 - (NSString *) version;
1090 - (NSString *) defaultIcon;
1094 @implementation Source
1096 #define _clear(field) \
1103 _clear(distribution_)
1106 _clear(description_)
1110 _clear(defaultIcon_)
1119 + (NSArray *) _attributeKeys {
1120 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1123 - (NSArray *) attributeKeys {
1124 return [[self class] _attributeKeys];
1127 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1128 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1131 - (void) setMetaIndex:(metaIndex *)index {
1134 trusted_ = index->IsTrusted();
1136 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1137 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1138 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1140 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1141 if (dindex != NULL) {
1142 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1144 while (std::getline(release, line)) {
1145 std::string::size_type colon(line.find(':'));
1146 if (colon == std::string::npos)
1149 std::string name(line.substr(0, colon));
1150 std::string value(line.substr(colon + 1));
1151 while (!value.empty() && value[0] == ' ')
1152 value = value.substr(1);
1154 if (name == "Default-Icon")
1155 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1156 else if (name == "Description")
1157 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1158 else if (name == "Label")
1159 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1160 else if (name == "Origin")
1161 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1162 else if (name == "Version")
1163 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1167 record_ = [Sources_ objectForKey:[self key]];
1169 record_ = [record_ retain];
1172 - (Source *) initWithMetaIndex:(metaIndex *)index {
1173 if ((self = [super init]) != nil) {
1174 [self setMetaIndex:index];
1178 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1179 NSDictionary *lhr = [self record];
1180 NSDictionary *rhr = [source record];
1183 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1185 NSString *lhs = [self name];
1186 NSString *rhs = [source name];
1188 if ([lhs length] != 0 && [rhs length] != 0) {
1189 unichar lhc = [lhs characterAtIndex:0];
1190 unichar rhc = [rhs characterAtIndex:0];
1192 if (isalpha(lhc) && !isalpha(rhc))
1193 return NSOrderedAscending;
1194 else if (!isalpha(lhc) && isalpha(rhc))
1195 return NSOrderedDescending;
1198 return [lhs compare:rhs options:LaxCompareOptions_];
1201 - (NSDictionary *) record {
1209 - (NSString *) uri {
1213 - (NSString *) distribution {
1214 return distribution_;
1217 - (NSString *) type {
1221 - (NSString *) key {
1222 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1225 - (NSString *) host {
1226 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1229 - (NSString *) name {
1230 return origin_ == nil ? [self host] : origin_;
1233 - (NSString *) description {
1234 return description_;
1237 - (NSString *) label {
1238 return label_ == nil ? [self host] : label_;
1241 - (NSString *) origin {
1245 - (NSString *) version {
1249 - (NSString *) defaultIcon {
1250 return defaultIcon_;
1255 /* Relationship Class {{{ */
1256 @interface Relationship : NSObject {
1261 - (NSString *) type;
1263 - (NSString *) name;
1267 @implementation Relationship
1275 - (NSString *) type {
1283 - (NSString *) name {
1290 /* Package Class {{{ */
1291 @interface Package : NSObject {
1292 pkgCache::PkgIterator iterator_;
1293 _transient Database *database_;
1294 pkgCache::VerIterator version_;
1295 pkgCache::VerFileIterator file_;
1303 NSString *installed_;
1309 NSString *depiction_;
1310 NSString *homepage_;
1316 NSArray *relationships_;
1319 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1320 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1322 - (pkgCache::PkgIterator) iterator;
1324 - (NSString *) section;
1325 - (NSString *) simpleSection;
1329 - (Address *) maintainer;
1331 - (NSString *) description;
1334 - (NSMutableDictionary *) metadata;
1336 - (BOOL) subscribed;
1339 - (NSString *) latest;
1340 - (NSString *) installed;
1343 - (BOOL) upgradableAndEssential:(BOOL)essential;
1346 - (BOOL) unfiltered;
1350 - (BOOL) halfConfigured;
1351 - (BOOL) halfInstalled;
1353 - (NSString *) mode;
1356 - (NSString *) name;
1357 - (NSString *) tagline;
1359 - (NSString *) homepage;
1360 - (NSString *) depiction;
1361 - (Address *) author;
1363 - (NSArray *) files;
1364 - (NSArray *) relationships;
1365 - (NSArray *) warnings;
1366 - (NSArray *) applications;
1368 - (Source *) source;
1369 - (NSString *) role;
1371 - (BOOL) matches:(NSString *)text;
1373 - (bool) hasSupportingRole;
1374 - (BOOL) hasTag:(NSString *)tag;
1375 - (NSString *) primaryPurpose;
1376 - (NSArray *) purposes;
1378 - (NSComparisonResult) compareByName:(Package *)package;
1379 - (NSComparisonResult) compareBySection:(Package *)package;
1381 - (uint32_t) compareForChanges;
1386 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
1387 - (bool) isInstalledAndVisible:(NSNumber *)number;
1388 - (bool) isVisiblyUninstalledInSection:(NSString *)section;
1389 - (bool) isVisibleInSource:(Source *)source;
1393 @implementation Package
1399 if (section_ != nil)
1403 if (installed_ != nil)
1404 [installed_ release];
1412 if (depiction_ != nil)
1413 [depiction_ release];
1414 if (homepage_ != nil)
1415 [homepage_ release];
1416 if (sponsor_ != nil)
1425 if (relationships_ != nil)
1426 [relationships_ release];
1431 + (NSArray *) _attributeKeys {
1432 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1435 - (NSArray *) attributeKeys {
1436 return [[self class] _attributeKeys];
1439 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1440 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1443 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1444 if ((self = [super init]) != nil) { _profile(Package$initWithIterator)
1445 iterator_ = iterator;
1446 database_ = database;
1448 _profile(Package$initWithIterator$Control)
1451 _profile(Package$initWithIterator$Version)
1452 version_ = [database_ policy]->GetCandidateVer(iterator_);
1455 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1457 _profile(Package$initWithIterator$Latest)
1458 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1461 pkgCache::VerIterator current;
1462 NSString *installed;
1464 _profile(Package$initWithIterator$Current)
1465 current = iterator_.CurrentVer();
1466 installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1469 _profile(Package$initWithIterator$Installed)
1470 installed_ = [StripVersion(installed) retain];
1473 _profile(Package$initWithIterator$File)
1474 if (!version_.end())
1475 file_ = version_.FileList();
1477 pkgCache &cache([database_ cache]);
1478 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1482 _profile(Package$initWithIterator$Name)
1483 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1487 _profile(Package$initWithIterator$Parse)
1488 pkgRecords::Parser *parser;
1490 _profile(Package$initWithIterator$Parse$Lookup)
1491 parser = &[database_ records]->Lookup(file_);
1494 const char *begin, *end;
1495 parser->GetRec(begin, end);
1497 NSString *website(nil);
1498 NSString *sponsor(nil);
1499 NSString *author(nil);
1508 {"depiction", &depiction_},
1509 {"homepage", &homepage_},
1510 {"website", &website},
1511 {"sponsor", &sponsor},
1512 {"author", &author},
1516 while (begin != end)
1517 if (*begin == '\n') {
1520 } else if (isblank(*begin)) next: {
1521 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1524 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1525 const char *name(begin);
1526 size_t size(colon - begin);
1528 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1531 const char *stop(begin == NULL ? end : begin);
1532 while (stop[-1] == '\r')
1534 while (++colon != stop && isblank(*colon));
1536 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1537 if (strncasecmp(names[i].name_, name, size) == 0) {
1540 _profile(Package$initWithIterator$Parse$Value)
1541 value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
1544 *names[i].value_ = value;
1554 _profile(Package$initWithIterator$Parse$Retain)
1556 name_ = [name_ retain];
1557 _profile(Package$initWithIterator$Parse$Tagline)
1558 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1561 icon_ = [icon_ retain];
1562 if (depiction_ != nil)
1563 depiction_ = [depiction_ retain];
1564 if (homepage_ == nil)
1565 homepage_ = website;
1566 if ([homepage_ isEqualToString:depiction_])
1568 if (homepage_ != nil)
1569 homepage_ = [homepage_ retain];
1571 sponsor_ = [[Address addressWithString:sponsor] retain];
1573 author_ = [[Address addressWithString:author] retain];
1575 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1579 _profile(Package$initWithIterator$Tags)
1581 for (NSString *tag in tags_)
1582 if ([tag hasPrefix:@"role::"]) {
1583 role_ = [[tag substringFromIndex:6] retain];
1588 NSString *solid(latest == nil ? installed : latest);
1589 bool changed(false);
1591 NSString *key([id_ lowercaseString]);
1593 _profile(Package$initWithIterator$Metadata)
1594 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1595 if (metadata == nil) {
1596 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1601 [metadata setObject:solid forKey:@"LastVersion"];
1604 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1605 NSDate *last([metadata objectForKey:@"LastSeen"]);
1606 NSString *version([metadata objectForKey:@"LastVersion"]);
1609 first = last == nil ? now_ : last;
1610 [metadata setObject:first forKey:@"FirstSeen"];
1615 if (version == nil) {
1616 [metadata setObject:solid forKey:@"LastVersion"];
1618 } else if (![version isEqualToString:solid]) {
1619 [metadata setObject:solid forKey:@"LastVersion"];
1621 [metadata setObject:last forKey:@"LastSeen"];
1627 [Packages_ setObject:metadata forKey:key];
1634 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1635 return [[[Package alloc]
1636 initWithIterator:iterator
1641 - (pkgCache::PkgIterator) iterator {
1645 - (NSString *) section {
1646 if (section_ != nil)
1649 const char *section = iterator_.Section();
1650 if (section == NULL)
1653 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1656 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1657 if (NSString *rename = [value objectForKey:@"Rename"]) {
1662 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1666 - (NSString *) simpleSection {
1667 if (NSString *section = [self section])
1668 return Simplify(section);
1673 - (NSString *) uri {
1676 pkgIndexFile *index;
1677 pkgCache::PkgFileIterator file(file_.File());
1678 if (![database_ list].FindIndex(file, index))
1680 return [NSString stringWithUTF8String:iterator_->Path];
1681 //return [NSString stringWithUTF8String:file.Site()];
1682 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1686 - (Address *) maintainer {
1689 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1690 const std::string &maintainer(parser->Maintainer());
1691 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
1695 return version_.end() ? 0 : version_->InstalledSize;
1698 - (NSString *) description {
1701 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1702 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1704 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1705 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1706 if ([lines count] < 2)
1709 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1710 for (size_t i(1), e([lines count]); i != e; ++i) {
1711 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1712 [trimmed addObject:trim];
1715 return [trimmed componentsJoinedByString:@"\n"];
1719 _profile(Package$index)
1720 NSString *name([self name]);
1721 if ([name length] == 0)
1723 unichar character([name characterAtIndex:0]);
1724 if (!isalpha(character))
1730 - (NSMutableDictionary *) metadata {
1731 return [Packages_ objectForKey:[id_ lowercaseString]];
1735 NSDictionary *metadata([self metadata]);
1736 if ([self subscribed])
1737 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1739 return [metadata objectForKey:@"FirstSeen"];
1742 - (BOOL) subscribed {
1743 NSDictionary *metadata([self metadata]);
1744 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1745 return [subscribed boolValue];
1751 NSDictionary *metadata([self metadata]);
1752 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1753 return [ignored boolValue];
1758 - (NSString *) latest {
1762 - (NSString *) installed {
1767 return !version_.end();
1770 - (BOOL) upgradableAndEssential:(BOOL)essential {
1771 pkgCache::VerIterator current = iterator_.CurrentVer();
1775 value = essential && [self essential] && [self visible];
1777 value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
1781 - (BOOL) essential {
1782 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1786 return [database_ cache][iterator_].InstBroken();
1789 - (BOOL) unfiltered {
1790 NSString *section = [self section];
1791 return section == nil || isSectionVisible(section);
1795 return [self hasSupportingRole] && [self unfiltered];
1799 unsigned char current = iterator_->CurrentState;
1800 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1803 - (BOOL) halfConfigured {
1804 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1807 - (BOOL) halfInstalled {
1808 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1812 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1813 return state.Mode != pkgDepCache::ModeKeep;
1816 - (NSString *) mode {
1817 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1819 switch (state.Mode) {
1820 case pkgDepCache::ModeDelete:
1821 if ((state.iFlags & pkgDepCache::Purge) != 0)
1825 case pkgDepCache::ModeKeep:
1826 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1830 case pkgDepCache::ModeInstall:
1831 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1832 return @"Reinstall";
1833 else switch (state.Status) {
1835 return @"Downgrade";
1841 return @"New Install";
1854 - (NSString *) name {
1855 return name_ == nil ? id_ : name_;
1858 - (NSString *) tagline {
1862 - (UIImage *) icon {
1863 NSString *section = [self simpleSection];
1867 if ([icon_ hasPrefix:@"file:///"])
1868 icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
1869 if (icon == nil) if (section != nil)
1870 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1871 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
1872 if ([dicon hasPrefix:@"file:///"])
1873 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
1875 icon = [UIImage applicationImageNamed:@"unknown.png"];
1879 - (NSString *) homepage {
1883 - (NSString *) depiction {
1887 - (Address *) sponsor {
1891 - (Address *) author {
1895 - (NSArray *) files {
1896 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1897 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1900 fin.open([path UTF8String]);
1905 while (std::getline(fin, line))
1906 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1911 - (NSArray *) relationships {
1912 return relationships_;
1915 - (NSArray *) warnings {
1916 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1917 const char *name(iterator_.Name());
1919 size_t length(strlen(name));
1920 if (length < 2) invalid:
1921 [warnings addObject:@"illegal package identifier"];
1922 else for (size_t i(0); i != length; ++i)
1924 /* XXX: technically this is not allowed */
1925 (name[i] < 'A' || name[i] > 'Z') &&
1926 (name[i] < 'a' || name[i] > 'z') &&
1927 (name[i] < '0' || name[i] > '9') &&
1928 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1931 if (strcmp(name, "cydia") != 0) {
1933 bool _private = false;
1936 bool repository = [[self section] isEqualToString:@"Repositories"];
1938 if (NSArray *files = [self files])
1939 for (NSString *file in files)
1940 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1942 else if (!_private && [file isEqualToString:@"/private"])
1944 else if (!stash && [file isEqualToString:@"/var/stash"])
1947 /* XXX: this is not sensitive enough. only some folders are valid. */
1948 if (cydia && !repository)
1949 [warnings addObject:@"files installed into Cydia.app"];
1951 [warnings addObject:@"files installed with /private/*"];
1953 [warnings addObject:@"files installed to /var/stash"];
1956 return [warnings count] == 0 ? nil : warnings;
1959 - (NSArray *) applications {
1960 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1962 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1964 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1965 if (NSArray *files = [self files])
1966 for (NSString *file in files)
1967 if (application_r(file)) {
1968 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1969 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1970 if ([id isEqualToString:me])
1973 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1975 display = application_r[1];
1977 NSString *bundle([file stringByDeletingLastPathComponent]);
1978 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1979 if (icon == nil || [icon length] == 0)
1981 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1983 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1984 [applications addObject:application];
1986 [application addObject:id];
1987 [application addObject:display];
1988 [application addObject:url];
1991 return [applications count] == 0 ? nil : applications;
1994 - (Source *) source {
1996 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
2003 - (NSString *) role {
2007 - (BOOL) matches:(NSString *)text {
2013 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
2014 if (range.location != NSNotFound)
2017 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
2018 if (range.location != NSNotFound)
2021 range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
2022 if (range.location != NSNotFound)
2028 - (bool) hasSupportingRole {
2031 if ([role_ isEqualToString:@"enduser"])
2033 if ([Role_ isEqualToString:@"User"])
2035 if ([role_ isEqualToString:@"hacker"])
2037 if ([Role_ isEqualToString:@"Hacker"])
2039 if ([role_ isEqualToString:@"developer"])
2041 if ([Role_ isEqualToString:@"Developer"])
2046 - (BOOL) hasTag:(NSString *)tag {
2047 return tags_ == nil ? NO : [tags_ containsObject:tag];
2050 - (NSString *) primaryPurpose {
2051 for (NSString *tag in tags_)
2052 if ([tag hasPrefix:@"purpose::"])
2053 return [tag substringFromIndex:9];
2057 - (NSArray *) purposes {
2058 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2059 for (NSString *tag in tags_)
2060 if ([tag hasPrefix:@"purpose::"])
2061 [purposes addObject:[tag substringFromIndex:9]];
2062 return [purposes count] == 0 ? nil : purposes;
2065 - (NSComparisonResult) compareByName:(Package *)package {
2066 NSString *lhs = [self name];
2067 NSString *rhs = [package name];
2069 if ([lhs length] != 0 && [rhs length] != 0) {
2070 unichar lhc = [lhs characterAtIndex:0];
2071 unichar rhc = [rhs characterAtIndex:0];
2073 if (isalpha(lhc) && !isalpha(rhc))
2074 return NSOrderedAscending;
2075 else if (!isalpha(lhc) && isalpha(rhc))
2076 return NSOrderedDescending;
2079 return [lhs compare:rhs options:LaxCompareOptions_];
2082 - (NSComparisonResult) compareBySection:(Package *)package {
2083 NSString *lhs = [self section];
2084 NSString *rhs = [package section];
2086 if (lhs == NULL && rhs != NULL)
2087 return NSOrderedAscending;
2088 else if (lhs != NULL && rhs == NULL)
2089 return NSOrderedDescending;
2090 else if (lhs != NULL && rhs != NULL) {
2091 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
2092 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
2095 return NSOrderedSame;
2098 - (uint32_t) compareForChanges {
2103 uint32_t timestamp : 30;
2104 uint32_t ignored : 1;
2105 uint32_t upgradable : 1;
2109 bool upgradable([self upgradableAndEssential:YES]);
2110 value.bits.upgradable = upgradable ? 1 : 0;
2113 value.bits.timestamp = 0;
2114 value.bits.ignored = [self ignored] ? 0 : 1;
2115 value.bits.upgradable = 1;
2117 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2118 value.bits.ignored = 0;
2119 value.bits.upgradable = 0;
2122 return _not(uint32_t) - value.key;
2126 pkgProblemResolver *resolver = [database_ resolver];
2127 resolver->Clear(iterator_);
2128 resolver->Protect(iterator_);
2129 pkgCacheFile &cache([database_ cache]);
2130 cache->MarkInstall(iterator_, false);
2131 pkgDepCache::StateCache &state((*cache)[iterator_]);
2132 if (!state.Install())
2133 cache->SetReInstall(iterator_, true);
2137 pkgProblemResolver *resolver = [database_ resolver];
2138 resolver->Clear(iterator_);
2139 resolver->Protect(iterator_);
2140 resolver->Remove(iterator_);
2141 [database_ cache]->MarkDelete(iterator_, true);
2144 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
2145 _profile(Package$isUnfilteredAndSearchedForBy)
2148 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2149 value &= [self unfiltered];
2152 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2153 value &= [self matches:search];
2160 - (bool) isInstalledAndVisible:(NSNumber *)number {
2161 return (![number boolValue] || [self visible]) && [self installed] != nil;
2164 - (bool) isVisiblyUninstalledInSection:(NSString *)name {
2165 NSString *section = [self section];
2169 [self installed] == nil && (
2171 section == nil && [name length] == 0 ||
2172 [name isEqualToString:section]
2176 - (bool) isVisibleInSource:(Source *)source {
2177 return [self source] == source && [self visible];
2182 /* Section Class {{{ */
2183 @interface Section : NSObject {
2190 - (NSComparisonResult) compareByName:(Section *)section;
2191 - (Section *) initWithName:(NSString *)name;
2192 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2193 - (Section *) initWithIndex:(unichar)index row:(size_t)row;
2194 - (NSString *) name;
2198 - (void) addToCount;
2202 @implementation Section
2209 - (NSComparisonResult) compareByName:(Section *)section {
2210 NSString *lhs = [self name];
2211 NSString *rhs = [section name];
2213 if ([lhs length] != 0 && [rhs length] != 0) {
2214 unichar lhc = [lhs characterAtIndex:0];
2215 unichar rhc = [rhs characterAtIndex:0];
2217 if (isalpha(lhc) && !isalpha(rhc))
2218 return NSOrderedAscending;
2219 else if (!isalpha(lhc) && isalpha(rhc))
2220 return NSOrderedDescending;
2223 return [lhs compare:rhs options:LaxCompareOptions_];
2226 - (Section *) initWithName:(NSString *)name {
2227 return [self initWithName:name row:0];
2230 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2231 if ((self = [super init]) != nil) {
2232 name_ = [name retain];
2238 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
2239 if ((self = [super init]) != nil) {
2240 name_ = [[NSString stringWithCharacters:&index length:1] retain];
2246 - (NSString *) name {
2262 - (void) addToCount {
2270 static NSArray *Finishes_;
2272 /* Database Implementation {{{ */
2273 @implementation Database
2275 + (Database *) sharedInstance {
2276 static Database *instance;
2277 if (instance == nil)
2278 instance = [[Database alloc] init];
2287 - (void) _readCydia:(NSNumber *)fd { _pooled
2288 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2289 std::istream is(&ib);
2292 static Pcre finish_r("^finish:([^:]*)$");
2294 while (std::getline(is, line)) {
2295 const char *data(line.c_str());
2296 size_t size = line.size();
2297 lprintf("C:%s\n", data);
2299 if (finish_r(data, size)) {
2300 NSString *finish = finish_r[1];
2301 int index = [Finishes_ indexOfObject:finish];
2302 if (index != INT_MAX && index > Finish_)
2310 - (void) _readStatus:(NSNumber *)fd { _pooled
2311 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2312 std::istream is(&ib);
2315 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2316 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2318 while (std::getline(is, line)) {
2319 const char *data(line.c_str());
2320 size_t size = line.size();
2321 lprintf("S:%s\n", data);
2323 if (conffile_r(data, size)) {
2324 [delegate_ setConfigurationData:conffile_r[1]];
2325 } else if (strncmp(data, "status: ", 8) == 0) {
2326 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2327 [delegate_ setProgressTitle:string];
2328 } else if (pmstatus_r(data, size)) {
2329 std::string type([pmstatus_r[1] UTF8String]);
2330 NSString *id = pmstatus_r[2];
2332 float percent([pmstatus_r[3] floatValue]);
2333 [delegate_ setProgressPercent:(percent / 100)];
2335 NSString *string = pmstatus_r[4];
2337 if (type == "pmerror")
2338 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2339 withObject:[NSArray arrayWithObjects:string, id, nil]
2342 else if (type == "pmstatus") {
2343 [delegate_ setProgressTitle:string];
2344 } else if (type == "pmconffile")
2345 [delegate_ setConfigurationData:string];
2346 else _assert(false);
2347 } else _assert(false);
2353 - (void) _readOutput:(NSNumber *)fd { _pooled
2354 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2355 std::istream is(&ib);
2358 while (std::getline(is, line)) {
2359 lprintf("O:%s\n", line.c_str());
2360 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2370 - (Package *) packageWithName:(NSString *)name {
2371 if (static_cast<pkgDepCache *>(cache_) == NULL)
2373 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2374 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2377 - (Database *) init {
2378 if ((self = [super init]) != nil) {
2385 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2386 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2390 _assert(pipe(fds) != -1);
2393 _config->Set("APT::Keep-Fds::", cydiafd_);
2394 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2397 detachNewThreadSelector:@selector(_readCydia:)
2399 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2402 _assert(pipe(fds) != -1);
2406 detachNewThreadSelector:@selector(_readStatus:)
2408 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2411 _assert(pipe(fds) != -1);
2412 _assert(dup2(fds[0], 0) != -1);
2413 _assert(close(fds[0]) != -1);
2415 input_ = fdopen(fds[1], "a");
2417 _assert(pipe(fds) != -1);
2418 _assert(dup2(fds[1], 1) != -1);
2419 _assert(close(fds[1]) != -1);
2422 detachNewThreadSelector:@selector(_readOutput:)
2424 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2429 - (pkgCacheFile &) cache {
2433 - (pkgDepCache::Policy *) policy {
2437 - (pkgRecords *) records {
2441 - (pkgProblemResolver *) resolver {
2445 - (pkgAcquire &) fetcher {
2449 - (pkgSourceList &) list {
2453 - (NSArray *) packages {
2457 - (NSArray *) sources {
2458 return [sources_ allValues];
2461 - (NSArray *) issues {
2462 if (cache_->BrokenCount() == 0)
2465 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2467 for (Package *package in packages_) {
2468 if (![package broken])
2470 pkgCache::PkgIterator pkg([package iterator]);
2472 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2473 [entry addObject:[package name]];
2474 [issues addObject:entry];
2476 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2480 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2481 pkgCache::DepIterator start;
2482 pkgCache::DepIterator end;
2483 dep.GlobOr(start, end); // ++dep
2485 if (!cache_->IsImportantDep(end))
2487 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2490 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2491 [entry addObject:failure];
2492 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2494 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2495 [failure addObject:[package name]];
2497 pkgCache::PkgIterator target(start.TargetPkg());
2498 if (target->ProvidesList != 0)
2499 [failure addObject:@"?"];
2501 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2503 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2504 else if (!cache_[target].CandidateVerIter(cache_).end())
2505 [failure addObject:@"-"];
2506 else if (target->ProvidesList == 0)
2507 [failure addObject:@"!"];
2509 [failure addObject:@"%"];
2513 if (start.TargetVer() != 0)
2514 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2525 - (void) reloadData { _pooled
2545 if (!cache_.Open(progress_, true)) {
2547 if (!_error->PopMessage(error))
2550 lprintf("cache_.Open():[%s]\n", error.c_str());
2552 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2553 [delegate_ repairWithSelector:@selector(configure)];
2554 else if (error == "The package lists or status file could not be parsed or opened.")
2555 [delegate_ repairWithSelector:@selector(update)];
2556 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2557 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2558 // else if (error == "The list of sources could not be read.")
2559 else _assert(false);
2565 now_ = [[NSDate date] retain];
2567 policy_ = new pkgDepCache::Policy();
2568 records_ = new pkgRecords(cache_);
2569 resolver_ = new pkgProblemResolver(cache_);
2570 fetcher_ = new pkgAcquire(&status_);
2573 list_ = new pkgSourceList();
2574 _assert(list_->ReadMainList());
2576 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2577 _assert(pkgApplyStatus(cache_));
2579 if (cache_->BrokenCount() != 0) {
2580 _assert(pkgFixBroken(cache_));
2581 _assert(cache_->BrokenCount() == 0);
2582 _assert(pkgMinimizeUpgrade(cache_));
2585 [sources_ removeAllObjects];
2586 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2587 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2588 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2590 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2591 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2595 [packages_ removeAllObjects];
2597 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2598 if (Package *package = [Package packageWithIterator:iterator database:self])
2599 [packages_ addObject:package];
2601 [packages_ sortUsingSelector:@selector(compareByName:)];
2604 _config->Set("Acquire::http::Timeout", 15);
2605 _config->Set("Acquire::http::MaxParallel", 4);
2608 - (void) configure {
2609 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2610 system([dpkg UTF8String]);
2618 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2619 _assert(!_error->PendingError());
2622 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2625 public pkgArchiveCleaner
2628 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2633 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2635 while (_error->PopMessage(error))
2636 lprintf("ArchiveCleaner: %s\n", error.c_str());
2641 pkgRecords records(cache_);
2643 lock_ = new FileFd();
2644 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2645 _assert(!_error->PendingError());
2648 // XXX: explain this with an error message
2649 _assert(list.ReadMainList());
2651 manager_ = (_system->CreatePM(cache_));
2652 _assert(manager_->GetArchives(fetcher_, &list, &records));
2653 _assert(!_error->PendingError());
2657 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2659 _assert(list.ReadMainList());
2660 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2661 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2664 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2669 bool failed = false;
2670 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2671 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2674 std::string uri = (*item)->DescURI();
2675 std::string error = (*item)->ErrorText;
2677 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2680 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2681 withObject:[NSArray arrayWithObjects:
2682 [NSString stringWithUTF8String:error.c_str()],
2694 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2696 if (_error->PendingError()) {
2701 if (result == pkgPackageManager::Failed) {
2706 if (result != pkgPackageManager::Completed) {
2711 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2713 _assert(list.ReadMainList());
2714 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2715 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2718 if (![before isEqualToArray:after])
2723 _assert(pkgDistUpgrade(cache_));
2727 [self updateWithStatus:status_];
2730 - (void) updateWithStatus:(Status &)status {
2732 _assert(list.ReadMainList());
2735 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2736 _assert(!_error->PendingError());
2738 pkgAcquire fetcher(&status);
2739 _assert(list.GetIndexes(&fetcher));
2741 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2742 bool failed = false;
2743 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2744 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2745 (*item)->Finished();
2749 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2750 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2751 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2754 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2759 - (void) setDelegate:(id)delegate {
2760 delegate_ = delegate;
2761 status_.setDelegate(delegate);
2762 progress_.setDelegate(delegate);
2765 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2766 pkgIndexFile *index(NULL);
2767 list_->FindIndex(file, index);
2768 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2774 /* PopUp Windows {{{ */
2775 @interface PopUpView : UIView {
2776 _transient id delegate_;
2777 UITransitionView *transition_;
2782 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2786 @implementation PopUpView
2789 [transition_ setDelegate:nil];
2790 [transition_ release];
2796 [transition_ transition:UITransitionPushFromTop toView:nil];
2799 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2800 if (from != nil && to == nil)
2801 [self removeFromSuperview];
2804 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2805 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2806 delegate_ = delegate;
2808 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2809 [self addSubview:transition_];
2811 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2813 [view addSubview:self];
2815 [transition_ setDelegate:self];
2817 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2818 [transition_ transition:UITransitionNone toView:blank];
2819 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2826 /* Mail Composition {{{ */
2827 @interface MailToView : PopUpView {
2828 MailComposeController *controller_;
2831 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2835 @implementation MailToView
2838 [controller_ release];
2842 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2846 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2847 NSLog(@"did:%@", delivery);
2848 // [UIApp setStatusBarShowsProgress:NO];
2849 if ([controller error]){
2850 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2851 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2852 [mailAlertSheet setBodyText:[controller error]];
2853 [mailAlertSheet popupAlertAnimated:YES];
2857 - (void) showError {
2858 NSLog(@"%@", [controller_ error]);
2859 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2860 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2861 [mailAlertSheet setBodyText:[controller_ error]];
2862 [mailAlertSheet popupAlertAnimated:YES];
2865 - (void) deliverMessage { _pooled
2869 if (![controller_ deliverMessage])
2870 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2873 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2874 if ([controller_ needsDelivery])
2875 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2880 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2881 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2882 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2883 [controller_ setDelegate:self];
2884 [controller_ initializeUI];
2885 [controller_ setupForURL:url];
2887 UIView *view([controller_ view]);
2888 [overlay_ addSubview:view];
2894 /* Confirmation View {{{ */
2895 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2896 if (!iterator.end())
2897 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2898 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2900 pkgCache::PkgIterator package(dep.TargetPkg());
2903 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2910 @protocol ConfirmationViewDelegate
2915 @interface ConfirmationView : BrowserView {
2916 _transient Database *database_;
2917 UIActionSheet *essential_;
2924 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2928 @implementation ConfirmationView
2935 if (essential_ != nil)
2936 [essential_ release];
2942 [book_ popFromSuperviewAnimated:YES];
2945 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2946 NSString *context([sheet context]);
2948 if ([context isEqualToString:@"remove"]) {
2956 [delegate_ confirm];
2963 } else if ([context isEqualToString:@"unable"]) {
2967 [super alertSheet:sheet buttonClicked:button];
2970 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2971 [window setValue:changes_ forKey:@"changes"];
2972 [window setValue:issues_ forKey:@"issues"];
2973 [window setValue:sizes_ forKey:@"sizes"];
2974 [super webView:sender didClearWindowObject:window forFrame:frame];
2977 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2978 if ((self = [super initWithBook:book]) != nil) {
2979 database_ = database;
2981 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2982 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2983 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2984 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2985 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2989 pkgDepCache::Policy *policy([database_ policy]);
2991 pkgCacheFile &cache([database_ cache]);
2992 NSArray *packages = [database_ packages];
2993 for (Package *package in packages) {
2994 pkgCache::PkgIterator iterator = [package iterator];
2995 pkgDepCache::StateCache &state(cache[iterator]);
2997 NSString *name([package name]);
2999 if (state.NewInstall())
3000 [installing addObject:name];
3001 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
3002 [reinstalling addObject:name];
3003 else if (state.Upgrade())
3004 [upgrading addObject:name];
3005 else if (state.Downgrade())
3006 [downgrading addObject:name];
3007 else if (state.Delete()) {
3008 if ([package essential])
3010 [removing addObject:name];
3013 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
3014 substrate_ |= DepSubstrate(iterator.CurrentVer());
3019 else if (Advanced_ || true) {
3020 essential_ = [[UIActionSheet alloc]
3021 initWithTitle:@"Removing Essentials"
3022 buttons:[NSArray arrayWithObjects:
3023 @"Cancel Operation (Safe)",
3024 @"Force Removal (Unsafe)",
3026 defaultButtonIndex:0
3032 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
3034 [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."];
3036 essential_ = [[UIActionSheet alloc]
3037 initWithTitle:@"Unable to Comply"
3038 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3039 defaultButtonIndex:0
3044 [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."];
3047 changes_ = [[NSArray alloc] initWithObjects:
3055 issues_ = [database_ issues];
3057 issues_ = [issues_ retain];
3059 sizes_ = [[NSArray alloc] initWithObjects:
3060 SizeString([database_ fetcher].FetchNeeded()),
3061 SizeString([database_ fetcher].PartialPresent()),
3062 SizeString([database_ cache]->UsrSize()),
3065 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
3069 - (NSString *) backButtonTitle {
3073 - (NSString *) leftButtonTitle {
3077 - (id) _rightButtonTitle {
3078 #if AlwaysReload || IgnoreInstall
3081 return issues_ == nil ? @"Confirm" : nil;
3085 - (void) _leftButtonClicked {
3090 - (void) _rightButtonClicked {
3092 return [super _rightButtonClicked];
3094 if (essential_ != nil)
3095 [essential_ popupAlertAnimated:YES];
3099 [delegate_ confirm];
3107 /* Progress Data {{{ */
3108 @interface ProgressData : NSObject {
3114 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
3121 @implementation ProgressData
3123 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
3124 if ((self = [super init]) != nil) {
3125 selector_ = selector;
3145 /* Progress View {{{ */
3146 @interface ProgressView : UIView <
3147 ConfigurationDelegate,
3150 _transient Database *database_;
3152 UIView *background_;
3153 UITransitionView *transition_;
3155 UINavigationBar *navbar_;
3156 UIProgressBar *progress_;
3157 UITextView *output_;
3158 UITextLabel *status_;
3159 UIPushButton *close_;
3162 SHA1SumValue springlist_;
3163 SHA1SumValue notifyconf_;
3164 SHA1SumValue sandplate_;
3167 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
3169 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
3170 - (void) setContentView:(UIView *)view;
3173 - (void) _retachThread;
3174 - (void) _detachNewThreadData:(ProgressData *)data;
3175 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
3181 @protocol ProgressViewDelegate
3182 - (void) progressViewIsComplete:(ProgressView *)sender;
3185 @implementation ProgressView
3188 [transition_ setDelegate:nil];
3189 [navbar_ setDelegate:nil];
3192 if (background_ != nil)
3193 [background_ release];
3194 [transition_ release];
3197 [progress_ release];
3204 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3205 if (bootstrap_ && from == overlay_ && to == view_)
3209 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3210 if ((self = [super initWithFrame:frame]) != nil) {
3211 database_ = database;
3212 delegate_ = delegate;
3214 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3215 [transition_ setDelegate:self];
3217 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3220 [overlay_ setBackgroundColor:[UIColor blackColor]];
3222 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3223 [background_ setBackgroundColor:[UIColor blackColor]];
3224 [self addSubview:background_];
3227 [self addSubview:transition_];
3229 CGSize navsize = [UINavigationBar defaultSize];
3230 CGRect navrect = {{0, 0}, navsize};
3232 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3233 [overlay_ addSubview:navbar_];
3235 [navbar_ setBarStyle:1];
3236 [navbar_ setDelegate:self];
3238 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3239 [navbar_ pushNavigationItem:navitem];
3241 CGRect bounds = [overlay_ bounds];
3242 CGSize prgsize = [UIProgressBar defaultSize];
3245 (bounds.size.width - prgsize.width) / 2,
3246 bounds.size.height - prgsize.height - 20
3249 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3250 [progress_ setStyle:0];
3252 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3254 bounds.size.height - prgsize.height - 50,
3255 bounds.size.width - 20,
3259 [status_ setColor:[UIColor whiteColor]];
3260 [status_ setBackgroundColor:[UIColor clearColor]];
3262 [status_ setCentersHorizontally:YES];
3263 //[status_ setFont:font];
3266 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3268 navrect.size.height + 20,
3269 bounds.size.width - 20,
3270 bounds.size.height - navsize.height - 62 - navrect.size.height
3274 //[output_ setTextFont:@"Courier New"];
3275 [output_ setTextSize:12];
3277 [output_ setTextColor:[UIColor whiteColor]];
3278 [output_ setBackgroundColor:[UIColor clearColor]];
3280 [output_ setMarginTop:0];
3281 [output_ setAllowsRubberBanding:YES];
3282 [output_ setEditable:NO];
3284 [overlay_ addSubview:output_];
3286 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3288 bounds.size.height - prgsize.height - 50,
3289 bounds.size.width - 20,
3293 [close_ setAutosizesToFit:NO];
3294 [close_ setDrawsShadow:YES];
3295 [close_ setStretchBackground:YES];
3296 [close_ setEnabled:YES];
3298 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3299 [close_ setTitleFont:bold];
3301 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3302 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3303 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3307 - (void) setContentView:(UIView *)view {
3308 view_ = [view retain];
3311 - (void) resetView {
3312 [transition_ transition:6 toView:view_];
3315 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3316 NSString *context([sheet context]);
3318 if ([context isEqualToString:@"error"])
3320 else if ([context isEqualToString:@"conffile"]) {
3321 FILE *input = [database_ input];
3325 fprintf(input, "N\n");
3329 fprintf(input, "Y\n");
3340 - (void) closeButtonPushed {
3349 [delegate_ suspendWithAnimation:YES];
3353 system("launchctl stop com.apple.SpringBoard");
3357 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3366 - (void) _retachThread {
3367 UINavigationItem *item = [navbar_ topItem];
3368 [item setTitle:@"Complete"];
3370 [overlay_ addSubview:close_];
3371 [progress_ removeFromSuperview];
3372 [status_ removeFromSuperview];
3374 [delegate_ progressViewIsComplete:self];
3377 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3378 MMap mmap(file, MMap::ReadOnly);
3380 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3381 if (!(sandplate_ == sha1.Result()))
3386 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3387 MMap mmap(file, MMap::ReadOnly);
3389 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3390 if (!(notifyconf_ == sha1.Result()))
3395 FileFd file(SpringBoard_, FileFd::ReadOnly);
3396 MMap mmap(file, MMap::ReadOnly);
3398 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3399 if (!(springlist_ == sha1.Result()))
3404 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3405 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3406 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3407 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3408 case 4: [close_ setTitle:@"Reboot Device"]; break;
3411 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3413 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3414 [cache autorelease];
3416 NSFileManager *manager = [NSFileManager defaultManager];
3417 NSError *error = nil;
3419 id system = [cache objectForKey:@"System"];
3424 if (stat(Cache_, &info) == -1)
3427 [system removeAllObjects];
3429 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3430 for (NSString *app in apps)
3431 if ([app hasSuffix:@".app"]) {
3432 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3433 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3434 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3436 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3437 [info setObject:path forKey:@"Path"];
3438 [info setObject:@"System" forKey:@"ApplicationType"];
3439 [system addInfoDictionary:info];
3445 [cache writeToFile:@Cache_ atomically:YES];
3447 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3449 if (chmod(Cache_, info.st_mode) == -1)
3453 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3456 notify_post("com.apple.mobile.application_installed");
3458 [delegate_ setStatusBarShowsProgress:NO];
3461 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3462 [[data target] performSelector:[data selector] withObject:[data object]];
3465 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3468 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3469 UINavigationItem *item = [navbar_ topItem];
3470 [item setTitle:title];
3472 [status_ setText:nil];
3473 [output_ setText:@""];
3474 [progress_ setProgress:0];
3476 [close_ removeFromSuperview];
3477 [overlay_ addSubview:progress_];
3478 [overlay_ addSubview:status_];
3480 [delegate_ setStatusBarShowsProgress:YES];
3484 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3485 MMap mmap(file, MMap::ReadOnly);
3487 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3488 sandplate_ = sha1.Result();
3492 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3493 MMap mmap(file, MMap::ReadOnly);
3495 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3496 notifyconf_ = sha1.Result();
3500 FileFd file(SpringBoard_, FileFd::ReadOnly);
3501 MMap mmap(file, MMap::ReadOnly);
3503 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3504 springlist_ = sha1.Result();
3507 [transition_ transition:6 toView:overlay_];
3510 detachNewThreadSelector:@selector(_detachNewThreadData:)
3512 withObject:[[ProgressData alloc]
3513 initWithSelector:selector
3520 - (void) repairWithSelector:(SEL)selector {
3522 detachNewThreadSelector:selector
3529 - (void) setConfigurationData:(NSString *)data {
3531 performSelectorOnMainThread:@selector(_setConfigurationData:)
3537 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3538 Package *package = id == nil ? nil : [database_ packageWithName:id];
3540 UIActionSheet *sheet = [[[UIActionSheet alloc]
3541 initWithTitle:(package == nil ? id : [package name])
3542 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3543 defaultButtonIndex:0
3548 [sheet setBodyText:error];
3549 [sheet popupAlertAnimated:YES];
3552 - (void) setProgressTitle:(NSString *)title {
3554 performSelectorOnMainThread:@selector(_setProgressTitle:)
3560 - (void) setProgressPercent:(float)percent {
3562 performSelectorOnMainThread:@selector(_setProgressPercent:)
3563 withObject:[NSNumber numberWithFloat:percent]
3568 - (void) startProgress {
3571 - (void) addProgressOutput:(NSString *)output {
3573 performSelectorOnMainThread:@selector(_addProgressOutput:)
3579 - (bool) isCancelling:(size_t)received {
3583 - (void) _setConfigurationData:(NSString *)data {
3584 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3586 _assert(conffile_r(data));
3588 NSString *ofile = conffile_r[1];
3589 //NSString *nfile = conffile_r[2];
3591 UIActionSheet *sheet = [[[UIActionSheet alloc]
3592 initWithTitle:@"Configuration Upgrade"
3593 buttons:[NSArray arrayWithObjects:
3594 @"Keep My Old Copy",
3595 @"Accept The New Copy",
3596 // XXX: @"See What Changed",
3598 defaultButtonIndex:0
3603 [sheet setBodyText:[NSString stringWithFormat:
3604 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3607 [sheet popupAlertAnimated:YES];
3610 - (void) _setProgressTitle:(NSString *)title {
3611 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3612 for (size_t i(0), e([words count]); i != e; ++i) {
3613 NSString *word([words objectAtIndex:i]);
3614 if (Package *package = [database_ packageWithName:word])
3615 [words replaceObjectAtIndex:i withObject:[package name]];
3618 [status_ setText:[words componentsJoinedByString:@" "]];
3621 - (void) _setProgressPercent:(NSNumber *)percent {
3622 [progress_ setProgress:[percent floatValue]];
3625 - (void) _addProgressOutput:(NSString *)output {
3626 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3627 CGSize size = [output_ contentSize];
3628 CGRect rect = {{0, size.height}, {size.width, 0}};
3629 [output_ scrollRectToVisible:rect animated:YES];
3632 - (BOOL) isRunning {
3639 /* Package Cell {{{ */
3640 @interface PackageCell : UISimpleTableCell {
3643 NSString *description_;
3647 UITextLabel *status_;
3651 - (PackageCell *) init;
3652 - (void) setPackage:(Package *)package;
3654 + (int) heightForPackage:(Package *)package;
3658 @implementation PackageCell
3660 - (void) clearPackage {
3671 if (description_ != nil) {
3672 [description_ release];
3676 if (source_ != nil) {
3681 if (badge_ != nil) {
3688 [self clearPackage];
3695 - (PackageCell *) init {
3696 if ((self = [super init]) != nil) {
3698 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3699 [status_ setBackgroundColor:[UIColor clearColor]];
3700 [status_ setFont:small];
3705 - (void) setPackage:(Package *)package {
3706 [self clearPackage];
3708 Source *source = [package source];
3709 NSString *section = [package simpleSection];
3711 icon_ = [[package icon] retain];
3713 name_ = [[package name] retain];
3714 description_ = [[package tagline] retain];
3716 NSString *label = nil;
3717 bool trusted = false;
3719 if (source != nil) {
3720 label = [source label];
3721 trusted = [source trusted];
3722 } else if ([[package id] isEqualToString:@"firmware"])
3725 label = @"Unknown/Local";
3727 NSString *from = [NSString stringWithFormat:@"from %@", label];
3729 if (section != nil && ![section isEqualToString:label])
3730 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3732 source_ = [from retain];
3734 if (NSString *purpose = [package primaryPurpose])
3735 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3736 badge_ = [badge_ retain];
3739 if (NSString *mode = [package mode]) {
3740 [badge_ setImage:[UIImage applicationImageNamed:
3741 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3744 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3745 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3746 } else if ([package half]) {
3747 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3748 [status_ setText:@"Package Damaged"];
3749 [status_ setColor:[UIColor redColor]];
3751 [badge_ setImage:nil];
3752 [status_ setText:nil];
3757 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3760 rect.size = [icon_ size];
3762 rect.size.width /= 2;
3763 rect.size.height /= 2;
3765 rect.origin.x = 25 - rect.size.width / 2;
3766 rect.origin.y = 25 - rect.size.height / 2;
3768 [icon_ drawInRect:rect];
3771 if (badge_ != nil) {
3772 CGSize size = [badge_ size];
3774 [badge_ drawAtPoint:CGPointMake(
3775 36 - size.width / 2,
3776 36 - size.height / 2
3785 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3786 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3790 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3792 [super drawContentInRect:rect selected:selected];
3795 + (int) heightForPackage:(Package *)package {
3796 NSString *tagline([package tagline]);
3797 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3799 if ([package hasMode] || [package half])
3808 /* Section Cell {{{ */
3809 @interface SectionCell : UISimpleTableCell {
3814 _UISwitchSlider *switch_;
3819 - (void) setSection:(Section *)section editing:(BOOL)editing;
3823 @implementation SectionCell
3825 - (void) clearSection {
3826 if (section_ != nil) {
3836 if (count_ != nil) {
3843 [self clearSection];
3850 if ((self = [super init]) != nil) {
3851 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3853 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3854 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3858 - (void) onSwitch:(id)sender {
3859 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3860 if (metadata == nil) {
3861 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3862 [Sections_ setObject:metadata forKey:section_];
3866 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3869 - (void) setSection:(Section *)section editing:(BOOL)editing {
3870 if (editing != editing_) {
3872 [switch_ removeFromSuperview];
3874 [self addSubview:switch_];
3878 [self clearSection];
3880 if (section == nil) {
3881 name_ = [@"All Packages" retain];
3884 section_ = [section name];
3885 if (section_ != nil)
3886 section_ = [section_ retain];
3887 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3888 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3891 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3895 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3896 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3903 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3905 CGSize size = [count_ sizeWithFont:Font14_];
3909 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3911 [super drawContentInRect:rect selected:selected];
3917 /* File Table {{{ */
3918 @interface FileTable : RVPage {
3919 _transient Database *database_;
3922 NSMutableArray *files_;
3926 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3927 - (void) setPackage:(Package *)package;
3931 @implementation FileTable
3934 if (package_ != nil)
3943 - (int) numberOfRowsInTable:(UITable *)table {
3944 return files_ == nil ? 0 : [files_ count];
3947 - (float) table:(UITable *)table heightForRow:(int)row {
3951 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3952 if (reusing == nil) {
3953 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3954 UIFont *font = [UIFont systemFontOfSize:16];
3955 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3957 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3961 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3965 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3966 if ((self = [super initWithBook:book]) != nil) {
3967 database_ = database;
3969 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3971 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3972 [self addSubview:list_];
3974 UITableColumn *column = [[[UITableColumn alloc]
3975 initWithTitle:@"Name"
3977 width:[self frame].size.width
3980 [list_ setDataSource:self];
3981 [list_ setSeparatorStyle:1];
3982 [list_ addTableColumn:column];
3983 [list_ setDelegate:self];
3984 [list_ setReusesTableCells:YES];
3988 - (void) setPackage:(Package *)package {
3989 if (package_ != nil) {
3990 [package_ autorelease];
3999 [files_ removeAllObjects];
4001 if (package != nil) {
4002 package_ = [package retain];
4003 name_ = [[package id] retain];
4005 if (NSArray *files = [package files])
4006 [files_ addObjectsFromArray:files];
4008 if ([files_ count] != 0) {
4009 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
4010 [files_ removeObjectAtIndex:0];
4011 [files_ sortUsingSelector:@selector(compareByPath:)];
4013 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
4014 [stack addObject:@"/"];
4016 for (int i(0), e([files_ count]); i != e; ++i) {
4017 NSString *file = [files_ objectAtIndex:i];
4018 while (![file hasPrefix:[stack lastObject]])
4019 [stack removeLastObject];
4020 NSString *directory = [stack lastObject];
4021 [stack addObject:[file stringByAppendingString:@"/"]];
4022 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
4023 ([stack count] - 2) * 3, "",
4024 [file substringFromIndex:[directory length]]
4033 - (void) resetViewAnimated:(BOOL)animated {
4034 [list_ resetViewAnimated:animated];
4037 - (void) reloadData {
4038 [self setPackage:[database_ packageWithName:name_]];
4039 [self reloadButtons];
4042 - (NSString *) title {
4043 return @"Installed Files";
4046 - (NSString *) backButtonTitle {
4052 /* Package View {{{ */
4053 @interface PackageView : BrowserView {
4054 _transient Database *database_;
4057 NSMutableArray *buttons_;
4060 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4061 - (void) setPackage:(Package *)package;
4065 @implementation PackageView
4068 if (package_ != nil)
4076 - (void) _clickButtonWithName:(NSString *)name {
4077 if ([name isEqualToString:@"Install"])
4078 [delegate_ installPackage:package_];
4079 else if ([name isEqualToString:@"Reinstall"])
4080 [delegate_ installPackage:package_];
4081 else if ([name isEqualToString:@"Remove"])
4082 [delegate_ removePackage:package_];
4083 else if ([name isEqualToString:@"Upgrade"])
4084 [delegate_ installPackage:package_];
4085 else _assert(false);
4088 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4089 NSString *context([sheet context]);
4091 if ([context isEqualToString:@"modify"]) {
4092 int count = [buttons_ count];
4093 _assert(count != 0);
4094 _assert(button <= count + 1);
4096 if (count != button - 1)
4097 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
4101 [super alertSheet:sheet buttonClicked:button];
4104 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4105 return [super webView:sender didFinishLoadForFrame:frame];
4108 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4109 [window setValue:package_ forKey:@"package"];
4110 [super webView:sender didClearWindowObject:window forFrame:frame];
4114 - (void) _rightButtonClicked {
4115 /*[super _rightButtonClicked];
4118 int count = [buttons_ count];
4119 _assert(count != 0);
4122 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4124 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4125 [buttons addObjectsFromArray:buttons_];
4126 [buttons addObject:@"Cancel"];
4128 [delegate_ slideUp:[[[UIActionSheet alloc]
4131 defaultButtonIndex:2
4139 - (id) _rightButtonTitle {
4140 int count = [buttons_ count];
4141 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4144 - (NSString *) backButtonTitle {
4148 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4149 if ((self = [super initWithBook:book]) != nil) {
4150 database_ = database;
4151 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4155 - (void) setPackage:(Package *)package {
4156 if (package_ != nil) {
4157 [package_ autorelease];
4166 [buttons_ removeAllObjects];
4168 if (package != nil) {
4169 package_ = [package retain];
4170 name_ = [[package id] retain];
4172 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4174 if ([package_ source] == nil);
4175 else if ([package_ upgradableAndEssential:NO])
4176 [buttons_ addObject:@"Upgrade"];
4177 else if ([package_ installed] == nil)
4178 [buttons_ addObject:@"Install"];
4180 [buttons_ addObject:@"Reinstall"];
4181 if ([package_ installed] != nil)
4182 [buttons_ addObject:@"Remove"];
4190 - (void) reloadData {
4191 [self setPackage:[database_ packageWithName:name_]];
4192 [self reloadButtons];
4197 /* Package Table {{{ */
4198 @interface PackageTable : RVPage {
4199 _transient Database *database_;
4201 NSMutableArray *packages_;
4202 NSMutableArray *sections_;
4203 UISectionList *list_;
4206 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4208 - (void) setDelegate:(id)delegate;
4210 - (void) reloadData;
4211 - (void) resetCursor;
4213 - (UISectionList *) list;
4215 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4219 @implementation PackageTable
4222 [list_ setDataSource:nil];
4225 [packages_ release];
4226 [sections_ release];
4231 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4232 return [sections_ count];
4235 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4236 return [[sections_ objectAtIndex:section] name];
4239 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4240 return [[sections_ objectAtIndex:section] row];
4243 - (int) numberOfRowsInTable:(UITable *)table {
4244 return [packages_ count];
4247 - (float) table:(UITable *)table heightForRow:(int)row {
4248 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4251 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4253 reusing = [[[PackageCell alloc] init] autorelease];
4254 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4258 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4262 - (void) tableRowSelected:(NSNotification *)notification {
4263 int row = [[notification object] selectedRow];
4267 Package *package = [packages_ objectAtIndex:row];
4268 package = [database_ packageWithName:[package id]];
4269 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4270 [view setPackage:package];
4271 [view setDelegate:delegate_];
4272 [book_ pushPage:view];
4275 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4276 if ((self = [super initWithBook:book]) != nil) {
4277 database_ = database;
4278 title_ = [title retain];
4280 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4281 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4283 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4284 [list_ setDataSource:self];
4286 UITableColumn *column = [[[UITableColumn alloc]
4287 initWithTitle:@"Name"
4289 width:[self frame].size.width
4292 UITable *table = [list_ table];
4293 [table setSeparatorStyle:1];
4294 [table addTableColumn:column];
4295 [table setDelegate:self];
4296 [table setReusesTableCells:YES];
4298 [self addSubview:list_];
4300 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4301 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4305 - (void) setDelegate:(id)delegate {
4306 delegate_ = delegate;
4309 - (bool) hasPackage:(Package *)package {
4313 - (void) reloadData {
4314 NSArray *packages = [database_ packages];
4316 [packages_ removeAllObjects];
4317 [sections_ removeAllObjects];
4319 _profile(PackageTable$reloadData$Filter)
4320 for (Package *package in packages)
4321 if ([self hasPackage:package])
4322 [packages_ addObject:package];
4325 Section *section = nil;
4327 _profile(PackageTable$reloadData$Section)
4328 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4332 _profile(PackageTable$reloadData$Section$Package)
4333 package = [packages_ objectAtIndex:offset];
4334 index = [package index];
4337 if (section == nil || [section index] != index) {
4338 _profile(PackageTable$reloadData$Section$Allocate)
4339 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4342 _profile(PackageTable$reloadData$Section$Add)
4343 [sections_ addObject:section];
4347 [section addToCount];
4351 _profile(PackageTable$reloadData$List)
4356 - (NSString *) title {
4360 - (void) resetViewAnimated:(BOOL)animated {
4361 [list_ resetViewAnimated:animated];
4364 - (void) resetCursor {
4365 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4368 - (UISectionList *) list {
4372 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4373 [list_ setShouldHideHeaderInShortLists:hide];
4378 /* Filtered Package Table {{{ */
4379 @interface FilteredPackageTable : PackageTable {
4385 - (void) setObject:(id)object;
4387 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4391 @implementation FilteredPackageTable
4399 - (void) setObject:(id)object {
4405 object_ = [object retain];
4408 - (bool) hasPackage:(Package *)package {
4409 _profile(FilteredPackageTable$hasPackage)
4410 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
4414 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4415 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4417 object_ = object == nil ? nil : [object retain];
4419 /* XXX: this is an unsafe optimization of doomy hell */
4420 Method method = class_getInstanceMethod([Package class], filter);
4421 imp_ = method_getImplementation(method);
4422 _assert(imp_ != NULL);
4431 /* Add Source View {{{ */
4432 @interface AddSourceView : RVPage {
4433 _transient Database *database_;
4436 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4440 @implementation AddSourceView
4442 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4443 if ((self = [super initWithBook:book]) != nil) {
4444 database_ = database;
4450 /* Source Cell {{{ */
4451 @interface SourceCell : UITableCell {
4454 NSString *description_;
4460 - (SourceCell *) initWithSource:(Source *)source;
4464 @implementation SourceCell
4469 [description_ release];
4474 - (SourceCell *) initWithSource:(Source *)source {
4475 if ((self = [super init]) != nil) {
4477 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4479 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4480 icon_ = [icon_ retain];
4482 origin_ = [[source name] retain];
4483 label_ = [[source uri] retain];
4484 description_ = [[source description] retain];
4488 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4490 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4497 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4501 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4505 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4507 [super drawContentInRect:rect selected:selected];
4512 /* Source Table {{{ */
4513 @interface SourceTable : RVPage {
4514 _transient Database *database_;
4515 UISectionList *list_;
4516 NSMutableArray *sources_;
4517 UIActionSheet *alert_;
4521 UIProgressHUD *hud_;
4524 //NSURLConnection *installer_;
4525 NSURLConnection *trivial_bz2_;
4526 NSURLConnection *trivial_gz_;
4527 //NSURLConnection *automatic_;
4532 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4536 @implementation SourceTable
4538 - (void) _deallocConnection:(NSURLConnection *)connection {
4539 if (connection != nil) {
4540 [connection cancel];
4541 //[connection setDelegate:nil];
4542 [connection release];
4547 [[list_ table] setDelegate:nil];
4548 [list_ setDataSource:nil];
4557 //[self _deallocConnection:installer_];
4558 [self _deallocConnection:trivial_gz_];
4559 [self _deallocConnection:trivial_bz2_];
4560 //[self _deallocConnection:automatic_];
4567 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4568 return offset_ == 0 ? 1 : 2;
4571 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4572 switch (section + (offset_ == 0 ? 1 : 0)) {
4573 case 0: return @"Entered by User";
4574 case 1: return @"Installed by Packages";
4582 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4583 switch (section + (offset_ == 0 ? 1 : 0)) {
4585 case 1: return offset_;
4593 - (int) numberOfRowsInTable:(UITable *)table {
4594 return [sources_ count];
4597 - (float) table:(UITable *)table heightForRow:(int)row {
4598 Source *source = [sources_ objectAtIndex:row];
4599 return [source description] == nil ? 56 : 73;
4602 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4603 Source *source = [sources_ objectAtIndex:row];
4604 // XXX: weird warning, stupid selectors ;P
4605 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4608 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4612 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4616 - (void) tableRowSelected:(NSNotification*)notification {
4617 UITable *table([list_ table]);
4618 int row([table selectedRow]);
4622 Source *source = [sources_ objectAtIndex:row];
4624 PackageTable *packages = [[[FilteredPackageTable alloc]
4627 title:[source label]
4628 filter:@selector(isVisibleInSource:)
4632 [packages setDelegate:delegate_];
4634 [book_ pushPage:packages];
4637 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4638 Source *source = [sources_ objectAtIndex:row];
4639 return [source record] != nil;
4642 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4643 [[list_ table] setDeleteConfirmationRow:row];
4646 - (void) table:(UITable *)table deleteRow:(int)row {
4647 Source *source = [sources_ objectAtIndex:row];
4648 [Sources_ removeObjectForKey:[source key]];
4649 [delegate_ syncData];
4652 - (void) _endConnection:(NSURLConnection *)connection {
4653 NSURLConnection **field = NULL;
4654 if (connection == trivial_bz2_)
4655 field = &trivial_bz2_;
4656 else if (connection == trivial_gz_)
4657 field = &trivial_gz_;
4658 _assert(field != NULL);
4659 [connection release];
4663 trivial_bz2_ == nil &&
4666 [delegate_ setStatusBarShowsProgress:NO];
4667 [delegate_ removeProgressHUD:hud_];
4673 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4676 @"./", @"Distribution",
4677 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4679 [delegate_ syncData];
4680 } else if (error_ != nil) {
4681 UIActionSheet *sheet = [[[UIActionSheet alloc]
4682 initWithTitle:@"Verification Error"
4683 buttons:[NSArray arrayWithObjects:@"OK", nil]
4684 defaultButtonIndex:0
4689 [sheet setBodyText:[error_ localizedDescription]];
4690 [sheet popupAlertAnimated:YES];
4692 UIActionSheet *sheet = [[[UIActionSheet alloc]
4693 initWithTitle:@"Did not Find Repository"
4694 buttons:[NSArray arrayWithObjects:@"OK", nil]
4695 defaultButtonIndex:0
4700 [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."];
4701 [sheet popupAlertAnimated:YES];
4707 if (error_ != nil) {
4714 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4715 switch ([response statusCode]) {
4721 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4722 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4724 error_ = [error retain];
4725 [self _endConnection:connection];
4728 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4729 [self _endConnection:connection];
4732 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4733 NSMutableURLRequest *request = [NSMutableURLRequest
4734 requestWithURL:[NSURL URLWithString:href]
4735 cachePolicy:NSURLRequestUseProtocolCachePolicy
4736 timeoutInterval:20.0
4739 [request setHTTPMethod:method];
4741 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4744 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4745 NSString *context([sheet context]);
4747 if ([context isEqualToString:@"source"]) {
4750 NSString *href = [[sheet textField] text];
4752 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4754 if (![href hasSuffix:@"/"])
4755 href_ = [href stringByAppendingString:@"/"];
4758 href_ = [href_ retain];
4760 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4761 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4762 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4766 hud_ = [[delegate_ addProgressHUD] retain];
4767 [hud_ setText:@"Verifying URL"];
4778 } else if ([context isEqualToString:@"trivial"])
4780 else if ([context isEqualToString:@"urlerror"])
4784 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4785 if ((self = [super initWithBook:book]) != nil) {
4786 database_ = database;
4787 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4789 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4790 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4791 [list_ setShouldHideHeaderInShortLists:NO];
4793 [self addSubview:list_];
4794 [list_ setDataSource:self];
4796 UITableColumn *column = [[UITableColumn alloc]
4797 initWithTitle:@"Name"
4799 width:[self frame].size.width
4802 UITable *table = [list_ table];
4803 [table setSeparatorStyle:1];
4804 [table addTableColumn:column];
4805 [table setDelegate:self];
4809 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4810 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4814 - (void) reloadData {
4816 _assert(list.ReadMainList());
4818 [sources_ removeAllObjects];
4819 [sources_ addObjectsFromArray:[database_ sources]];
4821 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4824 int count = [sources_ count];
4825 for (offset_ = 0; offset_ != count; ++offset_) {
4826 Source *source = [sources_ objectAtIndex:offset_];
4827 if ([source record] == nil)
4834 - (void) resetViewAnimated:(BOOL)animated {
4835 [list_ resetViewAnimated:animated];
4838 - (void) _leftButtonClicked {
4839 /*[book_ pushPage:[[[AddSourceView alloc]
4844 UIActionSheet *sheet = [[[UIActionSheet alloc]
4845 initWithTitle:@"Enter Cydia/APT URL"
4846 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4847 defaultButtonIndex:0
4852 [sheet setNumberOfRows:1];
4854 [sheet addTextFieldWithValue:@"http://" label:@""];
4856 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4857 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4858 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4859 [traits setKeyboardType:UIKeyboardTypeURL];
4860 // XXX: UIReturnKeyDone
4861 [traits setReturnKeyType:UIReturnKeyNext];
4863 [sheet popupAlertAnimated:YES];
4866 - (void) _rightButtonClicked {
4867 UITable *table = [list_ table];
4868 BOOL editing = [table isRowDeletionEnabled];
4869 [table enableRowDeletion:!editing animated:YES];
4870 [book_ reloadButtonsForPage:self];
4873 - (NSString *) title {
4877 - (NSString *) leftButtonTitle {
4878 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4881 - (id) rightButtonTitle {
4882 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4885 - (UINavigationButtonStyle) rightButtonStyle {
4886 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4892 /* Installed View {{{ */
4893 @interface InstalledView : RVPage {
4894 _transient Database *database_;
4895 FilteredPackageTable *packages_;
4899 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4903 @implementation InstalledView
4906 [packages_ release];
4910 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4911 if ((self = [super initWithBook:book]) != nil) {
4912 database_ = database;
4914 packages_ = [[FilteredPackageTable alloc]
4918 filter:@selector(isInstalledAndVisible:)
4919 with:[NSNumber numberWithBool:YES]
4922 [self addSubview:packages_];
4924 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4925 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4929 - (void) resetViewAnimated:(BOOL)animated {
4930 [packages_ resetViewAnimated:animated];
4933 - (void) reloadData {
4934 [packages_ reloadData];
4937 - (void) _rightButtonClicked {
4938 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4939 [packages_ reloadData];
4941 [book_ reloadButtonsForPage:self];
4944 - (NSString *) title {
4945 return @"Installed";
4948 - (NSString *) backButtonTitle {
4952 - (id) rightButtonTitle {
4953 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4956 - (UINavigationButtonStyle) rightButtonStyle {
4957 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4960 - (void) setDelegate:(id)delegate {
4961 [super setDelegate:delegate];
4962 [packages_ setDelegate:delegate];
4969 @interface HomeView : BrowserView {
4974 @implementation HomeView
4976 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4977 NSString *context([sheet context]);
4979 if ([context isEqualToString:@"about"])
4982 [super alertSheet:sheet buttonClicked:button];
4985 - (void) _leftButtonClicked {
4986 UIActionSheet *sheet = [[[UIActionSheet alloc]
4987 initWithTitle:@"About Cydia Installer"
4988 buttons:[NSArray arrayWithObjects:@"Close", nil]
4989 defaultButtonIndex:0
4995 @"Copyright (C) 2008\n"
4996 "Jay Freeman (saurik)\n"
4997 "saurik@saurik.com\n"
4998 "http://www.saurik.com/\n"
5001 "http://www.theokorigroup.com/\n"
5003 "College of Creative Studies,\n"
5004 "University of California,\n"
5006 "http://www.ccs.ucsb.edu/"
5009 [sheet popupAlertAnimated:YES];
5012 - (NSString *) leftButtonTitle {
5018 /* Manage View {{{ */
5019 @interface ManageView : BrowserView {
5024 @implementation ManageView
5026 - (NSString *) title {
5030 - (void) _leftButtonClicked {
5031 [delegate_ askForSettings];
5034 - (NSString *) leftButtonTitle {
5039 - (id) _rightButtonTitle {
5051 #include <BrowserView.m>
5053 /* Cydia Book {{{ */
5054 @interface CYBook : RVBook <
5057 _transient Database *database_;
5058 UINavigationBar *overlay_;
5059 UINavigationBar *underlay_;
5060 UIProgressIndicator *indicator_;
5061 UITextLabel *prompt_;
5062 UIProgressBar *progress_;
5063 UINavigationButton *cancel_;
5067 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5073 @implementation CYBook
5077 [indicator_ release];
5079 [progress_ release];
5084 - (NSString *) getTitleForPage:(RVPage *)page {
5085 return Simplify([super getTitleForPage:page]);
5093 [UIView beginAnimations:nil context:NULL];
5095 CGRect ovrframe = [overlay_ frame];
5096 ovrframe.origin.y = 0;
5097 [overlay_ setFrame:ovrframe];
5099 CGRect barframe = [navbar_ frame];
5100 barframe.origin.y += ovrframe.size.height;
5101 [navbar_ setFrame:barframe];
5103 CGRect trnframe = [transition_ frame];
5104 trnframe.origin.y += ovrframe.size.height;
5105 trnframe.size.height -= ovrframe.size.height;
5106 [transition_ setFrame:trnframe];
5108 [UIView endAnimations];
5110 [indicator_ startAnimation];
5111 [prompt_ setText:@"Updating Database"];
5112 [progress_ setProgress:0];
5115 [overlay_ addSubview:cancel_];
5118 detachNewThreadSelector:@selector(_update)
5127 [indicator_ stopAnimation];
5129 [UIView beginAnimations:nil context:NULL];
5131 CGRect ovrframe = [overlay_ frame];
5132 ovrframe.origin.y = -ovrframe.size.height;
5133 [overlay_ setFrame:ovrframe];
5135 CGRect barframe = [navbar_ frame];
5136 barframe.origin.y -= ovrframe.size.height;
5137 [navbar_ setFrame:barframe];
5139 CGRect trnframe = [transition_ frame];
5140 trnframe.origin.y -= ovrframe.size.height;
5141 trnframe.size.height += ovrframe.size.height;
5142 [transition_ setFrame:trnframe];
5144 [UIView commitAnimations];
5146 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5149 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5150 if ((self = [super initWithFrame:frame]) != nil) {
5151 database_ = database;
5153 CGRect ovrrect = [navbar_ bounds];
5154 ovrrect.size.height = [UINavigationBar defaultSize].height;
5155 ovrrect.origin.y = -ovrrect.size.height;
5157 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5158 [self addSubview:overlay_];
5160 ovrrect.origin.y = frame.size.height;
5161 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5162 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5163 [self addSubview:underlay_];
5165 [overlay_ setBarStyle:1];
5166 [underlay_ setBarStyle:1];
5168 int barstyle = [overlay_ _barStyle:NO];
5169 bool ugly = barstyle == 0;
5171 UIProgressIndicatorStyle style = ugly ?
5172 UIProgressIndicatorStyleMediumBrown :
5173 UIProgressIndicatorStyleMediumWhite;
5175 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5176 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5177 CGRect indrect = {{indoffset, indoffset}, indsize};
5179 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5180 [indicator_ setStyle:style];
5181 [overlay_ addSubview:indicator_];
5183 CGSize prmsize = {215, indsize.height + 4};
5186 indoffset * 2 + indsize.width,
5190 unsigned(ovrrect.size.height - prmsize.height) / 2
5193 UIFont *font = [UIFont systemFontOfSize:15];
5195 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5197 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5198 [prompt_ setBackgroundColor:[UIColor clearColor]];
5199 [prompt_ setFont:font];
5201 [overlay_ addSubview:prompt_];
5203 CGSize prgsize = {75, 100};
5206 ovrrect.size.width - prgsize.width - 10,
5207 (ovrrect.size.height - prgsize.height) / 2
5210 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5211 [progress_ setStyle:0];
5212 [overlay_ addSubview:progress_];
5214 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5215 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5217 CGRect frame = [cancel_ frame];
5218 frame.size.width = 65;
5219 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5220 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5221 [cancel_ setFrame:frame];
5223 [cancel_ setBarStyle:barstyle];
5227 - (void) _onCancel {
5229 [cancel_ removeFromSuperview];
5232 - (void) _update { _pooled
5234 status.setDelegate(self);
5236 [database_ updateWithStatus:status];
5239 performSelectorOnMainThread:@selector(_update_)
5245 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5246 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5249 - (void) setProgressTitle:(NSString *)title {
5251 performSelectorOnMainThread:@selector(_setProgressTitle:)
5257 - (void) setProgressPercent:(float)percent {
5259 performSelectorOnMainThread:@selector(_setProgressPercent:)
5260 withObject:[NSNumber numberWithFloat:percent]
5265 - (void) startProgress {
5268 - (void) addProgressOutput:(NSString *)output {
5270 performSelectorOnMainThread:@selector(_addProgressOutput:)
5276 - (bool) isCancelling:(size_t)received {
5280 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5284 - (void) _setProgressTitle:(NSString *)title {
5285 [prompt_ setText:title];
5288 - (void) _setProgressPercent:(NSNumber *)percent {
5289 [progress_ setProgress:[percent floatValue]];
5292 - (void) _addProgressOutput:(NSString *)output {
5297 /* Cydia:// Protocol {{{ */
5298 @interface CydiaURLProtocol : NSURLProtocol {
5303 @implementation CydiaURLProtocol
5305 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5306 NSURL *url([request URL]);
5309 NSString *scheme([[url scheme] lowercaseString]);
5310 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5315 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5319 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5320 id<NSURLProtocolClient> client([self client]);
5322 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5324 NSData *data(UIImagePNGRepresentation(icon));
5326 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5327 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5328 [client URLProtocol:self didLoadData:data];
5329 [client URLProtocolDidFinishLoading:self];
5333 - (void) startLoading {
5334 id<NSURLProtocolClient> client([self client]);
5335 NSURLRequest *request([self request]);
5337 NSURL *url([request URL]);
5338 NSString *href([url absoluteString]);
5340 NSString *path([href substringFromIndex:8]);
5341 NSRange slash([path rangeOfString:@"/"]);
5344 if (slash.location == NSNotFound) {
5348 command = [path substringToIndex:slash.location];
5349 path = [path substringFromIndex:(slash.location + 1)];
5352 Database *database([Database sharedInstance]);
5354 if ([command isEqualToString:@"package-icon"]) {
5357 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5358 Package *package([database packageWithName:path]);
5361 UIImage *icon([package icon]);
5362 [self _returnPNGWithImage:icon forRequest:request];
5363 } else if ([command isEqualToString:@"source-icon"]) {
5366 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5367 NSString *source(Simplify(path));
5368 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5370 icon = [UIImage applicationImageNamed:@"unknown.png"];
5371 [self _returnPNGWithImage:icon forRequest:request];
5372 } else if ([command isEqualToString:@"uikit-image"]) {
5375 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5376 UIImage *icon(_UIImageWithName(path));
5377 [self _returnPNGWithImage:icon forRequest:request];
5378 } else if ([command isEqualToString:@"section-icon"]) {
5381 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5382 NSString *section(Simplify(path));
5383 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5385 icon = [UIImage applicationImageNamed:@"unknown.png"];
5386 [self _returnPNGWithImage:icon forRequest:request];
5388 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5392 - (void) stopLoading {
5398 /* Sections View {{{ */
5399 @interface SectionsView : RVPage {
5400 _transient Database *database_;
5401 NSMutableArray *sections_;
5402 NSMutableArray *filtered_;
5403 UITransitionView *transition_;
5409 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5410 - (void) reloadData;
5415 @implementation SectionsView
5418 [list_ setDataSource:nil];
5419 [list_ setDelegate:nil];
5421 [sections_ release];
5422 [filtered_ release];
5423 [transition_ release];
5425 [accessory_ release];
5429 - (int) numberOfRowsInTable:(UITable *)table {
5430 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5433 - (float) table:(UITable *)table heightForRow:(int)row {
5437 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5439 reusing = [[[SectionCell alloc] init] autorelease];
5440 [(SectionCell *)reusing setSection:(editing_ ?
5441 [sections_ objectAtIndex:row] :
5442 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5443 ) editing:editing_];
5447 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5451 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5455 - (void) tableRowSelected:(NSNotification *)notification {
5456 int row = [[notification object] selectedRow];
5467 title = @"All Packages";
5469 section = [filtered_ objectAtIndex:(row - 1)];
5470 name = [section name];
5476 title = @"(No Section)";
5480 PackageTable *table = [[[FilteredPackageTable alloc]
5484 filter:@selector(isVisiblyUninstalledInSection:)
5488 [table setDelegate:delegate_];
5490 [book_ pushPage:table];
5493 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5494 if ((self = [super initWithBook:book]) != nil) {
5495 database_ = database;
5497 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5498 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5500 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5501 [self addSubview:transition_];
5503 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5504 [transition_ transition:0 toView:list_];
5506 UITableColumn *column = [[[UITableColumn alloc]
5507 initWithTitle:@"Name"
5509 width:[self frame].size.width
5512 [list_ setDataSource:self];
5513 [list_ setSeparatorStyle:1];
5514 [list_ addTableColumn:column];
5515 [list_ setDelegate:self];
5516 [list_ setReusesTableCells:YES];
5520 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5521 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5525 - (void) reloadData {
5526 NSArray *packages = [database_ packages];
5528 [sections_ removeAllObjects];
5529 [filtered_ removeAllObjects];
5531 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5532 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5535 for (Package *package in packages) {
5536 NSString *name([package section]);
5539 Section *section([sections objectForKey:name]);
5540 if (section == nil) {
5541 section = [[[Section alloc] initWithName:name] autorelease];
5542 [sections setObject:section forKey:name];
5546 if ([package valid] && [package installed] == nil && [package visible])
5547 [filtered addObject:package];
5551 [sections_ addObjectsFromArray:[sections allValues]];
5552 [sections_ sortUsingSelector:@selector(compareByName:)];
5555 [filtered sortUsingSelector:@selector(compareBySection:)];
5558 Section *section = nil;
5559 for (Package *package in filtered) {
5560 NSString *name = [package section];
5562 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5563 section = name == nil ?
5564 [[[Section alloc] initWithName:nil] autorelease] :
5565 [sections objectForKey:name];
5566 [filtered_ addObject:section];
5569 [section addToCount];
5577 - (void) resetView {
5579 [self _rightButtonClicked];
5582 - (void) resetViewAnimated:(BOOL)animated {
5583 [list_ resetViewAnimated:animated];
5586 - (void) _rightButtonClicked {
5587 if ((editing_ = !editing_))
5590 [delegate_ updateData];
5591 [book_ reloadTitleForPage:self];
5592 [book_ reloadButtonsForPage:self];
5595 - (NSString *) title {
5596 return editing_ ? @"Section Visibility" : @"Install by Section";
5599 - (NSString *) backButtonTitle {
5603 - (id) rightButtonTitle {
5604 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5607 - (UINavigationButtonStyle) rightButtonStyle {
5608 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5611 - (UIView *) accessoryView {
5617 /* Changes View {{{ */
5618 @interface ChangesView : RVPage {
5619 _transient Database *database_;
5620 NSMutableArray *packages_;
5621 NSMutableArray *sections_;
5622 UISectionList *list_;
5626 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5627 - (void) reloadData;
5631 @implementation ChangesView
5634 [[list_ table] setDelegate:nil];
5635 [list_ setDataSource:nil];
5637 [packages_ release];
5638 [sections_ release];
5643 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5644 return [sections_ count];
5647 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5648 return [[sections_ objectAtIndex:section] name];
5651 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5652 return [[sections_ objectAtIndex:section] row];
5655 - (int) numberOfRowsInTable:(UITable *)table {
5656 return [packages_ count];
5659 - (float) table:(UITable *)table heightForRow:(int)row {
5660 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5663 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5665 reusing = [[[PackageCell alloc] init] autorelease];
5666 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5670 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5674 - (void) tableRowSelected:(NSNotification *)notification {
5675 int row = [[notification object] selectedRow];
5678 Package *package = [packages_ objectAtIndex:row];
5679 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5680 [view setDelegate:delegate_];
5681 [view setPackage:package];
5682 [book_ pushPage:view];
5685 - (void) _leftButtonClicked {
5686 [(CYBook *)book_ update];
5687 [self reloadButtons];
5690 - (void) _rightButtonClicked {
5691 [delegate_ distUpgrade];
5694 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5695 if ((self = [super initWithBook:book]) != nil) {
5696 database_ = database;
5698 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5699 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5701 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5702 [self addSubview:list_];
5704 [list_ setShouldHideHeaderInShortLists:NO];
5705 [list_ setDataSource:self];
5706 //[list_ setSectionListStyle:1];
5708 UITableColumn *column = [[[UITableColumn alloc]
5709 initWithTitle:@"Name"
5711 width:[self frame].size.width
5714 UITable *table = [list_ table];
5715 [table setSeparatorStyle:1];
5716 [table addTableColumn:column];
5717 [table setDelegate:self];
5718 [table setReusesTableCells:YES];
5722 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5723 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5727 - (void) reloadData {
5728 NSArray *packages = [database_ packages];
5730 [packages_ removeAllObjects];
5731 [sections_ removeAllObjects];
5734 for (Package *package in packages)
5736 [package installed] == nil && [package valid] && [package visible] ||
5737 [package upgradableAndEssential:YES]
5739 [packages_ addObject:package];
5742 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5745 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5746 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5747 Section *section = nil;
5751 bool unseens = false;
5753 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5756 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5757 Package *package = [packages_ objectAtIndex:offset];
5759 if (![package upgradableAndEssential:YES]) {
5761 NSDate *seen = [package seen];
5763 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5766 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5767 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5768 [sections_ addObject:section];
5772 [section addToCount];
5773 } else if ([package ignored])
5774 [ignored addToCount];
5777 [upgradable addToCount];
5782 CFRelease(formatter);
5785 Section *last = [sections_ lastObject];
5786 size_t count = [last count];
5787 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5788 [sections_ removeLastObject];
5791 if ([ignored count] != 0)
5792 [sections_ insertObject:ignored atIndex:0];
5794 [sections_ insertObject:upgradable atIndex:0];
5797 [self reloadButtons];
5800 - (void) resetViewAnimated:(BOOL)animated {
5801 [list_ resetViewAnimated:animated];
5804 - (NSString *) leftButtonTitle {
5805 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5808 - (id) rightButtonTitle {
5809 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5812 - (NSString *) title {
5818 /* Search View {{{ */
5819 @protocol SearchViewDelegate
5820 - (void) showKeyboard:(BOOL)show;
5823 @interface SearchView : RVPage {
5825 UISearchField *field_;
5826 UITransitionView *transition_;
5827 FilteredPackageTable *table_;
5828 UIPreferencesTable *advanced_;
5834 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5835 - (void) reloadData;
5839 @implementation SearchView
5842 [field_ setDelegate:nil];
5844 [accessory_ release];
5846 [transition_ release];
5848 [advanced_ release];
5853 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5857 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5859 case 0: return @"Advanced Search (Coming Soon!)";
5861 default: _assert(false);
5865 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5869 default: _assert(false);
5873 - (void) _showKeyboard:(BOOL)show {
5874 CGSize keysize = [UIKeyboard defaultSize];
5875 CGRect keydown = [book_ pageBounds];
5876 CGRect keyup = keydown;
5877 keyup.size.height -= keysize.height - ButtonBarHeight_;
5879 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5881 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5882 [animation setSignificantRectFields:8];
5885 [animation setStartFrame:keydown];
5886 [animation setEndFrame:keyup];
5888 [animation setStartFrame:keyup];
5889 [animation setEndFrame:keydown];
5892 UIAnimator *animator = [UIAnimator sharedAnimator];
5895 addAnimations:[NSArray arrayWithObjects:animation, nil]
5896 withDuration:(KeyboardTime_ - delay)
5901 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5903 [delegate_ showKeyboard:show];
5906 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5907 [self _showKeyboard:YES];
5910 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5911 [self _showKeyboard:NO];
5914 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5916 NSString *text([field_ text]);
5917 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5923 - (void) textFieldClearButtonPressed:(UITextField *)field {
5927 - (void) keyboardInputShouldDelete:(id)input {
5931 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5932 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5936 [field_ resignFirstResponder];
5941 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5942 if ((self = [super initWithBook:book]) != nil) {
5943 CGRect pageBounds = [book_ pageBounds];
5945 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5946 [self addSubview:transition_];
5948 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5950 [advanced_ setReusesTableCells:YES];
5951 [advanced_ setDataSource:self];
5952 [advanced_ reloadData];
5954 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5955 CGColor dimmed(space_, 0, 0, 0, 0.5);
5956 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5958 table_ = [[FilteredPackageTable alloc]
5962 filter:@selector(isUnfilteredAndSearchedForBy:)
5966 [table_ setShouldHideHeaderInShortLists:NO];
5967 [transition_ transition:0 toView:table_];
5976 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5983 [self bounds].size.width - area.origin.x - 18;
5985 area.size.height = [UISearchField defaultHeight];
5987 field_ = [[UISearchField alloc] initWithFrame:area];
5989 UIFont *font = [UIFont systemFontOfSize:16];
5990 [field_ setFont:font];
5992 [field_ setPlaceholder:@"Package Names & Descriptions"];
5993 [field_ setDelegate:self];
5995 [field_ setPaddingTop:5];
5997 UITextInputTraits *traits([field_ textInputTraits]);
5998 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5999 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6000 [traits setReturnKeyType:UIReturnKeySearch];
6002 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6004 accessory_ = [[UIView alloc] initWithFrame:accrect];
6005 [accessory_ addSubview:field_];
6007 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6008 [configure setShowPressFeedback:YES];
6009 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6010 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6011 [accessory_ addSubview:configure];*/
6013 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6014 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6020 LKAnimation *animation = [LKTransition animation];
6021 [animation setType:@"oglFlip"];
6022 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6023 [animation setFillMode:@"extended"];
6024 [animation setTransitionFlags:3];
6025 [animation setDuration:10];
6026 [animation setSpeed:0.35];
6027 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6028 [[transition_ _layer] addAnimation:animation forKey:0];
6029 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6030 flipped_ = !flipped_;
6034 - (void) configurePushed {
6035 [field_ resignFirstResponder];
6039 - (void) resetViewAnimated:(BOOL)animated {
6042 [table_ resetViewAnimated:animated];
6045 - (void) _reloadData {
6048 - (void) reloadData {
6051 [table_ setObject:[field_ text]];
6052 _profile(SearchView$reloadData)
6053 [table_ reloadData];
6056 [table_ resetCursor];
6059 - (UIView *) accessoryView {
6063 - (NSString *) title {
6067 - (NSString *) backButtonTitle {
6071 - (void) setDelegate:(id)delegate {
6072 [table_ setDelegate:delegate];
6073 [super setDelegate:delegate];
6079 @interface SettingsView : RVPage {
6080 _transient Database *database_;
6083 UIPreferencesTable *table_;
6084 _UISwitchSlider *subscribedSwitch_;
6085 _UISwitchSlider *ignoredSwitch_;
6086 UIPreferencesControlTableCell *subscribedCell_;
6087 UIPreferencesControlTableCell *ignoredCell_;
6090 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6094 @implementation SettingsView
6097 [table_ setDataSource:nil];
6100 if (package_ != nil)
6103 [subscribedSwitch_ release];
6104 [ignoredSwitch_ release];
6105 [subscribedCell_ release];
6106 [ignoredCell_ release];
6110 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6111 if (package_ == nil)
6117 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6118 if (package_ == nil)
6125 default: _assert(false);
6131 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6132 if (package_ == nil)
6139 default: _assert(false);
6145 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6146 if (package_ == nil)
6153 default: _assert(false);
6159 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6160 if (package_ == nil)
6163 _UISwitchSlider *slider([cell control]);
6164 BOOL value([slider value] != 0);
6165 NSMutableDictionary *metadata([package_ metadata]);
6168 if (NSNumber *number = [metadata objectForKey:key])
6169 before = [number boolValue];
6173 if (value != before) {
6174 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6176 [delegate_ updateData];
6180 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6181 [self onSomething:cell withKey:@"IsSubscribed"];
6184 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6185 [self onSomething:cell withKey:@"IsIgnored"];
6188 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6189 if (package_ == nil)
6193 case 0: switch (row) {
6195 return subscribedCell_;
6197 return ignoredCell_;
6198 default: _assert(false);
6201 case 1: switch (row) {
6203 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6204 [cell setShowSelection:NO];
6205 [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."];
6209 default: _assert(false);
6212 default: _assert(false);
6218 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6219 if ((self = [super initWithBook:book])) {
6220 database_ = database;
6221 name_ = [package retain];
6223 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6224 [self addSubview:table_];
6226 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6227 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6229 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6230 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6232 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6233 [subscribedCell_ setShowSelection:NO];
6234 [subscribedCell_ setTitle:@"Show All Changes"];
6235 [subscribedCell_ setControl:subscribedSwitch_];
6237 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6238 [ignoredCell_ setShowSelection:NO];
6239 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6240 [ignoredCell_ setControl:ignoredSwitch_];
6242 [table_ setDataSource:self];
6247 - (void) resetViewAnimated:(BOOL)animated {
6248 [table_ resetViewAnimated:animated];
6251 - (void) reloadData {
6252 if (package_ != nil)
6253 [package_ autorelease];
6254 package_ = [database_ packageWithName:name_];
6255 if (package_ != nil) {
6257 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6258 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6261 [table_ reloadData];
6264 - (NSString *) title {
6270 /* Signature View {{{ */
6271 @interface SignatureView : BrowserView {
6272 _transient Database *database_;
6276 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6280 @implementation SignatureView
6287 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6289 [super webView:sender didClearWindowObject:window forFrame:frame];
6292 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6293 if ((self = [super initWithBook:book]) != nil) {
6294 database_ = database;
6295 package_ = [package retain];
6300 - (void) resetViewAnimated:(BOOL)animated {
6303 - (void) reloadData {
6304 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6310 @interface Cydia : UIApplication <
6311 ConfirmationViewDelegate,
6312 ProgressViewDelegate,
6321 UIToolbar *buttonbar_;
6325 NSMutableArray *essential_;
6326 NSMutableArray *broken_;
6328 Database *database_;
6329 ProgressView *progress_;
6333 UIKeyboard *keyboard_;
6334 UIProgressHUD *hud_;
6336 SectionsView *sections_;
6337 ChangesView *changes_;
6338 ManageView *manage_;
6339 SearchView *search_;
6344 @implementation Cydia
6347 if ([broken_ count] != 0) {
6348 int count = [broken_ count];
6350 UIActionSheet *sheet = [[[UIActionSheet alloc]
6351 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6352 buttons:[NSArray arrayWithObjects:
6354 @"Ignore (Temporary)",
6356 defaultButtonIndex:0
6361 [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."];
6362 [sheet popupAlertAnimated:YES];
6363 } else if (!Ignored_ && [essential_ count] != 0) {
6364 int count = [essential_ count];
6366 UIActionSheet *sheet = [[[UIActionSheet alloc]
6367 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6368 buttons:[NSArray arrayWithObjects:
6369 @"Upgrade Essential",
6370 @"Complete Upgrade",
6371 @"Ignore (Temporary)",
6373 defaultButtonIndex:0
6378 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6379 [sheet popupAlertAnimated:YES];
6383 - (void) _reloadData {
6386 UIProgressHUD *hud([self addProgressHUD]);
6387 [hud setText:@"Reloading Data"];
6389 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6392 [self removeProgressHUD:hud];
6396 [essential_ removeAllObjects];
6397 [broken_ removeAllObjects];
6399 NSArray *packages = [database_ packages];
6400 for (Package *package in packages) {
6402 [broken_ addObject:package];
6403 if ([package upgradableAndEssential:NO]) {
6404 if ([package essential])
6405 [essential_ addObject:package];
6411 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6412 [buttonbar_ setBadgeValue:badge forButton:3];
6413 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6414 [buttonbar_ setBadgeAnimated:YES forButton:3];
6415 [self setApplicationBadge:badge];
6417 [buttonbar_ setBadgeValue:nil forButton:3];
6418 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6419 [buttonbar_ setBadgeAnimated:NO forButton:3];
6420 [self removeApplicationBadge];
6425 // XXX: what is this line of code for?
6426 if ([packages count] == 0);
6427 else if (Loaded_) loaded:
6432 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6433 NSTimeInterval interval([update timeIntervalSinceNow]);
6434 if (interval <= 0 && interval > -600)
6442 - (void) _saveConfig {
6445 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6451 - (void) updateData {
6454 /* XXX: this is just stupid */
6455 if (tag_ != 2 && sections_ != nil)
6456 [sections_ reloadData];
6457 if (tag_ != 3 && changes_ != nil)
6458 [changes_ reloadData];
6459 if (tag_ != 5 && search_ != nil)
6460 [search_ reloadData];
6470 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6471 _assert(file != NULL);
6473 NSArray *keys = [Sources_ allKeys];
6475 for (NSString *key in keys) {
6476 NSDictionary *source = [Sources_ objectForKey:key];
6478 fprintf(file, "%s %s %s\n",
6479 [[source objectForKey:@"Type"] UTF8String],
6480 [[source objectForKey:@"URI"] UTF8String],
6481 [[source objectForKey:@"Distribution"] UTF8String]
6490 detachNewThreadSelector:@selector(update_)
6493 title:@"Updating Sources"
6497 - (void) reloadData {
6498 @synchronized (self) {
6499 if (confirm_ == nil)
6505 pkgProblemResolver *resolver = [database_ resolver];
6507 resolver->InstallProtect();
6508 if (!resolver->Resolve(true))
6512 - (void) popUpBook:(RVBook *)book {
6513 [underlay_ popSubview:book];
6516 - (CGRect) popUpBounds {
6517 return [underlay_ bounds];
6521 [database_ prepare];
6523 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6524 [confirm_ setDelegate:self];
6526 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6527 [page setDelegate:self];
6529 [confirm_ setPage:page];
6530 [self popUpBook:confirm_];
6533 - (void) installPackage:(Package *)package {
6534 @synchronized (self) {
6541 - (void) removePackage:(Package *)package {
6542 @synchronized (self) {
6549 - (void) distUpgrade {
6550 @synchronized (self) {
6551 [database_ upgrade];
6557 @synchronized (self) {
6559 if (confirm_ != nil) {
6567 [overlay_ removeFromSuperview];
6571 detachNewThreadSelector:@selector(perform)
6578 - (void) bootstrap_ {
6580 [database_ upgrade];
6581 [database_ prepare];
6582 [database_ perform];
6585 - (void) bootstrap {
6587 detachNewThreadSelector:@selector(bootstrap_)
6590 title:@"Bootstrap Install"
6594 - (void) progressViewIsComplete:(ProgressView *)progress {
6595 if (confirm_ != nil) {
6596 [underlay_ addSubview:overlay_];
6597 [confirm_ popFromSuperviewAnimated:NO];
6603 - (void) setPage:(RVPage *)page {
6604 [page resetViewAnimated:NO];
6605 [page setDelegate:self];
6606 [book_ setPage:page];
6609 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6610 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6611 [browser loadURL:url];
6615 - (void) _setHomePage {
6616 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6619 - (void) buttonBarItemTapped:(id)sender {
6620 unsigned tag = [sender tag];
6622 [book_ resetViewAnimated:YES];
6624 } else if (tag_ == 2 && tag != 2)
6625 [sections_ resetView];
6628 case 1: [self _setHomePage]; break;
6630 case 2: [self setPage:sections_]; break;
6631 case 3: [self setPage:changes_]; break;
6632 case 4: [self setPage:manage_]; break;
6633 case 5: [self setPage:search_]; break;
6635 default: _assert(false);
6641 - (void) applicationWillSuspend {
6643 [super applicationWillSuspend];
6646 - (void) askForSettings {
6647 UIActionSheet *role = [[[UIActionSheet alloc]
6648 initWithTitle:@"Who Are You?"
6649 buttons:[NSArray arrayWithObjects:
6650 @"User (Graphical Only)",
6651 @"Hacker (+ Command Line)",
6652 @"Developer (No Filters)",
6654 defaultButtonIndex:-1
6659 [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."];
6660 [role popupAlertAnimated:YES];
6665 [self setStatusBarShowsProgress:NO];
6666 [self removeProgressHUD:hud_];
6671 pid_t pid = ExecFork();
6673 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6674 perror("launchctl stop");
6681 [self askForSettings];
6686 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6688 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6689 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6690 0, 0, screenrect.size.width, screenrect.size.height - 48
6691 ) database:database_];
6693 [book_ setDelegate:self];
6695 [overlay_ addSubview:book_];
6697 NSArray *buttonitems = [NSArray arrayWithObjects:
6698 [NSDictionary dictionaryWithObjectsAndKeys:
6699 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6700 @"home-up.png", kUIButtonBarButtonInfo,
6701 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6702 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6703 self, kUIButtonBarButtonTarget,
6704 @"Home", kUIButtonBarButtonTitle,
6705 @"0", kUIButtonBarButtonType,
6708 [NSDictionary dictionaryWithObjectsAndKeys:
6709 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6710 @"install-up.png", kUIButtonBarButtonInfo,
6711 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6712 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6713 self, kUIButtonBarButtonTarget,
6714 @"Sections", kUIButtonBarButtonTitle,
6715 @"0", kUIButtonBarButtonType,
6718 [NSDictionary dictionaryWithObjectsAndKeys:
6719 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6720 @"changes-up.png", kUIButtonBarButtonInfo,
6721 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6722 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6723 self, kUIButtonBarButtonTarget,
6724 @"Changes", kUIButtonBarButtonTitle,
6725 @"0", kUIButtonBarButtonType,
6728 [NSDictionary dictionaryWithObjectsAndKeys:
6729 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6730 @"manage-up.png", kUIButtonBarButtonInfo,
6731 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6732 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6733 self, kUIButtonBarButtonTarget,
6734 @"Manage", kUIButtonBarButtonTitle,
6735 @"0", kUIButtonBarButtonType,
6738 [NSDictionary dictionaryWithObjectsAndKeys:
6739 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6740 @"search-up.png", kUIButtonBarButtonInfo,
6741 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6742 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6743 self, kUIButtonBarButtonTarget,
6744 @"Search", kUIButtonBarButtonTitle,
6745 @"0", kUIButtonBarButtonType,
6749 buttonbar_ = [[UIToolbar alloc]
6751 withFrame:CGRectMake(
6752 0, screenrect.size.height - ButtonBarHeight_,
6753 screenrect.size.width, ButtonBarHeight_
6755 withItemList:buttonitems
6758 [buttonbar_ setDelegate:self];
6759 [buttonbar_ setBarStyle:1];
6760 [buttonbar_ setButtonBarTrackingMode:2];
6762 int buttons[5] = {1, 2, 3, 4, 5};
6763 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6764 [buttonbar_ showButtonGroup:0 withDuration:0];
6766 for (int i = 0; i != 5; ++i)
6767 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6768 i * 64 + 2, 1, 60, ButtonBarHeight_
6771 [buttonbar_ showSelectionForButton:1];
6772 [overlay_ addSubview:buttonbar_];
6774 [UIKeyboard initImplementationNow];
6775 CGSize keysize = [UIKeyboard defaultSize];
6776 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6777 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6778 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6779 [overlay_ addSubview:keyboard_];
6782 [underlay_ addSubview:overlay_];
6786 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6787 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6788 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6790 manage_ = (ManageView *) [[self
6791 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6792 withClass:[ManageView class]
6800 [self _setHomePage];
6803 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6804 NSString *context([sheet context]);
6806 if ([context isEqualToString:@"missing"])
6808 else if ([context isEqualToString:@"fixhalf"]) {
6811 @synchronized (self) {
6812 for (Package *broken in broken_) {
6815 NSString *id = [broken id];
6816 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6817 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6818 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6819 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6828 [broken_ removeAllObjects];
6837 } else if ([context isEqualToString:@"role"]) {
6839 case 1: Role_ = @"User"; break;
6840 case 2: Role_ = @"Hacker"; break;
6841 case 3: Role_ = @"Developer"; break;
6848 bool reset = Settings_ != nil;
6850 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6854 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6864 } else if ([context isEqualToString:@"upgrade"]) {
6867 @synchronized (self) {
6868 for (Package *essential in essential_)
6869 [essential install];
6892 - (void) reorganize { _pooled
6893 system("/usr/libexec/cydia/free.sh");
6894 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6897 - (void) applicationSuspend:(__GSEvent *)event {
6898 if (hud_ == nil && ![progress_ isRunning])
6899 [super applicationSuspend:event];
6902 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6904 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6907 - (void) _setSuspended:(BOOL)value {
6909 [super _setSuspended:value];
6912 - (UIProgressHUD *) addProgressHUD {
6913 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
6914 [window_ setUserInteractionEnabled:NO];
6916 [progress_ addSubview:hud];
6920 - (void) removeProgressHUD:(UIProgressHUD *)hud {
6922 [hud removeFromSuperview];
6923 [window_ setUserInteractionEnabled:YES];
6926 - (void) openMailToURL:(NSURL *)url {
6927 // XXX: this makes me sad
6929 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6931 [UIApp openURL:url];// asPanel:YES];
6935 - (void) clearFirstResponder {
6936 if (id responder = [window_ firstResponder])
6937 [responder resignFirstResponder];
6940 - (RVPage *) pageForPackage:(NSString *)name {
6941 if (Package *package = [database_ packageWithName:name]) {
6942 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6943 [view setPackage:package];
6946 UIActionSheet *sheet = [[[UIActionSheet alloc]
6947 initWithTitle:@"Cannot Locate Package"
6948 buttons:[NSArray arrayWithObjects:@"Close", nil]
6949 defaultButtonIndex:0
6954 [sheet setBodyText:[NSString stringWithFormat:
6955 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6958 [sheet popupAlertAnimated:YES];
6963 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6967 NSString *scheme([[url scheme] lowercaseString]);
6968 if (![scheme isEqualToString:@"cydia"])
6970 NSString *path([url absoluteString]);
6971 if ([path length] < 8)
6973 path = [path substringFromIndex:8];
6974 if (![path hasPrefix:@"/"])
6975 path = [@"/" stringByAppendingString:path];
6977 if ([path isEqualToString:@"/add-source"])
6978 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6979 else if ([path isEqualToString:@"/storage"])
6980 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6981 else if ([path isEqualToString:@"/sources"])
6982 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6983 else if ([path isEqualToString:@"/packages"])
6984 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6985 else if ([path hasPrefix:@"/url/"])
6986 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6987 else if ([path hasPrefix:@"/launch/"])
6988 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6989 else if ([path hasPrefix:@"/package-settings/"])
6990 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6991 else if ([path hasPrefix:@"/package-signature/"])
6992 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6993 else if ([path hasPrefix:@"/package/"])
6994 return [self pageForPackage:[path substringFromIndex:9]];
6995 else if ([path hasPrefix:@"/files/"]) {
6996 NSString *name = [path substringFromIndex:7];
6998 if (Package *package = [database_ packageWithName:name]) {
6999 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7000 [files setPackage:package];
7008 - (void) applicationOpenURL:(NSURL *)url {
7009 [super applicationOpenURL:url];
7011 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7012 [self setPage:page];
7013 [buttonbar_ showSelectionForButton:tag];
7018 - (void) applicationDidFinishLaunching:(id)unused {
7020 Font12_ = [[UIFont systemFontOfSize:12] retain];
7021 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7022 Font14_ = [[UIFont systemFontOfSize:14] retain];
7023 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7024 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7026 _assert(pkgInitConfig(*_config));
7027 _assert(pkgInitSystem(*_config, _system));
7031 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7032 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7034 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7036 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7037 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7039 [window_ orderFront:self];
7040 [window_ makeKey:self];
7041 [window_ setHidden:NO];
7043 database_ = [Database sharedInstance];
7044 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7045 [database_ setDelegate:progress_];
7046 [window_ setContentView:progress_];
7048 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7049 [progress_ setContentView:underlay_];
7051 [progress_ resetView];
7054 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7055 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7056 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7057 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7058 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7059 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7060 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7062 [self setIdleTimerDisabled:YES];
7064 hud_ = [[self addProgressHUD] retain];
7065 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7067 [self setStatusBarShowsProgress:YES];
7070 detachNewThreadSelector:@selector(reorganize)
7078 - (void) showKeyboard:(BOOL)show {
7079 CGSize keysize = [UIKeyboard defaultSize];
7080 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7081 CGRect keyup = keydown;
7082 keyup.origin.y -= keysize.height;
7084 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7085 [animation setSignificantRectFields:2];
7088 [animation setStartFrame:keydown];
7089 [animation setEndFrame:keyup];
7090 [keyboard_ activate];
7092 [animation setStartFrame:keyup];
7093 [animation setEndFrame:keydown];
7094 [keyboard_ deactivate];
7097 [[UIAnimator sharedAnimator]
7098 addAnimations:[NSArray arrayWithObjects:animation, nil]
7099 withDuration:KeyboardTime_
7104 - (void) slideUp:(UIActionSheet *)alert {
7106 [alert presentSheetFromButtonBar:buttonbar_];
7108 [alert presentSheetInView:overlay_];
7113 void AddPreferences(NSString *plist) { _pooled
7114 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7115 _assert(settings != NULL);
7116 NSMutableArray *items = [settings objectForKey:@"items"];
7120 for (NSMutableDictionary *item in items) {
7121 NSString *label = [item objectForKey:@"label"];
7122 if (label != nil && [label isEqualToString:@"Cydia"]) {
7129 for (size_t i(0); i != [items count]; ++i) {
7130 NSDictionary *item([items objectAtIndex:i]);
7131 NSString *label = [item objectForKey:@"label"];
7132 if (label != nil && [label isEqualToString:@"General"]) {
7133 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7134 @"CydiaSettings", @"bundle",
7135 @"PSLinkCell", @"cell",
7136 [NSNumber numberWithBool:YES], @"hasIcon",
7137 [NSNumber numberWithBool:YES], @"isController",
7139 nil] atIndex:(i + 1)];
7145 _assert([settings writeToFile:plist atomically:YES] == YES);
7150 id Alloc_(id self, SEL selector) {
7151 id object = alloc_(self, selector);
7152 lprintf("[%s]A-%p\n", self->isa->name, object);
7157 id Dealloc_(id self, SEL selector) {
7158 id object = dealloc_(self, selector);
7159 lprintf("[%s]D-%p\n", self->isa->name, object);
7163 int main(int argc, char *argv[]) { _pooled
7165 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7167 bool substrate(false);
7173 for (int argi(1); argi != argc; ++argi)
7174 if (strcmp(argv[argi], "--") == 0) {
7176 argv[argi] = argv[0];
7182 for (int argi(1); argi != arge; ++argi)
7183 if (strcmp(args[argi], "--bootstrap") == 0)
7185 else if (strcmp(args[argi], "--substrate") == 0)
7188 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7191 App_ = [[NSBundle mainBundle] bundlePath];
7192 Home_ = NSHomeDirectory();
7193 Locale_ = CFLocaleCopyCurrent();
7196 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7197 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7198 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7199 Sounds_Keyboard_ = [keyboard boolValue];
7205 #if 0 /* XXX: this costs 1.4s of startup performance */
7206 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7207 _assert(errno == ENOENT);
7208 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7209 _assert(errno == ENOENT);
7212 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7213 alloc_ = alloc->method_imp;
7214 alloc->method_imp = (IMP) &Alloc_;*/
7216 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7217 dealloc_ = dealloc->method_imp;
7218 dealloc->method_imp = (IMP) &Dealloc_;*/
7223 size = sizeof(maxproc);
7224 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7225 perror("sysctlbyname(\"kern.maxproc\", ?)");
7226 else if (maxproc < 64) {
7228 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7229 perror("sysctlbyname(\"kern.maxproc\", #)");
7232 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7233 char *machine = new char[size];
7234 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7235 perror("sysctlbyname(\"hw.machine\", ?)");
7239 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7241 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7242 Build_ = [system objectForKey:@"ProductBuildVersion"];
7244 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7245 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7248 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7251 if (Metadata_ == NULL)
7252 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7254 Settings_ = [Metadata_ objectForKey:@"Settings"];
7256 Packages_ = [Metadata_ objectForKey:@"Packages"];
7257 Sections_ = [Metadata_ objectForKey:@"Sections"];
7258 Sources_ = [Metadata_ objectForKey:@"Sources"];
7261 if (Settings_ != nil)
7262 Role_ = [Settings_ objectForKey:@"Role"];
7264 if (Packages_ == nil) {
7265 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7266 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7269 if (Sections_ == nil) {
7270 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7271 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7274 if (Sources_ == nil) {
7275 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7276 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7280 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7283 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7284 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7285 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7286 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7288 if (access("/User", F_OK) != 0) {
7290 system("/usr/libexec/cydia/firmware.sh");
7294 _assert([[NSFileManager defaultManager]
7295 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7296 withIntermediateDirectories:YES
7301 space_ = CGColorSpaceCreateDeviceRGB();
7303 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7304 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7305 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7306 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7307 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7308 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7310 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7312 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7314 UIApplicationUseLegacyEvents(YES);
7315 UIKeyboardDisableAutomaticAppearance();
7318 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7320 CGColorSpaceRelease(space_);